Skip to content

Commit b27032f

Browse files
authored
fix(cache): display server-provided skipped message (#113)
* fix(cache): display server-provided skipped message in cache step Use the new `skipped_message` field from the StoreResponse proto when available, falling back to the existing hardcoded message for backward compatibility with older controllers. * docs(cache): clarify cache write behavior with read-only tokens Document that cache writes require read-write tokens, and that read-only tokens on PRs are expected — cache entries are written by main branch workflows and restored on PRs. * docs(cache): update base-branch and cache write docs for scoped tokens Rewrite base-branch section to reflect that PR branches typically use read-only tokens and cannot write to cache. Consolidate with the cache writes section to avoid duplication. Add note about using read-write tokens on non-protected branches.
1 parent 181c659 commit b27032f

5 files changed

Lines changed: 54 additions & 14 deletions

File tree

workflow-steps/cache/README.md

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,21 +58,29 @@ paths: |
5858

5959
## `base-branch`
6060

61-
For security reasons, this step will only write cache entries **for the current branch only**. This isolation is
62-
essential, for example, for open source projects, where anyone can create PRs and potentially push malicious artefacts
63-
to the cache.
61+
By default, the cache step will only attempt to restore caches written **by the current branch**. This means each
62+
branch's cache is isolated from other branches.
6463

65-
So by default, if you do not pass the `base-branch`, the cache step will only attempt to restore caches written **by the
66-
current branch only**. This can be fine, as any subsequent pushes to your PR will re-use the cache, and all the commits
67-
that go into your
68-
`main` branch will also re-use the cache.
69-
70-
However, for an extra optimisation, we recommend setting:
64+
Setting the `base-branch` input allows a branch to fall back to cache entries from another branch when no match is
65+
found for the current one:
7166

7267
```yaml
7368
base-branch: 'main' # or another branch
7469
```
7570

76-
This will ensure that when you first open a PR, if a cached entry isn't found for the current branch, it will try to
77-
look at entries
78-
on your default protected branch (usually `main`).
71+
This is especially useful when combined with
72+
[scoped access tokens](https://nx.dev/docs/concepts/ci-concepts/cache-security#use-scoped-tokens-in-ci) (the
73+
recommended setup). In this configuration, your protected branch (e.g. `main`) uses a read-write token and writes
74+
cache entries, while PR branches use read-only tokens and can only restore from the cache. Setting `base-branch`
75+
ensures PR workflows can still benefit from cache entries written by `main`.
76+
77+
> **Note:** If you use read-write tokens on non-protected branches, each branch will also write its own cache
78+
> entries, enabling cache reuse across subsequent pushes to the same PR. However, this is **not recommended** as it
79+
> allows any PR to write to the shared cache, which is a security risk — especially for open source projects where
80+
> anyone can open a PR and potentially push malicious artifacts.
81+
82+
## Cache writes and access token permissions
83+
84+
The cache step requires a **read-write** access token to write cache entries. If your CI access token only has **read**
85+
permissions, cache restores will work normally but cache uploads will be skipped with a message indicating the
86+
token does not have write permissions.

workflow-steps/cache/generated_protos/cache_pb.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,11 @@ export class StoreResponse extends Message<StoreResponse> {
210210
*/
211211
skipped = false;
212212

213+
/**
214+
* @generated from field: string skipped_message = 3;
215+
*/
216+
skippedMessage = '';
217+
213218
constructor(data?: PartialMessage<StoreResponse>) {
214219
super();
215220
proto3.util.initPartial(data, this);
@@ -220,6 +225,12 @@ export class StoreResponse extends Message<StoreResponse> {
220225
static readonly fields: FieldList = proto3.util.newFieldList(() => [
221226
{ no: 1, name: 'success', kind: 'scalar', T: 8 /* ScalarType.BOOL */ },
222227
{ no: 2, name: 'skipped', kind: 'scalar', T: 8 /* ScalarType.BOOL */ },
228+
{
229+
no: 3,
230+
name: 'skipped_message',
231+
kind: 'scalar',
232+
T: 9 /* ScalarType.STRING */,
233+
},
223234
]);
224235

225236
static fromBinary(

workflow-steps/cache/output/main.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3510,6 +3510,10 @@ var StoreResponse = class _StoreResponse extends Message {
35103510
* @generated from field: bool skipped = 2;
35113511
*/
35123512
skipped = false;
3513+
/**
3514+
* @generated from field: string skipped_message = 3;
3515+
*/
3516+
skippedMessage = "";
35133517
constructor(data) {
35143518
super();
35153519
proto3.util.initPartial(data, this);
@@ -3530,6 +3534,12 @@ var StoreResponse = class _StoreResponse extends Message {
35303534
kind: "scalar",
35313535
T: 8
35323536
/* ScalarType.BOOL */
3537+
},
3538+
{
3539+
no: 3,
3540+
name: "skipped_message",
3541+
kind: "scalar",
3542+
T: 9
35333543
}
35343544
]);
35353545
static fromBinary(bytes, options) {

workflow-steps/cache/output/post.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3498,6 +3498,10 @@ var StoreResponse = class _StoreResponse extends Message {
34983498
* @generated from field: bool skipped = 2;
34993499
*/
35003500
skipped = false;
3501+
/**
3502+
* @generated from field: string skipped_message = 3;
3503+
*/
3504+
skippedMessage = "";
35013505
constructor(data) {
35023506
super();
35033507
proto3.util.initPartial(data, this);
@@ -3518,6 +3522,12 @@ var StoreResponse = class _StoreResponse extends Message {
35183522
kind: "scalar",
35193523
T: 8
35203524
/* ScalarType.BOOL */
3525+
},
3526+
{
3527+
no: 3,
3528+
name: "skipped_message",
3529+
kind: "scalar",
3530+
T: 9
35213531
}
35223532
]);
35233533
static fromBinary(bytes, options) {
@@ -10289,7 +10299,8 @@ if (!!cacheWasHit) {
1028910299
Successfully uploaded marked directories.`);
1029010300
if (r.skipped)
1029110301
console.log(
10292-
"\nSkipped storing to cache, another instance has already started the upload."
10302+
`
10303+
${r.skippedMessage || "Skipped storing to cache, another instance has already started the upload."}`
1029310304
);
1029410305
process.exit(0);
1029510306
}).catch((err) => {

workflow-steps/cache/post.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ if (!!cacheWasHit) {
4646
if (r.success) console.log(`\nSuccessfully uploaded marked directories.`);
4747
if (r.skipped)
4848
console.log(
49-
'\nSkipped storing to cache, another instance has already started the upload.',
49+
`\n${r.skippedMessage || 'Skipped storing to cache, another instance has already started the upload.'}`,
5050
);
5151
process.exit(0);
5252
})

0 commit comments

Comments
 (0)