arisa 5.1.10 → 5.1.11
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/package.json +1 -1
- package/src/runtime/slave-cli.js +1 -13
- package/src/runtime/slave-service.js +5 -20
- package/test/slave-cli.test.js +23 -11
package/package.json
CHANGED
package/src/runtime/slave-cli.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { access, mkdir, readFile, readdir, writeFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import readline from "node:readline/promises";
|
|
5
|
-
import { stdin, stdout } from "node:process";
|
|
6
4
|
import { applyConfigDefaults } from "../core/config/config-defaults.js";
|
|
7
5
|
import { installLockedOfficialTool } from "../core/tools/official-tool-installer.js";
|
|
8
6
|
import { createHeadlessApp } from "./create-headless-app.js";
|
|
@@ -100,15 +98,6 @@ export async function invokeSlaveTool(paths, args, { run = runProcess } = {}) {
|
|
|
100
98
|
});
|
|
101
99
|
}
|
|
102
100
|
|
|
103
|
-
async function askFromTerminal(prompt) {
|
|
104
|
-
const terminal = readline.createInterface({ input: stdin, output: stdout });
|
|
105
|
-
try {
|
|
106
|
-
return await terminal.question(`${prompt}: `);
|
|
107
|
-
} finally {
|
|
108
|
-
terminal.close();
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
101
|
async function listSlaveTools(paths) {
|
|
113
102
|
const entries = await readdir(paths.toolsDir, { withFileTypes: true }).catch(() => []);
|
|
114
103
|
const tools = [];
|
|
@@ -175,7 +164,6 @@ function explainSlaveBootstrapError(error) {
|
|
|
175
164
|
|
|
176
165
|
export async function runSlaveBootstrap(url, {
|
|
177
166
|
paths = getSlavePaths(resolveSlaveHome()),
|
|
178
|
-
ask = askFromTerminal,
|
|
179
167
|
selectAccount = selectSlaveServiceAccount,
|
|
180
168
|
ensureTool = ensureMasterSlaveTool,
|
|
181
169
|
installService = installSlaveSystemdService,
|
|
@@ -186,7 +174,7 @@ export async function runSlaveBootstrap(url, {
|
|
|
186
174
|
} = {}) {
|
|
187
175
|
parseSlaveBootstrapUrl(url);
|
|
188
176
|
if (platform !== "linux") throw new Error("Arisa Slave service installation currently requires Linux with systemd");
|
|
189
|
-
const account = await selectAccount(
|
|
177
|
+
const account = await selectAccount();
|
|
190
178
|
await ensureSlaveConfig(paths);
|
|
191
179
|
await ensureTool(paths);
|
|
192
180
|
let result;
|
|
@@ -39,29 +39,13 @@ export function getSlavePaths(slaveHome) {
|
|
|
39
39
|
export async function selectSlaveServiceAccount({
|
|
40
40
|
euid = process.geteuid?.(),
|
|
41
41
|
currentUser = os.userInfo().username,
|
|
42
|
-
|
|
42
|
+
environment = process.env
|
|
43
43
|
} = {}) {
|
|
44
44
|
if (euid !== 0) {
|
|
45
45
|
return { scope: "user", user: requireAccountName(currentUser), root: false, dedicated: false };
|
|
46
46
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"Run Arisa Slave as:",
|
|
50
|
-
"1. dedicated user arisa-slave (recommended)",
|
|
51
|
-
"2. another existing user",
|
|
52
|
-
"3. root",
|
|
53
|
-
"Selection"
|
|
54
|
-
].join("\n"))).trim();
|
|
55
|
-
if (choice === "1") return { scope: "system", user: "arisa-slave", root: false, dedicated: true };
|
|
56
|
-
if (choice === "2") {
|
|
57
|
-
return { scope: "system", user: requireAccountName(await ask("Existing service user")), root: false, dedicated: false };
|
|
58
|
-
}
|
|
59
|
-
if (choice === "3") {
|
|
60
|
-
const confirmation = String(await ask("Type RUN AS ROOT to confirm full root authority")).trim();
|
|
61
|
-
if (confirmation !== "RUN AS ROOT") throw new Error("Root execution was not confirmed");
|
|
62
|
-
return { scope: "system", user: "root", root: true, dedicated: false };
|
|
63
|
-
}
|
|
64
|
-
throw new Error("Invalid Arisa Slave service account selection");
|
|
47
|
+
const user = requireAccountName(environment.SUDO_USER || currentUser);
|
|
48
|
+
return { scope: "system", user, root: user === "root", dedicated: false };
|
|
65
49
|
}
|
|
66
50
|
|
|
67
51
|
function quoteSystemd(value) {
|
|
@@ -166,7 +150,8 @@ export async function installSlaveSystemdService({
|
|
|
166
150
|
}
|
|
167
151
|
const systemctlArgs = account.scope === "user" ? ["--user"] : [];
|
|
168
152
|
await execute("systemctl", [...systemctlArgs, "daemon-reload"], { env: environment });
|
|
169
|
-
await execute("systemctl", [...systemctlArgs, "enable",
|
|
153
|
+
await execute("systemctl", [...systemctlArgs, "enable", slaveServiceName], { env: environment });
|
|
154
|
+
await execute("systemctl", [...systemctlArgs, "restart", slaveServiceName], { env: environment });
|
|
170
155
|
return { unitFile, serviceName: slaveServiceName, account, paths };
|
|
171
156
|
}
|
|
172
157
|
|
package/test/slave-cli.test.js
CHANGED
|
@@ -58,16 +58,21 @@ test("hands one-shot requests through a 0600 file and removes it", async (t) =>
|
|
|
58
58
|
await assert.rejects(() => access(handedFile), { code: "ENOENT" });
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
-
test("
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
() => selectSlaveServiceAccount({ euid: 0, ask: async () => rejectedAnswers.shift() }),
|
|
66
|
-
/Root execution was not confirmed/
|
|
61
|
+
test("selects the invoking service account without prompting", async () => {
|
|
62
|
+
assert.deepEqual(
|
|
63
|
+
await selectSlaveServiceAccount({ euid: 1000, currentUser: "storybot", environment: {} }),
|
|
64
|
+
{ scope: "user", user: "storybot", root: false, dedicated: false }
|
|
67
65
|
);
|
|
68
|
-
const acceptedAnswers = ["3", "RUN AS ROOT"];
|
|
69
66
|
assert.deepEqual(
|
|
70
|
-
await selectSlaveServiceAccount({
|
|
67
|
+
await selectSlaveServiceAccount({
|
|
68
|
+
euid: 0,
|
|
69
|
+
currentUser: "root",
|
|
70
|
+
environment: { SUDO_USER: "storybot" }
|
|
71
|
+
}),
|
|
72
|
+
{ scope: "system", user: "storybot", root: false, dedicated: false }
|
|
73
|
+
);
|
|
74
|
+
assert.deepEqual(
|
|
75
|
+
await selectSlaveServiceAccount({ euid: 0, currentUser: "root", environment: {} }),
|
|
71
76
|
{ scope: "system", user: "root", root: true, dedicated: false }
|
|
72
77
|
);
|
|
73
78
|
});
|
|
@@ -109,7 +114,7 @@ test("refuses to replace the PID of an active Slave host", async (t) => {
|
|
|
109
114
|
await assert.rejects(() => registerSlaveServiceProcess(paths), /already running/);
|
|
110
115
|
});
|
|
111
116
|
|
|
112
|
-
test("installs the
|
|
117
|
+
test("installs and restarts the Linux systemd target after pairing", async (t) => {
|
|
113
118
|
const root = await mkdtemp(path.join(os.tmpdir(), "arisa-slave-systemd-"));
|
|
114
119
|
t.after(() => rm(root, { recursive: true, force: true }));
|
|
115
120
|
const calls = [];
|
|
@@ -132,7 +137,11 @@ test("installs the dedicated Linux systemd target without silently selecting roo
|
|
|
132
137
|
assert.equal(result.account.root, false);
|
|
133
138
|
assert.equal(await access(result.unitFile).then(() => true, () => false), true);
|
|
134
139
|
assert.ok(calls.some(([command]) => command === "useradd"));
|
|
135
|
-
assert.
|
|
140
|
+
assert.deepEqual(calls.filter(([command]) => command === "systemctl"), [
|
|
141
|
+
["systemctl", ["daemon-reload"]],
|
|
142
|
+
["systemctl", ["enable", "arisa-slave.service"]],
|
|
143
|
+
["systemctl", ["restart", "arisa-slave.service"]]
|
|
144
|
+
]);
|
|
136
145
|
});
|
|
137
146
|
|
|
138
147
|
test("validates before effects and keeps the bootstrap secret out of service metadata", async (t) => {
|
|
@@ -144,7 +153,10 @@ test("validates before effects and keeps the bootstrap secret out of service met
|
|
|
144
153
|
paths,
|
|
145
154
|
entryFile: "/opt/arisa/src/index.js",
|
|
146
155
|
platform: "linux",
|
|
147
|
-
selectAccount: async () =>
|
|
156
|
+
selectAccount: async (...args) => {
|
|
157
|
+
assert.deepEqual(args, []);
|
|
158
|
+
return { scope: "user", user: "tester", root: false, dedicated: false };
|
|
159
|
+
},
|
|
148
160
|
ensureTool: async () => { calls.push("tool"); },
|
|
149
161
|
installService: async () => { calls.push("service"); },
|
|
150
162
|
invokeTool: async (_paths, args) => {
|