diff --git a/.github/scripts/history-read.sh b/.github/scripts/history-read.sh index 1b711924d..8f0e93d63 100755 --- a/.github/scripts/history-read.sh +++ b/.github/scripts/history-read.sh @@ -6,7 +6,7 @@ # # Usage: history-read.sh [--absent ] # -# Existence comes from the ref's tree (`git ls-tree`, local data only). A path +# Existence comes from the ref's tree (`git ls-tree --full-tree`, local data only). A path # the tree does not list is absent: prints (default: nothing), exits 0. # A listed path is read with `git show`, tried 3 times in a partial clone # (only a fetch can recover) and once otherwise, waiting @@ -30,7 +30,7 @@ fail() { exit 1 } -listed=$(git ls-tree "$ref" -- "$file" 2>"$err") || fail "git ls-tree failed: $(head -n 1 "$err")" +listed=$(git ls-tree --full-tree "$ref" -- "$file" 2>"$err") || fail "git ls-tree failed: $(head -n 1 "$err")" if [ -z "$listed" ]; then printf '%s' "$absent" exit 0 diff --git a/lib/history-read.ts b/lib/history-read.ts index 87cf0652c..c0702512a 100644 --- a/lib/history-read.ts +++ b/lib/history-read.ts @@ -25,7 +25,7 @@ export function readHistoryFile(repoRoot: string, ref: string, file: string): st return new HistoryReadError(`cannot read ${file} at ${ref}. ${cause} (${version}). Fix: see docs/troubleshooting.md#history-read-failed (HISTORY_READ_FAILED)`); }; const firstLine = (r: ReturnType) => (r.stderr ?? '').split('\n')[0] || r.error?.message || `exit ${r.status}`; - const listed = git(['ls-tree', ref, '--', file]); + const listed = git(['ls-tree', '--full-tree', ref, '--', file]); if (listed.status !== 0) throw fail(`git ls-tree failed: ${firstLine(listed)}`); if (!listed.stdout.trim()) return null; const promisors = (git(['config', '--get-regexp', '^(remote\\..+\\.promisor|extensions\\.partialclone)$']).stdout ?? '').split('\n'); diff --git a/lib/version-gate-train.ts b/lib/version-gate-train.ts index 2a18867ec..818f18730 100644 --- a/lib/version-gate-train.ts +++ b/lib/version-gate-train.ts @@ -109,7 +109,9 @@ function classify(o: { repoRoot: string; baseRef: string; head: string; author?: const npmVersion = (rev: string) => { const text = show(rev, 'package.json'); try { return JSON.parse(text ?? '{}').version ?? null; } catch { return null; } }; if (policy.bump === 'never') return show(mergeBase, 'VERSION') === show(o.head, 'VERSION') && npmVersion(mergeBase) === npmVersion(o.head) ? { kind: 'unstamped', entry: 'kept' } : { kind: 'off' }; - const changed = (git(o.repoRoot, ['diff', '--name-only', mergeBase, o.head]) ?? '').split('\n').filter(Boolean); + const diff = git(o.repoRoot, ['diff', '--no-renames', '--name-only', mergeBase, o.head]); + if (diff === null) return { kind: 'invalid', reason: `cannot list the files ${o.head} changes since its merge base ${mergeBase.slice(0, 12)}. Fix: see docs/troubleshooting.md#history-read-failed (HISTORY_READ_FAILED)` }; + const changed = diff.split('\n').filter(Boolean); const releaseOutputs: string[] = Array.isArray(policy.release_outputs) ? policy.release_outputs : []; // touched: what marks the head as stamped (the labels only feed the length check below) const touched: string[] = changed.filter(f => f === 'VERSION'); diff --git a/test/ci-history-read.test.ts b/test/ci-history-read.test.ts index 3e4c4e2c2..79061d8cc 100644 --- a/test/ci-history-read.test.ts +++ b/test/ci-history-read.test.ts @@ -108,6 +108,10 @@ describe('history-read.sh and lib/history-read.ts on a blobless clone', () => { expect(r.stderr).toBe(''); expect([r.status, r.stdout]).toEqual([0, '1.0.0.1\n']); expect(readHistoryFile(fx.clone, fx.one, 'VERSION')).toBe('1.0.0.0\n'); + const sub = path.join(fx.clone, 'sub'); + fs.mkdirSync(sub); + expect(helper(sub, fx.one, POLICY)).toMatchObject({ status: 0, stdout: JSON.stringify({ bump: 'patch' }) }); + expect(readHistoryFile(sub, fx.one, POLICY)).toBe(JSON.stringify({ bump: 'patch' })); }); test('a path the tree does not list is absent even with the remote gone', () => {