@dbx-tools/projen 0.6.200 → 0.6.202

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
@@ -406,9 +406,11 @@ release branch when needed, and pushes it. It prepares the dedicated
406
406
  checkout on its current branch throughout validation, local publication, and
407
407
  PR creation. Failed preparation can resume from that worktree; success removes
408
408
  it. `--message` sets the source commit message. The task never merges
409
- the PR unless `--approve` is passed; that option requests an immediate admin
410
- merge so the main release can start without waiting for PR checks. npm uses
411
- `npm config get registry` and publishes to a local Verdaccio automatically.
409
+ the PR unless `--approve` is passed; that option merges the release branch
410
+ directly through GitHub's merge API so no PR checks are created. A repository
411
+ that blocks direct merges falls back to an immediate admin-merged PR. The task
412
+ then fast-forwards the still-active source branch to the release commit. npm
413
+ uses `npm config get registry` and publishes to a local Verdaccio automatically.
412
414
  Publishable JavaScript members compile once
413
415
  from the root in parallel, then upload through a bounded pool without rerunning
414
416
  their `prepack` tasks. Python prefers uv's default index and only
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.6.200";
6
+ export const PACKAGE_VERSION = "0.6.202";
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
@@ -25,9 +25,9 @@
25
25
  },
26
26
  "dependencies": {
27
27
  "@clack/prompts": "^1.7.0",
28
- "@dbx-tools/core": "0.6.200",
29
- "@dbx-tools/path": "0.6.200",
30
- "@dbx-tools/shared-core": "0.6.200",
28
+ "@dbx-tools/core": "0.6.202",
29
+ "@dbx-tools/path": "0.6.202",
30
+ "@dbx-tools/shared-core": "0.6.202",
31
31
  "commander": "^15.0.0",
32
32
  "concurrently": "^10.0.3",
33
33
  "constructs": "^10.6.0",
@@ -47,7 +47,7 @@
47
47
  },
48
48
  "main": "index.ts",
49
49
  "license": "Apache-2.0",
50
- "version": "0.6.200",
50
+ "version": "0.6.202",
51
51
  "types": "index.ts",
52
52
  "type": "module",
53
53
  "exports": {
package/src/project-js.ts CHANGED
@@ -13,7 +13,7 @@ import { ignore, match } from "@dbx-tools/path";
13
13
  import { object, string, type OneOrMany } from "@dbx-tools/shared-core";
14
14
  import { type IConstruct } from "constructs";
15
15
  import { Component, IgnoreFile, Project, type TaskOptions, javascript, typescript } from "projen";
16
- import type { JobStep } from "projen/lib/github/workflows-model";
16
+ import { JobPermission, type JobStep } from "projen/lib/github/workflows-model";
17
17
  import { ReleaseTrigger } from "projen/lib/release";
18
18
  import { mixin } from "..";
19
19
  import { generateBarrels } from "./barrels.ts";
@@ -456,6 +456,10 @@ function defaultProjectOptions(
456
456
  }
457
457
  : {}),
458
458
  ...options,
459
+ githubOptions: {
460
+ ...options.githubOptions,
461
+ pullRequestLint: false,
462
+ },
459
463
  ...copiedGitIgnoreOptions(options),
460
464
  };
461
465
  }
@@ -861,6 +865,23 @@ class WorkflowDefaults extends Component {
861
865
  ? () => rewrite(configuredSteps())
862
866
  : rewrite(configuredSteps);
863
867
  }
868
+ workflow.addJob("pr-title", {
869
+ name: "Validate PR title",
870
+ runsOn: ["ubuntu-latest"],
871
+ permissions: { pullRequests: JobPermission.READ },
872
+ if: "${{ github.event_name == 'pull_request' && github.event.action != 'closed' }}",
873
+ steps: [
874
+ {
875
+ name: "Validate semantic title",
876
+ uses: "amannn/action-semantic-pull-request@v6",
877
+ env: { GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" },
878
+ with: {
879
+ types: ["feat", "fix", "chore"].join("\n"),
880
+ requireScope: false,
881
+ },
882
+ },
883
+ ],
884
+ });
864
885
  }
865
886
  const build = this.project.tryFindObjectFile(".github/workflows/build.yml");
866
887
  build?.addOverride("on.pull_request.types", ["opened", "synchronize", "reopened", "closed"]);
@@ -871,31 +892,11 @@ class WorkflowDefaults extends Component {
871
892
  for (const job of ["build", "self-mutation"]) {
872
893
  build?.addOverride(`jobs.${job}.timeout-minutes`, 30);
873
894
  }
874
- const pullRequestLint = this.project.tryFindObjectFile(
875
- ".github/workflows/pull-request-lint.yml",
876
- );
877
- pullRequestLint?.addOverride("on.pull_request_target.types", [
878
- "labeled",
879
- "opened",
880
- "synchronize",
881
- "reopened",
882
- "ready_for_review",
883
- "edited",
884
- "closed",
885
- ]);
886
- pullRequestLint?.addOverride(
887
- "jobs.validate.if",
888
- "(github.event_name == 'pull_request' || github.event_name == 'pull_request_target') && github.event.action != 'closed'",
889
- );
890
- pullRequestLint?.addOverride("jobs.validate.timeout-minutes", 10);
891
- for (const name of ["build", "pull-request-lint"]) {
892
- const workflow = this.project.tryFindObjectFile(`.github/workflows/${name}.yml`);
893
- workflow?.addOverride("permissions", { contents: "read" });
894
- workflow?.addOverride("concurrency", {
895
- group: "${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}",
896
- "cancel-in-progress": true,
897
- });
898
- }
895
+ build?.addOverride("permissions", { contents: "read" });
896
+ build?.addOverride("concurrency", {
897
+ group: "${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}",
898
+ "cancel-in-progress": true,
899
+ });
899
900
  }
900
901
  }
901
902
 
@@ -105,11 +105,11 @@ function runIgnoringStdout(root: string, command: string, args: string[]): void
105
105
  });
106
106
  }
107
107
 
108
- function githubAccount(root: string): { owner: string; token: string } {
108
+ function githubAccount(root: string): { owner: string; repository: string; token: string } {
109
109
  const repository = project.repositoryUrl(root);
110
110
  if (!repository) throw new Error("Release preparation requires a GitHub repository");
111
- const owner = new URL(repository).pathname.split("/").filter(Boolean)[0];
112
- if (!owner) throw new Error(`Cannot determine GitHub owner from ${repository}`);
111
+ const [owner, name] = new URL(repository).pathname.split("/").filter(Boolean);
112
+ if (!owner || !name) throw new Error(`Cannot determine GitHub identity from ${repository}`);
113
113
  const token = exec
114
114
  .spawnSync("gh", ["auth", "token", "--user", owner], {
115
115
  cwd: root,
@@ -120,7 +120,7 @@ function githubAccount(root: string): { owner: string; token: string } {
120
120
  })
121
121
  .stdout?.trim();
122
122
  if (!token) throw new Error(`No GitHub CLI authentication found for ${owner}`);
123
- return { owner, token };
123
+ return { owner, repository: name.replace(/\.git$/, ""), token };
124
124
  }
125
125
 
126
126
  const program = new Command();
@@ -148,7 +148,7 @@ program
148
148
  .option("--local-pypi <value>", "local PyPI index: auto, false, or an explicit URL", "auto")
149
149
  .option("--python-root <path>", "Python workspace package root", "packages/py")
150
150
  .option("--no-local-cargo", "skip local Cargo publication")
151
- .option("--approve", "merge the release PR immediately with admin bypass")
151
+ .option("--approve", "merge the release branch directly into the release base")
152
152
  .action(
153
153
  async (opts: {
154
154
  level: VersionLevel;
@@ -209,7 +209,6 @@ program
209
209
  throw new Error(`Release branch already exists without its worktree: ${releaseBranch}`);
210
210
  }
211
211
  git(root, ["worktree", "add", "-b", releaseBranch, releaseRoot, "HEAD"]);
212
- run(releaseRoot, process.execPath, ["install"]);
213
212
  } else {
214
213
  const releaseStatus = git(
215
214
  releaseRoot,
@@ -227,6 +226,7 @@ program
227
226
  }
228
227
  logger.info(`resuming ${releaseBranch} in ${releaseRoot}`);
229
228
  }
229
+ run(releaseRoot, process.execPath, ["install"]);
230
230
 
231
231
  const bumpScript = fileURLToPath(new URL("./bump.ts", import.meta.url));
232
232
  const versionCheckScript = fileURLToPath(new URL("./version-check.ts", import.meta.url));
@@ -272,7 +272,10 @@ program
272
272
  } else if (!worktreeExists) {
273
273
  throw new Error("Release preparation produced no changes");
274
274
  }
275
- git(releaseRoot, ["push", "--set-upstream", "origin", releaseBranch]);
275
+ // The source push and release commit hooks have already scanned every new
276
+ // byte. A new remote branch has no upstream comparison point, so the
277
+ // managed pre-push hook would rescan the repository's complete history.
278
+ git(releaseRoot, ["push", "--no-verify", "--set-upstream", "origin", releaseBranch]);
276
279
 
277
280
  const title = `chore(release): ${next.version}`;
278
281
  const body = [
@@ -283,7 +286,8 @@ program
283
286
  "Merging this PR updates VERSION on main and starts the public release workflow.",
284
287
  ].join("\n");
285
288
  const githubEnvironment = { ...process.env, GH_TOKEN: account.token };
286
- if (!commandSucceeds(root, "gh", ["pr", "view", releaseBranch], githubEnvironment)) {
289
+ const ensurePullRequest = (): void => {
290
+ if (commandSucceeds(root, "gh", ["pr", "view", releaseBranch], githubEnvironment)) return;
287
291
  run(
288
292
  root,
289
293
  "gh",
@@ -301,9 +305,35 @@ program
301
305
  ],
302
306
  githubEnvironment,
303
307
  );
304
- }
308
+ };
305
309
  if (opts.approve) {
306
- run(root, "gh", ["pr", "merge", releaseBranch, "--admin", "--merge"], githubEnvironment);
310
+ const merged = commandSucceeds(
311
+ root,
312
+ "gh",
313
+ [
314
+ "api",
315
+ "--method",
316
+ "POST",
317
+ `repos/${account.owner}/${account.repository}/merges`,
318
+ "-f",
319
+ `base=${opts.base}`,
320
+ "-f",
321
+ `head=${releaseBranch}`,
322
+ "-f",
323
+ `commit_message=Merge ${releaseTag}`,
324
+ ],
325
+ githubEnvironment,
326
+ );
327
+ if (!merged) {
328
+ ensurePullRequest();
329
+ run(root, "gh", ["pr", "merge", releaseBranch, "--admin", "--merge"], githubEnvironment);
330
+ }
331
+ git(releaseRoot, ["push", "--no-verify", "origin", "--delete", releaseBranch]);
332
+ git(root, ["fetch", "origin", opts.base]);
333
+ git(root, ["merge", "--ff-only", `origin/${opts.base}`]);
334
+ git(root, ["push", "origin", currentBranch]);
335
+ } else {
336
+ ensurePullRequest();
307
337
  }
308
338
  git(root, ["worktree", "remove", "--force", releaseRoot]);
309
339
  git(root, ["branch", "--delete", "--force", releaseBranch]);