diff --git a/.github/workflows/go-ci.yml b/.github/workflows/go-ci.yml index 32f325a..08229c8 100644 --- a/.github/workflows/go-ci.yml +++ b/.github/workflows/go-ci.yml @@ -67,9 +67,16 @@ on: description: 'Check that nested go.mod files require their siblings at versions the module proxy can serve. A nested module replace is ignored once the module is somebody else dependency, so a require naming an unpublished version breaks go get for every consumer while every local build keeps working. No-op in a repo with a single go.mod.' type: boolean default: true + goprivate: + description: 'GOPRIVATE glob applied when XRAPH_REPO_TOKEN is set. Narrow it to what is genuinely private - the glob also sets GONOPROXY, so a broad one drags every public module under it off the proxy and onto a full git clone per job, for nothing. Comma-separated for more than one.' + type: string + default: 'github.com/xraph/*' secrets: CODECOV_TOKEN: required: false + XRAPH_REPO_TOKEN: + description: 'Read access to the private repositories in this org. Without it, private modules are never fetched and a repo that needs one fails at download.' + required: false jobs: setup: @@ -129,6 +136,27 @@ jobs: - name: Checkout uses: actions/checkout@v7 + # kgkit and any other private xraph module is not on the public proxy, and + # an unauthenticated fetch comes back "repository not found", which reads + # as a missing repo rather than a missing credential. Carry the token on + # the org's clone URL and take the private paths off the proxy. + # + # The rewrite is scoped to the org rather than to all of github.com, so it + # cannot clobber a credential a consumer has already set for somewhere + # else. No-op without the secret, so public-only consumers are unaffected. + - name: Configure Go module auth + shell: bash + env: + XRAPH_REPO_TOKEN: ${{ secrets.XRAPH_REPO_TOKEN }} + GOPRIVATE_GLOB: ${{ inputs.goprivate }} + run: | + set -euo pipefail + if [ -n "${XRAPH_REPO_TOKEN:-}" ]; then + git config --global url."https://x-access-token:${XRAPH_REPO_TOKEN}@github.com/xraph/".insteadOf "https://github.com/xraph/" + echo "GOPRIVATE=${GOPRIVATE_GLOB}" >> "$GITHUB_ENV" + echo "- private modules: \`${GOPRIVATE_GLOB}\`, fetched over the token" >> "$GITHUB_STEP_SUMMARY" + fi + - name: Set up Go uses: actions/setup-go@v7 with: @@ -267,6 +295,27 @@ jobs: - name: Checkout uses: actions/checkout@v7 + # kgkit and any other private xraph module is not on the public proxy, and + # an unauthenticated fetch comes back "repository not found", which reads + # as a missing repo rather than a missing credential. Carry the token on + # the org's clone URL and take the private paths off the proxy. + # + # The rewrite is scoped to the org rather than to all of github.com, so it + # cannot clobber a credential a consumer has already set for somewhere + # else. No-op without the secret, so public-only consumers are unaffected. + - name: Configure Go module auth + shell: bash + env: + XRAPH_REPO_TOKEN: ${{ secrets.XRAPH_REPO_TOKEN }} + GOPRIVATE_GLOB: ${{ inputs.goprivate }} + run: | + set -euo pipefail + if [ -n "${XRAPH_REPO_TOKEN:-}" ]; then + git config --global url."https://x-access-token:${XRAPH_REPO_TOKEN}@github.com/xraph/".insteadOf "https://github.com/xraph/" + echo "GOPRIVATE=${GOPRIVATE_GLOB}" >> "$GITHUB_ENV" + echo "- private modules: \`${GOPRIVATE_GLOB}\`, fetched over the token" >> "$GITHUB_STEP_SUMMARY" + fi + - name: Set up Go uses: actions/setup-go@v7 with: @@ -299,6 +348,27 @@ jobs: - name: Checkout uses: actions/checkout@v7 + # kgkit and any other private xraph module is not on the public proxy, and + # an unauthenticated fetch comes back "repository not found", which reads + # as a missing repo rather than a missing credential. Carry the token on + # the org's clone URL and take the private paths off the proxy. + # + # The rewrite is scoped to the org rather than to all of github.com, so it + # cannot clobber a credential a consumer has already set for somewhere + # else. No-op without the secret, so public-only consumers are unaffected. + - name: Configure Go module auth + shell: bash + env: + XRAPH_REPO_TOKEN: ${{ secrets.XRAPH_REPO_TOKEN }} + GOPRIVATE_GLOB: ${{ inputs.goprivate }} + run: | + set -euo pipefail + if [ -n "${XRAPH_REPO_TOKEN:-}" ]; then + git config --global url."https://x-access-token:${XRAPH_REPO_TOKEN}@github.com/xraph/".insteadOf "https://github.com/xraph/" + echo "GOPRIVATE=${GOPRIVATE_GLOB}" >> "$GITHUB_ENV" + echo "- private modules: \`${GOPRIVATE_GLOB}\`, fetched over the token" >> "$GITHUB_STEP_SUMMARY" + fi + - name: Set up Go uses: actions/setup-go@v7 with: @@ -366,6 +436,27 @@ jobs: - name: Checkout uses: actions/checkout@v7 + # kgkit and any other private xraph module is not on the public proxy, and + # an unauthenticated fetch comes back "repository not found", which reads + # as a missing repo rather than a missing credential. Carry the token on + # the org's clone URL and take the private paths off the proxy. + # + # The rewrite is scoped to the org rather than to all of github.com, so it + # cannot clobber a credential a consumer has already set for somewhere + # else. No-op without the secret, so public-only consumers are unaffected. + - name: Configure Go module auth + shell: bash + env: + XRAPH_REPO_TOKEN: ${{ secrets.XRAPH_REPO_TOKEN }} + GOPRIVATE_GLOB: ${{ inputs.goprivate }} + run: | + set -euo pipefail + if [ -n "${XRAPH_REPO_TOKEN:-}" ]; then + git config --global url."https://x-access-token:${XRAPH_REPO_TOKEN}@github.com/xraph/".insteadOf "https://github.com/xraph/" + echo "GOPRIVATE=${GOPRIVATE_GLOB}" >> "$GITHUB_ENV" + echo "- private modules: \`${GOPRIVATE_GLOB}\`, fetched over the token" >> "$GITHUB_STEP_SUMMARY" + fi + - name: Set up Go uses: actions/setup-go@v7 with: diff --git a/README.md b/README.md index b81899d..c5c330e 100644 --- a/README.md +++ b/README.md @@ -189,6 +189,43 @@ npm-global-packages: 'typescript@5.8.2 esbuild@0.28.1' Both default to empty, so nothing is installed unless asked. +## Private modules + +`kgkit` is private. Anything that depends on it dies at `go mod download` with +"repository not found", which reads like a typo in the import path and isn't +one: Go asked proxy.golang.org, got a 404 back, and never got as far as trying +git, because nothing had told it that path was private to begin with. + +Hand the workflow a token with read access and it sorts out the rest: + +```yaml +jobs: + ci: + uses: xraph/workflows/.github/workflows/go-ci.yml@v1 + with: + goprivate: github.com/xraph/kgkit + secrets: + XRAPH_REPO_TOKEN: ${{ secrets.XRAPH_REPO_TOKEN }} +``` + +Every job that downloads dependencies rewrites `https://github.com/xraph/` +to carry that token, then sets `GOPRIVATE` so those paths skip the proxy and go +straight to git. Leave the secret out and the step does nothing. A public-only +repo never has to think about it. The rewrite is scoped to the org and not to +all of github.com, so it won't stamp on a credential you already set up for +somewhere else. + +Keep `goprivate` narrow. It sets `GONOPROXY` too, so a glob wider than your real +private modules drags perfectly public ones off the proxy and clones each of +them from scratch, in every job, on every run. foundry names a single module for +that reason: it pulls in twenty-two xraph modules and exactly one of them is +private. The default is `github.com/xraph/*`, which is the safe answer when you +do not know and the slow one when you do. + +Read access on the private repos is all the token needs. A fine-grained PAT +works. So does a GitHub App installation token. The default `GITHUB_TOKEN` does +not, and cannot: it only ever reaches the repo the workflow is running in. + ## Escaping the Makefile probe `go-ci.yml` prefers a Makefile target when one matching the step exists. That