From 4b4ec6d2eeb7548536c1d0b7ad0238ab32db054f Mon Sep 17 00:00:00 2001 From: Vikram Reddy Date: Mon, 8 Jun 2026 13:51:46 +0530 Subject: [PATCH] Add WinGet package deployment workflow --- .github/workflows/winget-package-publish.yml | 103 +++++++++++++++++++ docs/how-to/README.md | 20 +++- docs/how-to/setup/winget.md | 8 +- packaging/winget/devspec.yaml | 6 +- 4 files changed, 128 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/winget-package-publish.yml diff --git a/.github/workflows/winget-package-publish.yml b/.github/workflows/winget-package-publish.yml new file mode 100644 index 0000000..fe7fe75 --- /dev/null +++ b/.github/workflows/winget-package-publish.yml @@ -0,0 +1,103 @@ +name: WinGet Package Publish + +on: + workflow_dispatch: + push: + tags: + - "v*" + +permissions: + contents: write + +jobs: + winget-package: + name: Build WinGet portable package + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v5 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Run tests + run: uv run pytest + + - name: Build Python wheel + run: uv build + + - name: Build devspec.exe + shell: pwsh + run: | + $ErrorActionPreference = "Stop" + $wheel = Get-ChildItem dist -Filter "*.whl" | Select-Object -First 1 + if (-not $wheel) { + throw "No wheel found in dist." + } + + uv venv .venv-winget --python 3.12 --clear + uv pip install --python .\.venv-winget\Scripts\python.exe "pyinstaller>=6.0" $wheel.FullName + + New-Item -ItemType Directory -Force -Path build\winget | Out-Null + @( + "from devspec_installer.cli import main" + "" + "raise SystemExit(main())" + ) | Set-Content -Path build\winget\devspec-entry.py -Encoding UTF8 + + .\.venv-winget\Scripts\pyinstaller.exe ` + --noconfirm ` + --clean ` + --onefile ` + --name devspec ` + --distpath dist\winget ` + --workpath build\pyinstaller ` + --specpath build\pyinstaller ` + --collect-data devspec_installer ` + build\winget\devspec-entry.py + + - name: Smoke test devspec.exe + shell: pwsh + run: | + $ErrorActionPreference = "Stop" + $target = Join-Path $env:RUNNER_TEMP "devspec-winget-smoke" + New-Item -ItemType Directory -Force -Path $target | Out-Null + + .\dist\winget\devspec.exe version + .\dist\winget\devspec.exe init --target $target --profile core --repo-state existing + .\dist\winget\devspec.exe doctor --target $target --profile core + + - name: Compute devspec.exe SHA256 + shell: pwsh + run: | + $ErrorActionPreference = "Stop" + $hash = (Get-FileHash dist\winget\devspec.exe -Algorithm SHA256).Hash.ToLowerInvariant() + "$hash devspec.exe" | Set-Content -Path dist\winget\devspec.exe.sha256 -Encoding ASCII + + $manifest = Get-Content packaging\winget\devspec.yaml -Raw + $manifest = $manifest.Replace("REPLACE_WITH_RELEASE_SHA256", $hash) + $manifest | Set-Content -Path dist\winget\devspec.yaml -Encoding UTF8 + + Write-Host "devspec.exe SHA256: $hash" + + - name: Upload WinGet package artifacts + uses: actions/upload-artifact@v4 + with: + name: devspec-winget-package + path: | + dist/winget/devspec.exe + dist/winget/devspec.exe.sha256 + dist/winget/devspec.yaml + + - name: Attach WinGet package artifacts + if: startsWith(github.ref, 'refs/tags/v') + uses: softprops/action-gh-release@v2 + with: + files: | + dist/winget/devspec.exe + dist/winget/devspec.exe.sha256 + dist/winget/devspec.yaml diff --git a/docs/how-to/README.md b/docs/how-to/README.md index f2a94bd..01f3213 100644 --- a/docs/how-to/README.md +++ b/docs/how-to/README.md @@ -506,14 +506,30 @@ powershell -ExecutionPolicy Bypass -File scripts/test-local-install.ps1 bash scripts/test-local-install.sh ``` -Use provider-specific GitHub workflow filenames and display names. The current Python package workflows are: +Use provider-specific GitHub workflow filenames and display names. The current package-provider workflows are: ```text .github/workflows/python-package-ci.yml .github/workflows/python-package-publish.yml +.github/workflows/winget-package-publish.yml ``` -Future package-provider workflows should follow the same pattern, such as `homebrew-package-publish.yml`, `winget-package-publish.yml`, or `npm-package-publish.yml`. +Future package-provider workflows should follow the same pattern, such as `homebrew-package-publish.yml` or `npm-package-publish.yml`. + +For WinGet releases, publish the Windows portable executable from GitHub Releases and submit the generated manifest to `microsoft/winget-pkgs`: + +```text +dist/winget/devspec.exe +dist/winget/devspec.exe.sha256 +dist/winget/devspec.yaml +``` + +Validate the generated manifest on Windows before submission: + +```text +winget validate +winget install --manifest +``` When adapter files are added, removed, or moved, update: diff --git a/docs/how-to/setup/winget.md b/docs/how-to/setup/winget.md index 518903c..d1dfade 100644 --- a/docs/how-to/setup/winget.md +++ b/docs/how-to/setup/winget.md @@ -2,7 +2,7 @@ Use this guide on Windows when your machine allows approved WinGet packages. WinGet is Windows' package installer for command-line and desktop tools. -The WinGet package identifier is a placeholder until the public package is finalized. +The planned public WinGet package identifier is `SpecLabs.Devspec`. Until Microsoft approves the package submission, use `uvx devspec ...` or manual copy as the fallback. ## Before You Start @@ -31,7 +31,7 @@ cd D:\code\my-app Install the CLI: ```text -winget install .devspec +winget install SpecLabs.Devspec ``` Install devspec files into your repository: @@ -122,7 +122,7 @@ devspec init --target D:\code\my-app --profile all --repo-state existing | Argument | Meaning | Beginner explanation | | --- | --- | --- | | `winget --version` | Print the WinGet version. | Confirms WinGet is available on the machine. | -| `winget install .devspec` | Install the devspec CLI. | The `` placeholder will become the real WinGet publisher/package ID after release. | +| `winget install SpecLabs.Devspec` | Install the devspec CLI. | Installs the approved WinGet package after Microsoft accepts the package submission. | | `version` | Print the devspec CLI version. | Use this to confirm the command runs. It does not change files. | | `init` | Install devspec files. | Copies framework files into your repo. | | `--target .` | Target repo folder. | `.` means the folder your terminal is currently in. | @@ -139,7 +139,7 @@ devspec init --target D:\code\my-app --profile all --repo-state existing | Problem | What to try | | --- | --- | | `winget` is not found. | Use Windows App Installer or ask your IT team whether WinGet is disabled. | -| `.devspec` is not found. | The package ID may still be a placeholder. Use `uvx devspec ...` until the package is published. | +| `SpecLabs.Devspec` is not found. | The package may still be awaiting Microsoft approval. Use `uvx devspec ...` until the package is published. | | `devspec` is not found after install. | Close and reopen PowerShell so PATH changes reload. | | PowerShell blocks a command. | Ask your team about execution policy or approved package sources. | | Corporate software policy blocks WinGet. | Use [uv and uvx](uv.md) if allowed, or [manual copy](manual-copy.md). | diff --git a/packaging/winget/devspec.yaml b/packaging/winget/devspec.yaml index f32f4d8..8dbc42f 100644 --- a/packaging/winget/devspec.yaml +++ b/packaging/winget/devspec.yaml @@ -1,14 +1,14 @@ -PackageIdentifier: OWNER.devspec +PackageIdentifier: SpecLabs.Devspec PackageVersion: 0.1.0 PackageLocale: en-US -Publisher: devspec contributors +Publisher: SpecLabs PackageName: devspec License: Apache-2.0 ShortDescription: Installer and synchronizer CLI for the devspec workflow framework. Installers: - Architecture: x64 InstallerType: portable - InstallerUrl: https://github.com/OWNER/devspec/releases/download/v0.1.0/devspec.exe + InstallerUrl: https://github.com/speclabs/devspec/releases/download/v0.1.0/devspec.exe InstallerSha256: REPLACE_WITH_RELEASE_SHA256 ManifestType: singleton ManifestVersion: 1.6.0