@dbx-tools/projen 0.9.149 → 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 CHANGED
@@ -542,10 +542,11 @@ 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
544
  local task completes local publication and demo deployment first, then waits for
545
- this workflow by default. It preserves the live checklist through a scroll-safe
546
- non-TTY stream and prints the final logs. Local-only releases and repositories
547
- without a resolved authenticated GitHub account skip the watcher. Stopping the
548
- local watcher does not cancel the remote run.
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.
549
550
 
550
551
  One `build-release` job installs workspace dependencies, runs validation, packs
551
552
  the npm archives, builds all Python distributions, and builds the documentation
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.149";
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.149",
34
- "@dbx-tools/core": "^0.9.149",
35
- "@dbx-tools/path": "^0.9.149",
36
- "@dbx-tools/shared-core": "^0.9.149",
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.149",
58
+ "version": "0.9.150",
59
59
  "types": "index.ts",
60
60
  "type": "module",
61
61
  "exports": {
@@ -61,31 +61,6 @@ export function spawnTaskCommand(
61
61
  });
62
62
  }
63
63
 
64
- /**
65
- * Run a command with piped stdout forwarded line-by-line.
66
- *
67
- * The child sees a non-TTY output stream, so interactive redraw commands fall
68
- * back to append-only output while the parent still receives live progress.
69
- */
70
- export async function streamTaskCommand(
71
- cwd: string,
72
- command: string,
73
- args: readonly string[],
74
- options: TaskCommandOptions = {},
75
- ): Promise<void> {
76
- await exec.spawn(executable(command), [...args], {
77
- cwd,
78
- env: options.env,
79
- stdout: {
80
- capture: false,
81
- onLine: (line) => console.log(line),
82
- },
83
- stderr: options.stderr ?? "inherit",
84
- stdin: "ignore",
85
- check: true,
86
- });
87
- }
88
-
89
64
  /** Capture trimmed stdout, optionally throwing when the command fails. */
90
65
  export function captureTaskCommand(
91
66
  cwd: string,
package/tasks/release.ts CHANGED
@@ -11,12 +11,7 @@ import { publishLocalRelease } from "./local-publish.ts";
11
11
  import { TaskDirectoriesOptionSchema, TaskRootOptionSchema } from "./options.ts";
12
12
  import { writeReleaseNotes } from "./release-notes.ts";
13
13
  import { assertReleaseVersion } from "./release-version.ts";
14
- import {
15
- captureTaskCommand,
16
- runTaskCommand,
17
- streamTaskCommand,
18
- taskCommandSucceeds,
19
- } from "../src/_task-command.ts";
14
+ import { captureTaskCommand, runTaskCommand, taskCommandSucceeds } from "../src/_task-command.ts";
20
15
  import { RELEASE_WORKFLOW_FILE } from "../src/release-context.ts";
21
16
  import {
22
17
  RELEASE_INSTALL_MODES,
@@ -43,6 +38,33 @@ const GithubReleaseRunSchema = z
43
38
  })
44
39
  .passthrough();
45
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
+
46
68
  /** Inputs required to locate and watch one tag-triggered release workflow. */
47
69
  export interface WatchReleaseGithubActionsOptions {
48
70
  readonly account: projectUtils.ProjectGhAccount;
@@ -68,6 +90,61 @@ function delay(milliseconds: number): Promise<void> {
68
90
  return new Promise((resolve) => setTimeout(resolve, milliseconds));
69
91
  }
70
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
+
71
148
  /** Whether this release selection has remote work worth watching. */
72
149
  export function shouldWatchReleaseGithubActions(
73
150
  options: Pick<ReleaseSelectionOptions, "publish"> & { readonly watchGithubActions?: boolean },
@@ -139,26 +216,39 @@ export async function watchReleaseGithubActions(
139
216
  url: run.url,
140
217
  });
141
218
  try {
142
- await streamTaskCommand(
143
- root,
144
- "gh",
145
- [
146
- "run",
147
- "watch",
148
- String(run.databaseId),
149
- "--repo",
150
- repository,
151
- "--interval",
152
- String(Math.max(1, Math.round(pollIntervalMs / 1_000))),
153
- ],
154
- { env: { ...account.env, NO_COLOR: "1" } },
155
- );
156
- runTaskCommand(
157
- root,
158
- "gh",
159
- ["run", "view", String(run.databaseId), "--repo", repository, "--log"],
160
- { env: account.env },
161
- );
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
+ }
162
252
  } catch (error) {
163
253
  logger.warn("GitHub Actions release watch stopped; the remote run was not cancelled", {
164
254
  error: errorUtils.toError(error),