@notation/cli 0.11.1 → 0.14.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/index.js +187 -42
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -6,17 +6,23 @@ import { program } from "commander";
|
|
|
6
6
|
// src/compile.ts
|
|
7
7
|
import chokidar from "chokidar";
|
|
8
8
|
import { glob } from "glob";
|
|
9
|
-
import { log } from "console";
|
|
10
9
|
import esbuild from "esbuild";
|
|
11
10
|
import {
|
|
12
11
|
functionInfraPlugin,
|
|
13
12
|
functionRuntimePlugin
|
|
14
13
|
} from "@notation/esbuild-plugins";
|
|
15
14
|
import { filePaths } from "@notation/core";
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
|
|
16
|
+
// src/logger.ts
|
|
17
|
+
var defaultLogger = console;
|
|
18
|
+
|
|
19
|
+
// src/compile.ts
|
|
20
|
+
async function compile(entryPoint, opts = {}) {
|
|
21
|
+
const watch2 = opts.watch ?? false;
|
|
22
|
+
const logger = opts.logger ?? defaultLogger;
|
|
23
|
+
logger.info(`${watch2 ? "Watching" : "Compiling"} infrastructure`, entryPoint);
|
|
18
24
|
await compileInfra(entryPoint, watch2);
|
|
19
|
-
|
|
25
|
+
logger.info(`${watch2 ? "Watching" : "Compiling"} functions`);
|
|
20
26
|
const fnEntryPoints = await glob("runtime/**/*.fn.ts");
|
|
21
27
|
let disposeFnCompiler = await compileFns(fnEntryPoints, watch2);
|
|
22
28
|
if (!watch2) return;
|
|
@@ -72,59 +78,175 @@ async function compileFns(entryPoints, watch2 = false) {
|
|
|
72
78
|
}
|
|
73
79
|
|
|
74
80
|
// src/deploy.ts
|
|
75
|
-
import {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
import {
|
|
82
|
+
createLoggerReconcilerSubscriber,
|
|
83
|
+
createNdjsonEventEmitter,
|
|
84
|
+
deployApp
|
|
85
|
+
} from "@notation/core";
|
|
86
|
+
import { randomUUID } from "crypto";
|
|
87
|
+
|
|
88
|
+
// src/stdio.ts
|
|
89
|
+
function redirectStdoutToStderr() {
|
|
90
|
+
const originalWrite = process.stdout.write.bind(process.stdout);
|
|
91
|
+
const write = (line) => {
|
|
92
|
+
originalWrite(line);
|
|
93
|
+
};
|
|
94
|
+
process.stdout.write = ((chunk, ...args) => process.stderr.write(
|
|
95
|
+
chunk,
|
|
96
|
+
...args
|
|
97
|
+
));
|
|
98
|
+
return {
|
|
99
|
+
write,
|
|
100
|
+
restore: () => {
|
|
101
|
+
process.stdout.write = originalWrite;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/deploy.ts
|
|
107
|
+
async function deploy(entryPoint, opts = {}) {
|
|
108
|
+
const logger = opts.logger ?? defaultLogger;
|
|
109
|
+
const emit = opts.json ? createNdjsonEventEmitter(redirectStdoutToStderr().write) : createLoggerReconcilerSubscriber({ logger });
|
|
110
|
+
await compile(entryPoint, { logger });
|
|
111
|
+
logger.info(`Deploying ${entryPoint}`);
|
|
112
|
+
const executionId = opts.executionId ?? randomUUID();
|
|
113
|
+
logger.info(`Execution ID ${executionId}`);
|
|
114
|
+
await deployApp({ entryPoint, emit, executionId });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/destroy.ts
|
|
118
|
+
import {
|
|
119
|
+
createLoggerReconcilerSubscriber as createLoggerReconcilerSubscriber2,
|
|
120
|
+
createNdjsonEventEmitter as createNdjsonEventEmitter2,
|
|
121
|
+
destroyApp
|
|
122
|
+
} from "@notation/core";
|
|
123
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
124
|
+
async function destroy(entryPoint, opts = {}) {
|
|
125
|
+
const logger = opts.logger ?? defaultLogger;
|
|
126
|
+
const emit = opts.json ? createNdjsonEventEmitter2(redirectStdoutToStderr().write) : createLoggerReconcilerSubscriber2({ logger });
|
|
127
|
+
await compile(entryPoint, { logger });
|
|
128
|
+
logger.info(`Destroying ${entryPoint}
|
|
129
|
+
`);
|
|
130
|
+
const executionId = opts.executionId ?? randomUUID2();
|
|
131
|
+
logger.info(`Execution ID ${executionId}`);
|
|
132
|
+
await destroyApp({ entryPoint, emit, executionId });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/plan.ts
|
|
136
|
+
import {
|
|
137
|
+
createLoggerReconcilerSubscriber as createLoggerReconcilerSubscriber3,
|
|
138
|
+
planApp
|
|
139
|
+
} from "@notation/core";
|
|
140
|
+
var decisionSymbols = {
|
|
141
|
+
create: "+",
|
|
142
|
+
update: "~",
|
|
143
|
+
replace: "-/+",
|
|
144
|
+
"drift-update": "~",
|
|
145
|
+
"drift-replace": "-/+",
|
|
146
|
+
"drift-recreate": "\xB1",
|
|
147
|
+
"delete-orphan": "-",
|
|
148
|
+
noop: " "
|
|
149
|
+
};
|
|
150
|
+
async function plan(entryPoint, opts = {}) {
|
|
151
|
+
const logger = opts.logger ?? defaultLogger;
|
|
152
|
+
const emit = createLoggerReconcilerSubscriber3({ logger });
|
|
153
|
+
if (opts.json) {
|
|
154
|
+
let result2;
|
|
155
|
+
const { restore } = redirectStdoutToStderr();
|
|
156
|
+
try {
|
|
157
|
+
await compile(entryPoint, { logger });
|
|
158
|
+
result2 = await planApp({
|
|
159
|
+
entryPoint,
|
|
160
|
+
emit
|
|
161
|
+
});
|
|
162
|
+
} finally {
|
|
163
|
+
restore();
|
|
164
|
+
}
|
|
165
|
+
process.stdout.write(`${JSON.stringify(result2, null, 2)}
|
|
166
|
+
`);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
await compile(entryPoint, { logger });
|
|
170
|
+
logger.info(`Planning ${entryPoint}
|
|
171
|
+
`);
|
|
172
|
+
const result = await planApp({
|
|
173
|
+
entryPoint,
|
|
174
|
+
emit
|
|
175
|
+
});
|
|
176
|
+
printPlanSummary(result, logger);
|
|
177
|
+
}
|
|
178
|
+
function printPlanSummary(result, logger) {
|
|
179
|
+
const changedNodes = result.nodes.filter((node) => node.decision !== "noop");
|
|
180
|
+
for (const node of changedNodes) {
|
|
181
|
+
logger.info(
|
|
182
|
+
`${decisionSymbols[node.decision]} ${node.decision} ${node.type} ${node.id}`
|
|
183
|
+
);
|
|
184
|
+
for (const field of node.replaceFields ?? []) {
|
|
185
|
+
logger.info(
|
|
186
|
+
field.known ? ` ${field.name} forces replacement` : ` ${field.name} unknown, may force replacement`
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const count = (decision) => result.nodes.filter((node) => node.decision === decision).length;
|
|
191
|
+
const summary = [
|
|
192
|
+
`${count("create")} to create`,
|
|
193
|
+
`${count("update") + count("drift-update")} to update`,
|
|
194
|
+
`${count("replace") + count("drift-replace")} to replace`,
|
|
195
|
+
`${count("drift-recreate")} to recreate`,
|
|
196
|
+
`${count("delete-orphan")} to delete`,
|
|
197
|
+
`${count("noop")} unchanged`
|
|
198
|
+
].join(", ");
|
|
199
|
+
logger.info(`${changedNodes.length > 0 ? "\n" : ""}Plan: ${summary}.`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// src/run-with-error-handling.ts
|
|
203
|
+
async function runWithErrorHandling(fn, opts) {
|
|
79
204
|
try {
|
|
80
|
-
await
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
205
|
+
await fn();
|
|
206
|
+
return 0;
|
|
207
|
+
} catch (error) {
|
|
208
|
+
if (error instanceof Error && error.name === "CredentialsProviderError") {
|
|
209
|
+
opts.logger.error(
|
|
84
210
|
"\nAWS credentials not found.",
|
|
85
211
|
"\n\nEnsure you have a default profile set up in ~/.aws/credentials.",
|
|
86
|
-
|
|
212
|
+
`
|
|
213
|
+
|
|
214
|
+
If using another profile run AWS_PROFILE=otherProfile notation ${opts.command}.
|
|
215
|
+
`
|
|
87
216
|
);
|
|
88
|
-
|
|
217
|
+
return 1;
|
|
89
218
|
}
|
|
90
|
-
|
|
91
|
-
|
|
219
|
+
opts.logger.error(error);
|
|
220
|
+
return 1;
|
|
92
221
|
}
|
|
93
222
|
}
|
|
94
223
|
|
|
95
|
-
// src/destroy.ts
|
|
96
|
-
import { destroyApp } from "@notation/core";
|
|
97
|
-
async function destroy(entryPoint) {
|
|
98
|
-
await compile(entryPoint);
|
|
99
|
-
await destroyApp(entryPoint);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
224
|
// src/visualise.ts
|
|
103
225
|
import {
|
|
104
226
|
createMermaidFlowChart,
|
|
105
227
|
createMermaidLiveUrl,
|
|
106
228
|
getResourceGraph
|
|
107
229
|
} from "@notation/core";
|
|
108
|
-
|
|
109
|
-
async function visualise(entryPoint) {
|
|
230
|
+
async function visualise(entryPoint, logger = defaultLogger) {
|
|
110
231
|
await compileInfra(entryPoint);
|
|
111
|
-
await generateGraph(entryPoint);
|
|
232
|
+
await generateGraph(entryPoint, logger);
|
|
112
233
|
}
|
|
113
|
-
async function generateGraph(entryPoint) {
|
|
114
|
-
|
|
234
|
+
async function generateGraph(entryPoint, logger = defaultLogger) {
|
|
235
|
+
logger.info(`Generating graph for ${entryPoint}`);
|
|
115
236
|
const graph = await getResourceGraph(entryPoint);
|
|
116
237
|
const chart = createMermaidFlowChart(graph.resourceGroups, graph.resources);
|
|
117
238
|
const chartUrl = createMermaidLiveUrl(chart);
|
|
118
|
-
|
|
119
|
-
|
|
239
|
+
logger.info("\nGenerated infrastructure chart:\n");
|
|
240
|
+
logger.info(chartUrl);
|
|
120
241
|
}
|
|
121
242
|
|
|
122
243
|
// src/watch.ts
|
|
123
244
|
import chokidar2 from "chokidar";
|
|
124
|
-
import {
|
|
245
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
246
|
+
import { createLoggerReconcilerSubscriber as createLoggerReconcilerSubscriber4, deployApp as deployApp2 } from "@notation/core";
|
|
125
247
|
var dotFilesRe = /(^|[\/\\])\../;
|
|
126
|
-
async function watch(entryPoint) {
|
|
127
|
-
await compile(entryPoint, true);
|
|
248
|
+
async function watch(entryPoint, logger = defaultLogger) {
|
|
249
|
+
await compile(entryPoint, { watch: true, logger });
|
|
128
250
|
const watcher = chokidar2.watch("dist", {
|
|
129
251
|
ignored: dotFilesRe,
|
|
130
252
|
persistent: true
|
|
@@ -148,14 +270,21 @@ async function watch(entryPoint) {
|
|
|
148
270
|
return;
|
|
149
271
|
}
|
|
150
272
|
isDeploying = true;
|
|
151
|
-
|
|
273
|
+
const executionId = randomUUID3();
|
|
274
|
+
logger.info(`Execution ID ${executionId}`);
|
|
275
|
+
deployApp2({
|
|
276
|
+
entryPoint,
|
|
277
|
+
executionId,
|
|
278
|
+
driftDetection: false,
|
|
279
|
+
emit: createLoggerReconcilerSubscriber4({ logger })
|
|
280
|
+
}).then(() => {
|
|
152
281
|
isDeploying = false;
|
|
153
282
|
if (deployQueued) {
|
|
154
283
|
deployQueued = false;
|
|
155
284
|
triggerDeploy();
|
|
156
285
|
}
|
|
157
286
|
}).catch((err) => {
|
|
158
|
-
|
|
287
|
+
logger.error(err);
|
|
159
288
|
isDeploying = false;
|
|
160
289
|
});
|
|
161
290
|
}
|
|
@@ -163,17 +292,30 @@ async function watch(entryPoint) {
|
|
|
163
292
|
|
|
164
293
|
// src/index.ts
|
|
165
294
|
import { startDashboardServer } from "@notation/dashboard";
|
|
295
|
+
import { NodeDurableRuntime, resolveDeploymentId } from "@notation/core";
|
|
166
296
|
program.command("compile").argument("<entryPoint>", "entryPoint").description("Compile Notation App").action(async (entryPoint) => {
|
|
167
297
|
await compile(entryPoint);
|
|
168
298
|
});
|
|
169
|
-
program.command("dashboard").description("Start Notation Dashboard").action(async () => {
|
|
170
|
-
|
|
299
|
+
program.command("dashboard").argument("<entryPoint>", "entryPoint").description("Start Notation Dashboard").action(async (entryPoint) => {
|
|
300
|
+
const runtime = new NodeDurableRuntime({
|
|
301
|
+
deploymentId: resolveDeploymentId(entryPoint)
|
|
302
|
+
});
|
|
303
|
+
await startDashboardServer({ state: runtime.state });
|
|
304
|
+
});
|
|
305
|
+
program.command("deploy").argument("<entryPoint>", "entryPoint").description("Deploy Notation App").option("--json", "stream reconciler events as NDJSON").option("--execution-id <id>", "resume a durable execution").action(async (entryPoint, options) => {
|
|
306
|
+
await deploy(entryPoint, {
|
|
307
|
+
json: options.json,
|
|
308
|
+
executionId: options.executionId
|
|
309
|
+
});
|
|
171
310
|
});
|
|
172
|
-
program.command("
|
|
173
|
-
await
|
|
311
|
+
program.command("destroy").argument("<entryPoint>", "entryPoint").description("Destroy Notation App").option("--json", "stream reconciler events as NDJSON").option("--execution-id <id>", "resume a durable execution").action(async (entryPoint, options) => {
|
|
312
|
+
await destroy(entryPoint, {
|
|
313
|
+
json: options.json,
|
|
314
|
+
executionId: options.executionId
|
|
315
|
+
});
|
|
174
316
|
});
|
|
175
|
-
program.command("
|
|
176
|
-
await
|
|
317
|
+
program.command("plan").argument("<entryPoint>", "entryPoint").description("Plan Notation App").option("--json", "print the plan as JSON").action(async (entryPoint, options) => {
|
|
318
|
+
await plan(entryPoint, { json: options.json });
|
|
177
319
|
});
|
|
178
320
|
program.command("viz").argument("<entryPoint>", "entryPoint").description("Visualise Notation App").action(async (entryPoint) => {
|
|
179
321
|
await visualise(entryPoint);
|
|
@@ -181,4 +323,7 @@ program.command("viz").argument("<entryPoint>", "entryPoint").description("Visua
|
|
|
181
323
|
program.command("watch").argument("<entryPoint>", "entryPoint").description("Watch Notation App").action(async (entryPoint) => {
|
|
182
324
|
await watch(entryPoint);
|
|
183
325
|
});
|
|
184
|
-
|
|
326
|
+
process.exitCode = await runWithErrorHandling(
|
|
327
|
+
() => program.parseAsync(process.argv),
|
|
328
|
+
{ logger: defaultLogger, command: process.argv[2] ?? program.name() }
|
|
329
|
+
);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@notation/cli",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.14.0",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
7
7
|
"templates"
|
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
"chokidar": "^4.0.3",
|
|
14
14
|
"commander": "^13.1.0",
|
|
15
15
|
"esbuild": "^0.25.0",
|
|
16
|
-
"glob": "^
|
|
17
|
-
"@notation/core": "0.
|
|
18
|
-
"@notation/dashboard": "0.
|
|
19
|
-
"@notation/esbuild-plugins": "0.
|
|
16
|
+
"glob": "^13.0.6",
|
|
17
|
+
"@notation/core": "0.14.0",
|
|
18
|
+
"@notation/dashboard": "0.14.0",
|
|
19
|
+
"@notation/esbuild-plugins": "0.14.0"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@types/which-pm-runs": "^1.0.2"
|