mirror of
https://github.com/Fission-AI/OpenSpec.git
synced 2026-10-02 05:24:34 +08:00
* test(flake): give the bash-spawning scope test a 60s timeout The Windows runner took 13.1s to spawn bash three times on the Version Packages push to main, tripping the 10s default. The same test ran in 0.3s and 4.2s on the two previous main runs; nothing in the code changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(e2e): give the git-clone init test a 60s timeout Timed out at the 10s default on windows-pwsh three times (#1953 merge queue, two changeset-release runs); it normally takes ~2.6s there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
116 lines
4.7 KiB
TypeScript
116 lines
4.7 KiB
TypeScript
import { execFileSync } from 'child_process';
|
|
import fs from 'fs';
|
|
import os from 'os';
|
|
import path from 'path';
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
const projectRoot = process.cwd();
|
|
const scriptPath = path.join(projectRoot, 'scripts', 'update-flake.sh');
|
|
const script = fs.readFileSync(scriptPath, 'utf8');
|
|
|
|
/**
|
|
* `scripts/update-flake.sh` rewrites the pnpmDeps hash in flake.nix in place.
|
|
*
|
|
* flake.nix holds exactly one fixed-output derivation today, so an unscoped
|
|
* `hash = "sha256-..."` happens to land on the right line and the bug is
|
|
* invisible. Add a second FOD and an unscoped script stamps the placeholder
|
|
* over both, reads back whichever mismatch Nix reported first, and writes
|
|
* pnpmDeps' hash into the other derivation. That is a silent corruption of a
|
|
* supply-chain pin, so the scoping is pinned here rather than left to review.
|
|
*/
|
|
describe('update-flake.sh confines every hash rewrite to the pnpmDeps block', () => {
|
|
const BLOCK = "PNPM_DEPS_BLOCK='/pnpmDeps = /,/};/'";
|
|
|
|
it('declares the block address once, so the scoping cannot drift per call site', () => {
|
|
expect(script).toContain(BLOCK);
|
|
});
|
|
|
|
it('scopes every line that reads or rewrites a hash', () => {
|
|
const unscoped = script
|
|
.split('\n')
|
|
.map((line, index) => [index + 1, line.trim()] as const)
|
|
.filter(([, line]) => !line.startsWith('#'))
|
|
// Every line that extracts a hash or edits one in place.
|
|
.filter(([, line]) => /CURRENT_HASH=\$\(sed|sed "\$\{SED_INPLACE\[@\]\}"/.test(line))
|
|
.filter(([, line]) => !line.includes('PNPM_DEPS_BLOCK'));
|
|
|
|
expect(unscoped).toEqual([]);
|
|
});
|
|
|
|
// The static checks above say the range is spelled everywhere; this one says
|
|
// the range actually selects the right derivation. Runs the script's own
|
|
// three sed operations against a flake with three FODs, pnpmDeps in the
|
|
// middle, so a first-match bug and a global-replace bug both show up.
|
|
it('touches only the pnpmDeps hash in a flake with several derivations', () => {
|
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'openspec-flake-scope-'));
|
|
const flake = path.join(dir, 'flake.nix');
|
|
const other = 'sha256-OTHEROTHEROTHEROTHEROTHEROTHEROTHEROTHEROT0=';
|
|
const pnpm = 'sha256-PNPMPNPMPNPMPNPMPNPMPNPMPNPMPNPMPNPMPNPMPN0=';
|
|
const another = 'sha256-ANOTHERANOTHERANOTHERANOTHERANOTHERANOTHE0=';
|
|
const fresh = 'sha256-NEWNEWNEWNEWNEWNEWNEWNEWNEWNEWNEWNEWNEWNE0=';
|
|
|
|
fs.writeFileSync(
|
|
flake,
|
|
[
|
|
'{',
|
|
' other = pkgs.fetchFromGitHub {',
|
|
` hash = "${other}";`,
|
|
' };',
|
|
' pnpmDeps = pkgs.fetchPnpmDeps {',
|
|
` hash = "${pnpm}";`,
|
|
' };',
|
|
' another = pkgs.fetchurl {',
|
|
` hash = "${another}";`,
|
|
' };',
|
|
'}',
|
|
'',
|
|
].join('\n')
|
|
);
|
|
|
|
// Mirrors the script: read the current hash, stamp the placeholder, write
|
|
// the calculated hash back.
|
|
// `bash` runs inside the fixture directory and addresses the file by name:
|
|
// `sed -i` writes its temp file in the working directory and renames it
|
|
// into place, which fails with "Invalid cross-device link" on Windows when
|
|
// the repo (D:) and os.tmpdir() (C:) are different volumes.
|
|
const inFixture = (command: string): string =>
|
|
execFileSync('bash', ['-c', `${BLOCK}\n${command}`, '_', 'flake.nix'], {
|
|
cwd: dir,
|
|
encoding: 'utf8',
|
|
});
|
|
|
|
const read = inFixture(
|
|
`sed -nE "$PNPM_DEPS_BLOCK"' s/.*hash = "(sha256-[^"]+)".*/\\1/p' "$1" | head -1`
|
|
).trim();
|
|
|
|
// The whole point: an unscoped read returns the first derivation's hash.
|
|
expect(read).toBe(pnpm);
|
|
expect(read).not.toBe(other);
|
|
|
|
const placeholder = 'sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=';
|
|
inFixture(
|
|
`sed -i.bak "$PNPM_DEPS_BLOCK s|hash = \\"sha256-[^\\"]*\\"|hash = \\"${placeholder}\\"|" "$1"`
|
|
);
|
|
expect(fs.readFileSync(flake, 'utf8').split(placeholder).length - 1).toBe(1);
|
|
|
|
inFixture(
|
|
`sed -i.bak "$PNPM_DEPS_BLOCK s|hash = \\"${placeholder}\\"|hash = \\"${fresh}\\"|" "$1"`
|
|
);
|
|
|
|
const updated = fs.readFileSync(flake, 'utf8');
|
|
expect(updated).toContain(`hash = "${fresh}"`);
|
|
// The neighbours are untouched, which is what a global replace would break.
|
|
expect(updated).toContain(`hash = "${other}"`);
|
|
expect(updated).toContain(`hash = "${another}"`);
|
|
expect(updated).not.toContain(placeholder);
|
|
|
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
// Three `bash` spawns: 0.3s to 13s on the Windows runner depending on load.
|
|
}, 60_000);
|
|
|
|
it('refuses to touch the file when no pnpmDeps hash is found', () => {
|
|
expect(script).toContain('no pnpmDeps hash found in flake.nix');
|
|
expect(script).toContain('Nothing was modified.');
|
|
});
|
|
});
|