Conversation
`hashFiles` dropped any entry of an explicit `roots` list that did not resolve under the workspace unless `allowFilesOutsideWorkspace` was also set. The documented `roots: [GITHUB_WORKSPACE, GITHUB_ACTION_PATH]` usage therefore silently hashed only the workspace half, and callers who added the opt-in to work around it widened the allowlist to every matched file. An explicit `roots` list is itself the allowlist, so only apply the workspace restriction when the caller did not supply one.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
@actions/glob'shashFiles()discards any entry of an explicitrootslist that does not resolve underGITHUB_WORKSPACE, unless the unrelatedallowFilesOutsideWorkspaceflag is also set. The discard itself is logged atcore.debugonly.rootsexists to provide and a root outside the workspace — an action hashing its own files (GITHUB_ACTION_PATH) alongside the workspace is the motivating case — has no way to express it. The declared root is dropped and the hash covers only the workspace half. For a cache key that means a permanent miss, or a key shared by two genuinely different inputs.core.warningnames each skipped file), but the only remedy that warning suggests,allowFilesOutsideWorkspace: true, disables the root check for every matched file, so the caller gets a wider allowlist than they asked for. Correct containment plus an outside root is unreachable onmain.rootslist is the allowlist, so the workspace restriction only applies when the caller did not supply one.allowFilesOutsideWorkspacekeeps its documented meaning for the default-roots case and for the per-file check.@actions/glob: extend hashFiles options, merged 2026-07-14), which addedrootsand this restriction together. 8 lines changed insrc; 5 regression tests (3 discriminating + 2 controls) added next to the existingrootstests.Decisions
Why this is the minimal correct change. The defect is one conjunct in one predicate.
explicitRootsdistinguishes the two cases the code was conflating: with norootsgiven,rootsdefaults to[resolvedWorkspace], the workspace is the only allowed root, andallowFilesOutsideWorkspaceis the documented opt-in that widens it — unchanged. Withrootsgiven, the caller has stated the allowlist, so it is honored.Everything downstream is untouched: the per-file
isInResolvedRootscheck, theallowOutsideper-file branch, the outside-root warning, dedup viaresolvedRootsSet, and therealpathSyncfailure path all behave exactly as before. Containment is not weakened — a file under no declared root is still skipped without the opt-in.The documented contract is unchanged: the README's option text already says "Only files that resolve under (or equal) one of these roots are hashed" and "Explicit opt-in to include files outside the specified root path(s)" — root path(s), not workspace. The prose needed no edit; only the guard did not match it.
options?.roots !== undefinedrather than a truthiness test is kept because it states the intent (was the option supplied?) and is robust if the declared type ever admitsnull; it is not load-bearing for present behaviour since[]is truthy in JS and onlynulldistinguishes the two forms, which?? [resolvedWorkspace]sends to the default either way.Alternatives rejected: emitting a warning while keeping the behaviour doesn't help, since a warning already exists and the problem is that the documented
GITHUB_ACTION_PATHusage remains impossible without over-widening; restricting against the union of the workspace and all supplied roots is self-referential and reduces to this fix with more code; documenting the flag as required alongside outside-workspace roots is exactly the compromise this fix removes, since the opt-in is all-or-nothing per file and makes containment unavailable to the callers who most need it.Also noted, deliberately not in this PR:
packages/cache/package.jsonstill declares"@actions/glob": "^0.6.1", a range that predates this fix — a separate dependency-bump change, one bug per PR.Not run: the full monorepo test suite (
npm testat root) — scoped topackages/globper the repository's own guidance for package-level changes;npm run formatandnpm run lintwere run and are clean.AI assistance: this bug was found and the fix and tests were drafted with AI tooling in my workflow; the tests and checks above were executed as pasted. I'm responsible for the change and will handle review feedback.