mirror of
https://github.com/AikidoSec/safe-chain.git
synced 2026-05-26 12:10:49 +00:00
Merge branch 'main' into feat/pdm-support
This commit is contained in:
commit
abbe0480b6
52 changed files with 1603 additions and 1348 deletions
|
|
@ -66,6 +66,12 @@ export const knownAikidoTools = [
|
|||
ecoSystem: ECOSYSTEM_PY,
|
||||
internalPackageManagerName: "uv",
|
||||
},
|
||||
{
|
||||
tool: "uvx",
|
||||
aikidoCommand: "aikido-uvx",
|
||||
ecoSystem: ECOSYSTEM_PY,
|
||||
internalPackageManagerName: "uvx",
|
||||
},
|
||||
{
|
||||
tool: "pip",
|
||||
aikidoCommand: "aikido-pip",
|
||||
|
|
@ -127,20 +133,6 @@ export function getPackageManagerList() {
|
|||
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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { describe, it, beforeEach, afterEach, mock } from "node:test";
|
||||
import assert from "node:assert";
|
||||
import { tmpdir } from "node:os";
|
||||
import { tmpdir, homedir } from "node:os";
|
||||
import fs from "node:fs";
|
||||
import path from "path";
|
||||
|
||||
|
|
@ -15,6 +15,7 @@ describe("removeLinesMatchingPatternTests", () => {
|
|||
mock.module("node:os", {
|
||||
namedExports: {
|
||||
EOL: "\r\n", // Simulate Windows line endings
|
||||
homedir,
|
||||
tmpdir: tmpdir,
|
||||
platform: () => "linux",
|
||||
},
|
||||
|
|
@ -182,3 +183,30 @@ describe("removeLinesMatchingPatternTests", () => {
|
|||
assert.strictEqual(resultLines.length, 5, "Should have exactly 5 lines");
|
||||
});
|
||||
});
|
||||
|
||||
describe("getSafeChainBaseDir / getBinDir / getShimsDir / getScriptsDir", () => {
|
||||
it("defaults base dir to ~/.safe-chain when no packaged install dir is available", async () => {
|
||||
const { getSafeChainBaseDir } = await import("../config/safeChainDir.js");
|
||||
assert.strictEqual(getSafeChainBaseDir(), path.join(homedir(), ".safe-chain"));
|
||||
});
|
||||
|
||||
it("getBinDir returns ~/.safe-chain/bin by default", async () => {
|
||||
const { getBinDir } = await import("../config/safeChainDir.js");
|
||||
assert.strictEqual(getBinDir(), path.join(homedir(), ".safe-chain", "bin"));
|
||||
});
|
||||
|
||||
it("getShimsDir returns ~/.safe-chain/shims by default", async () => {
|
||||
const { getShimsDir } = await import("../config/safeChainDir.js");
|
||||
assert.strictEqual(getShimsDir(), path.join(homedir(), ".safe-chain", "shims"));
|
||||
});
|
||||
|
||||
it("getScriptsDir returns ~/.safe-chain/scripts by default", async () => {
|
||||
const { getScriptsDir } = await import("../config/safeChainDir.js");
|
||||
assert.strictEqual(getScriptsDir(), path.join(homedir(), ".safe-chain", "scripts"));
|
||||
});
|
||||
|
||||
it("getCertsDir returns ~/.safe-chain/certs by default", async () => {
|
||||
const { getCertsDir } = await import("../config/safeChainDir.js");
|
||||
assert.strictEqual(getCertsDir(), path.join(homedir(), ".safe-chain", "certs"));
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,13 +4,28 @@
|
|||
|
||||
# Function to remove shim from PATH (POSIX-compliant)
|
||||
remove_shim_from_path() {
|
||||
echo "$PATH" | sed "s|$HOME/.safe-chain/shims:||g"
|
||||
_safe_chain_phys=$(CDPATH= cd -- "$(dirname -- "$0")" 2>/dev/null && pwd -P)
|
||||
if [ -z "$_safe_chain_phys" ]; then
|
||||
echo "$PATH"
|
||||
return
|
||||
fi
|
||||
_path=$(echo "$PATH" | sed "s|${_safe_chain_phys}:||g")
|
||||
# Also remove via dirname of $0 directly — on macOS /tmp is a symlink to /private/tmp,
|
||||
# so pwd -P resolves to /private/tmp/… but PATH may still contain /tmp/….
|
||||
_dir=$(dirname -- "$0")
|
||||
case "$_dir" in
|
||||
/*) [ "$_dir" != "$_safe_chain_phys" ] && _path=$(echo "$_path" | sed "s|${_dir}:||g") ;;
|
||||
esac
|
||||
echo "$_path"
|
||||
}
|
||||
|
||||
if command -v safe-chain >/dev/null 2>&1; then
|
||||
# Remove shim directory from PATH when calling {{AIKIDO_COMMAND}} to prevent infinite loops
|
||||
PATH=$(remove_shim_from_path) exec safe-chain {{PACKAGE_MANAGER}} "$@"
|
||||
else
|
||||
# safe-chain is not reachable — warn the user so they know protection is inactive
|
||||
printf "\033[43;30mWarning:\033[0m safe-chain is not available to protect you from installing malware. {{PACKAGE_MANAGER}} will run without it.\n" >&2
|
||||
|
||||
# Dynamically find original {{PACKAGE_MANAGER}} (excluding this shim directory)
|
||||
original_cmd=$(PATH=$(remove_shim_from_path) command -v {{PACKAGE_MANAGER}})
|
||||
if [ -n "$original_cmd" ]; then
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ REM Generated wrapper for {{PACKAGE_MANAGER}} by safe-chain
|
|||
REM This wrapper intercepts {{PACKAGE_MANAGER}} calls for non-interactive environments
|
||||
|
||||
REM Remove shim directory from PATH to prevent infinite loops
|
||||
set "SHIM_DIR=%USERPROFILE%\.safe-chain\shims"
|
||||
set "SHIM_DIR=%~dp0"
|
||||
if "%SHIM_DIR:~-1%"=="\" set "SHIM_DIR=%SHIM_DIR:~0,-1%"
|
||||
call set "CLEAN_PATH=%%PATH:%SHIM_DIR%;=%%"
|
||||
|
||||
REM Check if aikido command is available with clean PATH
|
||||
|
|
@ -21,4 +22,4 @@ if %errorlevel%==0 (
|
|||
REM If we get here, original command was not found
|
||||
echo Error: Could not find original {{PACKAGE_MANAGER}} >&2
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,24 +1,14 @@
|
|||
import chalk from "chalk";
|
||||
import { ui } from "../environment/userInteraction.js";
|
||||
import { getPackageManagerList, knownAikidoTools, getShimsDir } from "./helpers.js";
|
||||
import { getPackageManagerList, knownAikidoTools } from "./helpers.js";
|
||||
import {
|
||||
getShimsDir,
|
||||
getBinDir,
|
||||
getPathWrapperTemplatePath,
|
||||
} from "../config/safeChainDir.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.
|
||||
|
|
@ -31,7 +21,7 @@ export async function setupCi() {
|
|||
ui.emptyLine();
|
||||
|
||||
const shimsDir = getShimsDir();
|
||||
const binDir = path.join(os.homedir(), ".safe-chain", "bin");
|
||||
const binDir = getBinDir();
|
||||
// Create the shims directory if it doesn't exist
|
||||
if (!fs.existsSync(shimsDir)) {
|
||||
fs.mkdirSync(shimsDir, { recursive: true });
|
||||
|
|
@ -50,12 +40,7 @@ export async function setupCi() {
|
|||
*/
|
||||
function createUnixShims(shimsDir) {
|
||||
// Read the template file
|
||||
const templatePath = path.resolve(
|
||||
dirname,
|
||||
"path-wrappers",
|
||||
"templates",
|
||||
"unix-wrapper.template.sh"
|
||||
);
|
||||
const templatePath = getPathWrapperTemplatePath(import.meta.url, "unix-wrapper.template.sh");
|
||||
|
||||
if (!fs.existsSync(templatePath)) {
|
||||
ui.writeError(`Template file not found: ${templatePath}`);
|
||||
|
|
@ -89,12 +74,7 @@ function createUnixShims(shimsDir) {
|
|||
*/
|
||||
function createWindowsShims(shimsDir) {
|
||||
// Read the template file
|
||||
const templatePath = path.resolve(
|
||||
dirname,
|
||||
"path-wrappers",
|
||||
"templates",
|
||||
"windows-wrapper.template.cmd"
|
||||
);
|
||||
const templatePath = getPathWrapperTemplatePath(import.meta.url, "windows-wrapper.template.cmd");
|
||||
|
||||
if (!fs.existsSync(templatePath)) {
|
||||
ui.writeError(`Windows template file not found: ${templatePath}`);
|
||||
|
|
|
|||
|
|
@ -22,12 +22,12 @@ describe("Setup CI shell integration", () => {
|
|||
fs.mkdirSync(path.join(mockTemplateDir, "path-wrappers", "templates"), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(mockTemplateDir, "path-wrappers", "templates", "unix-wrapper.template.sh"),
|
||||
"#!/bin/bash\n# Template for {{PACKAGE_MANAGER}}\nexec {{AIKIDO_COMMAND}} \"$@\"\n",
|
||||
"#!/bin/bash\n# Template for {{PACKAGE_MANAGER}}\n_safe_chain_shims=$(CDPATH= cd -- \"$(dirname -- \"$0\")\" 2>/dev/null && pwd -P)\nexec {{AIKIDO_COMMAND}} \"$@\"\n",
|
||||
"utf-8"
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(mockTemplateDir, "path-wrappers", "templates", "windows-wrapper.template.cmd"),
|
||||
"@echo off\nREM Template for {{PACKAGE_MANAGER}}\n{{AIKIDO_COMMAND}} %*\n",
|
||||
"@echo off\nset \"SHIM_DIR=%~dp0\"\n{{AIKIDO_COMMAND}} %*\n",
|
||||
"utf-8"
|
||||
);
|
||||
|
||||
|
|
@ -50,7 +50,15 @@ describe("Setup CI shell integration", () => {
|
|||
{ tool: "yarn", aikidoCommand: "aikido-yarn" },
|
||||
],
|
||||
getPackageManagerList: () => "npm, yarn",
|
||||
},
|
||||
});
|
||||
|
||||
mock.module("../config/safeChainDir.js", {
|
||||
namedExports: {
|
||||
getShimsDir: () => mockShimsDir,
|
||||
getBinDir: () => path.join(mockHomeDir, ".safe-chain", "bin"),
|
||||
getPathWrapperTemplatePath: (_moduleUrl, fileName) =>
|
||||
path.join(mockTemplateDir, "path-wrappers", "templates", fileName),
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -63,22 +71,6 @@ describe("Setup CI shell integration", () => {
|
|||
},
|
||||
});
|
||||
|
||||
// Mock path module to resolve templates correctly
|
||||
mock.module("path", {
|
||||
namedExports: {
|
||||
join: path.join,
|
||||
dirname: () => mockTemplateDir,
|
||||
resolve: (...args) => path.resolve(mockTemplateDir, ...args.slice(1)),
|
||||
},
|
||||
});
|
||||
|
||||
// Mock fileURLToPath
|
||||
mock.module("url", {
|
||||
namedExports: {
|
||||
fileURLToPath: () => path.join(mockTemplateDir, "setup-ci.js"),
|
||||
},
|
||||
});
|
||||
|
||||
// Import setupCi module after mocking
|
||||
setupCi = (await import("./setup-ci.js")).setupCi;
|
||||
});
|
||||
|
|
@ -119,6 +111,10 @@ describe("Setup CI shell integration", () => {
|
|||
const npmShimContent = fs.readFileSync(npmShimPath, "utf-8");
|
||||
assert.ok(npmShimContent.includes("aikido-npm"), "npm shim should contain aikido-npm");
|
||||
assert.ok(npmShimContent.includes("#!/bin/bash"), "npm shim should have bash shebang");
|
||||
assert.ok(
|
||||
npmShimContent.includes("_safe_chain_shims=$(CDPATH= cd -- \"$(dirname -- \"$0\")\" 2>/dev/null && pwd -P)"),
|
||||
"npm shim should derive the shims directory from its own location",
|
||||
);
|
||||
});
|
||||
|
||||
it("should create Windows .cmd shims on win32 platform", async () => {
|
||||
|
|
@ -142,6 +138,10 @@ describe("Setup CI shell integration", () => {
|
|||
assert.ok(npmShimContent.includes("aikido-npm"), "npm.cmd should contain aikido-npm");
|
||||
assert.ok(npmShimContent.includes("@echo off"), "npm.cmd should have Windows batch header");
|
||||
assert.ok(npmShimContent.includes("%*"), "npm.cmd should use Windows argument passing");
|
||||
assert.ok(
|
||||
npmShimContent.includes('set "SHIM_DIR=%~dp0"'),
|
||||
"npm.cmd should derive the shims directory from its own location",
|
||||
);
|
||||
|
||||
// Verify Unix shims were NOT created
|
||||
const unixNpmShim = path.join(mockShimsDir, "npm");
|
||||
|
|
|
|||
|
|
@ -1,28 +1,10 @@
|
|||
import chalk from "chalk";
|
||||
import { ui } from "../environment/userInteraction.js";
|
||||
import { detectShells } from "./shellDetection.js";
|
||||
import {
|
||||
knownAikidoTools,
|
||||
getPackageManagerList,
|
||||
getScriptsDir,
|
||||
} from "./helpers.js";
|
||||
import { knownAikidoTools, getPackageManagerList } from "./helpers.js";
|
||||
import { getScriptsDir, getStartupScriptSourcePath } from "../config/safeChainDir.js";
|
||||
import fs from "fs";
|
||||
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.
|
||||
|
|
@ -122,8 +104,7 @@ function copyStartupFiles() {
|
|||
fs.mkdirSync(targetDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Use absolute path for source
|
||||
const sourcePath = path.join(dirname, "startup-scripts", file);
|
||||
const sourcePath = getStartupScriptSourcePath(import.meta.url, file);
|
||||
fs.copyFileSync(sourcePath, targetPath);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
set -gx PATH $PATH $HOME/.safe-chain/bin
|
||||
set -l safe_chain_script (status filename)
|
||||
set -l safe_chain_scripts_dir (dirname $safe_chain_script)
|
||||
set -l safe_chain_base (dirname $safe_chain_scripts_dir)
|
||||
set -gx PATH $PATH $safe_chain_base/bin
|
||||
|
||||
function npx
|
||||
wrapSafeChainCommand "npx" $argv
|
||||
|
|
@ -51,6 +54,10 @@ function uv
|
|||
wrapSafeChainCommand "uv" $argv
|
||||
end
|
||||
|
||||
function uvx
|
||||
wrapSafeChainCommand "uvx" $argv
|
||||
end
|
||||
|
||||
function poetry
|
||||
wrapSafeChainCommand "poetry" $argv
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,4 +1,16 @@
|
|||
export PATH="$PATH:$HOME/.safe-chain/bin"
|
||||
if [ -n "${BASH_SOURCE[0]:-}" ]; then
|
||||
_sc_script_path="${BASH_SOURCE[0]}"
|
||||
elif [ -n "${ZSH_VERSION:-}" ]; then
|
||||
# ${(%):-%x} uses Zsh prompt expansion to get the sourced file's path.
|
||||
# eval is required so other shells don't try to parse the Zsh-specific syntax.
|
||||
eval '_sc_script_path="${(%):-%x}"'
|
||||
else
|
||||
_sc_script_path="$0"
|
||||
fi
|
||||
_sc_scripts_dir=$(CDPATH= cd -- "$(dirname -- "$_sc_script_path")" 2>/dev/null && pwd -P)
|
||||
_sc_base=$(dirname -- "$_sc_scripts_dir")
|
||||
export PATH="$PATH:${_sc_base}/bin"
|
||||
unset _sc_base _sc_script_path _sc_scripts_dir
|
||||
|
||||
function npx() {
|
||||
wrapSafeChainCommand "npx" "$@"
|
||||
|
|
@ -47,6 +59,10 @@ function uv() {
|
|||
wrapSafeChainCommand "uv" "$@"
|
||||
}
|
||||
|
||||
function uvx() {
|
||||
wrapSafeChainCommand "uvx" "$@"
|
||||
}
|
||||
|
||||
function poetry() {
|
||||
wrapSafeChainCommand "poetry" "$@"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
# $IsWindows is only available in PowerShell Core 6.0+. If it doesn't exist, assume Windows PowerShell
|
||||
$isWindowsPlatform = if (Test-Path variable:IsWindows) { $IsWindows } else { $true }
|
||||
$pathSeparator = if ($isWindowsPlatform) { ';' } else { ':' }
|
||||
$safeChainBin = Join-Path (Join-Path $HOME '.safe-chain') 'bin'
|
||||
$safeChainBase = Split-Path -Parent $PSScriptRoot
|
||||
$safeChainBin = Join-Path $safeChainBase 'bin'
|
||||
$env:PATH = "$env:PATH$pathSeparator$safeChainBin"
|
||||
|
||||
function npx {
|
||||
|
|
@ -52,6 +53,10 @@ function uv {
|
|||
Invoke-WrappedCommand "uv" $args $MyInvocation.Line $MyInvocation.OffsetInLine
|
||||
}
|
||||
|
||||
function uvx {
|
||||
Invoke-WrappedCommand "uvx" $args $MyInvocation.Line $MyInvocation.OffsetInLine
|
||||
}
|
||||
|
||||
function poetry {
|
||||
Invoke-WrappedCommand "poetry" $args $MyInvocation.Line $MyInvocation.OffsetInLine
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@ import {
|
|||
doesExecutableExistOnSystem,
|
||||
removeLinesMatchingPattern,
|
||||
} from "../helpers.js";
|
||||
import { getScriptsDir } from "../../config/safeChainDir.js";
|
||||
import { execSync, spawnSync } from "child_process";
|
||||
import * as os from "os";
|
||||
import path from "path";
|
||||
|
||||
const shellName = "Bash";
|
||||
const executableName = "bash";
|
||||
|
|
@ -32,10 +34,10 @@ function teardown(tools) {
|
|||
);
|
||||
}
|
||||
|
||||
// Removes the line that sources the safe-chain bash initialization script (~/.safe-chain/scripts/init-posix.sh)
|
||||
// Remove sourcing line to disable safe-chain shell integration
|
||||
removeLinesMatchingPattern(
|
||||
startupFile,
|
||||
/^source\s+~\/\.safe-chain\/scripts\/init-posix\.sh/,
|
||||
/^source\s+.*init-posix\.sh.*#\s*Safe-chain/,
|
||||
eol
|
||||
);
|
||||
|
||||
|
|
@ -44,10 +46,11 @@ function teardown(tools) {
|
|||
|
||||
function setup() {
|
||||
const startupFile = getStartupFile();
|
||||
const scriptsDir = getShellScriptsDir();
|
||||
|
||||
addLineToFile(
|
||||
startupFile,
|
||||
`source ~/.safe-chain/scripts/init-posix.sh # Safe-chain bash initialization script`,
|
||||
`source ${path.posix.join(scriptsDir, "init-posix.sh")} # Safe-chain bash initialization script`,
|
||||
eol
|
||||
);
|
||||
|
||||
|
|
@ -94,6 +97,51 @@ function windowsFixPath(path) {
|
|||
}
|
||||
}
|
||||
|
||||
function getShellScriptsDir() {
|
||||
return toBashPath(getScriptsDir());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
function toBashPath(path) {
|
||||
try {
|
||||
if (os.platform() !== "win32") {
|
||||
return path.replace(/\\/g, "/");
|
||||
}
|
||||
|
||||
const directWindowsPath = windowsPathToBashPath(path);
|
||||
if (directWindowsPath) {
|
||||
return directWindowsPath;
|
||||
}
|
||||
|
||||
if (hasCygpath()) {
|
||||
return convertCygwinPathToUnix(path);
|
||||
}
|
||||
|
||||
return path.replace(/\\/g, "/");
|
||||
} catch {
|
||||
return path.replace(/\\/g, "/");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
*
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
function windowsPathToBashPath(path) {
|
||||
const match = /^([A-Za-z]):[\\/](.*)$/.exec(path);
|
||||
if (!match) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const [, driveLetter, rest] = match;
|
||||
return `/${driveLetter.toLowerCase()}/${rest.replace(/\\/g, "/")}`;
|
||||
}
|
||||
|
||||
function hasCygpath() {
|
||||
try {
|
||||
var result = spawnSync("where", ["cygpath"], { shell: executableName });
|
||||
|
|
@ -123,18 +171,40 @@ function cygpathw(path) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
function convertCygwinPathToUnix(path) {
|
||||
try {
|
||||
var result = spawnSync("cygpath", ["-u", path], {
|
||||
encoding: "utf8",
|
||||
shell: executableName,
|
||||
});
|
||||
if (result.status === 0) {
|
||||
return result.stdout.trim();
|
||||
}
|
||||
return path.replace(/\\/g, "/");
|
||||
} catch {
|
||||
return path.replace(/\\/g, "/");
|
||||
}
|
||||
}
|
||||
|
||||
function getManualTeardownInstructions() {
|
||||
const scriptsDir = getShellScriptsDir();
|
||||
return [
|
||||
`Remove the following line from your ~/.bashrc file:`,
|
||||
` source ~/.safe-chain/scripts/init-posix.sh`,
|
||||
` source ${path.posix.join(scriptsDir, "init-posix.sh")}`,
|
||||
`Then restart your terminal or run: source ~/.bashrc`,
|
||||
];
|
||||
}
|
||||
|
||||
function getManualSetupInstructions() {
|
||||
const scriptsDir = getShellScriptsDir();
|
||||
return [
|
||||
`Add the following line to your ~/.bashrc file:`,
|
||||
` source ~/.safe-chain/scripts/init-posix.sh`,
|
||||
` source ${path.posix.join(scriptsDir, "init-posix.sh")}`,
|
||||
`Then restart your terminal or run: source ~/.bashrc`,
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ describe("Bash shell integration", () => {
|
|||
let mockStartupFile;
|
||||
let bash;
|
||||
let windowsCygwinPath = "";
|
||||
let mockScriptsDir = "/test-home/.safe-chain/scripts";
|
||||
let platform = "linux";
|
||||
|
||||
beforeEach(async () => {
|
||||
|
|
@ -35,6 +36,12 @@ describe("Bash shell integration", () => {
|
|||
},
|
||||
});
|
||||
|
||||
mock.module("../../config/safeChainDir.js", {
|
||||
namedExports: {
|
||||
getScriptsDir: () => mockScriptsDir,
|
||||
},
|
||||
});
|
||||
|
||||
// Mock child_process execSync
|
||||
mock.module("child_process", {
|
||||
namedExports: {
|
||||
|
|
@ -61,6 +68,17 @@ describe("Bash shell integration", () => {
|
|||
stdout: windowsCygwinPath + "\n",
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
command === "cygpath" &&
|
||||
args[0] === "-u" &&
|
||||
args[1] === mockScriptsDir
|
||||
) {
|
||||
return {
|
||||
status: 0,
|
||||
stdout: "/c/test-home/.safe-chain/scripts\n",
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
@ -87,6 +105,7 @@ describe("Bash shell integration", () => {
|
|||
|
||||
// Reset mocks
|
||||
mock.reset();
|
||||
mockScriptsDir = "/test-home/.safe-chain/scripts";
|
||||
platform = "linux";
|
||||
});
|
||||
|
||||
|
|
@ -109,7 +128,7 @@ describe("Bash shell integration", () => {
|
|||
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
||||
assert.ok(
|
||||
content.includes(
|
||||
"source ~/.safe-chain/scripts/init-posix.sh # Safe-chain bash initialization script"
|
||||
"source /test-home/.safe-chain/scripts/init-posix.sh # Safe-chain bash initialization script"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
|
@ -129,7 +148,24 @@ describe("Bash shell integration", () => {
|
|||
const content = fs.readFileSync(windowsCygwinPath, "utf-8");
|
||||
assert.ok(
|
||||
content.includes(
|
||||
"source ~/.safe-chain/scripts/init-posix.sh # Safe-chain bash initialization script"
|
||||
"source /c/test-home/.safe-chain/scripts/init-posix.sh # Safe-chain bash initialization script"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it("should write a bash-compatible scripts path on Windows", () => {
|
||||
platform = "win32";
|
||||
windowsCygwinPath = mockStartupFile;
|
||||
mockScriptsDir = "C:\\test-home\\.safe-chain\\scripts";
|
||||
mockStartupFile = "DUMMY";
|
||||
|
||||
const result = bash.setup();
|
||||
assert.strictEqual(result, true);
|
||||
|
||||
const content = fs.readFileSync(windowsCygwinPath, "utf-8");
|
||||
assert.ok(
|
||||
content.includes(
|
||||
"source /c/test-home/.safe-chain/scripts/init-posix.sh # Safe-chain bash initialization script"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
|
@ -209,13 +245,13 @@ describe("Bash shell integration", () => {
|
|||
// Setup
|
||||
bash.setup(tools);
|
||||
let content = fs.readFileSync(mockStartupFile, "utf-8");
|
||||
assert.ok(content.includes("source ~/.safe-chain/scripts/init-posix.sh"));
|
||||
assert.ok(content.includes("source /test-home/.safe-chain/scripts/init-posix.sh"));
|
||||
|
||||
// Teardown
|
||||
bash.teardown(tools);
|
||||
content = fs.readFileSync(mockStartupFile, "utf-8");
|
||||
assert.ok(
|
||||
!content.includes("source ~/.safe-chain/scripts/init-posix.sh")
|
||||
!content.includes("source /test-home/.safe-chain/scripts/init-posix.sh")
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -236,7 +272,7 @@ describe("Bash shell integration", () => {
|
|||
const initialContent = [
|
||||
"#!/bin/bash",
|
||||
"alias npm='old-npm'",
|
||||
"source ~/.safe-chain/scripts/init-posix.sh",
|
||||
"source /test-home/.safe-chain/scripts/init-posix.sh # Safe-chain bash initialization script",
|
||||
"alias ls='ls --color=auto'",
|
||||
].join("\n");
|
||||
|
||||
|
|
@ -247,7 +283,7 @@ describe("Bash shell integration", () => {
|
|||
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
||||
assert.ok(!content.includes("alias npm="));
|
||||
assert.ok(
|
||||
!content.includes("source ~/.safe-chain/scripts/init-posix.sh")
|
||||
!content.includes("source /test-home/.safe-chain/scripts/init-posix.sh # Safe-chain bash initialization script")
|
||||
);
|
||||
assert.ok(content.includes("alias ls="));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ import {
|
|||
doesExecutableExistOnSystem,
|
||||
removeLinesMatchingPattern,
|
||||
} from "../helpers.js";
|
||||
import { getScriptsDir } from "../../config/safeChainDir.js";
|
||||
import { execSync } from "child_process";
|
||||
import path from "path";
|
||||
|
||||
const shellName = "Fish";
|
||||
const executableName = "fish";
|
||||
|
|
@ -31,10 +33,10 @@ function teardown(tools) {
|
|||
);
|
||||
}
|
||||
|
||||
// Removes the line that sources the safe-chain fish initialization script (~/.safe-chain/scripts/init-fish.fish)
|
||||
// Remove sourcing line to prevent safe-chain initialization in future shell sessions
|
||||
removeLinesMatchingPattern(
|
||||
startupFile,
|
||||
/^source\s+~\/\.safe-chain\/scripts\/init-fish\.fish/,
|
||||
/^source\s+.*init-fish\.fish.*#\s*Safe-chain/,
|
||||
eol
|
||||
);
|
||||
|
||||
|
|
@ -46,7 +48,7 @@ function setup() {
|
|||
|
||||
addLineToFile(
|
||||
startupFile,
|
||||
`source ~/.safe-chain/scripts/init-fish.fish # Safe-chain Fish initialization script`,
|
||||
`source ${path.join(getScriptsDir(), "init-fish.fish")} # Safe-chain Fish initialization script`,
|
||||
eol
|
||||
);
|
||||
|
||||
|
|
@ -69,7 +71,7 @@ function getStartupFile() {
|
|||
function getManualTeardownInstructions() {
|
||||
return [
|
||||
`Remove the following line from your ~/.config/fish/config.fish file:`,
|
||||
` source ~/.safe-chain/scripts/init-fish.fish`,
|
||||
` source ${path.join(getScriptsDir(), "init-fish.fish")}`,
|
||||
`Then restart your terminal or run: source ~/.config/fish/config.fish`,
|
||||
];
|
||||
}
|
||||
|
|
@ -77,7 +79,7 @@ function getManualTeardownInstructions() {
|
|||
function getManualSetupInstructions() {
|
||||
return [
|
||||
`Add the following line to your ~/.config/fish/config.fish file:`,
|
||||
` source ~/.safe-chain/scripts/init-fish.fish`,
|
||||
` source ${path.join(getScriptsDir(), "init-fish.fish")}`,
|
||||
`Then restart your terminal or run: source ~/.config/fish/config.fish`,
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,12 @@ describe("Fish shell integration", () => {
|
|||
},
|
||||
});
|
||||
|
||||
mock.module("../../config/safeChainDir.js", {
|
||||
namedExports: {
|
||||
getScriptsDir: () => "/test-home/.safe-chain/scripts",
|
||||
},
|
||||
});
|
||||
|
||||
// Mock child_process execSync
|
||||
mock.module("child_process", {
|
||||
namedExports: {
|
||||
|
|
@ -72,7 +78,7 @@ describe("Fish shell integration", () => {
|
|||
|
||||
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
||||
assert.ok(
|
||||
content.includes('source ~/.safe-chain/scripts/init-fish.fish # Safe-chain Fish initialization script')
|
||||
content.includes('source /test-home/.safe-chain/scripts/init-fish.fish # Safe-chain Fish initialization script')
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -81,7 +87,7 @@ describe("Fish shell integration", () => {
|
|||
fish.setup();
|
||||
|
||||
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
||||
const sourceMatches = (content.match(/source ~\/\.safe-chain\/scripts\/init-fish\.fish/g) || []).length;
|
||||
const sourceMatches = (content.match(/source \/test-home\/\.safe-chain\/scripts\/init-fish\.fish/g) || []).length;
|
||||
assert.strictEqual(sourceMatches, 2, "Should allow multiple source lines (helper doesn't dedupe)");
|
||||
});
|
||||
});
|
||||
|
|
@ -93,7 +99,7 @@ describe("Fish shell integration", () => {
|
|||
"alias npm 'aikido-npm'",
|
||||
"alias npx 'aikido-npx'",
|
||||
"alias yarn 'aikido-yarn'",
|
||||
"source ~/.safe-chain/scripts/init-fish.fish # Safe-chain Fish initialization script",
|
||||
"source /test-home/.safe-chain/scripts/init-fish.fish # Safe-chain Fish initialization script",
|
||||
"alias ls 'ls --color=auto'",
|
||||
"alias grep 'grep --color=auto'",
|
||||
].join("\n");
|
||||
|
|
@ -107,7 +113,7 @@ describe("Fish shell integration", () => {
|
|||
assert.ok(!content.includes("alias npm "));
|
||||
assert.ok(!content.includes("alias npx "));
|
||||
assert.ok(!content.includes("alias yarn "));
|
||||
assert.ok(!content.includes("source ~/.safe-chain/scripts/init-fish.fish"));
|
||||
assert.ok(!content.includes("source /test-home/.safe-chain/scripts/init-fish.fish"));
|
||||
assert.ok(content.includes("alias ls "));
|
||||
assert.ok(content.includes("alias grep "));
|
||||
});
|
||||
|
|
@ -162,12 +168,12 @@ describe("Fish shell integration", () => {
|
|||
// Setup
|
||||
fish.setup();
|
||||
let content = fs.readFileSync(mockStartupFile, "utf-8");
|
||||
assert.ok(content.includes('source ~/.safe-chain/scripts/init-fish.fish'));
|
||||
assert.ok(content.includes('source /test-home/.safe-chain/scripts/init-fish.fish'));
|
||||
|
||||
// Teardown
|
||||
fish.teardown(tools);
|
||||
content = fs.readFileSync(mockStartupFile, "utf-8");
|
||||
assert.ok(!content.includes("source ~/.safe-chain/scripts/init-fish.fish"));
|
||||
assert.ok(!content.includes("source /test-home/.safe-chain/scripts/init-fish.fish"));
|
||||
});
|
||||
|
||||
it("should handle multiple setup calls", () => {
|
||||
|
|
@ -176,7 +182,7 @@ describe("Fish shell integration", () => {
|
|||
fish.setup();
|
||||
|
||||
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
||||
const sourceMatches = (content.match(/source ~\/\.safe-chain\/scripts\/init-fish\.fish/g) || []).length;
|
||||
const sourceMatches = (content.match(/source \/test-home\/\.safe-chain\/scripts\/init-fish\.fish/g) || []).length;
|
||||
assert.strictEqual(sourceMatches, 1, "Should have exactly one source line after setup-teardown-setup cycle");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ import {
|
|||
removeLinesMatchingPattern,
|
||||
validatePowerShellExecutionPolicy,
|
||||
} from "../helpers.js";
|
||||
import { getScriptsDir } from "../../config/safeChainDir.js";
|
||||
import { execSync } from "child_process";
|
||||
import path from "path";
|
||||
|
||||
const shellName = "PowerShell Core";
|
||||
const executableName = "pwsh";
|
||||
|
|
@ -30,10 +32,10 @@ function teardown(tools) {
|
|||
);
|
||||
}
|
||||
|
||||
// Remove the line that sources the safe-chain PowerShell initialization script
|
||||
// Remove sourcing line to prevent shell from loading safe-chain after uninstallation
|
||||
removeLinesMatchingPattern(
|
||||
startupFile,
|
||||
/^\.\s+["']?\$HOME[/\\].safe-chain[/\\]scripts[/\\]init-pwsh\.ps1["']?/,
|
||||
/^\.\s+["']?.*init-pwsh\.ps1["']?.*#\s*Safe-chain/,
|
||||
);
|
||||
|
||||
return true;
|
||||
|
|
@ -52,7 +54,7 @@ async function setup() {
|
|||
|
||||
addLineToFile(
|
||||
startupFile,
|
||||
`. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1" # Safe-chain PowerShell initialization script`,
|
||||
`. "${path.join(getScriptsDir(), "init-pwsh.ps1")}" # Safe-chain PowerShell initialization script`,
|
||||
);
|
||||
|
||||
return true;
|
||||
|
|
@ -74,7 +76,7 @@ function getStartupFile() {
|
|||
function getManualTeardownInstructions() {
|
||||
return [
|
||||
`Remove the following line from your PowerShell profile (run "echo $PROFILE" to find its location):`,
|
||||
` . "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"`,
|
||||
` . "${path.join(getScriptsDir(), "init-pwsh.ps1")}"`,
|
||||
`Then restart your terminal or run: . $PROFILE`,
|
||||
];
|
||||
}
|
||||
|
|
@ -82,7 +84,7 @@ function getManualTeardownInstructions() {
|
|||
function getManualSetupInstructions() {
|
||||
return [
|
||||
`Add the following line to your PowerShell profile (run "echo $PROFILE" to find its location):`,
|
||||
` . "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"`,
|
||||
` . "${path.join(getScriptsDir(), "init-pwsh.ps1")}"`,
|
||||
`Then restart your terminal or run: . $PROFILE`,
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,12 @@ describe("PowerShell Core shell integration", () => {
|
|||
},
|
||||
});
|
||||
|
||||
mock.module("../../config/safeChainDir.js", {
|
||||
namedExports: {
|
||||
getScriptsDir: () => "/test-home/.safe-chain/scripts",
|
||||
},
|
||||
});
|
||||
|
||||
// Mock child_process execSync
|
||||
mock.module("child_process", {
|
||||
namedExports: {
|
||||
|
|
@ -83,7 +89,7 @@ describe("PowerShell Core shell integration", () => {
|
|||
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
||||
assert.ok(
|
||||
content.includes(
|
||||
'. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1" # Safe-chain PowerShell initialization script',
|
||||
'. "/test-home/.safe-chain/scripts/init-pwsh.ps1" # Safe-chain PowerShell initialization script',
|
||||
),
|
||||
);
|
||||
});
|
||||
|
|
@ -93,7 +99,7 @@ describe("PowerShell Core shell integration", () => {
|
|||
it("should remove init-pwsh.ps1 source line", () => {
|
||||
const initialContent = [
|
||||
"# PowerShell profile",
|
||||
'. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1" # Safe-chain PowerShell initialization script',
|
||||
'. "/test-home/.safe-chain/scripts/init-pwsh.ps1" # Safe-chain PowerShell initialization script',
|
||||
"Set-Alias ls Get-ChildItem",
|
||||
"Set-Alias grep Select-String",
|
||||
].join("\n");
|
||||
|
|
@ -105,7 +111,7 @@ describe("PowerShell Core shell integration", () => {
|
|||
|
||||
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
||||
assert.ok(
|
||||
!content.includes('. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"'),
|
||||
!content.includes('. "/test-home/.safe-chain/scripts/init-pwsh.ps1"'),
|
||||
);
|
||||
assert.ok(content.includes("Set-Alias ls "));
|
||||
assert.ok(content.includes("Set-Alias grep "));
|
||||
|
|
@ -180,14 +186,14 @@ describe("PowerShell Core shell integration", () => {
|
|||
await powershell.setup();
|
||||
let content = fs.readFileSync(mockStartupFile, "utf-8");
|
||||
assert.ok(
|
||||
content.includes('. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"'),
|
||||
content.includes('. "/test-home/.safe-chain/scripts/init-pwsh.ps1"'),
|
||||
);
|
||||
|
||||
// Teardown
|
||||
powershell.teardown(knownAikidoTools);
|
||||
content = fs.readFileSync(mockStartupFile, "utf-8");
|
||||
assert.ok(
|
||||
!content.includes('. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"'),
|
||||
!content.includes('. "/test-home/.safe-chain/scripts/init-pwsh.ps1"'),
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -198,7 +204,7 @@ describe("PowerShell Core shell integration", () => {
|
|||
|
||||
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
||||
const sourceMatches = (
|
||||
content.match(/\. "\$HOME\\.safe-chain\\scripts\\init-pwsh\.ps1"/g) ||
|
||||
content.match(/\. "\/test-home\/\.safe-chain\/scripts\/init-pwsh\.ps1"/g) ||
|
||||
[]
|
||||
).length;
|
||||
assert.strictEqual(sourceMatches, 1, "Should not duplicate source lines");
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ import {
|
|||
removeLinesMatchingPattern,
|
||||
validatePowerShellExecutionPolicy,
|
||||
} from "../helpers.js";
|
||||
import { getScriptsDir } from "../../config/safeChainDir.js";
|
||||
import { execSync } from "child_process";
|
||||
import path from "path";
|
||||
|
||||
const shellName = "Windows PowerShell";
|
||||
const executableName = "powershell";
|
||||
|
|
@ -30,10 +32,10 @@ function teardown(tools) {
|
|||
);
|
||||
}
|
||||
|
||||
// Remove the line that sources the safe-chain PowerShell initialization script
|
||||
// Remove sourcing line to clean up safe-chain integration from the shell profile
|
||||
removeLinesMatchingPattern(
|
||||
startupFile,
|
||||
/^\.\s+["']?\$HOME[/\\].safe-chain[/\\]scripts[/\\]init-pwsh\.ps1["']?/,
|
||||
/^\.\s+["']?.*init-pwsh\.ps1["']?.*#\s*Safe-chain/,
|
||||
);
|
||||
|
||||
return true;
|
||||
|
|
@ -52,7 +54,7 @@ async function setup() {
|
|||
|
||||
addLineToFile(
|
||||
startupFile,
|
||||
`. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1" # Safe-chain PowerShell initialization script`,
|
||||
`. "${path.join(getScriptsDir(), "init-pwsh.ps1")}" # Safe-chain PowerShell initialization script`,
|
||||
);
|
||||
|
||||
return true;
|
||||
|
|
@ -74,7 +76,7 @@ function getStartupFile() {
|
|||
function getManualTeardownInstructions() {
|
||||
return [
|
||||
`Remove the following line from your PowerShell profile (run "echo $PROFILE" to find its location):`,
|
||||
` . "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"`,
|
||||
` . "${path.join(getScriptsDir(), "init-pwsh.ps1")}"`,
|
||||
`Then restart your terminal or run: . $PROFILE`,
|
||||
];
|
||||
}
|
||||
|
|
@ -82,7 +84,7 @@ function getManualTeardownInstructions() {
|
|||
function getManualSetupInstructions() {
|
||||
return [
|
||||
`Add the following line to your PowerShell profile (run "echo $PROFILE" to find its location):`,
|
||||
` . "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"`,
|
||||
` . "${path.join(getScriptsDir(), "init-pwsh.ps1")}"`,
|
||||
`Then restart your terminal or run: . $PROFILE`,
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,12 @@ describe("Windows PowerShell shell integration", () => {
|
|||
},
|
||||
});
|
||||
|
||||
mock.module("../../config/safeChainDir.js", {
|
||||
namedExports: {
|
||||
getScriptsDir: () => "/test-home/.safe-chain/scripts",
|
||||
},
|
||||
});
|
||||
|
||||
// Mock child_process execSync
|
||||
mock.module("child_process", {
|
||||
namedExports: {
|
||||
|
|
@ -83,7 +89,7 @@ describe("Windows PowerShell shell integration", () => {
|
|||
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
||||
assert.ok(
|
||||
content.includes(
|
||||
'. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1" # Safe-chain PowerShell initialization script',
|
||||
'. "/test-home/.safe-chain/scripts/init-pwsh.ps1" # Safe-chain PowerShell initialization script',
|
||||
),
|
||||
);
|
||||
});
|
||||
|
|
@ -93,7 +99,7 @@ describe("Windows PowerShell shell integration", () => {
|
|||
it("should remove init-pwsh.ps1 source line", () => {
|
||||
const initialContent = [
|
||||
"# Windows PowerShell profile",
|
||||
'. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1" # Safe-chain PowerShell initialization script',
|
||||
'. "/test-home/.safe-chain/scripts/init-pwsh.ps1" # Safe-chain PowerShell initialization script',
|
||||
"Set-Alias ls Get-ChildItem",
|
||||
"Set-Alias grep Select-String",
|
||||
].join("\n");
|
||||
|
|
@ -105,7 +111,7 @@ describe("Windows PowerShell shell integration", () => {
|
|||
|
||||
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
||||
assert.ok(
|
||||
!content.includes('. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"'),
|
||||
!content.includes('. "/test-home/.safe-chain/scripts/init-pwsh.ps1"'),
|
||||
);
|
||||
assert.ok(content.includes("Set-Alias ls "));
|
||||
assert.ok(content.includes("Set-Alias grep "));
|
||||
|
|
@ -180,14 +186,14 @@ describe("Windows PowerShell shell integration", () => {
|
|||
await windowsPowershell.setup();
|
||||
let content = fs.readFileSync(mockStartupFile, "utf-8");
|
||||
assert.ok(
|
||||
content.includes('. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"'),
|
||||
content.includes('. "/test-home/.safe-chain/scripts/init-pwsh.ps1"'),
|
||||
);
|
||||
|
||||
// Teardown
|
||||
windowsPowershell.teardown(knownAikidoTools);
|
||||
content = fs.readFileSync(mockStartupFile, "utf-8");
|
||||
assert.ok(
|
||||
!content.includes('. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"'),
|
||||
!content.includes('. "/test-home/.safe-chain/scripts/init-pwsh.ps1"'),
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -198,7 +204,7 @@ describe("Windows PowerShell shell integration", () => {
|
|||
|
||||
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
||||
const sourceMatches = (
|
||||
content.match(/\. "\$HOME\\.safe-chain\\scripts\\init-pwsh\.ps1"/g) ||
|
||||
content.match(/\. "\/test-home\/\.safe-chain\/scripts\/init-pwsh\.ps1"/g) ||
|
||||
[]
|
||||
).length;
|
||||
assert.strictEqual(sourceMatches, 1, "Should not duplicate source lines");
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ import {
|
|||
doesExecutableExistOnSystem,
|
||||
removeLinesMatchingPattern,
|
||||
} from "../helpers.js";
|
||||
import { getScriptsDir } from "../../config/safeChainDir.js";
|
||||
import { execSync } from "child_process";
|
||||
import path from "path";
|
||||
|
||||
const shellName = "Zsh";
|
||||
const executableName = "zsh";
|
||||
|
|
@ -31,10 +33,10 @@ function teardown(tools) {
|
|||
);
|
||||
}
|
||||
|
||||
// Removes the line that sources the safe-chain zsh initialization script (~/.safe-chain/scripts/init-posix.sh)
|
||||
// Remove sourcing line to complete shell integration cleanup
|
||||
removeLinesMatchingPattern(
|
||||
startupFile,
|
||||
/^source\s+~\/\.safe-chain\/scripts\/init-posix\.sh/,
|
||||
/^source\s+.*init-posix\.sh.*#\s*Safe-chain/,
|
||||
eol
|
||||
);
|
||||
|
||||
|
|
@ -46,7 +48,7 @@ function setup() {
|
|||
|
||||
addLineToFile(
|
||||
startupFile,
|
||||
`source ~/.safe-chain/scripts/init-posix.sh # Safe-chain Zsh initialization script`,
|
||||
`source ${path.join(getScriptsDir(), "init-posix.sh")} # Safe-chain Zsh initialization script`,
|
||||
eol
|
||||
);
|
||||
|
||||
|
|
@ -69,7 +71,7 @@ function getStartupFile() {
|
|||
function getManualTeardownInstructions() {
|
||||
return [
|
||||
`Remove the following line from your ~/.zshrc file:`,
|
||||
` source ~/.safe-chain/scripts/init-posix.sh`,
|
||||
` source ${path.join(getScriptsDir(), "init-posix.sh")}`,
|
||||
`Then restart your terminal or run: source ~/.zshrc`,
|
||||
];
|
||||
}
|
||||
|
|
@ -77,7 +79,7 @@ function getManualTeardownInstructions() {
|
|||
function getManualSetupInstructions() {
|
||||
return [
|
||||
`Add the following line to your ~/.zshrc file:`,
|
||||
` source ~/.safe-chain/scripts/init-posix.sh`,
|
||||
` source ${path.join(getScriptsDir(), "init-posix.sh")}`,
|
||||
`Then restart your terminal or run: source ~/.zshrc`,
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,12 @@ describe("Zsh shell integration", () => {
|
|||
},
|
||||
});
|
||||
|
||||
mock.module("../../config/safeChainDir.js", {
|
||||
namedExports: {
|
||||
getScriptsDir: () => "/test-home/.safe-chain/scripts",
|
||||
},
|
||||
});
|
||||
|
||||
// Mock child_process execSync
|
||||
mock.module("child_process", {
|
||||
namedExports: {
|
||||
|
|
@ -73,7 +79,7 @@ describe("Zsh shell integration", () => {
|
|||
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
||||
assert.ok(
|
||||
content.includes(
|
||||
"source ~/.safe-chain/scripts/init-posix.sh # Safe-chain Zsh initialization script"
|
||||
"source /test-home/.safe-chain/scripts/init-posix.sh # Safe-chain Zsh initialization script"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
|
@ -83,7 +89,7 @@ describe("Zsh shell integration", () => {
|
|||
assert.strictEqual(result, true);
|
||||
|
||||
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
||||
assert.ok(content.includes("source ~/.safe-chain/scripts/init-posix.sh"));
|
||||
assert.ok(content.includes("source /test-home/.safe-chain/scripts/init-posix.sh"));
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -114,7 +120,7 @@ describe("Zsh shell integration", () => {
|
|||
it("should remove zsh initialization script source line", () => {
|
||||
const initialContent = [
|
||||
"#!/bin/zsh",
|
||||
"source ~/.safe-chain/scripts/init-posix.sh",
|
||||
"source /test-home/.safe-chain/scripts/init-posix.sh # Safe-chain Zsh initialization script",
|
||||
"alias ls='ls --color=auto'",
|
||||
].join("\n");
|
||||
|
||||
|
|
@ -125,7 +131,7 @@ describe("Zsh shell integration", () => {
|
|||
|
||||
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
||||
assert.ok(
|
||||
!content.includes("source ~/.safe-chain/scripts/init-posix.sh")
|
||||
!content.includes("source /test-home/.safe-chain/scripts/init-posix.sh # Safe-chain Zsh initialization script")
|
||||
);
|
||||
assert.ok(content.includes("alias ls="));
|
||||
});
|
||||
|
|
@ -180,13 +186,13 @@ describe("Zsh shell integration", () => {
|
|||
// Setup
|
||||
zsh.setup();
|
||||
let content = fs.readFileSync(mockStartupFile, "utf-8");
|
||||
assert.ok(content.includes("source ~/.safe-chain/scripts/init-posix.sh"));
|
||||
assert.ok(content.includes("source /test-home/.safe-chain/scripts/init-posix.sh"));
|
||||
|
||||
// Teardown
|
||||
zsh.teardown(tools);
|
||||
content = fs.readFileSync(mockStartupFile, "utf-8");
|
||||
assert.ok(
|
||||
!content.includes("source ~/.safe-chain/scripts/init-posix.sh")
|
||||
!content.includes("source /test-home/.safe-chain/scripts/init-posix.sh")
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -207,7 +213,7 @@ describe("Zsh shell integration", () => {
|
|||
const initialContent = [
|
||||
"#!/bin/zsh",
|
||||
"alias npm='old-npm'",
|
||||
"source ~/.safe-chain/scripts/init-posix.sh",
|
||||
"source /test-home/.safe-chain/scripts/init-posix.sh # Safe-chain Zsh initialization script",
|
||||
"alias ls='ls --color=auto'",
|
||||
].join("\n");
|
||||
|
||||
|
|
@ -218,7 +224,7 @@ describe("Zsh shell integration", () => {
|
|||
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
||||
assert.ok(!content.includes("alias npm="));
|
||||
assert.ok(
|
||||
!content.includes("source ~/.safe-chain/scripts/init-posix.sh")
|
||||
!content.includes("source /test-home/.safe-chain/scripts/init-posix.sh # Safe-chain Zsh initialization script")
|
||||
);
|
||||
assert.ok(content.includes("alias ls="));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import chalk from "chalk";
|
||||
import { ui } from "../environment/userInteraction.js";
|
||||
import { detectShells } from "./shellDetection.js";
|
||||
import { knownAikidoTools, getPackageManagerList, getShimsDir, getScriptsDir } from "./helpers.js";
|
||||
import { knownAikidoTools, getPackageManagerList } from "./helpers.js";
|
||||
import { getShimsDir, getScriptsDir } from "../config/safeChainDir.js";
|
||||
import fs from "fs";
|
||||
|
||||
/**
|
||||
|
|
@ -109,4 +110,5 @@ export async function teardownDirectories() {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue