Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions packages/glob/__tests__/hash-files.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,46 @@ describe('globber', () => {
}
})

it('honors an explicit root outside the workspace without the opt-in', async () => {
// Mirrors the documented GITHUB_ACTION_PATH usage: an action passes its own
// directory as an allowed root, and that directory is not under the workspace.
const insideRoot = path.join(getTestTemp(), 'explicit-root-inside')
await fs.mkdir(insideRoot, {recursive: true})
await fs.writeFile(path.join(insideRoot, 'inside.txt'), 'inside content')

// Outside GITHUB_WORKSPACE (which the suite pins to __dirname).
const actionRoot = await fs.mkdtemp(
path.join(os.tmpdir(), 'hash-files-explicit-root-')
)
try {
await fs.writeFile(path.join(actionRoot, 'action.txt'), 'action content')

const patterns = `${insideRoot}/*\n${actionRoot}/*`

const insideOnly = await hashFiles(`${insideRoot}/*`, '', {
roots: [insideRoot]
})
expect(insideOnly).not.toEqual('')

// The explicit roots list is the allowlist, so files under actionRoot
// must be hashed even though allowFilesOutsideWorkspace is not set.
const both = await hashFiles(patterns, '', {
roots: [insideRoot, actionRoot]
})
expect(both).not.toEqual('')
expect(both).not.toEqual(insideOnly)

// And it hashes exactly those two files, no more.
const expected = await hashFiles(patterns, '', {
roots: [insideRoot, actionRoot],
allowFilesOutsideWorkspace: true
})
expect(both).toEqual(expected)
} finally {
await io.rmRF(actionRoot)
}
})

it('applies relative exclude patterns across all allowed roots', async () => {
const root = path.join(getTestTemp(), 'exclude-across-roots')
const dir1 = path.join(root, 'dir1')
Expand Down
8 changes: 7 additions & 1 deletion packages/glob/src/internal-hash-files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,12 @@ export async function hashFiles(
const excludeMatchers = buildExcludeMatchers(options?.exclude ?? [])

// Resolve roots up front; warn and skip any that fail to resolve.
// If allowFilesOutsideWorkspace is not enabled, roots are restricted to the resolved workspace.
// When the caller did not specify roots, the workspace is the only allowed
// root and `allowFilesOutsideWorkspace` is the opt-in that widens it.
// An explicit `roots` list is itself the allowlist, so its entries are
// honored as given - otherwise a root outside the workspace would be
// dropped here and the files under it silently skipped.
const explicitRoots = options?.roots !== undefined
const resolvedRootsSet = new Set<string>()
const roots = options?.roots ?? [resolvedWorkspace]

Expand All @@ -123,6 +128,7 @@ export async function hashFiles(
root === resolvedWorkspace ? root : fs.realpathSync(root)

if (
!explicitRoots &&
!allowOutside &&
!isInResolvedRoots(resolvedRoot, [resolvedWorkspace])
) {
Expand Down