@dbx-tools/projen 0.9.21 → 0.9.29

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.
Files changed (111) hide show
  1. package/README.md +77 -207
  2. package/index.ts +10 -14
  3. package/package.json +4 -4
  4. package/src/barrels.ts +1 -74
  5. package/src/module-exports.ts +175 -1
  6. package/src/openapi.ts +5 -72
  7. package/src/packages.ts +45 -0
  8. package/src/project-js.ts +3 -6
  9. package/src/project-predicate.ts +1 -2
  10. package/src/project-py.ts +82 -103
  11. package/src/project.ts +2 -2
  12. package/src/python-node-bindings.ts +138 -0
  13. package/src/python-node-bundle.ts +92 -0
  14. package/src/release-catalog.ts +3 -5
  15. package/src/{release-dispatch.ts → release-context.ts} +0 -11
  16. package/src/release.ts +70 -152
  17. package/tasks/bump.ts +12 -21
  18. package/tasks/databricks-cli-assets.ts +129 -0
  19. package/tasks/local-publish.ts +23 -107
  20. package/tasks/publish-npm.ts +10 -1
  21. package/tasks/publish-python.ts +26 -11
  22. package/tasks/publish.ts +12 -48
  23. package/tasks/python-node-bindings-watch.ts +47 -0
  24. package/tasks/python-node-bindings.ts +1017 -0
  25. package/tasks/python-release.ts +74 -0
  26. package/tasks/release.ts +99 -0
  27. package/tasks/sync.ts +13 -18
  28. package/tasks/version-check.ts +1 -28
  29. package/._LICENSE +0 -0
  30. package/._README.md +0 -0
  31. package/._index.ts +0 -0
  32. package/._package.json +0 -0
  33. package/src/.__lazy-require.ts +0 -0
  34. package/src/.__release-platform.ts +0 -0
  35. package/src/.__rust-project.ts +0 -0
  36. package/src/.__rust-release-workflow.ts +0 -0
  37. package/src/.__task-command.ts +0 -0
  38. package/src/._barrels.ts +0 -0
  39. package/src/._bun-app.ts +0 -0
  40. package/src/._bun-workflow.ts +0 -0
  41. package/src/._clean.ts +0 -0
  42. package/src/._codegen.ts +0 -0
  43. package/src/._dbx-tools-config.ts +0 -0
  44. package/src/._engine-root.ts +0 -0
  45. package/src/._generated.ts +0 -0
  46. package/src/._mixin.ts +0 -0
  47. package/src/._module-exports.ts +0 -0
  48. package/src/._openapi.ts +0 -0
  49. package/src/._packages.ts +0 -0
  50. package/src/._pnpm-workspace.ts +0 -0
  51. package/src/._project-js.ts +0 -0
  52. package/src/._project-predicate.ts +0 -0
  53. package/src/._project-py.ts +0 -0
  54. package/src/._project-rs.ts +0 -0
  55. package/src/._project.ts +0 -0
  56. package/src/._projen-version.ts +0 -0
  57. package/src/._publish.ts +0 -0
  58. package/src/._release-catalog.ts +0 -0
  59. package/src/._release-dispatch.ts +0 -0
  60. package/src/._release-github.ts +0 -0
  61. package/src/._release.ts +0 -0
  62. package/src/._scaffold.ts +0 -0
  63. package/src/._tags.ts +0 -0
  64. package/src/._tsconfig.ts +0 -0
  65. package/src/._uniffi.ts +0 -0
  66. package/src/._vscode.ts +0 -0
  67. package/src/._watch.ts +0 -0
  68. package/src/._workspace-lock.ts +0 -0
  69. package/src/._workspace-version.ts +0 -0
  70. package/src/_release-platform.ts +0 -78
  71. package/src/_rust-project.ts +0 -429
  72. package/src/_rust-release-workflow.ts +0 -311
  73. package/src/project-rs.ts +0 -1054
  74. package/src/release-github.ts +0 -109
  75. package/src/uniffi.ts +0 -65
  76. package/tasks/._barrels.ts +0 -0
  77. package/tasks/._bump.ts +0 -0
  78. package/tasks/._clean.ts +0 -0
  79. package/tasks/._compile-workspace.ts +0 -0
  80. package/tasks/._local-publish.ts +0 -0
  81. package/tasks/._openapi.ts +0 -0
  82. package/tasks/._projenrc.ts +0 -0
  83. package/tasks/._publish-npm.ts +0 -0
  84. package/tasks/._publish-python.ts +0 -0
  85. package/tasks/._publish-uniffi-local.ts +0 -0
  86. package/tasks/._publish.ts +0 -0
  87. package/tasks/._python-registry.ts +0 -0
  88. package/tasks/._release-assets.ts +0 -0
  89. package/tasks/._release-candidate.ts +0 -0
  90. package/tasks/._release-manifest.ts +0 -0
  91. package/tasks/._release-pr.ts +0 -0
  92. package/tasks/._release-summary.ts +0 -0
  93. package/tasks/._release-version.ts +0 -0
  94. package/tasks/._rust.ts +0 -0
  95. package/tasks/._sync.ts +0 -0
  96. package/tasks/._uniffi-python.d.ts +0 -0
  97. package/tasks/._uniffi-python.js +0 -0
  98. package/tasks/._uniffi-release.mjs +0 -0
  99. package/tasks/._uniffi.ts +0 -0
  100. package/tasks/._version-check.ts +0 -0
  101. package/tasks/publish-uniffi-local.ts +0 -359
  102. package/tasks/release-assets.ts +0 -467
  103. package/tasks/release-candidate.ts +0 -416
  104. package/tasks/release-manifest.ts +0 -273
  105. package/tasks/release-pr.ts +0 -589
  106. package/tasks/release-summary.ts +0 -379
  107. package/tasks/rust.ts +0 -173
  108. package/tasks/uniffi-python.d.ts +0 -60
  109. package/tasks/uniffi-python.js +0 -178
  110. package/tasks/uniffi-release.mjs +0 -579
  111. package/tasks/uniffi.ts +0 -262
package/src/release.ts CHANGED
@@ -6,14 +6,7 @@ import { JobPermission, type Job, type JobStep } from "projen/lib/github/workflo
6
6
  import { BUN_VERSION, bunCacheRestoreSteps, bunCacheSaveStep } from "./bun-workflow.ts";
7
7
  import { projectReleaseBranch, type DBXToolsJavaScriptProject } from "./project-js.ts";
8
8
  import { applyTasks, taskScript } from "./project.ts";
9
- import {
10
- RELEASE_SHA,
11
- RELEASE_TAG,
12
- RELEASE_VERSION,
13
- releaseSourceSteps,
14
- type ReleaseSummaryProviderName,
15
- } from "./release-dispatch.ts";
16
- import { readWorkspaceVersion } from "./workspace-version.ts";
9
+ import { RELEASE_VERSION, releaseSourceSteps } from "./release-context.ts";
17
10
 
18
11
  const NODE_VERSION = "24";
19
12
  const NPM_VERSION = "11.4.2";
@@ -23,7 +16,7 @@ const releaseTagPrefixes = new WeakMap<DBXToolsJavaScriptProject, string>();
23
16
  const releaseWorkflows = new WeakMap<DBXToolsJavaScriptProject, GithubWorkflow>();
24
17
 
25
18
  /** Independently recoverable portions of the release workflow. */
26
- export type ReleaseStage = "all" | "node" | "python" | "cargo" | "docs";
19
+ export type ReleaseStage = "all" | "node" | "python" | "docs";
27
20
 
28
21
  /** GitHub Pages configuration included in the unified release workflow. */
29
22
  export interface ReleaseDocsOptions {
@@ -37,16 +30,6 @@ export interface ReleaseDocsOptions {
37
30
  readonly artifactPath: string;
38
31
  }
39
32
 
40
- /** Optional local AI summary generation during reviewed release preparation. */
41
- export interface ReleaseSummaryOptions {
42
- /**
43
- * Provider fallback order.
44
- *
45
- * @default ["cursor", "codex", "claude"]
46
- */
47
- readonly providers?: readonly ReleaseSummaryProviderName[];
48
- }
49
-
50
33
  /** Options for {@link DBXToolsRelease}. */
51
34
  export interface DBXToolsReleaseOptions {
52
35
  /** Git tag prefix. Defaults to `v`. */
@@ -55,15 +38,10 @@ export interface DBXToolsReleaseOptions {
55
38
  readonly nodeRelease?: boolean;
56
39
  /** Build and deploy generated documentation through GitHub Pages. */
57
40
  readonly docs?: ReleaseDocsOptions;
58
- /** Python package root passed to local release preparation. */
41
+ /** Python package root used by attached Python publication jobs. */
59
42
  readonly pythonRoot?: string;
60
- /** Repository task names run after VERSION generation and before candidate construction. */
43
+ /** Repository task names run after VERSION verification and before publication. */
61
44
  readonly validationTasks?: readonly string[];
62
- /**
63
- * Generate a versioned release summary locally. Defaults to enabled with the
64
- * standard Cursor, Codex, Claude fallback order.
65
- */
66
- readonly summary?: boolean | ReleaseSummaryOptions;
67
45
  }
68
46
 
69
47
  /** Locate the unified workflow so attached language workspaces can add jobs. */
@@ -90,32 +68,22 @@ export function releaseTagPattern(project: DBXToolsJavaScriptProject): string {
90
68
  return `${prefix}*`;
91
69
  }
92
70
 
93
- /** Run a release stage on promotion or when selected for manual recovery. */
71
+ /** Run a release stage after its prerequisite tag-build jobs succeed. */
94
72
  export function releaseStageCondition(
95
- stage: Exclude<ReleaseStage, "all">,
73
+ _stage: Exclude<ReleaseStage, "all">,
96
74
  prerequisites: readonly string[] = [],
97
75
  ): string {
98
- if (prerequisites.length === 0) {
99
- return `\${{ github.event_name == 'release' || inputs.stage == 'all' || inputs.stage == '${stage}' }}`;
100
- }
101
- const prefix = prerequisites.length
102
- ? `always() && ${prerequisites.map((condition) => `(${condition})`).join(" && ")} && `
103
- : "";
104
- return `\${{ ${prefix}(github.event_name == 'release' || inputs.stage == 'all' || inputs.stage == '${stage}') }}`;
76
+ if (prerequisites.length === 0) return "${{ success() }}";
77
+ return `\${{ always() && ${prerequisites.map((condition) => `(${condition})`).join(" && ")} }}`;
105
78
  }
106
79
 
107
- /** Publish a selected stage unless a manual run remains in dry-run mode. */
80
+ /** Publish a release stage after its prerequisite tag-build jobs succeed. */
108
81
  export function releasePublishCondition(
109
- stage: Exclude<ReleaseStage, "all">,
82
+ _stage: Exclude<ReleaseStage, "all">,
110
83
  prerequisites: readonly string[] = [],
111
84
  ): string {
112
- if (prerequisites.length === 0) {
113
- return `\${{ github.event_name == 'release' || (inputs.dry_run != true && (inputs.stage == 'all' || inputs.stage == '${stage}')) }}`;
114
- }
115
- const prefix = prerequisites.length
116
- ? `always() && ${prerequisites.map((condition) => `(${condition})`).join(" && ")} && `
117
- : "";
118
- return `\${{ ${prefix}(github.event_name == 'release' || (inputs.dry_run != true && (inputs.stage == 'all' || inputs.stage == '${stage}'))) }}`;
85
+ if (prerequisites.length === 0) return "${{ success() }}";
86
+ return `\${{ always() && ${prerequisites.map((condition) => `(${condition})`).join(" && ")} }}`;
119
87
  }
120
88
 
121
89
  /** Shared Bun, Node, cache, and install setup for Node release jobs. */
@@ -142,17 +110,49 @@ export function nodeReleaseSetupSteps(project: DBXToolsJavaScriptProject): reado
142
110
  export function npmPublishEnvironment(): Record<string, string> {
143
111
  return {
144
112
  NODE_AUTH_TOKEN: "${{ secrets.NPM_TOKEN }}",
145
- NPM_CONFIG_PROVENANCE:
146
- "${{ (github.event_name == 'release' || inputs.dry_run != true) && 'true' || 'false' }}",
147
- DRY_RUN:
148
- "${{ github.event_name == 'workflow_dispatch' && inputs.dry_run && '--dry-run' || '' }}",
113
+ NPM_CONFIG_PROVENANCE: "true",
114
+ DRY_RUN: "",
149
115
  };
150
116
  }
151
117
 
152
- function verifyContextJob(tagPrefix: string, releaseBranch: string): Job {
118
+ /** Install uv and restore its download/build cache using Python manifests. */
119
+ export function uvSetupStep(): JobStep {
120
+ return {
121
+ name: "Setup uv",
122
+ uses: "astral-sh/setup-uv@v7",
123
+ with: {
124
+ "enable-cache": true,
125
+ "cache-dependency-glob": "**/pyproject.toml",
126
+ },
127
+ };
128
+ }
129
+
130
+ function refreshDocsRegistryDependencies(workflow: GithubWorkflow): void {
131
+ const docs = workflow.getJob("build-docs") as Job | undefined;
132
+ if (!docs) return;
133
+ const registryJobs = Object.keys(workflow.jobs).filter(
134
+ (name) => name === "publish-node" || name.startsWith("publish-pypi-"),
135
+ );
136
+ if (registryJobs.length > 0) workflow.updateJob("build-docs", { ...docs, needs: registryJobs });
137
+ }
138
+
139
+ /** Keep documentation publication behind every configured package registry. */
140
+ export function refreshReleaseDocsDependencies(project: DBXToolsJavaScriptProject): void {
141
+ const workflow = releaseWorkflows.get(project);
142
+ if (workflow) refreshDocsRegistryDependencies(workflow);
143
+ }
144
+
145
+ function verifyContextJob(
146
+ project: DBXToolsJavaScriptProject,
147
+ tagPrefix: string,
148
+ releaseBranch: string,
149
+ options: DBXToolsReleaseOptions,
150
+ ): Job {
153
151
  return {
154
152
  runsOn: ["ubuntu-latest"],
155
153
  permissions: { contents: JobPermission.READ },
154
+ timeoutMinutes: 30,
155
+ env: { BUN_VERSION, CI: "true" },
156
156
  outputs: {
157
157
  release_tag: { stepId: "release", outputName: "release_tag" },
158
158
  expected_sha: { stepId: "release", outputName: "expected_sha" },
@@ -163,24 +163,19 @@ function verifyContextJob(tagPrefix: string, releaseBranch: string): Job {
163
163
  name: "Checkout release source",
164
164
  uses: "actions/checkout@v6",
165
165
  with: {
166
- ref: "${{ github.event_name == 'release' && github.event.release.tag_name || inputs.release_tag }}",
166
+ ref: "${{ github.ref_name }}",
167
167
  "fetch-depth": 0,
168
168
  },
169
169
  },
170
- {
171
- name: "Setup Bun",
172
- uses: "oven-sh/setup-bun@v2",
173
- with: { "bun-version": BUN_VERSION },
174
- },
170
+ ...bunCacheRestoreSteps(project, { ignorePaths: project.workflowCacheIgnorePaths }),
175
171
  { name: "Install release validation dependencies", run: "bun install --frozen-lockfile" },
172
+ bunCacheSaveStep(),
176
173
  {
177
174
  name: "Verify release context",
178
175
  id: "release",
179
176
  shell: "bash",
180
177
  env: {
181
- RELEASE_TAG:
182
- "${{ github.event_name == 'release' && github.event.release.tag_name || inputs.release_tag }}",
183
- GH_TOKEN: "${{ github.token }}",
178
+ RELEASE_TAG: "${{ github.ref_name }}",
184
179
  },
185
180
  // prettier-ignore
186
181
  run: stringUtils.dedent(
@@ -194,17 +189,8 @@ function verifyContextJob(tagPrefix: string, releaseBranch: string): Job {
194
189
  test "$(git cat-file -t "$RELEASE_TAG")" = "tag"
195
190
  RELEASE_SHA="$(git rev-parse "$RELEASE_TAG^{commit}")"
196
191
  test "$(git rev-parse HEAD)" = "$RELEASE_SHA"
197
- git merge-base --is-ancestor "$RELEASE_SHA" "origin/${releaseBranch}"
192
+ test "$(git rev-parse "origin/${releaseBranch}")" = "$RELEASE_SHA"
198
193
  test "$(tr -d '\\r\\n' < VERSION)" = "$RELEASE_VERSION"
199
- test "$(gh release view "$RELEASE_TAG" --json isDraft --jq .isDraft)" = "false"
200
- rm -rf dist/release-candidate
201
- mkdir -p dist/release-candidate
202
- gh release download "$RELEASE_TAG" --dir dist/release-candidate
203
- bun node_modules/@dbx-tools/projen/tasks/release-manifest.ts verify \
204
- --directory dist/release-candidate \
205
- --tag "$RELEASE_TAG" \
206
- --sha "$RELEASE_SHA" \
207
- --version "$RELEASE_VERSION"
208
194
  echo "release_tag=$RELEASE_TAG" >> "$GITHUB_OUTPUT"
209
195
  echo "expected_sha=$RELEASE_SHA" >> "$GITHUB_OUTPUT"
210
196
  echo "release_version=$RELEASE_VERSION" >> "$GITHUB_OUTPUT"
@@ -212,6 +198,10 @@ function verifyContextJob(tagPrefix: string, releaseBranch: string): Job {
212
198
  // ============================================================================
213
199
  ),
214
200
  },
201
+ ...(options.validationTasks ?? []).map((task) => ({
202
+ name: `Validate ${task}`,
203
+ run: `bun run ${task}`,
204
+ })),
215
205
  ],
216
206
  };
217
207
  }
@@ -227,26 +217,9 @@ function nodePublishJob(project: DBXToolsJavaScriptProject): Job {
227
217
  steps: [
228
218
  ...nodeReleaseSetupSteps(project),
229
219
  {
230
- name: "Download approved npm archives",
231
- env: { GH_TOKEN: "${{ github.token }}", RELEASE_SHA, RELEASE_TAG, RELEASE_VERSION },
232
- shell: "bash",
233
- run: [
234
- "rm -rf dist/release-download dist/npm-release",
235
- "mkdir -p dist/release-download",
236
- 'gh release download "$RELEASE_TAG" --pattern release-manifest.json --pattern SHA256SUMS --pattern "*.tgz" --dir dist/release-download',
237
- "bun node_modules/@dbx-tools/projen/tasks/release-manifest.ts verify \\",
238
- " --directory dist/release-download \\",
239
- ' --tag "$RELEASE_TAG" \\',
240
- ' --sha "$RELEASE_SHA" \\',
241
- ' --version "$RELEASE_VERSION" \\',
242
- " --kind npm \\",
243
- " --output dist/npm-release",
244
- ].join("\n"),
245
- },
246
- {
247
- name: "Publish approved npm archives",
220
+ name: "Publish npm workspace",
248
221
  env: { RELEASE_VERSION, ...npmPublishEnvironment() },
249
- run: 'bun node_modules/@dbx-tools/projen/tasks/publish-npm.ts --directory dist/npm-release --version "$RELEASE_VERSION" $DRY_RUN',
222
+ run: 'bun node_modules/@dbx-tools/projen/tasks/publish.ts "$RELEASE_VERSION" $DRY_RUN',
250
223
  },
251
224
  ],
252
225
  };
@@ -303,6 +276,7 @@ function addDocsJobs(
303
276
  },
304
277
  ],
305
278
  });
279
+ refreshDocsRegistryDependencies(workflow);
306
280
  }
307
281
 
308
282
  /** Owns the single release workflow and local release preparation tasks. */
@@ -311,18 +285,11 @@ export class DBXToolsRelease extends Component {
311
285
  super(project);
312
286
  const tagPrefix = options.tagPrefix ?? "v";
313
287
  const releaseBranch = projectReleaseBranch(project);
314
- const summary = options.summary ?? true;
315
- const summaryArgs =
316
- summary === false || (typeof summary === "object" && summary.providers?.length === 0)
317
- ? ["--no-release-summary"]
318
- : typeof summary === "object" && summary.providers
319
- ? [`--release-summary-providers ${summary.providers.join(",")}`]
320
- : [];
321
288
  releaseTagPrefixes.set(project, tagPrefix);
322
289
  if (options.nodeRelease !== false) nodeReleaseProjects.add(project);
323
290
  applyTasks(project, {
324
291
  bump: {
325
- exec: taskScript(project, "bump.ts", `--prefix ${tagPrefix}`),
292
+ exec: taskScript(project, "bump.ts"),
326
293
  receiveArgs: true,
327
294
  description: "Increment VERSION and synchronize generated workspace versions",
328
295
  },
@@ -335,33 +302,11 @@ export class DBXToolsRelease extends Component {
335
302
  release: {
336
303
  exec: taskScript(
337
304
  project,
338
- "release-pr.ts",
339
- [
340
- `--prefix ${tagPrefix}`,
341
- `--base ${releaseBranch}`,
342
- ...(options.pythonRoot
343
- ? [`--python-root ${JSON.stringify(options.pythonRoot)}`]
344
- : []),
345
- ...(options.validationTasks ?? []).map(
346
- (task) => `--validate-task ${JSON.stringify(task)}`,
347
- ),
348
- ...summaryArgs,
349
- ].join(" "),
350
- ),
351
- receiveArgs: true,
352
- description:
353
- "Prepare, validate, locally publish, and upload a draft release candidate",
354
- },
355
- "release:assets": {
356
- exec: taskScript(
357
- project,
358
- "release-candidate.ts",
359
- options.pythonRoot
360
- ? `--python-root ${JSON.stringify(options.pythonRoot)}`
361
- : undefined,
305
+ "release.ts",
306
+ `--prefix ${tagPrefix} --branch ${releaseBranch}`,
362
307
  ),
363
308
  receiveArgs: true,
364
- description: "Rebuild and upload a complete draft release candidate",
309
+ description: "Run bump, commit it, and push an annotated release tag",
365
310
  },
366
311
  }
367
312
  : {}),
@@ -372,49 +317,22 @@ export class DBXToolsRelease extends Component {
372
317
  fileName: "release.yml",
373
318
  limitConcurrency: true,
374
319
  concurrencyOptions: {
375
- group:
376
- "release-${{ github.event_name == 'release' && github.event.release.tag_name || inputs.release_tag }}",
320
+ group: "release-${{ github.ref_name }}",
377
321
  cancelInProgress: false,
378
322
  },
379
323
  });
380
- const version = readWorkspaceVersion(project.outdir);
381
- workflow.runName =
382
- `release ${version} ` +
383
- "${{ github.event_name == 'release' && github.event.release.tag_name || inputs.release_tag }}";
324
+ releaseWorkflows.set(project, workflow);
325
+ workflow.runName = "release ${{ github.ref_name }}";
384
326
  workflow.on({
385
- release: { types: ["published"] },
386
- workflowDispatch: {
387
- inputs: {
388
- release_tag: {
389
- description: "Annotated release tag to validate",
390
- type: "string",
391
- required: true,
392
- },
393
- stage: {
394
- description: "Published release stage to validate or recover",
395
- type: "choice",
396
- options: ["all", "node", "python", "cargo", "docs"],
397
- default: "all",
398
- required: true,
399
- },
400
- dry_run: {
401
- description: "Build and validate without publishing",
402
- type: "boolean",
403
- default: "true",
404
- required: true,
405
- },
406
- },
407
- },
327
+ push: { tags: [`${tagPrefix}*`] },
408
328
  });
409
329
  workflow.file?.addOverride("permissions.contents", "read");
410
- workflow.file?.addOverride("on.workflow_dispatch.inputs.dry_run.default", true);
411
- workflow.addJob("verify-context", verifyContextJob(tagPrefix, releaseBranch));
330
+ workflow.addJob("verify-context", verifyContextJob(project, tagPrefix, releaseBranch, options));
412
331
  if (options.nodeRelease !== false) {
413
332
  workflow.addJob("publish-node", nodePublishJob(project));
414
333
  }
415
334
  if (options.docs) {
416
335
  addDocsJobs(workflow, project, options.docs);
417
336
  }
418
- releaseWorkflows.set(project, workflow);
419
337
  }
420
338
  }
package/tasks/bump.ts CHANGED
@@ -6,43 +6,34 @@
6
6
  */
7
7
  import * as projectUtils from "@dbx-tools/core/project-utils";
8
8
  import { log } from "@dbx-tools/shared-core";
9
- import { Command } from "commander";
9
+ import { Command, Option } from "commander";
10
+ import { runTaskCommand } from "../src/_task-command.ts";
10
11
  import {
11
- releaseArchitectureOption,
12
- releaseLevelOption,
13
- releaseOperatingSystemOption,
14
- releasePlatformFilter,
15
- type ReleaseArch,
16
- type ReleaseOs,
12
+ resolveNextVersion,
17
13
  type VersionLevel,
18
- } from "../src/_release-platform.ts";
19
- import { runTaskCommand } from "../src/_task-command.ts";
20
- import { resolveNextVersion, writeWorkspaceVersion } from "../src/workspace-version.ts";
14
+ writeWorkspaceVersion,
15
+ } from "../src/workspace-version.ts";
21
16
 
22
17
  const logger = log.logger("projen:bump");
23
18
 
24
19
  const program = new Command();
25
20
  program
26
21
  .description("Increment VERSION and synchronize generated workspace versions")
27
- .addOption(releaseLevelOption())
28
- .addOption(releaseOperatingSystemOption())
29
- .addOption(releaseArchitectureOption())
22
+ .addOption(
23
+ new Option("-l, --level <level>", "semver increment")
24
+ .choices(["patch", "minor", "major"])
25
+ .default("patch"),
26
+ )
30
27
  .option("--no-synth", "write VERSION without synchronizing generated files")
31
- .action((opts: { level: VersionLevel; os: ReleaseOs[]; arch: ReleaseArch[]; synth: boolean }) => {
28
+ .action((opts: { level: VersionLevel; synth: boolean }) => {
32
29
  const root = projectUtils.root() ?? process.cwd();
33
30
  const next = resolveNextVersion(root, opts.level);
34
- const releasePlatforms = releasePlatformFilter(opts.os, opts.arch);
35
31
 
36
32
  logger.info(`bump ${next.base} -> ${next.version} (${opts.level})`);
37
33
  writeWorkspaceVersion(root, next.version);
38
34
 
39
35
  if (opts.synth) {
40
- runTaskCommand(root, process.execPath, [".projenrc.ts"], {
41
- env: {
42
- ...process.env,
43
- ...(releasePlatforms ? { DBX_TOOLS_RELEASE_PLATFORMS: releasePlatforms } : {}),
44
- },
45
- });
36
+ runTaskCommand(root, process.execPath, [".projenrc.ts"]);
46
37
  }
47
38
  logger.success(`workspace version synchronized at ${next.version}`);
48
39
  });
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env -S bun
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import { parseArgs } from "node:util";
5
+
6
+ import * as projectUtils from "@dbx-tools/core/project-utils";
7
+
8
+ import { makeReadonly, makeWritable } from "../src/generated.ts";
9
+
10
+ const MINIMUM_VERSION = "0.296.0";
11
+ const REPOSITORY = "databricks/cli";
12
+ const PLATFORM_ASSETS = {
13
+ "darwin-arm64": "darwin_arm64",
14
+ "darwin-x64": "darwin_amd64",
15
+ "linux-arm64": "linux_arm64",
16
+ "linux-x64": "linux_amd64",
17
+ "win32-arm64": "windows_arm64",
18
+ "win32-x64": "windows_amd64",
19
+ } as const;
20
+
21
+ interface GitHubAsset {
22
+ readonly browser_download_url: string;
23
+ readonly digest?: string;
24
+ readonly name: string;
25
+ }
26
+
27
+ interface GitHubRelease {
28
+ readonly assets: GitHubAsset[];
29
+ readonly published_at: string;
30
+ readonly tag_name: string;
31
+ }
32
+
33
+ interface CliAsset {
34
+ readonly sha256: string;
35
+ readonly url: string;
36
+ }
37
+
38
+ interface CliAssetManifest {
39
+ readonly schemaVersion: 1;
40
+ readonly dbxToolsVersion: string;
41
+ readonly minimumVersion: string;
42
+ readonly version: string;
43
+ readonly publishedAt: string;
44
+ readonly assets: Record<keyof typeof PLATFORM_ASSETS, CliAsset>;
45
+ }
46
+
47
+ function validManifest(value: unknown, dbxToolsVersion: string): value is CliAssetManifest {
48
+ if (!value || typeof value !== "object") return false;
49
+ const manifest = value as Partial<CliAssetManifest>;
50
+ return (
51
+ manifest.schemaVersion === 1 &&
52
+ manifest.dbxToolsVersion === dbxToolsVersion &&
53
+ manifest.minimumVersion === MINIMUM_VERSION &&
54
+ typeof manifest.version === "string" &&
55
+ typeof manifest.publishedAt === "string" &&
56
+ Boolean(manifest.assets) &&
57
+ Object.keys(PLATFORM_ASSETS).every((key) => {
58
+ const asset = manifest.assets?.[key as keyof typeof PLATFORM_ASSETS];
59
+ return (
60
+ typeof asset?.url === "string" &&
61
+ typeof asset.sha256 === "string" &&
62
+ /^[0-9a-f]{64}$/.test(asset.sha256)
63
+ );
64
+ })
65
+ );
66
+ }
67
+
68
+ async function latestManifest(dbxToolsVersion: string): Promise<CliAssetManifest> {
69
+ const response = await fetch(`https://api.github.com/repos/${REPOSITORY}/releases/latest`, {
70
+ headers: {
71
+ accept: "application/vnd.github+json",
72
+ "user-agent": `dbx-tools/${dbxToolsVersion}`,
73
+ },
74
+ });
75
+ if (!response.ok) {
76
+ throw new Error(`Could not resolve the latest Databricks CLI release (${response.status})`);
77
+ }
78
+ const release = (await response.json()) as GitHubRelease;
79
+ const version = release.tag_name.replace(/^v/, "");
80
+ const assets = Object.fromEntries(
81
+ Object.entries(PLATFORM_ASSETS).map(([platform, suffix]) => {
82
+ const name = `databricks_cli_${version}_${suffix}.tar.gz`;
83
+ const asset = release.assets.find((candidate) => candidate.name === name);
84
+ const sha256 = asset?.digest?.match(/^sha256:([0-9a-f]{64})$/i)?.[1]?.toLowerCase();
85
+ if (!asset || !sha256)
86
+ throw new Error(`Release ${release.tag_name} has no digest for ${name}`);
87
+ return [platform, { url: asset.browser_download_url, sha256 }];
88
+ }),
89
+ ) as CliAssetManifest["assets"];
90
+ return {
91
+ schemaVersion: 1,
92
+ dbxToolsVersion,
93
+ minimumVersion: MINIMUM_VERSION,
94
+ version,
95
+ publishedAt: release.published_at,
96
+ assets,
97
+ };
98
+ }
99
+
100
+ const { values } = parseArgs({
101
+ options: {
102
+ check: { type: "boolean", default: false },
103
+ root: { type: "string" },
104
+ },
105
+ strict: true,
106
+ });
107
+ const root = resolve(values.root ?? projectUtils.root() ?? process.cwd());
108
+ const output = join(root, "packages/js/node/auth/src/generated/databricks-cli-assets.json");
109
+ const dbxToolsVersion = readFileSync(join(root, "VERSION"), "utf8").trim();
110
+ let current: unknown;
111
+ if (existsSync(output)) {
112
+ try {
113
+ current = JSON.parse(readFileSync(output, "utf8"));
114
+ } catch {
115
+ current = undefined;
116
+ }
117
+ }
118
+ if (validManifest(current, dbxToolsVersion)) {
119
+ console.log(`verified ${output.slice(root.length + 1)}`);
120
+ } else if (values.check) {
121
+ throw new Error("Databricks CLI asset manifest is missing or stale");
122
+ } else {
123
+ const manifest = await latestManifest(dbxToolsVersion);
124
+ mkdirSync(dirname(output), { recursive: true });
125
+ makeWritable(output);
126
+ writeFileSync(output, `${JSON.stringify(manifest, undefined, 2)}\n`);
127
+ makeReadonly(output);
128
+ console.log(`generated ${output.slice(root.length + 1)}`);
129
+ }