Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions packages/safe-chain/src/shell-integration/setup-ci.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import chalk from "chalk";
import { ui } from "../environment/userInteraction.js";
import { getPackageManagerList, knownAikidoTools, getShimsDir } from "./helpers.js";
import { detectShells } from "./shellDetection.js";
import fs from "fs";
import os from "os";
import path from "path";
Expand Down Expand Up @@ -37,12 +38,33 @@ export async function setupCi() {
fs.mkdirSync(shimsDir, { recursive: true });
}

cleanupLegacyShellInit();
createShims(shimsDir);
ui.writeInformation(`Created shims in ${shimsDir}`);
modifyPathForCi(shimsDir, binDir);
ui.writeInformation(`Added shims directory to PATH for CI environments.`);
}

/**
* Removes shell-based initialization from RC files when switching to --ci install mode.
*/
function cleanupLegacyShellInit() {
let shells;
try {
shells = detectShells();
} catch {
Comment thread
reiniercriel marked this conversation as resolved.
return;
}

for (const shell of shells) {
try {
shell.teardown(knownAikidoTools);
} catch {
// Best-effort cleanup — don't fail the install if teardown errors
}
}
}

/**
* @param {string} shimsDir
*
Expand Down
7 changes: 7 additions & 0 deletions packages/safe-chain/src/shell-integration/setup-ci.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ describe("Setup CI shell integration", () => {
},
});

// Mock shell detection to avoid touching real RC files during tests
mock.module("./shellDetection.js", {
namedExports: {
detectShells: () => [],
},
});

// Mock the helpers module
mock.module("./helpers.js", {
namedExports: {
Expand Down
40 changes: 40 additions & 0 deletions test/e2e/setup-ci.e2e.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,46 @@ describe("E2E: safe-chain setup-ci command", () => {
});
}

for (let shell of ["bash", "zsh"]) {
it(`safe-chain setup-ci removes legacy shell init left by a previous setup for ${shell}`, async () => {
const rcFile = shell === "zsh" ? "~/.zshrc" : "~/.bashrc";

// Simulate a previous non-CI install
const installationShell = await container.openShell(shell);
await installationShell.runCommand("safe-chain setup");

// Verify the legacy init line was added
const afterSetup = await installationShell.runCommand(`grep -c "init-posix.sh" ${rcFile} || true`);
assert.ok(
afterSetup.output.includes("1"),
`Expected init-posix.sh to be present in ${rcFile} after setup`
);

// Now run the CI install — should clean up the legacy line
await installationShell.runCommand("safe-chain setup-ci");
await installationShell.runCommand(
`echo 'export PATH="$HOME/.safe-chain/shims:$PATH"' >> ${rcFile}`
);

const afterSetupCi = await installationShell.runCommand(`grep -c "init-posix.sh" ${rcFile} || true`);
assert.ok(
!afterSetupCi.output.includes("1"),
`Expected init-posix.sh to be removed from ${rcFile} after setup-ci, but it was still present`
);

// Verify npm still works through shims in a new shell
const projectShell = await container.openShell(shell);
const result = await projectShell.runCommand(
"npm i axios@1.13.0 --safe-chain-logging=verbose"
);

assert.ok(
result.output.includes("Safe-chain: Scanned"),
`Expected npm to be wrapped by safe-chain shim after setup-ci.\nOutput was:\n${result.output}`
);
});
}

it("writes to GITHUB_PATH when GITHUB_PATH is set", async () => {
const installationShell = await container.openShell("zsh");
await installationShell.runCommand("export GITHUB_PATH=/tmp/github_path");
Expand Down
Loading