@intentic/cli 1.0.2
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/LICENSE +21 -0
- package/README.md +80 -0
- package/dist/add-app/add-app.command.d.ts +3 -0
- package/dist/add-app/add-app.command.d.ts.map +1 -0
- package/dist/add-app/add-app.command.js +50 -0
- package/dist/add-app/add-app.command.js.map +1 -0
- package/dist/adopt/adopt.command.d.ts +3 -0
- package/dist/adopt/adopt.command.d.ts.map +1 -0
- package/dist/adopt/adopt.command.js +77 -0
- package/dist/adopt/adopt.command.js.map +1 -0
- package/dist/adopt/adopt.d.ts +21 -0
- package/dist/adopt/adopt.d.ts.map +1 -0
- package/dist/adopt/adopt.js +31 -0
- package/dist/adopt/adopt.js.map +1 -0
- package/dist/app.d.ts +2 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +46 -0
- package/dist/app.js.map +1 -0
- package/dist/apply/access.d.ts +16 -0
- package/dist/apply/access.d.ts.map +1 -0
- package/dist/apply/access.js +72 -0
- package/dist/apply/access.js.map +1 -0
- package/dist/apply/apply-lock.d.ts +12 -0
- package/dist/apply/apply-lock.d.ts.map +1 -0
- package/dist/apply/apply-lock.js +102 -0
- package/dist/apply/apply-lock.js.map +1 -0
- package/dist/apply/apply.command.d.ts +3 -0
- package/dist/apply/apply.command.d.ts.map +1 -0
- package/dist/apply/apply.command.js +144 -0
- package/dist/apply/apply.command.js.map +1 -0
- package/dist/apply/migrate.d.ts +20 -0
- package/dist/apply/migrate.d.ts.map +1 -0
- package/dist/apply/migrate.js +112 -0
- package/dist/apply/migrate.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +5 -0
- package/dist/cli.js.map +1 -0
- package/dist/demo.d.ts +2 -0
- package/dist/demo.d.ts.map +1 -0
- package/dist/demo.js +281 -0
- package/dist/demo.js.map +1 -0
- package/dist/deployments/deployments.command.d.ts +3 -0
- package/dist/deployments/deployments.command.d.ts.map +1 -0
- package/dist/deployments/deployments.command.js +19 -0
- package/dist/deployments/deployments.command.js.map +1 -0
- package/dist/deployments/deployments.d.ts +14 -0
- package/dist/deployments/deployments.d.ts.map +1 -0
- package/dist/deployments/deployments.js +83 -0
- package/dist/deployments/deployments.js.map +1 -0
- package/dist/env.config.d.ts +25 -0
- package/dist/env.config.d.ts.map +1 -0
- package/dist/env.config.js +31 -0
- package/dist/env.config.js.map +1 -0
- package/dist/host-ssh-tunnel/host-ssh-tunnel.command.d.ts +3 -0
- package/dist/host-ssh-tunnel/host-ssh-tunnel.command.d.ts.map +1 -0
- package/dist/host-ssh-tunnel/host-ssh-tunnel.command.js +39 -0
- package/dist/host-ssh-tunnel/host-ssh-tunnel.command.js.map +1 -0
- package/dist/host-ssh-tunnel/host-ssh-tunnel.d.ts +14 -0
- package/dist/host-ssh-tunnel/host-ssh-tunnel.d.ts.map +1 -0
- package/dist/host-ssh-tunnel/host-ssh-tunnel.js +21 -0
- package/dist/host-ssh-tunnel/host-ssh-tunnel.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/init/init.command.d.ts +3 -0
- package/dist/init/init.command.d.ts.map +1 -0
- package/dist/init/init.command.js +45 -0
- package/dist/init/init.command.js.map +1 -0
- package/dist/init/init.d.ts +7 -0
- package/dist/init/init.d.ts.map +1 -0
- package/dist/init/init.js +39 -0
- package/dist/init/init.js.map +1 -0
- package/dist/init/scaffold-app.d.ts +2 -0
- package/dist/init/scaffold-app.d.ts.map +1 -0
- package/dist/init/scaffold-app.js +27 -0
- package/dist/init/scaffold-app.js.map +1 -0
- package/dist/lib/artifact.d.ts +10 -0
- package/dist/lib/artifact.d.ts.map +1 -0
- package/dist/lib/artifact.js +29 -0
- package/dist/lib/artifact.js.map +1 -0
- package/dist/lib/cf-tunnel.d.ts +11 -0
- package/dist/lib/cf-tunnel.d.ts.map +1 -0
- package/dist/lib/cf-tunnel.js +30 -0
- package/dist/lib/cf-tunnel.js.map +1 -0
- package/dist/lib/known-hosts.d.ts +3 -0
- package/dist/lib/known-hosts.d.ts.map +1 -0
- package/dist/lib/known-hosts.js +18 -0
- package/dist/lib/known-hosts.js.map +1 -0
- package/dist/lib/output.d.ts +14 -0
- package/dist/lib/output.d.ts.map +1 -0
- package/dist/lib/output.js +56 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/lib/run-log.d.ts +3 -0
- package/dist/lib/run-log.d.ts.map +1 -0
- package/dist/lib/run-log.js +62 -0
- package/dist/lib/run-log.js.map +1 -0
- package/dist/lib/templates.d.ts +2 -0
- package/dist/lib/templates.d.ts.map +1 -0
- package/dist/lib/templates.js +6 -0
- package/dist/lib/templates.js.map +1 -0
- package/dist/lib/version.d.ts +2 -0
- package/dist/lib/version.d.ts.map +1 -0
- package/dist/lib/version.js +3 -0
- package/dist/lib/version.js.map +1 -0
- package/dist/logs/logs.command.d.ts +3 -0
- package/dist/logs/logs.command.d.ts.map +1 -0
- package/dist/logs/logs.command.js +86 -0
- package/dist/logs/logs.command.js.map +1 -0
- package/dist/monorepo/monorepo.command.d.ts +3 -0
- package/dist/monorepo/monorepo.command.d.ts.map +1 -0
- package/dist/monorepo/monorepo.command.js +28 -0
- package/dist/monorepo/monorepo.command.js.map +1 -0
- package/dist/pipelines/adopt-pipelines.d.ts +35 -0
- package/dist/pipelines/adopt-pipelines.d.ts.map +1 -0
- package/dist/pipelines/adopt-pipelines.js +77 -0
- package/dist/pipelines/adopt-pipelines.js.map +1 -0
- package/dist/pipelines/control-plane-sync.d.ts +24 -0
- package/dist/pipelines/control-plane-sync.d.ts.map +1 -0
- package/dist/pipelines/control-plane-sync.js +62 -0
- package/dist/pipelines/control-plane-sync.js.map +1 -0
- package/dist/plan/plan.command.d.ts +3 -0
- package/dist/plan/plan.command.d.ts.map +1 -0
- package/dist/plan/plan.command.js +33 -0
- package/dist/plan/plan.command.js.map +1 -0
- package/dist/resolve/resolve.command.d.ts +3 -0
- package/dist/resolve/resolve.command.d.ts.map +1 -0
- package/dist/resolve/resolve.command.js +78 -0
- package/dist/resolve/resolve.command.js.map +1 -0
- package/dist/resolve/resolve.d.ts +4 -0
- package/dist/resolve/resolve.d.ts.map +1 -0
- package/dist/resolve/resolve.js +33 -0
- package/dist/resolve/resolve.js.map +1 -0
- package/dist/restore/restore.command.d.ts +3 -0
- package/dist/restore/restore.command.d.ts.map +1 -0
- package/dist/restore/restore.command.js +68 -0
- package/dist/restore/restore.command.js.map +1 -0
- package/dist/sandbox-tunnel/sandbox-tunnel.command.d.ts +3 -0
- package/dist/sandbox-tunnel/sandbox-tunnel.command.d.ts.map +1 -0
- package/dist/sandbox-tunnel/sandbox-tunnel.command.js +62 -0
- package/dist/sandbox-tunnel/sandbox-tunnel.command.js.map +1 -0
- package/dist/sandbox-tunnel/sandbox-tunnel.d.ts +18 -0
- package/dist/sandbox-tunnel/sandbox-tunnel.d.ts.map +1 -0
- package/dist/sandbox-tunnel/sandbox-tunnel.js +37 -0
- package/dist/sandbox-tunnel/sandbox-tunnel.js.map +1 -0
- package/dist/secrets/generated-secrets.d.ts +6 -0
- package/dist/secrets/generated-secrets.d.ts.map +1 -0
- package/dist/secrets/generated-secrets.js +26 -0
- package/dist/secrets/generated-secrets.js.map +1 -0
- package/dist/secrets/secret-store.d.ts +14 -0
- package/dist/secrets/secret-store.d.ts.map +1 -0
- package/dist/secrets/secret-store.js +111 -0
- package/dist/secrets/secret-store.js.map +1 -0
- package/dist/secrets/secrets.command.d.ts +6 -0
- package/dist/secrets/secrets.command.d.ts.map +1 -0
- package/dist/secrets/secrets.command.js +106 -0
- package/dist/secrets/secrets.command.js.map +1 -0
- package/dist/secrets/secrets.d.ts +7 -0
- package/dist/secrets/secrets.d.ts.map +1 -0
- package/dist/secrets/secrets.js +14 -0
- package/dist/secrets/secrets.js.map +1 -0
- package/package.json +69 -0
- package/templates/access.md.eta +21 -0
- package/templates/demo/Dockerfile.eta +5 -0
- package/templates/demo/deploy.config.ts.eta +23 -0
- package/templates/demo/env.eta +2 -0
- package/templates/env-example.eta +6 -0
- package/templates/scaffold/Dockerfile.eta +7 -0
- package/templates/scaffold/deploy.config.selfhost.ts.eta +18 -0
- package/templates/scaffold/deploy.config.ts.eta +22 -0
- package/templates/scaffold/server.js.eta +9 -0
- package/templates/workflows/apply.yaml.eta +28 -0
- package/templates/workflows/resolve.yaml.eta +26 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { createStore, resolveInputs } from "@intentic/engine";
|
|
5
|
+
import { hostTarget } from "@intentic/providers";
|
|
6
|
+
import { SECRETS_FILE } from "../lib/artifact.js";
|
|
7
|
+
export const createLocalSecretStore = (dir) => {
|
|
8
|
+
const path = join(dir, SECRETS_FILE);
|
|
9
|
+
const read = async () => existsSync(path) ? JSON.parse(await readFile(path, "utf8")) : {};
|
|
10
|
+
return {
|
|
11
|
+
get: async (key) => (await read())[key],
|
|
12
|
+
set: async (key, value) => {
|
|
13
|
+
const store = await read();
|
|
14
|
+
store[key] = value;
|
|
15
|
+
await writeFile(path, `${JSON.stringify(store, undefined, 4)}\n`, { mode: 0o600 });
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
const HOST_SECRETS_PATH = "/opt/intentic/secrets.json";
|
|
20
|
+
export const createHostSecretStore = (target, executor) => {
|
|
21
|
+
let cache;
|
|
22
|
+
const load = async () => {
|
|
23
|
+
if (cache !== undefined) {
|
|
24
|
+
return cache;
|
|
25
|
+
}
|
|
26
|
+
const session = await executor.connect(target);
|
|
27
|
+
try {
|
|
28
|
+
const result = await session.exec(`cat ${HOST_SECRETS_PATH} 2>/dev/null || echo '{}'`);
|
|
29
|
+
cache = JSON.parse(result.stdout.trim() || "{}");
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
await session.dispose();
|
|
33
|
+
}
|
|
34
|
+
return cache;
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
get: async (key) => (await load())[key],
|
|
38
|
+
set: async (key, value) => {
|
|
39
|
+
const store = await load();
|
|
40
|
+
store[key] = value;
|
|
41
|
+
const json = JSON.stringify(store, undefined, 4);
|
|
42
|
+
const session = await executor.connect(target);
|
|
43
|
+
try {
|
|
44
|
+
await session.exec(`mkdir -p /opt/intentic && cat > ${HOST_SECRETS_PATH}.tmp <<'INTENTIC_SECRETS_EOF'\n${json}\nINTENTIC_SECRETS_EOF\nchmod 600 ${HOST_SECRETS_PATH}.tmp && mv ${HOST_SECRETS_PATH}.tmp ${HOST_SECRETS_PATH}`);
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
await session.dispose();
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
export const createLayeredSecretStore = (layers, options) => {
|
|
53
|
+
const log = options.log ?? (() => { });
|
|
54
|
+
return {
|
|
55
|
+
get: async (key) => {
|
|
56
|
+
const values = [];
|
|
57
|
+
for (const layer of layers) {
|
|
58
|
+
try {
|
|
59
|
+
values.push(await layer.get(key));
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
log(`secret-store: a layer's read of "${key}" failed, skipping it: ${String(error)}`);
|
|
63
|
+
values.push(undefined);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const result = values.find((value) => value !== undefined);
|
|
67
|
+
if (result !== undefined && options.backfill) {
|
|
68
|
+
for (const [i, layer] of layers.entries()) {
|
|
69
|
+
if (values[i] === result) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
await layer.set(key, result);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
log(`secret-store: failed to backfill "${key}" into a layer: ${String(error)}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
},
|
|
82
|
+
set: async (key, value) => {
|
|
83
|
+
let persisted = false;
|
|
84
|
+
for (const layer of layers) {
|
|
85
|
+
try {
|
|
86
|
+
await layer.set(key, value);
|
|
87
|
+
persisted = true;
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
log(`secret-store: a layer's write of "${key}" failed: ${String(error)}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (!persisted) {
|
|
94
|
+
throw new Error(`failed to persist generated secret "${key}" to any store`);
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
export const generatedSecretStore = (graph, dir, ssh, backfill, log) => {
|
|
100
|
+
const local = createLocalSecretStore(dir);
|
|
101
|
+
const forgejo = Object.values(graph.resources).find((node) => node.type === "forgejo");
|
|
102
|
+
const serverRef = forgejo?.inputs["server"];
|
|
103
|
+
const hostId = typeof serverRef === "object" && serverRef !== null && "$ref" in serverRef ? serverRef.$ref : undefined;
|
|
104
|
+
const hostNode = hostId !== undefined ? graph.resources[hostId] : undefined;
|
|
105
|
+
if (hostNode === undefined) {
|
|
106
|
+
return local;
|
|
107
|
+
}
|
|
108
|
+
const target = hostTarget(resolveInputs(hostNode.inputs, createStore(), process.env, { lenient: false }));
|
|
109
|
+
return createLayeredSecretStore([createHostSecretStore(target, ssh), local], { backfill, log });
|
|
110
|
+
};
|
|
111
|
+
//# sourceMappingURL=secret-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret-store.js","sourceRoot":"","sources":["../../src/secrets/secret-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type ForgejoApi } from "@intentic/providers";
|
|
2
|
+
import { type CommandContext } from "@stricli/core";
|
|
3
|
+
import { type Output } from "../lib/output.js";
|
|
4
|
+
export declare const pushSecrets: (out: Output, artifact: string, api?: ForgejoApi) => Promise<void>;
|
|
5
|
+
export declare const secretsCommand: import("@stricli/core").RouteMap<CommandContext>;
|
|
6
|
+
//# sourceMappingURL=secrets.command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.command.d.ts","sourceRoot":"","sources":["../../src/secrets/secrets.command.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,UAAU,EAAc,MAAM,qBAAqB,CAAC;AAGlE,OAAO,EAA+B,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAGjF,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AA+B7D,eAAO,MAAM,WAAW,GAAU,KAAK,MAAM,EAAE,UAAU,MAAM,EAAE,MAAK,UAAuB,KAAG,OAAO,CAAC,IAAI,CAmE3G,CAAC;AAYF,eAAO,MAAM,cAAc,kDAGzB,CAAC"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { dirname } from "node:path";
|
|
2
|
+
import { forgejoApi } from "@intentic/providers";
|
|
3
|
+
import { collectSecretInventory, readSyncState, secretDigest, writeSyncState } from "@intentic/scaffold";
|
|
4
|
+
import { buildCommand, buildRouteMap } from "@stricli/core";
|
|
5
|
+
import { loadConfig } from "../env.config.js";
|
|
6
|
+
import { ARTIFACT_FILE, ARTIFACT_PATH, CONFIG_FILE, INTENT_DIR, loadEnvFile, readArtifact, TARGET_DIR } from "../lib/artifact.js";
|
|
7
|
+
import { createOutput } from "../lib/output.js";
|
|
8
|
+
import { version } from "../lib/version.js";
|
|
9
|
+
import { APPLY_WORKFLOW_PATH, applyWorkflowYaml, collectSecretValues, setRepoSecrets, writeWorkflow } from "../pipelines/adopt-pipelines.js";
|
|
10
|
+
import { forgejoIdentity } from "../pipelines/control-plane-sync.js";
|
|
11
|
+
import { readGeneratedSecrets } from "../secrets/generated-secrets.js";
|
|
12
|
+
const entryLine = (entry) => {
|
|
13
|
+
const requiredBy = entry.requiredBy.length > 0 ? ` → required by ${entry.requiredBy.map((r) => r.resourceId).join(", ")}` : "";
|
|
14
|
+
const ci = entry.ci === undefined ? "" : ` [CI: ${entry.ci.synced ? "synced" : "out of date"}]`;
|
|
15
|
+
return `${entry.key} ${entry.status} (${entry.kind}, ${entry.storedAt})${requiredBy}${ci}`;
|
|
16
|
+
};
|
|
17
|
+
const list = buildCommand({
|
|
18
|
+
docs: { brief: "List every secret the workspace knows about — status, consumers, and CI sync state (never values)" },
|
|
19
|
+
parameters: {
|
|
20
|
+
flags: { artifact: { kind: "parsed", parse: String, optional: true, brief: `Path to the artifact (default: ${ARTIFACT_PATH})` } },
|
|
21
|
+
},
|
|
22
|
+
async func(flags) {
|
|
23
|
+
const out = createOutput(this.process.stdout, loadConfig().intenticOutput);
|
|
24
|
+
const entries = await collectSecretInventory(dirname(flags.artifact ?? ARTIFACT_PATH));
|
|
25
|
+
for (const entry of entries) {
|
|
26
|
+
out.text(entryLine(entry));
|
|
27
|
+
}
|
|
28
|
+
out.result({ entries });
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
export const pushSecrets = async (out, artifact, api = forgejoApi) => {
|
|
32
|
+
const targetDir = dirname(artifact);
|
|
33
|
+
const sync = await readSyncState(targetDir);
|
|
34
|
+
if (Object.keys(sync).length === 0) {
|
|
35
|
+
out.text("not adopted (no CI secret record) — nothing to push");
|
|
36
|
+
out.result({ pushed: [], reason: "not adopted" });
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
loadEnvFile(targetDir);
|
|
40
|
+
const graph = await readArtifact(artifact);
|
|
41
|
+
const { domain, user, adminPasswordRef: ref } = forgejoIdentity(graph);
|
|
42
|
+
const generatedValues = await readGeneratedSecrets(targetDir);
|
|
43
|
+
const password = ref.source === "generated" ? generatedValues[ref.key] : process.env[ref.key];
|
|
44
|
+
if (password === undefined || password === "") {
|
|
45
|
+
throw new Error(`forgejo admin password (${ref.source} secret ${ref.key}) is not available`);
|
|
46
|
+
}
|
|
47
|
+
const current = collectSecretValues(graph, process.env, generatedValues);
|
|
48
|
+
const changed = Object.entries(current).filter(([key, value]) => sync[key]?.digest !== secretDigest(value));
|
|
49
|
+
const baseUrl = `https://${domain}`;
|
|
50
|
+
if (changed.length > 0) {
|
|
51
|
+
await setRepoSecrets({ api, baseUrl, user, password, owner: user, name: TARGET_DIR, secrets: Object.fromEntries(changed) });
|
|
52
|
+
const cloudflare = changed.find(([key]) => key === "CLOUDFLARE_API_TOKEN");
|
|
53
|
+
if (cloudflare !== undefined) {
|
|
54
|
+
await setRepoSecrets({
|
|
55
|
+
api,
|
|
56
|
+
baseUrl,
|
|
57
|
+
user,
|
|
58
|
+
password,
|
|
59
|
+
owner: user,
|
|
60
|
+
name: INTENT_DIR,
|
|
61
|
+
secrets: { CLOUDFLARE_API_TOKEN: cloudflare[1] },
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const keySetChanged = Object.keys(current).some((key) => sync[key] === undefined);
|
|
66
|
+
if (keySetChanged) {
|
|
67
|
+
const inputs = {
|
|
68
|
+
cliVersion: version,
|
|
69
|
+
user,
|
|
70
|
+
domain,
|
|
71
|
+
configFile: CONFIG_FILE,
|
|
72
|
+
artifactFile: ARTIFACT_FILE,
|
|
73
|
+
intentRepo: INTENT_DIR,
|
|
74
|
+
desiredStateRepo: TARGET_DIR,
|
|
75
|
+
applySecretKeys: Object.keys(current).toSorted(),
|
|
76
|
+
forgejoPasswordKey: ref.key,
|
|
77
|
+
};
|
|
78
|
+
await writeWorkflow(targetDir, APPLY_WORKFLOW_PATH, applyWorkflowYaml(inputs));
|
|
79
|
+
out.text(`regenerated ${APPLY_WORKFLOW_PATH} for the new key set — commit and push desired-state to activate it`);
|
|
80
|
+
}
|
|
81
|
+
const pushedAt = new Date().toISOString();
|
|
82
|
+
await writeSyncState(targetDir, {
|
|
83
|
+
...sync,
|
|
84
|
+
...Object.fromEntries(changed.map(([key, value]) => [key, { digest: secretDigest(value), pushedAt }])),
|
|
85
|
+
});
|
|
86
|
+
const pushed = changed.map(([key]) => key).toSorted();
|
|
87
|
+
const skipped = Object.keys(current)
|
|
88
|
+
.filter((key) => !pushed.includes(key))
|
|
89
|
+
.toSorted();
|
|
90
|
+
out.text(pushed.length > 0 ? `pushed ${pushed.length} secret(s) to ${user}/${TARGET_DIR}: ${pushed.join(", ")}` : "CI secrets already in sync");
|
|
91
|
+
out.result({ pushed, skipped });
|
|
92
|
+
};
|
|
93
|
+
const push = buildCommand({
|
|
94
|
+
docs: { brief: "Push changed secret values to the provisioned Forgejo's Actions secrets (no-op before adopt)" },
|
|
95
|
+
parameters: {
|
|
96
|
+
flags: { artifact: { kind: "parsed", parse: String, optional: true, brief: `Path to the artifact (default: ${ARTIFACT_PATH})` } },
|
|
97
|
+
},
|
|
98
|
+
async func(flags) {
|
|
99
|
+
await pushSecrets(createOutput(this.process.stdout, loadConfig().intenticOutput), flags.artifact ?? ARTIFACT_PATH);
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
export const secretsCommand = buildRouteMap({
|
|
103
|
+
routes: { list, push },
|
|
104
|
+
docs: { brief: "Inspect and sync the workspace's secrets (keys and status only — values never print)" },
|
|
105
|
+
});
|
|
106
|
+
//# sourceMappingURL=secrets.command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.command.js","sourceRoot":"","sources":["../../src/secrets/secrets.command.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type DesiredStateGraph } from "@intentic/graph";
|
|
2
|
+
export declare const collectSecrets: (graph: DesiredStateGraph) => {
|
|
3
|
+
readonly env: string[];
|
|
4
|
+
readonly generated: string[];
|
|
5
|
+
};
|
|
6
|
+
export declare const writeEnvExample: (path: string, keys: readonly string[]) => Promise<void>;
|
|
7
|
+
//# sourceMappingURL=secrets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../../src/secrets/secrets.ts"],"names":[],"mappings":"AACA,OAAO,EAAsB,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAK7E,eAAO,MAAM,cAAc,GAAI,OAAO,iBAAiB,KAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAM/G,CAAC;AAIF,eAAO,MAAM,eAAe,GAAU,MAAM,MAAM,EAAE,MAAM,SAAS,MAAM,EAAE,KAAG,OAAO,CAAC,IAAI,CAEzF,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { writeFile } from "node:fs/promises";
|
|
2
|
+
import { collectSecretUsage } from "@intentic/graph";
|
|
3
|
+
import { renderTemplate } from "../lib/templates.js";
|
|
4
|
+
export const collectSecrets = (graph) => {
|
|
5
|
+
const usage = collectSecretUsage(graph);
|
|
6
|
+
return {
|
|
7
|
+
env: usage.filter((u) => u.source === "env").map((u) => u.key),
|
|
8
|
+
generated: usage.filter((u) => u.source === "generated").map((u) => u.key),
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
export const writeEnvExample = async (path, keys) => {
|
|
12
|
+
await writeFile(path, renderTemplate("env-example", { keys }));
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=secrets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/secrets/secrets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAA0B,MAAM,iBAAiB,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAIrD,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAAwB,EAA4D,EAAE;IACjH,MAAM,KAAK,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACxC,OAAO;QACH,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;QAC9D,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;KAC7E,CAAC;AACN,CAAC,CAAC;AAIF,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAAE,IAAY,EAAE,IAAuB,EAAiB,EAAE;IAC1F,MAAM,SAAS,CAAC,IAAI,EAAE,cAAc,CAAC,aAAa,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACnE,CAAC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@intentic/cli",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://gitlab.com/radarsu/intentic.git",
|
|
10
|
+
"directory": "_apps/cli"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"templates"
|
|
15
|
+
],
|
|
16
|
+
"bin": {
|
|
17
|
+
"intentic": "./dist/cli.js"
|
|
18
|
+
},
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public",
|
|
21
|
+
"registry": "https://registry.npmjs.org/"
|
|
22
|
+
},
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"import": {
|
|
27
|
+
"@intentic/src": "./src/index.ts",
|
|
28
|
+
"default": "./dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"require": {
|
|
31
|
+
"@intentic/src": "./src/index.ts",
|
|
32
|
+
"default": "./dist/index.js"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@puristic/env": "1.4.1",
|
|
38
|
+
"@stricli/core": "1.2.8",
|
|
39
|
+
"eta": "4.6.0",
|
|
40
|
+
"tslib": "2.8.1",
|
|
41
|
+
"zod": "4.4.3",
|
|
42
|
+
"@intentic/engine": "1.0.2",
|
|
43
|
+
"@intentic/graph": "1.0.2",
|
|
44
|
+
"@intentic/need-resolver": "1.0.2",
|
|
45
|
+
"@intentic/providers": "1.0.2",
|
|
46
|
+
"@intentic/sandbox-contract": "1.0.2",
|
|
47
|
+
"@intentic/scaffold": "0.0.0",
|
|
48
|
+
"@intentic/state-resolver": "1.0.2"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "24.13.2",
|
|
52
|
+
"@types/ssh2": "1.15.5",
|
|
53
|
+
"ssh2": "1.17.0",
|
|
54
|
+
"testcontainers": "12.0.3",
|
|
55
|
+
"typescript": "5.9.3",
|
|
56
|
+
"vitest": "4.1.9",
|
|
57
|
+
"yaml": "2.9.0",
|
|
58
|
+
"@intentic/sdk": "1.0.2",
|
|
59
|
+
"@intentic/tsconfig": "0.0.0"
|
|
60
|
+
},
|
|
61
|
+
"scripts": {
|
|
62
|
+
"build": "tsc",
|
|
63
|
+
"dev": "tsc --build --watch",
|
|
64
|
+
"watch": "tsc --build --watch",
|
|
65
|
+
"test": "vitest run",
|
|
66
|
+
"test:watch": "vitest",
|
|
67
|
+
"e2e": "INTENTIC_E2E=1 vitest run cli.e2e"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Access
|
|
2
|
+
|
|
3
|
+
Generated by `intentic apply`. User-supplied passwords are the values you set in `.env`; generated passwords are stored in `.secrets.json` (gitignored).
|
|
4
|
+
<% if (it.services.length > 0) { -%>
|
|
5
|
+
|
|
6
|
+
| Service | URL | Username | Password |
|
|
7
|
+
| --- | --- | --- | --- |
|
|
8
|
+
<% it.services.forEach(function (service) { -%>
|
|
9
|
+
| <%= service.label %> | <%= service.url %> | <%= service.username %> | <%= service.password %> |
|
|
10
|
+
<% }) -%>
|
|
11
|
+
<% } -%>
|
|
12
|
+
<% if (it.apps.length > 0) { -%>
|
|
13
|
+
|
|
14
|
+
## App environments
|
|
15
|
+
|
|
16
|
+
| Environment | URL |
|
|
17
|
+
| --- | --- |
|
|
18
|
+
<% it.apps.forEach(function (app) { -%>
|
|
19
|
+
| <%= app.id %> | <%= app.url %> |
|
|
20
|
+
<% }) -%>
|
|
21
|
+
<% } -%>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { env } from "@intentic/graph";
|
|
2
|
+
import { defineIntent } from "@intentic/sdk";
|
|
3
|
+
|
|
4
|
+
export const intent = defineIntent((i) => {
|
|
5
|
+
const host = i.have.host("host", {
|
|
6
|
+
address: "127.0.0.1",
|
|
7
|
+
user: "root",
|
|
8
|
+
sshKey: env("HOST_SSH_KEY"),
|
|
9
|
+
port: <%= it.sshPort %>,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const cf = i.have.cloudflare("cf", {
|
|
13
|
+
apiToken: env("CLOUDFLARE_API_TOKEN"),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
i.want.app("<%= it.app %>", {
|
|
17
|
+
on: host,
|
|
18
|
+
expose: cf,
|
|
19
|
+
environments: {
|
|
20
|
+
<%= it.env %>: { domain: "<%= it.domain %>", branch: "main", env: { PORT: "<%= it.appPort %>" } },
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { env } from "@intentic/graph";
|
|
2
|
+
import { defineIntent } from "@intentic/sdk";
|
|
3
|
+
|
|
4
|
+
export const intent = defineIntent((i) => {
|
|
5
|
+
const cf = i.have.cloudflare("cf", {
|
|
6
|
+
apiToken: env("CLOUDFLARE_API_TOKEN"),
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
// `self` is your local deploy target (this machine / its Docker-in-Docker host). intentic registers it in
|
|
10
|
+
// the managed `// <intentic>` block at the top of this file — reference it with `on: self`, don't redeclare it.
|
|
11
|
+
i.want.app("my-app", {
|
|
12
|
+
on: self,
|
|
13
|
+
expose: cf,
|
|
14
|
+
environments: {
|
|
15
|
+
production: { domain: "app.<%= it.zone %>", branch: "main" },
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { env } from "@intentic/graph";
|
|
2
|
+
import { defineIntent } from "@intentic/sdk";
|
|
3
|
+
|
|
4
|
+
export const intent = defineIntent((i) => {
|
|
5
|
+
const host = i.have.host("host", {
|
|
6
|
+
address: "203.0.113.10",
|
|
7
|
+
user: "deploy",
|
|
8
|
+
sshKey: env("HOST_SSH_KEY"),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const cf = i.have.cloudflare("cf", {
|
|
12
|
+
apiToken: env("CLOUDFLARE_API_TOKEN"),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
i.want.app("my-app", {
|
|
16
|
+
on: host,
|
|
17
|
+
expose: cf,
|
|
18
|
+
environments: {
|
|
19
|
+
production: { domain: "app.example.com", branch: "main", env: { DATABASE_URL: env("PRODUCTION_DATABASE_URL") } },
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
|
|
3
|
+
// Komodo sets PORT in production; the sandbox's app process manager injects PORT for the live preview.
|
|
4
|
+
const port = Number(process.env.PORT ?? 3000);
|
|
5
|
+
|
|
6
|
+
createServer((_req, res) => {
|
|
7
|
+
res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
8
|
+
res.end("<!doctype html><title>intentic app</title><h1>It works 🎉</h1><p>Edit <code>server.js</code> — the agent works on this repo.</p>");
|
|
9
|
+
}).listen(port, () => console.log(`app listening on :${port}`));
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Generated by intentic: apply the desired-state artifact, pruning resources it no longer declares.
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [ "main" ]
|
|
5
|
+
paths: [ "<%= it.artifactFile %>" ]
|
|
6
|
+
jobs:
|
|
7
|
+
apply:
|
|
8
|
+
runs-on: docker
|
|
9
|
+
env:
|
|
10
|
+
<% it.envEntries.forEach(function (entry) { -%>
|
|
11
|
+
<%= entry.env %>: ${{ secrets.<%= entry.secret %> }}
|
|
12
|
+
<% }) -%>
|
|
13
|
+
steps:
|
|
14
|
+
- uses: https://github.com/actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
|
15
|
+
with:
|
|
16
|
+
fetch-depth: 0
|
|
17
|
+
- name: apply and prune
|
|
18
|
+
run: |
|
|
19
|
+
corepack enable
|
|
20
|
+
PREV=''
|
|
21
|
+
if git rev-parse -q --verify refs/tags/<%= it.appliedTag %> >/dev/null; then \
|
|
22
|
+
git show <%= it.appliedTag %>:<%= it.artifactFile %> > /tmp/previous.json && PREV="--previous /tmp/previous.json"; fi
|
|
23
|
+
pnpm dlx <%= it.cliPackage %>@<%= it.cliVersion %> apply --artifact <%= it.artifactFile %> $PREV
|
|
24
|
+
- name: mark applied
|
|
25
|
+
run: |
|
|
26
|
+
AUTH="$(printf '%s:%s' '<%= it.user %>' "$<%= it.forgejoPasswordKey %>" | base64 -w0)"
|
|
27
|
+
git tag -f <%= it.appliedTag %> HEAD
|
|
28
|
+
git -c http.extraHeader="Authorization: basic $AUTH" push -f origin refs/tags/<%= it.appliedTag %>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Generated by intentic: resolve deploy.config.ts and push the new artifact into the desired-state repo.
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [ "main" ]
|
|
5
|
+
paths: [ "<%= it.configFile %>", "package.json", "pnpm-lock.yaml" ]
|
|
6
|
+
jobs:
|
|
7
|
+
resolve:
|
|
8
|
+
runs-on: docker
|
|
9
|
+
env:
|
|
10
|
+
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
11
|
+
<%= it.gitUserEnv %>: ${{ secrets.<%= it.gitUserSecret %> }}
|
|
12
|
+
<%= it.gitTokenEnv %>: ${{ secrets.<%= it.gitTokenSecret %> }}
|
|
13
|
+
steps:
|
|
14
|
+
- uses: https://github.com/actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
|
15
|
+
- name: resolve and push desired-state
|
|
16
|
+
run: |
|
|
17
|
+
corepack enable
|
|
18
|
+
pnpm install --ignore-workspace
|
|
19
|
+
AUTH="$(printf '%s:%s' "$GIT_USER" "$GIT_TOKEN" | base64 -w0)"
|
|
20
|
+
git -c http.extraHeader="Authorization: basic $AUTH" clone <%= it.desiredStateCloneUrl %> /tmp/ds
|
|
21
|
+
pnpm dlx <%= it.cliPackage %>@<%= it.cliVersion %> resolve --config <%= it.configFile %> --out /tmp/ds/<%= it.artifactFile %> --sync-control-plane
|
|
22
|
+
cd /tmp/ds
|
|
23
|
+
git add -A
|
|
24
|
+
if git diff --cached --quiet; then echo 'desired-state unchanged'; exit 0; fi
|
|
25
|
+
git -c user.name="<%= it.user %>" -c user.email="<%= it.user %>@<%= it.domain %>" commit -m "intentic resolve"
|
|
26
|
+
git -c http.extraHeader="Authorization: basic $AUTH" push origin HEAD:main
|