@dbx-tools/projen 0.9.146 → 0.9.150
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 -12
- package/index.ts +1 -1
- package/package.json +5 -5
- package/src/_task-command.ts +11 -1
- package/tasks/local-publish.ts +24 -5
- package/tasks/release.ts +137 -31
package/README.md
CHANGED
|
@@ -316,14 +316,14 @@ scripts, and generated files on top.
|
|
|
316
316
|
Every workspace root exposes a generic development watcher:
|
|
317
317
|
|
|
318
318
|
```bash
|
|
319
|
-
bun run dev:watch
|
|
319
|
+
bun run dev:watch <command> [args...]
|
|
320
320
|
```
|
|
321
321
|
|
|
322
|
-
It restarts after watched writes have been quiet for 15 seconds. Command paths
|
|
323
|
-
|
|
322
|
+
It restarts after watched writes have been quiet for 15 seconds. Command paths
|
|
323
|
+
and task selectors identify the owning package. Its transitive workspace
|
|
324
324
|
dependencies are watched with the shared generated/dependency/test/temp ignores.
|
|
325
|
-
|
|
326
|
-
run its concrete server entrypoint. The `server` tag does not assume one.
|
|
325
|
+
The configured restart key triggers an immediate restart. The demo uses this
|
|
326
|
+
task to run its concrete server entrypoint. The `server` tag does not assume one.
|
|
327
327
|
|
|
328
328
|
AppKit 0.81's `appkitServerConfig()` is the preferred tsdown preset for a
|
|
329
329
|
consumer app using conventional compiled `server/agents/*/agent.ts` entries. It
|
|
@@ -541,10 +541,12 @@ diverged. From `main`, it calls the existing `bump` task, commits the generated
|
|
|
541
541
|
version changes, pushes `main`, and pushes the matching annotated `vX.Y.Z` tag.
|
|
542
542
|
Pass `--no-bump` only to release an existing synchronized local bump. The tag
|
|
543
543
|
workflow verifies that the tag points at the exact `origin/main` commit. The
|
|
544
|
-
local task
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
544
|
+
local task completes local publication and demo deployment first, then waits for
|
|
545
|
+
this workflow by default. It prints the initial job and step state, then only
|
|
546
|
+
append-only status changes, without annotations. Successful runs omit full logs;
|
|
547
|
+
unsuccessful runs print failed-step logs only. Local-only releases and
|
|
548
|
+
repositories without a resolved authenticated GitHub account skip the watcher.
|
|
549
|
+
Stopping the local watcher does not cancel the remote run.
|
|
548
550
|
|
|
549
551
|
One `build-release` job installs workspace dependencies, runs validation, packs
|
|
550
552
|
the npm archives, builds all Python distributions, and builds the documentation
|
|
@@ -556,7 +558,9 @@ workspace. Each Python package retains its own publishing environment and waits
|
|
|
556
558
|
for its package dependencies to publish. The npm job uses the bundled archive
|
|
557
559
|
publisher without a checkout or workspace install. Pages deploys the already
|
|
558
560
|
built site after registry publication succeeds. Local npm and Python registry publication remains available
|
|
559
|
-
through the direct local publication tooling.
|
|
561
|
+
through the direct local publication tooling. Those local publishers run
|
|
562
|
+
concurrently; a failure or release termination stops every live publisher before
|
|
563
|
+
the command returns.
|
|
560
564
|
|
|
561
565
|
The default release publishes npm, Python, and docs, then publishes to
|
|
562
566
|
configured local registries. Use `bun run release --help` or the generated
|
|
@@ -658,8 +662,8 @@ release [options]
|
|
|
658
662
|
| `--no-docs` | Disable build and deploy documentation |
|
|
659
663
|
| `--validation` | Run optional release validation tasks before publishing (default: true, env: PROJEN_RELEASE_VALIDATION) |
|
|
660
664
|
| `--no-validation` | Disable run optional release validation tasks before publishing |
|
|
661
|
-
| `-w, --watch-github-actions` | Wait for and
|
|
662
|
-
| `--no-watch-github-actions` | Disable wait for and
|
|
665
|
+
| `-w, --watch-github-actions` | Wait for and log the tag-triggered GitHub Actions release (default: true, env: PROJEN_RELEASE_WATCH_GITHUB_ACTIONS) |
|
|
666
|
+
| `--no-watch-github-actions` | Disable wait for and log the tag-triggered github actions release |
|
|
663
667
|
| `--release-notes` | Generate and commit release notes for a new version bump (default: true, env: PROJEN_RELEASE_RELEASE_NOTES) |
|
|
664
668
|
| `--no-release-notes` | Disable generate and commit release notes for a new version bump |
|
|
665
669
|
| `--release-notes-text <value>` | Write supplied release-note Markdown without invoking Genie (env: PROJEN_RELEASE_RELEASE_NOTES_TEXT) |
|
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.150";
|
|
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,10 +30,10 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@clack/prompts": "^1.7.0",
|
|
33
|
-
"@dbx-tools/cli-args": "^0.9.
|
|
34
|
-
"@dbx-tools/core": "^0.9.
|
|
35
|
-
"@dbx-tools/path": "^0.9.
|
|
36
|
-
"@dbx-tools/shared-core": "^0.9.
|
|
33
|
+
"@dbx-tools/cli-args": "^0.9.150",
|
|
34
|
+
"@dbx-tools/core": "^0.9.150",
|
|
35
|
+
"@dbx-tools/path": "^0.9.150",
|
|
36
|
+
"@dbx-tools/shared-core": "^0.9.150",
|
|
37
37
|
"commander": "^15.0.0",
|
|
38
38
|
"concurrently": "^10.0.3",
|
|
39
39
|
"constructs": "^10.6.0",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
},
|
|
56
56
|
"main": "index.ts",
|
|
57
57
|
"license": "Apache-2.0",
|
|
58
|
-
"version": "0.9.
|
|
58
|
+
"version": "0.9.150",
|
|
59
59
|
"types": "index.ts",
|
|
60
60
|
"type": "module",
|
|
61
61
|
"exports": {
|
package/src/_task-command.ts
CHANGED
|
@@ -40,7 +40,17 @@ export async function runTaskCommandAsync(
|
|
|
40
40
|
args: readonly string[],
|
|
41
41
|
options: TaskCommandOptions & { readonly signal?: AbortSignal } = {},
|
|
42
42
|
): Promise<void> {
|
|
43
|
-
await
|
|
43
|
+
await spawnTaskCommand(cwd, command, args, options);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Start one supervised task command and return its live process handle. */
|
|
47
|
+
export function spawnTaskCommand(
|
|
48
|
+
cwd: string,
|
|
49
|
+
command: string,
|
|
50
|
+
args: readonly string[],
|
|
51
|
+
options: TaskCommandOptions & { readonly signal?: AbortSignal } = {},
|
|
52
|
+
): exec.ChildProcessResult {
|
|
53
|
+
return exec.spawn(executable(command), [...args], {
|
|
44
54
|
cwd,
|
|
45
55
|
env: options.env,
|
|
46
56
|
stdout: "inherit",
|
package/tasks/local-publish.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/** Build and publish the current workspace to configured loopback registries. */
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
+
import type { ChildProcessResult } from "@dbx-tools/core/exec";
|
|
4
5
|
import * as projectUtils from "@dbx-tools/core/project-utils";
|
|
5
6
|
import { log, net } from "@dbx-tools/shared-core";
|
|
6
7
|
import { activePythonIndexes, resolveLocalPypi } from "./python-registry.ts";
|
|
7
|
-
import {
|
|
8
|
+
import { spawnTaskCommand } from "../src/_task-command.ts";
|
|
8
9
|
|
|
9
10
|
const logger = log.logger("projen:local-publish");
|
|
10
11
|
|
|
@@ -38,6 +39,24 @@ export function localPublishTaskPath(task: "publish.ts" | "publish-python.ts"):
|
|
|
38
39
|
return fileURLToPath(new URL(task, import.meta.url));
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
/** Await concurrent publishers and terminate every live sibling after one fails. */
|
|
43
|
+
export async function awaitLocalPublishers(
|
|
44
|
+
publishes: readonly ChildProcessResult[],
|
|
45
|
+
): Promise<void> {
|
|
46
|
+
try {
|
|
47
|
+
await Promise.all(publishes);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
await Promise.all(
|
|
50
|
+
publishes.map((publish) =>
|
|
51
|
+
publish.exitCode === null && publish.signalCode === null
|
|
52
|
+
? publish.shutdown()
|
|
53
|
+
: Promise.resolve(),
|
|
54
|
+
),
|
|
55
|
+
);
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
41
60
|
/** Build and publish the current workspace directly to local registries. */
|
|
42
61
|
export async function publishLocalRelease(
|
|
43
62
|
options: LocalPublishOptions,
|
|
@@ -46,7 +65,7 @@ export async function publishLocalRelease(
|
|
|
46
65
|
const localRegistry = resolveLocalRegistry(options.localRegistry);
|
|
47
66
|
const activeIndexes = activePythonIndexes();
|
|
48
67
|
const localPypi = resolveLocalPypi(options.localPypi, activeIndexes);
|
|
49
|
-
const publishes:
|
|
68
|
+
const publishes: ChildProcessResult[] = [];
|
|
50
69
|
|
|
51
70
|
if (
|
|
52
71
|
options.localPypi.toLowerCase() === "auto" &&
|
|
@@ -62,7 +81,7 @@ export async function publishLocalRelease(
|
|
|
62
81
|
const publishNpmScript = localPublishTaskPath("publish.ts");
|
|
63
82
|
logger.info(`building and publishing npm workspace to ${localRegistry}`);
|
|
64
83
|
publishes.push(
|
|
65
|
-
|
|
84
|
+
spawnTaskCommand(root, process.execPath, [
|
|
66
85
|
publishNpmScript,
|
|
67
86
|
options.version,
|
|
68
87
|
"--registry",
|
|
@@ -75,7 +94,7 @@ export async function publishLocalRelease(
|
|
|
75
94
|
const publishPythonScript = localPublishTaskPath("publish-python.ts");
|
|
76
95
|
logger.info(`building and publishing Python workspace to ${localPypi.publishUrl}`);
|
|
77
96
|
publishes.push(
|
|
78
|
-
|
|
97
|
+
spawnTaskCommand(root, process.execPath, [
|
|
79
98
|
publishPythonScript,
|
|
80
99
|
options.version,
|
|
81
100
|
"--root",
|
|
@@ -93,7 +112,7 @@ export async function publishLocalRelease(
|
|
|
93
112
|
return { npm: false, python: false };
|
|
94
113
|
}
|
|
95
114
|
|
|
96
|
-
await
|
|
115
|
+
await awaitLocalPublishers(publishes);
|
|
97
116
|
logger.success(`published local workspace ${options.version}`);
|
|
98
117
|
return { npm: Boolean(localRegistry), python: Boolean(localPypi) };
|
|
99
118
|
}
|
package/tasks/release.ts
CHANGED
|
@@ -38,6 +38,33 @@ const GithubReleaseRunSchema = z
|
|
|
38
38
|
})
|
|
39
39
|
.passthrough();
|
|
40
40
|
|
|
41
|
+
const GithubReleaseStepSchema = z
|
|
42
|
+
.object({
|
|
43
|
+
name: z.string(),
|
|
44
|
+
number: z.number().int(),
|
|
45
|
+
status: z.string(),
|
|
46
|
+
conclusion: z.string().nullable().optional(),
|
|
47
|
+
})
|
|
48
|
+
.passthrough();
|
|
49
|
+
|
|
50
|
+
const GithubReleaseJobSchema = z
|
|
51
|
+
.object({
|
|
52
|
+
databaseId: z.number().int().positive(),
|
|
53
|
+
name: z.string(),
|
|
54
|
+
status: z.string(),
|
|
55
|
+
conclusion: z.string().nullable().optional(),
|
|
56
|
+
steps: z.array(GithubReleaseStepSchema),
|
|
57
|
+
})
|
|
58
|
+
.passthrough();
|
|
59
|
+
|
|
60
|
+
const GithubReleaseRunViewSchema = z
|
|
61
|
+
.object({
|
|
62
|
+
status: z.string(),
|
|
63
|
+
conclusion: z.string().nullable().optional(),
|
|
64
|
+
jobs: z.array(GithubReleaseJobSchema),
|
|
65
|
+
})
|
|
66
|
+
.passthrough();
|
|
67
|
+
|
|
41
68
|
/** Inputs required to locate and watch one tag-triggered release workflow. */
|
|
42
69
|
export interface WatchReleaseGithubActionsOptions {
|
|
43
70
|
readonly account: projectUtils.ProjectGhAccount;
|
|
@@ -63,6 +90,61 @@ function delay(milliseconds: number): Promise<void> {
|
|
|
63
90
|
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
64
91
|
}
|
|
65
92
|
|
|
93
|
+
interface GithubProgressEntry {
|
|
94
|
+
readonly conclusion?: string | null;
|
|
95
|
+
readonly key: string;
|
|
96
|
+
readonly label: string;
|
|
97
|
+
readonly status: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function githubProgressEntries(
|
|
101
|
+
run: z.infer<typeof GithubReleaseRunViewSchema>,
|
|
102
|
+
): GithubProgressEntry[] {
|
|
103
|
+
return [
|
|
104
|
+
{
|
|
105
|
+
key: "run",
|
|
106
|
+
label: "release",
|
|
107
|
+
status: run.status,
|
|
108
|
+
conclusion: run.conclusion,
|
|
109
|
+
},
|
|
110
|
+
...run.jobs.flatMap((job) => [
|
|
111
|
+
{
|
|
112
|
+
key: `job:${job.databaseId}`,
|
|
113
|
+
label: job.name,
|
|
114
|
+
status: job.status,
|
|
115
|
+
conclusion: job.conclusion,
|
|
116
|
+
},
|
|
117
|
+
...job.steps.map((step) => ({
|
|
118
|
+
key: `job:${job.databaseId}:step:${step.number}`,
|
|
119
|
+
label: `${job.name} / ${step.name}`,
|
|
120
|
+
status: step.status,
|
|
121
|
+
conclusion: step.conclusion,
|
|
122
|
+
})),
|
|
123
|
+
]),
|
|
124
|
+
];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function githubProgressMarker(entry: GithubProgressEntry): string {
|
|
128
|
+
if (entry.status === "completed") {
|
|
129
|
+
if (entry.conclusion === "success") return "[done]";
|
|
130
|
+
if (entry.conclusion === "skipped") return "[skip]";
|
|
131
|
+
return "[fail]";
|
|
132
|
+
}
|
|
133
|
+
return entry.status === "in_progress" ? "[run]" : "[wait]";
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function reportGithubProgress(
|
|
137
|
+
run: z.infer<typeof GithubReleaseRunViewSchema>,
|
|
138
|
+
previous: Map<string, string>,
|
|
139
|
+
): void {
|
|
140
|
+
for (const entry of githubProgressEntries(run)) {
|
|
141
|
+
const state = `${entry.status}:${entry.conclusion ?? ""}`;
|
|
142
|
+
if (previous.get(entry.key) === state) continue;
|
|
143
|
+
previous.set(entry.key, state);
|
|
144
|
+
logger.info(`${githubProgressMarker(entry)} ${entry.label}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
66
148
|
/** Whether this release selection has remote work worth watching. */
|
|
67
149
|
export function shouldWatchReleaseGithubActions(
|
|
68
150
|
options: Pick<ReleaseSelectionOptions, "publish"> & { readonly watchGithubActions?: boolean },
|
|
@@ -134,18 +216,39 @@ export async function watchReleaseGithubActions(
|
|
|
134
216
|
url: run.url,
|
|
135
217
|
});
|
|
136
218
|
try {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
219
|
+
const progress = new Map<string, string>();
|
|
220
|
+
let completed = false;
|
|
221
|
+
let conclusion: string | null | undefined;
|
|
222
|
+
while (!completed) {
|
|
223
|
+
const output = captureTaskCommand(
|
|
224
|
+
root,
|
|
225
|
+
"gh",
|
|
226
|
+
[
|
|
227
|
+
"run",
|
|
228
|
+
"view",
|
|
229
|
+
String(run.databaseId),
|
|
230
|
+
"--repo",
|
|
231
|
+
repository,
|
|
232
|
+
"--json",
|
|
233
|
+
"status,conclusion,jobs",
|
|
234
|
+
],
|
|
235
|
+
{ check: true, env: account.env, stderr: "inherit" },
|
|
236
|
+
);
|
|
237
|
+
const parsed = GithubReleaseRunViewSchema.safeParse(json.parse(output));
|
|
238
|
+
if (!parsed.success) throw new Error("GitHub returned invalid release progress metadata");
|
|
239
|
+
reportGithubProgress(parsed.data, progress);
|
|
240
|
+
completed = parsed.data.status === "completed";
|
|
241
|
+
conclusion = parsed.data.conclusion;
|
|
242
|
+
if (!completed) await delay(pollIntervalMs);
|
|
243
|
+
}
|
|
244
|
+
if (conclusion !== "success" && conclusion !== "skipped") {
|
|
245
|
+
runTaskCommand(
|
|
246
|
+
root,
|
|
247
|
+
"gh",
|
|
248
|
+
["run", "view", String(run.databaseId), "--repo", repository, "--log-failed"],
|
|
249
|
+
{ env: account.env },
|
|
250
|
+
);
|
|
251
|
+
}
|
|
149
252
|
} catch (error) {
|
|
150
253
|
logger.warn("GitHub Actions release watch stopped; the remote run was not cancelled", {
|
|
151
254
|
error: errorUtils.toError(error),
|
|
@@ -374,9 +477,10 @@ export async function runRelease(
|
|
|
374
477
|
/** Path to complete release notes supplied instead of invoking Genie. */
|
|
375
478
|
readonly releaseNotesFile?: string;
|
|
376
479
|
readonly writeReleaseNotes?: typeof writeReleaseNotes;
|
|
377
|
-
/**
|
|
480
|
+
/** After local release actions, wait for and stream the matching GitHub Actions run. */
|
|
378
481
|
readonly watchGithubActions?: boolean;
|
|
379
482
|
readonly githubActionsWatcher?: ReleaseGithubActionsWatcher;
|
|
483
|
+
readonly resolveGithubAccount?: typeof projectUtils.resolveProjectGhAccount;
|
|
380
484
|
},
|
|
381
485
|
): Promise<string> {
|
|
382
486
|
const { branch, prefix, remote, root } = options;
|
|
@@ -480,23 +584,6 @@ export async function runRelease(
|
|
|
480
584
|
}
|
|
481
585
|
logger.success(`released ${tag}`, { sha: head });
|
|
482
586
|
|
|
483
|
-
if (shouldWatchReleaseGithubActions(options, selection)) {
|
|
484
|
-
const account = projectUtils.resolveProjectGhAccount(root, { remote });
|
|
485
|
-
if (account) {
|
|
486
|
-
await (options.githubActionsWatcher ?? watchReleaseGithubActions)({
|
|
487
|
-
account,
|
|
488
|
-
head,
|
|
489
|
-
root,
|
|
490
|
-
tag,
|
|
491
|
-
});
|
|
492
|
-
} else {
|
|
493
|
-
logger.info("skipping GitHub Actions release watch: no authenticated account", {
|
|
494
|
-
remote,
|
|
495
|
-
tag,
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
|
|
500
587
|
// Restore local deploys: when a non-standard (loopback) npm or uv registry is
|
|
501
588
|
// configured, publish the freshly tagged version to it directly. Detection and
|
|
502
589
|
// publishing are owned by local-publish.ts; this no-ops on standard registries.
|
|
@@ -518,6 +605,25 @@ export async function runRelease(
|
|
|
518
605
|
]);
|
|
519
606
|
}
|
|
520
607
|
|
|
608
|
+
if (shouldWatchReleaseGithubActions(options, selection)) {
|
|
609
|
+
const account = (options.resolveGithubAccount ?? projectUtils.resolveProjectGhAccount)(root, {
|
|
610
|
+
remote,
|
|
611
|
+
});
|
|
612
|
+
if (account) {
|
|
613
|
+
await (options.githubActionsWatcher ?? watchReleaseGithubActions)({
|
|
614
|
+
account,
|
|
615
|
+
head,
|
|
616
|
+
root,
|
|
617
|
+
tag,
|
|
618
|
+
});
|
|
619
|
+
} else {
|
|
620
|
+
logger.info("skipping GitHub Actions release watch: no authenticated account", {
|
|
621
|
+
remote,
|
|
622
|
+
tag,
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
521
627
|
return tag;
|
|
522
628
|
}
|
|
523
629
|
|
|
@@ -552,7 +658,7 @@ export const ReleaseOptionsSchema = z.object({
|
|
|
552
658
|
watchGithubActions: z
|
|
553
659
|
.boolean()
|
|
554
660
|
.default(true)
|
|
555
|
-
.describe("Wait for and
|
|
661
|
+
.describe("Wait for and log the tag-triggered GitHub Actions release"),
|
|
556
662
|
releaseNotes: z
|
|
557
663
|
.boolean()
|
|
558
664
|
.default(true)
|