test: the launch markers come from one list, and the echo ratchet scans setup and scripts/*.sh

The preload imports CAPY_LAUNCH_MARKERS instead of re-typing the four names,
and the ratchet asserts the installer exports every name on that list, so a
fifth marker cannot be added to one place only. The shell scan also covers
./setup and the scripts/*.sh install scripts callers run first.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
garrytanandcapy-ai[bot] committed 2026-10-26 04:37:52 +00:00
1 parent 87796fe415
commit 55a89afe02
2 files changed
+9 -3

No files matched your search

+2 -1
View File
@@ -18,6 +18,7 @@
import { afterEach, beforeAll } from 'bun:test';
import * as os from 'node:os';
import * as path from 'node:path';
import { CAPY_LAUNCH_MARKERS } from './scripts/lib/shard-engine';
// State-root hermeticity (docs/state-root.md). Tests isolate state with
// GSTACK_HOME, but GSTACK_STATE_ROOT and GSTACK_STATE_DIR outrank or join it
@@ -37,7 +38,7 @@ for (const name of ['GSTACK_STATE_ROOT', 'GSTACK_STATE_DIR']) {
// run from a shell that sourced the launch file would hand every product child
// an unattended Capy session it never set up. The shard engine strips the same
// list for the lanes; this reaches direct runs too.
for (const name of ['GSTACK_SESSION_KIND', 'GSTACK_CAPY', 'GSTACK_EPHEMERAL', 'GSTACK_INSTALL_VERDICT']) delete process.env[name];
for (const name of CAPY_LAUNCH_MARKERS) delete process.env[name];
process.env.GSTACK_TEST_LEGACY_ROOT = path.join(os.tmpdir(), `gstack-test-legacy-root-${process.pid}-${Date.now()}-unused`);
// Workflow leases (bin/gstack-lease) default to the installation's git dir;
// for the dev checkout under test that is this repo's .git, and a live test
+7 -2
View File
@@ -26,6 +26,7 @@
import { describe, expect, test } from 'bun:test';
import * as fs from 'fs';
import * as path from 'path';
import { CAPY_LAUNCH_MARKERS } from '../scripts/lib/shard-engine';
const ROOT = path.resolve(import.meta.dir, '..');
@@ -110,10 +111,13 @@ function markdownTargets(): string[] {
/^capy\/skill\/gstack\/[^/]+\.md$/.test(rel));
}
/** Every shell helper under bin/, the setup script and the install scripts callers run first. */
function shellTargets(): string[] {
return fs.readdirSync(path.join(ROOT, 'bin')).map(name => path.join('bin', name)).filter(rel => {
const candidates = [...fs.readdirSync(path.join(ROOT, 'bin')).map(name => path.join('bin', name)), 'setup',
...fs.readdirSync(path.join(ROOT, 'scripts')).filter(name => name.endsWith('.sh')).map(name => path.join('scripts', name))];
return candidates.filter(rel => {
const full = path.join(ROOT, rel);
return fs.statSync(full).isFile() && isShell(rel, fs.readFileSync(full, 'utf8'));
return fs.existsSync(full) && fs.statSync(full).isFile() && isShell(rel, fs.readFileSync(full, 'utf8'));
});
}
@@ -177,6 +181,7 @@ describe('launch-env echo ratchet', () => {
const installer = fs.readFileSync(path.join(ROOT, 'bin/gstack-capy-install'), 'utf8');
expect(installer).toContain('LAUNCH_FILE="${VERDICT%.json}.env"');
expect(installer).toContain('chmod 0600 "$tmp"');
for (const name of CAPY_LAUNCH_MARKERS) expect(installer, name).toMatch(new RegExp(`^\\s*echo "export ${name}=`, 'm'));
expect(installer).toContain('echo "GSTACK_LAUNCH_FILE: $LAUNCH_FILE"');
for (const file of ['ship/sections/documentation.md', 'document-release/SKILL.md', 'autoplan/DRIVER.md', 'capy/skill/gstack/SKILL.md']) {
const text = fs.readFileSync(path.join(ROOT, file), 'utf8');