mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 22:14:25 +01:00
fix: hold unreleased policy keys newer than any visible heading; pin trailer placement
The CHANGELOG-heading test asked for the exact next release after the newest heading, which a depth-1 CI clone (no origin/main) computes from the branch alone, so it went red there. It now requires an unreleased key's version to be newer than every heading the checkout can see, which holds at depth 1, on the stamped train tree and on main; the ship-policy comment says who moves the constant when the stamp lands under another number. gstack-coauthor-trailers pins git interpret-trailers' placement on the command line (--no-divider so a --- body line is not a divider, --where end, --if-exists addIfDifferentNeighbor, trailer.separators=:, -- before the path) so a user's trailer.* config cannot replace or drop the existing co-author. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
48ab55ae3f
commit
47e693b304
3 files changed
+16
-12
No files matched your search
@@ -80,8 +80,10 @@ function main(argv: string[]): number {
|
||||
if (appended.length) {
|
||||
// git places the lines in the message's trailer block (joining an existing block, above comment
|
||||
// lines, CRLF kept), so an existing co-author keeps GitHub's attribution; a separate paragraph would not.
|
||||
// Placement is pinned on the command line: a `---` body line is not a divider, and the user's trailer.*
|
||||
// config cannot replace or drop the existing lines.
|
||||
if (!fs.existsSync(message)) fs.writeFileSync(message, '');
|
||||
const placed = git(['interpret-trailers', '--in-place', ...appended.flatMap(t => ['--trailer', t]), message]);
|
||||
const placed = git(['-c', 'trailer.separators=:', 'interpret-trailers', '--in-place', '--no-divider', '--where', 'end', '--if-exists', 'addIfDifferentNeighbor', '--if-missing', 'add', ...appended.flatMap(t => ['--trailer', t]), '--', message]);
|
||||
if (!placed.ok) { process.stderr.write(`gstack-coauthor-trailers: cannot append to ${message}: ${placed.err.split('\n')[0] ?? ''}\n`); return EXIT.fail; }
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -162,8 +162,8 @@ export const TRAIN_KEYS_VERSION = '1.91.146.0';
|
||||
// Each constant is the release that first reads its key: a policy declaring the key must name at least
|
||||
// that version as min_gstack_version, so an older gstack refuses the policy by name instead of failing on
|
||||
// an unknown key. review_scope shipped in 1.91.256.0 (#3481). docs.caps is unreleased: its constant names the
|
||||
// next release after the newest CHANGELOG heading, and test/gstack-ship-policy.test.ts fails when a release that
|
||||
// does not read the key is stamped under that number (the stamper then moves the constant with the heading).
|
||||
// release expected to stamp it (test/gstack-ship-policy.test.ts holds it newer than every CHANGELOG heading the
|
||||
// checkout sees); whoever stamps that release under another number moves the constant to the stamped version.
|
||||
export const REVIEW_SCOPE_KEY_VERSION = '1.91.256.0';
|
||||
export const DOCS_CAPS_KEY_VERSION = '1.91.258.0';
|
||||
export const POLICY_KEY_INTRODUCED: Record<PolicyKey, string> = {
|
||||
|
||||
@@ -75,18 +75,20 @@ describe('validatePolicy (schema + containment, pure)', () => {
|
||||
});
|
||||
|
||||
describe('policy key versions against the CHANGELOG', () => {
|
||||
test('an unreleased key names the release right after the newest heading; a released key has a heading', () => {
|
||||
// The base branch's CHANGELOG counts too: a stamp-at-merge branch carries main's headings only after the slot merge.
|
||||
test('an unreleased key names a release newer than every heading the checkout can see', () => {
|
||||
// The base branch's CHANGELOG counts when the checkout has it (a depth-1 CI clone does not): a stamp-at-merge
|
||||
// branch carries main's headings only after the slot merge, and a release stamped under another number
|
||||
// leaves the constant naming a release that never read the key, which the stamper moves by hand.
|
||||
const root = path.join(import.meta.dir, '..');
|
||||
const base = spawnSync('git', ['show', 'origin/main:CHANGELOG.md'], { cwd: root, encoding: 'utf8', timeout: 30_000 });
|
||||
const text = fs.readFileSync(path.join(root, 'CHANGELOG.md'), 'utf8') + (base.stdout ?? '');
|
||||
const headings = [...text.matchAll(/^## \[(\d+\.\d+\.\d+\.\d+)\]/gm)].map(m => m[1]!.split('.').map(Number));
|
||||
const newest = headings.sort((a, b) => a[0]! - b[0]! || a[1]! - b[1]! || a[2]! - b[2]! || a[3]! - b[3]!).at(-1)!;
|
||||
const next = `${newest[0]}.${newest[1]}.${newest[2]! + 1}.0`;
|
||||
const released = new Set(headings.map(h => h.join('.')));
|
||||
const text = fs.readFileSync(path.join(root, 'CHANGELOG.md'), 'utf8') + (base.status === 0 ? base.stdout : '');
|
||||
const parse = (v: string) => v.split('.').map(Number);
|
||||
const cmp = (a: number[], b: number[]) => a[0]! - b[0]! || a[1]! - b[1]! || a[2]! - b[2]! || a[3]! - b[3]!;
|
||||
const headings = [...text.matchAll(/^## \[(\d+\.\d+\.\d+\.\d+)\]/gm)].map(m => m[1]!);
|
||||
const newest = headings.map(parse).sort(cmp).at(-1)!;
|
||||
for (const [key, version] of Object.entries(POLICY_KEY_INTRODUCED)) {
|
||||
if (released.has(version)) continue;
|
||||
expect(version, `${key} is introduced in ${version}, which has no CHANGELOG heading; an unreleased key names the next release ${next} (move it when the stamp lands elsewhere)`).toBe(next);
|
||||
if (headings.includes(version)) continue;
|
||||
expect(cmp(parse(version), newest), `${key} is introduced in ${version}, which has no CHANGELOG heading and is not newer than the newest heading ${newest.join('.')}; an unreleased key names the release that will stamp it`).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user