@dbx-tools/projen 0.9.38 → 0.9.43
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 +73 -9
- package/index.ts +5 -3
- package/package.json +4 -4
- package/src/bun-workflow.ts +6 -166
- package/src/codegen.ts +3 -3
- package/src/project-js.ts +8 -44
- package/src/project-py.ts +52 -17
- package/src/release-context.ts +2 -33
- package/src/release-options.ts +82 -0
- package/src/release.ts +114 -63
- package/tasks/lib/publish-python.ts +16 -2
- package/tasks/lib/python-node-bindings.ts +30 -5
- package/tasks/lib/release.ts +97 -31
- package/tasks/release-options.ts +32 -0
- package/tasks/release.ts +1 -1
package/README.md
CHANGED
|
@@ -58,16 +58,16 @@ workflow.
|
|
|
58
58
|
Repository policy stays in the consuming `.projenrc.ts`:
|
|
59
59
|
|
|
60
60
|
- `releaseDocs` supplies repository-defined preparation and build steps plus
|
|
61
|
-
the Pages artifact path. The engine adds release checkout,
|
|
62
|
-
|
|
61
|
+
the Pages artifact path. The engine adds release checkout, artifact upload,
|
|
62
|
+
and deployment without naming a docs script or output tree.
|
|
63
63
|
- `releasePythonRoot` passes the actual Python package root to local release
|
|
64
64
|
preparation. Omit it when the workspace has no standard Python packages.
|
|
65
65
|
- `releaseValidationTasks` names repository tasks that must pass in tag CI
|
|
66
66
|
before release artifacts are built and published.
|
|
67
|
+
- `releaseSetupSteps` installs any extra validation/build prerequisites in the
|
|
68
|
+
shared release job before the validation tasks run.
|
|
67
69
|
- `pullRequestTitlePolicy` configures semantic title types and scope policy.
|
|
68
70
|
Omit it or pass `false` to disable the title job.
|
|
69
|
-
- `workflowCacheIgnorePaths` excludes generated output trees that may contain
|
|
70
|
-
package manifests from the dependency-only Bun cache key.
|
|
71
71
|
- `extraWorkspaceMembers` declares self-synthesizing tooling packages outside
|
|
72
72
|
`packageRoots`. Their version, generated entrypoint, formatting, linting, and
|
|
73
73
|
workspace membership are derived from that declaration.
|
|
@@ -380,17 +380,45 @@ list, so a new package is covered without a re-synth. Work from the root:
|
|
|
380
380
|
| `bun run version:check` | verify every generated version against `VERSION` |
|
|
381
381
|
| `bun run release` | run the configured release transaction |
|
|
382
382
|
|
|
383
|
-
Run
|
|
383
|
+
Run `bun run release` from any branch. Pending changes are committed and
|
|
384
384
|
the current branch is pushed first. A non-`main` branch then fast-forwards
|
|
385
385
|
`main`; the command fails without creating a merge commit if either branch has
|
|
386
386
|
diverged. From `main`, it calls the existing `bump` task, commits the generated
|
|
387
387
|
version changes, pushes `main`, and pushes the matching annotated `vX.Y.Z` tag.
|
|
388
388
|
Pass `--no-bump` only to release an existing synchronized local bump. The tag
|
|
389
|
-
workflow verifies that the tag points at the exact `origin/main` commit
|
|
390
|
-
|
|
391
|
-
|
|
389
|
+
workflow verifies that the tag points at the exact `origin/main` commit. One
|
|
390
|
+
`build-release` job installs workspace dependencies, runs validation, packs the
|
|
391
|
+
npm archives, builds all Python distributions, and builds the documentation
|
|
392
|
+
site. Install validation prerequisites through `releaseSetupSteps`;
|
|
393
|
+
`releaseDocs.prepareSteps` runs after validation.
|
|
394
|
+
|
|
395
|
+
Publication jobs download the resulting artifacts rather than rebuilding the
|
|
396
|
+
workspace. Each Python package retains its own publishing environment and waits
|
|
397
|
+
for its package dependencies to publish. The npm job uses the bundled archive
|
|
398
|
+
publisher without a checkout or workspace install. Pages deploys the already
|
|
399
|
+
built site after registry publication succeeds. Local npm and Python registry publication remains available
|
|
392
400
|
through the direct local publication tooling.
|
|
393
401
|
|
|
402
|
+
The default release still publishes npm, Python, and docs, then publishes to
|
|
403
|
+
configured local registries automatically. Select a scope or disable individual
|
|
404
|
+
steps for one run without changing the Projen definition:
|
|
405
|
+
|
|
406
|
+
```sh
|
|
407
|
+
bun run release --publish pypi --no-docs
|
|
408
|
+
bun run release --no-npm --no-local-publish
|
|
409
|
+
bun run release --publish local --install never
|
|
410
|
+
bun run release --local-registry false --local-pypi auto
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
`--install auto` keeps Projen's normal local dependency-install behavior;
|
|
414
|
+
`always` performs a frozen install first, and `never` uses dependencies already
|
|
415
|
+
installed while still regenerating versioned sources. `--no-validation` skips
|
|
416
|
+
optional task checks locally and in CI, not version or immutable-source checks.
|
|
417
|
+
`--no-docs` skips building and deploying the site, not package README validation.
|
|
418
|
+
Scopes select artifacts, not the normal commit and tag-push transaction.
|
|
419
|
+
The annotated tag records CI step selections, so publishing a selected scope does
|
|
420
|
+
not change the next release's defaults. Local registry URLs stay local.
|
|
421
|
+
|
|
394
422
|
Members intentionally keep only the tasks that something OTHER than a human
|
|
395
423
|
invokes, so there is no second place to run the same thing:
|
|
396
424
|
|
|
@@ -402,7 +430,7 @@ invokes, so there is no second place to run the same thing:
|
|
|
402
430
|
edit/compile loop to one package.
|
|
403
431
|
- `build` / `package` - a complete compile/test/pack lifecycle when invoked in
|
|
404
432
|
one package. Release preparation validates through the root's filtered tasks,
|
|
405
|
-
and publication
|
|
433
|
+
and publication uploads archives that were each packed and
|
|
406
434
|
validated once with lifecycle scripts disabled. The package phase also packs
|
|
407
435
|
with `--ignore-scripts` because its build already compiled; `prepack` remains
|
|
408
436
|
available for a standalone publish that did not run `build` first.
|
|
@@ -421,3 +449,39 @@ from the root instead.
|
|
|
421
449
|
Projen-owned and reproduce it exactly across JavaScript and Python packages.
|
|
422
450
|
Release jobs publish the generated workspace inventory in dependency-safe
|
|
423
451
|
workflow order without introducing another version owner.
|
|
452
|
+
|
|
453
|
+
<!-- cli-reference:start -->
|
|
454
|
+
|
|
455
|
+
## Command Reference
|
|
456
|
+
|
|
457
|
+
### `release`
|
|
458
|
+
|
|
459
|
+
Prepare an annotated release and select its build and publication steps
|
|
460
|
+
|
|
461
|
+
```sh
|
|
462
|
+
release [options]
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
#### Options
|
|
466
|
+
|
|
467
|
+
| Option | Description |
|
|
468
|
+
| ------------------------------------- | --------------------------------------------------------------------------------------------- |
|
|
469
|
+
| `--root <path>` | repository root |
|
|
470
|
+
| `--branch <name>` | release branch (default: "main") |
|
|
471
|
+
| `--prefix <prefix>` | release tag prefix (default: "v") |
|
|
472
|
+
| `--remote <name>` | git remote (default: "origin") |
|
|
473
|
+
| `--python-root <path>` | Python package root for local publish (default: "packages/py") |
|
|
474
|
+
| `--validate <task>` | task to run before pushing (default: []) |
|
|
475
|
+
| `--no-bump` | use an existing synchronized local version bump |
|
|
476
|
+
| `--publish <target>` | publication scope (choices: "auto", "npm", "pypi", "local", "none", default: "auto") |
|
|
477
|
+
| `--install <mode>` | local workspace dependency installation (choices: "auto", "always", "never", default: "auto") |
|
|
478
|
+
| `--no-npm` | skip npm build and publication, including local npm publication |
|
|
479
|
+
| `--no-pypi` | skip Python build and publication, including local Python publication |
|
|
480
|
+
| `--docs` | build and deploy docs for a selected scope |
|
|
481
|
+
| `--no-docs` | skip documentation build and deployment |
|
|
482
|
+
| `--no-validation` | skip optional release validation tasks; version/source checks remain mandatory |
|
|
483
|
+
| `--no-local-publish` | skip publishing to configured local registries |
|
|
484
|
+
| `--local-registry <auto\|false\|url>` | local npm registry selection (default: "auto") |
|
|
485
|
+
| `--local-pypi <auto\|false\|url>` | local devpi registry selection (default: "auto") |
|
|
486
|
+
|
|
487
|
+
<!-- cli-reference:end -->
|
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.43";
|
|
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";
|
|
@@ -27,6 +27,7 @@ export * as pythonNodeBindings from "./src/python-node-bindings.ts";
|
|
|
27
27
|
export * as pythonNodeBundle from "./src/python-node-bundle.ts";
|
|
28
28
|
export * as release from "./src/release.ts";
|
|
29
29
|
export * as releaseContext from "./src/release-context.ts";
|
|
30
|
+
export * as releaseOptions from "./src/release-options.ts";
|
|
30
31
|
export * as scaffold from "./src/scaffold.ts";
|
|
31
32
|
export * as tags from "./src/tags.ts";
|
|
32
33
|
export * as tsconfig from "./src/tsconfig.ts";
|
|
@@ -36,7 +37,6 @@ export * as workspaceLock from "./src/workspace-lock.ts";
|
|
|
36
37
|
export * as workspaceVersion from "./src/workspace-version.ts";
|
|
37
38
|
export { BUN_DEV_OVERRIDE, BUN_BUILD_OVERRIDE, BUN_APP_OVERRIDES, RootBunfigFile, BunfigFile, BunDevServerFile, BunBuildFile } from "./src/bun-app.ts";
|
|
38
39
|
export { BUN_VERSION } from "./src/bun-workflow.ts";
|
|
39
|
-
export type { BunWorkflowCacheOptions } from "./src/bun-workflow.ts";
|
|
40
40
|
export { DBXToolsConfig } from "./src/dbx-tools-config.ts";
|
|
41
41
|
export type { DBXToolsConfigOptions } from "./src/dbx-tools-config.ts";
|
|
42
42
|
export { resolvePkgRoot } from "./src/engine-root.ts";
|
|
@@ -60,7 +60,9 @@ export { PythonNodeBundle } from "./src/python-node-bundle.ts";
|
|
|
60
60
|
export type { PythonNodeFunctionOverride, PythonNodeBindingsOptions, PythonNodeBundleOptions } from "./src/python-node-bundle.ts";
|
|
61
61
|
export { DBXToolsRelease } from "./src/release.ts";
|
|
62
62
|
export type { ReleaseDocsOptions, DBXToolsReleaseOptions } from "./src/release.ts";
|
|
63
|
-
export {
|
|
63
|
+
export { RELEASE_VERSION } from "./src/release-context.ts";
|
|
64
|
+
export { RELEASE_PUBLISH_TARGETS, RELEASE_INSTALL_MODES } from "./src/release-options.ts";
|
|
65
|
+
export type { ReleasePublishTarget, ReleaseInstallMode, ReleaseStepSelection, ReleaseSelectionOptions } from "./src/release-options.ts";
|
|
64
66
|
export { AGNOSTIC_COMPILER_OPTIONS, PACKAGE_TAG_MIXINS } from "./src/tags.ts";
|
|
65
67
|
export type { PackageTag } from "./src/tags.ts";
|
|
66
68
|
export { DBXToolsRootTsconfig } from "./src/tsconfig.ts";
|
package/package.json
CHANGED
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@clack/prompts": "^1.7.0",
|
|
35
|
-
"@dbx-tools/core": "^0.9.
|
|
36
|
-
"@dbx-tools/path": "^0.9.
|
|
37
|
-
"@dbx-tools/shared-core": "^0.9.
|
|
35
|
+
"@dbx-tools/core": "^0.9.43",
|
|
36
|
+
"@dbx-tools/path": "^0.9.43",
|
|
37
|
+
"@dbx-tools/shared-core": "^0.9.43",
|
|
38
38
|
"commander": "^15.0.0",
|
|
39
39
|
"concurrently": "^10.0.3",
|
|
40
40
|
"constructs": "^10.6.0",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
},
|
|
57
57
|
"main": "index.ts",
|
|
58
58
|
"license": "Apache-2.0",
|
|
59
|
-
"version": "0.9.
|
|
59
|
+
"version": "0.9.43",
|
|
60
60
|
"types": "index.ts",
|
|
61
61
|
"type": "module",
|
|
62
62
|
"exports": {
|
package/src/bun-workflow.ts
CHANGED
|
@@ -1,172 +1,12 @@
|
|
|
1
|
-
/** Shared Bun setup
|
|
2
|
-
import { stringUtils } from "@dbx-tools/shared-core";
|
|
3
|
-
import { TextFile, javascript } from "projen";
|
|
1
|
+
/** Shared Bun setup for generated workflows. */
|
|
4
2
|
|
|
5
3
|
export const BUN_VERSION = "1.3.14";
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
".venv",
|
|
10
|
-
".worktrees",
|
|
11
|
-
"coverage",
|
|
12
|
-
"dist",
|
|
13
|
-
"lib",
|
|
14
|
-
"node_modules",
|
|
15
|
-
"target",
|
|
16
|
-
] as const;
|
|
17
|
-
|
|
18
|
-
function cacheKeyScript(extraIgnorePaths: readonly string[]): string {
|
|
19
|
-
const ignorePaths = [...new Set(extraIgnorePaths)].sort();
|
|
20
|
-
// prettier-ignore
|
|
21
|
-
const source = (
|
|
22
|
-
// ============================================================================
|
|
23
|
-
/*js*/`
|
|
24
|
-
#!/usr/bin/env node
|
|
25
|
-
import { createHash } from "node:crypto";
|
|
26
|
-
import { readdirSync, readFileSync } from "node:fs";
|
|
27
|
-
import { join, sep } from "node:path";
|
|
28
|
-
|
|
29
|
-
const root = process.cwd();
|
|
30
|
-
const ignoredNames = new Set(${JSON.stringify(DEFAULT_CACHE_IGNORE_PATHS)});
|
|
31
|
-
const ignoredPaths = new Set(${JSON.stringify(ignorePaths)});
|
|
32
|
-
const manifests = [];
|
|
33
|
-
const walk = (directory) => {
|
|
34
|
-
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
|
35
|
-
const path = join(directory, entry.name);
|
|
36
|
-
const relativePath = path.slice(root.length + 1).split(sep).join("/");
|
|
37
|
-
if (ignoredNames.has(entry.name) || ignoredPaths.has(relativePath)) continue;
|
|
38
|
-
if (entry.isDirectory()) walk(path);
|
|
39
|
-
else if (entry.name === "package.json") manifests.push(path);
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
walk(root);
|
|
43
|
-
|
|
44
|
-
const dependencyFields = [
|
|
45
|
-
"catalog",
|
|
46
|
-
"dependencies",
|
|
47
|
-
"devDependencies",
|
|
48
|
-
"optionalDependencies",
|
|
49
|
-
"overrides",
|
|
50
|
-
"peerDependencies",
|
|
51
|
-
"peerDependenciesMeta",
|
|
52
|
-
"resolutions",
|
|
53
|
-
"trustedDependencies",
|
|
54
|
-
];
|
|
55
|
-
const canonical = (value) => {
|
|
56
|
-
if (Array.isArray(value)) return value.map(canonical);
|
|
57
|
-
if (!value || typeof value !== "object") return value;
|
|
58
|
-
return Object.fromEntries(
|
|
59
|
-
Object.entries(value)
|
|
60
|
-
.sort(([left], [right]) => left.localeCompare(right))
|
|
61
|
-
.map(([key, child]) => [key, canonical(child)]),
|
|
62
|
-
);
|
|
63
|
-
};
|
|
64
|
-
const dependencies = manifests
|
|
65
|
-
.sort()
|
|
66
|
-
.map((path) => {
|
|
67
|
-
const manifest = JSON.parse(readFileSync(path, "utf8"));
|
|
68
|
-
return [
|
|
69
|
-
path.slice(root.length + 1),
|
|
70
|
-
Object.fromEntries(
|
|
71
|
-
dependencyFields
|
|
72
|
-
.filter((field) => manifest[field] !== undefined)
|
|
73
|
-
.map((field) => [field, canonical(manifest[field])]),
|
|
74
|
-
),
|
|
75
|
-
];
|
|
76
|
-
});
|
|
77
|
-
process.stdout.write(createHash("sha256").update(JSON.stringify(dependencies)).digest("hex"));
|
|
78
|
-
`
|
|
79
|
-
// ============================================================================
|
|
80
|
-
);
|
|
81
|
-
return stringUtils.dedent(source, { trimEnd: false });
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const configured = new WeakMap<javascript.NodeProject, string>();
|
|
85
|
-
|
|
86
|
-
function ensureCacheKeyScript(
|
|
87
|
-
project: javascript.NodeProject,
|
|
88
|
-
ignorePaths: readonly string[],
|
|
89
|
-
): void {
|
|
90
|
-
const root = project.root as javascript.NodeProject;
|
|
91
|
-
const key = JSON.stringify([...new Set(ignorePaths)].sort());
|
|
92
|
-
const existing = configured.get(root);
|
|
93
|
-
if (existing !== undefined) {
|
|
94
|
-
if (existing !== key) {
|
|
95
|
-
throw new Error("Bun workflow cache ignore paths must be consistent across one project");
|
|
96
|
-
}
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
new TextFile(root, ".projen/bun-cache-key.mjs", {
|
|
100
|
-
lines: cacheKeyScript(ignorePaths).trimEnd().split("\n"),
|
|
101
|
-
});
|
|
102
|
-
configured.set(root, key);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function stepCondition(condition: string | undefined, cacheMiss = false): string | undefined {
|
|
106
|
-
const expressions = [
|
|
107
|
-
condition,
|
|
108
|
-
...(cacheMiss ? ["steps.bun_cache.outputs.cache-hit != 'true'"] : []),
|
|
109
|
-
].filter(Boolean);
|
|
110
|
-
return expressions.length ? `\${{ ${expressions.join(" && ")} }}` : undefined;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export interface BunWorkflowCacheOptions {
|
|
114
|
-
readonly setupCondition?: string;
|
|
115
|
-
readonly condition?: string;
|
|
116
|
-
/** Repository-relative output paths excluded from dependency manifest hashing. */
|
|
117
|
-
readonly ignorePaths?: readonly string[];
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/** Set up Bun and restore its global package cache. */
|
|
121
|
-
export function bunCacheRestoreSteps(
|
|
122
|
-
project: javascript.NodeProject,
|
|
123
|
-
options: BunWorkflowCacheOptions = {},
|
|
124
|
-
): readonly Record<string, unknown>[] {
|
|
125
|
-
ensureCacheKeyScript(project, options.ignorePaths ?? []);
|
|
126
|
-
const condition = stepCondition(options.condition);
|
|
127
|
-
const setupCondition = stepCondition(options.setupCondition);
|
|
128
|
-
return [
|
|
129
|
-
{
|
|
130
|
-
name: "Setup Bun",
|
|
131
|
-
...(setupCondition ? { if: setupCondition } : {}),
|
|
132
|
-
uses: "oven-sh/setup-bun@v2",
|
|
133
|
-
with: { "bun-version": "${{ env.BUN_VERSION }}" },
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
name: "Resolve Bun cache",
|
|
137
|
-
id: "bun_cache_metadata",
|
|
138
|
-
...(condition ? { if: condition } : {}),
|
|
139
|
-
shell: "bash",
|
|
140
|
-
run: [
|
|
141
|
-
'echo "path=$(bun pm cache)" >> "$GITHUB_OUTPUT"',
|
|
142
|
-
'echo "dependency_hash=$(node .projen/bun-cache-key.mjs)" >> "$GITHUB_OUTPUT"',
|
|
143
|
-
].join("\n"),
|
|
144
|
-
},
|
|
145
|
-
{
|
|
146
|
-
name: "Restore Bun cache",
|
|
147
|
-
id: "bun_cache",
|
|
148
|
-
...(condition ? { if: condition } : {}),
|
|
149
|
-
uses: "actions/cache/restore@v5",
|
|
150
|
-
with: {
|
|
151
|
-
path: "${{ steps.bun_cache_metadata.outputs.path }}",
|
|
152
|
-
key: `bun-\${{ runner.os }}-\${{ runner.arch }}-\${{ env.BUN_VERSION }}-\${{ steps.bun_cache_metadata.outputs.dependency_hash }}`,
|
|
153
|
-
"restore-keys": `bun-\${{ runner.os }}-\${{ runner.arch }}-\${{ env.BUN_VERSION }}-`,
|
|
154
|
-
},
|
|
155
|
-
},
|
|
156
|
-
];
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/** Save Bun's global package cache immediately after installation. */
|
|
160
|
-
export function bunCacheSaveStep(
|
|
161
|
-
options: BunWorkflowCacheOptions = {},
|
|
162
|
-
): Readonly<Record<string, unknown>> {
|
|
5
|
+
/** Install the pinned Bun version. */
|
|
6
|
+
export function bunSetupStep(): Readonly<Record<string, unknown>> {
|
|
163
7
|
return {
|
|
164
|
-
name: "
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
with: {
|
|
168
|
-
path: "${{ steps.bun_cache_metadata.outputs.path }}",
|
|
169
|
-
key: "${{ steps.bun_cache.outputs.cache-primary-key }}",
|
|
170
|
-
},
|
|
8
|
+
name: "Setup Bun",
|
|
9
|
+
uses: "oven-sh/setup-bun@v2",
|
|
10
|
+
with: { "bun-version": "${{ env.BUN_VERSION }}" },
|
|
171
11
|
};
|
|
172
12
|
}
|
package/src/codegen.ts
CHANGED
|
@@ -316,9 +316,9 @@ function generatePackage(
|
|
|
316
316
|
const sourceText = preprocess(stripImports(tsRuntime, sourcePath));
|
|
317
317
|
const { getZodSchemasFile, getInferredTypes, errors } = generate({
|
|
318
318
|
sourceText,
|
|
319
|
-
//
|
|
320
|
-
// the
|
|
321
|
-
keepComments:
|
|
319
|
+
// JSDoc is rewritten to `@description` so ts-to-zod emits `.describe()`.
|
|
320
|
+
// Keep the generated module comment-free; Zod owns the prose.
|
|
321
|
+
keepComments: false,
|
|
322
322
|
});
|
|
323
323
|
if (errors.length) {
|
|
324
324
|
for (const err of errors) logger.warn(`${destination} from ${source}: ${err}`);
|
package/src/project-js.ts
CHANGED
|
@@ -18,7 +18,7 @@ import type { ReleaseProjectOptions } from "projen/lib/release";
|
|
|
18
18
|
import { generateBarrels } from "./barrels.ts";
|
|
19
19
|
|
|
20
20
|
import { BUN_APP_OVERRIDES, RootBunfigFile } from "./bun-app.ts";
|
|
21
|
-
import { BUN_VERSION,
|
|
21
|
+
import { BUN_VERSION, bunSetupStep } from "./bun-workflow.ts";
|
|
22
22
|
import { codegenModulePaths, generateCodegen } from "./codegen.ts";
|
|
23
23
|
import { DBXToolsConfig, type DBXToolsConfigOptions } from "./dbx-tools-config.ts";
|
|
24
24
|
import { resolvePkgRoot } from "./engine-root.ts";
|
|
@@ -61,8 +61,6 @@ export interface DBXToolsJavaScriptProject extends DBXToolsProject, javascript.N
|
|
|
61
61
|
pnpmWorkspace?: PnpmWorkspaceState;
|
|
62
62
|
/** Root projenrc tsconfigs - only a tree ROOT has one. */
|
|
63
63
|
rootTsconfig?: DBXToolsRootTsconfig;
|
|
64
|
-
/** Repository outputs excluded from generated workflow dependency-cache hashing. */
|
|
65
|
-
readonly workflowCacheIgnorePaths: readonly string[];
|
|
66
64
|
}
|
|
67
65
|
|
|
68
66
|
/** Parsed npm package identifier: optional scope plus the unscoped package name. */
|
|
@@ -573,14 +571,14 @@ export type DBXToolsJavaScriptProjectOptions = CommonProjectOptions &
|
|
|
573
571
|
readonly releasePythonRoot?: string;
|
|
574
572
|
/** Repository task names run during local release preparation before publication. */
|
|
575
573
|
readonly releaseValidationTasks?: readonly string[];
|
|
574
|
+
/** Release build prerequisites installed before repository validation tasks. */
|
|
575
|
+
readonly releaseSetupSteps?: readonly JobStep[];
|
|
576
576
|
/** Set to `false` to omit normal npm workspace publication. */
|
|
577
577
|
readonly nodeRelease?: boolean;
|
|
578
578
|
/** Unified dbx-tools release workflow, or no release surface. Defaults to `dbx-tools`. */
|
|
579
579
|
readonly releaseMode?: DBXToolsReleaseMode;
|
|
580
580
|
/** Prefix for generated release tags. Defaults to `v`. */
|
|
581
581
|
readonly releaseTagPrefix?: string;
|
|
582
|
-
/** Repository output paths excluded from generated workflow dependency-cache hashing. */
|
|
583
|
-
readonly workflowCacheIgnorePaths?: readonly string[];
|
|
584
582
|
/**
|
|
585
583
|
* Extra workspace member paths (repo-relative, POSIX) to list in the workspace
|
|
586
584
|
* config ALONGSIDE the discovered `packageRoots` members - for a package that
|
|
@@ -607,16 +605,6 @@ export type DBXToolsTypeScriptProjectOptions = Partial<
|
|
|
607
605
|
> &
|
|
608
606
|
DBXToolsJavaScriptProjectOptions;
|
|
609
607
|
|
|
610
|
-
const initializingWorkflowCachePaths = new Map<string, readonly string[]>();
|
|
611
|
-
|
|
612
|
-
function configuredOutdir(
|
|
613
|
-
options: Pick<DBXToolsJavaScriptProjectOptions, "parent" | "outdir">,
|
|
614
|
-
): string {
|
|
615
|
-
return options.parent
|
|
616
|
-
? resolve(options.parent.outdir, options.outdir ?? ".")
|
|
617
|
-
: resolve(options.outdir ?? process.cwd());
|
|
618
|
-
}
|
|
619
|
-
|
|
620
608
|
/**
|
|
621
609
|
* A monorepo root. Scans `packageRoots` and appends a
|
|
622
610
|
* {@link DBXToolsTypeScriptProject} per `src`-bearing folder, then emits the
|
|
@@ -633,7 +621,6 @@ export class DBXToolsNodeProject
|
|
|
633
621
|
rootTsconfig?: DBXToolsRootTsconfig;
|
|
634
622
|
readonly extraWorkspaceMembers: readonly string[];
|
|
635
623
|
readonly releaseBranch: string;
|
|
636
|
-
readonly workflowCacheIgnorePaths: readonly string[];
|
|
637
624
|
private readonly rootInstallOnly: boolean;
|
|
638
625
|
|
|
639
626
|
constructor(options: DBXToolsJavaScriptProjectOptions = {}) {
|
|
@@ -646,8 +633,6 @@ export class DBXToolsNodeProject
|
|
|
646
633
|
// into `package.json` (`workspaces`/`catalog`) for bun to read. The
|
|
647
634
|
// `pnpm-workspace.yaml` is still emitted for the Databricks Apps pnpm install.
|
|
648
635
|
const pnpmWorkspace = new PnpmWorkspaceState(options);
|
|
649
|
-
const outdir = configuredOutdir(options);
|
|
650
|
-
initializingWorkflowCachePaths.set(outdir, options.workflowCacheIgnorePaths ?? []);
|
|
651
636
|
super({
|
|
652
637
|
...defaultProjectOptions(options),
|
|
653
638
|
pnpmOptions: {
|
|
@@ -666,8 +651,6 @@ export class DBXToolsNodeProject
|
|
|
666
651
|
this.scope = scope;
|
|
667
652
|
this.extraWorkspaceMembers = options.extraWorkspaceMembers ?? [];
|
|
668
653
|
this.releaseBranch = options.defaultReleaseBranch ?? "main";
|
|
669
|
-
this.workflowCacheIgnorePaths = options.workflowCacheIgnorePaths ?? [];
|
|
670
|
-
initializingWorkflowCachePaths.delete(outdir);
|
|
671
654
|
this.rootInstallOnly = options.rootInstallOnly !== false;
|
|
672
655
|
this.dbxToolsConfig = new DBXToolsConfig(this, options);
|
|
673
656
|
initProject(this, options);
|
|
@@ -676,28 +659,10 @@ export class DBXToolsNodeProject
|
|
|
676
659
|
public override renderWorkflowSetup(options?: javascript.RenderWorkflowSetupOptions): JobStep[] {
|
|
677
660
|
const steps = super.renderWorkflowSetup(options);
|
|
678
661
|
if (this.parent) return steps;
|
|
679
|
-
return
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
ignorePaths:
|
|
684
|
-
this.workflowCacheIgnorePaths ??
|
|
685
|
-
initializingWorkflowCachePaths.get(resolve(this.outdir)) ??
|
|
686
|
-
[],
|
|
687
|
-
}),
|
|
688
|
-
];
|
|
689
|
-
}
|
|
690
|
-
if (step.run?.startsWith("bun install")) {
|
|
691
|
-
return [
|
|
692
|
-
step,
|
|
693
|
-
bunCacheSaveStep({
|
|
694
|
-
condition:
|
|
695
|
-
"github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository",
|
|
696
|
-
}),
|
|
697
|
-
];
|
|
698
|
-
}
|
|
699
|
-
return [step];
|
|
700
|
-
}) as JobStep[];
|
|
662
|
+
return [
|
|
663
|
+
bunSetupStep() as JobStep,
|
|
664
|
+
...steps.filter((step) => !step.uses?.startsWith("oven-sh/setup-bun@")),
|
|
665
|
+
];
|
|
701
666
|
}
|
|
702
667
|
|
|
703
668
|
public override preSynthesize(): void {
|
|
@@ -752,7 +717,6 @@ export class DBXToolsTypeScriptProject
|
|
|
752
717
|
rootTsconfig?: DBXToolsRootTsconfig;
|
|
753
718
|
readonly extraWorkspaceMembers: readonly string[];
|
|
754
719
|
readonly releaseBranch: string;
|
|
755
|
-
readonly workflowCacheIgnorePaths: readonly string[];
|
|
756
720
|
private readonly rootInstallOnly: boolean;
|
|
757
721
|
|
|
758
722
|
constructor(options: DBXToolsTypeScriptProjectOptions) {
|
|
@@ -793,7 +757,6 @@ export class DBXToolsTypeScriptProject
|
|
|
793
757
|
this.scope = scope;
|
|
794
758
|
this.extraWorkspaceMembers = options.extraWorkspaceMembers ?? [];
|
|
795
759
|
this.releaseBranch = options.defaultReleaseBranch ?? "main";
|
|
796
|
-
this.workflowCacheIgnorePaths = options.workflowCacheIgnorePaths ?? [];
|
|
797
760
|
this.rootInstallOnly = options.rootInstallOnly !== false;
|
|
798
761
|
// Pairs with `jsx` in SHARED_COMPILER_OPTIONS: projen's default `include` is
|
|
799
762
|
// `src/**/*.ts` only, which silently omits a `.tsx` file from the program
|
|
@@ -1365,6 +1328,7 @@ function initProject(
|
|
|
1365
1328
|
docs: options.releaseDocs,
|
|
1366
1329
|
pythonRoot: options.releasePythonRoot,
|
|
1367
1330
|
validationTasks: options.releaseValidationTasks,
|
|
1331
|
+
setupSteps: options.releaseSetupSteps,
|
|
1368
1332
|
});
|
|
1369
1333
|
}
|
|
1370
1334
|
}
|
package/src/project-py.ts
CHANGED
|
@@ -2,16 +2,15 @@
|
|
|
2
2
|
import * as projectUtils from "@dbx-tools/core/project-utils";
|
|
3
3
|
import { stringUtils, type OneOrMany } from "@dbx-tools/shared-core";
|
|
4
4
|
import { Component, License, TextFile, type Project, javascript, python } from "projen";
|
|
5
|
-
import { JobPermission } from "projen/lib/github/workflows-model";
|
|
6
|
-
import { BUN_VERSION } from "./bun-workflow.ts";
|
|
5
|
+
import { JobPermission, type Job, type JobStep } from "projen/lib/github/workflows-model";
|
|
7
6
|
import { DBX_TOOLS_LICENSE, projectRepositoryUrl } from "./project-js.ts";
|
|
8
7
|
import { isDBXToolsJavaScriptProject } from "./project-predicate.ts";
|
|
9
8
|
import type { DBXToolsProject, DBXToolsProjectOptions } from "./project.ts";
|
|
10
9
|
import { PythonNodeBundle, type PythonNodeBindingsOptions } from "./python-node-bundle.ts";
|
|
11
|
-
import { RELEASE_VERSION, releaseSourceSteps } from "./release-context.ts";
|
|
12
10
|
import {
|
|
13
11
|
refreshReleaseDocsDependencies,
|
|
14
12
|
releaseCondition,
|
|
13
|
+
releaseBuildSteps,
|
|
15
14
|
releaseTagPattern,
|
|
16
15
|
tryReleaseWorkflow,
|
|
17
16
|
uvSetupStep,
|
|
@@ -88,6 +87,8 @@ export interface DBXToolsPythonWorkspaceOptions {
|
|
|
88
87
|
readonly devDependencies?: readonly string[];
|
|
89
88
|
readonly testPaths?: readonly string[];
|
|
90
89
|
readonly lintPaths?: readonly string[];
|
|
90
|
+
/** Repository-relative paths Ruff must not lint or format. */
|
|
91
|
+
readonly ruffExcludes?: readonly string[];
|
|
91
92
|
readonly ruffPerFileIgnores?: Readonly<Record<string, readonly string[]>>;
|
|
92
93
|
/** Generated Python implementation files Pyrefly should resolve but not type-check. */
|
|
93
94
|
readonly pyreflyProjectExcludes?: readonly string[];
|
|
@@ -348,6 +349,17 @@ export class DBXToolsPythonWorkspace extends Component {
|
|
|
348
349
|
if (options.indexStrategy) {
|
|
349
350
|
file.addOverride("tool.uv.index-strategy", options.indexStrategy);
|
|
350
351
|
}
|
|
352
|
+
const ruffExcludes = [
|
|
353
|
+
...(options.ruffExcludes ?? []),
|
|
354
|
+
...options.packages.flatMap((pkg) =>
|
|
355
|
+
pythonNodeGeneratedSources(pkg).map(
|
|
356
|
+
(source) => `${this.repository.root}/${pkg.directory}/${source}`,
|
|
357
|
+
),
|
|
358
|
+
),
|
|
359
|
+
];
|
|
360
|
+
if (ruffExcludes.length) {
|
|
361
|
+
file.addOverride("tool.ruff.exclude", [...new Set(ruffExcludes)]);
|
|
362
|
+
}
|
|
351
363
|
file.addOverride("tool.pyrefly.ignore-errors-in-generated-code", true);
|
|
352
364
|
const projectExcludes = [
|
|
353
365
|
...(options.pyreflyProjectExcludes ?? []),
|
|
@@ -401,16 +413,45 @@ export class DBXToolsPythonWorkspace extends Component {
|
|
|
401
413
|
if (!workflow) {
|
|
402
414
|
throw new Error("Python release requires the root dbx-tools release mode");
|
|
403
415
|
}
|
|
416
|
+
const build = workflow.getJob("build-release") as Job | undefined;
|
|
417
|
+
if (!build) throw new Error("Python release requires the shared release build job");
|
|
418
|
+
const pythonSteps: JobStep[] = [
|
|
419
|
+
uvSetupStep(),
|
|
420
|
+
{
|
|
421
|
+
name: "Build Python distributions",
|
|
422
|
+
env: { RELEASE_VERSION: "${{ steps.release.outputs.release_version }}" },
|
|
423
|
+
run: [
|
|
424
|
+
'bun node_modules/@dbx-tools/projen/tasks/publish-python.ts "$RELEASE_VERSION"',
|
|
425
|
+
`--root ${JSON.stringify(this.repository.root)}`,
|
|
426
|
+
`--package ${allPublications.map((publication) => JSON.stringify(publication.directory)).join(" ")}`,
|
|
427
|
+
"--output .release/python --package-directories",
|
|
428
|
+
].join(" \\\n "),
|
|
429
|
+
},
|
|
430
|
+
...allPublications.map((publication) => ({
|
|
431
|
+
name: `Upload ${publication.distribution} distributions`,
|
|
432
|
+
uses: "actions/upload-artifact@v4",
|
|
433
|
+
with: {
|
|
434
|
+
name: `release-python-${publication.directory}`,
|
|
435
|
+
path: `.release/python/${publication.directory}`,
|
|
436
|
+
"if-no-files-found": "error",
|
|
437
|
+
},
|
|
438
|
+
})),
|
|
439
|
+
];
|
|
440
|
+
workflow.updateJob("build-release", {
|
|
441
|
+
...build,
|
|
442
|
+
steps: [...build.steps, ...releaseBuildSteps("pypi", pythonSteps)],
|
|
443
|
+
});
|
|
404
444
|
for (const publication of allPublications) {
|
|
405
445
|
const dependencyJobs = (publication.dependencies ?? []).map(
|
|
406
446
|
(dependency) => `publish-pypi-${dependency}`,
|
|
407
447
|
);
|
|
408
448
|
workflow.addJob(`publish-pypi-${publication.directory}`, {
|
|
409
449
|
if: releaseCondition([
|
|
410
|
-
"needs.
|
|
450
|
+
"needs.build-release.result == 'success'",
|
|
451
|
+
"needs.build-release.outputs.pypi == 'true'",
|
|
411
452
|
...dependencyJobs.map((job) => `needs['${job}'].result == 'success'`),
|
|
412
453
|
]),
|
|
413
|
-
needs: ["
|
|
454
|
+
needs: ["build-release", ...dependencyJobs],
|
|
414
455
|
environment: {
|
|
415
456
|
name: publication.environment,
|
|
416
457
|
url:
|
|
@@ -420,20 +461,14 @@ export class DBXToolsPythonWorkspace extends Component {
|
|
|
420
461
|
runsOn: ["ubuntu-latest"],
|
|
421
462
|
permissions: { idToken: JobPermission.WRITE },
|
|
422
463
|
timeoutMinutes: 10,
|
|
423
|
-
env: { BUN_VERSION },
|
|
424
464
|
steps: [
|
|
425
|
-
...releaseSourceSteps(),
|
|
426
|
-
...project.renderWorkflowSetup({ mutable: false }),
|
|
427
|
-
uvSetupStep(),
|
|
428
465
|
{
|
|
429
|
-
name: `
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
`--output dist/${publication.directory}`,
|
|
436
|
-
].join(" \\\n "),
|
|
466
|
+
name: `Download ${publication.distribution} distributions`,
|
|
467
|
+
uses: "actions/download-artifact@v4",
|
|
468
|
+
with: {
|
|
469
|
+
name: `release-python-${publication.directory}`,
|
|
470
|
+
path: `dist/${publication.directory}`,
|
|
471
|
+
},
|
|
437
472
|
},
|
|
438
473
|
{
|
|
439
474
|
name: `Publish ${publication.distribution} to PyPI`,
|
package/src/release-context.ts
CHANGED
|
@@ -1,34 +1,3 @@
|
|
|
1
1
|
/** Verified release context shared by every job in the unified workflow. */
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export const RELEASE_TAG = "${{ needs.verify-context.outputs.release_tag }}";
|
|
6
|
-
export const RELEASE_SHA = "${{ needs.verify-context.outputs.expected_sha }}";
|
|
7
|
-
export const RELEASE_VERSION = "${{ needs.verify-context.outputs.release_version }}";
|
|
8
|
-
|
|
9
|
-
/** Check out and verify the immutable commit selected by the release tag. */
|
|
10
|
-
export function releaseSourceSteps(): readonly JobStep[] {
|
|
11
|
-
return [
|
|
12
|
-
github.WorkflowSteps.checkout({
|
|
13
|
-
name: "Checkout release commit",
|
|
14
|
-
with: {
|
|
15
|
-
ref: RELEASE_SHA,
|
|
16
|
-
fetchDepth: 1,
|
|
17
|
-
},
|
|
18
|
-
}),
|
|
19
|
-
{
|
|
20
|
-
name: "Verify release source",
|
|
21
|
-
shell: "bash",
|
|
22
|
-
env: {
|
|
23
|
-
RELEASE_TAG,
|
|
24
|
-
EXPECTED_SHA: RELEASE_SHA,
|
|
25
|
-
},
|
|
26
|
-
run: [
|
|
27
|
-
'git fetch --force origin "+refs/tags/$RELEASE_TAG:refs/tags/$RELEASE_TAG"',
|
|
28
|
-
'test "$(git cat-file -t "$RELEASE_TAG")" = "tag"',
|
|
29
|
-
'test "$(git rev-parse "$RELEASE_TAG^{commit}")" = "$EXPECTED_SHA"',
|
|
30
|
-
'test "$(git rev-parse HEAD)" = "$EXPECTED_SHA"',
|
|
31
|
-
].join("\n"),
|
|
32
|
-
},
|
|
33
|
-
];
|
|
34
|
-
}
|
|
2
|
+
/** Version verified by the shared build that produced every publication artifact. */
|
|
3
|
+
export const RELEASE_VERSION = "${{ needs.build-release.outputs.release_version }}";
|