diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1244ed2..4620e23 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,27 +50,47 @@ jobs: # Run the action against this very PR. A composite action can pass every # static check and still fail at runtime, so exercise it for real. # - # The action installs the linter with `go install`, which needs the - # DivergentCodes/commitlint module to be fetchable. While that repo is - # private the module proxy returns 404 on a hosted runner, so this job is - # skipped rather than left failing for a reason unrelated to this code. - # Once commitlint is public, delete the `if:` and this job runs everywhere. + # The action authenticates its module fetch, so this no longer depends on + # commitlint being public. It does depend on the token being able to read + # that repo: the default github.token is scoped to THIS repository, so while + # commitlint is private a cross-repo token (COMMITLINT_READ_TOKEN) is + # required. Skip when it is absent rather than fail for a reason unrelated + # to the code under review — once commitlint is public, neither is needed. self-test: - if: ${{ github.repository_owner == 'DivergentCodes' && vars.COMMITLINT_PUBLIC == 'true' }} runs-on: ubuntu-latest steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - name: check module is reachable + id: reach + env: + TOKEN: ${{ secrets.COMMITLINT_READ_TOKEN }} + run: | + if curl -fsS -o /dev/null "https://proxy.golang.org/github.com/!divergent!codes/commitlint/@v/list"; then + echo "reachable=true" >> "$GITHUB_OUTPUT" + echo "commitlint is public; running self-test with the default token" + elif [ -n "${TOKEN:-}" ]; then + echo "reachable=true" >> "$GITHUB_OUTPUT" + echo "commitlint is private; running self-test with COMMITLINT_READ_TOKEN" + else + echo "reachable=false" >> "$GITHUB_OUTPUT" + echo "::notice::self-test skipped: commitlint is private and COMMITLINT_READ_TOKEN is not set" + fi + - name: run this action (advisory) + if: steps.reach.outputs.reachable == 'true' uses: ./ with: # Never fail this repo's CI on a contributor's commit style; the point # is to prove the action executes, not to gate on its verdict. pr-title-mode: warn commits-mode: warn + github-token: ${{ secrets.COMMITLINT_READ_TOKEN || github.token }} - name: run with commits-mode off + if: steps.reach.outputs.reachable == 'true' uses: ./ with: pr-title-mode: warn commits-mode: off + github-token: ${{ secrets.COMMITLINT_READ_TOKEN || github.token }} diff --git a/README.md b/README.md index d9b3a09..933a3ac 100644 --- a/README.md +++ b/README.md @@ -60,13 +60,27 @@ merge with rebase or merge commits instead, set `commits-mode: block`. | `scopes` | any | comma-separated allowed scopes | | `require-scope` | `false` | require a `(scope)` | | `max-subject-length` | `72` | subject length limit | +| `github-token` | `github.token` | fetches the commitlint module while its repo is private; unused once public | ## Permissions `permissions: contents: read` is sufficient — enough for `actions/checkout` -to fetch the commits. The action makes no API calls, needs no token, and never -writes anything. With `commits-mode: off` it reads only the event payload and -needs no permissions beyond the workflow default. +to fetch the commits. The action makes no API calls and never writes anything. +With `commits-mode: off` it reads only the event payload. + +While the `commitlint` repository is private, `go install` needs credentials +to fetch the module. The action rewrites only `github.com/DivergentCodes/` +URLs to carry `github-token`, so the token is never offered to another host or +org, and sets `GOPRIVATE` so the public proxy and checksum database are +bypassed. Pass a token that can read that repo: + +```yaml + - uses: DivergentCodes/commitlint-action@ + with: + github-token: ${{ secrets.COMMITLINT_READ_TOKEN }} +``` + +Once `commitlint` is public this is unnecessary and the default applies. ## Runner requirements diff --git a/action.yml b/action.yml index 0c4db7b..37f4989 100644 --- a/action.yml +++ b/action.yml @@ -31,6 +31,11 @@ inputs: max-subject-length: description: Maximum subject line length default: "72" + github-token: + description: > + Token used to fetch the commitlint module while its repository is + private. Unused once commitlint is public. + default: ${{ github.token }} runs: using: composite @@ -40,11 +45,34 @@ runs: go-version: stable cache: false + # `go install` fetches over HTTPS with no credentials, so while the + # commitlint repo is private git prompts for a username and dies with + # "terminal prompts disabled". Rewrite only DivergentCodes URLs to carry + # the token, so it is never offered to another host or org, and set + # GOPRIVATE so the public proxy and checksum database — which cannot see a + # private module — are bypassed rather than consulted and failed. + # + # This is a no-op once commitlint is public: the rewrite still matches, but + # a public fetch would have succeeded anyway. - name: Install commitlint shell: bash env: VERSION: ${{ inputs.version }} - run: go install "github.com/DivergentCodes/commitlint@${VERSION}" + GH_TOKEN: ${{ inputs.github-token }} + GOPRIVATE: github.com/DivergentCodes/* + run: | + set -euo pipefail + cleanup() { + git config --global --unset-all \ + url."https://x-access-token:${GH_TOKEN}@github.com/DivergentCodes/".insteadOf || true + } + trap cleanup EXIT + # The token stays in the environment rather than argv, and Actions + # masks it in logs; the trap keeps it out of later steps' git config. + git config --global \ + url."https://x-access-token:${GH_TOKEN}@github.com/DivergentCodes/".insteadOf \ + "https://github.com/DivergentCodes/" + go install "github.com/DivergentCodes/commitlint@${VERSION}" - name: Lint PR title if: ${{ inputs.pr-title-mode != 'off' && github.event_name == 'pull_request' }}