修复Windows下Git钩子回归测试

统一比较Git恢复文件时的换行符
将WSL bash测试使用的临时工具路径转换为可执行路径
确保master推送门禁在Windows与Unix环境下均可重复验证
This commit is contained in:
kdletters
2026-08-13 13:34:45 +08:00
parent 15c227cebf
commit 1b4cb3c45e
+66 -38
View File
@@ -145,7 +145,7 @@ test('pre-commit hook fixes staged imports and formatting without swallowing uns
'const original = { value: 1 };\nconst keep = 2;\n',
);
assert.equal(
readFileSync(partialPath, 'utf8'),
readFileSync(partialPath, 'utf8').replaceAll('\r\n', '\n'),
'const original = { value: 1 };\nconst keep={unstaged:true}\n',
);
assert.equal(
@@ -160,53 +160,73 @@ test('pre-commit hook fixes staged imports and formatting without swallowing uns
test('pre-push runs repository parity only for master updates', () => {
const tempDir = mkdtempSync(join(tmpdir(), 'genarrative-pre-push-'));
try {
const binDir = join(tempDir, 'bin');
mkdirSync(binDir);
const npmLog = join(tempDir, 'npm.log');
const fakeNpm = join(binDir, 'npm');
const fakeGit = join(binDir, 'git');
writeFileSync(
fakeNpm,
`#!/usr/bin/env bash\nprintf '%s\\n' "$*" >> "${npmLog}"\n`,
const npmLog = join(tempDir, 'repo', 'npm.log');
const tempRepo = join(tempDir, 'repo');
mkdirSync(tempRepo);
git(tempRepo, 'init', '--quiet');
git(tempRepo, 'config', 'user.email', 'git-hooks-test@example.invalid');
git(tempRepo, 'config', 'user.name', 'Git Hooks Test');
writeFileSync(join(tempRepo, 'tracked.txt'), 'baseline\n');
git(tempRepo, 'add', 'tracked.txt');
git(
tempRepo,
'-c',
'commit.gpgsign=false',
'commit',
'--quiet',
'-m',
'baseline',
);
chmodSync(fakeNpm, 0o755);
const localSha = git(tempRepo, 'rev-parse', 'HEAD').trim();
writeFileSync(
fakeGit,
'#!/usr/bin/env bash\n' +
'if [[ "$1" == "rev-parse" && "$2" == "HEAD" ]]; then\n' +
' printf "%s\\n" "1111111111111111111111111111111111111111"\n' +
' exit 0\n' +
'fi\n' +
'if [[ "$1" == "diff" ]]; then exit 0; fi\n' +
'exit 1\n',
join(tempRepo, 'git'),
`#!/usr/bin/env bash
if [[ "$1" == 'rev-parse' && "$2" == 'HEAD' ]]; then
printf '%s\\n' '${localSha}'
exit 0
fi
if [[ "$1" == 'diff' ]]; then exit 0; fi
exit 1
`,
);
chmodSync(fakeGit, 0o755);
const env = {
...process.env,
PATH: `${binDir}${delimiter}${process.env.PATH ?? ''}`,
chmodSync(join(tempRepo, 'git'), 0o755);
writeFileSync(
join(tempRepo, 'pre-push-master.sh'),
readFileSync(join(repoRoot, 'scripts', 'pre-push-master.sh'), 'utf8'),
);
writeFileSync(
join(tempRepo, 'npm'),
'#!/usr/bin/env bash\nprintf \'%s\\n\' "$*" >> npm.log\n',
);
chmodSync(join(tempRepo, 'npm'), 0o755);
const spawnHook = (input) => {
writeFileSync(join(tempRepo, 'push.input'), input);
return spawnSync(
'bash',
[
'-c',
'PATH="$PWD:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"; export PATH; source ./pre-push-master.sh origin example.invalid < push.input',
],
{
cwd: tempRepo,
encoding: 'utf8',
env: process.env,
},
);
};
const hook = join(repoRoot, 'scripts', 'pre-push-master.sh');
const featurePush = spawnSync('bash', [hook, 'origin', 'example.invalid'], {
cwd: repoRoot,
encoding: 'utf8',
env,
input:
'refs/heads/feature 1111111111111111111111111111111111111111 refs/heads/feature 2222222222222222222222222222222222222222\n',
});
const featurePush = spawnHook(
'refs/heads/feature 1111111111111111111111111111111111111111 refs/heads/feature 2222222222222222222222222222222222222222\n',
);
assert.equal(featurePush.status, 0, featurePush.stderr);
assert.equal(readFileOrEmpty(npmLog), '');
const masterPush = spawnSync('bash', [hook, 'origin', 'example.invalid'], {
cwd: repoRoot,
encoding: 'utf8',
env,
input:
'refs/heads/master 1111111111111111111111111111111111111111 refs/heads/master 2222222222222222222222222222222222222222\n',
});
const masterPush = spawnHook(
`refs/heads/master ${localSha} refs/heads/master 2222222222222222222222222222222222222222\n`,
);
assert.equal(masterPush.status, 0, masterPush.stderr);
assert.equal(
readFileSync(npmLog, 'utf8'),
'run check:repository-ci -- 2222222222222222222222222222222222222222 1111111111111111111111111111111111111111\n',
`run check:repository-ci -- 2222222222222222222222222222222222222222 ${localSha}\n`,
);
} finally {
rmSync(tempDir, { force: true, recursive: true });
@@ -254,6 +274,14 @@ function readFileOrEmpty(path) {
}
}
function toBashPath(path) {
const windowsDrive = /^([A-Za-z]):[\\/](.*)$/u.exec(path);
if (windowsDrive) {
return `/mnt/${windowsDrive[1].toLowerCase()}/${windowsDrive[2].replaceAll('\\', '/')}`;
}
return path;
}
function git(cwd, ...args) {
return execFileSync('git', args, { cwd, encoding: 'utf8' });
}