diff --git a/scripts/ubicloud/ubi-runner.sh b/scripts/ubicloud/ubi-runner.sh index 57e1c7643..121a4f18f 100755 --- a/scripts/ubicloud/ubi-runner.sh +++ b/scripts/ubicloud/ubi-runner.sh @@ -1116,11 +1116,16 @@ cmd_down() { # subdirectory of a work tree ships the same file set without a .git: git's # local transport fetches only from a repository's root, and the whole # repository's .git would show the VM every file outside the subdirectory as -# deleted (GST-935). +# deleted (GST-935). A subdirectory git lists no files under (one the parent +# ignores) is refused rather than shipped empty: cmd_run keys the run on the +# local files and skips the tree check for a subdirectory, so an empty upload +# would pass on content that never ran. cmd_pack() { local src tmp gitdir branch head entry src=$(cd "${1:-.}" && pwd) if git -C "$src" rev-parse --is-inside-work-tree >/dev/null 2>&1 && [ -n "$(git -C "$src" rev-parse --show-prefix)" ]; then + [ -n "$(git -C "$src" ls-files -co --exclude-standard | head -n 1)" ] \ + || die "pack: git lists no files under $src (ignored by its checkout, or empty); nothing to ship" ( cd "$src" export COPYFILE_DISABLE=1 diff --git a/test/ubicloud-runner.test.ts b/test/ubicloud-runner.test.ts index 98fb5027a..bd9fe4844 100644 --- a/test/ubicloud-runner.test.ts +++ b/test/ubicloud-runner.test.ts @@ -395,6 +395,11 @@ test('pack ships a linked worktree with a self-contained .git on the checked-out expect(tree(vm)).toEqual(['./.gitignore', './a', './nested/b', './new']); expect(readFileSync(join(vm, 'a'), 'utf8')).toBe('edited\n'); } + // A subdirectory the checkout ignores lists no files; pack refuses it instead of shipping an empty archive. + sh('echo scratch/ > .gitignore && mkdir -p scratch/job && echo s > scratch/job/f', join(d, 'repo')); + const ignored = Bun.spawnSync(['bash', join(DIR, 'ubi-runner.sh'), 'pack', 'scratch/job'], { cwd: join(d, 'repo'), timeout: 30_000 }); + expect(ignored.exitCode).toBe(1); + expect(ignored.stderr.toString()).toContain('pack: git lists no files under'); } finally { rmSync(d, { recursive: true, force: true }); } }, 60_000);