Skip to content

Commit 5c91c41

Browse files
authored
fix: wire Winget automation into release pipeline (#551)
## Summary - wire Winget submission into the stable release pipeline instead of relying on a separate `release.published` workflow - keep the existing release asset resolution and Komac-backed submission flow - document the new trigger model and manual fallback path ## Validation - `git diff --check origin/dev...HEAD` - `node --check scripts/winget/resolve-release-asset.cjs` - `node scripts/winget/resolve-release-asset.cjs --help` - live release metadata and asset-resolution dry-run against stable `v0.17.0` ## Notes - this fixes the case where a release created by GitHub Actions with the default `GITHUB_TOKEN` does not fan out into a second workflow run - assumes the existing Winget repo secret/variables remain configured - refs #462
1 parent 14dfd3e commit 5c91c41

3 files changed

Lines changed: 92 additions & 11 deletions

File tree

.github/workflows/reusable-release.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,18 @@ jobs:
9797
release_name: ${{ needs.prepare-release.outputs.release_name }}
9898
secrets: inherit
9999

100+
update-winget:
101+
needs:
102+
- prepare-release
103+
- build-and-upload
104+
if: ${{ !inputs.prerelease }}
105+
permissions:
106+
contents: read
107+
uses: ./.github/workflows/update-winget.yml
108+
with:
109+
release_tag: ${{ needs.prepare-release.outputs.tag }}
110+
secrets: inherit
111+
100112
release-ui:
101113
needs: prepare-release
102114
if: ${{ inputs.release_ui }}

.github/workflows/update-winget.yml

Lines changed: 73 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,83 @@
11
name: Update Winget
22

33
on:
4-
release:
5-
types:
6-
- published
4+
workflow_call:
5+
inputs:
6+
release_tag:
7+
description: "Stable release tag to inspect"
8+
required: true
9+
type: string
10+
release_id:
11+
description: "Optional numeric GitHub release id"
12+
required: false
13+
default: ""
14+
type: string
15+
workflow_dispatch:
16+
inputs:
17+
release_tag:
18+
description: "Stable release tag to inspect"
19+
required: true
20+
type: string
21+
release_id:
22+
description: "Optional numeric GitHub release id"
23+
required: false
24+
default: ""
25+
type: string
726

827
permissions:
928
contents: read
1029

1130
jobs:
31+
resolve-release:
32+
name: Resolve release metadata
33+
runs-on: ubuntu-latest
34+
outputs:
35+
release_id: ${{ steps.release.outputs.release_id }}
36+
release_tag: ${{ steps.release.outputs.release_tag }}
37+
draft: ${{ steps.release.outputs.draft }}
38+
prerelease: ${{ steps.release.outputs.prerelease }}
39+
steps:
40+
- name: Resolve release metadata
41+
id: release
42+
env:
43+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
44+
RELEASE_TAG: ${{ inputs.release_tag }}
45+
RELEASE_ID_INPUT: ${{ inputs.release_id }}
46+
run: |
47+
set -euo pipefail
48+
49+
if [ -n "$RELEASE_ID_INPUT" ]; then
50+
release_api="repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID_INPUT}"
51+
else
52+
release_api="repos/${GITHUB_REPOSITORY}/releases/tags/${RELEASE_TAG}"
53+
fi
54+
55+
release_id="$(gh api "$release_api" --jq '.id')"
56+
release_tag="$(gh api "$release_api" --jq '.tag_name')"
57+
draft="$(gh api "$release_api" --jq '.draft')"
58+
prerelease="$(gh api "$release_api" --jq '.prerelease')"
59+
60+
if [ -z "$release_id" ] || [ "$release_id" = "null" ]; then
61+
echo "Unable to resolve release id for tag '$RELEASE_TAG'" >&2
62+
exit 1
63+
fi
64+
65+
echo "release_id=$release_id" >> "$GITHUB_OUTPUT"
66+
echo "release_tag=$release_tag" >> "$GITHUB_OUTPUT"
67+
echo "draft=$draft" >> "$GITHUB_OUTPUT"
68+
echo "prerelease=$prerelease" >> "$GITHUB_OUTPUT"
69+
70+
- name: Log resolved release metadata
71+
run: |
72+
echo "Release tag: ${{ steps.release.outputs.release_tag }}"
73+
echo "Release id: ${{ steps.release.outputs.release_id }}"
74+
echo "Draft: ${{ steps.release.outputs.draft }}"
75+
echo "Prerelease: ${{ steps.release.outputs.prerelease }}"
76+
1277
update-winget:
1378
name: Submit Winget manifest update
14-
if: ${{ !github.event.release.draft && !github.event.release.prerelease }}
79+
needs: resolve-release
80+
if: ${{ needs.resolve-release.outputs.draft != 'true' && needs.resolve-release.outputs.prerelease != 'true' }}
1581
runs-on: ubuntu-latest
1682
env:
1783
WINGET_PACKAGE_IDENTIFIER: ${{ vars.WINGET_PACKAGE_IDENTIFIER || 'NeuralNomadsAI.CodeNomad' }}
@@ -35,8 +101,8 @@ jobs:
35101
run: |
36102
args=(
37103
--repo "${{ github.repository }}"
38-
--release-id "${{ github.event.release.id }}"
39-
--tag "${{ github.event.release.tag_name }}"
104+
--release-id "${{ needs.resolve-release.outputs.release_id }}"
105+
--tag "${{ needs.resolve-release.outputs.release_tag }}"
40106
--asset-name-template "$WINGET_WINDOWS_ASSET_NAME_TEMPLATE"
41107
--timeout-seconds "$WINGET_ASSET_WAIT_TIMEOUT_SECONDS"
42108
--poll-interval-seconds "$WINGET_ASSET_POLL_INTERVAL_SECONDS"
@@ -79,7 +145,7 @@ jobs:
79145
with:
80146
identifier: ${{ env.WINGET_PACKAGE_IDENTIFIER }}
81147
version: ${{ steps.release_asset.outputs.version }}
82-
release-tag: ${{ github.event.release.tag_name }}
148+
release-tag: ${{ needs.resolve-release.outputs.release_tag }}
83149
installers-regex: ${{ steps.release_asset.outputs.asset_regex }}
84150
fork-user: ${{ env.WINGET_FORK_OWNER }}
85151
token: ${{ secrets.WINGET_GITHUB_TOKEN }}

docs/guides/winget-release-automation.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
# Winget release automation
22

3-
CodeNomad publishes Winget updates from GitHub Releases with `.github/workflows/update-winget.yml`.
3+
CodeNomad publishes Winget updates from the stable GitHub release pipeline. `.github/workflows/reusable-release.yml` now calls `.github/workflows/update-winget.yml` after the release assets finish uploading.
44

55
## Trigger
66

7-
- Runs on `release.published`.
8-
- Exits early for draft or prerelease releases.
7+
- Runs as a reusable workflow from the stable release pipeline, after `build-and-upload` completes.
8+
- Resolves the target release by tag through the GitHub API, then exits early for draft or prerelease releases.
9+
- Can also be rerun manually with `workflow_dispatch` by supplying the stable release tag (and optionally the numeric release id).
10+
- This avoids the old `release.published` trap where a release created by GitHub Actions with the default `GITHUB_TOKEN` does not fan out into a second workflow run.
911
- Waits for the expected stable Windows Tauri asset because the release record can exist before all assets finish uploading.
1012

1113
## Required configuration
@@ -28,7 +30,7 @@ CodeNomad publishes Winget updates from GitHub Releases with `.github/workflows/
2830

2931
## Runtime flow
3032

31-
1. Resolve the release version from `github.event.release.tag_name`.
33+
1. Resolve the target release by tag through the GitHub API, then derive the package version from the resolved release tag.
3234
2. Poll the release API until exactly one uploaded asset matches the configured Windows Tauri asset template.
3335
3. Download the matched asset once and compute a SHA-256 for logging and verification.
3436
4. Verify the PAT owner matches `WINGET_FORK_OWNER` and that `${WINGET_FORK_OWNER}/winget-pkgs` is a fork of `microsoft/winget-pkgs`.
@@ -38,3 +40,4 @@ CodeNomad publishes Winget updates from GitHub Releases with `.github/workflows/
3840

3941
- The workflow does not depend on a persistent local `winget-pkgs` clone.
4042
- The current package in `winget-pkgs` uses the release ZIP with a nested NSIS installer, so the workflow targets the stable `CodeNomad-Tauri-windows-x64-{version}.zip` asset.
43+
- If a maintainer publishes a release outside the standard release workflow, they should manually run `Update Winget` for that stable tag.

0 commit comments

Comments
 (0)