@dbx-tools/projen 0.9.60 → 0.9.61
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 +16 -0
- package/index.ts +1 -1
- package/package.json +4 -4
- package/src/_task-command.ts +19 -0
- package/tasks/release-notes.ts +97 -0
- package/tasks/release.ts +19 -0
package/README.md
CHANGED
|
@@ -481,8 +481,22 @@ bun run release --publish pypi --no-docs
|
|
|
481
481
|
bun run release --no-npm --no-local-publish
|
|
482
482
|
bun run release --publish local --install never
|
|
483
483
|
bun run release --local-registry false --local-pypi auto
|
|
484
|
+
bun run release --demo-deploy
|
|
485
|
+
bun run release --no-release-notes
|
|
484
486
|
```
|
|
485
487
|
|
|
488
|
+
`--demo-deploy` is off by default. After tagging and any local registry
|
|
489
|
+
publication it runs `bun run demo:deploy`, which stages the AppKit demo
|
|
490
|
+
and deploys it to the FEVM workspace. That step is local only and is not
|
|
491
|
+
recorded in the annotated tag.
|
|
492
|
+
|
|
493
|
+
After bump, release writes `docs/releases/vX.Y.Z.md` with `dbx genie exec -C
|
|
494
|
+
"$PWD" --sandbox read-only --ephemeral -o`. If Genie fails or writes an empty
|
|
495
|
+
file, the step keeps going with a short git-log summary. `--no-release-notes`
|
|
496
|
+
skips the file. Notes are committed with the version bump and are not recorded
|
|
497
|
+
in the annotated tag. They are skipped on `--no-bump` because that path
|
|
498
|
+
requires an already committed tree.
|
|
499
|
+
|
|
486
500
|
`--install auto` keeps Projen's normal local dependency-install behavior;
|
|
487
501
|
`always` performs a manifest-resolved install first, and `never` uses dependencies already
|
|
488
502
|
installed while still regenerating versioned sources. `--no-validation` skips
|
|
@@ -553,6 +567,8 @@ release [options]
|
|
|
553
567
|
| `--docs` | build and deploy docs for a selected scope |
|
|
554
568
|
| `--no-docs` | skip documentation build and deployment |
|
|
555
569
|
| `--no-validation` | skip optional release validation tasks; version/source checks remain mandatory |
|
|
570
|
+
| `--no-release-notes` | skip writing docs/releases notes (Genie and git-log fallback) |
|
|
571
|
+
| `--demo-deploy` | after tagging, stage and deploy the AppKit demo app (off by default) |
|
|
556
572
|
| `--no-local-publish` | skip publishing to configured local registries |
|
|
557
573
|
| `--local-registry <auto\|false\|url>` | local npm registry selection (default: "auto") |
|
|
558
574
|
| `--local-pypi <auto\|false\|url>` | local devpi registry selection (default: "auto") |
|
package/index.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Hand edits are overwritten on the next watch; this file is read-only.
|
|
4
4
|
|
|
5
5
|
export const PACKAGE_IDENTIFIER = "@dbx-tools/projen";
|
|
6
|
-
export const PACKAGE_VERSION = "0.9.
|
|
6
|
+
export const PACKAGE_VERSION = "0.9.61";
|
|
7
7
|
export * as barrels from "./src/barrels.ts";
|
|
8
8
|
export * as bunApp from "./src/bun-app.ts";
|
|
9
9
|
export * as bunWorkflow from "./src/bun-workflow.ts";
|
package/package.json
CHANGED
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@clack/prompts": "^1.7.0",
|
|
33
|
-
"@dbx-tools/core": "^0.9.
|
|
34
|
-
"@dbx-tools/path": "^0.9.
|
|
35
|
-
"@dbx-tools/shared-core": "^0.9.
|
|
33
|
+
"@dbx-tools/core": "^0.9.61",
|
|
34
|
+
"@dbx-tools/path": "^0.9.61",
|
|
35
|
+
"@dbx-tools/shared-core": "^0.9.61",
|
|
36
36
|
"commander": "^15.0.0",
|
|
37
37
|
"concurrently": "^10.0.3",
|
|
38
38
|
"constructs": "^10.6.0",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
},
|
|
55
55
|
"main": "index.ts",
|
|
56
56
|
"license": "Apache-2.0",
|
|
57
|
-
"version": "0.9.
|
|
57
|
+
"version": "0.9.61",
|
|
58
58
|
"types": "index.ts",
|
|
59
59
|
"type": "module",
|
|
60
60
|
"exports": {
|
package/src/_task-command.ts
CHANGED
|
@@ -69,6 +69,25 @@ export function captureTaskCommand(
|
|
|
69
69
|
return result.exitCode === 0 ? (result.stdout?.trim() ?? "") : "";
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
/** Run a command with inherited output and return whether it exited successfully. */
|
|
73
|
+
export function tryTaskCommand(
|
|
74
|
+
cwd: string,
|
|
75
|
+
command: string,
|
|
76
|
+
args: readonly string[],
|
|
77
|
+
options: TaskCommandOptions = {},
|
|
78
|
+
): boolean {
|
|
79
|
+
return (
|
|
80
|
+
exec.spawnSync(executable(command), [...args], {
|
|
81
|
+
cwd,
|
|
82
|
+
env: options.env,
|
|
83
|
+
stdout: "inherit",
|
|
84
|
+
stderr: options.stderr ?? "inherit",
|
|
85
|
+
stdin: "ignore",
|
|
86
|
+
check: false,
|
|
87
|
+
}).exitCode === 0
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
72
91
|
/** Whether a quiet, non-interactive command exits successfully. */
|
|
73
92
|
export function taskCommandSucceeds(
|
|
74
93
|
cwd: string,
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Write user-facing release notes under `docs/releases/` during `bun run release`.
|
|
3
|
+
*
|
|
4
|
+
* Prefers Genie Code (`dbx genie exec`). When that fails or writes an empty
|
|
5
|
+
* file, falls back to a short git-log summary so the release still commits notes.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { dirname, join } from "node:path";
|
|
12
|
+
import { log } from "@dbx-tools/shared-core";
|
|
13
|
+
|
|
14
|
+
import { captureTaskCommand, tryTaskCommand } from "../src/_task-command.ts";
|
|
15
|
+
|
|
16
|
+
const logger = log.logger("projen:release-notes");
|
|
17
|
+
|
|
18
|
+
const GENIE_PROMPT =
|
|
19
|
+
"Compare HEAD and the working tree against the latest v* tag. Produce concise user-facing release notes. Ignore generated-file churn and test-only changes. Include breaking changes, fixes, and validation results. Do not modify files.";
|
|
20
|
+
|
|
21
|
+
/** Inputs for one notes write. */
|
|
22
|
+
export interface WriteReleaseNotesOptions {
|
|
23
|
+
readonly prefix: string;
|
|
24
|
+
readonly root: string;
|
|
25
|
+
readonly version: string;
|
|
26
|
+
/** Override Genie invocation; return false to force the git-log fallback. */
|
|
27
|
+
readonly runGenie?: (args: readonly string[]) => boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Path of the notes file for one workspace version. */
|
|
31
|
+
export function releaseNotesPath(root: string, version: string): string {
|
|
32
|
+
return join(root, "docs", "releases", `v${version}.md`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Short markdown notes from commit subjects since the previous matching tag. */
|
|
36
|
+
export function fallbackReleaseNotes(
|
|
37
|
+
version: string,
|
|
38
|
+
commits: readonly string[],
|
|
39
|
+
previousTag?: string,
|
|
40
|
+
): string {
|
|
41
|
+
const range = previousTag ? `since ${previousTag}` : "in this release";
|
|
42
|
+
const items =
|
|
43
|
+
commits.length > 0
|
|
44
|
+
? commits.map((subject) => `- ${subject}`).join("\n")
|
|
45
|
+
: "- Version bump and synchronized package metadata.";
|
|
46
|
+
return `# Release ${version}\n\ndbx-tools ${version} ${range}.\n\n${items}\n`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Run Genie into `docs/releases/vX.Y.Z.md`, or write {@link fallbackReleaseNotes}. */
|
|
50
|
+
export function writeReleaseNotes(options: WriteReleaseNotesOptions): string {
|
|
51
|
+
const { prefix, root, version } = options;
|
|
52
|
+
const destination = releaseNotesPath(root, version);
|
|
53
|
+
mkdirSync(dirname(destination), { recursive: true });
|
|
54
|
+
const relative = join("docs", "releases", `v${version}.md`);
|
|
55
|
+
const genieArgs = [
|
|
56
|
+
"exec",
|
|
57
|
+
"-C",
|
|
58
|
+
root,
|
|
59
|
+
"--sandbox",
|
|
60
|
+
"read-only",
|
|
61
|
+
"--ephemeral",
|
|
62
|
+
"-o",
|
|
63
|
+
relative,
|
|
64
|
+
GENIE_PROMPT,
|
|
65
|
+
] as const;
|
|
66
|
+
const ran =
|
|
67
|
+
options.runGenie?.([...genieArgs]) ??
|
|
68
|
+
tryTaskCommand(root, "bun", ["run", "dbx", "genie", ...genieArgs]);
|
|
69
|
+
if (ran && readNotes(destination)) {
|
|
70
|
+
logger.info("wrote Genie release notes", { path: relative });
|
|
71
|
+
return destination;
|
|
72
|
+
}
|
|
73
|
+
const previousTag = captureTaskCommand(root, "git", [
|
|
74
|
+
"describe",
|
|
75
|
+
"--tags",
|
|
76
|
+
"--abbrev=0",
|
|
77
|
+
`--match=${prefix}*`,
|
|
78
|
+
]);
|
|
79
|
+
const logArgs = previousTag
|
|
80
|
+
? ["log", "--no-merges", "--pretty=format:%s", `${previousTag}..HEAD`]
|
|
81
|
+
: ["log", "--no-merges", "--pretty=format:%s", "-15"];
|
|
82
|
+
const commits = captureTaskCommand(root, "git", logArgs)
|
|
83
|
+
.split("\n")
|
|
84
|
+
.map((line) => line.trim())
|
|
85
|
+
.filter(Boolean);
|
|
86
|
+
writeFileSync(destination, fallbackReleaseNotes(version, commits, previousTag || undefined));
|
|
87
|
+
logger.warn("Genie release notes unavailable; wrote git-log summary", { path: relative });
|
|
88
|
+
return destination;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function readNotes(path: string): string {
|
|
92
|
+
try {
|
|
93
|
+
return readFileSync(path, "utf8").trim();
|
|
94
|
+
} catch {
|
|
95
|
+
return "";
|
|
96
|
+
}
|
|
97
|
+
}
|
package/tasks/release.ts
CHANGED
|
@@ -4,6 +4,7 @@ import * as projectUtils from "@dbx-tools/core/project-utils";
|
|
|
4
4
|
import { log } from "@dbx-tools/shared-core";
|
|
5
5
|
import { Command, Option } from "commander";
|
|
6
6
|
import { publishLocalRelease } from "./local-publish.ts";
|
|
7
|
+
import { writeReleaseNotes } from "./release-notes.ts";
|
|
7
8
|
import { assertReleaseVersion } from "./release-version.ts";
|
|
8
9
|
import { captureTaskCommand, runTaskCommand, taskCommandSucceeds } from "../src/_task-command.ts";
|
|
9
10
|
import {
|
|
@@ -105,6 +106,11 @@ export async function runRelease(
|
|
|
105
106
|
readonly install?: ReleaseInstallMode;
|
|
106
107
|
readonly pythonRoot?: string;
|
|
107
108
|
readonly validationTasks?: readonly string[];
|
|
109
|
+
/** After tagging, stage and deploy the AppKit demo app. Off by default. */
|
|
110
|
+
readonly demoDeploy?: boolean;
|
|
111
|
+
/** Write `docs/releases/vX.Y.Z.md` via Genie, with a git-log fallback. On by default. */
|
|
112
|
+
readonly releaseNotes?: boolean;
|
|
113
|
+
readonly writeReleaseNotes?: typeof writeReleaseNotes;
|
|
108
114
|
},
|
|
109
115
|
): Promise<string> {
|
|
110
116
|
const { branch, prefix, remote, root } = options;
|
|
@@ -125,6 +131,9 @@ export async function runRelease(
|
|
|
125
131
|
const version = readWorkspaceVersion(root);
|
|
126
132
|
const tag = `${prefix}${version}`;
|
|
127
133
|
assertReleaseVersion(version, { root, prefixes: [prefix], assertNext: true });
|
|
134
|
+
if ((options.releaseNotes ?? true) && (options.bump ?? true)) {
|
|
135
|
+
(options.writeReleaseNotes ?? writeReleaseNotes)({ prefix, root, version });
|
|
136
|
+
}
|
|
128
137
|
|
|
129
138
|
if (taskCommandSucceeds(root, "git", ["rev-parse", "--verify", `refs/tags/${tag}`])) {
|
|
130
139
|
throw new Error(`release tag ${tag} already exists`);
|
|
@@ -190,6 +199,10 @@ export async function runRelease(
|
|
|
190
199
|
});
|
|
191
200
|
}
|
|
192
201
|
|
|
202
|
+
if (options.demoDeploy) {
|
|
203
|
+
runTaskCommand(root, "bun", ["run", "demo:deploy"]);
|
|
204
|
+
}
|
|
205
|
+
|
|
193
206
|
return tag;
|
|
194
207
|
}
|
|
195
208
|
|
|
@@ -228,6 +241,8 @@ export function createReleaseCommand(): Command {
|
|
|
228
241
|
"--no-validation",
|
|
229
242
|
"skip optional release validation tasks; version/source checks remain mandatory",
|
|
230
243
|
)
|
|
244
|
+
.option("--no-release-notes", "skip writing docs/releases notes (Genie and git-log fallback)")
|
|
245
|
+
.option("--demo-deploy", "after tagging, stage and deploy the AppKit demo app (off by default)")
|
|
231
246
|
.option("--no-local-publish", "skip publishing to configured local registries")
|
|
232
247
|
.option("--local-registry <auto|false|url>", "local npm registry selection", "auto")
|
|
233
248
|
.option("--local-pypi <auto|false|url>", "local devpi registry selection", "auto")
|
|
@@ -245,6 +260,8 @@ export function createReleaseCommand(): Command {
|
|
|
245
260
|
localPypi: string;
|
|
246
261
|
install: ReleaseInstallMode;
|
|
247
262
|
validate: string[];
|
|
263
|
+
demoDeploy?: boolean;
|
|
264
|
+
releaseNotes?: boolean;
|
|
248
265
|
},
|
|
249
266
|
) => {
|
|
250
267
|
await runRelease({
|
|
@@ -264,6 +281,8 @@ export function createReleaseCommand(): Command {
|
|
|
264
281
|
docs: options.docs,
|
|
265
282
|
validation: options.validation,
|
|
266
283
|
validationTasks: options.validate,
|
|
284
|
+
demoDeploy: options.demoDeploy,
|
|
285
|
+
releaseNotes: options.releaseNotes,
|
|
267
286
|
});
|
|
268
287
|
},
|
|
269
288
|
);
|