hatchkit 0.1.4 → 0.1.5
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/adopt.d.ts +2 -0
- package/dist/adopt.d.ts.map +1 -0
- package/dist/adopt.js +552 -0
- package/dist/adopt.js.map +1 -0
- package/dist/completion.d.ts.map +1 -1
- package/dist/completion.js +3 -0
- package/dist/completion.js.map +1 -1
- package/dist/config.d.ts +30 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +108 -0
- package/dist/config.js.map +1 -1
- package/dist/deploy/pages.js +50 -9
- package/dist/deploy/pages.js.map +1 -1
- package/dist/deploy/rename-domain.d.ts.map +1 -1
- package/dist/deploy/rename-domain.js +26 -6
- package/dist/deploy/rename-domain.js.map +1 -1
- package/dist/deploy/rollback.d.ts +10 -0
- package/dist/deploy/rollback.d.ts.map +1 -0
- package/dist/deploy/rollback.js +295 -0
- package/dist/deploy/rollback.js.map +1 -0
- package/dist/deploy/terraform.d.ts +10 -1
- package/dist/deploy/terraform.d.ts.map +1 -1
- package/dist/deploy/terraform.js +177 -42
- package/dist/deploy/terraform.js.map +1 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +25 -0
- package/dist/doctor.js.map +1 -1
- package/dist/explain.d.ts.map +1 -1
- package/dist/explain.js +5 -0
- package/dist/explain.js.map +1 -1
- package/dist/index.js +356 -122
- package/dist/index.js.map +1 -1
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +283 -11
- package/dist/prompts.js.map +1 -1
- package/dist/provision/stripe.d.ts +19 -0
- package/dist/provision/stripe.d.ts.map +1 -0
- package/dist/provision/stripe.js +58 -0
- package/dist/provision/stripe.js.map +1 -0
- package/dist/scaffold/dotenvx.d.ts.map +1 -1
- package/dist/scaffold/dotenvx.js +35 -11
- package/dist/scaffold/dotenvx.js.map +1 -1
- package/dist/scaffold/infra.d.ts +21 -1
- package/dist/scaffold/infra.d.ts.map +1 -1
- package/dist/scaffold/infra.js +66 -20
- package/dist/scaffold/infra.js.map +1 -1
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +7 -0
- package/dist/status.js.map +1 -1
- package/dist/utils/coolify-api.d.ts +12 -0
- package/dist/utils/coolify-api.d.ts.map +1 -1
- package/dist/utils/coolify-api.js +33 -0
- package/dist/utils/coolify-api.js.map +1 -1
- package/dist/utils/run-ledger.d.ts +68 -0
- package/dist/utils/run-ledger.d.ts.map +1 -0
- package/dist/utils/run-ledger.js +99 -0
- package/dist/utils/run-ledger.js.map +1 -0
- package/dist/utils/secrets.d.ts +2 -0
- package/dist/utils/secrets.d.ts.map +1 -1
- package/dist/utils/secrets.js +2 -0
- package/dist/utils/secrets.js.map +1 -1
- package/package.json +2 -2
- package/scripts/release-prep.mjs +56 -98
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Run ledger — append-only record of what `hatchkit create` accomplished
|
|
3
|
+
* for one project. Persisted as JSON next to the Conf store so it
|
|
4
|
+
* survives crashes and lets us:
|
|
5
|
+
*
|
|
6
|
+
* 1. On failure mid-create, print a tailored cleanup recipe and offer
|
|
7
|
+
* to undo the steps that did succeed.
|
|
8
|
+
* 2. Drive `hatchkit destroy <project>` later — the same code path,
|
|
9
|
+
* just without the "should I?" prompt.
|
|
10
|
+
*
|
|
11
|
+
* Steps are recorded *immediately after each external mutation* so a
|
|
12
|
+
* SIGKILL between operations leaves an accurate (though possibly
|
|
13
|
+
* incomplete) ledger. Reverse-order undo is the cleanup strategy.
|
|
14
|
+
*
|
|
15
|
+
* Path: <configDir>/runs/<sanitized-name>.json
|
|
16
|
+
*/
|
|
17
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
18
|
+
import { dirname, join } from "node:path";
|
|
19
|
+
import { getStore } from "../config.js";
|
|
20
|
+
/** Sanitize project name for use as a filename. Project names are
|
|
21
|
+
* already validated to be slug-shaped, but belt-and-braces. */
|
|
22
|
+
function sanitize(name) {
|
|
23
|
+
return name.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
24
|
+
}
|
|
25
|
+
function runsDir() {
|
|
26
|
+
return join(dirname(getStore().path), "runs");
|
|
27
|
+
}
|
|
28
|
+
function ledgerPath(name) {
|
|
29
|
+
return join(runsDir(), `${sanitize(name)}.json`);
|
|
30
|
+
}
|
|
31
|
+
export class RunLedger {
|
|
32
|
+
_path;
|
|
33
|
+
data;
|
|
34
|
+
constructor(_path, data) {
|
|
35
|
+
this._path = _path;
|
|
36
|
+
this.data = data;
|
|
37
|
+
}
|
|
38
|
+
/** Begin a new ledger for this project. Overwrites any prior ledger
|
|
39
|
+
* for the same name (assumes the caller has already cleaned up). */
|
|
40
|
+
static start(name) {
|
|
41
|
+
const data = {
|
|
42
|
+
name,
|
|
43
|
+
startedAt: new Date().toISOString(),
|
|
44
|
+
steps: [],
|
|
45
|
+
};
|
|
46
|
+
const path = ledgerPath(name);
|
|
47
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
48
|
+
writeFileSync(path, JSON.stringify(data, null, 2));
|
|
49
|
+
return new RunLedger(path, data);
|
|
50
|
+
}
|
|
51
|
+
/** Load the ledger for a project, if one exists. */
|
|
52
|
+
static load(name) {
|
|
53
|
+
const path = ledgerPath(name);
|
|
54
|
+
if (!existsSync(path))
|
|
55
|
+
return null;
|
|
56
|
+
try {
|
|
57
|
+
const data = JSON.parse(readFileSync(path, "utf-8"));
|
|
58
|
+
return new RunLedger(path, data);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** Append a step and flush immediately. */
|
|
65
|
+
record(step) {
|
|
66
|
+
this.data.steps.push(step);
|
|
67
|
+
this.flush();
|
|
68
|
+
}
|
|
69
|
+
/** Mark the run finished. The ledger sticks around so `hatchkit
|
|
70
|
+
* destroy` can use it later. */
|
|
71
|
+
complete() {
|
|
72
|
+
this.data.finishedAt = new Date().toISOString();
|
|
73
|
+
this.flush();
|
|
74
|
+
}
|
|
75
|
+
/** Remove the ledger from disk. Call after a successful rollback. */
|
|
76
|
+
delete() {
|
|
77
|
+
if (existsSync(this._path))
|
|
78
|
+
unlinkSync(this._path);
|
|
79
|
+
}
|
|
80
|
+
get name() {
|
|
81
|
+
return this.data.name;
|
|
82
|
+
}
|
|
83
|
+
get steps() {
|
|
84
|
+
return this.data.steps;
|
|
85
|
+
}
|
|
86
|
+
get startedAt() {
|
|
87
|
+
return this.data.startedAt;
|
|
88
|
+
}
|
|
89
|
+
get finishedAt() {
|
|
90
|
+
return this.data.finishedAt;
|
|
91
|
+
}
|
|
92
|
+
get path() {
|
|
93
|
+
return this._path;
|
|
94
|
+
}
|
|
95
|
+
flush() {
|
|
96
|
+
writeFileSync(this._path, JSON.stringify(this.data, null, 2));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=run-ledger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-ledger.js","sourceRoot":"","sources":["../../src/utils/run-ledger.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/secrets.d.ts
CHANGED
|
@@ -19,6 +19,8 @@ export declare const SECRET_KEYS: {
|
|
|
19
19
|
readonly openpanelRootClientSecret: "openpanel:root-client-secret";
|
|
20
20
|
readonly openpanelClientSecret: (name: string) => string;
|
|
21
21
|
readonly resendApiKey: "resend:api-key";
|
|
22
|
+
readonly stripeSecretKey: "stripe:secret-key";
|
|
23
|
+
readonly stripePublishableKey: "stripe:publishable-key";
|
|
22
24
|
/** Per-scaffolded-project dotenvx private key for .env.production.
|
|
23
25
|
* Stored in the OS keychain so the CLI's on-disk state never holds
|
|
24
26
|
* decryption material for the starter's encrypted env. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../../src/utils/secrets.ts"],"names":[],"mappings":"AAoBA;mEACmE;AACnE,eAAO,MAAM,WAAW;;;;;IAKtB;;oDAEgD;;qCAExB,MAAM;qCACN,MAAM;mCACR,MAAM;;IAE5B;wEACoE;;;2CAGtC,MAAM
|
|
1
|
+
{"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../../src/utils/secrets.ts"],"names":[],"mappings":"AAoBA;mEACmE;AACnE,eAAO,MAAM,WAAW;;;;;IAKtB;;oDAEgD;;qCAExB,MAAM;qCACN,MAAM;mCACR,MAAM;;IAE5B;wEACoE;;;2CAGtC,MAAM;;;;IAIpC;;+DAE2D;8CAC1B,MAAM;CAC/B,CAAC;AAEX,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAEnE;AAED,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEzE;AAED,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAEhE;AAED,iEAAiE;AACjE,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAGrD"}
|
package/dist/utils/secrets.js
CHANGED
|
@@ -36,6 +36,8 @@ export const SECRET_KEYS = {
|
|
|
36
36
|
openpanelRootClientSecret: "openpanel:root-client-secret",
|
|
37
37
|
openpanelClientSecret: (name) => `openpanel:${name}:client-secret`,
|
|
38
38
|
resendApiKey: "resend:api-key",
|
|
39
|
+
stripeSecretKey: "stripe:secret-key",
|
|
40
|
+
stripePublishableKey: "stripe:publishable-key",
|
|
39
41
|
/** Per-scaffolded-project dotenvx private key for .env.production.
|
|
40
42
|
* Stored in the OS keychain so the CLI's on-disk state never holds
|
|
41
43
|
* decryption material for the starter's encrypted env. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/utils/secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,iEAAiE;AACjE,uEAAuE;AACvE,sEAAsE;AACtE,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,UAAU,CAAC;AAElE;mEACmE;AACnE,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,YAAY,EAAE,eAAe;IAC7B,YAAY,EAAE,eAAe;IAC7B,eAAe,EAAE,mBAAmB;IACpC,kBAAkB,EAAE,sBAAsB;IAC1C;;oDAEgD;IAChD,wBAAwB,EAAE,6BAA6B;IACvD,WAAW,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,MAAM,QAAQ,aAAa;IAC9D,WAAW,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,MAAM,QAAQ,aAAa;IAC9D,SAAS,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,OAAO,QAAQ,UAAU;IAC1D,cAAc,EAAE,sBAAsB;IACtC;wEACoE;IACpE,qBAAqB,EAAE,0BAA0B;IACjD,yBAAyB,EAAE,8BAA8B;IACzD,qBAAqB,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,aAAa,IAAI,gBAAgB;IAC1E,YAAY,EAAE,gBAAgB;IAC9B;;+DAE2D;IAC3D,iBAAiB,EAAE,CAAC,WAAmB,EAAE,EAAE,CAAC,WAAW,WAAW,yBAAyB;CACnF,CAAC;AAEX,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAW;IACzC,OAAO,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAW,EAAE,KAAa;IACxD,MAAM,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAW;IAC5C,OAAO,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;AAC7C,CAAC;AAED,iEAAiE;AACjE,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IACtD,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AACnF,CAAC"}
|
|
1
|
+
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/utils/secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,iEAAiE;AACjE,uEAAuE;AACvE,sEAAsE;AACtE,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,UAAU,CAAC;AAElE;mEACmE;AACnE,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,YAAY,EAAE,eAAe;IAC7B,YAAY,EAAE,eAAe;IAC7B,eAAe,EAAE,mBAAmB;IACpC,kBAAkB,EAAE,sBAAsB;IAC1C;;oDAEgD;IAChD,wBAAwB,EAAE,6BAA6B;IACvD,WAAW,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,MAAM,QAAQ,aAAa;IAC9D,WAAW,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,MAAM,QAAQ,aAAa;IAC9D,SAAS,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,OAAO,QAAQ,UAAU;IAC1D,cAAc,EAAE,sBAAsB;IACtC;wEACoE;IACpE,qBAAqB,EAAE,0BAA0B;IACjD,yBAAyB,EAAE,8BAA8B;IACzD,qBAAqB,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,aAAa,IAAI,gBAAgB;IAC1E,YAAY,EAAE,gBAAgB;IAC9B,eAAe,EAAE,mBAAmB;IACpC,oBAAoB,EAAE,wBAAwB;IAC9C;;+DAE2D;IAC3D,iBAAiB,EAAE,CAAC,WAAmB,EAAE,EAAE,CAAC,WAAW,WAAW,yBAAyB;CACnF,CAAC;AAEX,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAW;IACzC,OAAO,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAW,EAAE,KAAa;IACxD,MAAM,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAW;IAC5C,OAAO,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;AAC7C,CAAC;AAED,iEAAiE;AACjE,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IACtD,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AACnF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hatchkit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"packageManager": "pnpm@10.33.2",
|
|
5
5
|
"description": "Interactive CLI for scaffolding full-stack projects and provisioning observability/email clients",
|
|
6
6
|
"type": "module",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"release:patch": "node scripts/release-prep.mjs && npm version patch -m \"chore: release v%s\" && pnpm run _release:finish",
|
|
28
28
|
"release:minor": "node scripts/release-prep.mjs && npm version minor -m \"chore: release v%s\" && pnpm run _release:finish",
|
|
29
29
|
"release:major": "node scripts/release-prep.mjs && npm version major -m \"chore: release v%s\" && pnpm run _release:finish",
|
|
30
|
-
"_release:finish": "pnpm run build && pnpm run typecheck && npm publish --access public && git push --follow-tags && npm install -g .",
|
|
30
|
+
"_release:finish": "pnpm run build && pnpm run typecheck && npm publish --access public && git -C ../infra push && git -C ../starter push && git push --follow-tags && npm install -g .",
|
|
31
31
|
"prepublishOnly": "pnpm run build"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
package/scripts/release-prep.mjs
CHANGED
|
@@ -1,30 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/*
|
|
3
|
-
* release-prep —
|
|
4
|
-
* changes get committed and shipped with the release instead of hanging
|
|
5
|
-
* around on main afterwards.
|
|
3
|
+
* release-prep — strict pre-release verification.
|
|
6
4
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
5
|
+
* Refuses to release if any of the three repos (hatchkit main,
|
|
6
|
+
* infra submodule, starter submodule) has uncommitted or untracked
|
|
7
|
+
* changes. Exits with a clear list of which repos need handling and
|
|
8
|
+
* what's dirty in each — release scripts assume a clean baseline so
|
|
9
|
+
* the version commit + tag, the npm publish, and the cross-repo push
|
|
10
|
+
* all line up against the same set of trees.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* (.env*, *.pem, *.key, *credentials*, *secret*).
|
|
15
|
-
* - Submodule pointer changes (git shows as ` M infra` vs `M infra`)
|
|
16
|
-
* are fine, but untracked content INSIDE a submodule is NOT auto-
|
|
17
|
-
* committed from the parent repo — you'd want to commit that inside
|
|
18
|
-
* the submodule yourself first. We warn + bail in that case.
|
|
19
|
-
* - Interactive: prompts for a commit message (default:
|
|
20
|
-
* "chore: pre-release changes").
|
|
21
|
-
* - Non-interactive (no TTY): uses the default message and proceeds.
|
|
22
|
-
* Skip the whole thing with RELEASE_SKIP_PREP=1.
|
|
12
|
+
* Skip with RELEASE_SKIP_PREP=1 (e.g. a CI-driven release that's
|
|
13
|
+
* already vouched for cleanliness).
|
|
23
14
|
*/
|
|
24
15
|
|
|
25
|
-
import { execSync
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
16
|
+
import { execSync } from "node:child_process";
|
|
17
|
+
import { existsSync } from "node:fs";
|
|
18
|
+
import { join, relative } from "node:path";
|
|
28
19
|
|
|
29
20
|
if (process.env.RELEASE_SKIP_PREP === "1") {
|
|
30
21
|
console.log(" release-prep: RELEASE_SKIP_PREP=1 — skipping.");
|
|
@@ -35,96 +26,63 @@ function sh(cmd, opts = {}) {
|
|
|
35
26
|
return execSync(cmd, { encoding: "utf8", ...opts }).trim();
|
|
36
27
|
}
|
|
37
28
|
|
|
38
|
-
// Find the repo root so git commands work no matter where the script was
|
|
39
|
-
// launched from (pnpm invokes scripts with cwd=cli/).
|
|
40
29
|
let repoRoot;
|
|
41
30
|
try {
|
|
42
31
|
repoRoot = sh("git rev-parse --show-toplevel");
|
|
43
|
-
} catch
|
|
32
|
+
} catch {
|
|
44
33
|
console.error(" release-prep: not inside a git repo. Aborting.");
|
|
45
34
|
process.exit(1);
|
|
46
35
|
}
|
|
47
36
|
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
37
|
+
const repos = [
|
|
38
|
+
{ label: "hatchkit (main)", path: repoRoot, hint: `cd ${repoRoot}` },
|
|
39
|
+
{
|
|
40
|
+
label: "infra (submodule)",
|
|
41
|
+
path: join(repoRoot, "infra"),
|
|
42
|
+
hint: `cd ${join(repoRoot, "infra")}`,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
label: "starter (submodule)",
|
|
46
|
+
path: join(repoRoot, "starter"),
|
|
47
|
+
hint: `cd ${join(repoRoot, "starter")}`,
|
|
48
|
+
},
|
|
49
|
+
];
|
|
61
50
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
const SECRET_RE = /(^|\/)(\.env(\.|$)|[^/]*credentials[^/]*|[^/]*secret[^/]*|[^/]*\.(pem|key|pfx|p12)$)/i;
|
|
68
|
-
const secretsFound = entries.filter((e) => SECRET_RE.test(e.path));
|
|
69
|
-
if (secretsFound.length > 0) {
|
|
70
|
-
console.error("\n release-prep: refusing — these paths look like secrets:\n");
|
|
71
|
-
for (const e of secretsFound) console.error(` ${e.path}`);
|
|
72
|
-
console.error(
|
|
73
|
-
"\n Resolve manually: gitignore, delete, or commit yourself and re-run the release.",
|
|
74
|
-
);
|
|
75
|
-
process.exit(1);
|
|
76
|
-
}
|
|
51
|
+
const dirty = [];
|
|
52
|
+
for (const r of repos) {
|
|
53
|
+
// Submodule may not be initialized — skip silently rather than fail.
|
|
54
|
+
// (`.git` is a file inside an initialized submodule, a dir at the root.)
|
|
55
|
+
if (!existsSync(join(r.path, ".git"))) continue;
|
|
77
56
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
console.error(` ${e.path} — commit inside the submodule first`);
|
|
57
|
+
let status;
|
|
58
|
+
try {
|
|
59
|
+
status = sh("git status --porcelain", { cwd: r.path });
|
|
60
|
+
} catch (err) {
|
|
61
|
+
console.error(` release-prep: couldn't read ${r.label}: ${err.message}`);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
if (status) {
|
|
65
|
+
dirty.push({ ...r, status });
|
|
88
66
|
}
|
|
89
|
-
console.error(
|
|
90
|
-
"\n cd into each submodule, commit, then `git add <submodule>` + re-run the release.",
|
|
91
|
-
);
|
|
92
|
-
process.exit(1);
|
|
93
67
|
}
|
|
94
68
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (input.isTTY && output.isTTY) {
|
|
99
|
-
const rl = createInterface({ input, output });
|
|
100
|
-
message = await new Promise((resolve) => {
|
|
101
|
-
rl.question(` Commit message [${DEFAULT_MSG}]: `, (answer) => {
|
|
102
|
-
rl.close();
|
|
103
|
-
resolve(answer.trim() || DEFAULT_MSG);
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
} else {
|
|
107
|
-
console.log(` release-prep: non-interactive — using default message "${DEFAULT_MSG}".`);
|
|
69
|
+
if (dirty.length === 0) {
|
|
70
|
+
console.log(" ✓ release-prep: all trees clean. Continuing release.");
|
|
71
|
+
process.exit(0);
|
|
108
72
|
}
|
|
109
73
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
74
|
+
console.error("\n ✗ release-prep: cannot release — dangling changes.\n");
|
|
75
|
+
for (const r of dirty) {
|
|
76
|
+
const rel = r.path === repoRoot ? "." : relative(repoRoot, r.path);
|
|
77
|
+
console.error(` ── ${r.label} (${rel}/)`);
|
|
78
|
+
for (const line of r.status.split("\n")) {
|
|
79
|
+
console.error(` ${line}`);
|
|
80
|
+
}
|
|
81
|
+
console.error();
|
|
117
82
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
stdio: "inherit",
|
|
122
|
-
});
|
|
123
|
-
if (commit.status !== 0) {
|
|
124
|
-
console.error(
|
|
125
|
-
" release-prep: `git commit` failed (pre-commit hook? empty diff after filters?).",
|
|
126
|
-
);
|
|
127
|
-
process.exit(commit.status ?? 1);
|
|
83
|
+
console.error(" Handle each tree before releasing:");
|
|
84
|
+
for (const r of dirty) {
|
|
85
|
+
console.error(` ${r.hint} && git status # commit / stash / discard`);
|
|
128
86
|
}
|
|
129
|
-
|
|
130
|
-
|
|
87
|
+
console.error("\n Then re-run the release.\n");
|
|
88
|
+
process.exit(1);
|