From e4cd6d6fcb75b7aab6e2c86b1d54ea507760f9f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pascal=20Andr=C3=A9?= Date: Sat, 29 Aug 2026 17:02:35 +0200 Subject: [PATCH] fix(release): preserve PR notes and validate WinGet access (#658) ## Summary - prepend the merged stable release PR body to GitHub-generated release notes - keep nightly prerelease notes unchanged - verify the WinGet token can create and delete branches in the configured fork before invoking Komac - document writable PAT and GitHub CLI OAuth token options ## Root cause The v0.19.0 WinGet job reached Komac successfully, but `WINGET_GITHUB_TOKEN` could not execute `CreateRef` in `pascalandr/winget-pkgs`. The repository secret has been replaced with the currently authenticated writable GitHub CLI token. ## Validation - `actionlint .github/workflows/reusable-release.yml .github/workflows/update-winget.yml` - associated-PR lookup returned the #656 body for merge commit `db0716e7` - temporary branch create/delete succeeded in `pascalandr/winget-pkgs` - `winget validate` succeeded for the v0.19.0 manifests Manual WinGet submission: https://github.com/microsoft/winget-pkgs/pull/423225 --- .github/workflows/pr-build.yml | 2 +- .github/workflows/reusable-release.yml | 12 ++++++++++-- .github/workflows/update-winget.yml | 10 +++++++++- docs/guides/winget-release-automation.md | 4 ++-- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index c73a8f1a5..835b23bab 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -184,4 +184,4 @@ jobs: - name: Test Tauri crate on Windows working-directory: packages/tauri-app/src-tauri - run: cargo test --locked + run: cargo test --locked -- --test-threads=1 diff --git a/.github/workflows/reusable-release.yml b/.github/workflows/reusable-release.yml index 69a8114fd..489e5218b 100644 --- a/.github/workflows/reusable-release.yml +++ b/.github/workflows/reusable-release.yml @@ -86,11 +86,19 @@ jobs: if gh release view "$TAG" >/dev/null 2>&1; then echo "Release $TAG already exists" else + args=(--title "$TAG" --generate-notes) + if [ "${IS_PRERELEASE}" = "true" ]; then - gh release create "$TAG" --title "$TAG" --generate-notes --prerelease + args+=(--prerelease) else - gh release create "$TAG" --title "$TAG" --generate-notes + pr_body="$(gh api "repos/${GITHUB_REPOSITORY}/commits/${GITHUB_SHA}/pulls" \ + --jq 'map(select(.merged_at != null and .base.ref == "main")) | sort_by(.merged_at) | last | .body // ""')" + if [ -n "$pr_body" ]; then + args+=(--notes "$pr_body") + fi fi + + gh release create "$TAG" "${args[@]}" fi build-and-upload: diff --git a/.github/workflows/update-winget.yml b/.github/workflows/update-winget.yml index fd9a656f3..0fb40207c 100644 --- a/.github/workflows/update-winget.yml +++ b/.github/workflows/update-winget.yml @@ -117,7 +117,7 @@ jobs: echo "Resolved version: ${{ steps.release_asset.outputs.version }}" echo "Resolved SHA-256: ${{ steps.release_asset.outputs.asset_sha256 }}" - - name: Validate fork configuration + - name: Validate fork write access env: GH_TOKEN: ${{ secrets.WINGET_GITHUB_TOKEN }} EXPECTED_OWNER: ${{ env.WINGET_FORK_OWNER }} @@ -138,6 +138,14 @@ jobs: exit 1 fi + default_branch="$(gh api "repos/$fork_name" --jq '.default_branch')" + default_sha="$(gh api "repos/$fork_name/git/ref/heads/$default_branch" --jq '.object.sha')" + test_branch="codenomad-winget-token-check-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" + gh api --method POST "repos/$fork_name/git/refs" \ + -f ref="refs/heads/$test_branch" \ + -f sha="$default_sha" >/dev/null + gh api --method DELETE "repos/$fork_name/git/refs/heads/$test_branch" >/dev/null + echo "Validated fork: $fork_name" - name: Submit update to Winget diff --git a/docs/guides/winget-release-automation.md b/docs/guides/winget-release-automation.md index b61639c03..3ca27f161 100644 --- a/docs/guides/winget-release-automation.md +++ b/docs/guides/winget-release-automation.md @@ -14,7 +14,7 @@ CodeNomad publishes Winget updates from the stable GitHub release pipeline. `.gi ### Repository secret -- `WINGET_GITHUB_TOKEN`: Classic GitHub PAT with `public_repo` scope. +- `WINGET_GITHUB_TOKEN`: GitHub token with write access to the configured fork (for example, a classic PAT with `public_repo` scope or a GitHub CLI OAuth token with `repo` scope). - The token owner must own the fork that submits to `microsoft/winget-pkgs`. - Komac-based submission cannot open the PR with a fine-grained token today. @@ -33,7 +33,7 @@ CodeNomad publishes Winget updates from the stable GitHub release pipeline. `.gi 1. Resolve the target release by tag through the GitHub API, then derive the package version from the resolved release tag. 2. Poll the release API until exactly one uploaded asset matches the configured Windows Tauri asset template. 3. Download the matched asset once and compute a SHA-256 for logging and verification. -4. Verify the PAT owner matches `WINGET_FORK_OWNER` and that `${WINGET_FORK_OWNER}/winget-pkgs` is a fork of `microsoft/winget-pkgs`. +4. Verify the token owner matches `WINGET_FORK_OWNER`, that `${WINGET_FORK_OWNER}/winget-pkgs` is a fork of `microsoft/winget-pkgs`, and that the token can create and delete a temporary branch. 5. Invoke `vedantmgoyal9/winget-releaser@v2`, which uses Komac under the hood to update the existing `NeuralNomadsAI.CodeNomad` manifest and open the PR. ## Notes