@dbx-tools/projen 0.9.21 → 0.9.28
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 +77 -207
- package/index.ts +10 -12
- package/package.json +4 -4
- package/src/barrels.ts +1 -74
- package/src/module-exports.ts +175 -1
- package/src/openapi.ts +5 -72
- package/src/packages.ts +45 -0
- package/src/project-js.ts +3 -6
- package/src/project-predicate.ts +1 -2
- package/src/project-py.ts +82 -103
- package/src/project.ts +2 -2
- package/src/python-node-bindings.ts +138 -0
- package/src/python-node-bundle.ts +92 -0
- package/src/release-catalog.ts +3 -5
- package/src/{release-dispatch.ts → release-context.ts} +0 -11
- package/src/release.ts +70 -152
- package/tasks/databricks-cli-assets.ts +129 -0
- package/tasks/local-publish.ts +23 -107
- package/tasks/publish-npm.ts +10 -1
- package/tasks/publish-python.ts +26 -11
- package/tasks/publish.ts +12 -48
- package/tasks/python-node-bindings-watch.ts +47 -0
- package/tasks/python-node-bindings.ts +1017 -0
- package/tasks/python-release.ts +74 -0
- package/tasks/release.ts +99 -0
- package/tasks/sync.ts +13 -18
- package/tasks/version-check.ts +1 -28
- package/._LICENSE +0 -0
- package/._README.md +0 -0
- package/._index.ts +0 -0
- package/._package.json +0 -0
- package/src/.__lazy-require.ts +0 -0
- package/src/.__release-platform.ts +0 -0
- package/src/.__rust-project.ts +0 -0
- package/src/.__rust-release-workflow.ts +0 -0
- package/src/.__task-command.ts +0 -0
- package/src/._barrels.ts +0 -0
- package/src/._bun-app.ts +0 -0
- package/src/._bun-workflow.ts +0 -0
- package/src/._clean.ts +0 -0
- package/src/._codegen.ts +0 -0
- package/src/._dbx-tools-config.ts +0 -0
- package/src/._engine-root.ts +0 -0
- package/src/._generated.ts +0 -0
- package/src/._mixin.ts +0 -0
- package/src/._module-exports.ts +0 -0
- package/src/._openapi.ts +0 -0
- package/src/._packages.ts +0 -0
- package/src/._pnpm-workspace.ts +0 -0
- package/src/._project-js.ts +0 -0
- package/src/._project-predicate.ts +0 -0
- package/src/._project-py.ts +0 -0
- package/src/._project-rs.ts +0 -0
- package/src/._project.ts +0 -0
- package/src/._projen-version.ts +0 -0
- package/src/._publish.ts +0 -0
- package/src/._release-catalog.ts +0 -0
- package/src/._release-dispatch.ts +0 -0
- package/src/._release-github.ts +0 -0
- package/src/._release.ts +0 -0
- package/src/._scaffold.ts +0 -0
- package/src/._tags.ts +0 -0
- package/src/._tsconfig.ts +0 -0
- package/src/._uniffi.ts +0 -0
- package/src/._vscode.ts +0 -0
- package/src/._watch.ts +0 -0
- package/src/._workspace-lock.ts +0 -0
- package/src/._workspace-version.ts +0 -0
- package/src/_rust-project.ts +0 -429
- package/src/_rust-release-workflow.ts +0 -311
- package/src/project-rs.ts +0 -1054
- package/src/uniffi.ts +0 -65
- package/tasks/._barrels.ts +0 -0
- package/tasks/._bump.ts +0 -0
- package/tasks/._clean.ts +0 -0
- package/tasks/._compile-workspace.ts +0 -0
- package/tasks/._local-publish.ts +0 -0
- package/tasks/._openapi.ts +0 -0
- package/tasks/._projenrc.ts +0 -0
- package/tasks/._publish-npm.ts +0 -0
- package/tasks/._publish-python.ts +0 -0
- package/tasks/._publish-uniffi-local.ts +0 -0
- package/tasks/._publish.ts +0 -0
- package/tasks/._python-registry.ts +0 -0
- package/tasks/._release-assets.ts +0 -0
- package/tasks/._release-candidate.ts +0 -0
- package/tasks/._release-manifest.ts +0 -0
- package/tasks/._release-pr.ts +0 -0
- package/tasks/._release-summary.ts +0 -0
- package/tasks/._release-version.ts +0 -0
- package/tasks/._rust.ts +0 -0
- package/tasks/._sync.ts +0 -0
- package/tasks/._uniffi-python.d.ts +0 -0
- package/tasks/._uniffi-python.js +0 -0
- package/tasks/._uniffi-release.mjs +0 -0
- package/tasks/._uniffi.ts +0 -0
- package/tasks/._version-check.ts +0 -0
- package/tasks/publish-uniffi-local.ts +0 -359
- package/tasks/release-assets.ts +0 -467
- package/tasks/release-candidate.ts +0 -416
- package/tasks/release-manifest.ts +0 -273
- package/tasks/release-pr.ts +0 -589
- package/tasks/release-summary.ts +0 -379
- package/tasks/rust.ts +0 -173
- package/tasks/uniffi-python.d.ts +0 -60
- package/tasks/uniffi-python.js +0 -178
- package/tasks/uniffi-release.mjs +0 -579
- package/tasks/uniffi.ts +0 -262
package/tasks/release-pr.ts
DELETED
|
@@ -1,589 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env -S bun
|
|
2
|
-
/**
|
|
3
|
-
* Prepare a reviewed release pull request from the current branch.
|
|
4
|
-
*
|
|
5
|
-
* Pending work is committed and the current branch is pushed first. A dedicated
|
|
6
|
-
* release branch then receives the VERSION change, generated files, validation,
|
|
7
|
-
* and local registry preflight. Public publication remains owned by the
|
|
8
|
-
* main-branch release workflow.
|
|
9
|
-
*/
|
|
10
|
-
import { existsSync } from "node:fs";
|
|
11
|
-
import { join } from "node:path";
|
|
12
|
-
import { fileURLToPath } from "node:url";
|
|
13
|
-
import * as projectUtils from "@dbx-tools/core/project-utils";
|
|
14
|
-
import { asyncUtils, json, log, object } from "@dbx-tools/shared-core";
|
|
15
|
-
import { Command } from "commander";
|
|
16
|
-
import { hasLocalReleaseTargets, missingLocalReleaseTools } from "./release-assets.ts";
|
|
17
|
-
import { generateReleaseSummary } from "./release-summary.ts";
|
|
18
|
-
import {
|
|
19
|
-
releaseArchitectureOption,
|
|
20
|
-
releaseLevelOption,
|
|
21
|
-
releaseOperatingSystemOption,
|
|
22
|
-
type ReleaseArch,
|
|
23
|
-
type ReleaseOs,
|
|
24
|
-
type VersionLevel,
|
|
25
|
-
} from "../src/_release-platform.ts";
|
|
26
|
-
import {
|
|
27
|
-
captureGitTaskCommand,
|
|
28
|
-
captureTaskCommand,
|
|
29
|
-
gitTaskCommandSucceeds,
|
|
30
|
-
runGitTaskCommand,
|
|
31
|
-
runTaskCommand,
|
|
32
|
-
runTaskCommandAsync,
|
|
33
|
-
taskCommandSucceeds,
|
|
34
|
-
} from "../src/_task-command.ts";
|
|
35
|
-
import {
|
|
36
|
-
RELEASE_SUMMARY_PROVIDER_NAMES,
|
|
37
|
-
type ReleaseSummaryProviderName,
|
|
38
|
-
} from "../src/release-dispatch.ts";
|
|
39
|
-
import {
|
|
40
|
-
githubAccountSupportsWorkflowChanges,
|
|
41
|
-
githubAuthenticatedAccounts,
|
|
42
|
-
githubRepositoryApiPath,
|
|
43
|
-
githubRepositoryCanManage,
|
|
44
|
-
githubRepositoryIdentity,
|
|
45
|
-
githubRepositoryPermissions,
|
|
46
|
-
githubRepositorySpecifier,
|
|
47
|
-
githubTokenArguments,
|
|
48
|
-
githubTokenEnvironmentName,
|
|
49
|
-
} from "../src/release-github.ts";
|
|
50
|
-
import { withWorkspaceMutationLock } from "../src/workspace-lock.ts";
|
|
51
|
-
import {
|
|
52
|
-
readWorkspaceVersion,
|
|
53
|
-
resolveBaseVersion,
|
|
54
|
-
resolveNextVersion,
|
|
55
|
-
} from "../src/workspace-version.ts";
|
|
56
|
-
|
|
57
|
-
const logger = log.logger("projen:release");
|
|
58
|
-
|
|
59
|
-
function pushCurrentBranch(root: string, branch: string): void {
|
|
60
|
-
runGitTaskCommand(root, ["push", "--set-upstream", "origin", `HEAD:${branch}`]);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/** Wait for required checks and return the merged pull request commit. */
|
|
64
|
-
async function waitForPullRequestMerge(
|
|
65
|
-
root: string,
|
|
66
|
-
releaseBranch: string,
|
|
67
|
-
env: NodeJS.ProcessEnv,
|
|
68
|
-
timeoutMs: number,
|
|
69
|
-
): Promise<string> {
|
|
70
|
-
const checksDeadline = Date.now() + timeoutMs;
|
|
71
|
-
let checksReported = false;
|
|
72
|
-
while (Date.now() < checksDeadline) {
|
|
73
|
-
const count = Number(
|
|
74
|
-
captureTaskCommand(
|
|
75
|
-
root,
|
|
76
|
-
"gh",
|
|
77
|
-
[
|
|
78
|
-
"pr",
|
|
79
|
-
"view",
|
|
80
|
-
releaseBranch,
|
|
81
|
-
"--json",
|
|
82
|
-
"statusCheckRollup",
|
|
83
|
-
"--jq",
|
|
84
|
-
".statusCheckRollup | length",
|
|
85
|
-
],
|
|
86
|
-
{ env },
|
|
87
|
-
),
|
|
88
|
-
);
|
|
89
|
-
if (Number.isInteger(count) && count > 0) {
|
|
90
|
-
checksReported = true;
|
|
91
|
-
break;
|
|
92
|
-
}
|
|
93
|
-
await asyncUtils.sleep(2_000);
|
|
94
|
-
}
|
|
95
|
-
if (!checksReported) {
|
|
96
|
-
throw new Error(`Release pull request reported no checks within ${timeoutMs}ms`);
|
|
97
|
-
}
|
|
98
|
-
await runTaskCommandAsync(
|
|
99
|
-
root,
|
|
100
|
-
"gh",
|
|
101
|
-
["pr", "checks", releaseBranch, "--watch", "--fail-fast", "--required", "--interval", "10"],
|
|
102
|
-
{ env, signal: AbortSignal.timeout(timeoutMs) },
|
|
103
|
-
);
|
|
104
|
-
const deadline = Date.now() + timeoutMs;
|
|
105
|
-
while (Date.now() < deadline) {
|
|
106
|
-
const state = captureTaskCommand(
|
|
107
|
-
root,
|
|
108
|
-
"gh",
|
|
109
|
-
[
|
|
110
|
-
"pr",
|
|
111
|
-
"view",
|
|
112
|
-
releaseBranch,
|
|
113
|
-
"--json",
|
|
114
|
-
"state,mergeCommit",
|
|
115
|
-
"--jq",
|
|
116
|
-
'[.state, (.mergeCommit.oid // "")] | @tsv',
|
|
117
|
-
],
|
|
118
|
-
{ env },
|
|
119
|
-
);
|
|
120
|
-
const [status, mergeSha] = state.split("\t");
|
|
121
|
-
if (status === "MERGED" && mergeSha) return mergeSha;
|
|
122
|
-
if (status === "CLOSED") throw new Error(`Release pull request closed without merging`);
|
|
123
|
-
await asyncUtils.sleep(2_000);
|
|
124
|
-
}
|
|
125
|
-
throw new Error(`Release pull request did not merge within ${timeoutMs}ms`);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function githubAccount(root: string): {
|
|
129
|
-
hostname: string;
|
|
130
|
-
owner: string;
|
|
131
|
-
repository: string;
|
|
132
|
-
login: string;
|
|
133
|
-
token: string;
|
|
134
|
-
canManage: boolean;
|
|
135
|
-
} {
|
|
136
|
-
const repository = projectUtils.repositoryUrl(root);
|
|
137
|
-
if (!repository) throw new Error("Release preparation requires a GitHub repository");
|
|
138
|
-
const identity = githubRepositoryIdentity(repository);
|
|
139
|
-
const status = captureTaskCommand(root, "gh", [
|
|
140
|
-
"auth",
|
|
141
|
-
"status",
|
|
142
|
-
"--hostname",
|
|
143
|
-
identity.hostname,
|
|
144
|
-
"--json",
|
|
145
|
-
"hosts",
|
|
146
|
-
]);
|
|
147
|
-
const accounts = githubAuthenticatedAccounts(status, identity.hostname);
|
|
148
|
-
for (const account of accounts) {
|
|
149
|
-
if (!githubAccountSupportsWorkflowChanges(account)) continue;
|
|
150
|
-
const token = captureTaskCommand(
|
|
151
|
-
root,
|
|
152
|
-
"gh",
|
|
153
|
-
githubTokenArguments(identity.hostname, account.login),
|
|
154
|
-
);
|
|
155
|
-
if (!token) continue;
|
|
156
|
-
const tokenEnvironment = {
|
|
157
|
-
...process.env,
|
|
158
|
-
GH_HOST: identity.hostname,
|
|
159
|
-
GH_REPO: githubRepositorySpecifier(identity),
|
|
160
|
-
[githubTokenEnvironmentName(identity.hostname)]: token,
|
|
161
|
-
};
|
|
162
|
-
const repositoryData = json.parseRecord(
|
|
163
|
-
captureTaskCommand(root, "gh", ["api", githubRepositoryApiPath(identity)], {
|
|
164
|
-
env: tokenEnvironment,
|
|
165
|
-
}),
|
|
166
|
-
);
|
|
167
|
-
const permissions = githubRepositoryPermissions(repositoryData);
|
|
168
|
-
if (!permissions.push && !permissions.maintain && !permissions.admin) {
|
|
169
|
-
continue;
|
|
170
|
-
}
|
|
171
|
-
logger.info("selected GitHub CLI account", {
|
|
172
|
-
login: account.login,
|
|
173
|
-
repository: `${identity.owner}/${identity.repository}`,
|
|
174
|
-
});
|
|
175
|
-
return {
|
|
176
|
-
...identity,
|
|
177
|
-
login: account.login,
|
|
178
|
-
token,
|
|
179
|
-
canManage: githubRepositoryCanManage(permissions),
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
const detected =
|
|
183
|
-
accounts
|
|
184
|
-
.map((account) => {
|
|
185
|
-
const workflow = githubAccountSupportsWorkflowChanges(account)
|
|
186
|
-
? "workflow-capable"
|
|
187
|
-
: "missing workflow scope";
|
|
188
|
-
return `${account.login} (${workflow})`;
|
|
189
|
-
})
|
|
190
|
-
.join(", ") || "none";
|
|
191
|
-
throw new Error(
|
|
192
|
-
`No authenticated GitHub CLI account can write release workflow changes to ${identity.owner}/${identity.repository} on ${identity.hostname}; detected accounts: ${detected}`,
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
function releaseSummaryProviders(
|
|
197
|
-
value: string | undefined,
|
|
198
|
-
): ReleaseSummaryProviderName[] | undefined {
|
|
199
|
-
if (value === undefined) return undefined;
|
|
200
|
-
const providers = value
|
|
201
|
-
.split(",")
|
|
202
|
-
.map((provider) => provider.trim())
|
|
203
|
-
.filter(Boolean);
|
|
204
|
-
for (const provider of providers) {
|
|
205
|
-
if (!RELEASE_SUMMARY_PROVIDER_NAMES.includes(provider as ReleaseSummaryProviderName)) {
|
|
206
|
-
throw new Error(`Unknown release summary provider: ${provider}`);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
return providers as ReleaseSummaryProviderName[];
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const program = new Command();
|
|
213
|
-
program
|
|
214
|
-
.description("Prepare, validate, locally publish, and open a reviewed release PR")
|
|
215
|
-
.addOption(releaseLevelOption())
|
|
216
|
-
.option("--prefix <prefix>", "release tag prefix", "v")
|
|
217
|
-
.option("--base <branch>", "release pull request base branch", "main")
|
|
218
|
-
.option("--python-root <path>", "Python package root", "packages/py")
|
|
219
|
-
.option("--message <message>", "commit message for pending source work", "chore: prepare release")
|
|
220
|
-
.addOption(releaseOperatingSystemOption())
|
|
221
|
-
.addOption(releaseArchitectureOption())
|
|
222
|
-
.option("--local-registry <value>", "local npm registry: auto, false, or an explicit URL", "auto")
|
|
223
|
-
.option("--local-pypi <value>", "local PyPI index: auto, false, or an explicit URL", "auto")
|
|
224
|
-
.option(
|
|
225
|
-
"--validate-task <task>",
|
|
226
|
-
"repository task to run before release validation; repeatable",
|
|
227
|
-
(task: string, tasks: string[]) => [...tasks, task],
|
|
228
|
-
[],
|
|
229
|
-
)
|
|
230
|
-
.option("--no-release-summary", "skip optional AI release summary generation")
|
|
231
|
-
.option(
|
|
232
|
-
"--release-summary-providers <providers>",
|
|
233
|
-
"comma-separated provider order: cursor,codex,claude",
|
|
234
|
-
)
|
|
235
|
-
.option("--no-validate", "skip repository validation tasks, Rust tests, and TypeScript compile")
|
|
236
|
-
.option("--no-local-publish", "skip local npm, PyPI, and Cargo publication")
|
|
237
|
-
.option("--no-local-cargo", "skip local Cargo publication")
|
|
238
|
-
.option("--no-approve", "open the release pull request without enabling automatic merge")
|
|
239
|
-
.option("--no-wait", "return after enabling automatic merge without watching publication")
|
|
240
|
-
.option(
|
|
241
|
-
"--wait-timeout-minutes <minutes>",
|
|
242
|
-
"maximum time for pull request checks and merge",
|
|
243
|
-
"120",
|
|
244
|
-
)
|
|
245
|
-
.action(
|
|
246
|
-
async (opts: {
|
|
247
|
-
level: VersionLevel;
|
|
248
|
-
prefix: string;
|
|
249
|
-
base: string;
|
|
250
|
-
pythonRoot: string;
|
|
251
|
-
message: string;
|
|
252
|
-
os: ReleaseOs[];
|
|
253
|
-
arch: ReleaseArch[];
|
|
254
|
-
localRegistry: string;
|
|
255
|
-
localPypi: string;
|
|
256
|
-
validateTask: string[];
|
|
257
|
-
releaseSummary: boolean;
|
|
258
|
-
releaseSummaryProviders?: string;
|
|
259
|
-
validate: boolean;
|
|
260
|
-
localPublish: boolean;
|
|
261
|
-
localCargo: boolean;
|
|
262
|
-
approve: boolean;
|
|
263
|
-
wait: boolean;
|
|
264
|
-
waitTimeoutMinutes: string;
|
|
265
|
-
}) => {
|
|
266
|
-
const root = projectUtils.root() ?? process.cwd();
|
|
267
|
-
await withWorkspaceMutationLock(root, async () => {
|
|
268
|
-
const waitTimeoutMinutes = object.toNumber(opts.waitTimeoutMinutes, {
|
|
269
|
-
separators: false,
|
|
270
|
-
percent: false,
|
|
271
|
-
});
|
|
272
|
-
if (
|
|
273
|
-
waitTimeoutMinutes === undefined ||
|
|
274
|
-
!Number.isInteger(waitTimeoutMinutes) ||
|
|
275
|
-
waitTimeoutMinutes <= 0
|
|
276
|
-
) {
|
|
277
|
-
throw new Error("--wait-timeout-minutes must be a positive integer");
|
|
278
|
-
}
|
|
279
|
-
const waitTimeoutMs = waitTimeoutMinutes * 60_000;
|
|
280
|
-
if (opts.approve && !opts.wait) {
|
|
281
|
-
throw new Error("automatic merge requires waiting so the draft candidate can be built");
|
|
282
|
-
}
|
|
283
|
-
const currentBranch = captureGitTaskCommand(root, ["branch", "--show-current"], {
|
|
284
|
-
check: true,
|
|
285
|
-
});
|
|
286
|
-
if (!currentBranch) throw new Error("Release preparation requires a local branch");
|
|
287
|
-
const account = githubAccount(root);
|
|
288
|
-
if (!account.canManage) {
|
|
289
|
-
throw new Error("Release preparation requires maintain or admin permission");
|
|
290
|
-
}
|
|
291
|
-
if (hasLocalReleaseTargets(root)) {
|
|
292
|
-
const localTools = missingLocalReleaseTools(root);
|
|
293
|
-
if (localTools.length > 0) {
|
|
294
|
-
throw new Error(`Local release candidate build requires: ${localTools.join(", ")}`);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
runGitTaskCommand(root, ["fetch", "--tags", "origin", opts.base]);
|
|
298
|
-
const comparisonBase = resolveBaseVersion(root);
|
|
299
|
-
const next = resolveNextVersion(root, opts.level);
|
|
300
|
-
const releaseVersionScript = fileURLToPath(
|
|
301
|
-
new URL("./release-version.ts", import.meta.url),
|
|
302
|
-
);
|
|
303
|
-
runTaskCommand(root, process.execPath, [
|
|
304
|
-
releaseVersionScript,
|
|
305
|
-
"--version",
|
|
306
|
-
next.version,
|
|
307
|
-
"--prefix",
|
|
308
|
-
opts.prefix,
|
|
309
|
-
"--assert-next",
|
|
310
|
-
]);
|
|
311
|
-
const releaseTag = `${opts.prefix}${next.version}`;
|
|
312
|
-
const releaseBranch = `release/${releaseTag}`;
|
|
313
|
-
if (currentBranch.startsWith("release/")) {
|
|
314
|
-
throw new Error("Release preparation must start from a source branch");
|
|
315
|
-
}
|
|
316
|
-
if (
|
|
317
|
-
captureGitTaskCommand(root, ["ls-remote", "--tags", "origin", `refs/tags/${releaseTag}`])
|
|
318
|
-
) {
|
|
319
|
-
throw new Error(`Release tag already exists: ${releaseTag}`);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
const status = captureGitTaskCommand(
|
|
323
|
-
root,
|
|
324
|
-
["status", "--porcelain=v1", "--untracked-files=all"],
|
|
325
|
-
{ check: true },
|
|
326
|
-
);
|
|
327
|
-
if (status) {
|
|
328
|
-
runGitTaskCommand(root, ["add", "-A"]);
|
|
329
|
-
runGitTaskCommand(root, ["commit", "-m", opts.message]);
|
|
330
|
-
}
|
|
331
|
-
if (
|
|
332
|
-
!gitTaskCommandSucceeds(root, [
|
|
333
|
-
"merge-base",
|
|
334
|
-
"--is-ancestor",
|
|
335
|
-
`origin/${opts.base}`,
|
|
336
|
-
"HEAD",
|
|
337
|
-
])
|
|
338
|
-
) {
|
|
339
|
-
runGitTaskCommand(root, ["merge", "--no-edit", `origin/${opts.base}`]);
|
|
340
|
-
}
|
|
341
|
-
pushCurrentBranch(root, currentBranch);
|
|
342
|
-
const localBranch = Boolean(
|
|
343
|
-
captureGitTaskCommand(root, ["branch", "--list", releaseBranch]),
|
|
344
|
-
);
|
|
345
|
-
const remoteBranch = Boolean(
|
|
346
|
-
captureGitTaskCommand(root, [
|
|
347
|
-
"ls-remote",
|
|
348
|
-
"--heads",
|
|
349
|
-
"origin",
|
|
350
|
-
`refs/heads/${releaseBranch}`,
|
|
351
|
-
]),
|
|
352
|
-
);
|
|
353
|
-
let releaseCompleted = false;
|
|
354
|
-
try {
|
|
355
|
-
if (localBranch) {
|
|
356
|
-
runGitTaskCommand(root, ["switch", releaseBranch]);
|
|
357
|
-
} else if (remoteBranch) {
|
|
358
|
-
runGitTaskCommand(root, [
|
|
359
|
-
"switch",
|
|
360
|
-
"--track",
|
|
361
|
-
"-c",
|
|
362
|
-
releaseBranch,
|
|
363
|
-
`origin/${releaseBranch}`,
|
|
364
|
-
]);
|
|
365
|
-
} else {
|
|
366
|
-
runGitTaskCommand(root, ["switch", "-c", releaseBranch]);
|
|
367
|
-
}
|
|
368
|
-
const resumableStash = captureGitTaskCommand(root, [
|
|
369
|
-
"stash",
|
|
370
|
-
"list",
|
|
371
|
-
"--format=%gd%x09%s",
|
|
372
|
-
])
|
|
373
|
-
.split("\n")
|
|
374
|
-
.find((line) => line.includes(`release-resume:${releaseBranch}`))
|
|
375
|
-
?.split("\t")[0];
|
|
376
|
-
if (resumableStash) runGitTaskCommand(root, ["stash", "pop", resumableStash]);
|
|
377
|
-
if (
|
|
378
|
-
!gitTaskCommandSucceeds(root, ["merge-base", "--is-ancestor", currentBranch, "HEAD"])
|
|
379
|
-
) {
|
|
380
|
-
runGitTaskCommand(root, ["merge", "--no-edit", currentBranch]);
|
|
381
|
-
}
|
|
382
|
-
runTaskCommand(root, process.execPath, ["install"]);
|
|
383
|
-
|
|
384
|
-
const bumpScript = fileURLToPath(new URL("./bump.ts", import.meta.url));
|
|
385
|
-
const versionCheckScript = fileURLToPath(new URL("./version-check.ts", import.meta.url));
|
|
386
|
-
const preparedVersion = readWorkspaceVersion(root);
|
|
387
|
-
if (preparedVersion !== next.version) {
|
|
388
|
-
if (preparedVersion !== comparisonBase) {
|
|
389
|
-
throw new Error(
|
|
390
|
-
`${releaseBranch} carries ${preparedVersion}; expected ${comparisonBase} or ${next.version}`,
|
|
391
|
-
);
|
|
392
|
-
}
|
|
393
|
-
runTaskCommand(root, process.execPath, [
|
|
394
|
-
bumpScript,
|
|
395
|
-
"--level",
|
|
396
|
-
opts.level,
|
|
397
|
-
...opts.os.flatMap((value) => ["--os", value]),
|
|
398
|
-
...opts.arch.flatMap((value) => ["--arch", value]),
|
|
399
|
-
]);
|
|
400
|
-
} else {
|
|
401
|
-
logger.info(`resuming prepared ${releaseTag}`);
|
|
402
|
-
}
|
|
403
|
-
if (readWorkspaceVersion(root) !== next.version) {
|
|
404
|
-
throw new Error(`Release preparation did not produce ${next.version}`);
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
runTaskCommand(root, process.execPath, [versionCheckScript]);
|
|
408
|
-
if (opts.validate) {
|
|
409
|
-
for (const task of opts.validateTask) {
|
|
410
|
-
runTaskCommand(root, process.execPath, ["run", task]);
|
|
411
|
-
}
|
|
412
|
-
if (existsSync(join(root, "Cargo.toml"))) {
|
|
413
|
-
runTaskCommand(root, "cargo", [
|
|
414
|
-
"test",
|
|
415
|
-
"--workspace",
|
|
416
|
-
...(existsSync(join(root, "Cargo.lock")) ? ["--locked"] : []),
|
|
417
|
-
]);
|
|
418
|
-
}
|
|
419
|
-
runTaskCommand(root, process.execPath, ["run", "compile"]);
|
|
420
|
-
} else {
|
|
421
|
-
logger.warn("release validation skipped by --no-validate");
|
|
422
|
-
}
|
|
423
|
-
runTaskCommand(root, process.execPath, [versionCheckScript]);
|
|
424
|
-
const releaseSummary = opts.releaseSummary
|
|
425
|
-
? await generateReleaseSummary({
|
|
426
|
-
root,
|
|
427
|
-
version: next.version,
|
|
428
|
-
fromRef: `${opts.prefix}${comparisonBase}`,
|
|
429
|
-
providers: releaseSummaryProviders(opts.releaseSummaryProviders),
|
|
430
|
-
})
|
|
431
|
-
: undefined;
|
|
432
|
-
|
|
433
|
-
runGitTaskCommand(root, ["add", "-A"]);
|
|
434
|
-
const staged = captureGitTaskCommand(root, ["diff", "--cached", "--name-only"], {
|
|
435
|
-
check: true,
|
|
436
|
-
});
|
|
437
|
-
if (staged) {
|
|
438
|
-
runGitTaskCommand(root, ["commit", "-m", `chore(release): ${next.version}`]);
|
|
439
|
-
} else if (!localBranch && !remoteBranch) {
|
|
440
|
-
throw new Error("Release preparation produced no changes");
|
|
441
|
-
}
|
|
442
|
-
runGitTaskCommand(root, ["push", "--set-upstream", "origin", releaseBranch]);
|
|
443
|
-
|
|
444
|
-
const title = `chore(release): ${next.version}`;
|
|
445
|
-
const body = [
|
|
446
|
-
`Release ${releaseTag}.`,
|
|
447
|
-
"",
|
|
448
|
-
`Source commit: ${captureGitTaskCommand(root, ["rev-parse", `${releaseBranch}^`], { check: true })}`,
|
|
449
|
-
"",
|
|
450
|
-
"Merging this PR updates VERSION on main and prepares a draft GitHub Release.",
|
|
451
|
-
...(releaseSummary ? ["", releaseSummary] : []),
|
|
452
|
-
].join("\n");
|
|
453
|
-
const githubEnvironment = {
|
|
454
|
-
...process.env,
|
|
455
|
-
GH_HOST: account.hostname,
|
|
456
|
-
GH_REPO: githubRepositorySpecifier(account),
|
|
457
|
-
[githubTokenEnvironmentName(account.hostname)]: account.token,
|
|
458
|
-
};
|
|
459
|
-
if (
|
|
460
|
-
!taskCommandSucceeds(root, "gh", ["pr", "view", releaseBranch], {
|
|
461
|
-
env: githubEnvironment,
|
|
462
|
-
})
|
|
463
|
-
) {
|
|
464
|
-
runTaskCommand(
|
|
465
|
-
root,
|
|
466
|
-
"gh",
|
|
467
|
-
[
|
|
468
|
-
"pr",
|
|
469
|
-
"create",
|
|
470
|
-
"--base",
|
|
471
|
-
opts.base,
|
|
472
|
-
"--head",
|
|
473
|
-
releaseBranch,
|
|
474
|
-
"--title",
|
|
475
|
-
title,
|
|
476
|
-
"--body",
|
|
477
|
-
body,
|
|
478
|
-
],
|
|
479
|
-
{ env: githubEnvironment },
|
|
480
|
-
);
|
|
481
|
-
}
|
|
482
|
-
if (opts.approve) {
|
|
483
|
-
runTaskCommand(root, "gh", ["pr", "merge", releaseBranch, "--auto", "--merge"], {
|
|
484
|
-
env: githubEnvironment,
|
|
485
|
-
});
|
|
486
|
-
}
|
|
487
|
-
if (opts.approve && opts.wait) {
|
|
488
|
-
const mergeSha = await waitForPullRequestMerge(
|
|
489
|
-
root,
|
|
490
|
-
releaseBranch,
|
|
491
|
-
githubEnvironment,
|
|
492
|
-
waitTimeoutMs,
|
|
493
|
-
);
|
|
494
|
-
logger.info("release pull request merged", { releaseBranch, mergeSha });
|
|
495
|
-
runGitTaskCommand(root, ["fetch", "origin", opts.base]);
|
|
496
|
-
runGitTaskCommand(root, ["switch", "--detach", mergeSha]);
|
|
497
|
-
runTaskCommand(root, process.execPath, ["install"]);
|
|
498
|
-
const releaseCandidateScript = join(
|
|
499
|
-
root,
|
|
500
|
-
"node_modules/@dbx-tools/projen/tasks/release-candidate.ts",
|
|
501
|
-
);
|
|
502
|
-
const candidateArguments = [
|
|
503
|
-
releaseCandidateScript,
|
|
504
|
-
"--root",
|
|
505
|
-
root,
|
|
506
|
-
"--version",
|
|
507
|
-
next.version,
|
|
508
|
-
"--tag",
|
|
509
|
-
releaseTag,
|
|
510
|
-
"--sha",
|
|
511
|
-
mergeSha,
|
|
512
|
-
"--python-root",
|
|
513
|
-
opts.pythonRoot,
|
|
514
|
-
"--notes-file",
|
|
515
|
-
`docs/releases/v${next.version}.md`,
|
|
516
|
-
];
|
|
517
|
-
runTaskCommand(root, process.execPath, candidateArguments, {
|
|
518
|
-
env: githubEnvironment,
|
|
519
|
-
});
|
|
520
|
-
if (opts.localPublish) {
|
|
521
|
-
const localPublishScript = join(
|
|
522
|
-
root,
|
|
523
|
-
"node_modules/@dbx-tools/projen/tasks/local-publish.ts",
|
|
524
|
-
);
|
|
525
|
-
runTaskCommand(
|
|
526
|
-
root,
|
|
527
|
-
process.execPath,
|
|
528
|
-
[
|
|
529
|
-
localPublishScript,
|
|
530
|
-
"--root",
|
|
531
|
-
root,
|
|
532
|
-
"--candidate-directory",
|
|
533
|
-
"dist/release/upload",
|
|
534
|
-
"--version",
|
|
535
|
-
next.version,
|
|
536
|
-
"--tag",
|
|
537
|
-
releaseTag,
|
|
538
|
-
"--sha",
|
|
539
|
-
mergeSha,
|
|
540
|
-
"--local-registry",
|
|
541
|
-
opts.localRegistry,
|
|
542
|
-
"--local-pypi",
|
|
543
|
-
opts.localPypi,
|
|
544
|
-
...(opts.localCargo ? ["--local-cargo"] : []),
|
|
545
|
-
],
|
|
546
|
-
{ env: githubEnvironment },
|
|
547
|
-
);
|
|
548
|
-
} else {
|
|
549
|
-
logger.info("local publication skipped by --no-local-publish");
|
|
550
|
-
}
|
|
551
|
-
runTaskCommand(root, process.execPath, [...candidateArguments, "--upload-existing"], {
|
|
552
|
-
env: githubEnvironment,
|
|
553
|
-
});
|
|
554
|
-
logger.success(`prepared draft ${releaseTag} from ${mergeSha}`);
|
|
555
|
-
} else {
|
|
556
|
-
logger.success(`opened ${releaseBranch} for ${releaseTag}`);
|
|
557
|
-
}
|
|
558
|
-
releaseCompleted = true;
|
|
559
|
-
} finally {
|
|
560
|
-
const activeBranch = captureGitTaskCommand(root, ["branch", "--show-current"]);
|
|
561
|
-
if (activeBranch !== currentBranch) {
|
|
562
|
-
const releaseStatus = captureGitTaskCommand(
|
|
563
|
-
root,
|
|
564
|
-
["status", "--porcelain=v1", "--untracked-files=all"],
|
|
565
|
-
{ check: true },
|
|
566
|
-
);
|
|
567
|
-
if (releaseStatus) {
|
|
568
|
-
runGitTaskCommand(root, [
|
|
569
|
-
"stash",
|
|
570
|
-
"push",
|
|
571
|
-
"--include-untracked",
|
|
572
|
-
"--message",
|
|
573
|
-
`release-resume:${releaseBranch}`,
|
|
574
|
-
]);
|
|
575
|
-
}
|
|
576
|
-
runGitTaskCommand(root, ["switch", currentBranch]);
|
|
577
|
-
}
|
|
578
|
-
if (
|
|
579
|
-
releaseCompleted &&
|
|
580
|
-
captureGitTaskCommand(root, ["branch", "--list", releaseBranch])
|
|
581
|
-
) {
|
|
582
|
-
runGitTaskCommand(root, ["branch", "--delete", "--force", releaseBranch]);
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
});
|
|
586
|
-
},
|
|
587
|
-
);
|
|
588
|
-
|
|
589
|
-
if (import.meta.main) await program.parseAsync();
|