@intentic/cli 1.325.0 → 1.327.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 +7 -4
- package/dist/adopt/adopt.command.d.ts.map +1 -1
- package/dist/adopt/adopt.command.js +13 -6
- package/dist/adopt/adopt.command.js.map +1 -1
- package/dist/adopt/adopt.d.ts +5 -1
- package/dist/adopt/adopt.d.ts.map +1 -1
- package/dist/adopt/adopt.js +28 -3
- package/dist/adopt/adopt.js.map +1 -1
- package/dist/apply/apply-lock.d.ts +12 -0
- package/dist/apply/apply-lock.d.ts.map +1 -1
- package/dist/apply/apply-lock.js +50 -1
- package/dist/apply/apply-lock.js.map +1 -1
- package/dist/apply/apply.command.d.ts.map +1 -1
- package/dist/apply/apply.command.js +126 -48
- package/dist/apply/apply.command.js.map +1 -1
- package/dist/apply/baseline.d.ts +38 -0
- package/dist/apply/baseline.d.ts.map +1 -0
- package/dist/apply/baseline.js +108 -0
- package/dist/apply/baseline.js.map +1 -0
- package/dist/apply/prune-phase.d.ts +21 -0
- package/dist/apply/prune-phase.d.ts.map +1 -0
- package/dist/apply/prune-phase.js +46 -0
- package/dist/apply/prune-phase.js.map +1 -0
- package/dist/apply/retired-hosts.d.ts +18 -0
- package/dist/apply/retired-hosts.d.ts.map +1 -0
- package/dist/apply/retired-hosts.js +54 -0
- package/dist/apply/retired-hosts.js.map +1 -0
- package/dist/demo.js +2 -1
- package/dist/demo.js.map +1 -1
- package/dist/destroy/destroy.command.d.ts.map +1 -1
- package/dist/destroy/destroy.command.js +18 -6
- package/dist/destroy/destroy.command.js.map +1 -1
- package/dist/host-ssh-tunnel/host-ssh-tunnel.d.ts.map +1 -1
- package/dist/host-ssh-tunnel/host-ssh-tunnel.js +10 -5
- package/dist/host-ssh-tunnel/host-ssh-tunnel.js.map +1 -1
- package/dist/init/init.d.ts.map +1 -1
- package/dist/init/init.js +2 -1
- package/dist/init/init.js.map +1 -1
- package/dist/init/scaffold-app.js +1 -1
- package/dist/lib/cf-tunnel.d.ts +0 -1
- package/dist/lib/cf-tunnel.d.ts.map +1 -1
- package/dist/lib/cf-tunnel.js +12 -15
- package/dist/lib/cf-tunnel.js.map +1 -1
- package/dist/lib/flags.d.ts +2 -0
- package/dist/lib/flags.d.ts.map +1 -0
- package/dist/lib/flags.js +8 -0
- package/dist/lib/flags.js.map +1 -0
- package/dist/lib/known-hosts.d.ts +2 -1
- package/dist/lib/known-hosts.d.ts.map +1 -1
- package/dist/lib/known-hosts.js +2 -0
- package/dist/lib/known-hosts.js.map +1 -1
- package/dist/lib/output.d.ts.map +1 -1
- package/dist/lib/output.js +16 -5
- package/dist/lib/output.js.map +1 -1
- package/dist/lib/tables.d.ts +14 -0
- package/dist/lib/tables.d.ts.map +1 -1
- package/dist/lib/tables.js +42 -0
- package/dist/lib/tables.js.map +1 -1
- package/dist/logs/logs.command.d.ts.map +1 -1
- package/dist/logs/logs.command.js +6 -5
- package/dist/logs/logs.command.js.map +1 -1
- package/dist/pipelines/adopt-pipelines.d.ts +1 -0
- package/dist/pipelines/adopt-pipelines.d.ts.map +1 -1
- package/dist/pipelines/adopt-pipelines.js +6 -1
- package/dist/pipelines/adopt-pipelines.js.map +1 -1
- package/dist/plan/plan.command.d.ts +13 -0
- package/dist/plan/plan.command.d.ts.map +1 -1
- package/dist/plan/plan.command.js +94 -12
- package/dist/plan/plan.command.js.map +1 -1
- package/dist/resolve/resolve.command.d.ts.map +1 -1
- package/dist/resolve/resolve.command.js +10 -2
- package/dist/resolve/resolve.command.js.map +1 -1
- package/dist/resolve/resolve.d.ts +5 -1
- package/dist/resolve/resolve.d.ts.map +1 -1
- package/dist/resolve/resolve.js +31 -2
- package/dist/resolve/resolve.js.map +1 -1
- package/dist/restore/restore.command.js +3 -3
- package/dist/restore/restore.command.js.map +1 -1
- package/dist/secrets/generated-secrets.d.ts +1 -0
- package/dist/secrets/generated-secrets.d.ts.map +1 -1
- package/dist/secrets/generated-secrets.js +22 -0
- package/dist/secrets/generated-secrets.js.map +1 -1
- package/package.json +13 -14
- package/templates/workflows/apply.yaml.eta +26 -6
package/README.md
CHANGED
|
@@ -12,17 +12,20 @@ flowchart LR
|
|
|
12
12
|
engine --> infra["Hosts · Cloudflare<br/>Forgejo · Komodo"]
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
- Four route groups in `src/app.ts`: `deploy` (the tool itself), `sandbox` (prints the canonical `docker run` for a sandbox), `tunnel host` (a host's own Cloudflare SSH tunnel) and `scaffold` (a pnpm + turbo monorepo and its apps).
|
|
15
|
+
- Four route groups in `src/app.ts`: `deploy` (the tool itself), `sandbox` (prints the canonical `docker run` for a sandbox), `tunnel host` (a host's own Cloudflare SSH tunnel, made with the providers' own `findOrCreateTunnel` and `upsertCname` on the zone state-resolver's `selectZone` picks) and `scaffold` (a pnpm + turbo monorepo and its apps).
|
|
16
16
|
- A workspace has separate git repos: `intent/` holds `deploy.config.ts`, `desired-state/` holds the resolved `desired-state.json`, `.env` and generated secrets. `deploy init` scaffolds them; every other command reads these default paths from cwd.
|
|
17
|
-
- `deploy resolve` reaches the network only to discover the Cloudflare zone from the API token. `plan` and `apply` never re-resolve; they work from the baked artifact.
|
|
18
|
-
- `deploy apply` takes a lock on every host, generates missing secrets, applies authored renames, runs the reconcile loop, then prunes what the
|
|
19
|
-
-
|
|
17
|
+
- `deploy resolve` reaches the network only to discover the Cloudflare zone from the API token. `plan` and `apply` never re-resolve; they work from the baked artifact. Resolve keeps the artifact's `owner` id, the intent's mark on everything it deploys, and mints one only when neither the artifact nor the baseline has it; the CI pipeline never mints (2026-10-05).
|
|
18
|
+
- `deploy apply` takes a lock on every host, generates missing secrets, applies authored renames, runs the reconcile loop, then prunes what the baseline holds and this artifact does not, this intent's own orphans, and its leftovers on hosts a migration retired. Deletions wait for `--yes`. A heartbeat renews the lock while the run lasts and stops it if the lock is lost (2026-10-05).
|
|
19
|
+
- The prune baseline, `.last-applied.json` beside the artifact (in CI, the `intentic-applied` tag's message), is a record, not a copy (2026-10-05). It advances with every apply, even while deletions wait for `--yes`, and keeps removed nodes in `pendingDeletion` until a delete succeeds, and retired hosts until one scans clean. A baseline that cannot be read stops the run before it changes anything. A missing one is a first apply only when nothing live carries this intent's owner stamp; otherwise it was lost, and `--first-apply` is the person's way through.
|
|
20
|
+
- `deploy plan --check` is the drift check: it changes nothing and fails on anything apply would change or delete, and on unowned resources or a scan that could not look everywhere.
|
|
21
|
+
- `deploy adopt` pushes both repos to the provisioned Forgejo, seeds the `intentic-applied` tag with the local baseline, and installs pipelines, so later pushes resolve and apply in CI with this CLI's own version. The apply pipeline also runs the drift check daily.
|
|
20
22
|
|
|
21
23
|
## Key files
|
|
22
24
|
|
|
23
25
|
- [src/app.ts](src/app.ts) — the command tree and the one-line error format.
|
|
24
26
|
- [src/resolve/resolve.ts](src/resolve/resolve.ts) — imports the config in place and discovers the Cloudflare zone.
|
|
25
27
|
- [src/apply/apply.command.ts](src/apply/apply.command.ts) — lock, secrets, moves, reconcile loop, prune.
|
|
28
|
+
- [src/apply/baseline.ts](src/apply/baseline.ts) — the prune baseline record, and telling a first apply from a lost baseline.
|
|
26
29
|
- [src/lib/artifact.ts](src/lib/artifact.ts) — default paths, artifact read/write and `.env` loading.
|
|
27
30
|
- [src/pipelines/adopt-pipelines.ts](src/pipelines/adopt-pipelines.ts) — the CI workflows and repo secrets `adopt` installs.
|
|
28
31
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adopt.command.d.ts","sourceRoot":"","sources":["../../src/adopt/adopt.command.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"adopt.command.d.ts","sourceRoot":"","sources":["../../src/adopt/adopt.command.ts"],"names":[],"mappings":"AAKA,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAqBlE,eAAO,MAAM,KAAK,iDAwHhB,CAAC"}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
2
3
|
import { plural } from "@intentic/base/format";
|
|
3
|
-
import { FORGEJO_HTTP_PORT, forgejoApi, overSsh
|
|
4
|
+
import { FORGEJO_HTTP_PORT, forgejoApi, overSsh } from "@intentic/providers";
|
|
4
5
|
import { secretDigest, writeSyncState } from "@intentic/scaffold";
|
|
5
6
|
import { buildCommand } from "@stricli/core";
|
|
6
7
|
import { loadConfig } from "../env.config.js";
|
|
7
|
-
import { ARTIFACT_FILE, ARTIFACT_PATH, CONFIG_FILE, INTENT_DIR, loadEnvFile, readArtifact, TARGET_DIR } from "../lib/artifact.js";
|
|
8
|
+
import { ARTIFACT_FILE, ARTIFACT_PATH, CONFIG_FILE, INTENT_DIR, LAST_APPLIED_FILE, loadEnvFile, readArtifact, TARGET_DIR } from "../lib/artifact.js";
|
|
9
|
+
import { pinnedSshExecutor } from "../lib/known-hosts.js";
|
|
8
10
|
import { createOutput, teeOutput } from "../lib/output.js";
|
|
9
11
|
import { withRunLog } from "../lib/run-log.js";
|
|
10
12
|
import { version } from "../lib/version.js";
|
|
11
|
-
import { collectSecretValues, GIT_TOKEN_SECRET, GIT_USER_SECRET, setRepoSecrets, writeControlPlaneWorkflows, } from "../pipelines/adopt-pipelines.js";
|
|
13
|
+
import { APPLIED_TAG, collectSecretValues, GIT_TOKEN_SECRET, GIT_USER_SECRET, setRepoSecrets, writeControlPlaneWorkflows, } from "../pipelines/adopt-pipelines.js";
|
|
12
14
|
import { forgejoIdentity } from "../pipelines/control-plane-sync.js";
|
|
13
15
|
import { createEventsFileSink } from "../lib/events-file.js";
|
|
14
16
|
import { readGeneratedSecrets } from "../secrets/generated-secrets.js";
|
|
@@ -66,6 +68,7 @@ export const adopt = buildCommand({
|
|
|
66
68
|
if (desiredStateSecrets["CLOUDFLARE_API_TOKEN"] !== undefined) {
|
|
67
69
|
intentSecrets["CLOUDFLARE_API_TOKEN"] = desiredStateSecrets["CLOUDFLARE_API_TOKEN"];
|
|
68
70
|
}
|
|
71
|
+
const baselineFile = resolve(targetDir, LAST_APPLIED_FILE);
|
|
69
72
|
const run = async (baseUrl) => {
|
|
70
73
|
const repos = await adoptRepos({
|
|
71
74
|
baseUrl,
|
|
@@ -74,7 +77,11 @@ export const adopt = buildCommand({
|
|
|
74
77
|
password,
|
|
75
78
|
repos: [
|
|
76
79
|
{ dir: intentDir, name: INTENT_DIR },
|
|
77
|
-
{
|
|
80
|
+
{
|
|
81
|
+
dir: targetDir,
|
|
82
|
+
name: TARGET_DIR,
|
|
83
|
+
...(existsSync(baselineFile) ? { seedTag: { name: APPLIED_TAG, messageFile: baselineFile } } : {}),
|
|
84
|
+
},
|
|
78
85
|
],
|
|
79
86
|
log: out.log,
|
|
80
87
|
});
|
|
@@ -91,7 +98,7 @@ export const adopt = buildCommand({
|
|
|
91
98
|
if (sshKey === undefined || sshKey === "") {
|
|
92
99
|
throw new Error(`host ssh key (${ssh.sshKeyRef.source} secret ${ssh.sshKeyRef.key}) is not available`);
|
|
93
100
|
}
|
|
94
|
-
repos = await overSsh(
|
|
101
|
+
repos = await overSsh(pinnedSshExecutor(targetDir), { address: ssh.address, user: ssh.user, sshKey, port: ssh.port ?? 22, via: ssh.via ?? "direct" }, FORGEJO_HTTP_PORT, run);
|
|
95
102
|
}
|
|
96
103
|
const pushedAt = new Date().toISOString();
|
|
97
104
|
await writeSyncState(targetDir, Object.fromEntries(Object.entries(desiredStateSecrets).map(([key, value]) => [key, { digest: secretDigest(value), pushedAt }])));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adopt.command.js","sourceRoot":"","sources":["../../src/adopt/adopt.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"adopt.command.js","sourceRoot":"","sources":["../../src/adopt/adopt.command.ts"],"names":[],"mappings":"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"}
|
package/dist/adopt/adopt.d.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import type { ForgejoApi } from "@intentic/providers";
|
|
2
|
-
import { type GitRunner } from "@intentic/
|
|
2
|
+
import { type GitRunner } from "@intentic/base/git";
|
|
3
3
|
export interface AdoptRepo {
|
|
4
4
|
readonly dir: string;
|
|
5
5
|
readonly name: string;
|
|
6
|
+
readonly seedTag?: {
|
|
7
|
+
readonly name: string;
|
|
8
|
+
readonly messageFile: string;
|
|
9
|
+
};
|
|
6
10
|
}
|
|
7
11
|
export interface AdoptOptions {
|
|
8
12
|
readonly baseUrl: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adopt.d.ts","sourceRoot":"","sources":["../../src/adopt/adopt.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"adopt.d.ts","sourceRoot":"","sources":["../../src/adopt/adopt.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAA4B,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE9E,MAAM,WAAW,SAAS;IAEtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAGtB,QAAQ,CAAC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9E;AAED,MAAM,WAAW,YAAY;IAEzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,CAAC;IACrC,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,GAAG,CAAC,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC;CAC5B;AAID,eAAO,MAAM,UAAU,YAAmB,YAAY,KAAG,OAAO,CAAC;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EAAE,CAiDtH,CAAC"}
|
package/dist/adopt/adopt.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import { errorMessage } from "@intentic/base/errors";
|
|
1
2
|
import { forgejoApi } from "@intentic/providers";
|
|
2
|
-
import { defaultGit, gitCommitAll } from "@intentic/
|
|
3
|
+
import { defaultGit, gitCommitAll } from "@intentic/base/git";
|
|
3
4
|
export const adoptRepos = async (options) => {
|
|
4
5
|
const api = options.api ?? forgejoApi;
|
|
5
6
|
const git = options.git ?? defaultGit;
|
|
6
7
|
const { baseUrl, originBaseUrl, user, password, repos, log } = options;
|
|
7
8
|
const email = `${user}@${new URL(originBaseUrl).host}`;
|
|
8
9
|
const authHeader = `AUTHORIZATION: basic ${Buffer.from(`${user}:${password}`).toString("base64")}`;
|
|
9
|
-
return Promise.all(repos.map(async ({ dir, name }) => {
|
|
10
|
+
return Promise.all(repos.map(async ({ dir, name, seedTag }) => {
|
|
10
11
|
const existing = await api.findRepo({ baseUrl, user, password, owner: user, name });
|
|
11
12
|
if (existing === undefined) {
|
|
12
13
|
await api.createRepo({ baseUrl, user, password, owner: user, ownerIsOrg: false, name, private: true, autoInit: false });
|
|
@@ -16,7 +17,31 @@ export const adoptRepos = async (options) => {
|
|
|
16
17
|
const cloneUrl = `${originBaseUrl}/${user}/${name}.git`;
|
|
17
18
|
const remotes = (await git(dir, ["remote"])).stdout.split("\n").map((line) => line.trim());
|
|
18
19
|
await git(dir, remotes.includes("origin") ? ["remote", "set-url", "origin", cloneUrl] : ["remote", "add", "origin", cloneUrl]);
|
|
19
|
-
|
|
20
|
+
const pushUrl = `${baseUrl}/${user}/${name}.git`;
|
|
21
|
+
if (seedTag !== undefined) {
|
|
22
|
+
try {
|
|
23
|
+
await git(dir, [
|
|
24
|
+
"-c",
|
|
25
|
+
`user.name=${user}`,
|
|
26
|
+
"-c",
|
|
27
|
+
`user.email=${email}`,
|
|
28
|
+
"tag",
|
|
29
|
+
"-f",
|
|
30
|
+
"-a",
|
|
31
|
+
"--cleanup=verbatim",
|
|
32
|
+
"-F",
|
|
33
|
+
seedTag.messageFile,
|
|
34
|
+
seedTag.name,
|
|
35
|
+
"HEAD",
|
|
36
|
+
]);
|
|
37
|
+
await git(dir, ["-c", `http.extraHeader=${authHeader}`, "push", pushUrl, `refs/tags/${seedTag.name}`]);
|
|
38
|
+
log(`seeded ${user}/${name}'s ${seedTag.name} tag with the local prune baseline`);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
log(`kept ${user}/${name}'s ${seedTag.name} tag as it is (${errorMessage(error)})`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
await git(dir, ["-c", `http.extraHeader=${authHeader}`, "push", pushUrl, "main"]);
|
|
20
45
|
log(`pushed ${dir} → ${cloneUrl}`);
|
|
21
46
|
return { name, cloneUrl };
|
|
22
47
|
}));
|
package/dist/adopt/adopt.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adopt.js","sourceRoot":"","sources":["../../src/adopt/adopt.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"adopt.js","sourceRoot":"","sources":["../../src/adopt/adopt.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,10 +3,22 @@ export interface ApplyLock {
|
|
|
3
3
|
readonly verify: () => Promise<void>;
|
|
4
4
|
readonly renew: () => Promise<void>;
|
|
5
5
|
readonly release: () => Promise<void>;
|
|
6
|
+
readonly ttlSeconds: number;
|
|
7
|
+
}
|
|
8
|
+
export declare class LockLostError extends Error {
|
|
9
|
+
constructor(host: string);
|
|
6
10
|
}
|
|
7
11
|
export declare const acquireApplyLock: (executor: SshExecutor, targets: readonly SshTarget[], options?: {
|
|
8
12
|
readonly ttlSeconds?: number;
|
|
9
13
|
readonly holder?: string;
|
|
10
14
|
readonly log?: (message: string) => void;
|
|
11
15
|
}) => Promise<ApplyLock>;
|
|
16
|
+
export interface LockHeartbeat {
|
|
17
|
+
readonly stop: () => void;
|
|
18
|
+
}
|
|
19
|
+
export declare const startLockHeartbeat: (lock: ApplyLock, options: {
|
|
20
|
+
readonly onLost: (error: Error) => void;
|
|
21
|
+
readonly intervalMs?: number;
|
|
22
|
+
readonly log?: (message: string) => void;
|
|
23
|
+
}) => LockHeartbeat;
|
|
12
24
|
//# sourceMappingURL=apply-lock.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply-lock.d.ts","sourceRoot":"","sources":["../../src/apply/apply-lock.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAa,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAU7E,MAAM,WAAW,SAAS;IAEtB,QAAQ,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"apply-lock.d.ts","sourceRoot":"","sources":["../../src/apply/apply-lock.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAa,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAU7E,MAAM,WAAW,SAAS;IAEtB,QAAQ,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAGrC,QAAQ,CAAC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpC,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC/B;AAGD,qBAAa,aAAc,SAAQ,KAAK;IACpC,YAAY,IAAI,EAAE,MAAM,EAGvB;CACJ;AAmED,eAAO,MAAM,gBAAgB,aACf,WAAW,WACZ,SAAS,SAAS,EAAE,YACpB;IAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,KAC9G,OAAO,CAAC,SAAS,CAgEnB,CAAC;AAEF,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC;CAC7B;AAMD,eAAO,MAAM,kBAAkB,SACrB,SAAS,WACN;IACL,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5C,KACF,aAsCF,CAAC"}
|
package/dist/apply/apply-lock.js
CHANGED
|
@@ -4,6 +4,12 @@ import { HOST_STATE_ROOT } from "@intentic/constants";
|
|
|
4
4
|
import { shellQuote } from "@intentic/sandbox-run/quote";
|
|
5
5
|
const LOCK_DIR = `${HOST_STATE_ROOT}/apply.lock.d`;
|
|
6
6
|
const DEFAULT_TTL_SECONDS = 30 * 60;
|
|
7
|
+
export class LockLostError extends Error {
|
|
8
|
+
constructor(host) {
|
|
9
|
+
super(`apply lock on ${host} was taken over by another run`);
|
|
10
|
+
this.name = "LockLostError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
7
13
|
const lockKey = (target) => `${target.address}:${target.port}`;
|
|
8
14
|
const orderedHosts = (targets) => {
|
|
9
15
|
const byKey = new Map();
|
|
@@ -95,10 +101,53 @@ export const acquireApplyLock = async (executor, targets, options = {}) => {
|
|
|
95
101
|
},
|
|
96
102
|
renew: async () => {
|
|
97
103
|
for (const target of held) {
|
|
98
|
-
await run(executor, target, renewScript(holder, nonce, ttl));
|
|
104
|
+
const outcome = (await run(executor, target, renewScript(holder, nonce, ttl))).stdout.trim();
|
|
105
|
+
if (!outcome.startsWith("OK")) {
|
|
106
|
+
throw new LockLostError(lockKey(target));
|
|
107
|
+
}
|
|
99
108
|
}
|
|
100
109
|
},
|
|
101
110
|
release: releaseAll,
|
|
111
|
+
ttlSeconds: ttl,
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
export const startLockHeartbeat = (lock, options) => {
|
|
115
|
+
const log = options.log ?? (() => { });
|
|
116
|
+
const intervalMs = options.intervalMs ?? (lock.ttlSeconds * 1000) / 3;
|
|
117
|
+
let failures = 0;
|
|
118
|
+
let busy = false;
|
|
119
|
+
let stopped = false;
|
|
120
|
+
const timer = setInterval(() => {
|
|
121
|
+
if (busy || stopped) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
busy = true;
|
|
125
|
+
void lock
|
|
126
|
+
.renew()
|
|
127
|
+
.then(() => {
|
|
128
|
+
failures = 0;
|
|
129
|
+
})
|
|
130
|
+
.catch((error) => {
|
|
131
|
+
failures += 1;
|
|
132
|
+
const lost = error instanceof LockLostError;
|
|
133
|
+
if (lost || failures >= 2) {
|
|
134
|
+
stopped = true;
|
|
135
|
+
clearInterval(timer);
|
|
136
|
+
options.onLost(lost ? error : new Error(`could not renew the apply lock twice in a row: ${String(error)}`, { cause: error }));
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
log(`apply-lock: renewal failed, retrying at the next beat: ${String(error)}`);
|
|
140
|
+
})
|
|
141
|
+
.finally(() => {
|
|
142
|
+
busy = false;
|
|
143
|
+
});
|
|
144
|
+
}, intervalMs);
|
|
145
|
+
timer.unref();
|
|
146
|
+
return {
|
|
147
|
+
stop: () => {
|
|
148
|
+
stopped = true;
|
|
149
|
+
clearInterval(timer);
|
|
150
|
+
},
|
|
102
151
|
};
|
|
103
152
|
};
|
|
104
153
|
//# sourceMappingURL=apply-lock.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply-lock.js","sourceRoot":"","sources":["../../src/apply/apply-lock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAKzD,MAAM,QAAQ,GAAG,GAAG,eAAe,eAAe,CAAC;AAEnD,MAAM,mBAAmB,GAAG,EAAE,GAAG,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"apply-lock.js","sourceRoot":"","sources":["../../src/apply/apply-lock.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply.command.d.ts","sourceRoot":"","sources":["../../src/apply/apply.command.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"apply.command.d.ts","sourceRoot":"","sources":["../../src/apply/apply.command.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AA8ClE,eAAO,MAAM,KAAK,iDAoThB,CAAC"}
|
|
@@ -1,26 +1,32 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { readFile, writeFile } from "node:fs/promises";
|
|
3
1
|
import { tmpdir } from "node:os";
|
|
4
2
|
import { dirname, join } from "node:path";
|
|
5
3
|
import { plural } from "@intentic/base/format";
|
|
6
|
-
import { applyMoves, collectOrphans, createStore,
|
|
7
|
-
import { connectWithRetry, createProviders,
|
|
8
|
-
import { buildCommand
|
|
4
|
+
import { applyMoves, collectOrphans, createStore, ReadinessTimeoutError, reconcile, resolveInputs, rewriteGraphForMoves, } from "@intentic/engine";
|
|
5
|
+
import { connectWithRetry, createProviders, createSshProbe, hostTarget, readinessDiagnostics, } from "@intentic/providers";
|
|
6
|
+
import { buildCommand } from "@stricli/core";
|
|
9
7
|
import { collectSecretUsage, subgraph } from "@intentic/graph";
|
|
10
8
|
import { loadConfig } from "../env.config.js";
|
|
11
9
|
import { ACCESS_FILE, ARTIFACT_PATH, LAST_APPLIED_FILE, loadEnvFile, readArtifact, STATUS_FILE, writeStatus } from "../lib/artifact.js";
|
|
12
|
-
import {
|
|
10
|
+
import { countAtLeast } from "../lib/flags.js";
|
|
11
|
+
import { pinnedSshExecutor } from "../lib/known-hosts.js";
|
|
13
12
|
import { createOutput, createRedactor, teeOutput } from "../lib/output.js";
|
|
14
13
|
import { withRunLog } from "../lib/run-log.js";
|
|
15
|
-
import { resourceTable } from "../lib/tables.js";
|
|
16
|
-
import { ensureGeneratedSecrets } from "../secrets/generated-secrets.js";
|
|
14
|
+
import { pendingBlock, resourceTable, retiredBlock, scanGapBlock, unownedBlock } from "../lib/tables.js";
|
|
15
|
+
import { ensureGeneratedSecrets, loadStoredSecrets } from "../secrets/generated-secrets.js";
|
|
17
16
|
import { generatedSecretStore } from "../secrets/secret-store.js";
|
|
18
17
|
import { collectSecrets } from "../secrets/secrets.js";
|
|
19
18
|
import { collectAccess, formatAccessSummary, writeAccessFile } from "./access.js";
|
|
20
19
|
import { createEventsFileSink } from "../lib/events-file.js";
|
|
21
|
-
import { acquireApplyLock } from "./apply-lock.js";
|
|
20
|
+
import { acquireApplyLock, startLockHeartbeat } from "./apply-lock.js";
|
|
21
|
+
import { checkFirstApply, loadBaseline, mergeRetiredHosts, nextBaseline, pruneBase, removedNodes, writeBaseline, } from "./baseline.js";
|
|
22
22
|
import { detectHostMoves, migrateHosts } from "./migrate.js";
|
|
23
|
+
import { runPrunePhase } from "./prune-phase.js";
|
|
24
|
+
import { retiredTarget, scanRetiredHosts } from "./retired-hosts.js";
|
|
23
25
|
const DEFAULT_MAX_ITERATIONS = 5;
|
|
26
|
+
const trackedGraph = (base, retired) => ({
|
|
27
|
+
version: 1,
|
|
28
|
+
resources: { ...base?.resources, ...Object.fromEntries(retired.map((host) => [`retired:${host.id}@${host.address}`, host.node])) },
|
|
29
|
+
});
|
|
24
30
|
export const apply = buildCommand({
|
|
25
31
|
docs: { brief: "Execute the desired-state artifact until state reads true" },
|
|
26
32
|
parameters: {
|
|
@@ -28,7 +34,7 @@ export const apply = buildCommand({
|
|
|
28
34
|
artifact: { kind: "parsed", parse: String, optional: true, brief: `Path to the artifact (default: ${ARTIFACT_PATH})` },
|
|
29
35
|
maxIterations: {
|
|
30
36
|
kind: "parsed",
|
|
31
|
-
parse:
|
|
37
|
+
parse: countAtLeast(1),
|
|
32
38
|
optional: true,
|
|
33
39
|
brief: `Max reconcile iterations (default ${DEFAULT_MAX_ITERATIONS})`,
|
|
34
40
|
},
|
|
@@ -36,11 +42,11 @@ export const apply = buildCommand({
|
|
|
36
42
|
kind: "parsed",
|
|
37
43
|
parse: String,
|
|
38
44
|
optional: true,
|
|
39
|
-
brief:
|
|
45
|
+
brief: `Path to the prune baseline (default: ${LAST_APPLIED_FILE} beside the artifact); resources it holds and the new artifact lacks are pruned after convergence`,
|
|
40
46
|
},
|
|
41
47
|
yes: {
|
|
42
48
|
kind: "boolean",
|
|
43
|
-
brief: "Confirm deletions: without it, pending prunes are listed and left in place (converge still runs)",
|
|
49
|
+
brief: "Confirm deletions: without it, pending prunes are listed and kept in the baseline, left in place (converge still runs)",
|
|
44
50
|
},
|
|
45
51
|
target: {
|
|
46
52
|
kind: "parsed",
|
|
@@ -48,6 +54,10 @@ export const apply = buildCommand({
|
|
|
48
54
|
optional: true,
|
|
49
55
|
brief: "Comma-separated resource ids: reconcile only these and their dependencies (prune and orphan scan are skipped)",
|
|
50
56
|
},
|
|
57
|
+
firstApply: {
|
|
58
|
+
kind: "boolean",
|
|
59
|
+
brief: "Start a new prune baseline when none can be read, even though resources of this intent are live (what a lost baseline tracked is then removed by hand)",
|
|
60
|
+
},
|
|
51
61
|
},
|
|
52
62
|
},
|
|
53
63
|
async func(flags) {
|
|
@@ -65,10 +75,20 @@ export const apply = buildCommand({
|
|
|
65
75
|
.map((id) => id.trim())
|
|
66
76
|
.filter((id) => id !== "");
|
|
67
77
|
const graph = targetIds === undefined ? full : subgraph(full, targetIds);
|
|
68
|
-
const ssh =
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
const
|
|
78
|
+
const ssh = pinnedSshExecutor(dir);
|
|
79
|
+
const baselinePath = flags.previous ?? join(dir, LAST_APPLIED_FILE);
|
|
80
|
+
const lastApplied = join(dir, LAST_APPLIED_FILE);
|
|
81
|
+
const previous = await loadBaseline(baselinePath, { explicit: flags.previous !== undefined, firstApply: flags.firstApply, note: out.text });
|
|
82
|
+
const owner = full.owner ?? previous?.owner;
|
|
83
|
+
if (full.owner !== undefined && previous?.owner !== undefined && full.owner !== previous.owner) {
|
|
84
|
+
throw new Error(`the artifact's owner id (${full.owner}) is not the one this intent was applied with (${previous.owner}): every resource it stamped would read as another intent's. Put "owner": "${previous.owner}" back in ${artifact} (resolve keeps whatever is there).`);
|
|
85
|
+
}
|
|
86
|
+
if (full.owner === undefined) {
|
|
87
|
+
out.text("This artifact carries no owner id (it was resolved before owner stamps): nothing is stamped with an owner and no orphan is pruned. Re-run `intentic deploy resolve` to mint one.");
|
|
88
|
+
}
|
|
89
|
+
const hostMoves = previous !== undefined ? detectHostMoves({ version: 1, resources: previous.resources }, graph) : [];
|
|
90
|
+
const sinceUtcDay = new Date().toISOString().slice(0, 10);
|
|
91
|
+
const retiredHosts = mergeRetiredHosts(previous?.retiredHosts ?? [], hostMoves.map((move) => ({ id: move.id, address: move.oldAddress, node: move.oldNode, since: sinceUtcDay })), full);
|
|
72
92
|
const targets = Object.values(graph.resources)
|
|
73
93
|
.filter((node) => node.type === "host")
|
|
74
94
|
.map((node) => hostTarget(resolveInputs(node.inputs, createStore(), process.env, { lenient: false })));
|
|
@@ -88,7 +108,18 @@ export const apply = buildCommand({
|
|
|
88
108
|
await session.dispose();
|
|
89
109
|
}));
|
|
90
110
|
const oldMoveTargets = hostMoves.map((move) => hostTarget(resolveInputs(move.oldNode.inputs, createStore(), process.env, { lenient: false })));
|
|
91
|
-
const
|
|
111
|
+
const retiredTargets = retiredHosts
|
|
112
|
+
.map((host) => retiredTarget(host, process.env))
|
|
113
|
+
.filter((target) => typeof target !== "string");
|
|
114
|
+
const lock = await acquireApplyLock(ssh, [...targets, ...oldMoveTargets, ...retiredTargets], { log: out.log });
|
|
115
|
+
const stop = new AbortController();
|
|
116
|
+
const heartbeat = startLockHeartbeat(lock, {
|
|
117
|
+
log: out.log,
|
|
118
|
+
onLost: (error) => {
|
|
119
|
+
out.log(`apply-lock: ${error.message}; stopping before the next change`);
|
|
120
|
+
stop.abort(error);
|
|
121
|
+
},
|
|
122
|
+
});
|
|
92
123
|
const onSignal = () => {
|
|
93
124
|
void Promise.allSettled([lock.release(), ssh.dispose?.()]).finally(() => process.exit(130));
|
|
94
125
|
};
|
|
@@ -97,20 +128,43 @@ export const apply = buildCommand({
|
|
|
97
128
|
process.once("SIGHUP", onSignal);
|
|
98
129
|
const signals = process;
|
|
99
130
|
try {
|
|
100
|
-
|
|
101
|
-
redactor.add(collectSecretUsage(graph).map((usage) => process.env[usage.key]));
|
|
102
|
-
if (hostMoves.length > 0) {
|
|
103
|
-
await migrateHosts(hostMoves, { next: graph, ssh, env: process.env, tmpDir: tmpdir(), log: out.log });
|
|
104
|
-
}
|
|
105
|
-
const movedApplied = await applyMoves(graph, {
|
|
131
|
+
const engine = () => ({
|
|
106
132
|
providers: createProviders({ ssh }),
|
|
107
133
|
log: out.log,
|
|
108
134
|
onEvent: out.onEvent,
|
|
109
135
|
env: process.env,
|
|
136
|
+
signal: stop.signal,
|
|
137
|
+
...(owner !== undefined ? { owner } : {}),
|
|
110
138
|
});
|
|
139
|
+
const store = generatedSecretStore(graph, dir, ssh, true, out.log);
|
|
140
|
+
await ensureGeneratedSecrets(store, collectSecrets(graph).generated, process.env);
|
|
141
|
+
const tracked = trackedGraph(previous !== undefined ? pruneBase(previous) : undefined, retiredHosts);
|
|
142
|
+
const current = new Set(collectSecrets(graph).generated);
|
|
143
|
+
await loadStoredSecrets(store, collectSecrets(tracked).generated.filter((key) => !current.has(key)), process.env, out.log);
|
|
144
|
+
redactor.add([...collectSecretUsage(graph), ...collectSecretUsage(tracked)].map((usage) => process.env[usage.key]));
|
|
145
|
+
if (previous === undefined && targetIds === undefined) {
|
|
146
|
+
const line = await checkFirstApply({
|
|
147
|
+
path: baselinePath,
|
|
148
|
+
owner,
|
|
149
|
+
firstApply: flags.firstApply,
|
|
150
|
+
scan: () => collectOrphans(graph, { ...engine(), onEvent: () => { }, log: () => { } }),
|
|
151
|
+
});
|
|
152
|
+
out.text(line);
|
|
153
|
+
}
|
|
154
|
+
if (hostMoves.length > 0) {
|
|
155
|
+
stop.signal.throwIfAborted();
|
|
156
|
+
await migrateHosts(hostMoves, { next: graph, ssh, env: process.env, tmpDir: tmpdir(), log: out.log });
|
|
157
|
+
}
|
|
158
|
+
const movedApplied = await applyMoves(graph, engine());
|
|
159
|
+
const base = previous !== undefined ? rewriteGraphForMoves(pruneBase(previous), movedApplied) : undefined;
|
|
160
|
+
if (targetIds === undefined) {
|
|
161
|
+
await writeBaseline(lastApplied, nextBaseline({ graph, owner, pending: base === undefined ? [] : removedNodes(base, graph), retiredHosts }));
|
|
162
|
+
}
|
|
111
163
|
let result;
|
|
112
164
|
try {
|
|
113
|
-
result = await reconcile(graph, {
|
|
165
|
+
result = await reconcile(graph, { ...engine(), ...(probe !== undefined ? { probe } : {}) }, {
|
|
166
|
+
maxIterations: flags.maxIterations ?? DEFAULT_MAX_ITERATIONS,
|
|
167
|
+
});
|
|
114
168
|
}
|
|
115
169
|
catch (error) {
|
|
116
170
|
if (error instanceof ReadinessTimeoutError && targets.length > 0) {
|
|
@@ -125,41 +179,63 @@ export const apply = buildCommand({
|
|
|
125
179
|
steps: result.outcome.steps,
|
|
126
180
|
access,
|
|
127
181
|
});
|
|
182
|
+
let report = {};
|
|
128
183
|
let pruned = { deleted: [], skipped: [] };
|
|
129
184
|
if (targetIds !== undefined) {
|
|
130
185
|
out.text("A targeted apply reconciles only the named resources, so prune and the orphan scan were skipped.");
|
|
131
186
|
}
|
|
132
187
|
else {
|
|
133
|
-
const
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
188
|
+
const retired = await scanRetiredHosts(retiredHosts, { ssh, env: process.env, owner, providers: createProviders({ ssh }) });
|
|
189
|
+
const phase = await runPrunePhase({
|
|
190
|
+
graph,
|
|
191
|
+
base,
|
|
192
|
+
owner,
|
|
193
|
+
retired,
|
|
194
|
+
yes: flags.yes,
|
|
195
|
+
config: engine(),
|
|
196
|
+
beforeDelete: async () => {
|
|
197
|
+
await lock.renew();
|
|
198
|
+
await lock.verify();
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
pruned = phase.pruned;
|
|
202
|
+
const retiredReports = retired.map((scan) => ({
|
|
203
|
+
id: scan.host.id,
|
|
204
|
+
address: scan.host.address,
|
|
205
|
+
leftovers: scan.leftovers.map(({ id, type }) => ({ id, type })),
|
|
206
|
+
unowned: scan.unowned,
|
|
207
|
+
...(scan.error !== undefined ? { error: scan.error } : {}),
|
|
208
|
+
}));
|
|
209
|
+
for (const line of [...unownedBlock(phase.scan.unowned), ...retiredBlock(retiredReports), ...scanGapBlock(phase.scan.skipped)]) {
|
|
210
|
+
out.text(line);
|
|
211
|
+
}
|
|
212
|
+
if (phase.scan.foreign > 0) {
|
|
213
|
+
out.text(`${plural(phase.scan.foreign, "stamped resource")} on shared hosts or zones belong to other intents; left alone.`);
|
|
214
|
+
}
|
|
215
|
+
if (phase.pending.length > 0) {
|
|
142
216
|
out.text("");
|
|
143
|
-
for (const line of resourceTable("delete", pending)) {
|
|
217
|
+
for (const line of resourceTable("delete", phase.pending)) {
|
|
144
218
|
out.text(line);
|
|
145
219
|
}
|
|
146
|
-
out.text(`${plural(pending.length, "deletion")} pending, nothing was deleted. Re-run \`intentic deploy apply --yes\` to prune.`);
|
|
220
|
+
out.text(`${plural(phase.pending.length, "deletion")} pending, nothing was deleted; the baseline keeps them. Re-run \`intentic deploy apply --yes\` to prune.`);
|
|
147
221
|
}
|
|
148
|
-
else {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
}
|
|
156
|
-
const orphaned = await pruneOrphans(orphans, pruneConfig);
|
|
157
|
-
pruned = { deleted: [...pruned.deleted, ...orphaned.deleted], skipped: [...pruned.skipped, ...orphaned.skipped] };
|
|
158
|
-
if (pruned.deleted.length > 0 || pruned.skipped.length > 0) {
|
|
159
|
-
out.text(`pruned ${plural(pruned.deleted.length, "resource")}${pruned.skipped.length > 0 ? `, ${pruned.skipped.length} left in place` : ""}`);
|
|
222
|
+
else if (phase.pruned.deleted.length > 0 || phase.pruned.skipped.length > 0) {
|
|
223
|
+
out.text(`pruned ${plural(phase.pruned.deleted.length, "resource")}${phase.pruned.skipped.length > 0 ? `, ${phase.pruned.skipped.length} left in place` : ""}`);
|
|
224
|
+
}
|
|
225
|
+
const stillPending = Object.values(phase.baseline.pendingDeletion ?? {});
|
|
226
|
+
if (phase.pending.length === 0) {
|
|
227
|
+
for (const line of pendingBlock(stillPending)) {
|
|
228
|
+
out.text(line);
|
|
160
229
|
}
|
|
161
|
-
await writeFile(join(dir, LAST_APPLIED_FILE), await readFile(artifact, "utf8"));
|
|
162
230
|
}
|
|
231
|
+
stop.signal.throwIfAborted();
|
|
232
|
+
await writeBaseline(lastApplied, phase.baseline);
|
|
233
|
+
report = {
|
|
234
|
+
unowned: phase.scan.unowned,
|
|
235
|
+
pendingDeletion: phase.pending.length > 0 ? phase.pending : stillPending.map(({ id, type }) => ({ id, type })),
|
|
236
|
+
retiredHosts: retiredReports,
|
|
237
|
+
scanSkipped: phase.scan.skipped,
|
|
238
|
+
};
|
|
163
239
|
}
|
|
164
240
|
out.text(`${result.converged ? "converged" : "did not converge"} in ${plural(result.iterations, "iteration")}`);
|
|
165
241
|
if (access.length > 0) {
|
|
@@ -173,6 +249,7 @@ export const apply = buildCommand({
|
|
|
173
249
|
outputs: result.outcome.outputs,
|
|
174
250
|
pruned,
|
|
175
251
|
access,
|
|
252
|
+
...report,
|
|
176
253
|
});
|
|
177
254
|
const reconcileWebhook = result.outcome.outputs["discord"]?.["reconcileWebhook"];
|
|
178
255
|
if (typeof reconcileWebhook === "string" && reconcileWebhook !== "") {
|
|
@@ -198,6 +275,7 @@ export const apply = buildCommand({
|
|
|
198
275
|
}
|
|
199
276
|
}
|
|
200
277
|
finally {
|
|
278
|
+
heartbeat.stop();
|
|
201
279
|
signals.removeListener("SIGINT", onSignal);
|
|
202
280
|
signals.removeListener("SIGTERM", onSignal);
|
|
203
281
|
signals.removeListener("SIGHUP", onSignal);
|