From 495e7dbf5e1235260e3da7e8f3bde4398fda1a49 Mon Sep 17 00:00:00 2001 From: Joseph Mearman Date: Sat, 12 Sep 2026 13:35:32 +0100 Subject: [PATCH] fix(release): push over SSH via repositoryUrl, not a git config rewrite semantic-release builds its release-commit/tag push URL from the resolved repositoryUrl, embedding an x-access-token:$GITHUB_TOKEN@ credential whenever that URL is https:// -- it never reads or respects the ambient git remote or any url.insteadOf rewrite for this purpose. The previous fix's global insteadOf mapping targeted a plain https://github.com/ prefix, but the actual push URL already had a token embedded before that prefix, so the rewrite never matched and the push kept failing identically. Setting repositoryUrl to the SSH form in release.config.ts (separate from package.json's own git+https:// repository field, which is public consumer metadata and stays as-is) skips the token-embedding path entirely, so the push authenticates with whatever key actions/checkout's ssh-key input already wired into core.sshCommand -- the deploy key registered as a DeployKey bypass actor on the ruleset. --- .github/workflows/ci.yml | 4 +--- release.config.ts | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf4b362..03bff1f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -88,15 +88,13 @@ jobs: published: ${{ steps.before.outputs.version != steps.after.outputs.version }} version: ${{ steps.after.outputs.version }} steps: - # main's own ruleset requires the "Required Checks" status and blocks direct pushes, and the default GITHUB_TOKEN has no way to bypass a repository ruleset (GitHub doesn't support naming the github-actions[bot] identity as a bypass actor at all) -- so semantic-release's own release-commit/tag push needs a bypass identity. A deploy key with write access, added as a DeployKey bypass actor on the ruleset, is the lightest-weight one: repo-scoped, no GitHub App installation to manage, no personal/org-admin credential involved. + # main's own ruleset requires the "Required Checks" status and blocks direct pushes, and the default GITHUB_TOKEN has no way to bypass a repository ruleset (GitHub doesn't support naming the github-actions[bot] identity as a bypass actor at all) -- so semantic-release's own release-commit/tag push needs a bypass identity. A deploy key with write access, added as a DeployKey bypass actor on the ruleset, is the lightest-weight one: repo-scoped, no GitHub App installation to manage, no personal/org-admin credential involved. ssh-key only wires the key into core.sshCommand for later git commands to use -- release.config.ts's own repositoryUrl is what actually makes semantic-release's push go out over SSH instead of an https:// URL with the default GITHUB_TOKEN embedded. - uses: actions/checkout@v7 with: # semantic-release analyses the full commit history since the last release. fetch-depth: 0 ssh-key: ${{ secrets.RELEASE_DEPLOY_KEY }} - # ssh-key only wires the deploy key into core.sshCommand for actions/checkout's own git commands -- it does not rewrite the "origin" remote or any later command's push URL. @semantic-release/git constructs its push against a plain https://github.com/... URL, which persist-credentials would otherwise satisfy with the default GITHUB_TOKEN instead of the deploy key, so that credential is turned off and every https://github.com/ URL is rewritten to the SSH form the deploy key actually authenticates. persist-credentials: false - - run: git config --global url."git@github.com:".insteadOf "https://github.com/" - uses: pnpm/action-setup@v6 - uses: actions/setup-node@v7 with: diff --git a/release.config.ts b/release.config.ts index cd3a76a..14e5111 100644 --- a/release.config.ts +++ b/release.config.ts @@ -29,6 +29,8 @@ export const commitTypes: readonly CommitType[] = [ */ const config: Options = { branches: ['main'], + // Deliberately the SSH form, not package.json's own git+https:// repository field (that field stays https:// -- it's public consumer-facing metadata, unrelated to how this release pushes). semantic-release only embeds an x-access-token:$GITHUB_TOKEN@ credential into an https:// repositoryUrl; the default GITHUB_TOKEN it would embed has no way to bypass main's branch ruleset. An SSH URL skips that embedding entirely and pushes using whatever key actions/checkout's ssh-key input already wired into core.sshCommand -- the deploy key added as a DeployKey bypass actor on the ruleset. + repositoryUrl: 'git@github.com:ExaDev/eslint-config.git', plugins: [ [ '@semantic-release/commit-analyzer',