@bensandee/tooling 0.31.0 → 0.33.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/README.md +36 -8
- package/dist/bin.mjs +260 -246
- package/dist/{check-DMDdHanG.mjs → check-B2AAPCBO.mjs} +18 -10
- package/dist/docker-check/index.mjs +1 -1
- package/package.json +8 -10
package/README.md
CHANGED
|
@@ -63,11 +63,37 @@ The generated `ci:check` script defaults to `pnpm check --skip 'docker:*'` since
|
|
|
63
63
|
| Command | Description |
|
|
64
64
|
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
65
65
|
| `tooling release:changesets` | Changesets version/publish for Forgejo CI. Flag: `--dry-run`. Env: `FORGEJO_SERVER_URL`, `FORGEJO_REPOSITORY`, `RELEASE_TOKEN`. |
|
|
66
|
-
| `tooling release:simple` | Streamlined release using commit-and-tag-version.
|
|
66
|
+
| `tooling release:simple` | Streamlined release using commit-and-tag-version. Flags: `--release-as`, `--first-release`, `--prerelease`. |
|
|
67
67
|
| `tooling release:trigger` | Trigger a release workflow. |
|
|
68
68
|
| `tooling forgejo:create-release` | Create a Forgejo release from a tag. |
|
|
69
69
|
| `tooling changesets:merge` | Merge a changesets version PR. |
|
|
70
70
|
|
|
71
|
+
#### `release:simple`
|
|
72
|
+
|
|
73
|
+
Uses `commit-and-tag-version` under the hood. Version bumps are auto-detected from [Conventional Commits](https://www.conventionalcommits.org/):
|
|
74
|
+
|
|
75
|
+
| Commit prefix | Bump | Example |
|
|
76
|
+
| ----------------------- | ----- | ------------------------------------ |
|
|
77
|
+
| `fix:` | patch | `fix: handle null response` |
|
|
78
|
+
| `feat:` | minor | `feat: add retry logic` |
|
|
79
|
+
| `feat!:` / `fix!:` etc | major | `feat!: drop v1 API` |
|
|
80
|
+
| `BREAKING CHANGE:` body | major | Any type with breaking change footer |
|
|
81
|
+
|
|
82
|
+
Override auto-detection with CLI flags:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Force a major bump
|
|
86
|
+
tooling release:simple --release-as major
|
|
87
|
+
|
|
88
|
+
# Force a specific version
|
|
89
|
+
tooling release:simple --release-as 2.0.0
|
|
90
|
+
|
|
91
|
+
# Create a prerelease
|
|
92
|
+
tooling release:simple --release-as major --prerelease beta # → 2.0.0-beta.0
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The generated release workflow exposes these as optional `workflow_dispatch` inputs (`bump` and `prerelease`), so bumps can also be controlled from the CI UI.
|
|
96
|
+
|
|
71
97
|
### Docker
|
|
72
98
|
|
|
73
99
|
| Command | Description |
|
|
@@ -133,14 +159,16 @@ Each package is tagged independently using its own version, so packages in a mon
|
|
|
133
159
|
|
|
134
160
|
**Flags:** `--dry-run` (build and tag only, skip login/push/logout)
|
|
135
161
|
|
|
136
|
-
**Required
|
|
162
|
+
**Required CI variables:**
|
|
163
|
+
|
|
164
|
+
| Variable | Type | Description |
|
|
165
|
+
| --------------------------- | -------- | --------------------------------------------------------------------- |
|
|
166
|
+
| `DOCKER_REGISTRY_HOST` | variable | Registry hostname (e.g. `code.orangebikelabs.com`) |
|
|
167
|
+
| `DOCKER_REGISTRY_NAMESPACE` | variable | Full namespace for tagging (e.g. `code.orangebikelabs.com/bensandee`) |
|
|
168
|
+
| `DOCKER_USERNAME` | secret | Registry username |
|
|
169
|
+
| `DOCKER_PASSWORD` | secret | Registry password |
|
|
137
170
|
|
|
138
|
-
|
|
139
|
-
| --------------------------- | --------------------------------------------------------------------- |
|
|
140
|
-
| `DOCKER_REGISTRY_HOST` | Registry hostname (e.g. `code.orangebikelabs.com`) |
|
|
141
|
-
| `DOCKER_REGISTRY_NAMESPACE` | Full namespace for tagging (e.g. `code.orangebikelabs.com/bensandee`) |
|
|
142
|
-
| `DOCKER_USERNAME` | Registry username |
|
|
143
|
-
| `DOCKER_PASSWORD` | Registry password |
|
|
171
|
+
**Forgejo setup:** On Forgejo, `DOCKER_USERNAME` is your Forgejo account username, and `DOCKER_PASSWORD` can reuse the same token as `RELEASE_TOKEN`. The token needs write permissions on the org, package, and repository scopes. These permissions should be set for the **user** if the package is under a user namespace (e.g. `bensandee`), or the **organization** if it's under an org namespace (e.g. `orangebikelabs`).
|
|
144
172
|
|
|
145
173
|
## Config file
|
|
146
174
|
|
package/dist/bin.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { l as createRealExecutor$1, t as runDockerCheck, u as isExecSyncError } from "./check-
|
|
2
|
+
import { l as createRealExecutor$1, t as runDockerCheck, u as isExecSyncError } from "./check-B2AAPCBO.mjs";
|
|
3
3
|
import { defineCommand, runMain } from "citty";
|
|
4
4
|
import * as clack from "@clack/prompts";
|
|
5
5
|
import { isCancel, select } from "@clack/prompts";
|
|
@@ -970,10 +970,6 @@ function runDockerPublish(executor, config) {
|
|
|
970
970
|
//#region src/utils/yaml-merge.ts
|
|
971
971
|
const IGNORE_PATTERN = "@bensandee/tooling:ignore";
|
|
972
972
|
const FORGEJO_SCHEMA_COMMENT = "# yaml-language-server: $schema=../../.vscode/forgejo-workflow.schema.json\n";
|
|
973
|
-
/** Returns a yaml-language-server schema comment for Forgejo workflows, empty string otherwise. */
|
|
974
|
-
function workflowSchemaComment(ci) {
|
|
975
|
-
return ci === "forgejo" ? FORGEJO_SCHEMA_COMMENT : "";
|
|
976
|
-
}
|
|
977
973
|
/** Prepend the Forgejo schema comment if it's not already present. No-op for GitHub. */
|
|
978
974
|
function ensureSchemaComment(content, ci) {
|
|
979
975
|
if (ci !== "forgejo") return content;
|
|
@@ -1028,6 +1024,29 @@ function mergeLefthookCommands(existing, requiredCommands) {
|
|
|
1028
1024
|
};
|
|
1029
1025
|
}
|
|
1030
1026
|
}
|
|
1027
|
+
/** Extract only the mergeable steps (those with a match) as RequiredSteps. */
|
|
1028
|
+
function toRequiredSteps(steps) {
|
|
1029
|
+
return steps.filter((s) => s.match !== void 0).map((s) => ({
|
|
1030
|
+
match: s.match,
|
|
1031
|
+
step: s.step
|
|
1032
|
+
}));
|
|
1033
|
+
}
|
|
1034
|
+
/** Build a complete workflow YAML string from structured options. Single source of truth for both new files and merge steps. */
|
|
1035
|
+
function buildWorkflowYaml(options) {
|
|
1036
|
+
const doc = { name: options.name };
|
|
1037
|
+
if (options.enableEmailNotifications) doc["enable-email-notifications"] = true;
|
|
1038
|
+
doc["on"] = options.on;
|
|
1039
|
+
if (options.permissions) doc["permissions"] = options.permissions;
|
|
1040
|
+
if (options.concurrency) doc["concurrency"] = options.concurrency;
|
|
1041
|
+
doc["jobs"] = { [options.jobName]: {
|
|
1042
|
+
"runs-on": options.runsOn ?? "ubuntu-latest",
|
|
1043
|
+
steps: options.steps.map((s) => s.step)
|
|
1044
|
+
} };
|
|
1045
|
+
return ensureSchemaComment(stringify(doc, {
|
|
1046
|
+
lineWidth: 0,
|
|
1047
|
+
nullStr: ""
|
|
1048
|
+
}), options.ci);
|
|
1049
|
+
}
|
|
1031
1050
|
/**
|
|
1032
1051
|
* Ensure required steps exist in a workflow job's steps array.
|
|
1033
1052
|
* Only adds missing steps at the end — never modifies existing ones.
|
|
@@ -1169,43 +1188,22 @@ function computeNodeVersionYaml(ctx) {
|
|
|
1169
1188
|
}
|
|
1170
1189
|
//#endregion
|
|
1171
1190
|
//#region src/generators/publish-ci.ts
|
|
1172
|
-
function
|
|
1173
|
-
return `${workflowSchemaComment(ci)}name: Publish
|
|
1174
|
-
on:
|
|
1175
|
-
workflow_dispatch:
|
|
1176
|
-
|
|
1177
|
-
jobs:
|
|
1178
|
-
publish:
|
|
1179
|
-
runs-on: ubuntu-latest
|
|
1180
|
-
steps:
|
|
1181
|
-
- uses: actions/checkout@v4
|
|
1182
|
-
- uses: pnpm/action-setup@v4
|
|
1183
|
-
- uses: actions/setup-node@v4
|
|
1184
|
-
with:
|
|
1185
|
-
${nodeVersionYaml}
|
|
1186
|
-
- run: pnpm install --frozen-lockfile
|
|
1187
|
-
- name: Publish Docker images
|
|
1188
|
-
env:
|
|
1189
|
-
DOCKER_REGISTRY_HOST: ${actionsExpr("vars.DOCKER_REGISTRY_HOST")}
|
|
1190
|
-
DOCKER_REGISTRY_NAMESPACE: ${actionsExpr("vars.DOCKER_REGISTRY_NAMESPACE")}
|
|
1191
|
-
DOCKER_USERNAME: ${actionsExpr("secrets.DOCKER_USERNAME")}
|
|
1192
|
-
DOCKER_PASSWORD: ${actionsExpr("secrets.DOCKER_PASSWORD")}
|
|
1193
|
-
run: pnpm exec bst docker:publish
|
|
1194
|
-
`;
|
|
1195
|
-
}
|
|
1196
|
-
function requiredDeploySteps() {
|
|
1191
|
+
function publishSteps(nodeVersionYaml) {
|
|
1197
1192
|
return [
|
|
1198
1193
|
{
|
|
1199
1194
|
match: { uses: "actions/checkout" },
|
|
1200
|
-
step: { uses: "actions/checkout@
|
|
1195
|
+
step: { uses: "actions/checkout@v6" }
|
|
1201
1196
|
},
|
|
1202
1197
|
{
|
|
1203
1198
|
match: { uses: "pnpm/action-setup" },
|
|
1204
|
-
step: { uses: "pnpm/action-setup@
|
|
1199
|
+
step: { uses: "pnpm/action-setup@v5" }
|
|
1205
1200
|
},
|
|
1206
1201
|
{
|
|
1207
1202
|
match: { uses: "actions/setup-node" },
|
|
1208
|
-
step: {
|
|
1203
|
+
step: {
|
|
1204
|
+
uses: "actions/setup-node@v6",
|
|
1205
|
+
with: nodeVersionYaml.startsWith("node-version-file") ? { "node-version-file": "package.json" } : { "node-version": "24" }
|
|
1206
|
+
}
|
|
1209
1207
|
},
|
|
1210
1208
|
{
|
|
1211
1209
|
match: { run: "pnpm install" },
|
|
@@ -1213,7 +1211,16 @@ function requiredDeploySteps() {
|
|
|
1213
1211
|
},
|
|
1214
1212
|
{
|
|
1215
1213
|
match: { run: "docker:publish" },
|
|
1216
|
-
step: {
|
|
1214
|
+
step: {
|
|
1215
|
+
name: "Publish Docker images",
|
|
1216
|
+
env: {
|
|
1217
|
+
DOCKER_REGISTRY_HOST: actionsExpr("vars.DOCKER_REGISTRY_HOST"),
|
|
1218
|
+
DOCKER_REGISTRY_NAMESPACE: actionsExpr("vars.DOCKER_REGISTRY_NAMESPACE"),
|
|
1219
|
+
DOCKER_USERNAME: actionsExpr("secrets.DOCKER_USERNAME"),
|
|
1220
|
+
DOCKER_PASSWORD: actionsExpr("secrets.DOCKER_PASSWORD")
|
|
1221
|
+
},
|
|
1222
|
+
run: "pnpm exec bst docker:publish"
|
|
1223
|
+
}
|
|
1217
1224
|
}
|
|
1218
1225
|
];
|
|
1219
1226
|
}
|
|
@@ -1255,8 +1262,14 @@ async function generateDeployCi(ctx) {
|
|
|
1255
1262
|
};
|
|
1256
1263
|
const isGitHub = ctx.config.ci === "github";
|
|
1257
1264
|
const workflowPath = isGitHub ? ".github/workflows/publish.yml" : ".forgejo/workflows/publish.yml";
|
|
1258
|
-
const
|
|
1259
|
-
const content =
|
|
1265
|
+
const steps = publishSteps(computeNodeVersionYaml(ctx));
|
|
1266
|
+
const content = buildWorkflowYaml({
|
|
1267
|
+
ci: ctx.config.ci,
|
|
1268
|
+
name: "Publish",
|
|
1269
|
+
on: { workflow_dispatch: null },
|
|
1270
|
+
jobName: "publish",
|
|
1271
|
+
steps
|
|
1272
|
+
});
|
|
1260
1273
|
if (ctx.exists(workflowPath)) {
|
|
1261
1274
|
const raw = ctx.read(workflowPath);
|
|
1262
1275
|
if (raw) {
|
|
@@ -1276,7 +1289,7 @@ async function generateDeployCi(ctx) {
|
|
|
1276
1289
|
description: "Publish workflow already up to date"
|
|
1277
1290
|
};
|
|
1278
1291
|
}
|
|
1279
|
-
const merged = mergeWorkflowSteps(existing, "publish",
|
|
1292
|
+
const merged = mergeWorkflowSteps(existing, "publish", toRequiredSteps(steps));
|
|
1280
1293
|
const withComment = ensureSchemaComment(merged.content, ctx.config.ci);
|
|
1281
1294
|
if (!merged.changed) {
|
|
1282
1295
|
if (withComment !== raw) {
|
|
@@ -1362,6 +1375,7 @@ const PER_PACKAGE_DEV_DEPS = {
|
|
|
1362
1375
|
"@types/node": {
|
|
1363
1376
|
"@changesets/cli": "2.30.0",
|
|
1364
1377
|
"@release-it/bumper": "7.0.5",
|
|
1378
|
+
"@types/node": "24.12.0",
|
|
1365
1379
|
"commit-and-tag-version": "12.7.0",
|
|
1366
1380
|
"knip": "5.87.0",
|
|
1367
1381
|
"lefthook": "2.1.4",
|
|
@@ -1369,7 +1383,6 @@ const PER_PACKAGE_DEV_DEPS = {
|
|
|
1369
1383
|
"oxlint": "1.56.0",
|
|
1370
1384
|
"prettier": "3.8.1",
|
|
1371
1385
|
"release-it": "19.2.4",
|
|
1372
|
-
"@types/node": "24.12.0",
|
|
1373
1386
|
"tsdown": "0.21.4",
|
|
1374
1387
|
"typescript": "5.9.3",
|
|
1375
1388
|
"vitest": "4.1.0",
|
|
@@ -1378,6 +1391,7 @@ const PER_PACKAGE_DEV_DEPS = {
|
|
|
1378
1391
|
tsdown: {
|
|
1379
1392
|
"@changesets/cli": "2.30.0",
|
|
1380
1393
|
"@release-it/bumper": "7.0.5",
|
|
1394
|
+
"@types/node": "24.12.0",
|
|
1381
1395
|
"commit-and-tag-version": "12.7.0",
|
|
1382
1396
|
"knip": "5.87.0",
|
|
1383
1397
|
"lefthook": "2.1.4",
|
|
@@ -1385,7 +1399,6 @@ const PER_PACKAGE_DEV_DEPS = {
|
|
|
1385
1399
|
"oxlint": "1.56.0",
|
|
1386
1400
|
"prettier": "3.8.1",
|
|
1387
1401
|
"release-it": "19.2.4",
|
|
1388
|
-
"@types/node": "24.12.0",
|
|
1389
1402
|
"tsdown": "0.21.4",
|
|
1390
1403
|
"typescript": "5.9.3",
|
|
1391
1404
|
"vitest": "4.1.0",
|
|
@@ -1394,6 +1407,7 @@ const PER_PACKAGE_DEV_DEPS = {
|
|
|
1394
1407
|
typescript: {
|
|
1395
1408
|
"@changesets/cli": "2.30.0",
|
|
1396
1409
|
"@release-it/bumper": "7.0.5",
|
|
1410
|
+
"@types/node": "24.12.0",
|
|
1397
1411
|
"commit-and-tag-version": "12.7.0",
|
|
1398
1412
|
"knip": "5.87.0",
|
|
1399
1413
|
"lefthook": "2.1.4",
|
|
@@ -1401,7 +1415,6 @@ const PER_PACKAGE_DEV_DEPS = {
|
|
|
1401
1415
|
"oxlint": "1.56.0",
|
|
1402
1416
|
"prettier": "3.8.1",
|
|
1403
1417
|
"release-it": "19.2.4",
|
|
1404
|
-
"@types/node": "24.12.0",
|
|
1405
1418
|
"tsdown": "0.21.4",
|
|
1406
1419
|
"typescript": "5.9.3",
|
|
1407
1420
|
"vitest": "4.1.0",
|
|
@@ -1410,6 +1423,7 @@ const PER_PACKAGE_DEV_DEPS = {
|
|
|
1410
1423
|
vitest: {
|
|
1411
1424
|
"@changesets/cli": "2.30.0",
|
|
1412
1425
|
"@release-it/bumper": "7.0.5",
|
|
1426
|
+
"@types/node": "24.12.0",
|
|
1413
1427
|
"commit-and-tag-version": "12.7.0",
|
|
1414
1428
|
"knip": "5.87.0",
|
|
1415
1429
|
"lefthook": "2.1.4",
|
|
@@ -1417,7 +1431,6 @@ const PER_PACKAGE_DEV_DEPS = {
|
|
|
1417
1431
|
"oxlint": "1.56.0",
|
|
1418
1432
|
"prettier": "3.8.1",
|
|
1419
1433
|
"release-it": "19.2.4",
|
|
1420
|
-
"@types/node": "24.12.0",
|
|
1421
1434
|
"tsdown": "0.21.4",
|
|
1422
1435
|
"typescript": "5.9.3",
|
|
1423
1436
|
"vitest": "4.1.0",
|
|
@@ -1429,6 +1442,7 @@ const ROOT_DEV_DEPS = {
|
|
|
1429
1442
|
knip: {
|
|
1430
1443
|
"@changesets/cli": "2.30.0",
|
|
1431
1444
|
"@release-it/bumper": "7.0.5",
|
|
1445
|
+
"@types/node": "24.12.0",
|
|
1432
1446
|
"commit-and-tag-version": "12.7.0",
|
|
1433
1447
|
"knip": "5.87.0",
|
|
1434
1448
|
"lefthook": "2.1.4",
|
|
@@ -1436,7 +1450,6 @@ const ROOT_DEV_DEPS = {
|
|
|
1436
1450
|
"oxlint": "1.56.0",
|
|
1437
1451
|
"prettier": "3.8.1",
|
|
1438
1452
|
"release-it": "19.2.4",
|
|
1439
|
-
"@types/node": "24.12.0",
|
|
1440
1453
|
"tsdown": "0.21.4",
|
|
1441
1454
|
"typescript": "5.9.3",
|
|
1442
1455
|
"vitest": "4.1.0",
|
|
@@ -1445,6 +1458,7 @@ const ROOT_DEV_DEPS = {
|
|
|
1445
1458
|
lefthook: {
|
|
1446
1459
|
"@changesets/cli": "2.30.0",
|
|
1447
1460
|
"@release-it/bumper": "7.0.5",
|
|
1461
|
+
"@types/node": "24.12.0",
|
|
1448
1462
|
"commit-and-tag-version": "12.7.0",
|
|
1449
1463
|
"knip": "5.87.0",
|
|
1450
1464
|
"lefthook": "2.1.4",
|
|
@@ -1452,7 +1466,6 @@ const ROOT_DEV_DEPS = {
|
|
|
1452
1466
|
"oxlint": "1.56.0",
|
|
1453
1467
|
"prettier": "3.8.1",
|
|
1454
1468
|
"release-it": "19.2.4",
|
|
1455
|
-
"@types/node": "24.12.0",
|
|
1456
1469
|
"tsdown": "0.21.4",
|
|
1457
1470
|
"typescript": "5.9.3",
|
|
1458
1471
|
"vitest": "4.1.0",
|
|
@@ -1461,6 +1474,7 @@ const ROOT_DEV_DEPS = {
|
|
|
1461
1474
|
oxlint: {
|
|
1462
1475
|
"@changesets/cli": "2.30.0",
|
|
1463
1476
|
"@release-it/bumper": "7.0.5",
|
|
1477
|
+
"@types/node": "24.12.0",
|
|
1464
1478
|
"commit-and-tag-version": "12.7.0",
|
|
1465
1479
|
"knip": "5.87.0",
|
|
1466
1480
|
"lefthook": "2.1.4",
|
|
@@ -1468,7 +1482,6 @@ const ROOT_DEV_DEPS = {
|
|
|
1468
1482
|
"oxlint": "1.56.0",
|
|
1469
1483
|
"prettier": "3.8.1",
|
|
1470
1484
|
"release-it": "19.2.4",
|
|
1471
|
-
"@types/node": "24.12.0",
|
|
1472
1485
|
"tsdown": "0.21.4",
|
|
1473
1486
|
"typescript": "5.9.3",
|
|
1474
1487
|
"vitest": "4.1.0",
|
|
@@ -1504,6 +1517,7 @@ function addReleaseDeps(deps, config) {
|
|
|
1504
1517
|
deps["release-it"] = {
|
|
1505
1518
|
"@changesets/cli": "2.30.0",
|
|
1506
1519
|
"@release-it/bumper": "7.0.5",
|
|
1520
|
+
"@types/node": "24.12.0",
|
|
1507
1521
|
"commit-and-tag-version": "12.7.0",
|
|
1508
1522
|
"knip": "5.87.0",
|
|
1509
1523
|
"lefthook": "2.1.4",
|
|
@@ -1511,7 +1525,6 @@ function addReleaseDeps(deps, config) {
|
|
|
1511
1525
|
"oxlint": "1.56.0",
|
|
1512
1526
|
"prettier": "3.8.1",
|
|
1513
1527
|
"release-it": "19.2.4",
|
|
1514
|
-
"@types/node": "24.12.0",
|
|
1515
1528
|
"tsdown": "0.21.4",
|
|
1516
1529
|
"typescript": "5.9.3",
|
|
1517
1530
|
"vitest": "4.1.0",
|
|
@@ -1520,6 +1533,7 @@ function addReleaseDeps(deps, config) {
|
|
|
1520
1533
|
if (config.structure === "monorepo") deps["@release-it/bumper"] = {
|
|
1521
1534
|
"@changesets/cli": "2.30.0",
|
|
1522
1535
|
"@release-it/bumper": "7.0.5",
|
|
1536
|
+
"@types/node": "24.12.0",
|
|
1523
1537
|
"commit-and-tag-version": "12.7.0",
|
|
1524
1538
|
"knip": "5.87.0",
|
|
1525
1539
|
"lefthook": "2.1.4",
|
|
@@ -1527,7 +1541,6 @@ function addReleaseDeps(deps, config) {
|
|
|
1527
1541
|
"oxlint": "1.56.0",
|
|
1528
1542
|
"prettier": "3.8.1",
|
|
1529
1543
|
"release-it": "19.2.4",
|
|
1530
|
-
"@types/node": "24.12.0",
|
|
1531
1544
|
"tsdown": "0.21.4",
|
|
1532
1545
|
"typescript": "5.9.3",
|
|
1533
1546
|
"vitest": "4.1.0",
|
|
@@ -1538,6 +1551,7 @@ function addReleaseDeps(deps, config) {
|
|
|
1538
1551
|
deps["commit-and-tag-version"] = {
|
|
1539
1552
|
"@changesets/cli": "2.30.0",
|
|
1540
1553
|
"@release-it/bumper": "7.0.5",
|
|
1554
|
+
"@types/node": "24.12.0",
|
|
1541
1555
|
"commit-and-tag-version": "12.7.0",
|
|
1542
1556
|
"knip": "5.87.0",
|
|
1543
1557
|
"lefthook": "2.1.4",
|
|
@@ -1545,7 +1559,6 @@ function addReleaseDeps(deps, config) {
|
|
|
1545
1559
|
"oxlint": "1.56.0",
|
|
1546
1560
|
"prettier": "3.8.1",
|
|
1547
1561
|
"release-it": "19.2.4",
|
|
1548
|
-
"@types/node": "24.12.0",
|
|
1549
1562
|
"tsdown": "0.21.4",
|
|
1550
1563
|
"typescript": "5.9.3",
|
|
1551
1564
|
"vitest": "4.1.0",
|
|
@@ -1556,6 +1569,7 @@ function addReleaseDeps(deps, config) {
|
|
|
1556
1569
|
deps["@changesets/cli"] = {
|
|
1557
1570
|
"@changesets/cli": "2.30.0",
|
|
1558
1571
|
"@release-it/bumper": "7.0.5",
|
|
1572
|
+
"@types/node": "24.12.0",
|
|
1559
1573
|
"commit-and-tag-version": "12.7.0",
|
|
1560
1574
|
"knip": "5.87.0",
|
|
1561
1575
|
"lefthook": "2.1.4",
|
|
@@ -1563,7 +1577,6 @@ function addReleaseDeps(deps, config) {
|
|
|
1563
1577
|
"oxlint": "1.56.0",
|
|
1564
1578
|
"prettier": "3.8.1",
|
|
1565
1579
|
"release-it": "19.2.4",
|
|
1566
|
-
"@types/node": "24.12.0",
|
|
1567
1580
|
"tsdown": "0.21.4",
|
|
1568
1581
|
"typescript": "5.9.3",
|
|
1569
1582
|
"vitest": "4.1.0",
|
|
@@ -1577,10 +1590,11 @@ function getAddedDevDepNames(config) {
|
|
|
1577
1590
|
const deps = { ...ROOT_DEV_DEPS };
|
|
1578
1591
|
if (config.structure !== "monorepo") Object.assign(deps, PER_PACKAGE_DEV_DEPS);
|
|
1579
1592
|
deps["@bensandee/config"] = "0.9.1";
|
|
1580
|
-
deps["@bensandee/tooling"] = "0.
|
|
1593
|
+
deps["@bensandee/tooling"] = "0.33.0";
|
|
1581
1594
|
if (config.formatter === "oxfmt") deps["oxfmt"] = {
|
|
1582
1595
|
"@changesets/cli": "2.30.0",
|
|
1583
1596
|
"@release-it/bumper": "7.0.5",
|
|
1597
|
+
"@types/node": "24.12.0",
|
|
1584
1598
|
"commit-and-tag-version": "12.7.0",
|
|
1585
1599
|
"knip": "5.87.0",
|
|
1586
1600
|
"lefthook": "2.1.4",
|
|
@@ -1588,7 +1602,6 @@ function getAddedDevDepNames(config) {
|
|
|
1588
1602
|
"oxlint": "1.56.0",
|
|
1589
1603
|
"prettier": "3.8.1",
|
|
1590
1604
|
"release-it": "19.2.4",
|
|
1591
|
-
"@types/node": "24.12.0",
|
|
1592
1605
|
"tsdown": "0.21.4",
|
|
1593
1606
|
"typescript": "5.9.3",
|
|
1594
1607
|
"vitest": "4.1.0",
|
|
@@ -1597,6 +1610,7 @@ function getAddedDevDepNames(config) {
|
|
|
1597
1610
|
if (config.formatter === "prettier") deps["prettier"] = {
|
|
1598
1611
|
"@changesets/cli": "2.30.0",
|
|
1599
1612
|
"@release-it/bumper": "7.0.5",
|
|
1613
|
+
"@types/node": "24.12.0",
|
|
1600
1614
|
"commit-and-tag-version": "12.7.0",
|
|
1601
1615
|
"knip": "5.87.0",
|
|
1602
1616
|
"lefthook": "2.1.4",
|
|
@@ -1604,7 +1618,6 @@ function getAddedDevDepNames(config) {
|
|
|
1604
1618
|
"oxlint": "1.56.0",
|
|
1605
1619
|
"prettier": "3.8.1",
|
|
1606
1620
|
"release-it": "19.2.4",
|
|
1607
|
-
"@types/node": "24.12.0",
|
|
1608
1621
|
"tsdown": "0.21.4",
|
|
1609
1622
|
"typescript": "5.9.3",
|
|
1610
1623
|
"vitest": "4.1.0",
|
|
@@ -1632,11 +1645,12 @@ async function generatePackageJson(ctx) {
|
|
|
1632
1645
|
const devDeps = { ...ROOT_DEV_DEPS };
|
|
1633
1646
|
if (!isMonorepo) Object.assign(devDeps, PER_PACKAGE_DEV_DEPS);
|
|
1634
1647
|
devDeps["@bensandee/config"] = isWorkspacePackage(ctx, "@bensandee/config") ? "workspace:*" : "0.9.1";
|
|
1635
|
-
devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.
|
|
1648
|
+
devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.33.0";
|
|
1636
1649
|
if (ctx.config.useEslintPlugin) devDeps["@bensandee/eslint-plugin"] = isWorkspacePackage(ctx, "@bensandee/eslint-plugin") ? "workspace:*" : "0.9.2";
|
|
1637
1650
|
if (ctx.config.formatter === "oxfmt") devDeps["oxfmt"] = {
|
|
1638
1651
|
"@changesets/cli": "2.30.0",
|
|
1639
1652
|
"@release-it/bumper": "7.0.5",
|
|
1653
|
+
"@types/node": "24.12.0",
|
|
1640
1654
|
"commit-and-tag-version": "12.7.0",
|
|
1641
1655
|
"knip": "5.87.0",
|
|
1642
1656
|
"lefthook": "2.1.4",
|
|
@@ -1644,7 +1658,6 @@ async function generatePackageJson(ctx) {
|
|
|
1644
1658
|
"oxlint": "1.56.0",
|
|
1645
1659
|
"prettier": "3.8.1",
|
|
1646
1660
|
"release-it": "19.2.4",
|
|
1647
|
-
"@types/node": "24.12.0",
|
|
1648
1661
|
"tsdown": "0.21.4",
|
|
1649
1662
|
"typescript": "5.9.3",
|
|
1650
1663
|
"vitest": "4.1.0",
|
|
@@ -1653,6 +1666,7 @@ async function generatePackageJson(ctx) {
|
|
|
1653
1666
|
if (ctx.config.formatter === "prettier") devDeps["prettier"] = {
|
|
1654
1667
|
"@changesets/cli": "2.30.0",
|
|
1655
1668
|
"@release-it/bumper": "7.0.5",
|
|
1669
|
+
"@types/node": "24.12.0",
|
|
1656
1670
|
"commit-and-tag-version": "12.7.0",
|
|
1657
1671
|
"knip": "5.87.0",
|
|
1658
1672
|
"lefthook": "2.1.4",
|
|
@@ -1660,7 +1674,6 @@ async function generatePackageJson(ctx) {
|
|
|
1660
1674
|
"oxlint": "1.56.0",
|
|
1661
1675
|
"prettier": "3.8.1",
|
|
1662
1676
|
"release-it": "19.2.4",
|
|
1663
|
-
"@types/node": "24.12.0",
|
|
1664
1677
|
"tsdown": "0.21.4",
|
|
1665
1678
|
"typescript": "5.9.3",
|
|
1666
1679
|
"vitest": "4.1.0",
|
|
@@ -1741,6 +1754,7 @@ async function generatePackageJson(ctx) {
|
|
|
1741
1754
|
packageManager: `pnpm@${{
|
|
1742
1755
|
"@changesets/cli": "2.30.0",
|
|
1743
1756
|
"@release-it/bumper": "7.0.5",
|
|
1757
|
+
"@types/node": "24.12.0",
|
|
1744
1758
|
"commit-and-tag-version": "12.7.0",
|
|
1745
1759
|
"knip": "5.87.0",
|
|
1746
1760
|
"lefthook": "2.1.4",
|
|
@@ -1748,7 +1762,6 @@ async function generatePackageJson(ctx) {
|
|
|
1748
1762
|
"oxlint": "1.56.0",
|
|
1749
1763
|
"prettier": "3.8.1",
|
|
1750
1764
|
"release-it": "19.2.4",
|
|
1751
|
-
"@types/node": "24.12.0",
|
|
1752
1765
|
"tsdown": "0.21.4",
|
|
1753
1766
|
"typescript": "5.9.3",
|
|
1754
1767
|
"vitest": "4.1.0",
|
|
@@ -2174,50 +2187,20 @@ const CI_CONCURRENCY = {
|
|
|
2174
2187
|
group: `ci-${actionsExpr("github.ref")}`,
|
|
2175
2188
|
"cancel-in-progress": actionsExpr("github.ref != 'refs/heads/main'")
|
|
2176
2189
|
};
|
|
2177
|
-
function
|
|
2178
|
-
const emailNotifications = isForgejo ? "\nenable-email-notifications: true\n" : "";
|
|
2179
|
-
const concurrencyBlock = isChangesets ? `
|
|
2180
|
-
concurrency:
|
|
2181
|
-
group: ci-${actionsExpr("github.ref")}
|
|
2182
|
-
cancel-in-progress: ${actionsExpr("github.ref != 'refs/heads/main'")}
|
|
2183
|
-
` : "";
|
|
2184
|
-
return `${workflowSchemaComment(isForgejo ? "forgejo" : "github")}name: CI
|
|
2185
|
-
${emailNotifications}on:
|
|
2186
|
-
push:
|
|
2187
|
-
branches: [main]
|
|
2188
|
-
tags-ignore:
|
|
2189
|
-
- "**"
|
|
2190
|
-
pull_request:
|
|
2191
|
-
${concurrencyBlock}
|
|
2192
|
-
jobs:
|
|
2193
|
-
check:
|
|
2194
|
-
runs-on: ubuntu-latest
|
|
2195
|
-
steps:
|
|
2196
|
-
- uses: actions/checkout@v4
|
|
2197
|
-
- uses: pnpm/action-setup@v4
|
|
2198
|
-
- uses: actions/setup-node@v4
|
|
2199
|
-
with:
|
|
2200
|
-
${nodeVersionYaml}
|
|
2201
|
-
cache: pnpm
|
|
2202
|
-
- run: pnpm install --frozen-lockfile
|
|
2203
|
-
- name: Run all checks
|
|
2204
|
-
run: pnpm ci:check
|
|
2205
|
-
`;
|
|
2206
|
-
}
|
|
2207
|
-
function requiredCheckSteps(nodeVersionYaml) {
|
|
2190
|
+
function checkSteps(nodeVersionYaml) {
|
|
2208
2191
|
return [
|
|
2209
2192
|
{
|
|
2210
2193
|
match: { uses: "actions/checkout" },
|
|
2211
|
-
step: { uses: "actions/checkout@
|
|
2194
|
+
step: { uses: "actions/checkout@v6" }
|
|
2212
2195
|
},
|
|
2213
2196
|
{
|
|
2214
2197
|
match: { uses: "pnpm/action-setup" },
|
|
2215
|
-
step: { uses: "pnpm/action-setup@
|
|
2198
|
+
step: { uses: "pnpm/action-setup@v5" }
|
|
2216
2199
|
},
|
|
2217
2200
|
{
|
|
2218
2201
|
match: { uses: "actions/setup-node" },
|
|
2219
2202
|
step: {
|
|
2220
|
-
uses: "actions/setup-node@
|
|
2203
|
+
uses: "actions/setup-node@v6",
|
|
2221
2204
|
with: {
|
|
2222
2205
|
...nodeVersionYaml.startsWith("node-version-file") ? { "node-version-file": "package.json" } : { "node-version": "24" },
|
|
2223
2206
|
cache: "pnpm"
|
|
@@ -2248,14 +2231,29 @@ async function generateCi(ctx) {
|
|
|
2248
2231
|
description: "CI workflow not requested"
|
|
2249
2232
|
};
|
|
2250
2233
|
const isGitHub = ctx.config.ci === "github";
|
|
2234
|
+
const isForgejo = !isGitHub;
|
|
2251
2235
|
const isChangesets = ctx.config.releaseStrategy === "changesets";
|
|
2252
|
-
const
|
|
2236
|
+
const steps = checkSteps(computeNodeVersionYaml(ctx));
|
|
2237
|
+
const content = buildWorkflowYaml({
|
|
2238
|
+
ci: ctx.config.ci,
|
|
2239
|
+
name: "CI",
|
|
2240
|
+
on: {
|
|
2241
|
+
push: {
|
|
2242
|
+
branches: ["main"],
|
|
2243
|
+
"tags-ignore": ["**"]
|
|
2244
|
+
},
|
|
2245
|
+
pull_request: null
|
|
2246
|
+
},
|
|
2247
|
+
enableEmailNotifications: isForgejo,
|
|
2248
|
+
...isChangesets && { concurrency: CI_CONCURRENCY },
|
|
2249
|
+
jobName: "check",
|
|
2250
|
+
steps
|
|
2251
|
+
});
|
|
2253
2252
|
const filePath = ciWorkflowPath(ctx.config.ci, ctx.config.releaseStrategy);
|
|
2254
|
-
const content = ciWorkflow(nodeVersionYaml, !isGitHub, isChangesets);
|
|
2255
2253
|
if (ctx.exists(filePath)) {
|
|
2256
2254
|
const existing = ctx.read(filePath);
|
|
2257
2255
|
if (existing) {
|
|
2258
|
-
let result = mergeWorkflowSteps(existing, "check",
|
|
2256
|
+
let result = mergeWorkflowSteps(existing, "check", toRequiredSteps(steps));
|
|
2259
2257
|
const withTagsIgnore = ensureWorkflowTagsIgnore(result.content);
|
|
2260
2258
|
result = {
|
|
2261
2259
|
content: withTagsIgnore.content,
|
|
@@ -2760,83 +2758,119 @@ async function generateChangesets(ctx) {
|
|
|
2760
2758
|
}
|
|
2761
2759
|
//#endregion
|
|
2762
2760
|
//#region src/generators/release-ci.ts
|
|
2761
|
+
/** Common setup steps shared by release-it and simple strategies. */
|
|
2763
2762
|
function commonSteps(nodeVersionYaml, publishesNpm) {
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
${tokenEnv}${npmEnv}
|
|
2794
|
-
`;
|
|
2763
|
+
const isNodeVersionFile = nodeVersionYaml.startsWith("node-version-file");
|
|
2764
|
+
return [
|
|
2765
|
+
{
|
|
2766
|
+
match: { uses: "actions/checkout" },
|
|
2767
|
+
step: {
|
|
2768
|
+
uses: "actions/checkout@v6",
|
|
2769
|
+
with: { "fetch-depth": 0 }
|
|
2770
|
+
}
|
|
2771
|
+
},
|
|
2772
|
+
{
|
|
2773
|
+
match: { uses: "pnpm/action-setup" },
|
|
2774
|
+
step: { uses: "pnpm/action-setup@v5" }
|
|
2775
|
+
},
|
|
2776
|
+
{
|
|
2777
|
+
match: { uses: "actions/setup-node" },
|
|
2778
|
+
step: {
|
|
2779
|
+
uses: "actions/setup-node@v6",
|
|
2780
|
+
with: {
|
|
2781
|
+
...isNodeVersionFile ? { "node-version-file": "package.json" } : { "node-version": "24" },
|
|
2782
|
+
cache: "pnpm",
|
|
2783
|
+
...publishesNpm && { "registry-url": "https://registry.npmjs.org" }
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
},
|
|
2787
|
+
{
|
|
2788
|
+
match: { run: "pnpm install" },
|
|
2789
|
+
step: { run: "pnpm install --frozen-lockfile" }
|
|
2790
|
+
}
|
|
2791
|
+
];
|
|
2795
2792
|
}
|
|
2796
|
-
function
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
}
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
$
|
|
2839
|
-
|
|
2793
|
+
function releaseItSteps(ci, nodeVersionYaml, publishesNpm) {
|
|
2794
|
+
const tokenEnv = ci === "github" ? { GITHUB_TOKEN: actionsExpr("github.token") } : { RELEASE_TOKEN: actionsExpr("secrets.RELEASE_TOKEN") };
|
|
2795
|
+
const npmEnv = publishesNpm ? { NODE_AUTH_TOKEN: actionsExpr("secrets.NPM_TOKEN") } : {};
|
|
2796
|
+
return [...commonSteps(nodeVersionYaml, publishesNpm), {
|
|
2797
|
+
match: { run: "release-it" },
|
|
2798
|
+
step: {
|
|
2799
|
+
run: "pnpm release-it --ci",
|
|
2800
|
+
env: {
|
|
2801
|
+
...tokenEnv,
|
|
2802
|
+
...npmEnv
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
}];
|
|
2806
|
+
}
|
|
2807
|
+
/** Build the workflow_dispatch trigger with optional inputs for the simple strategy. */
|
|
2808
|
+
function simpleWorkflowDispatchTrigger() {
|
|
2809
|
+
return { workflow_dispatch: { inputs: {
|
|
2810
|
+
bump: {
|
|
2811
|
+
description: "Version bump type (default: conventional-commits auto-detect)",
|
|
2812
|
+
required: false,
|
|
2813
|
+
type: "choice",
|
|
2814
|
+
default: "auto",
|
|
2815
|
+
options: [
|
|
2816
|
+
"auto",
|
|
2817
|
+
"major",
|
|
2818
|
+
"minor",
|
|
2819
|
+
"patch",
|
|
2820
|
+
"first-release"
|
|
2821
|
+
]
|
|
2822
|
+
},
|
|
2823
|
+
prerelease: {
|
|
2824
|
+
description: "Create a prerelease with the given tag (e.g., beta, alpha)",
|
|
2825
|
+
required: false,
|
|
2826
|
+
type: "string"
|
|
2827
|
+
}
|
|
2828
|
+
} } };
|
|
2829
|
+
}
|
|
2830
|
+
/** Build the release:simple run command with conditional flags from workflow inputs. */
|
|
2831
|
+
function simpleReleaseCommand() {
|
|
2832
|
+
return [
|
|
2833
|
+
"FLAGS=",
|
|
2834
|
+
`case "${actionsExpr("inputs.bump")}" in`,
|
|
2835
|
+
" major|minor|patch) FLAGS=\"$FLAGS --release-as " + actionsExpr("inputs.bump") + "\" ;;",
|
|
2836
|
+
" first-release) FLAGS=\"$FLAGS --first-release\" ;;",
|
|
2837
|
+
"esac",
|
|
2838
|
+
`if [ -n "${actionsExpr("inputs.prerelease")}" ]; then FLAGS="$FLAGS --prerelease ${actionsExpr("inputs.prerelease")}"; fi`,
|
|
2839
|
+
"pnpm exec bst release:simple $FLAGS"
|
|
2840
|
+
].join("\n");
|
|
2841
|
+
}
|
|
2842
|
+
function simpleReleaseSteps(ci, nodeVersionYaml, publishesNpm, hasDocker) {
|
|
2843
|
+
const releaseStep = {
|
|
2844
|
+
match: { run: "release:simple" },
|
|
2845
|
+
step: {
|
|
2846
|
+
name: "Release",
|
|
2847
|
+
env: ci === "github" ? { GITHUB_TOKEN: actionsExpr("github.token") } : {
|
|
2848
|
+
FORGEJO_SERVER_URL: actionsExpr("github.server_url"),
|
|
2849
|
+
FORGEJO_REPOSITORY: actionsExpr("github.repository"),
|
|
2850
|
+
RELEASE_TOKEN: actionsExpr("secrets.RELEASE_TOKEN")
|
|
2851
|
+
},
|
|
2852
|
+
run: simpleReleaseCommand()
|
|
2853
|
+
}
|
|
2854
|
+
};
|
|
2855
|
+
const dockerStep = {
|
|
2856
|
+
match: { run: "docker:publish" },
|
|
2857
|
+
step: {
|
|
2858
|
+
name: "Publish Docker images",
|
|
2859
|
+
if: "success()",
|
|
2860
|
+
env: {
|
|
2861
|
+
DOCKER_REGISTRY_HOST: actionsExpr("vars.DOCKER_REGISTRY_HOST"),
|
|
2862
|
+
DOCKER_REGISTRY_NAMESPACE: actionsExpr("vars.DOCKER_REGISTRY_NAMESPACE"),
|
|
2863
|
+
DOCKER_USERNAME: actionsExpr("secrets.DOCKER_USERNAME"),
|
|
2864
|
+
DOCKER_PASSWORD: actionsExpr("secrets.DOCKER_PASSWORD")
|
|
2865
|
+
},
|
|
2866
|
+
run: "pnpm exec bst docker:publish"
|
|
2867
|
+
}
|
|
2868
|
+
};
|
|
2869
|
+
return [
|
|
2870
|
+
...commonSteps(nodeVersionYaml, publishesNpm),
|
|
2871
|
+
releaseStep,
|
|
2872
|
+
...hasDocker ? [dockerStep] : []
|
|
2873
|
+
];
|
|
2840
2874
|
}
|
|
2841
2875
|
/** Build the required release step for the check job (changesets). */
|
|
2842
2876
|
function changesetsReleaseStep(ci, publishesNpm) {
|
|
@@ -2870,69 +2904,10 @@ function changesetsReleaseStep(ci, publishesNpm) {
|
|
|
2870
2904
|
}
|
|
2871
2905
|
};
|
|
2872
2906
|
}
|
|
2873
|
-
function
|
|
2874
|
-
const isNodeVersionFile = nodeVersionYaml.startsWith("node-version-file");
|
|
2875
|
-
const steps = [
|
|
2876
|
-
{
|
|
2877
|
-
match: { uses: "actions/checkout" },
|
|
2878
|
-
step: {
|
|
2879
|
-
uses: "actions/checkout@v4",
|
|
2880
|
-
with: { "fetch-depth": 0 }
|
|
2881
|
-
}
|
|
2882
|
-
},
|
|
2883
|
-
{
|
|
2884
|
-
match: { uses: "pnpm/action-setup" },
|
|
2885
|
-
step: { uses: "pnpm/action-setup@v4" }
|
|
2886
|
-
},
|
|
2887
|
-
{
|
|
2888
|
-
match: { uses: "actions/setup-node" },
|
|
2889
|
-
step: {
|
|
2890
|
-
uses: "actions/setup-node@v4",
|
|
2891
|
-
with: {
|
|
2892
|
-
...isNodeVersionFile ? { "node-version-file": "package.json" } : { "node-version": "24" },
|
|
2893
|
-
cache: "pnpm",
|
|
2894
|
-
...publishesNpm && { "registry-url": "https://registry.npmjs.org" }
|
|
2895
|
-
}
|
|
2896
|
-
}
|
|
2897
|
-
},
|
|
2898
|
-
{
|
|
2899
|
-
match: { run: "pnpm install" },
|
|
2900
|
-
step: { run: "pnpm install --frozen-lockfile" }
|
|
2901
|
-
}
|
|
2902
|
-
];
|
|
2907
|
+
function buildSteps(strategy, ci, nodeVersionYaml, publishesNpm, hasDocker) {
|
|
2903
2908
|
switch (strategy) {
|
|
2904
|
-
case "release-it":
|
|
2905
|
-
|
|
2906
|
-
match: { run: "release-it" },
|
|
2907
|
-
step: { run: "pnpm release-it --ci" }
|
|
2908
|
-
});
|
|
2909
|
-
break;
|
|
2910
|
-
case "simple":
|
|
2911
|
-
steps.push({
|
|
2912
|
-
match: { run: "release:simple" },
|
|
2913
|
-
step: { run: "pnpm exec bst release:simple" }
|
|
2914
|
-
});
|
|
2915
|
-
if (hasDocker) steps.push({
|
|
2916
|
-
match: { run: "docker:publish" },
|
|
2917
|
-
step: {
|
|
2918
|
-
name: "Publish Docker images",
|
|
2919
|
-
run: "pnpm exec bst docker:publish"
|
|
2920
|
-
}
|
|
2921
|
-
});
|
|
2922
|
-
break;
|
|
2923
|
-
case "changesets":
|
|
2924
|
-
steps.push({
|
|
2925
|
-
match: { run: "changeset" },
|
|
2926
|
-
step: { run: "pnpm exec bst release:changesets" }
|
|
2927
|
-
});
|
|
2928
|
-
break;
|
|
2929
|
-
}
|
|
2930
|
-
return steps;
|
|
2931
|
-
}
|
|
2932
|
-
function buildWorkflow(strategy, ci, nodeVersionYaml, publishesNpm, hasDocker) {
|
|
2933
|
-
switch (strategy) {
|
|
2934
|
-
case "release-it": return releaseItWorkflow(ci, nodeVersionYaml, publishesNpm);
|
|
2935
|
-
case "simple": return commitAndTagVersionWorkflow(ci, nodeVersionYaml, publishesNpm, hasDocker);
|
|
2909
|
+
case "release-it": return releaseItSteps(ci, nodeVersionYaml, publishesNpm);
|
|
2910
|
+
case "simple": return simpleReleaseSteps(ci, nodeVersionYaml, publishesNpm, hasDocker);
|
|
2936
2911
|
default: return null;
|
|
2937
2912
|
}
|
|
2938
2913
|
}
|
|
@@ -2983,12 +2958,21 @@ async function generateReleaseCi(ctx) {
|
|
|
2983
2958
|
const workflowPath = isGitHub ? ".github/workflows/release.yml" : ".forgejo/workflows/release.yml";
|
|
2984
2959
|
const nodeVersionYaml = computeNodeVersionYaml(ctx);
|
|
2985
2960
|
const hasDocker = hasDockerPackages(ctx);
|
|
2986
|
-
const
|
|
2987
|
-
if (!
|
|
2961
|
+
const steps = buildSteps(ctx.config.releaseStrategy, ctx.config.ci, nodeVersionYaml, publishesNpm, hasDocker);
|
|
2962
|
+
if (!steps) return {
|
|
2988
2963
|
filePath,
|
|
2989
2964
|
action: "skipped",
|
|
2990
2965
|
description: "Release CI workflow not applicable"
|
|
2991
2966
|
};
|
|
2967
|
+
const on = ctx.config.releaseStrategy === "simple" ? simpleWorkflowDispatchTrigger() : { workflow_dispatch: null };
|
|
2968
|
+
const content = buildWorkflowYaml({
|
|
2969
|
+
ci: ctx.config.ci,
|
|
2970
|
+
name: "Release",
|
|
2971
|
+
on,
|
|
2972
|
+
...isGitHub && { permissions: { contents: "write" } },
|
|
2973
|
+
jobName: "release",
|
|
2974
|
+
steps
|
|
2975
|
+
});
|
|
2992
2976
|
if (ctx.exists(workflowPath)) {
|
|
2993
2977
|
const raw = ctx.read(workflowPath);
|
|
2994
2978
|
if (raw) {
|
|
@@ -3008,7 +2992,7 @@ async function generateReleaseCi(ctx) {
|
|
|
3008
2992
|
description: "Release workflow already up to date"
|
|
3009
2993
|
};
|
|
3010
2994
|
}
|
|
3011
|
-
const merged = mergeWorkflowSteps(existing, "release",
|
|
2995
|
+
const merged = mergeWorkflowSteps(existing, "release", toRequiredSteps(steps));
|
|
3012
2996
|
const withComment = ensureSchemaComment(merged.content, ctx.config.ci);
|
|
3013
2997
|
if (!merged.changed) {
|
|
3014
2998
|
if (withComment !== raw) {
|
|
@@ -3337,7 +3321,7 @@ function generateMigratePrompt(results, config, detected) {
|
|
|
3337
3321
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
3338
3322
|
sections.push("# Migration Prompt");
|
|
3339
3323
|
sections.push("");
|
|
3340
|
-
sections.push(`_Generated by \`@bensandee/tooling@0.
|
|
3324
|
+
sections.push(`_Generated by \`@bensandee/tooling@0.33.0 repo:sync\` on ${timestamp}_`);
|
|
3341
3325
|
sections.push("");
|
|
3342
3326
|
sections.push("The following prompt was generated by `@bensandee/tooling repo:sync`. Paste it into Claude Code or another AI assistant to finish migrating this repository.");
|
|
3343
3327
|
sections.push("");
|
|
@@ -4345,33 +4329,53 @@ const releaseTriggerCommand = defineCommand({
|
|
|
4345
4329
|
name: "release:trigger",
|
|
4346
4330
|
description: "Trigger the release CI workflow"
|
|
4347
4331
|
},
|
|
4348
|
-
args: {
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4332
|
+
args: {
|
|
4333
|
+
ref: {
|
|
4334
|
+
type: "string",
|
|
4335
|
+
description: "Git ref to trigger on (default: main)",
|
|
4336
|
+
required: false
|
|
4337
|
+
},
|
|
4338
|
+
bump: {
|
|
4339
|
+
type: "string",
|
|
4340
|
+
description: "Version bump type: auto, major, minor, patch, or first-release",
|
|
4341
|
+
required: false
|
|
4342
|
+
},
|
|
4343
|
+
prerelease: {
|
|
4344
|
+
type: "string",
|
|
4345
|
+
description: "Create a prerelease with the given tag (e.g., beta, alpha)",
|
|
4346
|
+
required: false
|
|
4347
|
+
}
|
|
4348
|
+
},
|
|
4353
4349
|
async run({ args }) {
|
|
4354
4350
|
const ref = args.ref ?? "main";
|
|
4351
|
+
const inputs = {};
|
|
4352
|
+
if (args.bump) inputs["bump"] = args.bump;
|
|
4353
|
+
if (args.prerelease) inputs["prerelease"] = args.prerelease;
|
|
4355
4354
|
const resolved = resolveConnection(process.cwd());
|
|
4356
|
-
if (resolved.type === "forgejo") await triggerForgejo(resolved.conn, ref);
|
|
4357
|
-
else triggerGitHub(ref);
|
|
4355
|
+
if (resolved.type === "forgejo") await triggerForgejo(resolved.conn, ref, inputs);
|
|
4356
|
+
else triggerGitHub(ref, inputs);
|
|
4358
4357
|
}
|
|
4359
4358
|
});
|
|
4360
|
-
async function triggerForgejo(conn, ref) {
|
|
4359
|
+
async function triggerForgejo(conn, ref, inputs) {
|
|
4361
4360
|
const url = `${conn.serverUrl}/api/v1/repos/${conn.repository}/actions/workflows/release.yml/dispatches`;
|
|
4361
|
+
const body = { ref };
|
|
4362
|
+
if (Object.keys(inputs).length > 0) body["inputs"] = inputs;
|
|
4362
4363
|
const res = await fetch(url, {
|
|
4363
4364
|
method: "POST",
|
|
4364
4365
|
headers: {
|
|
4365
4366
|
Authorization: `token ${conn.token}`,
|
|
4366
4367
|
"Content-Type": "application/json"
|
|
4367
4368
|
},
|
|
4368
|
-
body: JSON.stringify(
|
|
4369
|
+
body: JSON.stringify(body)
|
|
4369
4370
|
});
|
|
4370
4371
|
if (!res.ok) throw new FatalError(`Failed to trigger Forgejo workflow: ${res.status} ${res.statusText}`);
|
|
4371
4372
|
log$2.info(`Triggered release workflow on Forgejo (ref: ${ref})`);
|
|
4372
4373
|
}
|
|
4373
|
-
function triggerGitHub(ref) {
|
|
4374
|
-
const
|
|
4374
|
+
function triggerGitHub(ref, inputs) {
|
|
4375
|
+
const executor = createRealExecutor();
|
|
4376
|
+
const inputFlags = Object.entries(inputs).map(([k, v]) => `-f ${k}=${v}`).join(" ");
|
|
4377
|
+
const cmd = `gh workflow run release.yml --ref ${ref}${inputFlags ? ` ${inputFlags}` : ""}`;
|
|
4378
|
+
const result = executor.exec(cmd, { cwd: process.cwd() });
|
|
4375
4379
|
if (result.exitCode !== 0) throw new FatalError(`Failed to trigger GitHub workflow: ${result.stderr || result.stdout || "unknown error"}`);
|
|
4376
4380
|
log$2.info(`Triggered release workflow on GitHub (ref: ${ref})`);
|
|
4377
4381
|
}
|
|
@@ -4473,8 +4477,18 @@ function readVersion(executor, cwd) {
|
|
|
4473
4477
|
if (!pkg?.version) throw new FatalError("No version field found in package.json");
|
|
4474
4478
|
return pkg.version;
|
|
4475
4479
|
}
|
|
4480
|
+
/** Configure git identity for CI bot commits. */
|
|
4481
|
+
function configureGitIdentity(executor, config) {
|
|
4482
|
+
const isGitHub = config.platform?.type === "github";
|
|
4483
|
+
const name = isGitHub ? "github-actions[bot]" : "forgejo-actions[bot]";
|
|
4484
|
+
const email = isGitHub ? "github-actions[bot]@users.noreply.github.com" : "forgejo-actions[bot]@noreply.localhost";
|
|
4485
|
+
executor.exec(`git config user.name "${name}"`, { cwd: config.cwd });
|
|
4486
|
+
executor.exec(`git config user.email "${email}"`, { cwd: config.cwd });
|
|
4487
|
+
debug(config, `Configured git identity: ${name} <${email}>`);
|
|
4488
|
+
}
|
|
4476
4489
|
/** Run the full commit-and-tag-version release flow. */
|
|
4477
4490
|
async function runSimpleRelease(executor, config) {
|
|
4491
|
+
configureGitIdentity(executor, config);
|
|
4478
4492
|
const command = buildCommand(config);
|
|
4479
4493
|
log$2.info(`Running: ${command}`);
|
|
4480
4494
|
const versionResult = executor.exec(command, { cwd: config.cwd });
|
|
@@ -5145,7 +5159,7 @@ const dockerCheckCommand = defineCommand({
|
|
|
5145
5159
|
const main = defineCommand({
|
|
5146
5160
|
meta: {
|
|
5147
5161
|
name: "bst",
|
|
5148
|
-
version: "0.
|
|
5162
|
+
version: "0.33.0",
|
|
5149
5163
|
description: "Bootstrap and maintain standardized TypeScript project tooling"
|
|
5150
5164
|
},
|
|
5151
5165
|
subCommands: {
|
|
@@ -5161,7 +5175,7 @@ const main = defineCommand({
|
|
|
5161
5175
|
"docker:check": dockerCheckCommand
|
|
5162
5176
|
}
|
|
5163
5177
|
});
|
|
5164
|
-
console.log(`@bensandee/tooling v0.
|
|
5178
|
+
console.log(`@bensandee/tooling v0.33.0`);
|
|
5165
5179
|
async function run() {
|
|
5166
5180
|
await runMain(main);
|
|
5167
5181
|
process.exit(process.exitCode ?? 0);
|
|
@@ -145,14 +145,19 @@ async function runDockerCheck(executor, config) {
|
|
|
145
145
|
const pollIntervalMs = config.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
146
146
|
const { compose } = config;
|
|
147
147
|
const cleanup = () => composeDown(executor, compose);
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
148
|
+
let cleaningUp = false;
|
|
149
|
+
const gracefulShutdown = () => {
|
|
150
|
+
if (cleaningUp) {
|
|
151
|
+
executor.log("Cleanup in progress, please wait...");
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
cleaningUp = true;
|
|
155
|
+
executor.log("Interrupted — shutting down compose stack...");
|
|
153
156
|
cleanup();
|
|
154
157
|
process.exit(1);
|
|
155
|
-
}
|
|
158
|
+
};
|
|
159
|
+
const disposeInt = executor.onSignal("SIGINT", gracefulShutdown);
|
|
160
|
+
const disposeTerm = executor.onSignal("SIGTERM", gracefulShutdown);
|
|
156
161
|
try {
|
|
157
162
|
if (config.buildCommand) {
|
|
158
163
|
executor.log("Building images...");
|
|
@@ -162,6 +167,7 @@ async function runDockerCheck(executor, config) {
|
|
|
162
167
|
composeUp(executor, compose);
|
|
163
168
|
executor.log(`Waiting for stack to be healthy (max ${timeoutMs / 1e3}s)...`);
|
|
164
169
|
const startTime = executor.now();
|
|
170
|
+
let lastStatusLogTime = startTime;
|
|
165
171
|
const healthStatus = new Map(config.healthChecks.map((c) => [c.name, false]));
|
|
166
172
|
while (executor.now() - startTime < timeoutMs) {
|
|
167
173
|
const containers = composePs(executor, compose);
|
|
@@ -195,10 +201,12 @@ async function runDockerCheck(executor, config) {
|
|
|
195
201
|
elapsedMs: executor.now() - startTime
|
|
196
202
|
};
|
|
197
203
|
}
|
|
198
|
-
const
|
|
199
|
-
if (
|
|
200
|
-
|
|
201
|
-
|
|
204
|
+
const now = executor.now();
|
|
205
|
+
if (now - lastStatusLogTime >= 5e3) {
|
|
206
|
+
lastStatusLogTime = now;
|
|
207
|
+
const elapsed = Math.floor((now - startTime) / 1e3);
|
|
208
|
+
const parts = [compose.services.map((s) => `${s}=${getContainerHealth(containers, s)}`).join(", "), [...healthStatus.entries()].map(([name, ok]) => `${name}=${ok ? "OK" : "Pending"}`).join(", ")].filter(Boolean).join(" | ");
|
|
209
|
+
executor.log(`Waiting... (${elapsed}s elapsed). ${parts}`);
|
|
202
210
|
}
|
|
203
211
|
await executor.sleep(pollIntervalMs);
|
|
204
212
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as composeDown, c as composeUp, i as composeCommand, l as createRealExecutor, n as checkHttpHealth, o as composeLogs, r as getContainerHealth, s as composePs, t as runDockerCheck } from "../check-
|
|
1
|
+
import { a as composeDown, c as composeUp, i as composeCommand, l as createRealExecutor, n as checkHttpHealth, o as composeLogs, r as getContainerHealth, s as composePs, t as runDockerCheck } from "../check-B2AAPCBO.mjs";
|
|
2
2
|
export { checkHttpHealth, composeCommand, composeDown, composeLogs, composePs, composeUp, createRealExecutor, getContainerHealth, runDockerCheck };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bensandee/tooling",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.0",
|
|
4
4
|
"description": "CLI tool to bootstrap and maintain standardized TypeScript project tooling",
|
|
5
5
|
"bin": {
|
|
6
6
|
"bst": "./dist/bin.mjs"
|
|
@@ -40,23 +40,21 @@
|
|
|
40
40
|
"@bensandee/common": "0.1.2"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@types/node": "24.12.0",
|
|
44
|
-
"@types/picomatch": "4.0.2",
|
|
45
|
-
"tsdown": "0.21.4",
|
|
46
|
-
"typescript": "5.9.3",
|
|
47
|
-
"vitest": "4.1.0",
|
|
48
|
-
"@bensandee/config": "0.9.1"
|
|
49
|
-
},
|
|
50
|
-
"optionalDependencies": {
|
|
51
43
|
"@changesets/cli": "2.30.0",
|
|
52
44
|
"@release-it/bumper": "7.0.5",
|
|
45
|
+
"@types/node": "24.12.0",
|
|
46
|
+
"@types/picomatch": "4.0.2",
|
|
53
47
|
"commit-and-tag-version": "12.7.0",
|
|
54
48
|
"knip": "5.87.0",
|
|
55
49
|
"lefthook": "2.1.4",
|
|
56
50
|
"oxfmt": "0.41.0",
|
|
57
51
|
"oxlint": "1.56.0",
|
|
58
52
|
"prettier": "3.8.1",
|
|
59
|
-
"release-it": "19.2.4"
|
|
53
|
+
"release-it": "19.2.4",
|
|
54
|
+
"tsdown": "0.21.4",
|
|
55
|
+
"typescript": "5.9.3",
|
|
56
|
+
"vitest": "4.1.0",
|
|
57
|
+
"@bensandee/config": "0.9.1"
|
|
60
58
|
},
|
|
61
59
|
"scripts": {
|
|
62
60
|
"build": "tsdown",
|