@dbx-tools/projen 0.9.18 → 0.9.21
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/._LICENSE +0 -0
- package/._README.md +0 -0
- package/._index.ts +0 -0
- package/._package.json +0 -0
- package/README.md +72 -88
- package/index.ts +2 -7
- package/package.json +9 -5
- 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-release-workflow.ts +18 -582
- package/src/_task-command.ts +75 -0
- package/src/project-js.ts +2 -42
- package/src/project-py.ts +60 -162
- package/src/project-rs.ts +4 -100
- package/src/release-catalog.ts +37 -203
- package/src/release.ts +96 -775
- package/src/scaffold.ts +2 -6
- package/src/workspace-version.ts +9 -121
- 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/bump.ts +17 -34
- package/tasks/local-publish.ts +124 -117
- package/tasks/publish-npm.ts +125 -41
- package/tasks/publish-python.ts +79 -106
- package/tasks/publish-uniffi-local.ts +129 -84
- package/tasks/publish.ts +44 -139
- package/tasks/python-registry.ts +2 -9
- package/tasks/release-assets.ts +56 -65
- package/tasks/release-candidate.ts +416 -0
- package/tasks/release-manifest.ts +273 -0
- package/tasks/release-pr.ts +312 -369
- package/tasks/release-summary.ts +8 -10
- package/tasks/release-version.ts +23 -2
- package/tasks/rust.ts +8 -13
- package/tasks/uniffi-python.d.ts +2 -3
- package/tasks/uniffi-python.js +7 -20
- package/tasks/uniffi-release.mjs +19 -13
- package/tasks/uniffi.ts +15 -24
- package/tasks/version-check.ts +24 -56
- package/src/release-plan.ts +0 -269
- package/src/release-please.ts +0 -242
- package/tasks/release-bootstrap.ts +0 -59
- package/tasks/release-plan.ts +0 -89
- package/tasks/release-please.ts +0 -162
- package/tasks/release-request.ts +0 -166
- package/tasks/release-summary-units.ts +0 -75
- package/tasks/stamp-python.ts +0 -13
package/tasks/release-pr.ts
CHANGED
|
@@ -10,11 +10,9 @@
|
|
|
10
10
|
import { existsSync } from "node:fs";
|
|
11
11
|
import { join } from "node:path";
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
|
-
import * as exec from "@dbx-tools/core/exec";
|
|
14
13
|
import * as projectUtils from "@dbx-tools/core/project-utils";
|
|
15
14
|
import { asyncUtils, json, log, object } from "@dbx-tools/shared-core";
|
|
16
15
|
import { Command } from "commander";
|
|
17
|
-
import { publishLocalRelease } from "./local-publish.ts";
|
|
18
16
|
import { hasLocalReleaseTargets, missingLocalReleaseTools } from "./release-assets.ts";
|
|
19
17
|
import { generateReleaseSummary } from "./release-summary.ts";
|
|
20
18
|
import {
|
|
@@ -26,7 +24,10 @@ import {
|
|
|
26
24
|
type VersionLevel,
|
|
27
25
|
} from "../src/_release-platform.ts";
|
|
28
26
|
import {
|
|
27
|
+
captureGitTaskCommand,
|
|
29
28
|
captureTaskCommand,
|
|
29
|
+
gitTaskCommandSucceeds,
|
|
30
|
+
runGitTaskCommand,
|
|
30
31
|
runTaskCommand,
|
|
31
32
|
runTaskCommandAsync,
|
|
32
33
|
taskCommandSucceeds,
|
|
@@ -55,57 +56,8 @@ import {
|
|
|
55
56
|
|
|
56
57
|
const logger = log.logger("projen:release");
|
|
57
58
|
|
|
58
|
-
export type ReleaseBuildMode = "auto" | "local" | "remote";
|
|
59
|
-
|
|
60
|
-
/** Resolve an explicit or environment-sensitive native release build mode. */
|
|
61
|
-
export function resolveReleaseBuildMode(options: {
|
|
62
|
-
readonly requested: ReleaseBuildMode;
|
|
63
|
-
readonly canManage: boolean;
|
|
64
|
-
readonly localTools: readonly string[];
|
|
65
|
-
readonly hasLocalTargets: boolean;
|
|
66
|
-
readonly approve: boolean;
|
|
67
|
-
readonly wait: boolean;
|
|
68
|
-
readonly hostPlatform?: NodeJS.Platform;
|
|
69
|
-
}): Exclude<ReleaseBuildMode, "auto"> {
|
|
70
|
-
const local =
|
|
71
|
-
options.canManage &&
|
|
72
|
-
options.hasLocalTargets &&
|
|
73
|
-
(options.hostPlatform ?? process.platform) === "darwin" &&
|
|
74
|
-
options.localTools.length === 0 &&
|
|
75
|
-
options.approve &&
|
|
76
|
-
options.wait;
|
|
77
|
-
if (options.requested === "auto") return local ? "local" : "remote";
|
|
78
|
-
if (options.requested === "remote") return "remote";
|
|
79
|
-
if (!options.canManage) throw new Error("--build local requires maintain or admin permission");
|
|
80
|
-
if (!options.hasLocalTargets) throw new Error("--build local requires Rust release targets");
|
|
81
|
-
if ((options.hostPlatform ?? process.platform) !== "darwin") {
|
|
82
|
-
throw new Error("--build local requires macOS");
|
|
83
|
-
}
|
|
84
|
-
if (options.localTools.length > 0) {
|
|
85
|
-
throw new Error(`--build local requires: ${options.localTools.join(", ")}`);
|
|
86
|
-
}
|
|
87
|
-
if (!options.approve || !options.wait) {
|
|
88
|
-
throw new Error("--build local requires automatic merge and waiting");
|
|
89
|
-
}
|
|
90
|
-
return "local";
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function git(
|
|
94
|
-
root: string,
|
|
95
|
-
args: string[],
|
|
96
|
-
{ capture = false, check = true }: { capture?: boolean; check?: boolean } = {},
|
|
97
|
-
): string {
|
|
98
|
-
if (capture) return captureTaskCommand(root, "git", args, { check });
|
|
99
|
-
runTaskCommand(root, "git", args);
|
|
100
|
-
return "";
|
|
101
|
-
}
|
|
102
|
-
|
|
103
59
|
function pushCurrentBranch(root: string, branch: string): void {
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function gitSucceeds(root: string, args: string[]): boolean {
|
|
108
|
-
return taskCommandSucceeds(root, "git", args);
|
|
60
|
+
runGitTaskCommand(root, ["push", "--set-upstream", "origin", `HEAD:${branch}`]);
|
|
109
61
|
}
|
|
110
62
|
|
|
111
63
|
/** Wait for required checks and return the merged pull request commit. */
|
|
@@ -115,6 +67,34 @@ async function waitForPullRequestMerge(
|
|
|
115
67
|
env: NodeJS.ProcessEnv,
|
|
116
68
|
timeoutMs: number,
|
|
117
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
|
+
}
|
|
118
98
|
await runTaskCommandAsync(
|
|
119
99
|
root,
|
|
120
100
|
"gh",
|
|
@@ -145,64 +125,6 @@ async function waitForPullRequestMerge(
|
|
|
145
125
|
throw new Error(`Release pull request did not merge within ${timeoutMs}ms`);
|
|
146
126
|
}
|
|
147
127
|
|
|
148
|
-
/** Find and watch the release workflow for an exact merged commit. */
|
|
149
|
-
async function waitForReleaseWorkflow(
|
|
150
|
-
root: string,
|
|
151
|
-
baseBranch: string,
|
|
152
|
-
mergeSha: string,
|
|
153
|
-
env: NodeJS.ProcessEnv,
|
|
154
|
-
timeoutMs: number,
|
|
155
|
-
): Promise<void> {
|
|
156
|
-
const deadline = Date.now() + timeoutMs;
|
|
157
|
-
let runId = "";
|
|
158
|
-
while (Date.now() < deadline) {
|
|
159
|
-
runId = captureTaskCommand(
|
|
160
|
-
root,
|
|
161
|
-
"gh",
|
|
162
|
-
[
|
|
163
|
-
"run",
|
|
164
|
-
"list",
|
|
165
|
-
"--workflow",
|
|
166
|
-
"release.yml",
|
|
167
|
-
"--branch",
|
|
168
|
-
baseBranch,
|
|
169
|
-
"--event",
|
|
170
|
-
"push",
|
|
171
|
-
"--commit",
|
|
172
|
-
mergeSha,
|
|
173
|
-
"--limit",
|
|
174
|
-
"1",
|
|
175
|
-
"--json",
|
|
176
|
-
"databaseId",
|
|
177
|
-
"--jq",
|
|
178
|
-
".[0].databaseId // empty",
|
|
179
|
-
],
|
|
180
|
-
{ env },
|
|
181
|
-
);
|
|
182
|
-
if (runId) break;
|
|
183
|
-
await asyncUtils.sleep(5_000);
|
|
184
|
-
}
|
|
185
|
-
if (!runId) throw new Error(`Release workflow did not start within ${timeoutMs}ms`);
|
|
186
|
-
await runTaskCommandAsync(root, "gh", ["run", "watch", runId, "--exit-status"], {
|
|
187
|
-
env,
|
|
188
|
-
signal: AbortSignal.timeout(timeoutMs),
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
async function waitForGitHubRelease(
|
|
193
|
-
root: string,
|
|
194
|
-
tag: string,
|
|
195
|
-
env: NodeJS.ProcessEnv,
|
|
196
|
-
timeoutMs: number,
|
|
197
|
-
): Promise<void> {
|
|
198
|
-
const deadline = Date.now() + timeoutMs;
|
|
199
|
-
while (Date.now() < deadline) {
|
|
200
|
-
if (taskCommandSucceeds(root, "gh", ["release", "view", tag], { env })) return;
|
|
201
|
-
await asyncUtils.sleep(2_000);
|
|
202
|
-
}
|
|
203
|
-
throw new Error(`GitHub Release ${tag} did not appear within ${timeoutMs}ms`);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
128
|
function githubAccount(root: string): {
|
|
207
129
|
hostname: string;
|
|
208
130
|
owner: string;
|
|
@@ -214,27 +136,22 @@ function githubAccount(root: string): {
|
|
|
214
136
|
const repository = projectUtils.repositoryUrl(root);
|
|
215
137
|
if (!repository) throw new Error("Release preparation requires a GitHub repository");
|
|
216
138
|
const identity = githubRepositoryIdentity(repository);
|
|
217
|
-
const status =
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const accounts = githubAuthenticatedAccounts(status ?? "", identity.hostname);
|
|
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);
|
|
227
148
|
for (const account of accounts) {
|
|
228
149
|
if (!githubAccountSupportsWorkflowChanges(account)) continue;
|
|
229
|
-
const token =
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
stdin: "ignore",
|
|
235
|
-
check: false,
|
|
236
|
-
})
|
|
237
|
-
.stdout?.trim();
|
|
150
|
+
const token = captureTaskCommand(
|
|
151
|
+
root,
|
|
152
|
+
"gh",
|
|
153
|
+
githubTokenArguments(identity.hostname, account.login),
|
|
154
|
+
);
|
|
238
155
|
if (!token) continue;
|
|
239
156
|
const tokenEnvironment = {
|
|
240
157
|
...process.env,
|
|
@@ -242,16 +159,11 @@ function githubAccount(root: string): {
|
|
|
242
159
|
GH_REPO: githubRepositorySpecifier(identity),
|
|
243
160
|
[githubTokenEnvironmentName(identity.hostname)]: token,
|
|
244
161
|
};
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
stdin: "ignore",
|
|
251
|
-
check: false,
|
|
252
|
-
});
|
|
253
|
-
const repositoryData =
|
|
254
|
-
repositoryResult.exitCode === 0 ? json.parseRecord(repositoryResult.stdout ?? "") : undefined;
|
|
162
|
+
const repositoryData = json.parseRecord(
|
|
163
|
+
captureTaskCommand(root, "gh", ["api", githubRepositoryApiPath(identity)], {
|
|
164
|
+
env: tokenEnvironment,
|
|
165
|
+
}),
|
|
166
|
+
);
|
|
255
167
|
const permissions = githubRepositoryPermissions(repositoryData);
|
|
256
168
|
if (!permissions.push && !permissions.maintain && !permissions.admin) {
|
|
257
169
|
continue;
|
|
@@ -303,12 +215,12 @@ program
|
|
|
303
215
|
.addOption(releaseLevelOption())
|
|
304
216
|
.option("--prefix <prefix>", "release tag prefix", "v")
|
|
305
217
|
.option("--base <branch>", "release pull request base branch", "main")
|
|
218
|
+
.option("--python-root <path>", "Python package root", "packages/py")
|
|
306
219
|
.option("--message <message>", "commit message for pending source work", "chore: prepare release")
|
|
307
220
|
.addOption(releaseOperatingSystemOption())
|
|
308
221
|
.addOption(releaseArchitectureOption())
|
|
309
222
|
.option("--local-registry <value>", "local npm registry: auto, false, or an explicit URL", "auto")
|
|
310
223
|
.option("--local-pypi <value>", "local PyPI index: auto, false, or an explicit URL", "auto")
|
|
311
|
-
.option("--python-root <path>", "Python workspace package root")
|
|
312
224
|
.option(
|
|
313
225
|
"--validate-task <task>",
|
|
314
226
|
"repository task to run before release validation; repeatable",
|
|
@@ -323,12 +235,11 @@ program
|
|
|
323
235
|
.option("--no-validate", "skip repository validation tasks, Rust tests, and TypeScript compile")
|
|
324
236
|
.option("--no-local-publish", "skip local npm, PyPI, and Cargo publication")
|
|
325
237
|
.option("--no-local-cargo", "skip local Cargo publication")
|
|
326
|
-
.option("--build <mode>", "native release build: auto, local, or remote", "auto")
|
|
327
238
|
.option("--no-approve", "open the release pull request without enabling automatic merge")
|
|
328
239
|
.option("--no-wait", "return after enabling automatic merge without watching publication")
|
|
329
240
|
.option(
|
|
330
241
|
"--wait-timeout-minutes <minutes>",
|
|
331
|
-
"maximum time for pull request checks
|
|
242
|
+
"maximum time for pull request checks and merge",
|
|
332
243
|
"120",
|
|
333
244
|
)
|
|
334
245
|
.action(
|
|
@@ -336,19 +247,18 @@ program
|
|
|
336
247
|
level: VersionLevel;
|
|
337
248
|
prefix: string;
|
|
338
249
|
base: string;
|
|
250
|
+
pythonRoot: string;
|
|
339
251
|
message: string;
|
|
340
252
|
os: ReleaseOs[];
|
|
341
253
|
arch: ReleaseArch[];
|
|
342
254
|
localRegistry: string;
|
|
343
255
|
localPypi: string;
|
|
344
|
-
pythonRoot?: string;
|
|
345
256
|
validateTask: string[];
|
|
346
257
|
releaseSummary: boolean;
|
|
347
258
|
releaseSummaryProviders?: string;
|
|
348
259
|
validate: boolean;
|
|
349
260
|
localPublish: boolean;
|
|
350
261
|
localCargo: boolean;
|
|
351
|
-
build: ReleaseBuildMode;
|
|
352
262
|
approve: boolean;
|
|
353
263
|
wait: boolean;
|
|
354
264
|
waitTimeoutMinutes: string;
|
|
@@ -367,35 +277,26 @@ program
|
|
|
367
277
|
throw new Error("--wait-timeout-minutes must be a positive integer");
|
|
368
278
|
}
|
|
369
279
|
const waitTimeoutMs = waitTimeoutMinutes * 60_000;
|
|
370
|
-
|
|
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
|
+
});
|
|
371
286
|
if (!currentBranch) throw new Error("Release preparation requires a local branch");
|
|
372
287
|
const account = githubAccount(root);
|
|
373
|
-
if (!
|
|
374
|
-
throw new Error("
|
|
288
|
+
if (!account.canManage) {
|
|
289
|
+
throw new Error("Release preparation requires maintain or admin permission");
|
|
375
290
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
wait: opts.wait,
|
|
386
|
-
});
|
|
387
|
-
logger.info("selected native release build", {
|
|
388
|
-
requested: opts.build,
|
|
389
|
-
resolved: buildMode,
|
|
390
|
-
manager: account.canManage,
|
|
391
|
-
...(localTools.length > 0 ? { missing: localTools } : {}),
|
|
392
|
-
});
|
|
393
|
-
git(root, ["fetch", "--tags", "origin", opts.base]);
|
|
394
|
-
const comparisonBase = resolveBaseVersion(root, [opts.prefix], {
|
|
395
|
-
fetch: false,
|
|
396
|
-
includeComponentTags: false,
|
|
397
|
-
}).version;
|
|
398
|
-
const next = resolveNextVersion(root, [opts.prefix], opts.level, { fetch: false });
|
|
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);
|
|
399
300
|
const releaseVersionScript = fileURLToPath(
|
|
400
301
|
new URL("./release-version.ts", import.meta.url),
|
|
401
302
|
);
|
|
@@ -409,236 +310,278 @@ program
|
|
|
409
310
|
]);
|
|
410
311
|
const releaseTag = `${opts.prefix}${next.version}`;
|
|
411
312
|
const releaseBranch = `release/${releaseTag}`;
|
|
412
|
-
const releaseRoot = join(root, ".worktrees", releaseTag);
|
|
413
313
|
if (currentBranch.startsWith("release/")) {
|
|
414
314
|
throw new Error("Release preparation must start from a source branch");
|
|
415
315
|
}
|
|
416
316
|
if (
|
|
417
|
-
|
|
418
|
-
capture: true,
|
|
419
|
-
check: false,
|
|
420
|
-
})
|
|
317
|
+
captureGitTaskCommand(root, ["ls-remote", "--tags", "origin", `refs/tags/${releaseTag}`])
|
|
421
318
|
) {
|
|
422
319
|
throw new Error(`Release tag already exists: ${releaseTag}`);
|
|
423
320
|
}
|
|
424
321
|
|
|
425
|
-
const status =
|
|
426
|
-
|
|
427
|
-
|
|
322
|
+
const status = captureGitTaskCommand(
|
|
323
|
+
root,
|
|
324
|
+
["status", "--porcelain=v1", "--untracked-files=all"],
|
|
325
|
+
{ check: true },
|
|
326
|
+
);
|
|
428
327
|
if (status) {
|
|
429
|
-
|
|
430
|
-
|
|
328
|
+
runGitTaskCommand(root, ["add", "-A"]);
|
|
329
|
+
runGitTaskCommand(root, ["commit", "-m", opts.message]);
|
|
431
330
|
}
|
|
432
|
-
if (
|
|
433
|
-
|
|
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}`]);
|
|
434
340
|
}
|
|
435
341
|
pushCurrentBranch(root, currentBranch);
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
{
|
|
445
|
-
)
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
"stash",
|
|
459
|
-
"push",
|
|
460
|
-
"--include-untracked",
|
|
461
|
-
"--message",
|
|
462
|
-
"release-resume",
|
|
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}`,
|
|
463
364
|
]);
|
|
365
|
+
} else {
|
|
366
|
+
runGitTaskCommand(root, ["switch", "-c", releaseBranch]);
|
|
464
367
|
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
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]);
|
|
470
381
|
}
|
|
471
|
-
|
|
472
|
-
}
|
|
473
|
-
runTaskCommand(releaseRoot, process.execPath, ["install"]);
|
|
474
|
-
|
|
475
|
-
const bumpScript = fileURLToPath(new URL("./bump.ts", import.meta.url));
|
|
476
|
-
const versionCheckScript = fileURLToPath(new URL("./version-check.ts", import.meta.url));
|
|
477
|
-
runTaskCommand(releaseRoot, process.execPath, [
|
|
478
|
-
bumpScript,
|
|
479
|
-
"--level",
|
|
480
|
-
opts.level,
|
|
481
|
-
"--prefix",
|
|
482
|
-
opts.prefix,
|
|
483
|
-
...opts.os.flatMap((value) => ["--os", value]),
|
|
484
|
-
...opts.arch.flatMap((value) => ["--arch", value]),
|
|
485
|
-
]);
|
|
486
|
-
if (readWorkspaceVersion(releaseRoot) !== next.version) {
|
|
487
|
-
throw new Error(`Release preparation did not produce ${next.version}`);
|
|
488
|
-
}
|
|
382
|
+
runTaskCommand(root, process.execPath, ["install"]);
|
|
489
383
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
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]),
|
|
500
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}`);
|
|
501
405
|
}
|
|
502
|
-
runTaskCommand(releaseRoot, process.execPath, ["run", "compile"]);
|
|
503
|
-
} else {
|
|
504
|
-
logger.warn("release validation skipped by --no-validate");
|
|
505
|
-
}
|
|
506
|
-
if (opts.localPublish) {
|
|
507
|
-
await publishLocalRelease({
|
|
508
|
-
root: releaseRoot,
|
|
509
|
-
version: next.version,
|
|
510
|
-
localRegistry: opts.localRegistry,
|
|
511
|
-
localPypi: opts.localPypi,
|
|
512
|
-
pythonRoot: opts.pythonRoot,
|
|
513
|
-
localCargo: opts.localCargo,
|
|
514
|
-
reuseValidatedNodeCompile: opts.validate,
|
|
515
|
-
});
|
|
516
|
-
} else {
|
|
517
|
-
logger.info("local publication skipped by --no-local-publish");
|
|
518
|
-
}
|
|
519
|
-
runTaskCommand(releaseRoot, process.execPath, [versionCheckScript]);
|
|
520
|
-
const releaseSummary = opts.releaseSummary
|
|
521
|
-
? await generateReleaseSummary({
|
|
522
|
-
root: releaseRoot,
|
|
523
|
-
version: next.version,
|
|
524
|
-
fromRef: `${opts.prefix}${comparisonBase}`,
|
|
525
|
-
providers: releaseSummaryProviders(opts.releaseSummaryProviders),
|
|
526
|
-
})
|
|
527
|
-
: undefined;
|
|
528
406
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
"
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
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;
|
|
546
432
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
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
|
+
};
|
|
565
459
|
if (
|
|
566
|
-
taskCommandSucceeds(root, "gh", ["pr", "view", releaseBranch], {
|
|
460
|
+
!taskCommandSucceeds(root, "gh", ["pr", "view", releaseBranch], {
|
|
567
461
|
env: githubEnvironment,
|
|
568
462
|
})
|
|
569
463
|
) {
|
|
570
|
-
return;
|
|
571
|
-
}
|
|
572
|
-
runTaskCommand(
|
|
573
|
-
root,
|
|
574
|
-
"gh",
|
|
575
|
-
[
|
|
576
|
-
"pr",
|
|
577
|
-
"create",
|
|
578
|
-
"--base",
|
|
579
|
-
opts.base,
|
|
580
|
-
"--head",
|
|
581
|
-
releaseBranch,
|
|
582
|
-
"--title",
|
|
583
|
-
title,
|
|
584
|
-
"--body",
|
|
585
|
-
body,
|
|
586
|
-
],
|
|
587
|
-
{ env: githubEnvironment },
|
|
588
|
-
);
|
|
589
|
-
};
|
|
590
|
-
ensurePullRequest();
|
|
591
|
-
if (opts.approve) {
|
|
592
|
-
runTaskCommand(root, "gh", ["pr", "merge", releaseBranch, "--auto", "--merge"], {
|
|
593
|
-
env: githubEnvironment,
|
|
594
|
-
});
|
|
595
|
-
}
|
|
596
|
-
if (opts.approve && opts.wait) {
|
|
597
|
-
const mergeSha = await waitForPullRequestMerge(
|
|
598
|
-
root,
|
|
599
|
-
releaseBranch,
|
|
600
|
-
githubEnvironment,
|
|
601
|
-
waitTimeoutMs,
|
|
602
|
-
);
|
|
603
|
-
logger.info("release pull request merged", { releaseBranch, mergeSha });
|
|
604
|
-
if (buildMode === "local") {
|
|
605
|
-
git(root, ["fetch", "origin", opts.base]);
|
|
606
|
-
git(releaseRoot, ["switch", "--detach", mergeSha]);
|
|
607
|
-
runTaskCommand(releaseRoot, process.execPath, ["install"]);
|
|
608
|
-
await waitForGitHubRelease(root, releaseTag, githubEnvironment, waitTimeoutMs);
|
|
609
|
-
const releaseAssetsScript = join(
|
|
610
|
-
releaseRoot,
|
|
611
|
-
"node_modules/@dbx-tools/projen/tasks/release-assets.ts",
|
|
612
|
-
);
|
|
613
464
|
runTaskCommand(
|
|
614
|
-
|
|
615
|
-
|
|
465
|
+
root,
|
|
466
|
+
"gh",
|
|
616
467
|
[
|
|
617
|
-
|
|
618
|
-
"
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
468
|
+
"pr",
|
|
469
|
+
"create",
|
|
470
|
+
"--base",
|
|
471
|
+
opts.base,
|
|
472
|
+
"--head",
|
|
473
|
+
releaseBranch,
|
|
474
|
+
"--title",
|
|
475
|
+
title,
|
|
476
|
+
"--body",
|
|
477
|
+
body,
|
|
625
478
|
],
|
|
626
479
|
{ env: githubEnvironment },
|
|
627
480
|
);
|
|
628
481
|
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
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
|
+
}
|
|
634
584
|
}
|
|
635
|
-
git(root, ["worktree", "remove", "--force", releaseRoot]);
|
|
636
|
-
git(root, ["branch", "--delete", "--force", releaseBranch]);
|
|
637
|
-
logger.success(
|
|
638
|
-
`${
|
|
639
|
-
opts.approve ? "enabled automatic merge for" : "opened"
|
|
640
|
-
} ${releaseBranch} for ${releaseTag}${opts.approve && !opts.wait ? " without waiting" : ""}`,
|
|
641
|
-
);
|
|
642
585
|
});
|
|
643
586
|
},
|
|
644
587
|
);
|