Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
43 changes: 8 additions & 35 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
70 changes: 70 additions & 0 deletions packaging/winget/New-WinGetManifest.ps1
Original file line number Diff line number Diff line change
@@ -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"
34 changes: 34 additions & 0 deletions packaging/winget/README.md
Original file line number Diff line number Diff line change
@@ -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.
14 changes: 9 additions & 5 deletions packaging/winget/harder.SkillView.installer.yaml.tmpl
Original file line number Diff line number Diff line change
@@ -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
18 changes: 12 additions & 6 deletions packaging/winget/harder.SkillView.locale.en-US.yaml.tmpl
Original file line number Diff line number Diff line change
@@ -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
3 changes: 2 additions & 1 deletion packaging/winget/harder.SkillView.yaml.tmpl
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Loading