nerdsystem 0.0.28 → 0.0.29
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/commands/deploy.d.ts +15 -3
- package/dist/commands/deploy.js +32 -6
- package/dist/commands/init.js +1 -1
- package/dist/util.d.ts +5 -0
- package/dist/util.js +13 -2
- package/package.json +1 -1
|
@@ -18,10 +18,22 @@ export interface BuildOutput {
|
|
|
18
18
|
*/
|
|
19
19
|
export declare function dropLocalSecretCopies(build: BuildOutput): number;
|
|
20
20
|
/**
|
|
21
|
-
* The build's own output
|
|
22
|
-
* it wrote), which reads as if local secrets were about to be uploaded.
|
|
21
|
+
* The build's own output names that copy twice ("Using secrets defined in .dev.vars", then the file among
|
|
22
|
+
* what it wrote), which reads as if local secrets were about to be uploaded. This sentence is printed in
|
|
23
|
+
* place of those lines, so the explanation is where the alarm would have been. True before and after the copy is deleted.
|
|
23
24
|
*/
|
|
24
|
-
export declare const SECRET_COPY_NOTE = "The build tool
|
|
25
|
+
export declare const SECRET_COPY_NOTE = "The build tool makes a copy of your local secrets file (.dev.vars) in its output folder, for local previews. nerd deletes that copy when the build ends and never uploads it: the live app gets its secrets from `nerd secret set`.";
|
|
26
|
+
/**
|
|
27
|
+
* Every check of a deploy as one list, so its closing count is the one `nerd check` gives for the same
|
|
28
|
+
* code: the scan of the built output is in it, and a check that ran on the code and again on the built
|
|
29
|
+
* output (the policy's size limits) is one check, with the later answer.
|
|
30
|
+
*/
|
|
31
|
+
export declare function wholeRun(source: GateResult[], built: GateResult[]): GateResult[];
|
|
32
|
+
/** For the build's output: the lines that name the local secrets file become the one sentence above, said once. */
|
|
33
|
+
export declare function secretCopyLines(): {
|
|
34
|
+
line: (text: string) => string | null;
|
|
35
|
+
said: () => boolean;
|
|
36
|
+
};
|
|
25
37
|
export declare function readBuildOutput(root: string): BuildOutput;
|
|
26
38
|
export declare function applyRemoteMigrations(root: string, api: ApiClient): Promise<{
|
|
27
39
|
applied: string[];
|
package/dist/commands/deploy.js
CHANGED
|
@@ -43,10 +43,34 @@ export function dropLocalSecretCopies(build) {
|
|
|
43
43
|
return dropped;
|
|
44
44
|
}
|
|
45
45
|
/**
|
|
46
|
-
* The build's own output
|
|
47
|
-
* it wrote), which reads as if local secrets were about to be uploaded.
|
|
46
|
+
* The build's own output names that copy twice ("Using secrets defined in .dev.vars", then the file among
|
|
47
|
+
* what it wrote), which reads as if local secrets were about to be uploaded. This sentence is printed in
|
|
48
|
+
* place of those lines, so the explanation is where the alarm would have been. True before and after the copy is deleted.
|
|
48
49
|
*/
|
|
49
|
-
export const SECRET_COPY_NOTE = "The build tool
|
|
50
|
+
export const SECRET_COPY_NOTE = "The build tool makes a copy of your local secrets file (.dev.vars) in its output folder, for local previews. nerd deletes that copy when the build ends and never uploads it: the live app gets its secrets from `nerd secret set`.";
|
|
51
|
+
/**
|
|
52
|
+
* Every check of a deploy as one list, so its closing count is the one `nerd check` gives for the same
|
|
53
|
+
* code: the scan of the built output is in it, and a check that ran on the code and again on the built
|
|
54
|
+
* output (the policy's size limits) is one check, with the later answer.
|
|
55
|
+
*/
|
|
56
|
+
export function wholeRun(source, built) {
|
|
57
|
+
return [...source.filter((r) => !built.some((b) => b.name === r.name)), ...built];
|
|
58
|
+
}
|
|
59
|
+
/** For the build's output: the lines that name the local secrets file become the one sentence above, said once. */
|
|
60
|
+
export function secretCopyLines() {
|
|
61
|
+
let said = false;
|
|
62
|
+
return {
|
|
63
|
+
line: (text) => {
|
|
64
|
+
if (!/\.dev\.vars\b/.test(text))
|
|
65
|
+
return text;
|
|
66
|
+
if (said)
|
|
67
|
+
return null;
|
|
68
|
+
said = true;
|
|
69
|
+
return SECRET_COPY_NOTE;
|
|
70
|
+
},
|
|
71
|
+
said: () => said,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
50
74
|
export function readBuildOutput(root) {
|
|
51
75
|
const dist = path.join(root, "dist");
|
|
52
76
|
if (!existsSync(dist))
|
|
@@ -241,18 +265,20 @@ export async function deploy(opts) {
|
|
|
241
265
|
const gates = await runGates(SOURCE_GATES, { root, knownSecrets, approvedSuppressions: await approvedSuppressions(root), log: (l) => log.info(l) }, { stopOnFail: true });
|
|
242
266
|
if (!gates.ok)
|
|
243
267
|
throw new CliError(`Blocked by: ${gates.failed.map((g) => g.title).join(", ")}. Fix the findings above, then deploy again.`, 2);
|
|
244
|
-
|
|
268
|
+
const secretCopy = secretCopyLines();
|
|
245
269
|
if (!opts["skip-build"]) {
|
|
246
270
|
log.step("Building with vite");
|
|
247
271
|
ensureDeps(root);
|
|
248
|
-
await runOrThrow("npx", ["vite", "build"], { cwd: root, stdoutToStderr: true });
|
|
272
|
+
await runOrThrow("npx", ["vite", "build"], { cwd: root, stdoutToStderr: true, stdoutLine: secretCopy.line });
|
|
249
273
|
}
|
|
250
274
|
const build = readBuildOutput(root);
|
|
251
|
-
|
|
275
|
+
// Said here only when the build's output did not carry it (a build made earlier, or a tool that words it differently).
|
|
276
|
+
if (dropLocalSecretCopies(build) && !secretCopy.said())
|
|
252
277
|
log.info(SECRET_COPY_NOTE);
|
|
253
278
|
const built = await runGates(BUILD_GATES, { root, build, knownSecrets, log: (l) => log.info(l) }, { stopOnFail: true });
|
|
254
279
|
if (!built.ok)
|
|
255
280
|
throw new CliError(`Blocked by: ${built.failed.map((g) => g.title).join(", ")}.`, 2);
|
|
281
|
+
log.ok(gateSummary(wholeRun(gates.results, built.results)));
|
|
256
282
|
const r = await shipBuild({ root, nerd, api, build, results: [...gates.results, ...built.results], opts });
|
|
257
283
|
// The app is live at this point. The platform says what the review on file holds, and now and then reads the code itself.
|
|
258
284
|
const reviewPage = `${dashboardUrl(api.apiUrl)}/app/${nerd.org}/projects/${nerd.project}/review`;
|
package/dist/commands/init.js
CHANGED
|
@@ -192,4 +192,4 @@ export async function init(dir, args) {
|
|
|
192
192
|
log.ok(`Ready. Next:\n cd ${dir}\n pnpm install\n nerd dev\n\nThen open the folder in your coding agent and ask it to build the first feature; the nerdsystem skill walks it through.\nDeploys go to ${project.url}`);
|
|
193
193
|
}
|
|
194
194
|
// Kept in sync with packages/sdk/package.json by the release script.
|
|
195
|
-
const SDK_VERSION = "0.0.
|
|
195
|
+
const SDK_VERSION = "0.0.29";
|
package/dist/util.d.ts
CHANGED
|
@@ -22,6 +22,11 @@ export interface RunOptions {
|
|
|
22
22
|
env?: NodeJS.ProcessEnv;
|
|
23
23
|
/** Send the child's stdout to our stderr, so stdout stays reserved for `--json` output. */
|
|
24
24
|
stdoutToStderr?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* With `stdoutToStderr`: each line the child prints is passed through this first. Return the line to
|
|
27
|
+
* print it, another line to print that instead, or null to print nothing.
|
|
28
|
+
*/
|
|
29
|
+
stdoutLine?: (line: string) => string | null;
|
|
25
30
|
}
|
|
26
31
|
/**
|
|
27
32
|
* How a command is started. On Windows the tools an app uses (npx, pnpm) are `.cmd` files, which only
|
package/dist/util.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { createInterface } from "node:readline";
|
|
4
5
|
export class CliError extends Error {
|
|
5
6
|
exitCode;
|
|
6
7
|
/** The API's error code (e.g. "not_found") when the error came from a response; callers branch on this, never on the message. */
|
|
@@ -40,9 +41,19 @@ export function run(cmd, args, opts = {}) {
|
|
|
40
41
|
return new Promise((resolve, reject) => {
|
|
41
42
|
const how = shellCommand(cmd, args);
|
|
42
43
|
const child = spawn(how.file, how.args, { cwd: opts.cwd, env: { ...process.env, ...opts.env }, stdio: ["inherit", opts.stdoutToStderr ? "pipe" : "inherit", "inherit"], shell: how.shell });
|
|
43
|
-
child.stdout
|
|
44
|
+
const perLine = child.stdout && opts.stdoutLine ? opts.stdoutLine : null;
|
|
45
|
+
if (perLine) {
|
|
46
|
+
createInterface({ input: child.stdout }).on("line", (line) => {
|
|
47
|
+
const out = perLine(line);
|
|
48
|
+
if (out !== null)
|
|
49
|
+
process.stderr.write(`${out}\n`);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
else
|
|
53
|
+
child.stdout?.pipe(process.stderr);
|
|
44
54
|
child.on("error", reject);
|
|
45
|
-
|
|
55
|
+
// "close" comes after the child's output has ended, so every line has been through `stdoutLine` by then.
|
|
56
|
+
child.on(perLine ? "close" : "exit", (code) => resolve(code ?? 1));
|
|
46
57
|
});
|
|
47
58
|
}
|
|
48
59
|
export async function runOrThrow(cmd, args, opts = {}) {
|