Skip to content

Allow an exact shared cache key written only by protected pushes - #269

Merged
rmcdaniel merged 1 commit into
mainfrom
ci/trusted-main-cache-restore-only
Oct 27, 2026
Merged

rmcdaniel merged 1 commit into
mainfrom
ci/trusted-main-cache-restore-only

Conversation

@rmcdaniel

@rmcdaniel rmcdaniel commented Oct 27, 2026 •

Copy link
Copy Markdown
Member

Why

The server's Shared Server fixtures spend about 4 of every run's critical-path minutes building the same pinned, --locked SDK adapter dependencies. Every fixture job waits for them. Caching the adapters' Cargo registry and build tree would save that time, but the central policy requires ${{ github.event_name }} in every cache key of a pull-request workflow. A cache saved only by pushes to main could then never be read by pull request or merge group runs, which are the runs it is meant to speed up.

The partition prevents untrusted writes from reaching trusted reads. The opposite direction is safe: trusted pushes to main write, and pull request and merge group runs only read. This exception is limited to restore-only consumers and saves that require a protected-branch push, with positive and negative policy tests.

Change

In a pull-request workflow, an exact key without the event partition is allowed only on:

  • actions/cache/restore with no restore-keys (a broad prefix could substitute stale output);
  • actions/cache/save whose if is a top-level conjunction requiring both github.event_name == 'push' and github.ref == '<protected ref>'. Other terms may narrow it further, such as success() or a cache-miss check.

Everything else is unchanged and still needs the partition:

  • the combined actions/cache action;
  • any restore-keys;
  • a save that another event or branch can run.

Tests

python -m unittest tests.test_qualification_policy: 23 tests pass, two of them new.

  • Positive: a restore and save pair with an exact key passes, including with the save condition's terms reordered and parenthesized.
  • Negative: each of these is still refused with "must partition trusted and untrusted events":
    • adding restore-keys;
    • dropping the push or protected-ref term, or the whole guard;
    • || in place of &&;
    • another branch;
    • != 'pull_request' in place of == 'push';
    • the combined actions/cache action.

The server-side change, with its key computed by a classified scripts/ci helper, follows once this lands.

🤖 Generated with Claude Code

In a pull-request workflow every actions/cache key must name
github.event_name, so a cache written by one event is never read by
another. That rule also forbids the safe direction: a cache that only
pushes to the protected branch save, which pull request and merge group
runs only restore.

Allow an exact, unpartitioned key on two steps:
- actions/cache/restore with no restore-keys;
- actions/cache/save whose condition is a top-level conjunction requiring
  github.event_name == 'push' and github.ref == the protected ref.
The combined actions/cache action, any restore prefix, and a save any
other event or branch can run still need the event partition.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@rmcdaniel

rmcdaniel commented Oct 27, 2026 •

Copy link
Copy Markdown
Member Author

Reviewed exact head cae4f3f2e2a2bc698350a6fce918bfc8b89a45f3: the new exception permits only restore consumers or saves with both protected-ref and push predicates in a top-level conjunction. The existing action/path checks remain in force. The negative tests retain event partitioning for combined restore/save, broad restore prefixes, disjunctions and missing/wrong guards. All seven hosted checks are green, including shared contract tests and the genuine SonarCloud check.

This follows GitHub's cache scope rules: default-branch consumers cannot restore caches from child branches. The consuming workflow must still cover all relevant source, lock, toolchain and platform inputs in its key. Skip a build only on an exact cache hit, and keep ordinary build validation on a miss.

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.

1 participant