\`lh doctor\`'s context7 check is a required, env-only check by design (ADR: never inferred, never persisted). Tests that shell out to \`lh\` or \`scripts/onboard.mjs\` inherited whatever CONTEXT7_API_KEY the developer's shell happened to export, so \`doctor passes once the repo is initialised\` and the onboard-script e2e test only ever passed on machines with a real key set — never verified in a clean environment until this CI run (no secret configured, correctly). Fixed by injecting an obviously-fake fixture key (TEST_ENV in helpers.mjs, exported and reused by onboard.test.mjs) into every subprocess these tests spawn, so behaviour no longer depends on the ambient shell. Verified locally with \`env -u CONTEXT7_API_KEY\` to reproduce the CI environment exactly: 58/58 pass either way now. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
65 lines
2.2 KiB
JavaScript
65 lines
2.2 KiB
JavaScript
import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join, dirname } from 'node:path';
|
|
import { execFileSync } from 'node:child_process';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
export const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
export const CLI = join(ROOT, 'src', 'cli.mjs');
|
|
|
|
const created = [];
|
|
|
|
/**
|
|
* `lh doctor`/`lh init` treat CONTEXT7_API_KEY as a required, env-only value
|
|
* (never persisted, never inferred). Tests must not depend on whatever the
|
|
* developer's shell happens to export — inject an obviously-fake fixture so
|
|
* behaviour is identical locally and in CI, without ever touching a real key.
|
|
*/
|
|
export const TEST_ENV = { ...process.env, CONTEXT7_API_KEY: process.env.CONTEXT7_API_KEY ?? 'fixture-not-a-real-key' };
|
|
|
|
export function tempRepo(files = {}) {
|
|
const dir = mkdtempSync(join(tmpdir(), 'lh-test-'));
|
|
created.push(dir);
|
|
git(dir, ['init', '-q', '-b', 'main']);
|
|
git(dir, ['config', 'user.email', 'test@example.com']);
|
|
git(dir, ['config', 'user.name', 'Test']);
|
|
git(dir, ['config', 'commit.gpgsign', 'false']);
|
|
for (const [rel, body] of Object.entries(files)) write(dir, rel, body);
|
|
if (Object.keys(files).length) commitAll(dir, 'init');
|
|
return dir;
|
|
}
|
|
|
|
export function write(dir, rel, body) {
|
|
const full = join(dir, rel);
|
|
mkdirSync(dirname(full), { recursive: true });
|
|
writeFileSync(full, body);
|
|
return full;
|
|
}
|
|
|
|
export function git(dir, args) {
|
|
return execFileSync('git', args, { cwd: dir, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
}
|
|
|
|
export function commitAll(dir, msg) {
|
|
git(dir, ['add', '-A']);
|
|
git(dir, ['commit', '-qm', msg]);
|
|
}
|
|
|
|
/** Run the lh CLI. Never throws — returns {code, stdout, stderr}. */
|
|
export function lh(dir, args) {
|
|
try {
|
|
const stdout = execFileSync(process.execPath, [CLI, ...args], {
|
|
cwd: dir, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], env: TEST_ENV,
|
|
});
|
|
return { code: 0, stdout, stderr: '' };
|
|
} catch (e) {
|
|
return { code: e.status ?? 1, stdout: e.stdout ?? '', stderr: e.stderr ?? '' };
|
|
}
|
|
}
|
|
|
|
export function cleanup() {
|
|
while (created.length) {
|
|
try { rmSync(created.pop(), { recursive: true, force: true }); } catch {}
|
|
}
|
|
}
|