Merge pull request #238 from AikidoSec/feature/cleanup-shims

Cleanup shims at teardown
This commit is contained in:
Reinier Criel 2025-12-15 01:36:19 -08:00 committed by GitHub
commit fc5df6cd14
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 164 additions and 8 deletions

View file

@ -3,7 +3,7 @@
import chalk from "chalk"; import chalk from "chalk";
import { ui } from "../src/environment/userInteraction.js"; import { ui } from "../src/environment/userInteraction.js";
import { setup } from "../src/shell-integration/setup.js"; import { setup } from "../src/shell-integration/setup.js";
import { teardown } from "../src/shell-integration/teardown.js"; import { teardown, teardownDirectories } from "../src/shell-integration/teardown.js";
import { setupCi } from "../src/shell-integration/setup-ci.js"; import { setupCi } from "../src/shell-integration/setup-ci.js";
import { initializeCliArguments } from "../src/config/cliArguments.js"; import { initializeCliArguments } from "../src/config/cliArguments.js";
import { setEcoSystem } from "../src/config/settings.js"; import { setEcoSystem } from "../src/config/settings.js";
@ -60,6 +60,7 @@ if (tool) {
} else if (command === "setup") { } else if (command === "setup") {
setup(); setup();
} else if (command === "teardown") { } else if (command === "teardown") {
teardownDirectories();
teardown(); teardown();
} else if (command === "setup-ci") { } else if (command === "setup-ci") {
setupCi(); setupCi();

View file

@ -113,6 +113,20 @@ export function getPackageManagerList() {
return `${tools.join(", ")}, and ${lastTool} commands`; return `${tools.join(", ")}, and ${lastTool} commands`;
} }
/**
* @returns {string}
*/
export function getShimsDir() {
return path.join(os.homedir(), ".safe-chain", "shims");
}
/**
* @returns {string}
*/
export function getScriptsDir() {
return path.join(os.homedir(), ".safe-chain", "scripts");
}
/** /**
* @param {string} executableName * @param {string} executableName
* *

View file

@ -1,6 +1,6 @@
import chalk from "chalk"; import chalk from "chalk";
import { ui } from "../environment/userInteraction.js"; import { ui } from "../environment/userInteraction.js";
import { getPackageManagerList, knownAikidoTools } from "./helpers.js"; import { getPackageManagerList, knownAikidoTools, getShimsDir } from "./helpers.js";
import fs from "fs"; import fs from "fs";
import os from "os"; import os from "os";
import path from "path"; import path from "path";
@ -32,7 +32,7 @@ export async function setupCi() {
); );
ui.emptyLine(); ui.emptyLine();
const shimsDir = path.join(os.homedir(), ".safe-chain", "shims"); const shimsDir = getShimsDir();
const binDir = path.join(os.homedir(), ".safe-chain", "bin"); const binDir = path.join(os.homedir(), ".safe-chain", "bin");
// Create the shims directory if it doesn't exist // Create the shims directory if it doesn't exist
if (!fs.existsSync(shimsDir)) { if (!fs.existsSync(shimsDir)) {

View file

@ -50,6 +50,7 @@ describe("Setup CI shell integration", () => {
{ tool: "yarn", aikidoCommand: "aikido-yarn" }, { tool: "yarn", aikidoCommand: "aikido-yarn" },
], ],
getPackageManagerList: () => "npm, yarn", getPackageManagerList: () => "npm, yarn",
getShimsDir: () => mockShimsDir,
}, },
}); });

View file

@ -1,9 +1,8 @@
import chalk from "chalk"; import chalk from "chalk";
import { ui } from "../environment/userInteraction.js"; import { ui } from "../environment/userInteraction.js";
import { detectShells } from "./shellDetection.js"; import { detectShells } from "./shellDetection.js";
import { knownAikidoTools, getPackageManagerList } from "./helpers.js"; import { knownAikidoTools, getPackageManagerList, getScriptsDir } from "./helpers.js";
import fs from "fs"; import fs from "fs";
import os from "os";
import path from "path"; import path from "path";
import { includePython } from "../config/cliArguments.js"; import { includePython } from "../config/cliArguments.js";
import { fileURLToPath } from "url"; import { fileURLToPath } from "url";
@ -107,10 +106,10 @@ function setupShell(shell) {
function copyStartupFiles() { function copyStartupFiles() {
const startupFiles = ["init-posix.sh", "init-pwsh.ps1", "init-fish.fish"]; const startupFiles = ["init-posix.sh", "init-pwsh.ps1", "init-fish.fish"];
const targetDir = getScriptsDir();
for (const file of startupFiles) { for (const file of startupFiles) {
const targetDir = path.join(os.homedir(), ".safe-chain", "scripts"); const targetPath = path.join(targetDir, file);
const targetPath = path.join(os.homedir(), ".safe-chain", "scripts", file);
if (!fs.existsSync(targetDir)) { if (!fs.existsSync(targetDir)) {
fs.mkdirSync(targetDir, { recursive: true }); fs.mkdirSync(targetDir, { recursive: true });

View file

@ -1,7 +1,8 @@
import chalk from "chalk"; import chalk from "chalk";
import { ui } from "../environment/userInteraction.js"; import { ui } from "../environment/userInteraction.js";
import { detectShells } from "./shellDetection.js"; import { detectShells } from "./shellDetection.js";
import { knownAikidoTools, getPackageManagerList } from "./helpers.js"; import { knownAikidoTools, getPackageManagerList, getShimsDir, getScriptsDir } from "./helpers.js";
import fs from "fs";
/** /**
* @returns {Promise<void>} * @returns {Promise<void>}
@ -62,3 +63,44 @@ export async function teardown() {
return; return;
} }
} }
/**
* Removes directories created by setup-ci and setup commands
* @returns {Promise<void>}
*/
export async function teardownDirectories() {
const shimsDir = getShimsDir();
const scriptsDir = getScriptsDir();
// Remove CI shims directory
if (fs.existsSync(shimsDir)) {
try {
fs.rmSync(shimsDir, { recursive: true, force: true });
ui.writeInformation(
`${chalk.bold("- CI Shims:")} ${chalk.green("Removed successfully")}`
);
} catch (/** @type {any} */ error) {
ui.writeError(
`${chalk.bold("- CI Shims:")} ${chalk.red(
"Failed to remove"
)}. Error: ${error.message}`
);
}
}
// Remove scripts directory
if (fs.existsSync(scriptsDir)) {
try {
fs.rmSync(scriptsDir, { recursive: true, force: true });
ui.writeInformation(
`${chalk.bold("- Scripts:")} ${chalk.green("Removed successfully")}`
);
} catch (/** @type {any} */ error) {
ui.writeError(
`${chalk.bold("- Scripts:")} ${chalk.red(
"Failed to remove"
)}. Error: ${error.message}`
);
}
}
}

View file

@ -0,0 +1,99 @@
import { describe, it, before, beforeEach, afterEach } from "node:test";
import { DockerTestContainer } from "./DockerTestContainer.js";
import assert from "node:assert";
describe("E2E: safe-chain teardown command", () => {
let container;
before(async () => {
DockerTestContainer.buildImage();
});
beforeEach(async () => {
container = new DockerTestContainer();
await container.start();
});
afterEach(async () => {
if (container) {
await container.stop();
container = null;
}
});
it("safe-chain teardown removes shims directory created by setup-ci", async () => {
const shell = await container.openShell("bash");
// Run setup-ci
await shell.runCommand("safe-chain setup-ci");
// Verify shims directory exists
const checkShimsExist = await shell.runCommand("test -d ~/.safe-chain/shims && echo 'exists' || echo 'missing'");
assert.ok(checkShimsExist.output.includes("exists"), "Shims directory should exist after setup-ci");
// Run teardown
await shell.runCommand("safe-chain teardown");
// Verify shims directory is gone
const checkShimsGone = await shell.runCommand("test -d ~/.safe-chain/shims && echo 'exists' || echo 'missing'");
assert.ok(checkShimsGone.output.includes("missing"), "Shims directory should be removed after teardown");
});
it("safe-chain teardown removes scripts directory created by setup", async () => {
const shell = await container.openShell("bash");
// Run setup
await shell.runCommand("safe-chain setup");
// Verify scripts directory exists
const checkScriptsExist = await shell.runCommand("test -d ~/.safe-chain/scripts && echo 'exists' || echo 'missing'");
assert.ok(checkScriptsExist.output.includes("exists"), "Scripts directory should exist after setup");
// Run teardown
await shell.runCommand("safe-chain teardown");
// Verify scripts directory is gone
const checkScriptsGone = await shell.runCommand("test -d ~/.safe-chain/scripts && echo 'exists' || echo 'missing'");
assert.ok(checkScriptsGone.output.includes("missing"), "Scripts directory should be removed after teardown");
});
it("safe-chain teardown removes shims directory created by setup-ci --include-python", async () => {
const shell = await container.openShell("bash");
// Run setup-ci with --include-python
await shell.runCommand("safe-chain setup-ci --include-python");
// Verify shims directory exists
const checkShimsExist = await shell.runCommand("test -d ~/.safe-chain/shims && echo 'exists' || echo 'missing'");
assert.ok(checkShimsExist.output.includes("exists"), "Shims directory should exist after setup-ci --include-python");
// Verify Python shims were created
const checkPythonShims = await shell.runCommand("test -f ~/.safe-chain/shims/pip && echo 'exists' || echo 'missing'");
assert.ok(checkPythonShims.output.includes("exists"), "Python shims should exist after setup-ci --include-python");
// Run teardown
await shell.runCommand("safe-chain teardown");
// Verify shims directory is gone
const checkShimsGone = await shell.runCommand("test -d ~/.safe-chain/shims && echo 'exists' || echo 'missing'");
assert.ok(checkShimsGone.output.includes("missing"), "Shims directory should be removed after teardown");
});
it("safe-chain teardown removes scripts directory created by setup --include-python", async () => {
const shell = await container.openShell("bash");
// Run setup with --include-python
await shell.runCommand("safe-chain setup --include-python");
// Verify scripts directory exists
const checkScriptsExist = await shell.runCommand("test -d ~/.safe-chain/scripts && echo 'exists' || echo 'missing'");
assert.ok(checkScriptsExist.output.includes("exists"), "Scripts directory should exist after setup --include-python");
// Run teardown
await shell.runCommand("safe-chain teardown");
// Verify scripts directory is gone
const checkScriptsGone = await shell.runCommand("test -d ~/.safe-chain/scripts && echo 'exists' || echo 'missing'");
assert.ok(checkScriptsGone.output.includes("missing"), "Scripts directory should be removed after teardown");
});
});