From 264b0d9be0635343feade24ba5ca0641d92e0a8b Mon Sep 17 00:00:00 2001 From: Allan Thraen Date: Sun, 6 Sep 2026 15:12:32 +0200 Subject: [PATCH] ci(winget): stop swallowing the fork-sync failure, and record the real cause MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sync step was `continue-on-error: true`. The reasoning at the time was "a sync hiccup must not block a submit that might still succeed" — but a stale fork is the one thing that guarantees the submit will NOT succeed, so the flag only ever hid the honest error and left the misleading one. That is why the same failure was rediagnosed from scratch three releases running. The full chain, verified this time: WINGET_TOKEN lacks `workflow` scope -> merge-upstream returns HTTP 422, because upstream winget-pkgs contains .github/workflows/*.yml and syncing means writing them -> the fork stays stale -> the SUBMIT step reports "does not have the correct permissions to execute CreateRef" Nothing in that final message mentions the sync, the fork, or the scope. So: drop continue-on-error, and correct the docs. CLAUDE.md said "`public_repo` is sufficient" and "this is almost never a token problem" — both written in good faith after v0.6.0, both wrong, and both actively steered the v0.7.0 diagnosis away from the answer. It IS partly a token problem; it just isn't the scope anyone reaches for first. Widening to full `repo` remains the wrong fix and that warning stays. No release behaviour changes for a correctly-scoped token. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01NDsEfog5kVkT5NmX1Ya5be --- .github/workflows/winget.yml | 38 +++++++++++++++++++++++++----------- CLAUDE.md | 15 ++++++++------ 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml index 632cdaa..91796df 100644 --- a/.github/workflows/winget.yml +++ b/.github/workflows/winget.yml @@ -13,10 +13,16 @@ name: Publish to WinGet # To make the automatic trigger real, CI / Release would need to create the Release # with a PAT instead of GITHUB_TOKEN. Left as-is deliberately — see CLAUDE.md. # -# Requires repo secret WINGET_TOKEN: a classic PAT with `public_repo` scope -# (winget-releaser does not support fine-grained tokens). `public_repo` IS enough — -# if you hit a permissions-looking error, read the sync step below before touching -# the token, because that error is almost never about the token. +# Requires repo secret WINGET_TOKEN: a classic PAT (winget-releaser does not support +# fine-grained tokens) with BOTH scopes: +# +# public_repo — submit the manifest PR to microsoft/winget-pkgs +# workflow — sync our fork, which copies upstream's .github/workflows/*.yml +# +# The `workflow` scope is the non-obvious one and its absence does NOT look like a +# scope problem: the sync fails with HTTP 422, the fork goes stale, and the SUBMIT +# step then reports "does not have the correct permissions to execute CreateRef". +# See the sync step below for the full chain. on: release: @@ -40,21 +46,31 @@ jobs: # dozens of commits a day, so a fork left alone between releases falls far # enough behind that GitHub rejects the branch creation. # - # The failure is badly disguised — it surfaces as + # The failure is badly disguised — it surfaces from the SUBMIT step as # " does not have the correct permissions to execute `CreateRef`" # "failed to create branch UmageAI.CodeShellManager--" - # which reads like a token-scope problem and is not one. v0.6.0 burned three - # regenerated PATs (including needlessly widening the scope to full `repo`) - # before the actual cause turned up. See komac#376 and winget-releaser#336. + # which reads like a token problem. Three releases were spent rotating and + # widening PATs before the real chain turned up: + # + # WINGET_TOKEN lacks `workflow` scope + # -> merge-upstream refuses with HTTP 422, because upstream winget-pkgs + # contains .github/workflows/*.yml and syncing means writing them + # -> the fork stays stale + # -> CreateRef is rejected + # + # So WINGET_TOKEN must be a classic PAT with BOTH `public_repo` AND `workflow`. + # See komac#376 and winget-releaser#336 for the symptom; the scope is ours. + # + # NOT continue-on-error, deliberately. It was — and that is precisely why this + # masqueraded as a token problem for three releases: the sync failed silently + # and only the misleading downstream error was ever seen. A failed sync means + # the submit is about to fail anyway, so fail here where the message is honest. # # NOTE the fork komac uses is the one under the SAME OWNER as this repo — # umage-ai/winget-pkgs — not a maintainer's personal fork. A personal fork of # winget-pkgs may also exist; syncing that one does nothing. Override with the # `fork-user` input if this ever needs to point elsewhere. - # continue-on-error: a sync hiccup must not block a submit that might still - # succeed, and if the submit then fails we want its real error, not this one. - name: Sync umage-ai/winget-pkgs with upstream - continue-on-error: true env: GH_TOKEN: ${{ secrets.WINGET_TOKEN }} run: | diff --git a/CLAUDE.md b/CLAUDE.md index 8094e79..100008c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -428,9 +428,9 @@ gh workflow run chocolatey.yml -f tag=vX.Y.Z To make it genuinely automatic, CI / Release would have to create the Release with a PAT rather than `GITHUB_TOKEN`. -### winget: the `CreateRef` error is not about the token +### winget: the `CreateRef` error names the wrong culprit -`winget.yml` submits the signed MSI to microsoft/winget-pkgs as `UmageAI.CodeShellManager` via [winget-releaser](https://github.com/vedantmgoyal9/winget-releaser). Needs `WINGET_TOKEN` — a **classic** PAT with `public_repo` (fine-grained tokens are unsupported). +`winget.yml` submits the signed MSI to microsoft/winget-pkgs as `UmageAI.CodeShellManager` via [winget-releaser](https://github.com/vedantmgoyal9/winget-releaser). Needs `WINGET_TOKEN` — a **classic** PAT (fine-grained tokens are unsupported) with **both** `public_repo` and `workflow`. When it fails you will see: @@ -439,14 +439,17 @@ When it fails you will see: 1: failed to create branch UmageAI.CodeShellManager-- ``` -**This is almost never a token problem.** It means our fork of winget-pkgs is too far behind upstream for GitHub to accept the new branch. Upstream lands dozens of commits a day, so a fork untouched since the last release is always stale by the next one. +**The message points at the wrong thing.** Two distinct causes produce it, and the token itself is the *second* one, not the first: -Two traps that cost real time on v0.6.0: +1. **The fork is stale.** komac creates its branch in `umage-ai/winget-pkgs`; upstream lands dozens of commits a day, so a fork untouched since the last release is always too far behind for GitHub to accept a new branch. +2. **`WINGET_TOKEN` is missing the `workflow` scope**, so the automatic sync that would have fixed (1) *cannot* run — `merge-upstream` returns HTTP 422 because upstream winget-pkgs contains `.github/workflows/*.yml` and syncing means writing them. The fork stays stale and you land back at (1). + +Two traps that cost real time across v0.6.0 and v0.7.0: - **Sync the fork under the org, `umage-ai/winget-pkgs`** — komac uses the fork owned by the same account as this repo. A maintainer's *personal* fork (`AThraen/winget-pkgs`) may also exist and is a red herring; syncing it changes nothing. -- **Don't widen the token scope.** `public_repo` is sufficient. winget-releaser's README once carried advice to use full `repo`; the PR proposing it was closed unmerged. Broadening the scope does not fix this and hands CI write access to every private repo the owner can reach. +- **`public_repo` alone is not enough — the token also needs `workflow`.** This was recorded backwards here through v0.6.0 ("`public_repo` is sufficient"), which is why the same failure was rediagnosed three releases running. It is still true that widening to *full* `repo` is wrong and does not help: that grants CI write access to every private repo the owner can reach. `public_repo` + `workflow`, nothing more. -`winget.yml` now syncs the org fork automatically before submitting, so this should not recur. If it does, sync manually and re-dispatch: +`winget.yml` syncs the org fork automatically before submitting, and that step is deliberately **not** `continue-on-error` — it used to be, which is exactly how a failing sync stayed invisible and only the misleading `CreateRef` error was ever seen. If the sync fails, fix the token scope; to unblock a release in the meantime, sync by hand and re-dispatch: ```bash gh api -X POST repos/umage-ai/winget-pkgs/merge-upstream -f branch=master