mirror of
https://github.com/AikidoSec/safe-chain.git
synced 2026-05-26 12:10:49 +00:00
194 lines
5.2 KiB
JavaScript
194 lines
5.2 KiB
JavaScript
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";
|
|
import { fileURLToPath } from "url";
|
|
|
|
/** @type {string} */
|
|
// This checks the current file's dirname in a way that's compatible with:
|
|
// - Modulejs (import.meta.url)
|
|
// - ES modules (__dirname)
|
|
// This is needed because safe-chain's npm package is built using ES modules,
|
|
// but building the binaries requires commonjs.
|
|
let dirname;
|
|
if (import.meta.url) {
|
|
const filename = fileURLToPath(import.meta.url);
|
|
dirname = path.dirname(filename);
|
|
} else {
|
|
dirname = __dirname;
|
|
}
|
|
|
|
/**
|
|
* Loops over the detected shells and calls the setup function for each.
|
|
*/
|
|
export async function setupCi() {
|
|
ui.writeInformation(
|
|
chalk.bold("Setting up shell aliases.") +
|
|
` This will wrap safe-chain around ${getPackageManagerList()}.`
|
|
);
|
|
ui.emptyLine();
|
|
|
|
const shimsDir = getShimsDir();
|
|
const binDir = path.join(os.homedir(), ".safe-chain", "bin");
|
|
// Create the shims directory if it doesn't exist
|
|
if (!fs.existsSync(shimsDir)) {
|
|
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 {
|
|
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
|
|
*
|
|
* @returns {void}
|
|
*/
|
|
function createUnixShims(shimsDir) {
|
|
// Read the template file
|
|
const templatePath = path.resolve(
|
|
dirname,
|
|
"path-wrappers",
|
|
"templates",
|
|
"unix-wrapper.template.sh"
|
|
);
|
|
|
|
if (!fs.existsSync(templatePath)) {
|
|
ui.writeError(`Template file not found: ${templatePath}`);
|
|
return;
|
|
}
|
|
|
|
const template = fs.readFileSync(templatePath, "utf-8");
|
|
|
|
// Create a shim for each tool
|
|
let created = 0;
|
|
for (const toolInfo of getToolsToSetup()) {
|
|
const shimContent = template
|
|
.replaceAll("{{PACKAGE_MANAGER}}", toolInfo.tool)
|
|
.replaceAll("{{AIKIDO_COMMAND}}", toolInfo.aikidoCommand);
|
|
|
|
const shimPath = path.join(shimsDir, toolInfo.tool);
|
|
fs.writeFileSync(shimPath, shimContent, "utf-8");
|
|
|
|
// Make the shim executable on Unix systems
|
|
fs.chmodSync(shimPath, 0o755);
|
|
created++;
|
|
}
|
|
|
|
ui.writeInformation(`Created ${created} Unix shim(s) in ${shimsDir}`);
|
|
}
|
|
|
|
/**
|
|
* @param {string} shimsDir
|
|
*
|
|
* @returns {void}
|
|
*/
|
|
function createWindowsShims(shimsDir) {
|
|
// Read the template file
|
|
const templatePath = path.resolve(
|
|
dirname,
|
|
"path-wrappers",
|
|
"templates",
|
|
"windows-wrapper.template.cmd"
|
|
);
|
|
|
|
if (!fs.existsSync(templatePath)) {
|
|
ui.writeError(`Windows template file not found: ${templatePath}`);
|
|
return;
|
|
}
|
|
|
|
const template = fs.readFileSync(templatePath, "utf-8");
|
|
|
|
// Create a shim for each tool
|
|
let created = 0;
|
|
for (const toolInfo of getToolsToSetup()) {
|
|
const shimContent = template
|
|
.replaceAll("{{PACKAGE_MANAGER}}", toolInfo.tool)
|
|
.replaceAll("{{AIKIDO_COMMAND}}", toolInfo.aikidoCommand);
|
|
|
|
const shimPath = `${shimsDir}/${toolInfo.tool}.cmd`;
|
|
fs.writeFileSync(shimPath, shimContent, "utf-8");
|
|
created++;
|
|
}
|
|
|
|
ui.writeInformation(`Created ${created} Windows shim(s) in ${shimsDir}`);
|
|
}
|
|
|
|
/**
|
|
* @param {string} shimsDir
|
|
*
|
|
* @returns {void}
|
|
*/
|
|
function createShims(shimsDir) {
|
|
if (os.platform() === "win32") {
|
|
createWindowsShims(shimsDir);
|
|
} else {
|
|
createUnixShims(shimsDir);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {string} shimsDir
|
|
* @param {string} binDir
|
|
*
|
|
* @returns {void}
|
|
*/
|
|
function modifyPathForCi(shimsDir, binDir) {
|
|
if (process.env.GITHUB_PATH) {
|
|
// In GitHub Actions, append the shims directory to GITHUB_PATH
|
|
fs.appendFileSync(
|
|
process.env.GITHUB_PATH,
|
|
shimsDir + os.EOL + binDir + os.EOL,
|
|
"utf-8"
|
|
);
|
|
ui.writeInformation(
|
|
`Added shims directory to GITHUB_PATH for GitHub Actions.`
|
|
);
|
|
}
|
|
|
|
if (process.env.TF_BUILD) {
|
|
// In Azure Pipelines, prepending the path is done via a logging command:
|
|
// ##vso[task.prependpath]/path/to/add
|
|
// Logging this to stdout will cause the Azure Pipelines agent to pick it up
|
|
ui.writeInformation("##vso[task.prependpath]" + shimsDir);
|
|
ui.writeInformation("##vso[task.prependpath]" + binDir);
|
|
}
|
|
|
|
if (process.env.BASH_ENV) {
|
|
// In CircleCI, persisting PATH across steps is done by appending shell exports
|
|
// to the file referenced by BASH_ENV. CircleCI sources this file for 'run' each step.
|
|
const exportLine = `export PATH="${shimsDir}:${binDir}:$PATH"` + os.EOL;
|
|
fs.appendFileSync(process.env.BASH_ENV, exportLine, "utf-8");
|
|
ui.writeInformation(`Added shims directory to BASH_ENV for CircleCI.`);
|
|
}
|
|
}
|
|
|
|
function getToolsToSetup() {
|
|
return knownAikidoTools;
|
|
}
|