directus-deploy-cli 0.18.0 → 0.20.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/README.md +31 -1
- package/dist/cli.js +69 -1
- package/dist/cli.js.map +1 -1
- package/dist/extensions.js +96 -19
- package/dist/extensions.js.map +1 -1
- package/dist/gcloud.js +91 -0
- package/dist/gcloud.js.map +1 -0
- package/dist/mcp-server.js +182 -1
- package/dist/mcp-server.js.map +1 -1
- package/dist/overview.js +525 -0
- package/dist/overview.js.map +1 -0
- package/dist/targets.js +10 -0
- package/dist/targets.js.map +1 -0
- package/dist/vm.js +109 -0
- package/dist/vm.js.map +1 -0
- package/package.json +1 -1
package/dist/vm.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { loadTargets } from "./targets.js";
|
|
2
|
+
import { mintIdToken } from "./gcloud.js";
|
|
3
|
+
// Re-export for existing importers/tests.
|
|
4
|
+
export { buildIdTokenAssertion } from "./gcloud.js";
|
|
5
|
+
// Pure resolver for the invoker SA key — also used by `push --publish` for
|
|
6
|
+
// GCS uploads when gsutil isn't available. Exported for tests.
|
|
7
|
+
export function resolveInvokerKey(name, target, env) {
|
|
8
|
+
const invokerEnv = target.control_invoker_key_env ?? `DIRECTUS_${name.toUpperCase()}_INVOKER_KEY_B64`;
|
|
9
|
+
const rawKey = env[invokerEnv];
|
|
10
|
+
if (!rawKey)
|
|
11
|
+
return undefined;
|
|
12
|
+
try {
|
|
13
|
+
const parsed = JSON.parse(Buffer.from(rawKey, "base64").toString("utf8"));
|
|
14
|
+
if (!parsed.client_email || !parsed.private_key)
|
|
15
|
+
throw new Error("missing fields");
|
|
16
|
+
return { client_email: parsed.client_email, private_key: parsed.private_key };
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
throw new Error(`target '${name}': $${invokerEnv} is not a base64-encoded service-account JSON key`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
// Pure resolver — exported for tests.
|
|
23
|
+
export function resolveVmControl(name, target, env) {
|
|
24
|
+
if (!target.control_url) {
|
|
25
|
+
throw new Error(`target '${name}' has no control_url in the targets file — deploy cloudfunctions/vm-control for it first`);
|
|
26
|
+
}
|
|
27
|
+
const tokenEnv = target.control_token_env ?? `DIRECTUS_${name.toUpperCase()}_CONTROL_TOKEN`;
|
|
28
|
+
const token = env[tokenEnv]; // optional — IAM-gated functions need no shared token
|
|
29
|
+
const invokerKey = resolveInvokerKey(name, target, env);
|
|
30
|
+
if (!token && !invokerKey) {
|
|
31
|
+
throw new Error(`target '${name}': no control credentials — set $${tokenEnv} (shared token) and/or $${target.control_invoker_key_env ?? `DIRECTUS_${name.toUpperCase()}_INVOKER_KEY_B64`} (invoker SA key)`);
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
controlUrl: target.control_url.replace(/\/+$/, ""),
|
|
35
|
+
token,
|
|
36
|
+
healthUrl: `${target.base_url.replace(/\/+$/, "")}/server/health`,
|
|
37
|
+
invokerKey,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export async function callControl(ctl, action, params = {}) {
|
|
41
|
+
const headers = {};
|
|
42
|
+
if (ctl.token)
|
|
43
|
+
headers["X-Control-Token"] = ctl.token;
|
|
44
|
+
if (ctl.invokerKey) {
|
|
45
|
+
headers.Authorization = `Bearer ${await mintIdToken(ctl.invokerKey, ctl.controlUrl)}`;
|
|
46
|
+
}
|
|
47
|
+
const qs = new URLSearchParams({ action, ...params });
|
|
48
|
+
const r = await fetch(`${ctl.controlUrl}?${qs.toString()}`, { method: "POST", headers });
|
|
49
|
+
const body = (await r.json().catch(() => ({})));
|
|
50
|
+
if (!r.ok) {
|
|
51
|
+
const hint = r.status === 401 || r.status === 403
|
|
52
|
+
? ctl.invokerKey
|
|
53
|
+
? " (invoker SA lacks run.invoker on the service, or the shared token mismatches)"
|
|
54
|
+
: " (service not publicly invokable? set DIRECTUS_<TARGET>_INVOKER_KEY_B64 with an invoker SA key)"
|
|
55
|
+
: "";
|
|
56
|
+
throw new Error(`vm-control ${action} failed: HTTP ${r.status}${hint} ${JSON.stringify(body)}`);
|
|
57
|
+
}
|
|
58
|
+
return body;
|
|
59
|
+
}
|
|
60
|
+
export async function isHealthy(healthUrl) {
|
|
61
|
+
try {
|
|
62
|
+
const r = await fetch(healthUrl, { signal: AbortSignal.timeout(8000) });
|
|
63
|
+
return r.ok;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export async function waitHealthy(healthUrl, timeoutMs, intervalMs = 10_000) {
|
|
70
|
+
const deadline = Date.now() + timeoutMs;
|
|
71
|
+
while (Date.now() < deadline) {
|
|
72
|
+
await new Promise((res) => setTimeout(res, intervalMs));
|
|
73
|
+
if (await isHealthy(healthUrl))
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
export async function runVm(input) {
|
|
79
|
+
const cfg = await loadTargets(input.targetsFile);
|
|
80
|
+
const t = cfg.targets[input.target];
|
|
81
|
+
if (!t) {
|
|
82
|
+
throw new Error(`unknown target '${input.target}' — known: ${Object.keys(cfg.targets).join(", ") || "(none)"}`);
|
|
83
|
+
}
|
|
84
|
+
const ctl = resolveVmControl(input.target, t, process.env);
|
|
85
|
+
const out = (o, human) => {
|
|
86
|
+
process.stdout.write(input.json ? JSON.stringify(o, null, 2) + "\n" : human + "\n");
|
|
87
|
+
};
|
|
88
|
+
if (input.action === "start") {
|
|
89
|
+
if (await isHealthy(ctl.healthUrl)) {
|
|
90
|
+
out({ target: input.target, healthy: true, operation: "no-op" }, `${input.target}: already up`);
|
|
91
|
+
return 0;
|
|
92
|
+
}
|
|
93
|
+
const r = await callControl(ctl, "start");
|
|
94
|
+
out(r, `${input.target}: ${String(r.operation ?? "start dispatched")} (was ${String(r.statusBefore)}) — waiting for health…`);
|
|
95
|
+
const ok = await waitHealthy(ctl.healthUrl, input.waitTimeoutMs);
|
|
96
|
+
out({ target: input.target, healthy: ok }, ok ? `${input.target}: up` : `${input.target}: still not healthy after ${Math.round(input.waitTimeoutMs / 1000)}s`);
|
|
97
|
+
return ok ? 0 : 1;
|
|
98
|
+
}
|
|
99
|
+
if (input.action === "stop") {
|
|
100
|
+
const r = await callControl(ctl, "stop");
|
|
101
|
+
out(r, `${input.target}: ${String(r.operation ?? "stop dispatched")} (was ${String(r.statusBefore)})`);
|
|
102
|
+
return 0;
|
|
103
|
+
}
|
|
104
|
+
const r = await callControl(ctl, "status");
|
|
105
|
+
const healthy = await isHealthy(ctl.healthUrl);
|
|
106
|
+
out({ ...r, healthy }, `${input.target}: instance ${String(r.statusBefore)}, directus ${healthy ? "responding" : "NOT responding"}`);
|
|
107
|
+
return 0;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=vm.js.map
|
package/dist/vm.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vm.js","sourceRoot":"","sources":["../src/vm.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "directus-deploy-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Per-entity, non-atomic Directus deployment tool. Reconciles collections/fields/relations/roles/policies/permissions/flows/operations/migrations/seeds from git to any environment.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|