fix(#229): normalize gsd-hook-version substitution in verify-reapply (#282)

This commit is contained in:
Tom Boucher
2026-05-26 12:06:57 -04:00
committed by GitHub
parent 6a94bb6afb
commit afba2afbe7
3 changed files with 54 additions and 2 deletions

View File

@@ -0,0 +1,5 @@
---
type: Fixed
pr: 229
---
Fixed verify-reapply false fail_user_lines_missing when gsd-hook-version lines are install-time substituted.

View File

@@ -34,6 +34,7 @@ const path = require('node:path');
const crypto = require('node:crypto');
const SIGNIFICANT_MIN_CHARS = 12;
const GSD_HOOK_VERSION_LINE_RE = /^(?:\/\/|#)\s*gsd-hook-version:\s*\S+\s*$/i;
function parseArgs(argv) {
const opts = { patchesDir: null, configDir: null, pristineDir: null, json: false };
@@ -67,6 +68,16 @@ function isSignificantLine(line) {
return true;
}
function normalizeUpstreamOwnedLine(line) {
const trimmed = line.trim();
if (!trimmed) return line;
if (GSD_HOOK_VERSION_LINE_RE.test(trimmed)) {
const prefix = trimmed.startsWith('#') ? '#' : '//';
return `${prefix} gsd-hook-version: __GSD_VERSION_TOKEN__`;
}
return line;
}
/**
* Compute the SHA-256 hex digest of a string (UTF-8 encoded).
*/
@@ -124,8 +135,13 @@ function computeUserAddedLines(backupContent, pristineContent) {
if (!pristineContent) {
return backupLines.filter(isSignificantLine);
}
const pristineSet = new Set(pristineContent.split(/\r?\n/));
return backupLines.filter((line) => isSignificantLine(line) && !pristineSet.has(line));
const pristineSet = new Set(
pristineContent.split(/\r?\n/).map(normalizeUpstreamOwnedLine),
);
return backupLines.filter((line) => {
if (!isSignificantLine(line)) return false;
return !pristineSet.has(normalizeUpstreamOwnedLine(line));
});
}
/**

View File

@@ -210,4 +210,35 @@ describe('Bug #2969: deterministic Step 5 verification gate', () => {
assert.ok(report.results[0].missing.includes(droppedLine));
assert.ok(!report.results[0].missing.includes(presentLine));
});
test('treats gsd-hook-version install-time substitution as upstream-owned, not missing user content (#229)', () => {
resetFixture();
const rel = path.join('hooks', 'gsd-statusline.js');
const pristine = [
'// gsd-hook-version: {{GSD_VERSION}}',
'console.log("statusline hook");',
'',
].join('\n');
const backup = [
'// gsd-hook-version: 1.41.0',
'console.log("statusline hook");',
'',
].join('\n');
const installed = [
'// gsd-hook-version: 1.42.3',
'console.log("statusline hook");',
'',
].join('\n');
writeFile(path.join(pristineDir, rel), pristine);
writeFile(path.join(patchesDir, rel), backup);
writeFile(path.join(configDir, rel), installed);
const { status, report } = runVerifier();
assert.equal(status, 0, `expected pass for upstream-owned version substitution; report=${JSON.stringify(report)}`);
assert.equal(report.failures, 0);
assert.equal(report.checked, 1);
assert.equal(report.results[0].status, 'ok');
assert.deepStrictEqual(report.results[0].missing, []);
});
});