Skip to content

Commit 4af395d

Browse files
Merge #7403: ci: hand built depends to source jobs via artifacts
7b8682d ci: hand freshly built depends to src jobs via artifact on PR runs (PastaClaw) Pull request description: ## Summary GitHub changed Actions cache behavior on June 26, 2026: untrusted triggers now receive read-only Actions cache tokens. See GitHub's changelog entry, ["Read-only Actions cache for untrusted triggers"](https://github.blog/changelog/2026-06-26-read-only-actions-cache-for-untrusted-triggers/). For `pull_request_target` runs, a depends job can still build dependencies successfully, but `actions/cache/save` only warns when the token cannot reserve/write the cache. The later source-build jobs then fail if their depends cache restore misses. This PR keeps `build.yml` least-privileged (`actions: read`) and adds a same-run handoff for freshly built depends trees: - trusted runs still save and reuse the persistent Actions cache; - `pull_request_target` runs skip the denied cache save path; - fresh depends builds are uploaded as short-lived artifacts; - source jobs first try the cache, then download the artifact on cache miss; - source jobs fail only if both the cache and the artifact are unavailable. ## Why The old workflow assumed that if a depends job built `depends/built/<host>`, the result could be saved to the Actions cache for downstream source jobs. That is no longer true for low-trust PR-triggered runs: restore may be allowed, while save is denied. The previous attempted fix granted `actions: write` at the top level. That was reverted because the workflow checks out and executes PR-controlled code, and writable Actions scope is the wrong security model for `pull_request_target`. ## Implementation - `.github/workflows/build-depends.yml` - exposes a new `built-artifact` output; - skips persistent cache save on `pull_request_target`; - uploads `depends/built/<host>` as `depends-built-<target>` when a fresh build was needed. - `.github/workflows/build-src.yml` - adds an optional `depends-artifact` input; - restores the persistent cache when present; - downloads the built-depends artifact when the cache misses; - errors only when there is no cache hit and no artifact to use. - `.github/workflows/build.yml` - forwards each depends job's `built-artifact` output to the matching source jobs; - documents why the workflow stays at `actions: read`. ## Current CI caveat The PR's own `pull_request_target` run cannot fully validate these reusable workflow changes yet, because GitHub runs reusable workflows from the base branch in this context. The current failing run shows source jobs using: ```text dashpay/dash/.github/workflows/build-src.yml@refs/heads/develop ``` That means the red source jobs are still using `develop`'s old `fail-on-cache-miss: true` restore path, not this PR's artifact fallback. ## References - [GitHub Changelog: Read-only Actions cache for untrusted triggers](https://github.blog/changelog/2026-06-26-read-only-actions-cache-for-untrusted-triggers/) - [GitHub dependency caching reference](https://docs.github.com/en/actions/reference/workflows-and-actions/dependency-caching) - [actions/cache README](https://github.com/actions/cache) ## Test plan - [ ] After merge, trigger or observe a `pull_request_target` run that needs a fresh depends build. - [ ] Confirm depends jobs skip the denied persistent cache save on `pull_request_target`. - [ ] Confirm those depends jobs upload `depends-built-<target>` artifacts. - [ ] Confirm source jobs restore from cache when available. - [ ] Confirm source jobs download the built-depends artifact when the cache misses. - [ ] Confirm trusted `push` runs still save persistent depends caches. ACKs for top commit: UdjinM6: utACK 7b8682d Tree-SHA512: b1ebcf2f9be78297806a3350a764b9c3074afff560675fcbe6364ee7c7890e47f012c784ccee1e8c94a17404756bdad892195308fbfa9b8319c78fd134bdf15c
2 parents b77d87c + 7b8682d commit 4af395d

3 files changed

Lines changed: 55 additions & 1 deletion

File tree

.github/workflows/build-depends.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ on:
2929
dep-opts:
3030
description: "DEP_OPTS used to build depends"
3131
value: ${{ jobs.check-cache.outputs.dep-opts }}
32+
built-artifact:
33+
description: "Artifact holding freshly built depends (empty when restored from cache)"
34+
value: ${{ jobs.build.outputs.built-artifact }}
3235

3336
jobs:
3437
check-cache:
@@ -100,6 +103,8 @@ jobs:
100103
needs: [check-cache]
101104
if: needs.check-cache.outputs.cache-hit != 'true'
102105
runs-on: ${{ inputs.runs-on }}
106+
outputs:
107+
built-artifact: depends-built-${{ inputs.build-target }}
103108
container:
104109
image: ${{ inputs.container-path }}
105110
options: --user root
@@ -139,7 +144,23 @@ jobs:
139144
env ${{ needs.check-cache.outputs.dep-opts }} make -j$(nproc) -C depends
140145
141146
- name: Save depends cache
147+
# Cache tokens on untrusted triggers are read-only (GitHub change,
148+
# June 2026), so only trusted events can save the persistent cache
149+
# for later read-only restores.
150+
if: github.event_name != 'pull_request_target'
142151
uses: actions/cache/save@v5
143152
with:
144153
path: depends/built/${{ needs.check-cache.outputs.host }}
145154
key: ${{ needs.check-cache.outputs.cache-key }}
155+
156+
- name: Upload built depends
157+
# Same-run handoff to build-src.yml for runs that cannot save the
158+
# cache. Uploaded on every fresh build (cache/save only warns when
159+
# the write is denied), so src jobs never depend on a save landing.
160+
uses: actions/upload-artifact@v6
161+
with:
162+
name: depends-built-${{ inputs.build-target }}
163+
path: depends/built/${{ needs.check-cache.outputs.host }}
164+
compression-level: 0
165+
retention-days: 1
166+
overwrite: true

.github/workflows/build-src.yml

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ on:
2424
required: false
2525
type: string
2626
default: ""
27+
depends-artifact:
28+
description: "Artifact holding freshly built depends, used if the cache restore misses"
29+
required: false
30+
type: string
31+
default: ""
2732
runs-on:
2833
description: "Runner label to use (e.g., ubuntu-24.04 or ubuntu-24.04-arm)"
2934
required: true
@@ -70,11 +75,29 @@ jobs:
7075
fail-on-cache-miss: true
7176

7277
- name: Restore depends cache
78+
id: depends-cache
7379
uses: actions/cache/restore@v5
7480
with:
7581
path: depends/built/${{ inputs.depends-host }}
7682
key: ${{ inputs.depends-key }}
77-
fail-on-cache-miss: true
83+
84+
- name: Download built depends
85+
# Same-run handoff from build-depends.yml: pull_request_target runs
86+
# have read-only cache tokens (GitHub change, June 2026), so freshly
87+
# built depends arrive as an artifact instead of a cache entry. Also
88+
# covers trusted runs whose cache save was denied (save only warns).
89+
if: steps.depends-cache.outputs.cache-hit != 'true' && inputs.depends-artifact != ''
90+
uses: actions/download-artifact@v8
91+
with:
92+
name: ${{ inputs.depends-artifact }}
93+
path: depends/built/${{ inputs.depends-host }}
94+
95+
- name: Check built depends are present
96+
if: steps.depends-cache.outputs.cache-hit != 'true' && inputs.depends-artifact == ''
97+
run: |
98+
echo "::error::Depends cache restore missed and no built depends artifact was provided"
99+
exit 1
100+
shell: bash
78101

79102
- name: Rebuild depends prefix
80103
run: |

.github/workflows/build.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ jobs:
202202
depends-key: ${{ needs.depends-aarch64-linux.outputs.key }}
203203
depends-host: ${{ needs.depends-aarch64-linux.outputs.host }}
204204
depends-dep-opts: ${{ needs.depends-aarch64-linux.outputs.dep-opts }}
205+
depends-artifact: ${{ needs.depends-aarch64-linux.outputs.built-artifact }}
205206
runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }}
206207

207208
src-linux64:
@@ -215,6 +216,7 @@ jobs:
215216
depends-key: ${{ needs.depends-linux64.outputs.key }}
216217
depends-host: ${{ needs.depends-linux64.outputs.host }}
217218
depends-dep-opts: ${{ needs.depends-linux64.outputs.dep-opts }}
219+
depends-artifact: ${{ needs.depends-linux64.outputs.built-artifact }}
218220
runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }}
219221

220222
src-linux64_fuzz:
@@ -228,6 +230,7 @@ jobs:
228230
depends-key: ${{ needs.depends-linux64.outputs.key }}
229231
depends-host: ${{ needs.depends-linux64.outputs.host }}
230232
depends-dep-opts: ${{ needs.depends-linux64.outputs.dep-opts }}
233+
depends-artifact: ${{ needs.depends-linux64.outputs.built-artifact }}
231234
runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }}
232235

233236
src-linux64_multiprocess:
@@ -241,6 +244,7 @@ jobs:
241244
depends-key: ${{ needs.depends-linux64_multiprocess.outputs.key }}
242245
depends-host: ${{ needs.depends-linux64_multiprocess.outputs.host }}
243246
depends-dep-opts: ${{ needs.depends-linux64_multiprocess.outputs.dep-opts }}
247+
depends-artifact: ${{ needs.depends-linux64_multiprocess.outputs.built-artifact }}
244248
runs-on: ${{ needs.check-skip.outputs['runner-arm64'] }}
245249

246250
src-linux64_nowallet:
@@ -253,6 +257,7 @@ jobs:
253257
depends-key: ${{ needs.depends-linux64_nowallet.outputs.key }}
254258
depends-host: ${{ needs.depends-linux64_nowallet.outputs.host }}
255259
depends-dep-opts: ${{ needs.depends-linux64_nowallet.outputs.dep-opts }}
260+
depends-artifact: ${{ needs.depends-linux64_nowallet.outputs.built-artifact }}
256261
runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }}
257262

258263
src-linux64_sqlite:
@@ -266,6 +271,7 @@ jobs:
266271
depends-key: ${{ needs.depends-linux64.outputs.key }}
267272
depends-host: ${{ needs.depends-linux64.outputs.host }}
268273
depends-dep-opts: ${{ needs.depends-linux64.outputs.dep-opts }}
274+
depends-artifact: ${{ needs.depends-linux64.outputs.built-artifact }}
269275
runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }}
270276

271277
src-linux64_tsan:
@@ -279,6 +285,7 @@ jobs:
279285
depends-key: ${{ needs.depends-linux64_multiprocess.outputs.key }}
280286
depends-host: ${{ needs.depends-linux64_multiprocess.outputs.host }}
281287
depends-dep-opts: ${{ needs.depends-linux64_multiprocess.outputs.dep-opts }}
288+
depends-artifact: ${{ needs.depends-linux64_multiprocess.outputs.built-artifact }}
282289
runs-on: ${{ needs.check-skip.outputs['runner-arm64'] }}
283290

284291
src-linux64_ubsan:
@@ -292,6 +299,7 @@ jobs:
292299
depends-key: ${{ needs.depends-linux64.outputs.key }}
293300
depends-host: ${{ needs.depends-linux64.outputs.host }}
294301
depends-dep-opts: ${{ needs.depends-linux64.outputs.dep-opts }}
302+
depends-artifact: ${{ needs.depends-linux64.outputs.built-artifact }}
295303
runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }}
296304

297305
src-mac:
@@ -304,6 +312,7 @@ jobs:
304312
depends-key: ${{ needs.depends-mac.outputs.key }}
305313
depends-host: ${{ needs.depends-mac.outputs.host }}
306314
depends-dep-opts: ${{ needs.depends-mac.outputs.dep-opts }}
315+
depends-artifact: ${{ needs.depends-mac.outputs.built-artifact }}
307316
runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }}
308317

309318
src-win64:
@@ -316,6 +325,7 @@ jobs:
316325
depends-key: ${{ needs.depends-win64.outputs.key }}
317326
depends-host: ${{ needs.depends-win64.outputs.host }}
318327
depends-dep-opts: ${{ needs.depends-win64.outputs.dep-opts }}
328+
depends-artifact: ${{ needs.depends-win64.outputs.built-artifact }}
319329
runs-on: ${{ needs.check-skip.outputs['runner-amd64'] }}
320330

321331
test-linux64:

0 commit comments

Comments
 (0)