Skip to content
This repository was archived by the owner on Nov 9, 2026. It is now read-only.

ci: use OIDC token-exchange for gh-pages deploys - #426

Merged
OGPoyraz merged 3 commits into
mainfrom
fix/publish-docs-to-gh-page
Sep 3, 2026
Merged

OGPoyraz merged 3 commits into
mainfrom
fix/publish-docs-to-gh-page

Conversation

@OGPoyraz

@OGPoyraz OGPoyraz commented Sep 1, 2026 •

Copy link
Copy Markdown
Member

Summary

Docs deploys to gh-pages were failing (GH013 ruleset rejection) because #421 switched the deploy token to GITHUB_TOKEN, which isn't allowed to bypass the org's push-protection ruleset on gh-pages.

Fixes it by switching to OIDC token-exchange (same pattern as MetaMask/snaps): each deploy job exchanges its OIDC identity for a short-lived contents: write token via MetaMask/github-tools/.github/actions/get-token@v1, instead of using a stored secret.

Why this approach over reverting to the old PUBLISH_DOCS_TOKEN secret

That secret still exists and would also fix the immediate issue, but it's been unused/unrotated since #421 — exactly the kind of staleness that let this regression go unnoticed.

Validation

  • actionlint v1.7.12: 0 errors on all changed files
  • Matches snaps' working implementation exactly (permissions, get-token step, personal_token wiring)
  • vars.TOKEN_EXCHANGE_URL confirmed already inherited by this repo at the org level

Caveat

Can't verify from the API whether the token-exchange service's server-side trust policy already allows eth-sig-util to bypass the gh-pages ruleset (it evidently does for snaps). If not yet configured, the Get access token step will fail with a clear error rather than a silent bad push.

The gh-pages branch is protected by an org-wide ruleset that blocks
direct pushes (requires a PR). The default GITHUB_TOKEN identity used
by the gh-pages deploy step is not on that ruleset's bypass list, so
every docs deploy has been failing with:

  remote: error: GH013: Repository rule violations found for
  refs/heads/gh-pages.
  remote: - Changes must be made through a pull request.

Prior to #421, the workflow used personal_token: ${{
secrets.PUBLISH_DOCS_TOKEN }} instead of GITHUB_TOKEN, and that token's
identity is allowed to bypass the ruleset (confirmed by successful
gh-pages deploys through March 2025, well after the ruleset was
created in May 2024). #421 switched to GITHUB_TOKEN as an intentional
deviation from the module template, which is what broke this.

Revert to PUBLISH_DOCS_TOKEN across the call chain, matching the
current metamask-module-template:
- publish-docs.yml: require PUBLISH_DOCS_TOKEN secret, use
  personal_token instead of github_token in the deploy step
- publish-main-docs.yml / publish-rc-docs.yml: forward
  PUBLISH_DOCS_TOKEN to publish-docs.yml
- publish-release.yml: require PUBLISH_DOCS_TOKEN secret, forward it
  to both gh-pages jobs
- main.yml: forward PUBLISH_DOCS_TOKEN to publish-release.yml
Switch the gh-pages deploy from the static PUBLISH_DOCS_TOKEN secret to
the org's OIDC token-exchange service, matching the pattern already
used by MetaMask/snaps (publish-github-pages.yml).

Each job requests a short-lived contents:write token via
MetaMask/github-tools/.github/actions/get-token@v1, which exchanges
the job's signed OIDC identity token against ${{ vars.TOKEN_EXCHANGE_URL
}} (an org variable already inherited by this repo). That token is fed
into peaceiris/actions-gh-pages as personal_token, replacing the
static PUBLISH_DOCS_TOKEN secret used in the previous commit.

Benefits over the static-token approach:
- No long-lived secret stored in the github-pages environment.
- Token is minted per-run and expires in minutes.
- Nothing to rotate or accidentally leave stale (this is what caused
  the original outage: PUBLISH_DOCS_TOKEN silently going unused for
  months after #421 switched to GITHUB_TOKEN).

Changes:
- publish-docs.yml: add id-token: write permission, add "Get access
  token" step using MetaMask/github-tools/.github/actions/get-token@v1,
  use the exchanged token as personal_token instead of
  secrets.PUBLISH_DOCS_TOKEN. Drop the now-unused PUBLISH_DOCS_TOKEN
  secret declaration.
- publish-main-docs.yml / publish-rc-docs.yml: update caller
  permissions to contents: read + id-token: write; drop the
  PUBLISH_DOCS_TOKEN secret forwarding (no longer needed).
- publish-release.yml: same permission update for both gh-pages jobs;
  drop the PUBLISH_DOCS_TOKEN secret requirement and forwarding.
- main.yml: drop the now-unused PUBLISH_DOCS_TOKEN forward to
  publish-release.yml.
@OGPoyraz OGPoyraz changed the title ci: use PUBLISH_DOCS_TOKEN for gh-pages deploys ci: use OIDC token-exchange for gh-pages deploys Sep 1, 2026

@mcmire mcmire left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, as long as a policy for the new token is present in token-exchange-service.

@mcmire

mcmire commented Sep 1, 2026

Copy link
Copy Markdown

Can't verify from the API whether the token-exchange service's server-side trust policy already allows eth-sig-util to bypass the gh-pages ruleset (it evidently does for snaps).

This is something that we had to explicitly ask TechOps to do, so you may have to do the same.

@OGPoyraz

OGPoyraz commented Sep 2, 2026 •

Copy link
Copy Markdown
Member Author

Thank you @mcmire !

Created a PR to add this package into token-exchange-service here: https://github.com/consensys-vertical-apps/token-exchange-service/pull/170

I will separately ask TechOps for the gh-pages ruleset.

Until all resolved I will keep this open

@OGPoyraz
OGPoyraz merged commit 996ac29 into main Sep 3, 2026
29 checks passed
@OGPoyraz
OGPoyraz deleted the fix/publish-docs-to-gh-page branch September 3, 2026 14:15
Chess-ETH pushed a commit to Chess-ETH/eth-sig-util that referenced this pull request Oct 23, 2026
## Summary

Docs deploys to `gh-pages` were failing (GH013 ruleset rejection)
because MetaMask#421 switched the deploy token to `GITHUB_TOKEN`, which isn't
allowed to bypass the org's push-protection ruleset on `gh-pages`.

Fixes it by switching to OIDC token-exchange (same pattern as
[`MetaMask/snaps`](https://github.com/MetaMask/snaps/blob/main/.github/workflows/publish-github-pages.yml)):
each deploy job exchanges its OIDC identity for a short-lived `contents:
write` token via `MetaMask/github-tools/.github/actions/get-token@v1`,
instead of using a stored secret.

## Why this approach over reverting to the old `PUBLISH_DOCS_TOKEN`
secret

That secret still exists and would also fix the immediate issue, but
it's been unused/unrotated since MetaMask#421 — exactly the kind of staleness
that let this regression go unnoticed.

## Validation

- `actionlint v1.7.12`: 0 errors on all changed files
- Matches `snaps`' working implementation exactly (permissions,
`get-token` step, `personal_token` wiring)
- `vars.TOKEN_EXCHANGE_URL` confirmed already inherited by this repo at
the org level

## Caveat

Can't verify from the API whether the token-exchange service's
server-side trust policy already allows `eth-sig-util` to bypass the
`gh-pages` ruleset (it evidently does for `snaps`). If not yet
configured, the `Get access token` step will fail with a clear error
rather than a silent bad push.
Chess-ETH pushed a commit to Chess-ETH/eth-sig-util that referenced this pull request Oct 23, 2026
## Summary

Docs deploys to `gh-pages` were failing (GH013 ruleset rejection)
because MetaMask#421 switched the deploy token to `GITHUB_TOKEN`, which isn't
allowed to bypass the org's push-protection ruleset on `gh-pages`.

Fixes it by switching to OIDC token-exchange (same pattern as
[`MetaMask/snaps`](https://github.com/MetaMask/snaps/blob/main/.github/workflows/publish-github-pages.yml)):
each deploy job exchanges its OIDC identity for a short-lived `contents:
write` token via `MetaMask/github-tools/.github/actions/get-token@v1`,
instead of using a stored secret.

## Why this approach over reverting to the old `PUBLISH_DOCS_TOKEN`
secret

That secret still exists and would also fix the immediate issue, but
it's been unused/unrotated since MetaMask#421 — exactly the kind of staleness
that let this regression go unnoticed.

## Validation

- `actionlint v1.7.12`: 0 errors on all changed files
- Matches `snaps`' working implementation exactly (permissions,
`get-token` step, `personal_token` wiring)
- `vars.TOKEN_EXCHANGE_URL` confirmed already inherited by this repo at
the org level

## Caveat

Can't verify from the API whether the token-exchange service's
server-side trust policy already allows `eth-sig-util` to bypass the
`gh-pages` ruleset (it evidently does for `snaps`). If not yet
configured, the `Get access token` step will fail with a clear error
rather than a silent bad push.
Chess-ETH pushed a commit to Chess-ETH/eth-sig-util that referenced this pull request Oct 23, 2026
## Summary

Docs deploys to `gh-pages` were failing (GH013 ruleset rejection)
because MetaMask#421 switched the deploy token to `GITHUB_TOKEN`, which isn't
allowed to bypass the org's push-protection ruleset on `gh-pages`.

Fixes it by switching to OIDC token-exchange (same pattern as
[`MetaMask/snaps`](https://github.com/MetaMask/snaps/blob/main/.github/workflows/publish-github-pages.yml)):
each deploy job exchanges its OIDC identity for a short-lived `contents:
write` token via `MetaMask/github-tools/.github/actions/get-token@v1`,
instead of using a stored secret.

## Why this approach over reverting to the old `PUBLISH_DOCS_TOKEN`
secret

That secret still exists and would also fix the immediate issue, but
it's been unused/unrotated since MetaMask#421 — exactly the kind of staleness
that let this regression go unnoticed.

## Validation

- `actionlint v1.7.12`: 0 errors on all changed files
- Matches `snaps`' working implementation exactly (permissions,
`get-token` step, `personal_token` wiring)
- `vars.TOKEN_EXCHANGE_URL` confirmed already inherited by this repo at
the org level

## Caveat

Can't verify from the API whether the token-exchange service's
server-side trust policy already allows `eth-sig-util` to bypass the
`gh-pages` ruleset (it evidently does for `snaps`). If not yet
configured, the `Get access token` step will fail with a clear error
rather than a silent bad push.
Chess-ETH pushed a commit to Chess-ETH/eth-sig-util that referenced this pull request Oct 23, 2026
## Summary

Docs deploys to `gh-pages` were failing (GH013 ruleset rejection)
because MetaMask#421 switched the deploy token to `GITHUB_TOKEN`, which isn't
allowed to bypass the org's push-protection ruleset on `gh-pages`.

Fixes it by switching to OIDC token-exchange (same pattern as
[`MetaMask/snaps`](https://github.com/MetaMask/snaps/blob/main/.github/workflows/publish-github-pages.yml)):
each deploy job exchanges its OIDC identity for a short-lived `contents:
write` token via `MetaMask/github-tools/.github/actions/get-token@v1`,
instead of using a stored secret.

## Why this approach over reverting to the old `PUBLISH_DOCS_TOKEN`
secret

That secret still exists and would also fix the immediate issue, but
it's been unused/unrotated since MetaMask#421 — exactly the kind of staleness
that let this regression go unnoticed.

## Validation

- `actionlint v1.7.12`: 0 errors on all changed files
- Matches `snaps`' working implementation exactly (permissions,
`get-token` step, `personal_token` wiring)
- `vars.TOKEN_EXCHANGE_URL` confirmed already inherited by this repo at
the org level

## Caveat

Can't verify from the API whether the token-exchange service's
server-side trust policy already allows `eth-sig-util` to bypass the
`gh-pages` ruleset (it evidently does for `snaps`). If not yet
configured, the `Get access token` step will fail with a clear error
rather than a silent bad push.
Chess-ETH pushed a commit to Chess-ETH/eth-sig-util that referenced this pull request Oct 23, 2026
Chess-ETH pushed a commit to Chess-ETH/eth-sig-util that referenced this pull request Oct 23, 2026
## Summary

Docs deploys to `gh-pages` were failing (GH013 ruleset rejection)
because MetaMask#421 switched the deploy token to `GITHUB_TOKEN`, which isn't
allowed to bypass the org's push-protection ruleset on `gh-pages`.

Fixes it by switching to OIDC token-exchange (same pattern as
[`MetaMask/snaps`](https://github.com/MetaMask/snaps/blob/main/.github/workflows/publish-github-pages.yml)):
each deploy job exchanges its OIDC identity for a short-lived `contents:
write` token via `MetaMask/github-tools/.github/actions/get-token@v1`,
instead of using a stored secret.

## Why this approach over reverting to the old `PUBLISH_DOCS_TOKEN`
secret

That secret still exists and would also fix the immediate issue, but
it's been unused/unrotated since MetaMask#421 — exactly the kind of staleness
that let this regression go unnoticed.

## Validation

- `actionlint v1.7.12`: 0 errors on all changed files
- Matches `snaps`' working implementation exactly (permissions,
`get-token` step, `personal_token` wiring)
- `vars.TOKEN_EXCHANGE_URL` confirmed already inherited by this repo at
the org level

## Caveat

Can't verify from the API whether the token-exchange service's
server-side trust policy already allows `eth-sig-util` to bypass the
`gh-pages` ruleset (it evidently does for `snaps`). If not yet
configured, the `Get access token` step will fail with a clear error
rather than a silent bad push.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants