From 47a1b742ea3d2b038e644fbc06c81d600f0d19c3 Mon Sep 17 00:00:00 2001 From: Copilot App <223556219+Copilot@users.noreply.github.com> Date: Sat, 3 Oct 2026 10:36:06 +0300 Subject: [PATCH 1/2] fix(release): replace broken external-repo checkout auth for Homebrew/WinGet publish The publish-homebrew and publish-winget jobs used actions/checkout with a custom classic PAT against external repositories (bradygaster/homebrew-squad, microsoft/winget-pkgs, tamirdresher/winget-pkgs). This failed deterministically in the v1.0.0 release run (reproduced on rerun) with: fatal: could not read Username for 'https://github.com': terminal prompts disabled Root cause not fully isolated (token validity could not be checked from outside Actions), so this change removes the dependency on actions/checkout's token/includeIf mechanism entirely rather than guessing around it: - All three target repos are public, so the initial read/clone never actually needs authentication - switched to plain anonymous 'git clone'. - Push credentials (needed only for the later 'git push'/gh pr create steps) are now set via a local, non-global 'git config --local http..extraheader' scoped to just that one clone directory - never embedded in a remote URL, never printed, and cleaned up automatically when the job's workspace is discarded. - Added an explicit preflight step to each job that validates the relevant token via 'gh api user' and checks '.permissions.push' on the target repo before attempting any clone/push, so an actually-invalid/expired PAT fails fast with a clear, actionable error instead of surfacing as a confusing low-level git auth error. No changes to job graph, secrets, or permissions scopes. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../workflows/squad-standalone-release.yml | 112 ++++++++++++------ 1 file changed, 74 insertions(+), 38 deletions(-) diff --git a/.github/workflows/squad-standalone-release.yml b/.github/workflows/squad-standalone-release.yml index 1a4582e4d..0d74d0f03 100644 --- a/.github/workflows/squad-standalone-release.yml +++ b/.github/workflows/squad-standalone-release.yml @@ -429,19 +429,36 @@ jobs: exit 1 fi + - name: Preflight - verify Homebrew tap token + env: + GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} + run: | + set -euo pipefail + login="$(gh api user --jq '.login')" || { + echo "::error::HOMEBREW_TAP_TOKEN failed to authenticate against the GitHub API. The token is likely invalid, expired, or revoked and must be rotated by a maintainer with write access to bradygaster/homebrew-squad." + exit 1 + } + echo "Authenticated as: ${login}" + push_access="$(gh api repos/bradygaster/homebrew-squad --jq '.permissions.push // false')" + echo "Push access to bradygaster/homebrew-squad: ${push_access}" + if [ "${push_access}" != "true" ]; then + echo "::error::HOMEBREW_TAP_TOKEN authenticates as '${login}', but that account/token does not have push access to bradygaster/homebrew-squad." + exit 1 + fi + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c #v8 with: name: packaging-manifests path: packaging - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7 - with: - repository: bradygaster/homebrew-squad - ref: main - path: homebrew-tap - fetch-depth: 1 - token: ${{ secrets.HOMEBREW_TAP_TOKEN }} - persist-credentials: true + - name: Clone Homebrew tap (read is anonymous; credential is scoped locally for the later push only) + env: + TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} + run: | + set -euo pipefail + git clone --depth 1 --branch main https://github.com/bradygaster/homebrew-squad.git homebrew-tap + auth_header="Authorization: basic $(printf 'x-access-token:%s' "${TOKEN}" | base64 -w0)" + git -C homebrew-tap config --local http.https://github.com/.extraheader "${auth_header}" - name: Publish cask to the tap env: @@ -571,6 +588,23 @@ jobs: exit 1 fi + - name: Preflight - verify WinGet publish token + env: + GH_TOKEN: ${{ secrets.WINGET_CREATE_GITHUB_TOKEN }} + run: | + set -euo pipefail + login="$(gh api user --jq '.login')" || { + echo "::error::WINGET_CREATE_GITHUB_TOKEN failed to authenticate against the GitHub API. The token is likely invalid, expired, or revoked and must be rotated by a maintainer with push access to tamirdresher/winget-pkgs." + exit 1 + } + echo "Authenticated as: ${login}" + push_access="$(gh api repos/tamirdresher/winget-pkgs --jq '.permissions.push // false')" + echo "Push access to tamirdresher/winget-pkgs: ${push_access}" + if [ "${push_access}" != "true" ]; then + echo "::error::WINGET_CREATE_GITHUB_TOKEN authenticates as '${login}', but that account/token does not have push access to tamirdresher/winget-pkgs." + exit 1 + fi + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c #v8 with: name: packaging-manifests @@ -609,15 +643,14 @@ jobs: echo "package_identifier=${PACKAGE_IDENTIFIER}" >> "${GITHUB_OUTPUT}" echo "package_root=${package_root}" >> "${GITHUB_OUTPUT}" - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7 - with: - repository: microsoft/winget-pkgs - ref: master - path: winget-base - fetch-depth: 1 - sparse-checkout: ${{ steps.release.outputs.package_root }} - token: ${{ secrets.WINGET_CREATE_GITHUB_TOKEN }} - persist-credentials: false + - name: Clone WinGet manifests directory (read-only, public repo; no credential needed) + env: + PACKAGE_ROOT: ${{ steps.release.outputs.package_root }} + run: | + set -euo pipefail + git clone --no-checkout --depth 1 --branch master --filter=blob:none https://github.com/microsoft/winget-pkgs.git winget-base + git -C winget-base sparse-checkout set "${PACKAGE_ROOT}" + git -C winget-base checkout master - name: Check WinGet publication state id: guard @@ -702,29 +735,32 @@ jobs: echo "skip=false" >> "${GITHUB_OUTPUT}" echo "branch_exists=${branch_exists}" >> "${GITHUB_OUTPUT}" - - name: Check out existing WinGet version branch + - name: Clone existing WinGet version branch (fork; credential scoped locally for the later push) if: steps.guard.outputs.skip == 'false' && steps.guard.outputs.branch_exists == 'true' - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7 - with: - repository: tamirdresher/winget-pkgs - ref: ${{ steps.release.outputs.branch }} - path: winget-pkgs - fetch-depth: 1 - sparse-checkout: ${{ steps.release.outputs.package_root }} - token: ${{ secrets.WINGET_CREATE_GITHUB_TOKEN }} - persist-credentials: true - - - name: Check out WinGet master for a new version branch + env: + TOKEN: ${{ secrets.WINGET_CREATE_GITHUB_TOKEN }} + BRANCH: ${{ steps.release.outputs.branch }} + PACKAGE_ROOT: ${{ steps.release.outputs.package_root }} + run: | + set -euo pipefail + git clone --no-checkout --depth 1 --branch "${BRANCH}" --filter=blob:none https://github.com/tamirdresher/winget-pkgs.git winget-pkgs + git -C winget-pkgs sparse-checkout set "${PACKAGE_ROOT}" + git -C winget-pkgs checkout "${BRANCH}" + auth_header="Authorization: basic $(printf 'x-access-token:%s' "${TOKEN}" | base64 -w0)" + git -C winget-pkgs config --local http.https://github.com/.extraheader "${auth_header}" + + - name: Clone WinGet master for a new version branch (credential scoped locally for the later push) if: steps.guard.outputs.skip == 'false' && steps.guard.outputs.branch_exists == 'false' - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7 - with: - repository: microsoft/winget-pkgs - ref: master - path: winget-pkgs - fetch-depth: 1 - sparse-checkout: ${{ steps.release.outputs.package_root }} - token: ${{ secrets.WINGET_CREATE_GITHUB_TOKEN }} - persist-credentials: true + env: + TOKEN: ${{ secrets.WINGET_CREATE_GITHUB_TOKEN }} + PACKAGE_ROOT: ${{ steps.release.outputs.package_root }} + run: | + set -euo pipefail + git clone --no-checkout --depth 1 --branch master --filter=blob:none https://github.com/microsoft/winget-pkgs.git winget-pkgs + git -C winget-pkgs sparse-checkout set "${PACKAGE_ROOT}" + git -C winget-pkgs checkout master + auth_header="Authorization: basic $(printf 'x-access-token:%s' "${TOKEN}" | base64 -w0)" + git -C winget-pkgs config --local http.https://github.com/.extraheader "${auth_header}" - name: Push manifests and open upstream PR if: steps.guard.outputs.skip == 'false' From 8d953018aef9033bd09bec8e991ff1312f6661e8 Mon Sep 17 00:00:00 2001 From: Copilot Date: Sat, 3 Oct 2026 11:11:06 +0300 Subject: [PATCH 2/2] fix(release): keep actions/checkout for contract-tested steps, disable credential persistence The contract test (test/standalone-release-workflow.test.ts) asserts the Homebrew tap checkout and the 'Check out existing WinGet version branch' step remain real actions/checkout steps with specific with: fields (repository/ref/path/token) - replacing them with raw git clone broke that contract and failed the test job on PR #2161. Revert those two specific steps to actions/checkout, but add persist-credentials: false so the action does not attempt to write its own git credential config (the suspected source of the original 'could not read Username... terminal prompts disabled' failure). Credential configuration for the later git push is now done explicitly via a scoped, local-only http.extraheader in a separate step - never persisted globally, never logged, never embedded in a remote URL. The two clones that are not covered by the contract test (the read-only winget-base base clone and the new-branch clone from microsoft/winget-pkgs master) remain anonymous git clone, since no credential is needed to read these public repos. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../workflows/squad-standalone-release.yml | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/.github/workflows/squad-standalone-release.yml b/.github/workflows/squad-standalone-release.yml index 0d74d0f03..5befaf35d 100644 --- a/.github/workflows/squad-standalone-release.yml +++ b/.github/workflows/squad-standalone-release.yml @@ -451,14 +451,22 @@ jobs: name: packaging-manifests path: packaging - - name: Clone Homebrew tap (read is anonymous; credential is scoped locally for the later push only) + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7 + with: + repository: bradygaster/homebrew-squad + ref: main + path: homebrew-tap + token: ${{ secrets.HOMEBREW_TAP_TOKEN }} + persist-credentials: false + + - name: Configure Homebrew tap push credential (scoped locally; checkout does not persist it) env: TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} + working-directory: homebrew-tap run: | set -euo pipefail - git clone --depth 1 --branch main https://github.com/bradygaster/homebrew-squad.git homebrew-tap auth_header="Authorization: basic $(printf 'x-access-token:%s' "${TOKEN}" | base64 -w0)" - git -C homebrew-tap config --local http.https://github.com/.extraheader "${auth_header}" + git config --local http.https://github.com/.extraheader "${auth_header}" - name: Publish cask to the tap env: @@ -735,19 +743,27 @@ jobs: echo "skip=false" >> "${GITHUB_OUTPUT}" echo "branch_exists=${branch_exists}" >> "${GITHUB_OUTPUT}" - - name: Clone existing WinGet version branch (fork; credential scoped locally for the later push) + - name: Check out existing WinGet version branch + if: steps.guard.outputs.skip == 'false' && steps.guard.outputs.branch_exists == 'true' + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7 + with: + repository: tamirdresher/winget-pkgs + ref: ${{ steps.release.outputs.branch }} + path: winget-pkgs + token: ${{ secrets.WINGET_CREATE_GITHUB_TOKEN }} + persist-credentials: false + fetch-depth: 1 + sparse-checkout: ${{ steps.release.outputs.package_root }} + + - name: Configure existing-branch push credential (scoped locally; checkout does not persist it) if: steps.guard.outputs.skip == 'false' && steps.guard.outputs.branch_exists == 'true' env: TOKEN: ${{ secrets.WINGET_CREATE_GITHUB_TOKEN }} - BRANCH: ${{ steps.release.outputs.branch }} - PACKAGE_ROOT: ${{ steps.release.outputs.package_root }} + working-directory: winget-pkgs run: | set -euo pipefail - git clone --no-checkout --depth 1 --branch "${BRANCH}" --filter=blob:none https://github.com/tamirdresher/winget-pkgs.git winget-pkgs - git -C winget-pkgs sparse-checkout set "${PACKAGE_ROOT}" - git -C winget-pkgs checkout "${BRANCH}" auth_header="Authorization: basic $(printf 'x-access-token:%s' "${TOKEN}" | base64 -w0)" - git -C winget-pkgs config --local http.https://github.com/.extraheader "${auth_header}" + git config --local http.https://github.com/.extraheader "${auth_header}" - name: Clone WinGet master for a new version branch (credential scoped locally for the later push) if: steps.guard.outputs.skip == 'false' && steps.guard.outputs.branch_exists == 'false'