Files
renovate/test/exec-util.ts
T
Jamie Tanna 5b1749983c feat: always set CI=true for subprocesses
As noted in #43761, we had recently set `CI=true` for the Renovate and
Containerbase orgs on Mend-hosted apps due to a pnpm v11 requirement:

    ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY  Aborted removal of
      modules directory due to no TTY

    If you are running pnpm in CI, set the CI environment variable to
      "true", or set "confirmModulesPurge" to "false".

Similarly in #43606, it was noted that we should do this for `mise lock`
to mark the workspace as "trusted".

Although we went with a different situation, there will be cases where
this is useful for tools being executed, for instance when we invoke `yarn`.

We also make sure to keep our documentation up-to-date as we add to the
specific overrides.

With help from Claude Sonnet and Opus to:

- perform some of the mechanical updates as part of this change
- ensure that the Docker sidecar always has `hardcodedProcessEnv` passed
  into it
- catch a needed change to `exposeAllEnv` handling

Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
2026-08-24 17:21:24 +01:00

104 lines
2.5 KiB
TypeScript

import is from '@sindresorhus/is';
import traverse from 'neotraverse/legacy';
import upath from 'upath';
import { rawExec as _exec } from '../lib/util/exec/common.ts';
import type {
CommandWithOptions,
RawExecOptions,
} from '../lib/util/exec/types.ts';
import { regEx } from '../lib/util/regex.ts';
export type ExecResult = { stdout: string; stderr: string } | Error;
export const exec = vi.mocked(_exec);
export interface ExecSnapshot {
cmd: string;
options?: RawExecOptions | null | undefined;
}
export type ExecSnapshots = ExecSnapshot[];
function execSnapshot(
cmd: string | CommandWithOptions,
options?: RawExecOptions,
): ExecSnapshot {
const snapshot = {
cmd,
options,
};
const cwd = upath.toUnix(process.cwd());
// traverse binds `this.update()` to the callback, requires regular function
// eslint-disable-next-line prefer-arrow-callback
return traverse(snapshot).map(function fixup(v) {
if (is.string(v)) {
const val = v
.replace(regEx(/\\(\w)/g), '/$1')
.replace(regEx(/^[A-Z]:\//), '/') // replace windows paths
.replace(regEx(/"[A-Z]:\//g), '"/') // replace windows paths
.replace(cwd, '/root/project');
this.update(val);
}
});
}
const defaultExecResult = { stdout: '', stderr: '' };
export function mockExecAll(
execResult: ExecResult = defaultExecResult,
): ExecSnapshots {
const snapshots: ExecSnapshots = [];
exec.mockImplementation((cmd, options) => {
snapshots.push(execSnapshot(cmd, options));
if (execResult instanceof Error) {
throw execResult;
}
return execResult as never;
});
return snapshots;
}
export function mockExecSequence(execResults: ExecResult[]): ExecSnapshots {
const snapshots: ExecSnapshots = [];
execResults.forEach((execResult) => {
exec.mockImplementationOnce((cmd, options) => {
snapshots.push(execSnapshot(cmd, options));
if (execResult instanceof Error) {
throw execResult;
}
return execResult as never;
});
});
return snapshots;
}
const basicEnvMock = {
CI: 'true',
HTTP_PROXY: 'http://example.com',
HTTPS_PROXY: 'https://example.com',
NO_PROXY: 'localhost',
HOME: '/home/user',
PATH: '/tmp/path',
LANG: 'en_US.UTF-8',
LC_ALL: 'en_US',
};
const fullEnvMock = {
...basicEnvMock,
SELECTED_ENV_VAR: 'Can be selected',
FILTERED_ENV_VAR: 'Should be filtered',
};
const filteredEnvMock = {
...basicEnvMock,
SELECTED_ENV_VAR: fullEnvMock.SELECTED_ENV_VAR,
};
export const envMock = {
basic: basicEnvMock,
full: fullEnvMock,
filtered: filteredEnvMock,
};