From 8d683cfa666ee526b3f23b996849189d4880495e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pascal=20Andr=C3=A9?= Date: Mon, 24 Aug 2026 12:09:41 +0200 Subject: [PATCH 1/2] fix(release): preserve release PR notes and validate WinGet access Prepend the merged stable release pull request body to GitHub's generated notes so user-facing highlights are present from the moment a release is created. Prerelease behavior remains unchanged. Validate that the configured WinGet token can create and delete a temporary branch in the contributor fork before invoking Komac. This catches the exact permission failure that blocked v0.19.0 and documents supported writable token types. Validated with actionlint, a live associated-PR lookup for the v0.19.0 merge commit, a temporary fork branch create/delete check, and winget manifest validation for the manual 0.19.0 submission. --- .github/workflows/reusable-release.yml | 12 ++++++++++-- .github/workflows/update-winget.yml | 10 +++++++++- docs/guides/winget-release-automation.md | 4 ++-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/.github/workflows/reusable-release.yml b/.github/workflows/reusable-release.yml index 59b1b0583..d8286eb8e 100644 --- a/.github/workflows/reusable-release.yml +++ b/.github/workflows/reusable-release.yml @@ -80,11 +80,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 From acfe5e12cea0c2633ec494410b1dfa799d9fb098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pascal=20Andr=C3=A9?= Date: Mon, 24 Aug 2026 12:42:52 +0200 Subject: [PATCH 2/2] test(ci): serialize Windows Tauri tests Run the Windows Rust test binary with one test thread so process-election tests do not compete for PowerShell/CIM process identity probes on slower GitHub runners. The Electron and Tauri startup scenario remains concurrent inside its integration test; only unrelated Rust tests stop running beside it. Validated with actionlint and cargo test --locked -- --test-threads=1 on Windows (86 passed). --- .github/workflows/pr-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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