@aikidosec/safe-chain 1.0.16 → 1.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/eslint.config.js +2 -1
- package/package.json +7 -4
- package/src/shell-integration/setup.js +25 -0
- package/src/shell-integration/startup-scripts/init-fish.fish +58 -0
- package/src/shell-integration/startup-scripts/init-posix.sh +54 -0
- package/src/shell-integration/startup-scripts/init-pwsh.ps1 +80 -0
- package/src/shell-integration/supported-shells/bash.js +11 -7
- package/src/shell-integration/supported-shells/bash.spec.js +32 -32
- package/src/shell-integration/supported-shells/fish.js +11 -7
- package/src/shell-integration/supported-shells/fish.spec.js +21 -37
- package/src/shell-integration/supported-shells/powershell.js +11 -7
- package/src/shell-integration/supported-shells/powershell.spec.js +43 -47
- package/src/shell-integration/supported-shells/windowsPowershell.js +11 -7
- package/src/shell-integration/supported-shells/windowsPowershell.spec.js +43 -47
- package/src/shell-integration/supported-shells/zsh.js +11 -7
- package/src/shell-integration/supported-shells/zsh.spec.js +57 -30
- package/.github/workflows/build-and-release.yml +0 -41
- package/.github/workflows/test-on-pr.yml +0 -28
package/eslint.config.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import js from "@eslint/js";
|
|
2
|
-
import { defineConfig } from "@eslint/config-helpers";
|
|
2
|
+
import { defineConfig, globalIgnores } from "@eslint/config-helpers";
|
|
3
3
|
import globals from "globals";
|
|
4
4
|
import importPlugin from "eslint-plugin-import";
|
|
5
5
|
|
|
@@ -22,4 +22,5 @@ export default defineConfig([
|
|
|
22
22
|
},
|
|
23
23
|
rules: {},
|
|
24
24
|
},
|
|
25
|
+
globalIgnores(['test/e2e']),
|
|
25
26
|
]);
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aikidosec/safe-chain",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.17",
|
|
4
4
|
"scripts": {
|
|
5
|
-
"test": "node --test --experimental-test-module-mocks
|
|
6
|
-
"test:watch": "node --test --watch --experimental-test-module-mocks
|
|
5
|
+
"test": "node --test --experimental-test-module-mocks 'src/**/*.spec.js'",
|
|
6
|
+
"test:watch": "node --test --watch --experimental-test-module-mocks 'src/**/*.spec.js'",
|
|
7
7
|
"lint": "eslint ."
|
|
8
8
|
},
|
|
9
9
|
"repository": {
|
|
@@ -42,5 +42,8 @@
|
|
|
42
42
|
"bugs": {
|
|
43
43
|
"url": "https://github.com/AikidoSec/safe-chain/issues"
|
|
44
44
|
},
|
|
45
|
-
"homepage": "https://github.com/AikidoSec/safe-chain#readme"
|
|
45
|
+
"homepage": "https://github.com/AikidoSec/safe-chain#readme",
|
|
46
|
+
"overrides": {
|
|
47
|
+
"brace-expansion@<=2.0.2": "2.0.2"
|
|
48
|
+
}
|
|
46
49
|
}
|
|
@@ -2,6 +2,10 @@ import chalk from "chalk";
|
|
|
2
2
|
import { ui } from "../environment/userInteraction.js";
|
|
3
3
|
import { detectShells } from "./shellDetection.js";
|
|
4
4
|
import { knownAikidoTools } from "./helpers.js";
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import os from "os";
|
|
7
|
+
import path from "path";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
5
9
|
|
|
6
10
|
/**
|
|
7
11
|
* Loops over the detected shells and calls the setup function for each.
|
|
@@ -13,6 +17,8 @@ export async function setup() {
|
|
|
13
17
|
);
|
|
14
18
|
ui.emptyLine();
|
|
15
19
|
|
|
20
|
+
copyStartupFiles();
|
|
21
|
+
|
|
16
22
|
try {
|
|
17
23
|
const shells = detectShells();
|
|
18
24
|
if (shells.length === 0) {
|
|
@@ -73,3 +79,22 @@ function setupShell(shell) {
|
|
|
73
79
|
|
|
74
80
|
return success;
|
|
75
81
|
}
|
|
82
|
+
|
|
83
|
+
function copyStartupFiles() {
|
|
84
|
+
const startupFiles = ["init-posix.sh", "init-pwsh.ps1", "init-fish.fish"];
|
|
85
|
+
|
|
86
|
+
for (const file of startupFiles) {
|
|
87
|
+
const targetDir = path.join(os.homedir(), ".safe-chain", "scripts");
|
|
88
|
+
const targetPath = path.join(os.homedir(), ".safe-chain", "scripts", file);
|
|
89
|
+
|
|
90
|
+
if (!fs.existsSync(targetDir)) {
|
|
91
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Use absolute path for source
|
|
95
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
96
|
+
const __dirname = path.dirname(__filename);
|
|
97
|
+
const sourcePath = path.resolve(__dirname, "startup-scripts", file);
|
|
98
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
function printSafeChainWarning
|
|
2
|
+
set original_cmd $argv[1]
|
|
3
|
+
|
|
4
|
+
# Fish equivalent of ANSI color codes: yellow background, black text for "Warning:"
|
|
5
|
+
set_color -b yellow black
|
|
6
|
+
printf "Warning:"
|
|
7
|
+
set_color normal
|
|
8
|
+
printf " safe-chain is not available to protect you from installing malware. %s will run without it.\n" $original_cmd
|
|
9
|
+
|
|
10
|
+
# Cyan text for the install command
|
|
11
|
+
printf "Install safe-chain by using "
|
|
12
|
+
set_color cyan
|
|
13
|
+
printf "npm install -g @aikidosec/safe-chain"
|
|
14
|
+
set_color normal
|
|
15
|
+
printf ".\n"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
function wrapSafeChainCommand
|
|
19
|
+
set original_cmd $argv[1]
|
|
20
|
+
set aikido_cmd $argv[2]
|
|
21
|
+
set cmd_args $argv[3..-1]
|
|
22
|
+
|
|
23
|
+
if type -q $aikido_cmd
|
|
24
|
+
# If the aikido command is available, just run it with the provided arguments
|
|
25
|
+
$aikido_cmd $cmd_args
|
|
26
|
+
else
|
|
27
|
+
# If the aikido command is not available, print a warning and run the original command
|
|
28
|
+
printSafeChainWarning $original_cmd
|
|
29
|
+
command $original_cmd $cmd_args
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
function npx
|
|
34
|
+
wrapSafeChainCommand "npx" "aikido-npx" $argv
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
function yarn
|
|
38
|
+
wrapSafeChainCommand "yarn" "aikido-yarn" $argv
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
function pnpm
|
|
42
|
+
wrapSafeChainCommand "pnpm" "aikido-pnpm" $argv
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
function pnpx
|
|
46
|
+
wrapSafeChainCommand "pnpx" "aikido-pnpx" $argv
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
function npm
|
|
50
|
+
if test (count $argv) -eq 1 -a \( "$argv[1]" = "-v" -o "$argv[1]" = "--version" \)
|
|
51
|
+
# If args is just -v or --version and nothing else, just run the npm version command
|
|
52
|
+
# This is because nvm uses this to check the version of npm
|
|
53
|
+
command npm $argv
|
|
54
|
+
return
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
wrapSafeChainCommand "npm" "aikido-npm" $argv
|
|
58
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
|
|
2
|
+
function printSafeChainWarning() {
|
|
3
|
+
# \033[43;30m is used to set the background color to yellow and text color to black
|
|
4
|
+
# \033[0m is used to reset the text formatting
|
|
5
|
+
printf "\033[43;30mWarning:\033[0m safe-chain is not available to protect you from installing malware. %s will run without it.\n" "$1"
|
|
6
|
+
# \033[36m is used to set the text color to cyan
|
|
7
|
+
printf "Install safe-chain by using \033[36mnpm install -g @aikidosec/safe-chain\033[0m.\n"
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function wrapSafeChainCommand() {
|
|
11
|
+
local original_cmd="$1"
|
|
12
|
+
local aikido_cmd="$2"
|
|
13
|
+
|
|
14
|
+
# Remove the first 2 arguments (original_cmd and aikido_cmd) from $@
|
|
15
|
+
# so that "$@" now contains only the arguments passed to the original command
|
|
16
|
+
shift 2
|
|
17
|
+
|
|
18
|
+
if command -v "$aikido_cmd" > /dev/null 2>&1; then
|
|
19
|
+
# If the aikido command is available, just run it with the provided arguments
|
|
20
|
+
"$aikido_cmd" "$@"
|
|
21
|
+
else
|
|
22
|
+
# If the aikido command is not available, print a warning and run the original command
|
|
23
|
+
printSafeChainWarning "$original_cmd"
|
|
24
|
+
|
|
25
|
+
command "$original_cmd" "$@"
|
|
26
|
+
fi
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function npx() {
|
|
30
|
+
wrapSafeChainCommand "npx" "aikido-npx" "$@"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function yarn() {
|
|
34
|
+
wrapSafeChainCommand "yarn" "aikido-yarn" "$@"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function pnpm() {
|
|
38
|
+
wrapSafeChainCommand "pnpm" "aikido-pnpm" "$@"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function pnpx() {
|
|
42
|
+
wrapSafeChainCommand "pnpx" "aikido-pnpx" "$@"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function npm() {
|
|
46
|
+
if [[ "$1" == "-v" || "$1" == "--version" ]] && [[ $# -eq 1 ]]; then
|
|
47
|
+
# If args is just -v or --version and nothing else, just run the npm version command
|
|
48
|
+
# This is because nvm uses this to check the version of npm
|
|
49
|
+
command npm "$@"
|
|
50
|
+
return
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
wrapSafeChainCommand "npm" "aikido-npm" "$@"
|
|
54
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
function Write-SafeChainWarning {
|
|
2
|
+
param([string]$Command)
|
|
3
|
+
|
|
4
|
+
# PowerShell equivalent of ANSI color codes: yellow background, black text for "Warning:"
|
|
5
|
+
Write-Host "Warning:" -BackgroundColor Yellow -ForegroundColor Black -NoNewline
|
|
6
|
+
Write-Host " safe-chain is not available to protect you from installing malware. $Command will run without it."
|
|
7
|
+
|
|
8
|
+
# Cyan text for the install command
|
|
9
|
+
Write-Host "Install safe-chain by using " -NoNewline
|
|
10
|
+
Write-Host "npm install -g @aikidosec/safe-chain" -ForegroundColor Cyan -NoNewline
|
|
11
|
+
Write-Host "."
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function Test-CommandAvailable {
|
|
15
|
+
param([string]$Command)
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
Get-Command $Command -ErrorAction Stop | Out-Null
|
|
19
|
+
return $true
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return $false
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function Invoke-RealCommand {
|
|
27
|
+
param(
|
|
28
|
+
[string]$Command,
|
|
29
|
+
[string[]]$Arguments
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# Find the real executable to avoid calling our wrapped functions
|
|
33
|
+
$realCommand = Get-Command -Name $Command -CommandType Application | Select-Object -First 1
|
|
34
|
+
if ($realCommand) {
|
|
35
|
+
& $realCommand.Source @Arguments
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function Invoke-WrappedCommand {
|
|
40
|
+
param(
|
|
41
|
+
[string]$OriginalCmd,
|
|
42
|
+
[string]$AikidoCmd,
|
|
43
|
+
[string[]]$Arguments
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
if (Test-CommandAvailable $AikidoCmd) {
|
|
47
|
+
& $AikidoCmd @Arguments
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
Write-SafeChainWarning $OriginalCmd
|
|
51
|
+
Invoke-RealCommand $OriginalCmd $Arguments
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function npx {
|
|
56
|
+
Invoke-WrappedCommand "npx" "aikido-npx" $args
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function yarn {
|
|
60
|
+
Invoke-WrappedCommand "yarn" "aikido-yarn" $args
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function pnpm {
|
|
64
|
+
Invoke-WrappedCommand "pnpm" "aikido-pnpm" $args
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function pnpx {
|
|
68
|
+
Invoke-WrappedCommand "pnpx" "aikido-pnpx" $args
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function npm {
|
|
72
|
+
# If args is just -v or --version and nothing else, just run the npm version command
|
|
73
|
+
# This is because nvm uses this to check the version of npm
|
|
74
|
+
if (($args.Length -eq 1) -and (($args[0] -eq "-v") -or ($args[0] -eq "--version"))) {
|
|
75
|
+
Invoke-RealCommand "npm" $args
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
Invoke-WrappedCommand "npm" "aikido-npm" $args
|
|
80
|
+
}
|
|
@@ -21,18 +21,22 @@ function teardown(tools) {
|
|
|
21
21
|
removeLinesMatchingPattern(startupFile, new RegExp(`^alias\\s+${tool}=`));
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
// Removes the line that sources the safe-chain bash initialization script (~/.aikido/scripts/init-posix.sh)
|
|
25
|
+
removeLinesMatchingPattern(
|
|
26
|
+
startupFile,
|
|
27
|
+
/^source\s+~\/\.safe-chain\/scripts\/init-posix\.sh/
|
|
28
|
+
);
|
|
29
|
+
|
|
24
30
|
return true;
|
|
25
31
|
}
|
|
26
32
|
|
|
27
|
-
function setup(
|
|
33
|
+
function setup() {
|
|
28
34
|
const startupFile = getStartupFile();
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
+
addLineToFile(
|
|
37
|
+
startupFile,
|
|
38
|
+
`source ~/.safe-chain/scripts/init-posix.sh # Safe-chain bash initialization script`
|
|
39
|
+
);
|
|
36
40
|
|
|
37
41
|
return true;
|
|
38
42
|
}
|
|
@@ -66,38 +66,17 @@ describe("Bash shell integration", () => {
|
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
describe("setup", () => {
|
|
69
|
-
it("should add
|
|
70
|
-
const
|
|
71
|
-
{ tool: "npm", aikidoCommand: "aikido-npm" },
|
|
72
|
-
{ tool: "npx", aikidoCommand: "aikido-npx" },
|
|
73
|
-
{ tool: "yarn", aikidoCommand: "aikido-yarn" },
|
|
74
|
-
];
|
|
75
|
-
|
|
76
|
-
const result = bash.setup(tools);
|
|
69
|
+
it("should add source line for bash initialization script", () => {
|
|
70
|
+
const result = bash.setup();
|
|
77
71
|
assert.strictEqual(result, true);
|
|
78
72
|
|
|
79
73
|
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
80
74
|
assert.ok(
|
|
81
|
-
content.includes(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
content.includes('alias npx="aikido-npx" # Safe-chain alias for npx')
|
|
85
|
-
);
|
|
86
|
-
assert.ok(
|
|
87
|
-
content.includes('alias yarn="aikido-yarn" # Safe-chain alias for yarn')
|
|
75
|
+
content.includes(
|
|
76
|
+
"source ~/.safe-chain/scripts/init-posix.sh # Safe-chain bash initialization script"
|
|
77
|
+
)
|
|
88
78
|
);
|
|
89
79
|
});
|
|
90
|
-
|
|
91
|
-
it("should handle empty tools array", () => {
|
|
92
|
-
const result = bash.setup([]);
|
|
93
|
-
assert.strictEqual(result, true);
|
|
94
|
-
|
|
95
|
-
// File should be created during teardown call even if no tools are provided
|
|
96
|
-
if (fs.existsSync(mockStartupFile)) {
|
|
97
|
-
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
98
|
-
assert.strictEqual(content.trim(), "");
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
80
|
});
|
|
102
81
|
|
|
103
82
|
describe("teardown", () => {
|
|
@@ -174,14 +153,14 @@ describe("Bash shell integration", () => {
|
|
|
174
153
|
// Setup
|
|
175
154
|
bash.setup(tools);
|
|
176
155
|
let content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
177
|
-
assert.ok(content.includes(
|
|
178
|
-
assert.ok(content.includes('alias yarn="aikido-yarn"'));
|
|
156
|
+
assert.ok(content.includes("source ~/.safe-chain/scripts/init-posix.sh"));
|
|
179
157
|
|
|
180
158
|
// Teardown
|
|
181
159
|
bash.teardown(tools);
|
|
182
160
|
content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
183
|
-
assert.ok(
|
|
184
|
-
|
|
161
|
+
assert.ok(
|
|
162
|
+
!content.includes("source ~/.safe-chain/scripts/init-posix.sh")
|
|
163
|
+
);
|
|
185
164
|
});
|
|
186
165
|
|
|
187
166
|
it("should handle multiple setup calls", () => {
|
|
@@ -192,8 +171,29 @@ describe("Bash shell integration", () => {
|
|
|
192
171
|
bash.setup(tools);
|
|
193
172
|
|
|
194
173
|
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
195
|
-
const
|
|
196
|
-
|
|
174
|
+
const sourceMatches = (content.match(/source.*init-posix\.sh/g) || [])
|
|
175
|
+
.length;
|
|
176
|
+
assert.strictEqual(sourceMatches, 1, "Should not duplicate source lines");
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("should handle mixed content with aliases and source lines", () => {
|
|
180
|
+
const initialContent = [
|
|
181
|
+
"#!/bin/bash",
|
|
182
|
+
"alias npm='old-npm'",
|
|
183
|
+
"source ~/.safe-chain/scripts/init-posix.sh",
|
|
184
|
+
"alias ls='ls --color=auto'",
|
|
185
|
+
].join("\n");
|
|
186
|
+
|
|
187
|
+
fs.writeFileSync(mockStartupFile, initialContent, "utf-8");
|
|
188
|
+
|
|
189
|
+
// Teardown should remove both aliases and source line
|
|
190
|
+
bash.teardown(knownAikidoTools);
|
|
191
|
+
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
192
|
+
assert.ok(!content.includes("alias npm="));
|
|
193
|
+
assert.ok(
|
|
194
|
+
!content.includes("source ~/.safe-chain/scripts/init-posix.sh")
|
|
195
|
+
);
|
|
196
|
+
assert.ok(content.includes("alias ls="));
|
|
197
197
|
});
|
|
198
198
|
});
|
|
199
199
|
});
|
|
@@ -24,18 +24,22 @@ function teardown(tools) {
|
|
|
24
24
|
);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
// Removes the line that sources the safe-chain fish initialization script (~/.safe-chain/scripts/init-fish.fish)
|
|
28
|
+
removeLinesMatchingPattern(
|
|
29
|
+
startupFile,
|
|
30
|
+
/^source\s+~\/\.safe-chain\/scripts\/init-fish\.fish/
|
|
31
|
+
);
|
|
32
|
+
|
|
27
33
|
return true;
|
|
28
34
|
}
|
|
29
35
|
|
|
30
|
-
function setup(
|
|
36
|
+
function setup() {
|
|
31
37
|
const startupFile = getStartupFile();
|
|
32
38
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
+
addLineToFile(
|
|
40
|
+
startupFile,
|
|
41
|
+
`source ~/.safe-chain/scripts/init-fish.fish # Safe-chain Fish initialization script`
|
|
42
|
+
);
|
|
39
43
|
|
|
40
44
|
return true;
|
|
41
45
|
}
|
|
@@ -66,47 +66,34 @@ describe("Fish shell integration", () => {
|
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
describe("setup", () => {
|
|
69
|
-
it("should add
|
|
70
|
-
const
|
|
71
|
-
{ tool: "npm", aikidoCommand: "aikido-npm" },
|
|
72
|
-
{ tool: "npx", aikidoCommand: "aikido-npx" },
|
|
73
|
-
{ tool: "yarn", aikidoCommand: "aikido-yarn" },
|
|
74
|
-
];
|
|
75
|
-
|
|
76
|
-
const result = fish.setup(tools);
|
|
69
|
+
it("should add source line for safe-chain fish initialization script", () => {
|
|
70
|
+
const result = fish.setup();
|
|
77
71
|
assert.strictEqual(result, true);
|
|
78
72
|
|
|
79
73
|
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
80
74
|
assert.ok(
|
|
81
|
-
content.includes('
|
|
82
|
-
);
|
|
83
|
-
assert.ok(
|
|
84
|
-
content.includes('alias npx "aikido-npx" # Safe-chain alias for npx')
|
|
85
|
-
);
|
|
86
|
-
assert.ok(
|
|
87
|
-
content.includes('alias yarn "aikido-yarn" # Safe-chain alias for yarn')
|
|
75
|
+
content.includes('source ~/.safe-chain/scripts/init-fish.fish # Safe-chain Fish initialization script')
|
|
88
76
|
);
|
|
89
77
|
});
|
|
90
78
|
|
|
91
|
-
it("should
|
|
92
|
-
|
|
93
|
-
|
|
79
|
+
it("should not duplicate source lines on multiple calls", () => {
|
|
80
|
+
fish.setup();
|
|
81
|
+
fish.setup();
|
|
94
82
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
assert.strictEqual(content.trim(), "");
|
|
99
|
-
}
|
|
83
|
+
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
84
|
+
const sourceMatches = (content.match(/source ~\/\.safe-chain\/scripts\/init-fish\.fish/g) || []).length;
|
|
85
|
+
assert.strictEqual(sourceMatches, 2, "Should allow multiple source lines (helper doesn't dedupe)");
|
|
100
86
|
});
|
|
101
87
|
});
|
|
102
88
|
|
|
103
89
|
describe("teardown", () => {
|
|
104
|
-
it("should remove npm, npx, and
|
|
90
|
+
it("should remove npm, npx, yarn aliases and source line", () => {
|
|
105
91
|
const initialContent = [
|
|
106
92
|
"#!/usr/bin/env fish",
|
|
107
93
|
"alias npm 'aikido-npm'",
|
|
108
94
|
"alias npx 'aikido-npx'",
|
|
109
95
|
"alias yarn 'aikido-yarn'",
|
|
96
|
+
"source ~/.safe-chain/scripts/init-fish.fish # Safe-chain Fish initialization script",
|
|
110
97
|
"alias ls 'ls --color=auto'",
|
|
111
98
|
"alias grep 'grep --color=auto'",
|
|
112
99
|
].join("\n");
|
|
@@ -120,6 +107,7 @@ describe("Fish shell integration", () => {
|
|
|
120
107
|
assert.ok(!content.includes("alias npm "));
|
|
121
108
|
assert.ok(!content.includes("alias npx "));
|
|
122
109
|
assert.ok(!content.includes("alias yarn "));
|
|
110
|
+
assert.ok(!content.includes("source ~/.safe-chain/scripts/init-fish.fish"));
|
|
123
111
|
assert.ok(content.includes("alias ls "));
|
|
124
112
|
assert.ok(content.includes("alias grep "));
|
|
125
113
|
});
|
|
@@ -133,7 +121,7 @@ describe("Fish shell integration", () => {
|
|
|
133
121
|
assert.strictEqual(result, true);
|
|
134
122
|
});
|
|
135
123
|
|
|
136
|
-
it("should handle file with no relevant aliases", () => {
|
|
124
|
+
it("should handle file with no relevant aliases or source lines", () => {
|
|
137
125
|
const initialContent = [
|
|
138
126
|
"#!/usr/bin/env fish",
|
|
139
127
|
"alias ls 'ls --color=auto'",
|
|
@@ -172,28 +160,24 @@ describe("Fish shell integration", () => {
|
|
|
172
160
|
];
|
|
173
161
|
|
|
174
162
|
// Setup
|
|
175
|
-
fish.setup(
|
|
163
|
+
fish.setup();
|
|
176
164
|
let content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
177
|
-
assert.ok(content.includes('
|
|
178
|
-
assert.ok(content.includes('alias yarn "aikido-yarn"'));
|
|
165
|
+
assert.ok(content.includes('source ~/.safe-chain/scripts/init-fish.fish'));
|
|
179
166
|
|
|
180
167
|
// Teardown
|
|
181
168
|
fish.teardown(tools);
|
|
182
169
|
content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
183
|
-
assert.ok(!content.includes("
|
|
184
|
-
assert.ok(!content.includes("alias yarn "));
|
|
170
|
+
assert.ok(!content.includes("source ~/.safe-chain/scripts/init-fish.fish"));
|
|
185
171
|
});
|
|
186
172
|
|
|
187
173
|
it("should handle multiple setup calls", () => {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
fish.setup(
|
|
191
|
-
fish.teardown(tools);
|
|
192
|
-
fish.setup(tools);
|
|
174
|
+
fish.setup();
|
|
175
|
+
fish.teardown(knownAikidoTools);
|
|
176
|
+
fish.setup();
|
|
193
177
|
|
|
194
178
|
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
195
|
-
const
|
|
196
|
-
assert.strictEqual(
|
|
179
|
+
const sourceMatches = (content.match(/source ~\/\.safe-chain\/scripts\/init-fish\.fish/g) || []).length;
|
|
180
|
+
assert.strictEqual(sourceMatches, 1, "Should have exactly one source line after setup-teardown-setup cycle");
|
|
197
181
|
});
|
|
198
182
|
});
|
|
199
183
|
});
|
|
@@ -24,18 +24,22 @@ function teardown(tools) {
|
|
|
24
24
|
);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
// Remove the line that sources the safe-chain PowerShell initialization script
|
|
28
|
+
removeLinesMatchingPattern(
|
|
29
|
+
startupFile,
|
|
30
|
+
/^\.\s+["']?\$HOME[/\\].safe-chain[/\\]scripts[/\\]init-pwsh\.ps1["']?/
|
|
31
|
+
);
|
|
32
|
+
|
|
27
33
|
return true;
|
|
28
34
|
}
|
|
29
35
|
|
|
30
|
-
function setup(
|
|
36
|
+
function setup() {
|
|
31
37
|
const startupFile = getStartupFile();
|
|
32
38
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
+
addLineToFile(
|
|
40
|
+
startupFile,
|
|
41
|
+
`. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1" # Safe-chain PowerShell initialization script`
|
|
42
|
+
);
|
|
39
43
|
|
|
40
44
|
return true;
|
|
41
45
|
}
|
|
@@ -69,49 +69,47 @@ describe("PowerShell Core shell integration", () => {
|
|
|
69
69
|
});
|
|
70
70
|
|
|
71
71
|
describe("setup", () => {
|
|
72
|
-
it("should add
|
|
73
|
-
const
|
|
74
|
-
{ tool: "npm", aikidoCommand: "aikido-npm" },
|
|
75
|
-
{ tool: "npx", aikidoCommand: "aikido-npx" },
|
|
76
|
-
{ tool: "yarn", aikidoCommand: "aikido-yarn" },
|
|
77
|
-
];
|
|
78
|
-
|
|
79
|
-
const result = powershell.setup(tools);
|
|
72
|
+
it("should add init-pwsh.ps1 source line", () => {
|
|
73
|
+
const result = powershell.setup();
|
|
80
74
|
assert.strictEqual(result, true);
|
|
81
75
|
|
|
82
76
|
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
83
|
-
assert.ok(
|
|
84
|
-
content.includes("Set-Alias npm aikido-npm # Safe-chain alias for npm")
|
|
85
|
-
);
|
|
86
|
-
assert.ok(
|
|
87
|
-
content.includes("Set-Alias npx aikido-npx # Safe-chain alias for npx")
|
|
88
|
-
);
|
|
89
77
|
assert.ok(
|
|
90
78
|
content.includes(
|
|
91
|
-
"
|
|
79
|
+
'. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1" # Safe-chain PowerShell initialization script'
|
|
92
80
|
)
|
|
93
81
|
);
|
|
94
82
|
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe("teardown", () => {
|
|
86
|
+
it("should remove init-pwsh.ps1 source line", () => {
|
|
87
|
+
const initialContent = [
|
|
88
|
+
"# PowerShell profile",
|
|
89
|
+
'. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1" # Safe-chain PowerShell initialization script',
|
|
90
|
+
"Set-Alias ls Get-ChildItem",
|
|
91
|
+
"Set-Alias grep Select-String",
|
|
92
|
+
].join("\n");
|
|
95
93
|
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
fs.writeFileSync(mockStartupFile, initialContent, "utf-8");
|
|
95
|
+
|
|
96
|
+
const result = powershell.teardown(knownAikidoTools);
|
|
98
97
|
assert.strictEqual(result, true);
|
|
99
98
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
99
|
+
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
100
|
+
assert.ok(
|
|
101
|
+
!content.includes('. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"')
|
|
102
|
+
);
|
|
103
|
+
assert.ok(content.includes("Set-Alias ls "));
|
|
104
|
+
assert.ok(content.includes("Set-Alias grep "));
|
|
105
105
|
});
|
|
106
|
-
});
|
|
107
106
|
|
|
108
|
-
|
|
109
|
-
it("should remove npm, npx, and yarn aliases", () => {
|
|
107
|
+
it("should remove old-style aliases from earlier versions", () => {
|
|
110
108
|
const initialContent = [
|
|
111
109
|
"# PowerShell profile",
|
|
112
|
-
"Set-Alias npm aikido-npm",
|
|
113
|
-
"Set-Alias npx aikido-npx",
|
|
114
|
-
"Set-Alias yarn aikido-yarn",
|
|
110
|
+
"Set-Alias npm aikido-npm # Safe-chain alias for npm",
|
|
111
|
+
"Set-Alias npx aikido-npx # Safe-chain alias for npx",
|
|
112
|
+
"Set-Alias yarn aikido-yarn # Safe-chain alias for yarn",
|
|
115
113
|
"Set-Alias ls Get-ChildItem",
|
|
116
114
|
"Set-Alias grep Select-String",
|
|
117
115
|
].join("\n");
|
|
@@ -138,7 +136,7 @@ describe("PowerShell Core shell integration", () => {
|
|
|
138
136
|
assert.strictEqual(result, true);
|
|
139
137
|
});
|
|
140
138
|
|
|
141
|
-
it("should handle file with no relevant
|
|
139
|
+
it("should handle file with no relevant content", () => {
|
|
142
140
|
const initialContent = [
|
|
143
141
|
"# PowerShell profile",
|
|
144
142
|
"Set-Alias ls Get-ChildItem",
|
|
@@ -171,34 +169,32 @@ describe("PowerShell Core shell integration", () => {
|
|
|
171
169
|
|
|
172
170
|
describe("integration tests", () => {
|
|
173
171
|
it("should handle complete setup and teardown cycle", () => {
|
|
174
|
-
const tools = [
|
|
175
|
-
{ tool: "npm", aikidoCommand: "aikido-npm" },
|
|
176
|
-
{ tool: "yarn", aikidoCommand: "aikido-yarn" },
|
|
177
|
-
];
|
|
178
|
-
|
|
179
172
|
// Setup
|
|
180
|
-
powershell.setup(
|
|
173
|
+
powershell.setup();
|
|
181
174
|
let content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
182
|
-
assert.ok(
|
|
183
|
-
|
|
175
|
+
assert.ok(
|
|
176
|
+
content.includes('. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"')
|
|
177
|
+
);
|
|
184
178
|
|
|
185
179
|
// Teardown
|
|
186
|
-
powershell.teardown(
|
|
180
|
+
powershell.teardown(knownAikidoTools);
|
|
187
181
|
content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
188
|
-
assert.ok(
|
|
189
|
-
|
|
182
|
+
assert.ok(
|
|
183
|
+
!content.includes('. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"')
|
|
184
|
+
);
|
|
190
185
|
});
|
|
191
186
|
|
|
192
187
|
it("should handle multiple setup calls", () => {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
powershell.setup(
|
|
196
|
-
powershell.teardown(tools);
|
|
197
|
-
powershell.setup(tools);
|
|
188
|
+
powershell.setup();
|
|
189
|
+
powershell.teardown(knownAikidoTools);
|
|
190
|
+
powershell.setup();
|
|
198
191
|
|
|
199
192
|
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
200
|
-
const
|
|
201
|
-
|
|
193
|
+
const sourceMatches = (
|
|
194
|
+
content.match(/\. "\$HOME\\.safe-chain\\scripts\\init-pwsh\.ps1"/g) ||
|
|
195
|
+
[]
|
|
196
|
+
).length;
|
|
197
|
+
assert.strictEqual(sourceMatches, 1, "Should not duplicate source lines");
|
|
202
198
|
});
|
|
203
199
|
});
|
|
204
200
|
});
|
|
@@ -24,18 +24,22 @@ function teardown(tools) {
|
|
|
24
24
|
);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
// Remove the line that sources the safe-chain PowerShell initialization script
|
|
28
|
+
removeLinesMatchingPattern(
|
|
29
|
+
startupFile,
|
|
30
|
+
/^\.\s+["']?\$HOME[/\\].safe-chain[/\\]scripts[/\\]init-pwsh\.ps1["']?/
|
|
31
|
+
);
|
|
32
|
+
|
|
27
33
|
return true;
|
|
28
34
|
}
|
|
29
35
|
|
|
30
|
-
function setup(
|
|
36
|
+
function setup() {
|
|
31
37
|
const startupFile = getStartupFile();
|
|
32
38
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
+
addLineToFile(
|
|
40
|
+
startupFile,
|
|
41
|
+
`. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1" # Safe-chain PowerShell initialization script`
|
|
42
|
+
);
|
|
39
43
|
|
|
40
44
|
return true;
|
|
41
45
|
}
|
|
@@ -69,49 +69,47 @@ describe("Windows PowerShell shell integration", () => {
|
|
|
69
69
|
});
|
|
70
70
|
|
|
71
71
|
describe("setup", () => {
|
|
72
|
-
it("should add
|
|
73
|
-
const
|
|
74
|
-
{ tool: "npm", aikidoCommand: "aikido-npm" },
|
|
75
|
-
{ tool: "npx", aikidoCommand: "aikido-npx" },
|
|
76
|
-
{ tool: "yarn", aikidoCommand: "aikido-yarn" },
|
|
77
|
-
];
|
|
78
|
-
|
|
79
|
-
const result = windowsPowershell.setup(tools);
|
|
72
|
+
it("should add init-pwsh.ps1 source line", () => {
|
|
73
|
+
const result = windowsPowershell.setup();
|
|
80
74
|
assert.strictEqual(result, true);
|
|
81
75
|
|
|
82
76
|
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
83
|
-
assert.ok(
|
|
84
|
-
content.includes("Set-Alias npm aikido-npm # Safe-chain alias for npm")
|
|
85
|
-
);
|
|
86
|
-
assert.ok(
|
|
87
|
-
content.includes("Set-Alias npx aikido-npx # Safe-chain alias for npx")
|
|
88
|
-
);
|
|
89
77
|
assert.ok(
|
|
90
78
|
content.includes(
|
|
91
|
-
"
|
|
79
|
+
'. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1" # Safe-chain PowerShell initialization script'
|
|
92
80
|
)
|
|
93
81
|
);
|
|
94
82
|
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe("teardown", () => {
|
|
86
|
+
it("should remove init-pwsh.ps1 source line", () => {
|
|
87
|
+
const initialContent = [
|
|
88
|
+
"# Windows PowerShell profile",
|
|
89
|
+
'. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1" # Safe-chain PowerShell initialization script',
|
|
90
|
+
"Set-Alias ls Get-ChildItem",
|
|
91
|
+
"Set-Alias grep Select-String",
|
|
92
|
+
].join("\n");
|
|
95
93
|
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
fs.writeFileSync(mockStartupFile, initialContent, "utf-8");
|
|
95
|
+
|
|
96
|
+
const result = windowsPowershell.teardown(knownAikidoTools);
|
|
98
97
|
assert.strictEqual(result, true);
|
|
99
98
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
99
|
+
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
100
|
+
assert.ok(
|
|
101
|
+
!content.includes('. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"')
|
|
102
|
+
);
|
|
103
|
+
assert.ok(content.includes("Set-Alias ls "));
|
|
104
|
+
assert.ok(content.includes("Set-Alias grep "));
|
|
105
105
|
});
|
|
106
|
-
});
|
|
107
106
|
|
|
108
|
-
|
|
109
|
-
it("should remove npm, npx, and yarn aliases", () => {
|
|
107
|
+
it("should remove old-style aliases from earlier versions", () => {
|
|
110
108
|
const initialContent = [
|
|
111
109
|
"# Windows PowerShell profile",
|
|
112
|
-
"Set-Alias npm aikido-npm",
|
|
113
|
-
"Set-Alias npx aikido-npx",
|
|
114
|
-
"Set-Alias yarn aikido-yarn",
|
|
110
|
+
"Set-Alias npm aikido-npm # Safe-chain alias for npm",
|
|
111
|
+
"Set-Alias npx aikido-npx # Safe-chain alias for npx",
|
|
112
|
+
"Set-Alias yarn aikido-yarn # Safe-chain alias for yarn",
|
|
115
113
|
"Set-Alias ls Get-ChildItem",
|
|
116
114
|
"Set-Alias grep Select-String",
|
|
117
115
|
].join("\n");
|
|
@@ -138,7 +136,7 @@ describe("Windows PowerShell shell integration", () => {
|
|
|
138
136
|
assert.strictEqual(result, true);
|
|
139
137
|
});
|
|
140
138
|
|
|
141
|
-
it("should handle file with no relevant
|
|
139
|
+
it("should handle file with no relevant content", () => {
|
|
142
140
|
const initialContent = [
|
|
143
141
|
"# Windows PowerShell profile",
|
|
144
142
|
"Set-Alias ls Get-ChildItem",
|
|
@@ -171,34 +169,32 @@ describe("Windows PowerShell shell integration", () => {
|
|
|
171
169
|
|
|
172
170
|
describe("integration tests", () => {
|
|
173
171
|
it("should handle complete setup and teardown cycle", () => {
|
|
174
|
-
const tools = [
|
|
175
|
-
{ tool: "npm", aikidoCommand: "aikido-npm" },
|
|
176
|
-
{ tool: "yarn", aikidoCommand: "aikido-yarn" },
|
|
177
|
-
];
|
|
178
|
-
|
|
179
172
|
// Setup
|
|
180
|
-
windowsPowershell.setup(
|
|
173
|
+
windowsPowershell.setup();
|
|
181
174
|
let content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
182
|
-
assert.ok(
|
|
183
|
-
|
|
175
|
+
assert.ok(
|
|
176
|
+
content.includes('. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"')
|
|
177
|
+
);
|
|
184
178
|
|
|
185
179
|
// Teardown
|
|
186
|
-
windowsPowershell.teardown(
|
|
180
|
+
windowsPowershell.teardown(knownAikidoTools);
|
|
187
181
|
content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
188
|
-
assert.ok(
|
|
189
|
-
|
|
182
|
+
assert.ok(
|
|
183
|
+
!content.includes('. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"')
|
|
184
|
+
);
|
|
190
185
|
});
|
|
191
186
|
|
|
192
187
|
it("should handle multiple setup calls", () => {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
windowsPowershell.setup(
|
|
196
|
-
windowsPowershell.teardown(tools);
|
|
197
|
-
windowsPowershell.setup(tools);
|
|
188
|
+
windowsPowershell.setup();
|
|
189
|
+
windowsPowershell.teardown(knownAikidoTools);
|
|
190
|
+
windowsPowershell.setup();
|
|
198
191
|
|
|
199
192
|
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
200
|
-
const
|
|
201
|
-
|
|
193
|
+
const sourceMatches = (
|
|
194
|
+
content.match(/\. "\$HOME\\.safe-chain\\scripts\\init-pwsh\.ps1"/g) ||
|
|
195
|
+
[]
|
|
196
|
+
).length;
|
|
197
|
+
assert.strictEqual(sourceMatches, 1, "Should not duplicate source lines");
|
|
202
198
|
});
|
|
203
199
|
});
|
|
204
200
|
});
|
|
@@ -21,18 +21,22 @@ function teardown(tools) {
|
|
|
21
21
|
removeLinesMatchingPattern(startupFile, new RegExp(`^alias\\s+${tool}=`));
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
// Removes the line that sources the safe-chain zsh initialization script (~/.aikido/scripts/init-posix.sh)
|
|
25
|
+
removeLinesMatchingPattern(
|
|
26
|
+
startupFile,
|
|
27
|
+
/^source\s+~\/\.safe-chain\/scripts\/init-posix\.sh/
|
|
28
|
+
);
|
|
29
|
+
|
|
24
30
|
return true;
|
|
25
31
|
}
|
|
26
32
|
|
|
27
|
-
function setup(
|
|
33
|
+
function setup() {
|
|
28
34
|
const startupFile = getStartupFile();
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
+
addLineToFile(
|
|
37
|
+
startupFile,
|
|
38
|
+
`source ~/.safe-chain/scripts/init-posix.sh # Safe-chain Zsh initialization script`
|
|
39
|
+
);
|
|
36
40
|
|
|
37
41
|
return true;
|
|
38
42
|
}
|
|
@@ -66,37 +66,24 @@ describe("Zsh shell integration", () => {
|
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
describe("setup", () => {
|
|
69
|
-
it("should add
|
|
70
|
-
const
|
|
71
|
-
{ tool: "npm", aikidoCommand: "aikido-npm" },
|
|
72
|
-
{ tool: "npx", aikidoCommand: "aikido-npx" },
|
|
73
|
-
{ tool: "yarn", aikidoCommand: "aikido-yarn" },
|
|
74
|
-
];
|
|
75
|
-
|
|
76
|
-
const result = zsh.setup(tools);
|
|
69
|
+
it("should add source line for zsh initialization script", () => {
|
|
70
|
+
const result = zsh.setup();
|
|
77
71
|
assert.strictEqual(result, true);
|
|
78
72
|
|
|
79
73
|
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
80
74
|
assert.ok(
|
|
81
|
-
content.includes(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
content.includes('alias npx="aikido-npx" # Safe-chain alias for npx')
|
|
85
|
-
);
|
|
86
|
-
assert.ok(
|
|
87
|
-
content.includes('alias yarn="aikido-yarn" # Safe-chain alias for yarn')
|
|
75
|
+
content.includes(
|
|
76
|
+
"source ~/.safe-chain/scripts/init-posix.sh # Safe-chain Zsh initialization script"
|
|
77
|
+
)
|
|
88
78
|
);
|
|
89
79
|
});
|
|
90
80
|
|
|
91
|
-
it("should handle empty
|
|
92
|
-
const result = zsh.setup(
|
|
81
|
+
it("should handle empty startup file", () => {
|
|
82
|
+
const result = zsh.setup();
|
|
93
83
|
assert.strictEqual(result, true);
|
|
94
84
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
98
|
-
assert.strictEqual(content.trim(), "");
|
|
99
|
-
}
|
|
85
|
+
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
86
|
+
assert.ok(content.includes("source ~/.safe-chain/scripts/init-posix.sh"));
|
|
100
87
|
});
|
|
101
88
|
});
|
|
102
89
|
|
|
@@ -124,6 +111,25 @@ describe("Zsh shell integration", () => {
|
|
|
124
111
|
assert.ok(content.includes("alias grep="));
|
|
125
112
|
});
|
|
126
113
|
|
|
114
|
+
it("should remove zsh initialization script source line", () => {
|
|
115
|
+
const initialContent = [
|
|
116
|
+
"#!/bin/zsh",
|
|
117
|
+
"source ~/.safe-chain/scripts/init-posix.sh",
|
|
118
|
+
"alias ls='ls --color=auto'",
|
|
119
|
+
].join("\n");
|
|
120
|
+
|
|
121
|
+
fs.writeFileSync(mockStartupFile, initialContent, "utf-8");
|
|
122
|
+
|
|
123
|
+
const result = zsh.teardown(knownAikidoTools);
|
|
124
|
+
assert.strictEqual(result, true);
|
|
125
|
+
|
|
126
|
+
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
127
|
+
assert.ok(
|
|
128
|
+
!content.includes("source ~/.safe-chain/scripts/init-posix.sh")
|
|
129
|
+
);
|
|
130
|
+
assert.ok(content.includes("alias ls="));
|
|
131
|
+
});
|
|
132
|
+
|
|
127
133
|
it("should handle file that doesn't exist", () => {
|
|
128
134
|
if (fs.existsSync(mockStartupFile)) {
|
|
129
135
|
fs.unlinkSync(mockStartupFile);
|
|
@@ -133,7 +139,7 @@ describe("Zsh shell integration", () => {
|
|
|
133
139
|
assert.strictEqual(result, true);
|
|
134
140
|
});
|
|
135
141
|
|
|
136
|
-
it("should handle file with no relevant aliases", () => {
|
|
142
|
+
it("should handle file with no relevant aliases or source lines", () => {
|
|
137
143
|
const initialContent = [
|
|
138
144
|
"#!/bin/zsh",
|
|
139
145
|
"alias ls='ls --color=auto'",
|
|
@@ -172,16 +178,16 @@ describe("Zsh shell integration", () => {
|
|
|
172
178
|
];
|
|
173
179
|
|
|
174
180
|
// Setup
|
|
175
|
-
zsh.setup(
|
|
181
|
+
zsh.setup();
|
|
176
182
|
let content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
177
|
-
assert.ok(content.includes(
|
|
178
|
-
assert.ok(content.includes('alias yarn="aikido-yarn"'));
|
|
183
|
+
assert.ok(content.includes("source ~/.safe-chain/scripts/init-posix.sh"));
|
|
179
184
|
|
|
180
185
|
// Teardown
|
|
181
186
|
zsh.teardown(tools);
|
|
182
187
|
content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
183
|
-
assert.ok(
|
|
184
|
-
|
|
188
|
+
assert.ok(
|
|
189
|
+
!content.includes("source ~/.safe-chain/scripts/init-posix.sh")
|
|
190
|
+
);
|
|
185
191
|
});
|
|
186
192
|
|
|
187
193
|
it("should handle multiple setup calls", () => {
|
|
@@ -192,8 +198,29 @@ describe("Zsh shell integration", () => {
|
|
|
192
198
|
zsh.setup(tools);
|
|
193
199
|
|
|
194
200
|
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
195
|
-
const
|
|
196
|
-
|
|
201
|
+
const sourceMatches = (content.match(/source.*init-posix\.sh/g) || [])
|
|
202
|
+
.length;
|
|
203
|
+
assert.strictEqual(sourceMatches, 1, "Should not duplicate source lines");
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("should handle mixed content with aliases and source lines", () => {
|
|
207
|
+
const initialContent = [
|
|
208
|
+
"#!/bin/zsh",
|
|
209
|
+
"alias npm='old-npm'",
|
|
210
|
+
"source ~/.safe-chain/scripts/init-posix.sh",
|
|
211
|
+
"alias ls='ls --color=auto'",
|
|
212
|
+
].join("\n");
|
|
213
|
+
|
|
214
|
+
fs.writeFileSync(mockStartupFile, initialContent, "utf-8");
|
|
215
|
+
|
|
216
|
+
// Teardown should remove both aliases and source line
|
|
217
|
+
zsh.teardown(knownAikidoTools);
|
|
218
|
+
const content = fs.readFileSync(mockStartupFile, "utf-8");
|
|
219
|
+
assert.ok(!content.includes("alias npm="));
|
|
220
|
+
assert.ok(
|
|
221
|
+
!content.includes("source ~/.safe-chain/scripts/init-posix.sh")
|
|
222
|
+
);
|
|
223
|
+
assert.ok(content.includes("alias ls="));
|
|
197
224
|
});
|
|
198
225
|
});
|
|
199
226
|
});
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
name: Create Release
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
tags:
|
|
6
|
-
- "*"
|
|
7
|
-
|
|
8
|
-
jobs:
|
|
9
|
-
build:
|
|
10
|
-
runs-on: ubuntu-latest
|
|
11
|
-
|
|
12
|
-
steps:
|
|
13
|
-
- name: Checkout code
|
|
14
|
-
uses: actions/checkout@v3
|
|
15
|
-
|
|
16
|
-
- name: Set up Node.js
|
|
17
|
-
uses: actions/setup-node@v3
|
|
18
|
-
with:
|
|
19
|
-
node-version: "lts/*"
|
|
20
|
-
registry-url: "https://registry.npmjs.org/"
|
|
21
|
-
env:
|
|
22
|
-
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
|
|
23
|
-
|
|
24
|
-
- name: Set version number
|
|
25
|
-
id: get_version
|
|
26
|
-
run: |
|
|
27
|
-
version="${{ github.ref_name }}"
|
|
28
|
-
echo "tag=$version" >> $GITHUB_OUTPUT
|
|
29
|
-
|
|
30
|
-
- name: Set the version
|
|
31
|
-
run: npm --no-git-tag-version version ${{ steps.get_version.outputs.tag }}
|
|
32
|
-
|
|
33
|
-
- name: Install dependencies
|
|
34
|
-
run: npm ci
|
|
35
|
-
|
|
36
|
-
- name: Publish to npm
|
|
37
|
-
run: |
|
|
38
|
-
echo "Publishing version ${{ steps.get_version.outputs.tag }} to NPM"
|
|
39
|
-
npm publish --access public
|
|
40
|
-
env:
|
|
41
|
-
NPM_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
name: Run Unit Tests
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
pull_request:
|
|
5
|
-
branches:
|
|
6
|
-
- main
|
|
7
|
-
|
|
8
|
-
jobs:
|
|
9
|
-
test:
|
|
10
|
-
runs-on: ubuntu-latest
|
|
11
|
-
|
|
12
|
-
steps:
|
|
13
|
-
- name: Checkout code
|
|
14
|
-
uses: actions/checkout@v3
|
|
15
|
-
|
|
16
|
-
- name: Set up Node.js
|
|
17
|
-
uses: actions/setup-node@v3
|
|
18
|
-
with:
|
|
19
|
-
node-version: "lts/*"
|
|
20
|
-
|
|
21
|
-
- name: Install dependencies
|
|
22
|
-
run: npm ci
|
|
23
|
-
|
|
24
|
-
- name: Run tests
|
|
25
|
-
run: npm test
|
|
26
|
-
|
|
27
|
-
- name: Run ESLint
|
|
28
|
-
run: npm run lint
|