From 4539ff346559c8ff17340cbb1ceb19deecbd5e76 Mon Sep 17 00:00:00 2001 From: radiantjade Date: Fri, 15 May 2026 13:39:29 +0900 Subject: [PATCH] Support protected-branch release tokens Protected branches need a release credential that can bypass branch protection, while normal GitHub Actions runs can keep using GITHUB_TOKEN. This gives pver an explicit PVER_GITHUB_TOKEN override, normalizes GitHub SSH and credentialed remotes to HTTPS, and documents the workflow setup for protected main branches. Constraint: GITHUB_TOKEN often cannot bypass protected branches unless the repository rules allow that actor. Rejected: Only document workflow permissions | the existing remote rewrite still ignored a dedicated release token. Confidence: medium Scope-risk: narrow Tested: npm run typecheck Tested: npm run build Tested: npm run lint Not-tested: Live protected-branch repository push. --- README.md | 29 ++++++++++++++++++++++++ src/release/configure-origin.ts | 39 +++++++++++++++------------------ 2 files changed, 47 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 2de9789..6aec230 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,35 @@ jobs: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} ``` +#### Protected branches + +If your release workflow needs to push the version bump back to a protected +branch, provide a token that is allowed to bypass that branch protection as +`PVER_GITHUB_TOKEN`. + +```yml +permissions: + contents: write + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - run: npm ci + - run: pver release + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + PVER_GITHUB_TOKEN: ${{ secrets.PVER_GITHUB_TOKEN }} +``` + +`PVER_GITHUB_TOKEN` takes precedence over `GITHUB_TOKEN`. Use a fine-grained PAT +or GitHub App token with contents write access, then add that token's actor to +the branch protection bypass list. `GITHUB_TOKEN` can still be used for normal +branches where no bypass is required. + ## Analysis Analysis has two steps. Getting the `current_version` and using the `transition_method` to diff --git a/src/release/configure-origin.ts b/src/release/configure-origin.ts index eb15c28..7771b9b 100644 --- a/src/release/configure-origin.ts +++ b/src/release/configure-origin.ts @@ -1,26 +1,23 @@ export const configureOrigin = async (git: any) => { - if (process.env.GITHUB_TOKEN) { - // This doesn't work- it's possible the checkout command needs to use the - // github token (in the workflow yaml) - const remote_url = await git.remote(["get-url", "origin"]) + const githubToken = process.env.PVER_GITHUB_TOKEN ?? process.env.GITHUB_TOKEN - // if the remote is in the form of git@github.com:foo/bar.git, convert it to - // https://github.com/foo/bar.git - if (remote_url.includes("git@")) { - const new_url = remote_url!.replace("git@", "https://") - await git.removeRemote("origin") - await git.addRemote("origin", new_url) - } + if (!githubToken) return - if (!remote_url.includes("oauth2:")) { - const new_url = remote_url! - .replace( - "https://", - `https://oauth2:${process.env.GITHUB_TOKEN.trim()}@` - ) - .replace(/\n/g, "") - await git.removeRemote("origin") - await git.addRemote("origin", new_url) - } + const remoteUrl = String(await git.remote(["get-url", "origin"])).trim() + + if (!remoteUrl.includes("github.com")) return + + const httpsRemoteUrl = remoteUrl + .replace(/^git@github\.com:/, "https://github.com/") + .replace(/^ssh:\/\/git@github\.com\//, "https://github.com/") + .replace(/^https:\/\/[^@/]+@github\.com\//, "https://github.com/") + + const authenticatedRemoteUrl = httpsRemoteUrl.replace( + /^https:\/\/github\.com\//, + `https://x-access-token:${githubToken.trim()}@github.com/` + ) + + if (authenticatedRemoteUrl !== remoteUrl) { + await git.remote(["set-url", "origin", authenticatedRemoteUrl]) } }