diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 7db6a74..3094495 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -43,7 +43,9 @@ failure issue. See `docs/runbooks/release-rollback.md` for recovery. Stable-tag Homebrew and WinGet jobs are opt-in via `HOMEBREW_TAP_ENABLED` and `WINGET_ENABLED`. They download **published** release assets and generate formula/manifests as workflow artifacts. They do not push to a tap or submit -to WinGet. `HOMEBREW_TAP_REPO` records the intended tap name. +to WinGet. The WinGet job verifies all Windows release checksums and renders a +copy-ready manifest tree; see `packaging/winget/README.md`. `HOMEBREW_TAP_REPO` +records the intended tap name. Manual `workflow_dispatch` exercises release packaging without publishing a GitHub Release. Keep the two product publishes sequential: they share core diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 53955b3..fe6a930 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -345,49 +345,22 @@ jobs: gh release download $env:RELEASE_REF --repo $env:RELEASE_REPO --pattern '*windows*' --dir artifacts if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } - - name: Verify published Windows checksums + - name: Verify release assets and generate submission manifests shell: pwsh run: | - Get-ChildItem artifacts/SHA256SUMS-windows-*.txt | ForEach-Object { - Get-Content $_.FullName | ForEach-Object { - $parts = $_ -split '\s+', 2 - $name = $parts[1].TrimStart('*') - $actual = (Get-FileHash (Join-Path artifacts $name) -Algorithm SHA256).Hash - if ($actual -ne $parts[0]) { throw "Checksum mismatch: $name" } - } - } - - - name: Generate manifests - shell: pwsh - run: | - New-Item -ItemType Directory -Force -Path generated/winget | Out-Null - $version = $env:RELEASE_REF.Substring(1) - $urlX64 = "https://github.com/$env:RELEASE_REPO/releases/download/$env:RELEASE_REF/skillview-windows-amd64.exe" - $urlArm64 = "https://github.com/$env:RELEASE_REPO/releases/download/$env:RELEASE_REF/skillview-windows-arm64.exe" - $shaX64 = (Get-FileHash artifacts/skillview-windows-amd64.exe -Algorithm SHA256).Hash - $shaArm64 = (Get-FileHash artifacts/skillview-windows-arm64.exe -Algorithm SHA256).Hash - - (Get-Content packaging/winget/harder.SkillView.yaml.tmpl -Raw).Replace('{{VERSION}}', $version) | - Set-Content generated/winget/$env:WINGET_PACKAGE_ID.yaml - - (Get-Content packaging/winget/harder.SkillView.locale.en-US.yaml.tmpl -Raw).Replace('{{VERSION}}', $version) | - Set-Content generated/winget/$env:WINGET_PACKAGE_ID.locale.en-US.yaml - - ((Get-Content packaging/winget/harder.SkillView.installer.yaml.tmpl -Raw). - Replace('{{VERSION}}', $version). - Replace('{{URL_WIN_X64}}', $urlX64). - Replace('{{SHA_WIN_X64}}', $shaX64). - Replace('{{URL_WIN_ARM64}}', $urlArm64). - Replace('{{SHA_WIN_ARM64}}', $shaArm64)) | - Set-Content generated/winget/$env:WINGET_PACKAGE_ID.installer.yaml + ./packaging/winget/New-WinGetManifest.ps1 ` + -ReleaseRef $env:RELEASE_REF ` + -AssetsDir artifacts ` + -OutputDir generated/winget ` + -Repository $env:RELEASE_REPO - Write-Host "Dark-launch only: generated WinGet manifests for $env:WINGET_PACKAGE_ID $version" + Write-Host "Dark-launch only: generated WinGet manifests for $env:WINGET_PACKAGE_ID $env:RELEASE_REF" - name: Upload generated WinGet manifests uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: winget-manifests-${{ github.ref_name }} - path: generated/winget/* + path: generated/winget/manifests/ retention-days: 30 notify-failure: diff --git a/AGENTS.md b/AGENTS.md index c8f8c52..645635a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -448,6 +448,10 @@ the terminal, with both a full-screen TUI and scriptable CLI commands. release workflow only generates Homebrew / WinGet artifacts when the repo variables (`HOMEBREW_TAP_ENABLED`, `HOMEBREW_TAP_REPO`, `WINGET_ENABLED`) are explicitly enabled. It does not push to a tap repo or submit to WinGet yet. + `packaging/winget/New-WinGetManifest.ps1` verifies published Windows assets + against their checksums and renders copy-ready multi-file manifests. Keep + the standalone WinGet package portable, its command alias `skillview`, and + its `GitHub.cli >= 2.97.0` dependency in sync with the product contract. - Terminal.Gui `2.5.0` supports `Application.Create().Init()`. Keep using `TuiConfigurationBuilder` and check the documented lifecycle, `View.Text`, and `IAcceptTarget` API changes before a later package upgrade. diff --git a/packaging/winget/New-WinGetManifest.ps1 b/packaging/winget/New-WinGetManifest.ps1 new file mode 100644 index 0000000..76d3c6c --- /dev/null +++ b/packaging/winget/New-WinGetManifest.ps1 @@ -0,0 +1,70 @@ +param( + [Parameter(Mandatory = $true)][string]$ReleaseRef, + [Parameter(Mandatory = $true)][string]$AssetsDir, + [Parameter(Mandatory = $true)][string]$OutputDir, + [string]$Repository = 'harder/gh-skillview' +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +if ($ReleaseRef -cnotmatch '^v(\d+\.\d+\.\d+)$') { + throw 'WinGet manifests require a stable vMAJOR.MINOR.PATCH release tag.' +} +$version = $Matches[1] +if ($Repository -cnotmatch '^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$') { + throw 'Repository must be an owner/name pair.' +} + +$assets = (Resolve-Path -LiteralPath $AssetsDir).Path +$checksums = @{} +foreach ($architecture in @('amd64', 'arm64')) { + $checksumPath = Join-Path $assets "SHA256SUMS-windows-$architecture.txt" + foreach ($line in Get-Content -LiteralPath $checksumPath) { + if ($line -cnotmatch '^([0-9a-fA-F]{64})\s+\*?(.+)$') { + throw "Invalid checksum line in $checksumPath`: $line" + } + if ($checksums.ContainsKey($Matches[2])) { + throw "Duplicate release checksum for $($Matches[2])" + } + $checksums[$Matches[2]] = $Matches[1].ToUpperInvariant() + } +} + +$fileNames = @('skillview-windows-amd64.exe', 'skillview-windows-arm64.exe', + 'gh-skillview-windows-amd64.exe', 'gh-skillview-windows-arm64.exe') +foreach ($name in $fileNames) { + if (-not $checksums.ContainsKey($name)) { + throw "Missing release checksum for $name" + } + $path = Join-Path $assets $name + $actual = (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash + if ($actual -cne $checksums[$name]) { + throw "Release checksum mismatch for $name" + } +} + +$replacements = @{ + '{{VERSION}}' = $version + '{{URL_WIN_X64}}' = "https://github.com/$Repository/releases/download/$ReleaseRef/skillview-windows-amd64.exe" + '{{SHA_WIN_X64}}' = $checksums['skillview-windows-amd64.exe'] + '{{URL_WIN_ARM64}}' = "https://github.com/$Repository/releases/download/$ReleaseRef/skillview-windows-arm64.exe" + '{{SHA_WIN_ARM64}}' = $checksums['skillview-windows-arm64.exe'] +} + +$manifestDir = Join-Path $OutputDir "manifests/h/harder/SkillView/$version" +New-Item -ItemType Directory -Path $manifestDir -Force | Out-Null +$templateDir = $PSScriptRoot +foreach ($name in @('harder.SkillView.yaml', 'harder.SkillView.locale.en-US.yaml', 'harder.SkillView.installer.yaml')) { + $content = Get-Content -LiteralPath (Join-Path $templateDir "$name.tmpl") -Raw + foreach ($key in $replacements.Keys) { + $content = $content.Replace($key, $replacements[$key]) + } + if ($content -match '{{[^}]+}}') { + throw "Unresolved template value in $name" + } + [System.IO.File]::WriteAllText((Join-Path $manifestDir $name), $content, + [System.Text.UTF8Encoding]::new($false)) +} + +Write-Host "Generated WinGet manifest set: $manifestDir" diff --git a/packaging/winget/README.md b/packaging/winget/README.md new file mode 100644 index 0000000..42249cb --- /dev/null +++ b/packaging/winget/README.md @@ -0,0 +1,34 @@ +# WinGet submission + +`harder.SkillView` packages the standalone Windows `skillview` command. It does +not register the `gh skillview` extension; install that separately with +`gh extension install harder/gh-skillview` if you prefer the extension entrypoint. + +The [1.0.0 manifest set](submission/manifests/h/harder/SkillView/1.0.0/) is +ready to copy to `manifests/h/harder/SkillView/1.0.0/` in +[`microsoft/winget-pkgs`](https://github.com/microsoft/winget-pkgs). It uses +the published Windows x64 and ARM64 executables, checksums from the release, +`InstallerType: portable`, the `skillview` command alias, and a `GitHub.cli` +dependency with the minimum supported version. + +To regenerate a manifest set from a future stable release: + +```powershell +gh release download v1.0.0 --repo harder/gh-skillview --pattern '*windows*' --dir artifacts +./packaging/winget/New-WinGetManifest.ps1 -ReleaseRef v1.0.0 -AssetsDir artifacts -OutputDir generated/winget +winget validate --manifest generated/winget/manifests/h/harder/SkillView/1.0.0 +``` + +The script verifies all four Windows binaries against the published checksum +files before rendering the three version-specific manifests. Replace `v1.0.0` +with the release tag when preparing a newer version. The opt-in release job +performs the same generation and uploads the manifests as an artifact when +`WINGET_ENABLED` is `true`; it does not submit a PR upstream. + +Before submission, install the manifest in Windows Sandbox or a clean Windows +environment, confirm `skillview --version` and `skillview --help`, then +uninstall it. Local manifest installation requires an administrator to enable +`LocalManifestFiles` with `winget settings --enable LocalManifestFiles`. +Restore the previous setting with `winget settings --disable LocalManifestFiles` +afterward if it was disabled before testing. The community repository accepts +one version and only manifest files per PR. diff --git a/packaging/winget/harder.SkillView.installer.yaml.tmpl b/packaging/winget/harder.SkillView.installer.yaml.tmpl index a4c0686..8f17120 100644 --- a/packaging/winget/harder.SkillView.installer.yaml.tmpl +++ b/packaging/winget/harder.SkillView.installer.yaml.tmpl @@ -1,15 +1,19 @@ +# yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.12.0.schema.json PackageIdentifier: harder.SkillView PackageVersion: {{VERSION}} +InstallerType: portable +Commands: + - skillview +Dependencies: + PackageDependencies: + - PackageIdentifier: GitHub.cli + MinimumVersion: 2.97.0 Installers: - Architecture: x64 - InstallerType: exe InstallerUrl: {{URL_WIN_X64}} InstallerSha256: {{SHA_WIN_X64}} - InstallerLocale: en-US - Architecture: arm64 - InstallerType: exe InstallerUrl: {{URL_WIN_ARM64}} InstallerSha256: {{SHA_WIN_ARM64}} - InstallerLocale: en-US ManifestType: installer -ManifestVersion: 1.9.0 +ManifestVersion: 1.12.0 diff --git a/packaging/winget/harder.SkillView.locale.en-US.yaml.tmpl b/packaging/winget/harder.SkillView.locale.en-US.yaml.tmpl index bad5d76..9c1d96b 100644 --- a/packaging/winget/harder.SkillView.locale.en-US.yaml.tmpl +++ b/packaging/winget/harder.SkillView.locale.en-US.yaml.tmpl @@ -1,19 +1,25 @@ +# yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.1.12.0.schema.json PackageIdentifier: harder.SkillView PackageVersion: {{VERSION}} PackageLocale: en-US -Publisher: harder -PublisherUrl: https://github.com/harder +Publisher: Kevin Harder +PublisherUrl: https://kevinharder.com/ PublisherSupportUrl: https://github.com/harder/gh-skillview/issues +Author: Kevin Harder PackageName: SkillView -PackageUrl: https://github.com/harder/gh-skillview -ShortDescription: Terminal UI and CLI for browsing and managing gh skill skills +PackageUrl: https://skillview.dev/ +ShortDescription: Terminal UI and CLI for browsing and managing GitHub CLI skills +Description: |- + SkillView helps you find, preview, install, update, and safely remove agent + skills. Use its full-screen terminal app or scriptable CLI on top of gh skill. Moniker: skillview License: MIT -LicenseUrl: https://github.com/harder/gh-skillview/blob/main/LICENSE +LicenseUrl: https://github.com/harder/gh-skillview/blob/v{{VERSION}}/LICENSE +ReleaseNotesUrl: https://github.com/harder/gh-skillview/releases/tag/v{{VERSION}} Tags: - github - gh - skills - terminal ManifestType: defaultLocale -ManifestVersion: 1.9.0 +ManifestVersion: 1.12.0 diff --git a/packaging/winget/harder.SkillView.yaml.tmpl b/packaging/winget/harder.SkillView.yaml.tmpl index 90df22c..43e5e5a 100644 --- a/packaging/winget/harder.SkillView.yaml.tmpl +++ b/packaging/winget/harder.SkillView.yaml.tmpl @@ -1,5 +1,6 @@ +# yaml-language-server: $schema=https://aka.ms/winget-manifest.version.1.12.0.schema.json PackageIdentifier: harder.SkillView PackageVersion: {{VERSION}} DefaultLocale: en-US ManifestType: version -ManifestVersion: 1.9.0 +ManifestVersion: 1.12.0 diff --git a/packaging/winget/submission/manifests/h/harder/SkillView/1.0.0/harder.SkillView.installer.yaml b/packaging/winget/submission/manifests/h/harder/SkillView/1.0.0/harder.SkillView.installer.yaml new file mode 100644 index 0000000..688cafa --- /dev/null +++ b/packaging/winget/submission/manifests/h/harder/SkillView/1.0.0/harder.SkillView.installer.yaml @@ -0,0 +1,19 @@ +# yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.12.0.schema.json +PackageIdentifier: harder.SkillView +PackageVersion: 1.0.0 +InstallerType: portable +Commands: + - skillview +Dependencies: + PackageDependencies: + - PackageIdentifier: GitHub.cli + MinimumVersion: 2.97.0 +Installers: + - Architecture: x64 + InstallerUrl: https://github.com/harder/gh-skillview/releases/download/v1.0.0/skillview-windows-amd64.exe + InstallerSha256: 34AD08714981F056164946AC01944A9409A1BAD9EF68D8449701F1CC7EEBBD50 + - Architecture: arm64 + InstallerUrl: https://github.com/harder/gh-skillview/releases/download/v1.0.0/skillview-windows-arm64.exe + InstallerSha256: 80E77253498ECE2662738ED746961C435A856EDA0D15FF84DAA9E3E4869CAFAC +ManifestType: installer +ManifestVersion: 1.12.0 diff --git a/packaging/winget/submission/manifests/h/harder/SkillView/1.0.0/harder.SkillView.locale.en-US.yaml b/packaging/winget/submission/manifests/h/harder/SkillView/1.0.0/harder.SkillView.locale.en-US.yaml new file mode 100644 index 0000000..2f1b618 --- /dev/null +++ b/packaging/winget/submission/manifests/h/harder/SkillView/1.0.0/harder.SkillView.locale.en-US.yaml @@ -0,0 +1,25 @@ +# yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.1.12.0.schema.json +PackageIdentifier: harder.SkillView +PackageVersion: 1.0.0 +PackageLocale: en-US +Publisher: Kevin Harder +PublisherUrl: https://kevinharder.com/ +PublisherSupportUrl: https://github.com/harder/gh-skillview/issues +Author: Kevin Harder +PackageName: SkillView +PackageUrl: https://skillview.dev/ +ShortDescription: Terminal UI and CLI for browsing and managing GitHub CLI skills +Description: |- + SkillView helps you find, preview, install, update, and safely remove agent + skills. Use its full-screen terminal app or scriptable CLI on top of gh skill. +Moniker: skillview +License: MIT +LicenseUrl: https://github.com/harder/gh-skillview/blob/v1.0.0/LICENSE +ReleaseNotesUrl: https://github.com/harder/gh-skillview/releases/tag/v1.0.0 +Tags: + - github + - gh + - skills + - terminal +ManifestType: defaultLocale +ManifestVersion: 1.12.0 diff --git a/packaging/winget/submission/manifests/h/harder/SkillView/1.0.0/harder.SkillView.yaml b/packaging/winget/submission/manifests/h/harder/SkillView/1.0.0/harder.SkillView.yaml new file mode 100644 index 0000000..8fc48a3 --- /dev/null +++ b/packaging/winget/submission/manifests/h/harder/SkillView/1.0.0/harder.SkillView.yaml @@ -0,0 +1,6 @@ +# yaml-language-server: $schema=https://aka.ms/winget-manifest.version.1.12.0.schema.json +PackageIdentifier: harder.SkillView +PackageVersion: 1.0.0 +DefaultLocale: en-US +ManifestType: version +ManifestVersion: 1.12.0