@intentius/chant-lexicon-fly 0.108.0 → 0.109.0
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/dist/integrity.json +2 -2
- package/dist/manifest.json +1 -1
- package/dist/op/activities/box-services.d.ts +11 -1
- package/dist/op/activities/box-services.d.ts.map +1 -1
- package/dist/op/activities/index.d.ts +1 -1
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/sprite-config.d.ts +19 -4
- package/dist/op/activities/sprite-config.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/op/activities/box-services.ts +21 -1
- package/src/op/activities/index.ts +1 -1
- package/src/op/activities/sprite-box-services.test.ts +92 -7
- package/src/op/activities/sprite-config.ts +69 -12
package/dist/integrity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"algorithm": "sha256",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
4
|
+
"manifest.json": "52b5947746f6de511466da05f6c850977890d2be72a890bc4eff35002acdeae0",
|
|
5
5
|
"meta.json": "273f2a523b66f08ca91642dba2592c44765ab59ddf7329998884a0b08c6c3f24",
|
|
6
6
|
"types/index.d.ts": "2f01714e1aa5f5695d68416e143f281584efb84457138c93825fea34e2f3fa0a",
|
|
7
7
|
"rules/guest-sizing.ts": "bc749d218df8f3cc3d8fbe388205ae96e6b204ae0d6d77e8bf82cb936cc53ff6",
|
|
@@ -15,5 +15,5 @@
|
|
|
15
15
|
"skills/chant-fly-ops.md": "44102f1fdf611b9262c04457258990e44026e83f9e5b19ae86b69322f3a37620",
|
|
16
16
|
"skills/chant-fly-sprites.md": "3d100680422151b58d082c3a7b30a286edfc52faa4887995c9ad5553873bab95"
|
|
17
17
|
},
|
|
18
|
-
"composite": "
|
|
18
|
+
"composite": "7569d9ceb0231354bb9b0143f8386ca3f0100cada16c3768324ca7335d1036dd"
|
|
19
19
|
}
|
package/dist/manifest.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
/** One declared service, as the declaration states it (core's `BoxService`, less its pointer). */
|
|
13
13
|
export interface BoxServiceDeclaration {
|
|
14
14
|
name: string;
|
|
15
|
-
/** As written: `${VAR}
|
|
15
|
+
/** As written: split into argv and `${VAR}`-expanded by {@link serviceCommandArgv}. */
|
|
16
16
|
cmd: string;
|
|
17
17
|
needs: string[];
|
|
18
18
|
httpPort: number | null;
|
|
@@ -28,6 +28,16 @@ export declare function boxServices(cwd?: string): Promise<BoxServiceDeclaration
|
|
|
28
28
|
* empty path in it. Pure.
|
|
29
29
|
*/
|
|
30
30
|
export declare function expandServiceCommand(name: string, cmd: string, env?: NodeJS.ProcessEnv): string;
|
|
31
|
+
/**
|
|
32
|
+
* A service's command as the supervisor runs it: the executable and its
|
|
33
|
+
* arguments. The declared `cmd` is split on whitespace, with no quoting or
|
|
34
|
+
* other shell syntax (a command that needs it belongs in a script), and each
|
|
35
|
+
* word is then expanded by {@link expandServiceCommand}, so a variable whose
|
|
36
|
+
* value has a space stays one word. sprite-env takes the arguments as one
|
|
37
|
+
* comma-separated `--args`, so an argument with a comma in it is refused
|
|
38
|
+
* here rather than split there. Pure.
|
|
39
|
+
*/
|
|
40
|
+
export declare function serviceCommandArgv(name: string, cmd: string, env?: NodeJS.ProcessEnv): string[];
|
|
31
41
|
/**
|
|
32
42
|
* The services in an order each one's `needs` come before it, and in file
|
|
33
43
|
* order otherwise. The list is one the declaration reader accepted, so every
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"box-services.d.ts","sourceRoot":"","sources":["../../../src/op/activities/box-services.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,kGAAkG;AAClG,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,
|
|
1
|
+
{"version":3,"file":"box-services.d.ts","sourceRoot":"","sources":["../../../src/op/activities/box-services.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,kGAAkG;AAClG,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,uFAAuF;IACvF,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,0FAA0F;AAC1F,wBAAsB,WAAW,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAiBvF;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAM5G;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,EAAE,CAS5G;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,EAAE,QAAQ,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,EAAE,CAe9G"}
|
|
@@ -24,7 +24,7 @@ export type { DeclaredSpriteService, SpriteServicesObserveArgs, SpriteServicesOb
|
|
|
24
24
|
export { spriteWriteFile, spriteReadFile, spriteListDir, spriteRemove, spriteFsUrl, defaultSpritesRawHttp, } from "./sprite-fs.js";
|
|
25
25
|
export type { SpritesRawHttp, SpriteWriteFileArgs, SpriteReadFileArgs, SpriteReadFileResult, SpriteListDirArgs, SpriteDirEntry, SpriteRemoveArgs, } from "./sprite-fs.js";
|
|
26
26
|
export { spriteApplyNetworkPolicy, spriteApplyServices, validateNetworkRules, networkRulesEqual, validateServices, serviceConfigEqual, parseServiceDefinitions, listedServiceDiffers, spriteEnvCreateArgs, } from "./sprite-config.js";
|
|
27
|
-
export { boxServices, expandServiceCommand, inStartOrder } from "./box-services.js";
|
|
27
|
+
export { boxServices, expandServiceCommand, inStartOrder, serviceCommandArgv } from "./box-services.js";
|
|
28
28
|
export type { BoxServiceDeclaration } from "./box-services.js";
|
|
29
29
|
export type { NetworkRule, SpriteApplyNetworkPolicyArgs, SpriteApplyNetworkPolicyResult, ServiceSpec, SpriteApplyServicesArgs, SpriteApplyServicesResult, ServiceApplyAction, ListedService, } from "./sprite-config.js";
|
|
30
30
|
export { spriteTaskCreate, spriteTaskRefresh, spriteTaskRelease, spriteTasksUrl, spriteTaskUrl, } from "./sprite-tasks.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/op/activities/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/op/activities/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -81,13 +81,19 @@ export interface SpriteApplyServicesArgs {
|
|
|
81
81
|
start?: boolean;
|
|
82
82
|
/** Without `id`: restart each applied service that was already converged. Default: false. */
|
|
83
83
|
restart?: boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Without `id` and without `only`: delete each service the supervisor has
|
|
86
|
+
* that the box block does not declare (an `optional` one is declared), so a
|
|
87
|
+
* service dropped from the block stops running. Default: true.
|
|
88
|
+
*/
|
|
89
|
+
prune?: boolean;
|
|
84
90
|
/** Without `id`: the `sprite-env` binary. Default: the one on PATH, else /.sprite/bin/sprite-env. */
|
|
85
91
|
spriteEnv?: string;
|
|
86
92
|
endpoint?: string;
|
|
87
93
|
token?: string;
|
|
88
94
|
}
|
|
89
95
|
/** What an apply through sprite-env did to one service (#2880). */
|
|
90
|
-
export type ServiceApplyAction = "created" | "replaced" | "restarted" | "started" | "left";
|
|
96
|
+
export type ServiceApplyAction = "created" | "replaced" | "restarted" | "started" | "left" | "deleted";
|
|
91
97
|
export interface SpriteApplyServicesResult {
|
|
92
98
|
/** Names that were created or updated (converged services are skipped). */
|
|
93
99
|
applied: string[];
|
|
@@ -96,7 +102,7 @@ export interface SpriteApplyServicesResult {
|
|
|
96
102
|
/**
|
|
97
103
|
* Without `id`: each service applied, in the order it was applied, with
|
|
98
104
|
* what the apply did to it (#2880). `left` is a converged service that was
|
|
99
|
-
* not restarted or started.
|
|
105
|
+
* not restarted or started, `deleted` one the block does not declare (`prune`).
|
|
100
106
|
*/
|
|
101
107
|
services?: {
|
|
102
108
|
name: string;
|
|
@@ -137,6 +143,15 @@ export interface ListedService {
|
|
|
137
143
|
export declare function parseServiceDefinitions(text: string): ListedService[];
|
|
138
144
|
/** Whether a listed service differs from the declared one in `cmd`, `needs` or `httpPort`. A field the listing does not carry is not compared. Pure. */
|
|
139
145
|
export declare function listedServiceDiffers(listed: ListedService, declared: BoxServiceDeclaration, cmd: string): string | null;
|
|
140
|
-
/**
|
|
141
|
-
|
|
146
|
+
/**
|
|
147
|
+
* The arguments of `sprite-env services create` for a declared service.
|
|
148
|
+
* `argv` is the command from {@link serviceCommandArgv}: its first word is
|
|
149
|
+
* `--cmd`, the rest `--args` (comma-separated), as sprite-env and upstream
|
|
150
|
+
* Sprites take them; neither runs `--cmd` through a shell. A string is split
|
|
151
|
+
* on whitespace. With a `duration`, sprite-env streams the service's output
|
|
152
|
+
* for that long and fails the create if the service exits in it, so
|
|
153
|
+
* `--no-stream` (which returns as soon as the service is defined) is passed
|
|
154
|
+
* only without one. Pure.
|
|
155
|
+
*/
|
|
156
|
+
export declare function spriteEnvCreateArgs(s: BoxServiceDeclaration, argv: string | readonly string[]): string[];
|
|
142
157
|
//# sourceMappingURL=sprite-config.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sprite-config.d.ts","sourceRoot":"","sources":["../../../src/op/activities/sprite-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,OAAO,
|
|
1
|
+
{"version":3,"file":"sprite-config.d.ts","sourceRoot":"","sources":["../../../src/op/activities/sprite-config.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant-lexicon-fly",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.109.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"src/",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"zod": "^4.3.6"
|
|
70
70
|
},
|
|
71
71
|
"peerDependencies": {
|
|
72
|
-
"@intentius/chant": "^0.
|
|
72
|
+
"@intentius/chant": "^0.109.0",
|
|
73
73
|
"typescript": "^5.9.3",
|
|
74
74
|
"zod": "^4.3.6"
|
|
75
75
|
},
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
/** One declared service, as the declaration states it (core's `BoxService`, less its pointer). */
|
|
14
14
|
export interface BoxServiceDeclaration {
|
|
15
15
|
name: string;
|
|
16
|
-
/** As written: `${VAR}
|
|
16
|
+
/** As written: split into argv and `${VAR}`-expanded by {@link serviceCommandArgv}. */
|
|
17
17
|
cmd: string;
|
|
18
18
|
needs: string[];
|
|
19
19
|
httpPort: number | null;
|
|
@@ -55,6 +55,26 @@ export function expandServiceCommand(name: string, cmd: string, env: NodeJS.Proc
|
|
|
55
55
|
});
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
/**
|
|
59
|
+
* A service's command as the supervisor runs it: the executable and its
|
|
60
|
+
* arguments. The declared `cmd` is split on whitespace, with no quoting or
|
|
61
|
+
* other shell syntax (a command that needs it belongs in a script), and each
|
|
62
|
+
* word is then expanded by {@link expandServiceCommand}, so a variable whose
|
|
63
|
+
* value has a space stays one word. sprite-env takes the arguments as one
|
|
64
|
+
* comma-separated `--args`, so an argument with a comma in it is refused
|
|
65
|
+
* here rather than split there. Pure.
|
|
66
|
+
*/
|
|
67
|
+
export function serviceCommandArgv(name: string, cmd: string, env: NodeJS.ProcessEnv = process.env): string[] {
|
|
68
|
+
const words = cmd.trim().split(/\s+/).filter((w) => w !== "");
|
|
69
|
+
if (words.length === 0) throw new Error(`service ${name}'s cmd is empty`);
|
|
70
|
+
const argv = words.map((w) => expandServiceCommand(name, w, env));
|
|
71
|
+
const comma = argv.slice(1).find((a) => a.includes(","));
|
|
72
|
+
if (comma !== undefined) {
|
|
73
|
+
throw new Error(`service ${name}'s cmd has the argument ${JSON.stringify(comma)}, and sprite-env's --args is comma-separated, so it can't carry a comma; run the command from a script`);
|
|
74
|
+
}
|
|
75
|
+
return argv;
|
|
76
|
+
}
|
|
77
|
+
|
|
58
78
|
/**
|
|
59
79
|
* The services in an order each one's `needs` come before it, and in file
|
|
60
80
|
* order otherwise. The list is one the declaration reader accepted, so every
|
|
@@ -231,7 +231,7 @@ export {
|
|
|
231
231
|
spriteEnvCreateArgs,
|
|
232
232
|
} from "./sprite-config";
|
|
233
233
|
// A box's declared services (#2880), read from its box block with `box: true`.
|
|
234
|
-
export { boxServices, expandServiceCommand, inStartOrder } from "./box-services";
|
|
234
|
+
export { boxServices, expandServiceCommand, inStartOrder, serviceCommandArgv } from "./box-services";
|
|
235
235
|
export type { BoxServiceDeclaration } from "./box-services";
|
|
236
236
|
export type {
|
|
237
237
|
NetworkRule,
|
|
@@ -23,7 +23,7 @@ import { convergeTick } from "@intentius/chant/op/activities";
|
|
|
23
23
|
import { readConvergeLedger } from "@intentius/chant/lifecycle/converge-ledger";
|
|
24
24
|
import { spriteApplyServices, listedServiceDiffers, parseServiceDefinitions, spriteEnvCreateArgs } from "./sprite-config";
|
|
25
25
|
import { spriteServiceRestart, spriteServicesObserve } from "./sprite-service-converge";
|
|
26
|
-
import { expandServiceCommand } from "./box-services";
|
|
26
|
+
import { expandServiceCommand, serviceCommandArgv } from "./box-services";
|
|
27
27
|
import { spriteServicesObserve as observeStep, spriteServiceRestart as restartStep } from "../builders";
|
|
28
28
|
|
|
29
29
|
let root: string;
|
|
@@ -48,23 +48,29 @@ const fail = (m) => { console.error("sprite-env: " + m); process.exit(1); };
|
|
|
48
48
|
if (verb === "list") {
|
|
49
49
|
const out = readdirSync(dir).filter((f) => f.endsWith(".json")).sort().map((f) => {
|
|
50
50
|
const s = JSON.parse(readFileSync(join(dir, f), "utf8"));
|
|
51
|
-
return { name: s.name, cmd: s.cmd, args:
|
|
51
|
+
return { name: s.name, cmd: s.cmd, args: s.args, needs: s.needs, http_port: s.httpPort, state: { name: s.name, status: s.status } };
|
|
52
52
|
});
|
|
53
53
|
console.log(JSON.stringify(out));
|
|
54
54
|
} else if (verb === "create") {
|
|
55
55
|
if (existsSync(file(name))) fail("service " + name + " already exists");
|
|
56
|
-
const s = { name, cmd: null, needs: [], httpPort: 0, status: "running" };
|
|
56
|
+
const s = { name, cmd: null, args: [], needs: [], httpPort: 0, status: "running" };
|
|
57
57
|
for (let i = 0; i < rest.length; i++) {
|
|
58
58
|
const a = rest[i];
|
|
59
59
|
if (a === "--no-stream") continue;
|
|
60
60
|
const v = rest[++i];
|
|
61
61
|
if (a === "--cmd") s.cmd = v;
|
|
62
|
+
else if (a === "--args") s.args = v.split(",");
|
|
62
63
|
else if (a === "--needs") s.needs = v.split(",");
|
|
63
64
|
else if (a === "--http-port") s.httpPort = Number(v);
|
|
64
65
|
else if (a === "--duration") s.duration = v;
|
|
65
66
|
else fail("unknown option " + a);
|
|
66
67
|
}
|
|
67
68
|
for (const n of s.needs) if (!existsSync(file(n))) fail("service " + name + " needs " + n + ", which does not exist");
|
|
69
|
+
if (s.httpPort > 0) {
|
|
70
|
+
for (const f of readdirSync(dir).filter((f) => f.endsWith(".json"))) {
|
|
71
|
+
if (JSON.parse(readFileSync(join(dir, f), "utf8")).httpPort > 0) fail("another service already has an HTTP port configured (409)");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
68
74
|
writeFileSync(file(name), JSON.stringify(s));
|
|
69
75
|
} else if (verb === "delete") {
|
|
70
76
|
if (!existsSync(file(name))) fail("no service " + name);
|
|
@@ -195,13 +201,22 @@ describe("the sprite-env listing and create line (#2880)", () => {
|
|
|
195
201
|
expect(listedServiceDiffers({ name: "door", status: "running", cmd: "/h/d.sh", needs: [], httpPort: 8080 }, declared, "/h/d.sh")).toMatch(/^needs/);
|
|
196
202
|
expect(listedServiceDiffers({ name: "door", status: "running", cmd: "/h/d.sh", needs: ["hud"], httpPort: null }, declared, "/h/d.sh")).toMatch(/^httpPort/);
|
|
197
203
|
expect(listedServiceDiffers({ name: "door", status: "running" }, declared, "/h/d.sh")).toBeNull();
|
|
198
|
-
expect(spriteEnvCreateArgs({ ...declared, duration: "2s" }, "/h/d.sh")).toEqual(["services", "create", "door", "--cmd", "/h/d.sh", "--needs", "hud", "--http-port", "8080", "--duration", "2s"
|
|
204
|
+
expect(spriteEnvCreateArgs({ ...declared, duration: "2s" }, "/h/d.sh")).toEqual(["services", "create", "door", "--cmd", "/h/d.sh", "--needs", "hud", "--http-port", "8080", "--duration", "2s"]);
|
|
205
|
+
expect(spriteEnvCreateArgs(declared, ["node", "/h/preview.mjs", "--port", "5173"])).toEqual(["services", "create", "door", "--cmd", "node", "--args", "/h/preview.mjs,--port,5173", "--needs", "hud", "--http-port", "8080", "--no-stream"]);
|
|
199
206
|
});
|
|
200
207
|
|
|
201
208
|
test("${VAR} expands from the environment, and an unset one fails naming it", () => {
|
|
202
209
|
expect(expandServiceCommand("app", "${HOME}/box/run.sh --port ${PORT}", { HOME: "/home/sprite", PORT: "5173" })).toBe("/home/sprite/box/run.sh --port 5173");
|
|
203
210
|
expect(() => expandServiceCommand("app", "${NOPE}/run.sh", {})).toThrow("service app's cmd names ${NOPE}, which is not set in this process's environment");
|
|
204
211
|
});
|
|
212
|
+
|
|
213
|
+
test("a cmd with arguments splits on whitespace into the executable and its arguments, expanding each word (sandpit#6)", () => {
|
|
214
|
+
expect(serviceCommandArgv("app", "node ${HOME}/box/app/steward/preview.mjs", { HOME: "/home/sprite" })).toEqual(["node", "/home/sprite/box/app/steward/preview.mjs"]);
|
|
215
|
+
expect(serviceCommandArgv("app", " npm --prefix app start ", {})).toEqual(["npm", "--prefix", "app", "start"]);
|
|
216
|
+
expect(serviceCommandArgv("app", "${BIN} --root ${ROOT}", { BIN: "/opt/x", ROOT: "/a b" })).toEqual(["/opt/x", "--root", "/a b"]);
|
|
217
|
+
expect(() => serviceCommandArgv("app", "node a.js --hosts a,b", {})).toThrow('service app\'s cmd has the argument "a,b"');
|
|
218
|
+
expect(() => serviceCommandArgv("app", " ", {})).toThrow("service app's cmd is empty");
|
|
219
|
+
});
|
|
205
220
|
});
|
|
206
221
|
|
|
207
222
|
describe("spriteApplyServices({ box: true }) through sprite-env (#2880)", () => {
|
|
@@ -212,8 +227,8 @@ describe("spriteApplyServices({ box: true }) through sprite-env (#2880)", () =>
|
|
|
212
227
|
expect(first.services).toEqual([{ name: "app", action: "created" }, { name: "hud", action: "created" }]);
|
|
213
228
|
expect(first).toMatchObject({ applied: ["app", "hud"], started: [] });
|
|
214
229
|
expect(takeCalls()).toEqual([
|
|
215
|
-
"services create app --cmd /home/sprite/box/run-app.sh --duration 3s
|
|
216
|
-
"services create hud --cmd /home/sprite/box/run-daemon.sh --needs app --http-port 8080 --duration 3s
|
|
230
|
+
"services create app --cmd /home/sprite/box/run-app.sh --duration 3s",
|
|
231
|
+
"services create hud --cmd /home/sprite/box/run-daemon.sh --needs app --http-port 8080 --duration 3s",
|
|
217
232
|
]);
|
|
218
233
|
|
|
219
234
|
const second = await spriteApplyServices({ box: true, start: true });
|
|
@@ -224,10 +239,80 @@ describe("spriteApplyServices({ box: true }) through sprite-env (#2880)", () =>
|
|
|
224
239
|
declare([SERVICES()[0], { ...SERVICES()[1], cmd: "${BOX_HOME}/run-hud.sh" }, SERVICES()[2]]);
|
|
225
240
|
const third = await spriteApplyServices({ box: true, start: true });
|
|
226
241
|
expect(third.services).toEqual([{ name: "app", action: "left" }, { name: "hud", action: "replaced" }]);
|
|
227
|
-
expect(takeCalls()).toEqual(["services delete hud", "services create hud --cmd /home/sprite/box/run-hud.sh --needs app --http-port 8080 --duration 3s
|
|
242
|
+
expect(takeCalls()).toEqual(["services delete hud", "services create hud --cmd /home/sprite/box/run-hud.sh --needs app --http-port 8080 --duration 3s"]);
|
|
228
243
|
expect((listed().get("hud") as { cmd: string }).cmd).toBe("/home/sprite/box/run-hud.sh");
|
|
229
244
|
});
|
|
230
245
|
|
|
246
|
+
test("a cmd with arguments goes to sprite-env as --cmd and --args, and the listing it makes reads back as converged (sandpit#6)", async () => {
|
|
247
|
+
declare([{ name: "app", cmd: "node ${BOX_HOME}/preview.mjs --port 5173", duration: "3s", health: health("app") }]);
|
|
248
|
+
rmSync(join(state, "app.json"), { force: true });
|
|
249
|
+
takeCalls();
|
|
250
|
+
// hud, from the earlier tests, is not declared here, so the apply deletes it.
|
|
251
|
+
expect((await spriteApplyServices({ box: true })).services).toEqual([{ name: "hud", action: "deleted" }, { name: "app", action: "created" }]);
|
|
252
|
+
expect(takeCalls()).toEqual(["services delete hud", "services create app --cmd node --args /home/sprite/box/preview.mjs,--port,5173 --duration 3s"]);
|
|
253
|
+
expect(listed().get("app")).toMatchObject({ cmd: "node", args: ["/home/sprite/box/preview.mjs", "--port", "5173"] });
|
|
254
|
+
expect((await spriteApplyServices({ box: true })).services).toEqual([{ name: "app", action: "left" }]);
|
|
255
|
+
expect(takeCalls()).toEqual([]);
|
|
256
|
+
rmSync(join(state, "app.json"), { force: true });
|
|
257
|
+
declare(SERVICES());
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test("a moved httpPort is let go by its old holder before the new holder is defined with it", async () => {
|
|
261
|
+
await spriteApplyServices({ box: true });
|
|
262
|
+
takeCalls();
|
|
263
|
+
// app comes first in start order, so without the release app would be
|
|
264
|
+
// defined with the port while hud still holds it, and the supervisor refuses that.
|
|
265
|
+
const { httpPort: _moved, ...hudWithoutPort } = SERVICES()[1];
|
|
266
|
+
declare([{ ...SERVICES()[0], httpPort: 8080 }, hudWithoutPort, SERVICES()[2]]);
|
|
267
|
+
const moved = await spriteApplyServices({ box: true });
|
|
268
|
+
expect(moved.services).toEqual([{ name: "app", action: "replaced" }, { name: "hud", action: "replaced" }]);
|
|
269
|
+
expect(takeCalls()).toEqual([
|
|
270
|
+
"services delete hud",
|
|
271
|
+
"services create hud --cmd /home/sprite/box/run-daemon.sh --needs app --duration 3s",
|
|
272
|
+
"services delete app",
|
|
273
|
+
"services create app --cmd /home/sprite/box/run-app.sh --http-port 8080 --duration 3s",
|
|
274
|
+
]);
|
|
275
|
+
expect((listed().get("app") as { http_port: number }).http_port).toBe(8080);
|
|
276
|
+
expect((listed().get("hud") as { http_port: number }).http_port).toBe(0);
|
|
277
|
+
|
|
278
|
+
// And back again: app lets go first this time.
|
|
279
|
+
declare(SERVICES());
|
|
280
|
+
expect((await spriteApplyServices({ box: true })).services).toEqual([{ name: "app", action: "replaced" }, { name: "hud", action: "replaced" }]);
|
|
281
|
+
expect(takeCalls()).toEqual([
|
|
282
|
+
"services delete app",
|
|
283
|
+
"services create app --cmd /home/sprite/box/run-app.sh --duration 3s",
|
|
284
|
+
"services delete hud",
|
|
285
|
+
"services create hud --cmd /home/sprite/box/run-daemon.sh --needs app --http-port 8080 --duration 3s",
|
|
286
|
+
]);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
test("a full apply deletes the services the block does not declare, dependants first; only and prune: false leave them", async () => {
|
|
290
|
+
await spriteApplyServices({ box: true });
|
|
291
|
+
// Two services nobody declared, one needing the other, and an optional one that is declared.
|
|
292
|
+
const extra = (name: string, needs: string[]) =>
|
|
293
|
+
writeFileSync(join(state, `${name}.json`), JSON.stringify({ name, cmd: `/x/${name}.sh`, args: [], needs, httpPort: 0, status: "running" }));
|
|
294
|
+
extra("old-db", []);
|
|
295
|
+
extra("old-api", ["old-db"]);
|
|
296
|
+
extra("site", []);
|
|
297
|
+
takeCalls();
|
|
298
|
+
|
|
299
|
+
expect((await spriteApplyServices({ box: true, only: ["app"] })).services).toEqual([{ name: "app", action: "left" }]);
|
|
300
|
+
expect((await spriteApplyServices({ box: true, prune: false })).services).toEqual([{ name: "app", action: "left" }, { name: "hud", action: "left" }]);
|
|
301
|
+
expect(takeCalls()).toEqual([]);
|
|
302
|
+
|
|
303
|
+
const pruned = await spriteApplyServices({ box: true });
|
|
304
|
+
expect(pruned.services).toEqual([
|
|
305
|
+
{ name: "old-api", action: "deleted" },
|
|
306
|
+
{ name: "old-db", action: "deleted" },
|
|
307
|
+
{ name: "app", action: "left" },
|
|
308
|
+
{ name: "hud", action: "left" },
|
|
309
|
+
]);
|
|
310
|
+
expect(pruned.applied).toEqual([]);
|
|
311
|
+
expect(takeCalls()).toEqual(["services delete old-api", "services delete old-db"]);
|
|
312
|
+
expect([...listed().keys()].sort()).toEqual(["app", "hud", "site"]);
|
|
313
|
+
rmSync(join(state, "site.json"));
|
|
314
|
+
});
|
|
315
|
+
|
|
231
316
|
test("start starts a defined service that is stopped; without start it is left", async () => {
|
|
232
317
|
await spriteApplyServices({ box: true });
|
|
233
318
|
setStatus("app", "stopped");
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
import { execFile } from "node:child_process";
|
|
26
26
|
import { promisify } from "node:util";
|
|
27
|
-
import {
|
|
27
|
+
import { serviceCommandArgv, inStartOrder, boxServices, type BoxServiceDeclaration } from "./box-services";
|
|
28
28
|
import { findSpriteEnv } from "./sprite-service-converge";
|
|
29
29
|
import { resolveSpritesEndpoint, defaultSpritesHttp, type SpritesHttp } from "./sprites";
|
|
30
30
|
import { logProgress } from "./progress";
|
|
@@ -144,6 +144,12 @@ export interface SpriteApplyServicesArgs {
|
|
|
144
144
|
start?: boolean;
|
|
145
145
|
/** Without `id`: restart each applied service that was already converged. Default: false. */
|
|
146
146
|
restart?: boolean;
|
|
147
|
+
/**
|
|
148
|
+
* Without `id` and without `only`: delete each service the supervisor has
|
|
149
|
+
* that the box block does not declare (an `optional` one is declared), so a
|
|
150
|
+
* service dropped from the block stops running. Default: true.
|
|
151
|
+
*/
|
|
152
|
+
prune?: boolean;
|
|
147
153
|
/** Without `id`: the `sprite-env` binary. Default: the one on PATH, else /.sprite/bin/sprite-env. */
|
|
148
154
|
spriteEnv?: string;
|
|
149
155
|
endpoint?: string;
|
|
@@ -151,7 +157,7 @@ export interface SpriteApplyServicesArgs {
|
|
|
151
157
|
}
|
|
152
158
|
|
|
153
159
|
/** What an apply through sprite-env did to one service (#2880). */
|
|
154
|
-
export type ServiceApplyAction = "created" | "replaced" | "restarted" | "started" | "left";
|
|
160
|
+
export type ServiceApplyAction = "created" | "replaced" | "restarted" | "started" | "left" | "deleted";
|
|
155
161
|
|
|
156
162
|
export interface SpriteApplyServicesResult {
|
|
157
163
|
/** Names that were created or updated (converged services are skipped). */
|
|
@@ -161,7 +167,7 @@ export interface SpriteApplyServicesResult {
|
|
|
161
167
|
/**
|
|
162
168
|
* Without `id`: each service applied, in the order it was applied, with
|
|
163
169
|
* what the apply did to it (#2880). `left` is a converged service that was
|
|
164
|
-
* not restarted or started.
|
|
170
|
+
* not restarted or started, `deleted` one the block does not declare (`prune`).
|
|
165
171
|
*/
|
|
166
172
|
services?: { name: string; action: ServiceApplyAction }[];
|
|
167
173
|
}
|
|
@@ -363,18 +369,28 @@ export function listedServiceDiffers(listed: ListedService, declared: BoxService
|
|
|
363
369
|
return null;
|
|
364
370
|
}
|
|
365
371
|
|
|
366
|
-
/**
|
|
367
|
-
|
|
372
|
+
/**
|
|
373
|
+
* The arguments of `sprite-env services create` for a declared service.
|
|
374
|
+
* `argv` is the command from {@link serviceCommandArgv}: its first word is
|
|
375
|
+
* `--cmd`, the rest `--args` (comma-separated), as sprite-env and upstream
|
|
376
|
+
* Sprites take them; neither runs `--cmd` through a shell. A string is split
|
|
377
|
+
* on whitespace. With a `duration`, sprite-env streams the service's output
|
|
378
|
+
* for that long and fails the create if the service exits in it, so
|
|
379
|
+
* `--no-stream` (which returns as soon as the service is defined) is passed
|
|
380
|
+
* only without one. Pure.
|
|
381
|
+
*/
|
|
382
|
+
export function spriteEnvCreateArgs(s: BoxServiceDeclaration, argv: string | readonly string[]): string[] {
|
|
383
|
+
const [cmd, ...rest] = typeof argv === "string" ? argv.trim().split(/\s+/) : argv;
|
|
368
384
|
return [
|
|
369
385
|
"services",
|
|
370
386
|
"create",
|
|
371
387
|
s.name,
|
|
372
388
|
"--cmd",
|
|
373
389
|
cmd,
|
|
390
|
+
...(rest.length > 0 ? ["--args", rest.join(",")] : []),
|
|
374
391
|
...(s.needs.length > 0 ? ["--needs", s.needs.join(",")] : []),
|
|
375
392
|
...(s.httpPort !== null ? ["--http-port", String(s.httpPort)] : []),
|
|
376
|
-
...(s.duration !== null ? ["--duration", s.duration] : []),
|
|
377
|
-
"--no-stream",
|
|
393
|
+
...(s.duration !== null ? ["--duration", s.duration] : ["--no-stream"]),
|
|
378
394
|
];
|
|
379
395
|
}
|
|
380
396
|
|
|
@@ -400,7 +416,7 @@ async function applyThroughSpriteEnv(args: SpriteApplyServicesArgs, signal?: Abo
|
|
|
400
416
|
const only = args.only ? new Set(args.only) : null;
|
|
401
417
|
const targets = inStartOrder(declared).filter((s) => (only ? only.has(s.name) : !s.optional));
|
|
402
418
|
// Expand every command before changing anything, so a missing variable fails the step with nothing half-applied.
|
|
403
|
-
const commands = new Map(targets.map((s) => [s.name,
|
|
419
|
+
const commands = new Map(targets.map((s) => [s.name, serviceCommandArgv(s.name, s.cmd)]));
|
|
404
420
|
|
|
405
421
|
const bin = findSpriteEnv(args.spriteEnv);
|
|
406
422
|
if (!bin) throw new Error("spriteApplyServices: no sprite-env on PATH or in /.sprite/bin, and no sprite id to reach the Sprites API with");
|
|
@@ -414,12 +430,52 @@ async function applyThroughSpriteEnv(args: SpriteApplyServicesArgs, signal?: Abo
|
|
|
414
430
|
};
|
|
415
431
|
const live = new Map(parseServiceDefinitions(await spriteEnv(["services", "list"])).map((s) => [s.name, s]));
|
|
416
432
|
|
|
433
|
+
// A full apply leaves the supervisor with the declared services only: one
|
|
434
|
+
// the block no longer declares is deleted, before anything is defined, so
|
|
435
|
+
// the HTTP port or a name it held is free. Dependants go before what they need.
|
|
417
436
|
const services: { name: string; action: ServiceApplyAction }[] = [];
|
|
437
|
+
if (!only && args.prune !== false) {
|
|
438
|
+
const undeclared = new Set([...live.keys()].filter((n) => !names.has(n)));
|
|
439
|
+
while (undeclared.size > 0) {
|
|
440
|
+
const needed = new Set([...undeclared].flatMap((n) => live.get(n)?.needs ?? []));
|
|
441
|
+
const next = [...undeclared].sort().find((n) => !needed.has(n)) ?? [...undeclared].sort()[0];
|
|
442
|
+
await spriteEnv(["services", "delete", next]);
|
|
443
|
+
logProgress(`services: deleted ${next}, which the box block does not declare`);
|
|
444
|
+
services.push({ name: next, action: "deleted" });
|
|
445
|
+
undeclared.delete(next);
|
|
446
|
+
live.delete(next);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// The supervisor gives one service the HTTP port and refuses a second holder
|
|
451
|
+
// (409) while the first still has it. So a service whose listed port is not
|
|
452
|
+
// the declared one is replaced first, before any service is defined with a
|
|
453
|
+
// port: when the port moves from one service to another, the old holder has
|
|
454
|
+
// let go of it by the time the new one asks. One whose declared needs are not
|
|
455
|
+
// all defined yet can't be created early and waits for its turn below.
|
|
456
|
+
const released = new Set<string>();
|
|
418
457
|
for (const s of targets) {
|
|
419
|
-
const
|
|
458
|
+
const cur = live.get(s.name);
|
|
459
|
+
if (!cur || cur.httpPort === undefined || cur.httpPort === null || cur.httpPort === s.httpPort) continue;
|
|
460
|
+
if (!s.needs.every((n) => live.has(n))) continue;
|
|
461
|
+
const argv = commands.get(s.name)!;
|
|
462
|
+
const why = listedServiceDiffers(cur, s, argv.join(" "));
|
|
463
|
+
await spriteEnv(["services", "delete", s.name]);
|
|
464
|
+
await spriteEnv(spriteEnvCreateArgs(s, argv));
|
|
465
|
+
logProgress(`services: replaced ${s.name} (${why}) first, to free the HTTP port`);
|
|
466
|
+
released.add(s.name);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
for (const s of targets) {
|
|
470
|
+
if (released.has(s.name)) {
|
|
471
|
+
services.push({ name: s.name, action: "replaced" });
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
const argv = commands.get(s.name)!;
|
|
475
|
+
const cmd = argv.join(" ");
|
|
420
476
|
const cur = live.get(s.name);
|
|
421
477
|
if (!cur) {
|
|
422
|
-
await spriteEnv(spriteEnvCreateArgs(s,
|
|
478
|
+
await spriteEnv(spriteEnvCreateArgs(s, argv));
|
|
423
479
|
logProgress(`services: created ${s.name}`);
|
|
424
480
|
services.push({ name: s.name, action: "created" });
|
|
425
481
|
continue;
|
|
@@ -427,7 +483,7 @@ async function applyThroughSpriteEnv(args: SpriteApplyServicesArgs, signal?: Abo
|
|
|
427
483
|
const why = listedServiceDiffers(cur, s, cmd);
|
|
428
484
|
if (why) {
|
|
429
485
|
await spriteEnv(["services", "delete", s.name]);
|
|
430
|
-
await spriteEnv(spriteEnvCreateArgs(s,
|
|
486
|
+
await spriteEnv(spriteEnvCreateArgs(s, argv));
|
|
431
487
|
logProgress(`services: replaced ${s.name} (${why})`);
|
|
432
488
|
services.push({ name: s.name, action: "replaced" });
|
|
433
489
|
continue;
|
|
@@ -452,6 +508,7 @@ async function applyThroughSpriteEnv(args: SpriteApplyServicesArgs, signal?: Abo
|
|
|
452
508
|
}
|
|
453
509
|
}
|
|
454
510
|
const applied = services.filter((e) => e.action === "created" || e.action === "replaced").map((e) => e.name);
|
|
455
|
-
|
|
511
|
+
const deleted = services.filter((e) => e.action === "deleted").length;
|
|
512
|
+
logProgress(`services: applied ${applied.length}/${targets.length} through sprite-env, started ${started.length}${deleted > 0 ? `, deleted ${deleted}` : ""}`);
|
|
456
513
|
return { applied, started, services };
|
|
457
514
|
}
|