mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-03-30 13:43:26 +08:00
Add PowerShell installer wrapper and update documentation (#532)
* Add install.ps1 PowerShell wrapper and tests Add a Windows-native PowerShell wrapper (install.ps1) that resolves symlinks and delegates to the Node-based installer runtime. Update README with PowerShell usage examples and cross-platform npx entrypoint guidance. Point the ecc-install bin to the Node installer (scripts/install-apply.js) in package.json (and refresh package-lock), include install.ps1 in package files, and add tests: a new install-ps1.test.js and a tweak to install-sh.test.js to skip on Windows. These changes provide native Windows installer support while keeping npm-compatible cross-platform invocation. * Improve tests for Windows HOME/USERPROFILE Make tests more cross-platform by ensuring HOME and USERPROFILE are kept in sync and by normalizing test file paths for display. - tests/lib/session-adapters.test.js: set USERPROFILE when temporarily setting HOME and restore previous USERPROFILE on teardown. - tests/run-all.js: use a normalized displayPath (forward-slash separated) for logging and error messages so output is consistent across platforms. - tests/scripts/ecc.test.js & tests/scripts/session-inspect.test.js: build envOverrides from options.env and add HOME <-> USERPROFILE fallbacks so spawned child processes receive both variables when only one is provided. These changes prevent test failures and inconsistent logs on Windows where USERPROFILE is used instead of HOME. * Fix Windows paths and test flakiness Improve cross-platform behavior and test stability. - Remove unused createLegacyInstallPlan import from install-lifecycle.js. - Change resolveInstallConfigPath to use path.normalize(path.join(cwd, configPath)) to produce normalized relative paths. - Tests: add toBashPath and normalizedRelativePath helpers to normalize Windows paths for bash and comparisons. - Make cleanupTestDir retry rmSync on transient Windows errors (EPERM/EBUSY/ENOTEMPTY) with short backoff using sleepMs. - Ensure spawned test processes receive USERPROFILE and convert repo/detect paths to bash format when invoking bash. These changes reduce Windows-specific failures and flakiness in the test suite and tidy up a small unused import.
This commit is contained in:
24
README.md
24
README.md
@@ -155,16 +155,24 @@ Get up and running in under 2 minutes:
|
||||
git clone https://github.com/affaan-m/everything-claude-code.git
|
||||
cd everything-claude-code
|
||||
|
||||
# Recommended: use the installer (handles common + language rules safely)
|
||||
# macOS/Linux
|
||||
./install.sh typescript # or python or golang or swift or php
|
||||
# You can pass multiple languages:
|
||||
# ./install.sh typescript python golang swift php
|
||||
# or target cursor:
|
||||
# ./install.sh --target cursor typescript
|
||||
# or target antigravity:
|
||||
# ./install.sh --target antigravity typescript
|
||||
```
|
||||
|
||||
```powershell
|
||||
# Windows PowerShell
|
||||
.\install.ps1 typescript # or python or golang or swift or php
|
||||
# .\install.ps1 typescript python golang swift php
|
||||
# .\install.ps1 --target cursor typescript
|
||||
# .\install.ps1 --target antigravity typescript
|
||||
|
||||
# npm-installed compatibility entrypoint also works cross-platform
|
||||
npx ecc-install typescript
|
||||
```
|
||||
|
||||
For manual install instructions see the README in the `rules/` folder.
|
||||
|
||||
### Step 3: Start Using
|
||||
@@ -875,11 +883,17 @@ ECC provides **full Cursor IDE support** with hooks, rules, agents, skills, comm
|
||||
### Quick Start (Cursor)
|
||||
|
||||
```bash
|
||||
# Install for your language(s)
|
||||
# macOS/Linux
|
||||
./install.sh --target cursor typescript
|
||||
./install.sh --target cursor python golang swift php
|
||||
```
|
||||
|
||||
```powershell
|
||||
# Windows PowerShell
|
||||
.\install.ps1 --target cursor typescript
|
||||
.\install.ps1 --target cursor python golang swift php
|
||||
```
|
||||
|
||||
### What's Included
|
||||
|
||||
| Component | Count | Details |
|
||||
|
||||
38
install.ps1
Normal file
38
install.ps1
Normal file
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env pwsh
|
||||
# install.ps1 — Windows-native entrypoint for the ECC installer.
|
||||
#
|
||||
# This wrapper resolves the real repo/package root when invoked through a
|
||||
# symlinked path, then delegates to the Node-based installer runtime.
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$scriptPath = $PSCommandPath
|
||||
|
||||
while ($true) {
|
||||
$item = Get-Item -LiteralPath $scriptPath -Force
|
||||
if (-not $item.LinkType) {
|
||||
break
|
||||
}
|
||||
|
||||
$targetPath = $item.Target
|
||||
if ($targetPath -is [array]) {
|
||||
$targetPath = $targetPath[0]
|
||||
}
|
||||
|
||||
if (-not $targetPath) {
|
||||
break
|
||||
}
|
||||
|
||||
if (-not [System.IO.Path]::IsPathRooted($targetPath)) {
|
||||
$targetPath = Join-Path -Path $item.DirectoryName -ChildPath $targetPath
|
||||
}
|
||||
|
||||
$scriptPath = [System.IO.Path]::GetFullPath($targetPath)
|
||||
}
|
||||
|
||||
$scriptDir = Split-Path -Parent $scriptPath
|
||||
$installerScript = Join-Path -Path (Join-Path -Path $scriptDir -ChildPath 'scripts') -ChildPath 'install-apply.js'
|
||||
|
||||
& node $installerScript @args
|
||||
exit $LASTEXITCODE
|
||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -14,7 +14,7 @@
|
||||
},
|
||||
"bin": {
|
||||
"ecc": "scripts/ecc.js",
|
||||
"ecc-install": "install.sh"
|
||||
"ecc-install": "scripts/install-apply.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.2",
|
||||
|
||||
@@ -89,11 +89,12 @@
|
||||
".claude-plugin/plugin.json",
|
||||
".claude-plugin/README.md",
|
||||
"install.sh",
|
||||
"install.ps1",
|
||||
"llms.txt"
|
||||
],
|
||||
"bin": {
|
||||
"ecc": "scripts/ecc.js",
|
||||
"ecc-install": "install.sh"
|
||||
"ecc-install": "scripts/install-apply.js"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "echo '\\n ecc-universal installed!\\n Run: npx ecc typescript\\n Compat: npx ecc-install typescript\\n Docs: https://github.com/affaan-m/everything-claude-code\\n'",
|
||||
|
||||
@@ -4,7 +4,6 @@ const path = require('path');
|
||||
const { resolveInstallPlan, loadInstallManifests } = require('./install-manifests');
|
||||
const { readInstallState, writeInstallState } = require('./install-state');
|
||||
const {
|
||||
createLegacyInstallPlan,
|
||||
createManifestInstallPlan,
|
||||
} = require('./install-executor');
|
||||
const {
|
||||
|
||||
@@ -44,7 +44,7 @@ function resolveInstallConfigPath(configPath, options = {}) {
|
||||
const cwd = options.cwd || process.cwd();
|
||||
return path.isAbsolute(configPath)
|
||||
? configPath
|
||||
: path.resolve(cwd, configPath);
|
||||
: path.normalize(path.join(cwd, configPath));
|
||||
}
|
||||
|
||||
function loadInstallConfig(configPath, options = {}) {
|
||||
|
||||
@@ -10,6 +10,20 @@ const fs = require('fs');
|
||||
const os = require('os');
|
||||
const { spawn, spawnSync } = require('child_process');
|
||||
|
||||
function toBashPath(filePath) {
|
||||
if (process.platform !== 'win32') {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
return String(filePath)
|
||||
.replace(/^([A-Za-z]):/, (_, driveLetter) => `/mnt/${driveLetter.toLowerCase()}`)
|
||||
.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
function sleepMs(ms) {
|
||||
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
||||
}
|
||||
|
||||
// Test helper
|
||||
function test(name, fn) {
|
||||
try {
|
||||
@@ -65,7 +79,7 @@ function runScript(scriptPath, input = '', env = {}) {
|
||||
|
||||
function runShellScript(scriptPath, args = [], input = '', env = {}, cwd = process.cwd()) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const proc = spawn('bash', [scriptPath, ...args], {
|
||||
const proc = spawn('bash', [toBashPath(scriptPath), ...args], {
|
||||
cwd,
|
||||
env: { ...process.env, ...env },
|
||||
stdio: ['pipe', 'pipe', 'pipe']
|
||||
@@ -93,7 +107,19 @@ function createTestDir() {
|
||||
|
||||
// Clean up test directory
|
||||
function cleanupTestDir(testDir) {
|
||||
const retryableCodes = new Set(['EPERM', 'EBUSY', 'ENOTEMPTY']);
|
||||
|
||||
for (let attempt = 0; attempt < 5; attempt++) {
|
||||
try {
|
||||
fs.rmSync(testDir, { recursive: true, force: true });
|
||||
return;
|
||||
} catch (error) {
|
||||
if (!retryableCodes.has(error.code) || attempt === 4) {
|
||||
throw error;
|
||||
}
|
||||
sleepMs(50 * (attempt + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createCommandShim(binDir, baseName, logFile) {
|
||||
@@ -2253,7 +2279,7 @@ async function runTests() {
|
||||
if (
|
||||
await asyncTest('detect-project exports the resolved Python command for downstream scripts', async () => {
|
||||
const detectProjectPath = path.join(__dirname, '..', '..', 'skills', 'continuous-learning-v2', 'scripts', 'detect-project.sh');
|
||||
const shellCommand = [`source "${detectProjectPath}" >/dev/null 2>&1`, 'printf "%s\\n" "${CLV2_PYTHON_CMD:-}"'].join('; ');
|
||||
const shellCommand = [`source "${toBashPath(detectProjectPath)}" >/dev/null 2>&1`, 'printf "%s\\n" "${CLV2_PYTHON_CMD:-}"'].join('; ');
|
||||
|
||||
const shell = process.platform === 'win32' ? 'bash' : 'bash';
|
||||
const proc = spawn(shell, ['-lc', shellCommand], {
|
||||
@@ -2292,14 +2318,14 @@ async function runTests() {
|
||||
spawnSync('git', ['remote', 'add', 'origin', 'https://github.com/example/ecc-test.git'], { cwd: repoDir, stdio: 'ignore' });
|
||||
|
||||
const shellCommand = [
|
||||
`cd "${repoDir}"`,
|
||||
`source "${detectProjectPath}" >/dev/null 2>&1`,
|
||||
`cd "${toBashPath(repoDir)}"`,
|
||||
`source "${toBashPath(detectProjectPath)}" >/dev/null 2>&1`,
|
||||
'printf "%s\\n" "$PROJECT_ID"',
|
||||
'printf "%s\\n" "$PROJECT_DIR"'
|
||||
].join('; ');
|
||||
|
||||
const proc = spawn('bash', ['-lc', shellCommand], {
|
||||
env: { ...process.env, HOME: homeDir },
|
||||
env: { ...process.env, HOME: homeDir, USERPROFILE: homeDir },
|
||||
stdio: ['ignore', 'pipe', 'pipe']
|
||||
});
|
||||
|
||||
@@ -2357,6 +2383,7 @@ async function runTests() {
|
||||
try {
|
||||
const result = await runShellScript(observePath, ['post'], payload, {
|
||||
HOME: homeDir,
|
||||
USERPROFILE: homeDir,
|
||||
CLAUDE_PROJECT_DIR: projectDir
|
||||
}, projectDir);
|
||||
|
||||
|
||||
@@ -11,6 +11,10 @@ const {
|
||||
planInstallTargetScaffold,
|
||||
} = require('../../scripts/lib/install-targets/registry');
|
||||
|
||||
function normalizedRelativePath(value) {
|
||||
return String(value || '').replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
@@ -86,7 +90,7 @@ function runTests() {
|
||||
|
||||
const flattened = plan.operations.find(operation => operation.sourceRelativePath === '.cursor');
|
||||
const preserved = plan.operations.find(operation => (
|
||||
operation.sourceRelativePath === path.join('rules', 'common', 'coding-style.md')
|
||||
normalizedRelativePath(operation.sourceRelativePath) === 'rules/common/coding-style.md'
|
||||
));
|
||||
|
||||
assert.ok(flattened, 'Should include .cursor scaffold operation');
|
||||
@@ -119,14 +123,14 @@ function runTests() {
|
||||
|
||||
assert.ok(
|
||||
plan.operations.some(operation => (
|
||||
operation.sourceRelativePath === path.join('rules', 'common', 'coding-style.md')
|
||||
normalizedRelativePath(operation.sourceRelativePath) === 'rules/common/coding-style.md'
|
||||
&& operation.destinationPath === path.join(projectRoot, '.cursor', 'rules', 'common-coding-style.md')
|
||||
)),
|
||||
'Should flatten common rules into namespaced files'
|
||||
);
|
||||
assert.ok(
|
||||
plan.operations.some(operation => (
|
||||
operation.sourceRelativePath === path.join('rules', 'typescript', 'testing.md')
|
||||
normalizedRelativePath(operation.sourceRelativePath) === 'rules/typescript/testing.md'
|
||||
&& operation.destinationPath === path.join(projectRoot, '.cursor', 'rules', 'typescript-testing.md')
|
||||
)),
|
||||
'Should flatten language rules into namespaced files'
|
||||
@@ -179,7 +183,7 @@ function runTests() {
|
||||
);
|
||||
assert.ok(
|
||||
plan.operations.some(operation => (
|
||||
operation.sourceRelativePath === path.join('rules', 'common', 'coding-style.md')
|
||||
normalizedRelativePath(operation.sourceRelativePath) === 'rules/common/coding-style.md'
|
||||
&& operation.destinationPath === path.join(projectRoot, '.agent', 'rules', 'common-coding-style.md')
|
||||
)),
|
||||
'Should flatten common rules for antigravity'
|
||||
|
||||
@@ -34,7 +34,9 @@ function test(name, fn) {
|
||||
|
||||
function withHome(homeDir, fn) {
|
||||
const previousHome = process.env.HOME;
|
||||
const previousUserProfile = process.env.USERPROFILE;
|
||||
process.env.HOME = homeDir;
|
||||
process.env.USERPROFILE = homeDir;
|
||||
|
||||
try {
|
||||
fn();
|
||||
@@ -44,6 +46,12 @@ function withHome(homeDir, fn) {
|
||||
} else {
|
||||
delete process.env.HOME;
|
||||
}
|
||||
|
||||
if (typeof previousUserProfile === 'string') {
|
||||
process.env.USERPROFILE = previousUserProfile;
|
||||
} else {
|
||||
delete process.env.USERPROFILE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,13 +64,14 @@ let totalTests = 0;
|
||||
|
||||
for (const testFile of testFiles) {
|
||||
const testPath = path.join(testsDir, testFile);
|
||||
const displayPath = testFile.split(path.sep).join('/');
|
||||
|
||||
if (!fs.existsSync(testPath)) {
|
||||
console.log(`⚠ Skipping ${testFile} (file not found)`);
|
||||
console.log(`⚠ Skipping ${displayPath} (file not found)`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`\n━━━ Running ${testFile} ━━━`);
|
||||
console.log(`\n━━━ Running ${displayPath} ━━━`);
|
||||
|
||||
const result = spawnSync('node', [testPath], {
|
||||
encoding: 'utf8',
|
||||
@@ -93,13 +94,13 @@ for (const testFile of testFiles) {
|
||||
if (failedMatch) totalFailed += parseInt(failedMatch[1], 10);
|
||||
|
||||
if (result.error) {
|
||||
console.log(`✗ ${testFile} failed to start: ${result.error.message}`);
|
||||
console.log(`✗ ${displayPath} failed to start: ${result.error.message}`);
|
||||
totalFailed += failedMatch ? 0 : 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (result.status !== 0) {
|
||||
console.log(`✗ ${testFile} exited with status ${result.status}`);
|
||||
console.log(`✗ ${displayPath} exited with status ${result.status}`);
|
||||
totalFailed += failedMatch ? 0 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,13 +11,25 @@ const { spawnSync } = require('child_process');
|
||||
const SCRIPT = path.join(__dirname, '..', '..', 'scripts', 'ecc.js');
|
||||
|
||||
function runCli(args, options = {}) {
|
||||
const envOverrides = {
|
||||
...(options.env || {}),
|
||||
};
|
||||
|
||||
if (typeof envOverrides.HOME === 'string' && !('USERPROFILE' in envOverrides)) {
|
||||
envOverrides.USERPROFILE = envOverrides.HOME;
|
||||
}
|
||||
|
||||
if (typeof envOverrides.USERPROFILE === 'string' && !('HOME' in envOverrides)) {
|
||||
envOverrides.HOME = envOverrides.USERPROFILE;
|
||||
}
|
||||
|
||||
return spawnSync('node', [SCRIPT, ...args], {
|
||||
encoding: 'utf8',
|
||||
cwd: options.cwd || process.cwd(),
|
||||
maxBuffer: 10 * 1024 * 1024,
|
||||
env: {
|
||||
...process.env,
|
||||
...(options.env || {}),
|
||||
...envOverrides,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
117
tests/scripts/install-ps1.test.js
Normal file
117
tests/scripts/install-ps1.test.js
Normal file
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Tests for install.ps1 wrapper delegation
|
||||
*/
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const { execFileSync, spawnSync } = require('child_process');
|
||||
|
||||
const SCRIPT = path.join(__dirname, '..', '..', 'install.ps1');
|
||||
const PACKAGE_JSON = path.join(__dirname, '..', '..', 'package.json');
|
||||
|
||||
function createTempDir(prefix) {
|
||||
return fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
||||
}
|
||||
|
||||
function cleanup(dirPath) {
|
||||
fs.rmSync(dirPath, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
function resolvePowerShellCommand() {
|
||||
const candidates = process.platform === 'win32'
|
||||
? ['powershell.exe', 'pwsh.exe', 'pwsh']
|
||||
: ['pwsh'];
|
||||
|
||||
for (const candidate of candidates) {
|
||||
const result = spawnSync(candidate, ['-NoLogo', '-NoProfile', '-Command', '$PSVersionTable.PSVersion.ToString()'], {
|
||||
encoding: 'utf8',
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
timeout: 5000,
|
||||
});
|
||||
|
||||
if (!result.error && result.status === 0) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function run(powerShellCommand, args = [], options = {}) {
|
||||
const env = {
|
||||
...process.env,
|
||||
HOME: options.homeDir || process.env.HOME,
|
||||
USERPROFILE: options.homeDir || process.env.USERPROFILE,
|
||||
};
|
||||
|
||||
try {
|
||||
const stdout = execFileSync(powerShellCommand, ['-NoLogo', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', SCRIPT, ...args], {
|
||||
cwd: options.cwd,
|
||||
env,
|
||||
encoding: 'utf8',
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
return { code: 0, stdout, stderr: '' };
|
||||
} catch (error) {
|
||||
return {
|
||||
code: error.status || 1,
|
||||
stdout: error.stdout || '',
|
||||
stderr: error.stderr || '',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(` \u2713 ${name}`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.log(` \u2717 ${name}`);
|
||||
console.log(` Error: ${error.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
console.log('\n=== Testing install.ps1 ===\n');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
const powerShellCommand = resolvePowerShellCommand();
|
||||
|
||||
if (test('publishes ecc-install through the Node installer runtime for cross-platform npm usage', () => {
|
||||
const packageJson = JSON.parse(fs.readFileSync(PACKAGE_JSON, 'utf8'));
|
||||
assert.strictEqual(packageJson.bin['ecc-install'], 'scripts/install-apply.js');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (!powerShellCommand) {
|
||||
console.log(' - skipped delegation test; PowerShell is not available in PATH');
|
||||
} else if (test('delegates to the Node installer and preserves dry-run output', () => {
|
||||
const homeDir = createTempDir('install-ps1-home-');
|
||||
const projectDir = createTempDir('install-ps1-project-');
|
||||
|
||||
try {
|
||||
const result = run(powerShellCommand, ['--target', 'cursor', '--dry-run', 'typescript'], {
|
||||
cwd: projectDir,
|
||||
homeDir,
|
||||
});
|
||||
|
||||
assert.strictEqual(result.code, 0, result.stderr);
|
||||
assert.ok(result.stdout.includes('Dry-run install plan'));
|
||||
assert.ok(!fs.existsSync(path.join(projectDir, '.cursor', 'hooks.json')));
|
||||
} finally {
|
||||
cleanup(homeDir);
|
||||
cleanup(projectDir);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
runTests();
|
||||
@@ -61,6 +61,12 @@ function runTests() {
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
console.log(' - skipped on Windows; install.ps1 covers the native wrapper path');
|
||||
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (test('delegates to the Node installer and preserves dry-run output', () => {
|
||||
const homeDir = createTempDir('install-sh-home-');
|
||||
const projectDir = createTempDir('install-sh-project-');
|
||||
|
||||
@@ -13,6 +13,18 @@ const { getFallbackSessionRecordingPath } = require('../../scripts/lib/session-a
|
||||
const SCRIPT = path.join(__dirname, '..', '..', 'scripts', 'session-inspect.js');
|
||||
|
||||
function run(args = [], options = {}) {
|
||||
const envOverrides = {
|
||||
...(options.env || {})
|
||||
};
|
||||
|
||||
if (typeof envOverrides.HOME === 'string' && !('USERPROFILE' in envOverrides)) {
|
||||
envOverrides.USERPROFILE = envOverrides.HOME;
|
||||
}
|
||||
|
||||
if (typeof envOverrides.USERPROFILE === 'string' && !('HOME' in envOverrides)) {
|
||||
envOverrides.HOME = envOverrides.USERPROFILE;
|
||||
}
|
||||
|
||||
try {
|
||||
const stdout = execFileSync('node', [SCRIPT, ...args], {
|
||||
encoding: 'utf8',
|
||||
@@ -21,7 +33,7 @@ function run(args = [], options = {}) {
|
||||
cwd: options.cwd || process.cwd(),
|
||||
env: {
|
||||
...process.env,
|
||||
...(options.env || {})
|
||||
...envOverrides
|
||||
}
|
||||
});
|
||||
return { code: 0, stdout, stderr: '' };
|
||||
|
||||
Reference in New Issue
Block a user