0.2.5-dev: a restore-defaults loadout sync is never overwritten by an older in-flight sync (PR #540 by @Xinglan233, ported); importing a loadout with ops is refused before writing when the DIY data is missing (PR #538 by @Xinglan233, ported); the player package's package.json keeps only scripts whose files ship (#531) and its docs link unshipped files to the release tag on GitHub (#530); golden: unchanged

This commit is contained in:
sganggs committed 2026-10-12 14:45:01 +08:00
1 parent 7fd0478dc0
commit 52b70434d0
13 files changed
+610 -5

No files matched your search

+36 -2
View File
@@ -11,8 +11,8 @@ import os from 'node:os';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import {
FOLDER, REFUSE, RUNTIME_RESEARCH, entryProblems, isRefused, packageOutIsUnsafe, plan, resolveSpecifier, scanFiles,
selectTracked,
FOLDER, REFUSE, RUNTIME_RESEARCH, entryProblems, isRefused, packageOutIsUnsafe, plan, playerPackageJson,
rewritePlayerDoc, resolveSpecifier, scanFiles, selectTracked,
} from '../tools/package.mjs';
// a fake home path, built at run time so the repository never contains one (the local push guard scans every patch)
@@ -311,6 +311,40 @@ test('content packs ship: every committed pack of both layouts, never one only o
}
});
test('the stage drops dishonest npm scripts and rewrites unshipped doc links', { skip: !hasZipTool && 'no zip / tar' }, () => {
const { dir, put } = fakeCheckout();
const out = fs.mkdtempSync(path.join(os.tmpdir(), 'sp-package-out-'));
const readme = '# r\n\n[design](docs/DESIGN.md)\n[play](docs/PLAYING.md)\n[rel](../../releases/latest)\n![shot](docs/img/x.jpg)\n';
try {
put('README.md', readme);
put('docs/PLAYING.md', '# p\n\n[idx](research/00-INDEX.md)\n[home](../README.md#操作)\n');
put('docs/DEPLOY.md', '# d\n\n[packs](PACKS.md)\n[win](WINDOWS.md)\n[notice](../NOTICE.md)\n');
const r = runTool(['--root', dir, '--out', out, '--no-install', '--allow-dirty', '--lite', '--keep-stage']);
assert.equal(r.status, 0, r.stdout + r.stderr);
const stage = path.join(out, `${FOLDER}-v9.9.9-lite`, FOLDER);
const stagedPkg = JSON.parse(fs.readFileSync(path.join(stage, 'package.json'), 'utf8'));
assert.deepEqual(Object.keys(stagedPkg.scripts).sort(), ['assets', 'doctor', 'launch', 'postinstall', 'setup', 'start', 'vendor']);
assert.equal(JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf8')).scripts.test, 'node --test');
const host = 'https://github.com/sganggs/Stronghold-Protocol';
const got = fs.readFileSync(path.join(stage, 'README.md'), 'utf8');
assert.equal(got, `# r\n\n[design](${host}/blob/v9.9.9/docs/DESIGN.md)\n[play](docs/PLAYING.md)\n[rel](${host}/releases/latest)\n![shot](${host}/raw/v9.9.9/docs/img/x.jpg)\n`);
assert.equal(fs.readFileSync(path.join(dir, 'README.md'), 'utf8'), readme, 'the git checkout keeps its relative links');
const playing = fs.readFileSync(path.join(stage, 'docs/PLAYING.md'), 'utf8');
assert.match(playing, new RegExp(`\\(${host}/blob/v9.9.9/docs/research/00-INDEX.md\\)`));
assert.match(playing, /\(\.\.\/README\.md#操作\)/);
const deploy = fs.readFileSync(path.join(stage, 'docs/DEPLOY.md'), 'utf8');
assert.match(deploy, new RegExp(`\\(${host}/blob/v9.9.9/docs/PACKS.md\\)`));
assert.match(deploy, new RegExp(`\\(${host}/blob/v9.9.9/docs/WINDOWS.md\\)`));
assert.match(deploy, /\(\.\.\/NOTICE\.md\)/);
const shipped = new Set(plan(dir, { lite: true, allowDirty: true, scan: false, measure: false }).tracked);
assert.equal(rewritePlayerDoc(readme, 'README.md', shipped, '9.9.9'), got);
assert.equal(playerPackageJson(JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf8')), shipped).scripts.test, undefined);
} finally {
fs.rmSync(dir, { recursive: true, force: true });
fs.rmSync(out, { recursive: true, force: true });
}
});
test('the personal scan reads bytes, never reports the matched text, and skips files it cannot read', () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'sp-package-scan-'));
try {