@notation/cli 0.11.1 → 0.13.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 +179 -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,167 @@ 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
|
+
"drift-update": "~",
|
|
144
|
+
"drift-recreate": "\xB1",
|
|
145
|
+
"delete-orphan": "-",
|
|
146
|
+
noop: " "
|
|
147
|
+
};
|
|
148
|
+
async function plan(entryPoint, opts = {}) {
|
|
149
|
+
const logger = opts.logger ?? defaultLogger;
|
|
150
|
+
const emit = createLoggerReconcilerSubscriber3({ logger });
|
|
151
|
+
if (opts.json) {
|
|
152
|
+
let result2;
|
|
153
|
+
const { restore } = redirectStdoutToStderr();
|
|
154
|
+
try {
|
|
155
|
+
await compile(entryPoint, { logger });
|
|
156
|
+
result2 = await planApp({
|
|
157
|
+
entryPoint,
|
|
158
|
+
emit
|
|
159
|
+
});
|
|
160
|
+
} finally {
|
|
161
|
+
restore();
|
|
162
|
+
}
|
|
163
|
+
process.stdout.write(`${JSON.stringify(result2, null, 2)}
|
|
164
|
+
`);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
await compile(entryPoint, { logger });
|
|
168
|
+
logger.info(`Planning ${entryPoint}
|
|
169
|
+
`);
|
|
170
|
+
const result = await planApp({
|
|
171
|
+
entryPoint,
|
|
172
|
+
emit
|
|
173
|
+
});
|
|
174
|
+
printPlanSummary(result, logger);
|
|
175
|
+
}
|
|
176
|
+
function printPlanSummary(result, logger) {
|
|
177
|
+
const changedNodes = result.nodes.filter((node) => node.decision !== "noop");
|
|
178
|
+
for (const node of changedNodes) {
|
|
179
|
+
logger.info(
|
|
180
|
+
`${decisionSymbols[node.decision]} ${node.decision} ${node.type} ${node.id}`
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
const count = (decision) => result.nodes.filter((node) => node.decision === decision).length;
|
|
184
|
+
const summary = [
|
|
185
|
+
`${count("create")} to create`,
|
|
186
|
+
`${count("update") + count("drift-update")} to update`,
|
|
187
|
+
`${count("drift-recreate")} to recreate`,
|
|
188
|
+
`${count("delete-orphan")} to delete`,
|
|
189
|
+
`${count("noop")} unchanged`
|
|
190
|
+
].join(", ");
|
|
191
|
+
logger.info(`${changedNodes.length > 0 ? "\n" : ""}Plan: ${summary}.`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// src/run-with-error-handling.ts
|
|
195
|
+
async function runWithErrorHandling(fn, opts) {
|
|
79
196
|
try {
|
|
80
|
-
await
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
197
|
+
await fn();
|
|
198
|
+
return 0;
|
|
199
|
+
} catch (error) {
|
|
200
|
+
if (error instanceof Error && error.name === "CredentialsProviderError") {
|
|
201
|
+
opts.logger.error(
|
|
84
202
|
"\nAWS credentials not found.",
|
|
85
203
|
"\n\nEnsure you have a default profile set up in ~/.aws/credentials.",
|
|
86
|
-
|
|
204
|
+
`
|
|
205
|
+
|
|
206
|
+
If using another profile run AWS_PROFILE=otherProfile notation ${opts.command}.
|
|
207
|
+
`
|
|
87
208
|
);
|
|
88
|
-
|
|
209
|
+
return 1;
|
|
89
210
|
}
|
|
90
|
-
|
|
91
|
-
|
|
211
|
+
opts.logger.error(error);
|
|
212
|
+
return 1;
|
|
92
213
|
}
|
|
93
214
|
}
|
|
94
215
|
|
|
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
216
|
// src/visualise.ts
|
|
103
217
|
import {
|
|
104
218
|
createMermaidFlowChart,
|
|
105
219
|
createMermaidLiveUrl,
|
|
106
220
|
getResourceGraph
|
|
107
221
|
} from "@notation/core";
|
|
108
|
-
|
|
109
|
-
async function visualise(entryPoint) {
|
|
222
|
+
async function visualise(entryPoint, logger = defaultLogger) {
|
|
110
223
|
await compileInfra(entryPoint);
|
|
111
|
-
await generateGraph(entryPoint);
|
|
224
|
+
await generateGraph(entryPoint, logger);
|
|
112
225
|
}
|
|
113
|
-
async function generateGraph(entryPoint) {
|
|
114
|
-
|
|
226
|
+
async function generateGraph(entryPoint, logger = defaultLogger) {
|
|
227
|
+
logger.info(`Generating graph for ${entryPoint}`);
|
|
115
228
|
const graph = await getResourceGraph(entryPoint);
|
|
116
229
|
const chart = createMermaidFlowChart(graph.resourceGroups, graph.resources);
|
|
117
230
|
const chartUrl = createMermaidLiveUrl(chart);
|
|
118
|
-
|
|
119
|
-
|
|
231
|
+
logger.info("\nGenerated infrastructure chart:\n");
|
|
232
|
+
logger.info(chartUrl);
|
|
120
233
|
}
|
|
121
234
|
|
|
122
235
|
// src/watch.ts
|
|
123
236
|
import chokidar2 from "chokidar";
|
|
124
|
-
import {
|
|
237
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
238
|
+
import { createLoggerReconcilerSubscriber as createLoggerReconcilerSubscriber4, deployApp as deployApp2 } from "@notation/core";
|
|
125
239
|
var dotFilesRe = /(^|[\/\\])\../;
|
|
126
|
-
async function watch(entryPoint) {
|
|
127
|
-
await compile(entryPoint, true);
|
|
240
|
+
async function watch(entryPoint, logger = defaultLogger) {
|
|
241
|
+
await compile(entryPoint, { watch: true, logger });
|
|
128
242
|
const watcher = chokidar2.watch("dist", {
|
|
129
243
|
ignored: dotFilesRe,
|
|
130
244
|
persistent: true
|
|
@@ -148,14 +262,21 @@ async function watch(entryPoint) {
|
|
|
148
262
|
return;
|
|
149
263
|
}
|
|
150
264
|
isDeploying = true;
|
|
151
|
-
|
|
265
|
+
const executionId = randomUUID3();
|
|
266
|
+
logger.info(`Execution ID ${executionId}`);
|
|
267
|
+
deployApp2({
|
|
268
|
+
entryPoint,
|
|
269
|
+
executionId,
|
|
270
|
+
driftDetection: false,
|
|
271
|
+
emit: createLoggerReconcilerSubscriber4({ logger })
|
|
272
|
+
}).then(() => {
|
|
152
273
|
isDeploying = false;
|
|
153
274
|
if (deployQueued) {
|
|
154
275
|
deployQueued = false;
|
|
155
276
|
triggerDeploy();
|
|
156
277
|
}
|
|
157
278
|
}).catch((err) => {
|
|
158
|
-
|
|
279
|
+
logger.error(err);
|
|
159
280
|
isDeploying = false;
|
|
160
281
|
});
|
|
161
282
|
}
|
|
@@ -163,17 +284,30 @@ async function watch(entryPoint) {
|
|
|
163
284
|
|
|
164
285
|
// src/index.ts
|
|
165
286
|
import { startDashboardServer } from "@notation/dashboard";
|
|
287
|
+
import { NodeDurableRuntime, resolveDeploymentId } from "@notation/core";
|
|
166
288
|
program.command("compile").argument("<entryPoint>", "entryPoint").description("Compile Notation App").action(async (entryPoint) => {
|
|
167
289
|
await compile(entryPoint);
|
|
168
290
|
});
|
|
169
|
-
program.command("dashboard").description("Start Notation Dashboard").action(async () => {
|
|
170
|
-
|
|
291
|
+
program.command("dashboard").argument("<entryPoint>", "entryPoint").description("Start Notation Dashboard").action(async (entryPoint) => {
|
|
292
|
+
const runtime = new NodeDurableRuntime({
|
|
293
|
+
deploymentId: resolveDeploymentId(entryPoint)
|
|
294
|
+
});
|
|
295
|
+
await startDashboardServer({ state: runtime.state });
|
|
296
|
+
});
|
|
297
|
+
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) => {
|
|
298
|
+
await deploy(entryPoint, {
|
|
299
|
+
json: options.json,
|
|
300
|
+
executionId: options.executionId
|
|
301
|
+
});
|
|
171
302
|
});
|
|
172
|
-
program.command("
|
|
173
|
-
await
|
|
303
|
+
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) => {
|
|
304
|
+
await destroy(entryPoint, {
|
|
305
|
+
json: options.json,
|
|
306
|
+
executionId: options.executionId
|
|
307
|
+
});
|
|
174
308
|
});
|
|
175
|
-
program.command("
|
|
176
|
-
await
|
|
309
|
+
program.command("plan").argument("<entryPoint>", "entryPoint").description("Plan Notation App").option("--json", "print the plan as JSON").action(async (entryPoint, options) => {
|
|
310
|
+
await plan(entryPoint, { json: options.json });
|
|
177
311
|
});
|
|
178
312
|
program.command("viz").argument("<entryPoint>", "entryPoint").description("Visualise Notation App").action(async (entryPoint) => {
|
|
179
313
|
await visualise(entryPoint);
|
|
@@ -181,4 +315,7 @@ program.command("viz").argument("<entryPoint>", "entryPoint").description("Visua
|
|
|
181
315
|
program.command("watch").argument("<entryPoint>", "entryPoint").description("Watch Notation App").action(async (entryPoint) => {
|
|
182
316
|
await watch(entryPoint);
|
|
183
317
|
});
|
|
184
|
-
|
|
318
|
+
process.exitCode = await runWithErrorHandling(
|
|
319
|
+
() => program.parseAsync(process.argv),
|
|
320
|
+
{ logger: defaultLogger, command: process.argv[2] ?? program.name() }
|
|
321
|
+
);
|
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.13.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/
|
|
18
|
-
"@notation/
|
|
19
|
-
"@notation/esbuild-plugins": "0.
|
|
16
|
+
"glob": "^13.0.6",
|
|
17
|
+
"@notation/dashboard": "0.13.0",
|
|
18
|
+
"@notation/core": "0.13.0",
|
|
19
|
+
"@notation/esbuild-plugins": "0.13.0"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@types/which-pm-runs": "^1.0.2"
|