Skip to content

Commit 928c723

Browse files
authored
Ignore Git-excluded files during workspace publication (#1845)
1 parent 4d62db3 commit 928c723

2 files changed

Lines changed: 44 additions & 17 deletions

File tree

packages/runtime-core/src/runner-workspace-apply.ts

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -235,25 +235,24 @@ function validatePatchPaths(patch: string, changed: RunnerWorkspaceChangedFile[]
235235

236236
async function snapshotWorkspace(root: string): Promise<RunnerWorkspacePublicationFile[]> {
237237
const output: RunnerWorkspacePublicationFile[] = []
238-
async function visit(directory: string): Promise<void> {
239-
for (const entry of await readdir(directory, { withFileTypes: true })) {
240-
if (entry.name === ".git" || entry.name === ".codebox") continue
241-
const absolute = resolve(directory, entry.name)
242-
const path = relative(root, absolute).replaceAll("\\", "/")
243-
const stat = await lstat(absolute)
244-
if (stat.isSymbolicLink()) throw new Error(`Runner workspace contains an unsupported path type: ${path}`)
245-
if (stat.isDirectory()) {
246-
await visit(absolute)
247-
continue
248-
}
249-
if (!stat.isFile()) throw new Error(`Runner workspace contains an unsupported path type: ${path}`)
250-
const mode = (stat.mode & 0o111) ? "100755" : "100644"
251-
const bytes = await readFile(absolute)
252-
output.push({ path, mode, content: bytes.toString("base64"), sha256: createHash("sha256").update(bytes).digest("hex"), deleted: false })
238+
const { stdout } = await execFileAsync("git", ["ls-files", "--cached", "--others", "--exclude-standard", "-z"], { cwd: root, maxBuffer: MAX_PATCH_BYTES })
239+
const paths = stdout.split("\0").filter(Boolean).sort((left, right) => left.localeCompare(right))
240+
for (const path of paths) {
241+
const absolute = resolve(root, path)
242+
if (!pathIsWithinRoot(absolute, root)) throw new Error(`Runner workspace contains a denied path: ${path}`)
243+
let stat
244+
try {
245+
stat = await lstat(absolute)
246+
} catch (error) {
247+
if ((error as NodeJS.ErrnoException).code === "ENOENT") continue
248+
throw error
253249
}
250+
if (stat.isSymbolicLink() || !stat.isFile()) throw new Error(`Runner workspace contains an unsupported path type: ${path}`)
251+
const mode = (stat.mode & 0o111) ? "100755" : "100644"
252+
const bytes = await readFile(absolute)
253+
output.push({ path, mode, content: bytes.toString("base64"), sha256: createHash("sha256").update(bytes).digest("hex"), deleted: false })
254254
}
255-
await visit(root)
256-
return output.sort((a, b) => a.path.localeCompare(b.path))
255+
return output
257256
}
258257

259258
function validateAppliedWorkspace(baseline: RunnerWorkspacePublicationFile[], current: RunnerWorkspacePublicationFile[], changed: RunnerWorkspaceChangedFile[]): void {

tests/runner-workspace-apply.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,34 @@ const files = [{ path: "/workspace/README.md", relativePath: "README.md", status
111111
await assert.rejects(() => verifyRunnerWorkspaceIntegrity(result.integrity!), /changed after approval/)
112112
}
113113

114+
{
115+
const input = await fixture(patch, files)
116+
await writeFile(join(input.workspace, ".gitignore"), "node_modules/\n")
117+
await exec("git", ["add", ".gitignore"], { cwd: input.workspace })
118+
await exec("git", ["commit", "--quiet", "-m", "ignore dependencies"], { cwd: input.workspace })
119+
const result = await applyRunnerWorkspacePatch({
120+
artifactRoot: input.artifacts,
121+
artifactRefs: input.refs,
122+
workspaceRoot: input.workspace,
123+
writablePaths: ["README.md"],
124+
verify: async () => {
125+
const packageLinks = join(input.workspace, "node_modules", ".pnpm", "hono", "node_modules")
126+
await mkdir(packageLinks, { recursive: true })
127+
await symlink(input.artifacts, join(packageLinks, "hono"))
128+
},
129+
})
130+
await verifyRunnerWorkspaceIntegrity(result.integrity!)
131+
}
132+
133+
{
134+
const input = await fixture(patch, files)
135+
await symlink(input.artifacts, join(input.workspace, "publishable-link"))
136+
await assert.rejects(
137+
() => applyRunnerWorkspacePatch({ artifactRoot: input.artifacts, artifactRefs: input.refs, workspaceRoot: input.workspace, writablePaths: ["README.md"] }),
138+
/unsupported path type: publishable-link/,
139+
)
140+
}
141+
114142
{
115143
const input = await fixture(patch, files)
116144
await assert.rejects(() => applyRunnerWorkspacePatch({ artifactRoot: input.artifacts, artifactRefs: input.refs, workspaceRoot: input.workspace, writablePaths: ["src/**"] }), /outside writable_paths/)

0 commit comments

Comments
 (0)