@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/tasks/lib/release.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/** Run the configured release transaction. */
|
|
3
3
|
import * as projectUtils from "@dbx-tools/core/project-utils";
|
|
4
4
|
import { log } from "@dbx-tools/shared-core";
|
|
5
|
-
import { Command } from "commander";
|
|
5
|
+
import { Command, Option } from "commander";
|
|
6
6
|
import { publishLocalRelease } from "./local-publish.ts";
|
|
7
7
|
import { assertReleaseVersion } from "./release-version.ts";
|
|
8
8
|
import {
|
|
@@ -10,6 +10,15 @@ import {
|
|
|
10
10
|
runTaskCommand,
|
|
11
11
|
taskCommandSucceeds,
|
|
12
12
|
} from "../../src/_task-command.ts";
|
|
13
|
+
import {
|
|
14
|
+
RELEASE_INSTALL_MODES,
|
|
15
|
+
RELEASE_PUBLISH_TARGETS,
|
|
16
|
+
releasePublishesLocally,
|
|
17
|
+
releaseStepSelection,
|
|
18
|
+
releaseTagAnnotation,
|
|
19
|
+
type ReleaseInstallMode,
|
|
20
|
+
type ReleaseSelectionOptions,
|
|
21
|
+
} from "../../src/release-options.ts";
|
|
13
22
|
import { readWorkspaceVersion } from "../../src/workspace-version.ts";
|
|
14
23
|
|
|
15
24
|
const logger = log.logger("projen:release");
|
|
@@ -87,23 +96,34 @@ function prepareReleaseBranch(options: {
|
|
|
87
96
|
runTaskCommand(root, "git", ["push", remote, `HEAD:${branch}`]);
|
|
88
97
|
}
|
|
89
98
|
|
|
90
|
-
export async function runRelease(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
export async function runRelease(
|
|
100
|
+
options: ReleaseSelectionOptions & {
|
|
101
|
+
readonly root: string;
|
|
102
|
+
readonly branch: string;
|
|
103
|
+
readonly bump?: boolean;
|
|
104
|
+
readonly prefix: string;
|
|
105
|
+
readonly remote: string;
|
|
106
|
+
readonly localPublish?: boolean;
|
|
107
|
+
readonly localRegistry?: string;
|
|
108
|
+
readonly localPypi?: string;
|
|
109
|
+
readonly install?: ReleaseInstallMode;
|
|
110
|
+
readonly pythonRoot?: string;
|
|
111
|
+
readonly validationTasks?: readonly string[];
|
|
112
|
+
},
|
|
113
|
+
): Promise<string> {
|
|
100
114
|
const { branch, prefix, remote, root } = options;
|
|
115
|
+
const selection = releaseStepSelection(options);
|
|
116
|
+
const install = options.install ?? "auto";
|
|
117
|
+
|
|
118
|
+
if (install === "always") runTaskCommand(root, "bun", ["install", "--frozen-lockfile"]);
|
|
101
119
|
|
|
102
120
|
prepareReleaseBranch({ branch, remote, root });
|
|
103
121
|
|
|
104
122
|
runTaskCommand(root, "git", ["fetch", remote, branch, "--tags"]);
|
|
105
123
|
if (options.bump ?? true) {
|
|
106
|
-
runTaskCommand(root, "bun", ["run", "bump"]
|
|
124
|
+
runTaskCommand(root, "bun", ["run", "bump"], {
|
|
125
|
+
env: { ...process.env, DBX_TOOLS_RELEASE_INSTALL: install === "auto" ? "auto" : "never" },
|
|
126
|
+
});
|
|
107
127
|
}
|
|
108
128
|
|
|
109
129
|
const version = readWorkspaceVersion(root);
|
|
@@ -115,7 +135,7 @@ export async function runRelease(options: {
|
|
|
115
135
|
}
|
|
116
136
|
|
|
117
137
|
runTaskCommand(root, "bun", ["run", "version:check"]);
|
|
118
|
-
for (const task of options.validationTasks ?? []) {
|
|
138
|
+
for (const task of selection.validation ? (options.validationTasks ?? []) : []) {
|
|
119
139
|
runTaskCommand(root, "bun", ["run", task]);
|
|
120
140
|
}
|
|
121
141
|
const status = captureTaskCommand(
|
|
@@ -145,7 +165,13 @@ export async function runRelease(options: {
|
|
|
145
165
|
);
|
|
146
166
|
}
|
|
147
167
|
|
|
148
|
-
runTaskCommand(root, "git", [
|
|
168
|
+
runTaskCommand(root, "git", [
|
|
169
|
+
"tag",
|
|
170
|
+
"--annotate",
|
|
171
|
+
tag,
|
|
172
|
+
"--message",
|
|
173
|
+
releaseTagAnnotation(tag, selection),
|
|
174
|
+
]);
|
|
149
175
|
try {
|
|
150
176
|
runTaskCommand(root, "git", ["push", "--atomic", remote, `HEAD:${branch}`, `refs/tags/${tag}`]);
|
|
151
177
|
} catch (error) {
|
|
@@ -157,10 +183,10 @@ export async function runRelease(options: {
|
|
|
157
183
|
// Restore local deploys: when a non-standard (loopback) npm or uv registry is
|
|
158
184
|
// configured, publish the freshly tagged version to it directly. Detection and
|
|
159
185
|
// publishing are owned by local-publish.ts; this no-ops on standard registries.
|
|
160
|
-
if (options.localPublish ?? true) {
|
|
186
|
+
if ((options.localPublish ?? true) && releasePublishesLocally(options.publish)) {
|
|
161
187
|
await publishLocalRelease({
|
|
162
|
-
localPypi: "auto",
|
|
163
|
-
localRegistry: "auto",
|
|
188
|
+
localPypi: options.pypi === false ? "false" : (options.localPypi ?? "auto"),
|
|
189
|
+
localRegistry: options.npm === false ? "false" : (options.localRegistry ?? "auto"),
|
|
164
190
|
pythonRoot: options.pythonRoot ?? "packages/py",
|
|
165
191
|
root,
|
|
166
192
|
version,
|
|
@@ -170,8 +196,11 @@ export async function runRelease(options: {
|
|
|
170
196
|
return tag;
|
|
171
197
|
}
|
|
172
198
|
|
|
173
|
-
|
|
174
|
-
|
|
199
|
+
/** Build the release task's native parser without executing git or registry operations. */
|
|
200
|
+
export function createReleaseCommand(): Command {
|
|
201
|
+
return new Command()
|
|
202
|
+
.name("release")
|
|
203
|
+
.description("Prepare an annotated release and select its build and publication steps")
|
|
175
204
|
.option("--root <path>", "repository root")
|
|
176
205
|
.option("--branch <name>", "release branch", "main")
|
|
177
206
|
.option("--prefix <prefix>", "release tag prefix", "v")
|
|
@@ -184,18 +213,43 @@ export async function main(): Promise<void> {
|
|
|
184
213
|
[],
|
|
185
214
|
)
|
|
186
215
|
.option("--no-bump", "use an existing synchronized local version bump")
|
|
216
|
+
.addOption(
|
|
217
|
+
new Option("--publish <target>", "publication scope")
|
|
218
|
+
.choices([...RELEASE_PUBLISH_TARGETS])
|
|
219
|
+
.default("auto"),
|
|
220
|
+
)
|
|
221
|
+
.addOption(
|
|
222
|
+
new Option("--install <mode>", "local workspace dependency installation")
|
|
223
|
+
.choices([...RELEASE_INSTALL_MODES])
|
|
224
|
+
.default("auto"),
|
|
225
|
+
)
|
|
226
|
+
.option("--no-npm", "skip npm build and publication, including local npm publication")
|
|
227
|
+
.option("--no-pypi", "skip Python build and publication, including local Python publication")
|
|
228
|
+
.option("--docs", "build and deploy docs for a selected scope")
|
|
229
|
+
.option("--no-docs", "skip documentation build and deployment")
|
|
230
|
+
.option(
|
|
231
|
+
"--no-validation",
|
|
232
|
+
"skip optional release validation tasks; version/source checks remain mandatory",
|
|
233
|
+
)
|
|
187
234
|
.option("--no-local-publish", "skip publishing to configured local registries")
|
|
235
|
+
.option("--local-registry <auto|false|url>", "local npm registry selection", "auto")
|
|
236
|
+
.option("--local-pypi <auto|false|url>", "local devpi registry selection", "auto")
|
|
188
237
|
.action(
|
|
189
|
-
async (
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
238
|
+
async (
|
|
239
|
+
options: ReleaseSelectionOptions & {
|
|
240
|
+
root?: string;
|
|
241
|
+
branch: string;
|
|
242
|
+
bump: boolean;
|
|
243
|
+
prefix: string;
|
|
244
|
+
remote: string;
|
|
245
|
+
pythonRoot: string;
|
|
246
|
+
localPublish: boolean;
|
|
247
|
+
localRegistry: string;
|
|
248
|
+
localPypi: string;
|
|
249
|
+
install: ReleaseInstallMode;
|
|
250
|
+
validate: string[];
|
|
251
|
+
},
|
|
252
|
+
) => {
|
|
199
253
|
await runRelease({
|
|
200
254
|
root: options.root ?? projectUtils.root() ?? process.cwd(),
|
|
201
255
|
branch: options.branch,
|
|
@@ -204,9 +258,21 @@ export async function main(): Promise<void> {
|
|
|
204
258
|
remote: options.remote,
|
|
205
259
|
pythonRoot: options.pythonRoot,
|
|
206
260
|
localPublish: options.localPublish,
|
|
261
|
+
localRegistry: options.localRegistry,
|
|
262
|
+
localPypi: options.localPypi,
|
|
263
|
+
install: options.install,
|
|
264
|
+
publish: options.publish,
|
|
265
|
+
npm: options.npm,
|
|
266
|
+
pypi: options.pypi,
|
|
267
|
+
docs: options.docs,
|
|
268
|
+
validation: options.validation,
|
|
207
269
|
validationTasks: options.validate,
|
|
208
270
|
});
|
|
209
271
|
},
|
|
210
|
-
)
|
|
211
|
-
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** Execute the native release task parser. */
|
|
276
|
+
export async function main(): Promise<void> {
|
|
277
|
+
await createReleaseCommand().parseAsync();
|
|
212
278
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env -S bun
|
|
2
|
+
/** Export the current annotated release tag's step selection to GitHub Actions. */
|
|
3
|
+
import { appendFileSync } from "node:fs";
|
|
4
|
+
import { parseArgs } from "node:util";
|
|
5
|
+
import { captureTaskCommand } from "../src/_task-command.ts";
|
|
6
|
+
import { parseReleaseTagAnnotation } from "../src/release-options.ts";
|
|
7
|
+
|
|
8
|
+
/** Verify and export a tag policy using the shared annotated-tag contract. */
|
|
9
|
+
export function main(args: string[] = process.argv.slice(2)): void {
|
|
10
|
+
const { values } = parseArgs({
|
|
11
|
+
args,
|
|
12
|
+
options: { tag: { type: "string" }, output: { type: "string" } },
|
|
13
|
+
strict: true,
|
|
14
|
+
});
|
|
15
|
+
if (!values.tag || !values.output) throw new Error("--tag and --output are required");
|
|
16
|
+
const annotation = captureTaskCommand(
|
|
17
|
+
process.cwd(),
|
|
18
|
+
"git",
|
|
19
|
+
["for-each-ref", "--format=%(contents)", `refs/tags/${values.tag}`],
|
|
20
|
+
{ check: true },
|
|
21
|
+
);
|
|
22
|
+
if (!annotation) throw new Error(`Release tag ${values.tag} has no annotation`);
|
|
23
|
+
const selection = parseReleaseTagAnnotation(annotation);
|
|
24
|
+
appendFileSync(
|
|
25
|
+
values.output,
|
|
26
|
+
Object.entries(selection)
|
|
27
|
+
.map(([name, enabled]) => `${name}=${enabled}\n`)
|
|
28
|
+
.join(""),
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (import.meta.main) main();
|
package/tasks/release.ts
CHANGED