fix(dsh): клон-маркер перед fetch — чужой git-репо не трогаем (I-1, #69)

This commit is contained in:
Andrey Epifancev 2026-10-25 01:07:45 +04:00
parent c309ae2d96
commit f7b616d32e
2 changed files with 43 additions and 0 deletions

View File

@ -412,6 +412,15 @@ ensure_pinned_clone() {
echo "FAIL: move it away and run: $CLONE_HINT" >&2
return 2
fi
# A foreign git repository must never be mutated by fetch/checkout: a real
# superpowers clone always carries skills/using-superpowers/SKILL.md (I-1).
# The marker also holds for the moved-tag flow — skills are present in
# every pinned clone.
if [ ! -f "$dir/skills/using-superpowers/SKILL.md" ]; then
echo "FAIL: $dir is not a superpowers clone (no skills/using-superpowers/SKILL.md); left untouched" >&2
echo "FAIL: move it away or use --superpowers-from <dir>" >&2
return 2
fi
# Online: refresh the pin. Fetching from $DSH_SUPERPOWERS_REPO (not from
# the clone's origin) makes a repo change take effect on the next run
# (T3.3); the force refspec updates the LOCAL tag as well as FETCH_HEAD, so

View File

@ -282,6 +282,40 @@ test('--skills removes a half-cloned dir after a failed fresh clone (RF5)', () =
}
})
test('a foreign git repo in $DSH_HOME/superpowers is never touched (I-1)', () => {
const work = tempDir()
try {
const home = join(work, 'home')
const pinned = makePinnedRepo(work)
const foreign = join(home, '.dsh', 'superpowers')
mkdirSync(foreign, { recursive: true })
git(foreign, ['init', '-b', 'main'])
git(foreign, ['config', 'user.email', 'dsh-harness@example.invalid'])
git(foreign, ['config', 'user.name', 'DSH Harness Test'])
write(join(foreign, 'witness.txt'), 'do not touch\n')
git(foreign, ['add', 'witness.txt'])
git(foreign, ['commit', '-m', 'foreign repo'])
const headBefore = git(foreign, ['rev-parse', 'HEAD'])
const run = runInstaller(home, ['--skills'], { DSH_SUPERPOWERS_REPO: `file://${pinned}` })
const out = run.stdout + run.stderr
assert.equal(run.status, 1, out)
assert.match(out, /not a superpowers clone/)
assert.match(out, /move it away or use --superpowers-from/)
assert.equal(readFileSync(join(foreign, 'witness.txt'), 'utf8'), 'do not touch\n')
assert.equal(git(foreign, ['rev-parse', 'HEAD']), headBefore, 'HEAD must not move')
assert.equal(existsSync(join(foreign, 'skills')), false, 'no skills tree may appear')
// --check agrees: the foreign repo is not reported as a source.
const check = runInstaller(home, ['--check'], { DSH_SUPERPOWERS_REPO: `file://${pinned}` })
const checkOut = check.stdout + check.stderr
assert.equal(check.status, 0, checkOut)
assert.doesNotMatch(checkOut, /ok: superpowers source/)
} finally {
cleanup(work)
}
})
test('--skills reuses an on-ref clone when offline (WARN)', () => {
const work = tempDir()
try {