Skip to content

fix: fall back to unauthenticated GitHub API when SAML-enforced token…#40250

Merged
pelikhan merged 6 commits into
mainfrom
update-saml-fix
Jun 19, 2026
Merged

fix: fall back to unauthenticated GitHub API when SAML-enforced token…#40250
pelikhan merged 6 commits into
mainfrom
update-saml-fix

Conversation

@pelikhan

@pelikhan pelikhan commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

Two related improvements land together:

  1. SAML-enforcement fallback — when a SAML-enforced enterprise token is rejected while accessing a public cross-org github.com repo, update_workflows and remote_fetch now transparently retry the same request against the unauthenticated public GitHub API (60 req/hour tier), instead of surfacing an authentication error to the user.

  2. Container-pin auto-recompileUpdateContainerPins now returns a boolean signalling whether new digests were written; callers (update and upgrade commands) use that signal to automatically invoke recompileAllWorkflows when fresh pins are detected and --no-compile is not set, eliminating the manual re-run step previously required.

Bundled alongside these fixes: a sweep bumping actions/checkout from v6.0.3 → v7.0.0 and github/stale-repos from v9.0.14 → v9.0.15, with both action_pins.json data files updated and all .md workflow specs recompiled.


Changes by area

SAML / unauthenticated API fallback (pkg/cli/update_workflows.go, pkg/parser/remote_fetch.go)

  • Added fetchPublicGitHubAPI (CLI layer) and fetchPublicGitHubContentsAPI (parser layer) — shared HTTP helpers that call the unauthenticated api.github.com endpoint with correct per-segment path encoding (preserving / separators).
  • Fallback is gated to github.com hosts only and is only attempted when gitutil.IsAuthError confirms the upstream error is authentication-related.
  • Covers all four resolution paths: ref → SHA (resolveRefToSHAViaPublicAPI), file download (downloadFileViaPublicAPI), workflow/directory file listing (listWorkflowFilesViaPublicAPI, listDirAllFilesViaPublicAPI), and subdirectory listing (listDirSubdirsViaPublicAPI).
  • Recursive directory listing intentionally receives no public-API fallback (rate-limit risk at 60 req/hour).
  • getRepoDefaultBranch, getLatestBranchCommitSHA, and the releases API closure in update_workflows.go all route through the new fallback.

Container-pin recompile signal (pkg/cli/update_container_pins.go, pkg/cli/update_command.go, pkg/cli/upgrade_command.go)

Before After
UpdateContainerPins() error UpdateContainerPins() (bool, error)
Callers ignore whether pins changed Callers recompile all .md files when bool == true && !noCompile
  • update_command.goRunUpdateWorkflows captures the new boolean and calls the new recompileAllWorkflows helper.
  • upgrade_command.go — same pattern applied to the upgrade command's UpdateContainerPins call site.
  • Breaking change: any external callers of UpdateContainerPins must update their call sites to capture the additional return value.

Action-pin data (pkg/actionpins/data/action_pins.json, pkg/workflow/data/action_pins.json)

  • actions/checkout pinned to v7.0.0 (SHA 9c091bb), replacing v6.0.3 (SHA df4cb1c).
  • github/stale-repos bumped from v9.0.14v9.0.15.
  • Both files reorganised for consistent key ordering: containers section placed before entries; object fields ordered alphabetically.
  • No semantic SHA changes beyond the two version bumps.

Workflow spec recompilation (.github/workflows/*.md, agentic_commands.yml, agentics-maintenance.yml)

  • All .md workflow specs updated with the new actions/checkout@v7.0.0 pin.
  • agentic_commands.yml and agentics-maintenance.yml (the two compiled YAML files in the diff) regenerated to match.
  • actions/upload-artifact updated to the more specific v7.0.1 tag in copilot-centralization-optimizer.md.

Impact assessment

Area Impact Breaking
SAML fallback (parser + CLI) High — unblocks SAML-org users No
UpdateContainerPins signature High — API change Yes
Auto-recompile on pin change High — behaviour change for update/upgrade commands No
Action-pin version bumps Medium — affects all compiled workflows No
JSON key reordering Low — formatting only No

Testing notes

  • Verify that gh aw update against a SAML-enforced repo no longer fails with an auth error when the target workflow references a public github.com action.
  • Verify that running gh aw update or gh aw upgrade when container pins change automatically triggers recompilation without requiring a separate gh aw compile invocation.
  • Confirm --no-compile suppresses the automatic recompile step.
  • Any package importing UpdateContainerPins must be updated to capture (bool, error).

Generated by PR Description Updater for issue #40250 · 157.6 AIC · ⌖ 7.46 AIC · ⊞ 4.5K ·

… fails

When the current token (e.g. an enterprise SAML-enforced token) cannot
access cross-organization public repositories, the update command now
falls back to unauthenticated GitHub public API calls instead of
failing immediately.

Affected functions in pkg/cli/update_workflows.go:
- getRepoDefaultBranch
- getLatestBranchCommitSHA
- defaultWorkflowUpdateDeps (releases API)

Affected functions in pkg/parser/remote_fetch.go:
- listDirSubdirsForHost
- downloadFileFromGitHubWithDepth
- listWorkflowFilesForHost
- listDirAllFilesForHost
- resolveRefToSHA

Each function now tries: authenticated API → git clone → unauthenticated
HTTP to api.github.com (for public repos).

Also includes action version bumps and container pin updates from the
update run.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 19, 2026 05:54
@github-actions

github-actions Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Comment Memory

CI lights the path
Green checks bloom at dawn
Quiet bots still sing

Note

This comment is managed by comment memory.

It stores persistent context for this thread in the code block at the top of this comment.
Edit only the text inside the backtick fences; workflow metadata and the footer are regenerated automatically.

Learn more about comment memory

Generated by 🧪 Smoke CI for issue #40250 ·

UpdateContainerPins runs after compilation, so lock files were missing
the @sha256: digest suffixes on container images. Now
UpdateContainerPins returns whether new pins were added, and both
update and upgrade commands recompile all workflows when new container
pins are stored.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR improves gh aw update resilience when the active GH_TOKEN is SAML-enforced / cross-org restricted by adding an unauthenticated GitHub.com REST API fallback after authenticated API and git-based fallbacks fail. It also refreshes pinned action versions (notably actions/checkout@v7) and updates various generated lock/pin artifacts produced by the update run.

Changes:

  • Add unauthenticated api.github.com fallback logic for repo/branch/release lookups in the update command and for remote workflow/file/ref fetching in the parser.
  • Bump pinned action versions (e.g., actions/checkout@v7.0.0, actions/setup-java@v5.3.0, github/stale-repos@v9.0.15) and update workflow sources accordingly.
  • Refresh generated .lock.yml workflows and action/container pin JSON artifacts.
Show a summary per file
File Description
pkg/cli/update_workflows.go Adds unauthenticated GitHub.com REST fallback for repo default-branch, branch head SHA, and releases listing when auth fails.
pkg/parser/remote_fetch.go Adds unauthenticated GitHub.com REST fallback paths for ref resolution and contents-based listing/download after auth+git fallbacks fail.
pkg/actionpins/data/action_pins.json Updates embedded action pins; container pins were pruned too aggressively (blocking issue).
pkg/workflow/data/action_pins.json Updates action pins data used by workflow compilation; container pins were pruned too aggressively (blocking issue).
.github/aw/actions-lock.json Updates repo action/cache pin data and container pins cache produced by the update run.
.github/workflows/visual-regression-checker.md Bumps actions/checkout to v7.0.0 in workflow source.
.github/workflows/visual-regression-checker.lock.yml Regenerates compiled lock workflow with updated pins.
.github/workflows/update-astro.md Bumps actions/checkout to v7.0.0 in workflow source.
.github/workflows/update-astro.lock.yml Regenerates compiled lock workflow with updated pins.
.github/workflows/unbloat-docs.md Bumps actions/checkout to v7.0.0 in workflow source.
.github/workflows/unbloat-docs.lock.yml Regenerates compiled lock workflow with updated pins.
.github/workflows/super-linter.md Bumps actions/checkout to v7.0.0 in workflow source.
.github/workflows/super-linter.lock.yml Regenerates compiled lock workflow with updated pins.
.github/workflows/stale-repo-identifier.md Bumps github/stale-repos to v9.0.15 in workflow source.
.github/workflows/stale-repo-identifier.lock.yml Regenerates compiled lock workflow with updated pins.
.github/workflows/shared/mcp-debug.md Bumps actions/checkout to v7.0.0 in workflow source.
.github/workflows/release.md Bumps actions/checkout to v7.0.0 in workflow source.
.github/workflows/release.lock.yml Regenerates compiled lock workflow with updated pins.
.github/workflows/hourly-ci-cleaner.md Bumps actions/checkout to v7.0.0 in workflow source.
.github/workflows/hourly-ci-cleaner.lock.yml Regenerated lock; contains incorrect checkout version metadata (blocking issue).
.github/workflows/go-pattern-detector.md Bumps actions/checkout to v7.0.0 in workflow source.
.github/workflows/go-pattern-detector.lock.yml Regenerates compiled lock workflow with updated pins.
.github/workflows/daily-skill-optimizer.md Bumps actions/checkout to v7.0.0 in workflow source.
.github/workflows/daily-skill-optimizer.lock.yml Regenerates compiled lock workflow with updated pins.
.github/workflows/daily-geo-optimizer.md Bumps actions/checkout to v7.0.0 in workflow source.
.github/workflows/daily-geo-optimizer.lock.yml Regenerates compiled lock workflow with updated pins.
.github/workflows/daily-astrostylelite-markdown-spellcheck.md Bumps actions/checkout to v7.0.0 in workflow source.
.github/workflows/daily-astrostylelite-markdown-spellcheck.lock.yml Regenerates compiled lock workflow with updated pins.
.github/workflows/copilot-centralization-optimizer.md Pins actions/upload-artifact to v7.0.1 in workflow source.
.github/workflows/copilot-centralization-optimizer.lock.yml Regenerates compiled lock workflow with updated pins.
.github/workflows/avenger.md Bumps actions/checkout to v7.0.0 in workflow source.
.github/workflows/avenger.lock.yml Regenerated lock; contains incorrect checkout version metadata (blocking issue).

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 35/35 changed files
  • Comments generated: 14

Comment thread pkg/parser/remote_fetch.go Outdated
Comment on lines +1313 to +1314
endpoint := fmt.Sprintf("https://api.github.com/repos/%s/%s/contents/%s?ref=%s",
owner, repo, url.PathEscape(path), url.QueryEscape(ref))
Comment thread pkg/parser/remote_fetch.go Outdated
Comment on lines +528 to +529
remoteLog.Printf("Git fallback also failed, attempting unauthenticated API for %s/%s@%s", owner, repo, ref)
return resolveRefToSHAViaPublicAPI(owner, repo, ref)
Comment thread pkg/parser/remote_fetch.go Outdated
Comment on lines +920 to +921
remoteLog.Printf("Git fallback also failed, attempting unauthenticated API for %s/%s/%s@%s", owner, repo, path, ref)
return downloadFileViaPublicAPI(owner, repo, path, ref)
Comment thread pkg/parser/remote_fetch.go Outdated
Comment on lines +1051 to +1052
remoteLog.Printf("Git fallback also failed, attempting unauthenticated API for %s/%s@%s", owner, repo, ref)
return listWorkflowFilesViaPublicAPI(owner, repo, ref, workflowPath)
Comment thread pkg/parser/remote_fetch.go Outdated
Comment on lines +1110 to +1111
remoteLog.Printf("Git fallback also failed, attempting unauthenticated API for %s/%s@%s", owner, repo, ref)
return listDirAllFilesViaPublicAPI(owner, repo, ref, dirPath)
Comment thread .github/workflows/hourly-ci-cleaner.lock.yml Outdated
Comment thread .github/workflows/hourly-ci-cleaner.lock.yml Outdated
Comment thread .github/workflows/avenger.lock.yml Outdated
# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"59322418e4416285cdf6f2652d03ef51d831d150733af0dc3d593ab4c01f9270","body_hash":"4d51ef429f578f6a81277bb5b7fb6972093ac204c21c50b06168c848812076b4","strict":true,"agent_id":"claude","engine_versions":{"claude":"2.1.179"}}
# gh-aw-manifest: {"version":1,"secrets":["ANTHROPIC_API_KEY","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/cache/save","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/checkout","sha":"df4cb1c069e1874edd31b4311f1884172cec0e10","version":"v6.0.3"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"4a3601121dd01d1626a1e23e37211e3254c1c06c","version":"v6.4.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.7"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.3.0","digest":"sha256:5c83359327a0bacc3d34db730bea6557d39d341cee0bf6c58c9a896e33150e80","pinned_image":"ghcr.io/github/github-mcp-server:v1.3.0@sha256:5c83359327a0bacc3d34db730bea6557d39d341cee0bf6c58c9a896e33150e80"}]}
# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"25cb376cd027add89e92da99ef0c8b00edfe0fbb21dfb12f16f88af3ea7c52cd","body_hash":"4d51ef429f578f6a81277bb5b7fb6972093ac204c21c50b06168c848812076b4","strict":true,"agent_id":"claude","engine_versions":{"claude":"2.1.179"}}
# gh-aw-manifest: {"version":1,"secrets":["ANTHROPIC_API_KEY","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/cache/save","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0"},{"repo":"actions/checkout","sha":"df4cb1c069e1874edd31b4311f1884172cec0e10","version":"v6.0.3"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"4a3601121dd01d1626a1e23e37211e3254c1c06c","version":"v6.4.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.7"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.3.0","digest":"sha256:5c83359327a0bacc3d34db730bea6557d39d341cee0bf6c58c9a896e33150e80","pinned_image":"ghcr.io/github/github-mcp-server:v1.3.0@sha256:5c83359327a0bacc3d34db730bea6557d39d341cee0bf6c58c9a896e33150e80"}]}
Comment thread .github/workflows/avenger.lock.yml Outdated
Comment thread .github/workflows/avenger.lock.yml Outdated
@pelikhan

Copy link
Copy Markdown
Collaborator Author

@copilot run pr-finisher skill

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…, fix path escaping, restore container pins

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

@copilot run pr-finisher skill

Addressed all open review threads in commit fix: address review feedback - gate public API fallback to github.com, fix path escaping, restore container pins:

  1. Path escapingfetchPublicGitHubContentsAPI now encodes each path segment individually so / separators are preserved (was using url.PathEscape on the whole path which turned / into %2F)
  2. GHES host guard — all 5 unauthenticated public API fallback call sites now check host == "" || host == "github.com" before calling api.github.com; GHES instances fall back to the original combined API+git error instead
  3. Baseline container pins — restored ~79 missing pins in .github/aw/actions-lock.json (the source of truth) by merging the original baseline with the new 0.27.7 firewall pins; make sync-action-pins propagated these to both pkg/actionpins/data/action_pins.json and pkg/workflow/data/action_pins.json; wasm golden files regenerated to reference actions/checkout@v7.0.0

make agent-report-progress (build + fmt + lint + test-unit) passes locally. CI must be re-triggered by a maintainer before merge.

@github-actions

Copy link
Copy Markdown
Contributor

Hey @pelikhan 👋 — thanks for tackling the SAML token fallback! The three-tier retry strategy (authenticated API → git clone → unauthenticated HTTP) is a solid approach for handling enterprise SAML restrictions on public repos, and the review feedback addressing the GHES host guard and path-escaping fixes is exactly the right kind of hardening.

A couple of things that would make this easier to land cleanly:

  • Split the lock-file updates — 270+ .lock.yml / actions-lock.json changes from the update run are bundled with ~380 lines of Go logic changes. Reviewers can't easily isolate the authentication fix from the automated pin bumps. Consider opening a follow-up PR (or squashing the lock bumps onto a separate branch) so the code changes can be reviewed on their own merits.
  • Add tests for the unauthenticated fallback pathgetRepoDefaultBranch, getLatestBranchCommitSHA, listDirSubdirsForHost, and friends now have a third code path that isn't covered by any test file. Even a simple table-driven unit test that mocks a 401/403 response and asserts the function retries unauthenticated would catch regressions here.

If you'd like a hand, you can assign this prompt to your coding agent:

Add unit tests for the unauthenticated public API fallback logic introduced in pkg/cli/update_workflows.go and pkg/parser/remote_fetch.go.

Cover the following scenarios for at least one function from each file (e.g. getRepoDefaultBranch and resolveRefToSHA):
1. Authenticated API succeeds — unauthenticated path is never reached.
2. Authenticated API returns a 401/403 (SAML-blocked) — function retries via unauthenticated api.github.com and succeeds.
3. Authenticated API returns a 401/403 on a GHES host (non github.com) — function does NOT fall back to api.github.com and returns the original error.
4. Both authenticated and unauthenticated calls fail — function returns a combined error.

Use httptest.NewServer or a similar mock to avoid real network calls.

Generated by ✅ Contribution Check · 152.4 AIC · ⌖ 10.5 AIC · ⊞ 5.9K ·

@pelikhan pelikhan merged commit f073cbe into main Jun 19, 2026
28 checks passed
@pelikhan pelikhan deleted the update-saml-fix branch June 19, 2026 13:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants