* fix(#131): pass explicit HOME and npm cache to before() npm invocations npm reads $HOME/.npmrc (user config) and writes to $HOME/.npm (default cache dir) unless overridden. On Docker hosts the running user's HOME may be uninitialized, unwritable, or contain stale state from prior runs — any of which causes `npm pack` / `npm install -g` in the before() hook to fail with EACCES, cancelling all 6 subtests (A–F). Fix: allocate a fresh mkdtemp dir once per test process in helpers.cjs and inject it as HOME, npm_config_cache, and npm_config_userconfig for every runNpm() call. A process.on('exit') handler removes the dir on teardown. The caller-supplied env option (if any) is merged on top of the isolated env so explicit overrides still win. TDD: tests/bug-131-release-tarball-smoke-explicit-home.test.cjs - Test 1: runNpm succeeds when process HOME is chmod-0500 (unwritable) - Test 2: npm_config_cache resolves under tmpdir, not caller HOME Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(#131): extend HOME isolation to runSmoke spawnSync calls so A-F pass Pass effectiveNpmEnv to the gsd-sdk --version and gsd-sdk query spawnSync invocations inside runSmoke(), matching the isolation already applied to the npm install step. Also add npmEnv: isolatedNpmEnv() to every runSmoke() call in the install test so the full env isolation chain is in effect. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(#131): address CI feedback — prompt-injection comment, Windows USERPROFILE stub, macOS realpath - Rephrase 'act as a poisoned HOME' comment to 'serve as a poisoned HOME' to avoid triggering the prompt-injection scanner's act-as pattern - Add paired process.env.USERPROFILE stub alongside process.env.HOME in Test 1 inline script so Windows parity guard offender count stays at 8 - Fix macOS /var→/private/var symlink false-negative in Test 2 by resolving the nearest existing ancestor with fs.realpathSync before the startsWith comparison Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(#131): export isolatedNpmEnv from helpers.cjs (CI repro of missing symbol) isolatedNpmEnv() was defined in tests/helpers.cjs but never committed — the function body and the updated module.exports line were left as unstaged local edits. CI checkouts saw the old module.exports (without isolatedNpmEnv), causing TypeError: isolatedNpmEnv is not a function at the call site in bug-131-release-tarball-smoke-explicit-home.test.cjs:178 and in release-tarball-smoke.install.test.cjs wherever the function is destructured. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(#131): canonicalize macOS tmpdir in remaining startsWith assertions Replace the ad-hoc try/catch realpathSync fallback chain in Test 2 and the inline try/catch in Test 3 with a shared safeRealpath() helper that walks up to the nearest existing ancestor before resolving, then reconstructs the canonical path. This ensures /var→/private/var symlink expansion succeeds even when the leaf (.npm cache dir) does not yet exist on macOS CI runners. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -282,6 +282,10 @@ function scanWorkflowMissingSdkFallback(filePath) {
|
||||
* @param {string} [opts.fixtureDir] - Temp dir to run `init` into (must NOT be HOME)
|
||||
* @param {string[]} [opts.lifecycleCommands] - Commands to file-check (default: see below)
|
||||
* @param {boolean} [opts.dryRun=false] - If true, skip actual npm install; validate input only
|
||||
* @param {object} [opts.npmEnv] - Optional env dict for the internal npm install
|
||||
* spawnSync call. Pass an isolated HOME env (e.g. from isolatedNpmEnv() in tests/helpers.cjs)
|
||||
* to prevent npm from reading/writing the caller's $HOME — required on Docker hosts where HOME
|
||||
* may be unwritable. Defaults to process.env. (#131)
|
||||
* @returns {{ code: string, details: object }}
|
||||
*/
|
||||
function runSmoke({
|
||||
@@ -291,6 +295,7 @@ function runSmoke({
|
||||
fixtureDir,
|
||||
lifecycleCommands = ['init', 'discuss-phase', 'plan-phase', 'execute-phase'],
|
||||
dryRun = false,
|
||||
npmEnv = undefined,
|
||||
}) {
|
||||
const details = {
|
||||
tarball: tarballPath,
|
||||
@@ -304,10 +309,14 @@ function runSmoke({
|
||||
|
||||
// --- Install the tarball into the temp prefix ----------------------------
|
||||
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
||||
// Use the caller-supplied npmEnv if provided (allows HOME isolation on Docker
|
||||
// hosts where HOME may be unwritable — same pattern as runNpm() in helpers.cjs).
|
||||
// Falls back to process.env to preserve existing CLI / programmatic behaviour. (#131)
|
||||
const effectiveNpmEnv = npmEnv !== undefined ? npmEnv : process.env;
|
||||
const installResult = spawnSync(
|
||||
npmCmd,
|
||||
['install', '-g', '--prefix', installPrefix, tarballPath],
|
||||
{ encoding: 'utf-8', shell: process.platform === 'win32', timeout: CHILD_TIMEOUT_MS },
|
||||
{ encoding: 'utf-8', shell: process.platform === 'win32', timeout: CHILD_TIMEOUT_MS, env: effectiveNpmEnv },
|
||||
);
|
||||
|
||||
if (installResult.status !== 0) {
|
||||
@@ -335,10 +344,12 @@ function runSmoke({
|
||||
}
|
||||
|
||||
// --- Invoke `gsd-sdk --version` ------------------------------------------
|
||||
// Use effectiveNpmEnv so the installed binary sees an isolated HOME on Docker
|
||||
// hosts where HOME may be unwritable (same isolation as the npm install). (#131)
|
||||
const versionResult = spawnSync(
|
||||
process.execPath,
|
||||
[actualBin, '--version'],
|
||||
{ encoding: 'utf-8', timeout: CHILD_TIMEOUT_MS },
|
||||
{ encoding: 'utf-8', timeout: CHILD_TIMEOUT_MS, env: effectiveNpmEnv },
|
||||
);
|
||||
|
||||
if (versionResult.status !== 0) {
|
||||
@@ -497,11 +508,13 @@ function runSmoke({
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
// --- Verify `gsd-sdk` query is callable and returns parseable JSON -------
|
||||
// Use effectiveNpmEnv so the installed binary sees an isolated HOME on Docker
|
||||
// hosts where HOME may be unwritable (same isolation as the npm install). (#131)
|
||||
const sdkQueryDir = fixtureDir || os.tmpdir();
|
||||
const sdkQueryResult = spawnSync(
|
||||
process.execPath,
|
||||
[actualBin, 'query', 'state.json', '--project-dir', sdkQueryDir],
|
||||
{ encoding: 'utf-8', timeout: CHILD_TIMEOUT_MS },
|
||||
{ encoding: 'utf-8', timeout: CHILD_TIMEOUT_MS, env: effectiveNpmEnv },
|
||||
);
|
||||
|
||||
if (sdkQueryResult.status !== 0) {
|
||||
|
||||
Reference in New Issue
Block a user