@dbx-tools/projen 0.9.19 → 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 +83 -229
- package/index.ts +11 -18
- package/package.json +9 -5
- package/src/_task-command.ts +75 -0
- 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 +5 -48
- package/src/project-predicate.ts +1 -2
- package/src/project-py.ts +87 -199
- 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 +40 -208
- package/src/{release-dispatch.ts → release-context.ts} +0 -11
- package/src/release.ts +74 -778
- package/src/scaffold.ts +2 -6
- package/src/workspace-version.ts +9 -121
- package/tasks/bump.ts +17 -34
- package/tasks/databricks-cli-assets.ts +129 -0
- package/tasks/local-publish.ts +61 -138
- package/tasks/publish-npm.ts +71 -30
- package/tasks/publish-python.ts +61 -148
- package/tasks/publish.ts +32 -182
- package/tasks/python-node-bindings-watch.ts +47 -0
- package/tasks/python-node-bindings.ts +1017 -0
- package/tasks/python-registry.ts +2 -9
- package/tasks/python-release.ts +74 -0
- package/tasks/release-version.ts +23 -2
- package/tasks/release.ts +99 -0
- package/tasks/sync.ts +13 -18
- package/tasks/version-check.ts +13 -72
- package/src/_rust-project.ts +0 -429
- package/src/_rust-release-workflow.ts +0 -698
- package/src/project-rs.ts +0 -1127
- package/src/release-plan.ts +0 -269
- package/src/release-please.ts +0 -242
- package/src/uniffi.ts +0 -65
- package/tasks/publish-uniffi-local.ts +0 -314
- package/tasks/release-assets.ts +0 -476
- package/tasks/release-bootstrap.ts +0 -59
- package/tasks/release-candidate.ts +0 -383
- package/tasks/release-manifest.ts +0 -273
- package/tasks/release-plan.ts +0 -89
- package/tasks/release-please.ts +0 -162
- package/tasks/release-pr.ts +0 -563
- package/tasks/release-request.ts +0 -166
- package/tasks/release-summary-units.ts +0 -75
- package/tasks/release-summary.ts +0 -381
- package/tasks/rust.ts +0 -178
- package/tasks/stamp-python.ts +0 -13
- package/tasks/uniffi-python.d.ts +0 -61
- package/tasks/uniffi-python.js +0 -191
- package/tasks/uniffi-release.mjs +0 -573
- package/tasks/uniffi.ts +0 -271
package/src/release.ts
CHANGED
|
@@ -6,24 +6,17 @@ import { JobPermission, type Job, type JobStep } from "projen/lib/github/workflo
|
|
|
6
6
|
import { BUN_VERSION, bunCacheRestoreSteps, bunCacheSaveStep } from "./bun-workflow.ts";
|
|
7
7
|
import { projectReleaseBranch, type DBXToolsJavaScriptProject } from "./project-js.ts";
|
|
8
8
|
import { applyTasks, taskScript } from "./project.ts";
|
|
9
|
-
import {
|
|
10
|
-
RELEASE_SHA,
|
|
11
|
-
RELEASE_TAG,
|
|
12
|
-
RELEASE_VERSION,
|
|
13
|
-
releaseSourceSteps,
|
|
14
|
-
type ReleaseSummaryProviderName,
|
|
15
|
-
} from "./release-dispatch.ts";
|
|
16
|
-
import { readWorkspaceVersion } from "./workspace-version.ts";
|
|
9
|
+
import { RELEASE_VERSION, releaseSourceSteps } from "./release-context.ts";
|
|
17
10
|
|
|
18
11
|
const NODE_VERSION = "24";
|
|
12
|
+
const NPM_VERSION = "11.4.2";
|
|
19
13
|
const NPM_REGISTRY_URL = "https://registry.npmjs.org";
|
|
20
14
|
const nodeReleaseProjects = new WeakSet<DBXToolsJavaScriptProject>();
|
|
21
15
|
const releaseTagPrefixes = new WeakMap<DBXToolsJavaScriptProject, string>();
|
|
22
16
|
const releaseWorkflows = new WeakMap<DBXToolsJavaScriptProject, GithubWorkflow>();
|
|
23
|
-
const independentPublicationJobs = new WeakMap<GithubWorkflow, Set<string>>();
|
|
24
17
|
|
|
25
18
|
/** Independently recoverable portions of the release workflow. */
|
|
26
|
-
export type ReleaseStage = "all" | "node" | "python" | "
|
|
19
|
+
export type ReleaseStage = "all" | "node" | "python" | "docs";
|
|
27
20
|
|
|
28
21
|
/** GitHub Pages configuration included in the unified release workflow. */
|
|
29
22
|
export interface ReleaseDocsOptions {
|
|
@@ -37,16 +30,6 @@ export interface ReleaseDocsOptions {
|
|
|
37
30
|
readonly artifactPath: string;
|
|
38
31
|
}
|
|
39
32
|
|
|
40
|
-
/** Optional local AI summary generation during reviewed release preparation. */
|
|
41
|
-
export interface ReleaseSummaryOptions {
|
|
42
|
-
/**
|
|
43
|
-
* Provider fallback order.
|
|
44
|
-
*
|
|
45
|
-
* @default ["cursor", "codex", "claude"]
|
|
46
|
-
*/
|
|
47
|
-
readonly providers?: readonly ReleaseSummaryProviderName[];
|
|
48
|
-
}
|
|
49
|
-
|
|
50
33
|
/** Options for {@link DBXToolsRelease}. */
|
|
51
34
|
export interface DBXToolsReleaseOptions {
|
|
52
35
|
/** Git tag prefix. Defaults to `v`. */
|
|
@@ -55,21 +38,10 @@ export interface DBXToolsReleaseOptions {
|
|
|
55
38
|
readonly nodeRelease?: boolean;
|
|
56
39
|
/** Build and deploy generated documentation through GitHub Pages. */
|
|
57
40
|
readonly docs?: ReleaseDocsOptions;
|
|
58
|
-
/** Python package root
|
|
41
|
+
/** Python package root used by attached Python publication jobs. */
|
|
59
42
|
readonly pythonRoot?: string;
|
|
60
|
-
/** Repository task names run
|
|
43
|
+
/** Repository task names run after VERSION verification and before publication. */
|
|
61
44
|
readonly validationTasks?: readonly string[];
|
|
62
|
-
/**
|
|
63
|
-
* Existing source branch safely fast-forwarded after publication.
|
|
64
|
-
*
|
|
65
|
-
* Missing or diverged branches are left untouched.
|
|
66
|
-
*/
|
|
67
|
-
readonly syncBranch?: string | false;
|
|
68
|
-
/**
|
|
69
|
-
* Generate a versioned release summary locally. Defaults to enabled with the
|
|
70
|
-
* standard Cursor, Codex, Claude fallback order.
|
|
71
|
-
*/
|
|
72
|
-
readonly summary?: boolean | ReleaseSummaryOptions;
|
|
73
45
|
}
|
|
74
46
|
|
|
75
47
|
/** Locate the unified workflow so attached language workspaces can add jobs. */
|
|
@@ -84,13 +56,6 @@ export function tryReleaseWorkflow(project: DBXToolsJavaScriptProject): GithubWo
|
|
|
84
56
|
return releaseWorkflows.get(project);
|
|
85
57
|
}
|
|
86
58
|
|
|
87
|
-
/** Register one publication job for the independent release completion barrier. */
|
|
88
|
-
export function registerIndependentPublicationJob(workflow: GithubWorkflow, jobId: string): void {
|
|
89
|
-
const jobs = independentPublicationJobs.get(workflow) ?? new Set<string>();
|
|
90
|
-
jobs.add(jobId);
|
|
91
|
-
independentPublicationJobs.set(workflow, jobs);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
59
|
/** Whether the unified workflow publishes the normal npm workspace. */
|
|
95
60
|
export function hasNodeRelease(project: DBXToolsJavaScriptProject): boolean {
|
|
96
61
|
return nodeReleaseProjects.has(project);
|
|
@@ -103,32 +68,22 @@ export function releaseTagPattern(project: DBXToolsJavaScriptProject): string {
|
|
|
103
68
|
return `${prefix}*`;
|
|
104
69
|
}
|
|
105
70
|
|
|
106
|
-
/** Run a release stage
|
|
71
|
+
/** Run a release stage after its prerequisite tag-build jobs succeed. */
|
|
107
72
|
export function releaseStageCondition(
|
|
108
|
-
|
|
73
|
+
_stage: Exclude<ReleaseStage, "all">,
|
|
109
74
|
prerequisites: readonly string[] = [],
|
|
110
75
|
): string {
|
|
111
|
-
if (prerequisites.length === 0) {
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
const prefix = prerequisites.length
|
|
115
|
-
? `always() && ${prerequisites.map((condition) => `(${condition})`).join(" && ")} && `
|
|
116
|
-
: "";
|
|
117
|
-
return `\${{ ${prefix}(github.event_name == 'release' || inputs.stage == 'all' || inputs.stage == '${stage}') }}`;
|
|
76
|
+
if (prerequisites.length === 0) return "${{ success() }}";
|
|
77
|
+
return `\${{ always() && ${prerequisites.map((condition) => `(${condition})`).join(" && ")} }}`;
|
|
118
78
|
}
|
|
119
79
|
|
|
120
|
-
/** Publish a
|
|
80
|
+
/** Publish a release stage after its prerequisite tag-build jobs succeed. */
|
|
121
81
|
export function releasePublishCondition(
|
|
122
|
-
|
|
82
|
+
_stage: Exclude<ReleaseStage, "all">,
|
|
123
83
|
prerequisites: readonly string[] = [],
|
|
124
84
|
): string {
|
|
125
|
-
if (prerequisites.length === 0) {
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
const prefix = prerequisites.length
|
|
129
|
-
? `always() && ${prerequisites.map((condition) => `(${condition})`).join(" && ")} && `
|
|
130
|
-
: "";
|
|
131
|
-
return `\${{ ${prefix}(github.event_name == 'release' || (inputs.dry_run != true && (inputs.stage == 'all' || inputs.stage == '${stage}'))) }}`;
|
|
85
|
+
if (prerequisites.length === 0) return "${{ success() }}";
|
|
86
|
+
return `\${{ always() && ${prerequisites.map((condition) => `(${condition})`).join(" && ")} }}`;
|
|
132
87
|
}
|
|
133
88
|
|
|
134
89
|
/** Shared Bun, Node, cache, and install setup for Node release jobs. */
|
|
@@ -145,25 +100,59 @@ export function nodeReleaseSetupSteps(project: DBXToolsJavaScriptProject): reado
|
|
|
145
100
|
"package-manager-cache": false,
|
|
146
101
|
},
|
|
147
102
|
},
|
|
103
|
+
{ name: "Install npm CLI", run: `npm install --global npm@${NPM_VERSION}` },
|
|
148
104
|
{ name: "Install", run: "bun install" },
|
|
149
105
|
bunCacheSaveStep(),
|
|
150
106
|
];
|
|
151
107
|
}
|
|
152
108
|
|
|
153
|
-
/**
|
|
109
|
+
/** Token authentication, GitHub provenance, and dry-run values shared by npm publishers. */
|
|
154
110
|
export function npmPublishEnvironment(): Record<string, string> {
|
|
155
111
|
return {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
DRY_RUN:
|
|
159
|
-
"${{ github.event_name == 'workflow_dispatch' && inputs.dry_run && '--dry-run' || '' }}",
|
|
112
|
+
NODE_AUTH_TOKEN: "${{ secrets.NPM_TOKEN }}",
|
|
113
|
+
NPM_CONFIG_PROVENANCE: "true",
|
|
114
|
+
DRY_RUN: "",
|
|
160
115
|
};
|
|
161
116
|
}
|
|
162
117
|
|
|
163
|
-
|
|
118
|
+
/** Install uv and restore its download/build cache using Python manifests. */
|
|
119
|
+
export function uvSetupStep(): JobStep {
|
|
120
|
+
return {
|
|
121
|
+
name: "Setup uv",
|
|
122
|
+
uses: "astral-sh/setup-uv@v7",
|
|
123
|
+
with: {
|
|
124
|
+
"enable-cache": true,
|
|
125
|
+
"cache-dependency-glob": "**/pyproject.toml",
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function refreshDocsRegistryDependencies(workflow: GithubWorkflow): void {
|
|
131
|
+
const docs = workflow.getJob("build-docs") as Job | undefined;
|
|
132
|
+
if (!docs) return;
|
|
133
|
+
const registryJobs = Object.keys(workflow.jobs).filter(
|
|
134
|
+
(name) => name === "publish-node" || name.startsWith("publish-pypi-"),
|
|
135
|
+
);
|
|
136
|
+
if (registryJobs.length > 0) workflow.updateJob("build-docs", { ...docs, needs: registryJobs });
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Keep documentation publication behind every configured package registry. */
|
|
140
|
+
export function refreshReleaseDocsDependencies(project: DBXToolsJavaScriptProject): void {
|
|
141
|
+
const workflow = releaseWorkflows.get(project);
|
|
142
|
+
if (workflow) refreshDocsRegistryDependencies(workflow);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function verifyContextJob(
|
|
146
|
+
project: DBXToolsJavaScriptProject,
|
|
147
|
+
tagPrefix: string,
|
|
148
|
+
releaseBranch: string,
|
|
149
|
+
options: DBXToolsReleaseOptions,
|
|
150
|
+
): Job {
|
|
164
151
|
return {
|
|
165
152
|
runsOn: ["ubuntu-latest"],
|
|
166
153
|
permissions: { contents: JobPermission.READ },
|
|
154
|
+
timeoutMinutes: 30,
|
|
155
|
+
env: { BUN_VERSION, CI: "true" },
|
|
167
156
|
outputs: {
|
|
168
157
|
release_tag: { stepId: "release", outputName: "release_tag" },
|
|
169
158
|
expected_sha: { stepId: "release", outputName: "expected_sha" },
|
|
@@ -174,24 +163,19 @@ function verifyContextJob(tagPrefix: string, releaseBranch: string): Job {
|
|
|
174
163
|
name: "Checkout release source",
|
|
175
164
|
uses: "actions/checkout@v6",
|
|
176
165
|
with: {
|
|
177
|
-
ref: "${{ github.
|
|
166
|
+
ref: "${{ github.ref_name }}",
|
|
178
167
|
"fetch-depth": 0,
|
|
179
168
|
},
|
|
180
169
|
},
|
|
181
|
-
{
|
|
182
|
-
name: "Setup Bun",
|
|
183
|
-
uses: "oven-sh/setup-bun@v2",
|
|
184
|
-
with: { "bun-version": BUN_VERSION },
|
|
185
|
-
},
|
|
170
|
+
...bunCacheRestoreSteps(project, { ignorePaths: project.workflowCacheIgnorePaths }),
|
|
186
171
|
{ name: "Install release validation dependencies", run: "bun install --frozen-lockfile" },
|
|
172
|
+
bunCacheSaveStep(),
|
|
187
173
|
{
|
|
188
174
|
name: "Verify release context",
|
|
189
175
|
id: "release",
|
|
190
176
|
shell: "bash",
|
|
191
177
|
env: {
|
|
192
|
-
RELEASE_TAG:
|
|
193
|
-
"${{ github.event_name == 'release' && github.event.release.tag_name || inputs.release_tag }}",
|
|
194
|
-
GH_TOKEN: "${{ github.token }}",
|
|
178
|
+
RELEASE_TAG: "${{ github.ref_name }}",
|
|
195
179
|
},
|
|
196
180
|
// prettier-ignore
|
|
197
181
|
run: stringUtils.dedent(
|
|
@@ -205,17 +189,8 @@ function verifyContextJob(tagPrefix: string, releaseBranch: string): Job {
|
|
|
205
189
|
test "$(git cat-file -t "$RELEASE_TAG")" = "tag"
|
|
206
190
|
RELEASE_SHA="$(git rev-parse "$RELEASE_TAG^{commit}")"
|
|
207
191
|
test "$(git rev-parse HEAD)" = "$RELEASE_SHA"
|
|
208
|
-
git
|
|
192
|
+
test "$(git rev-parse "origin/${releaseBranch}")" = "$RELEASE_SHA"
|
|
209
193
|
test "$(tr -d '\\r\\n' < VERSION)" = "$RELEASE_VERSION"
|
|
210
|
-
test "$(gh release view "$RELEASE_TAG" --json isDraft --jq .isDraft)" = "false"
|
|
211
|
-
rm -rf dist/release-candidate
|
|
212
|
-
mkdir -p dist/release-candidate
|
|
213
|
-
gh release download "$RELEASE_TAG" --dir dist/release-candidate
|
|
214
|
-
bun node_modules/@dbx-tools/projen/tasks/release-manifest.ts verify \
|
|
215
|
-
--directory dist/release-candidate \
|
|
216
|
-
--tag "$RELEASE_TAG" \
|
|
217
|
-
--sha "$RELEASE_SHA" \
|
|
218
|
-
--version "$RELEASE_VERSION"
|
|
219
194
|
echo "release_tag=$RELEASE_TAG" >> "$GITHUB_OUTPUT"
|
|
220
195
|
echo "expected_sha=$RELEASE_SHA" >> "$GITHUB_OUTPUT"
|
|
221
196
|
echo "release_version=$RELEASE_VERSION" >> "$GITHUB_OUTPUT"
|
|
@@ -223,6 +198,10 @@ function verifyContextJob(tagPrefix: string, releaseBranch: string): Job {
|
|
|
223
198
|
// ============================================================================
|
|
224
199
|
),
|
|
225
200
|
},
|
|
201
|
+
...(options.validationTasks ?? []).map((task) => ({
|
|
202
|
+
name: `Validate ${task}`,
|
|
203
|
+
run: `bun run ${task}`,
|
|
204
|
+
})),
|
|
226
205
|
],
|
|
227
206
|
};
|
|
228
207
|
}
|
|
@@ -238,26 +217,9 @@ function nodePublishJob(project: DBXToolsJavaScriptProject): Job {
|
|
|
238
217
|
steps: [
|
|
239
218
|
...nodeReleaseSetupSteps(project),
|
|
240
219
|
{
|
|
241
|
-
name: "
|
|
242
|
-
env: { GH_TOKEN: "${{ github.token }}", RELEASE_SHA, RELEASE_TAG, RELEASE_VERSION },
|
|
243
|
-
shell: "bash",
|
|
244
|
-
run: [
|
|
245
|
-
"rm -rf dist/release-download dist/npm-release",
|
|
246
|
-
"mkdir -p dist/release-download",
|
|
247
|
-
'gh release download "$RELEASE_TAG" --pattern release-manifest.json --pattern SHA256SUMS --pattern "*.tgz" --dir dist/release-download',
|
|
248
|
-
"bun node_modules/@dbx-tools/projen/tasks/release-manifest.ts verify \\",
|
|
249
|
-
" --directory dist/release-download \\",
|
|
250
|
-
' --tag "$RELEASE_TAG" \\',
|
|
251
|
-
' --sha "$RELEASE_SHA" \\',
|
|
252
|
-
' --version "$RELEASE_VERSION" \\',
|
|
253
|
-
" --kind npm \\",
|
|
254
|
-
" --output dist/npm-release",
|
|
255
|
-
].join("\n"),
|
|
256
|
-
},
|
|
257
|
-
{
|
|
258
|
-
name: "Publish approved npm archives",
|
|
220
|
+
name: "Publish npm workspace",
|
|
259
221
|
env: { RELEASE_VERSION, ...npmPublishEnvironment() },
|
|
260
|
-
run: 'bun node_modules/@dbx-tools/projen/tasks/publish
|
|
222
|
+
run: 'bun node_modules/@dbx-tools/projen/tasks/publish.ts "$RELEASE_VERSION" $DRY_RUN',
|
|
261
223
|
},
|
|
262
224
|
],
|
|
263
225
|
};
|
|
@@ -314,643 +276,20 @@ function addDocsJobs(
|
|
|
314
276
|
},
|
|
315
277
|
],
|
|
316
278
|
});
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
function independentReleasePleaseJob(project: DBXToolsJavaScriptProject, branch: string): Job {
|
|
320
|
-
return {
|
|
321
|
-
if: "${{ github.event_name == 'push' || inputs.automatic == true }}",
|
|
322
|
-
runsOn: ["ubuntu-latest"],
|
|
323
|
-
permissions: {
|
|
324
|
-
actions: JobPermission.WRITE,
|
|
325
|
-
contents: JobPermission.WRITE,
|
|
326
|
-
pullRequests: JobPermission.WRITE,
|
|
327
|
-
},
|
|
328
|
-
timeoutMinutes: 30,
|
|
329
|
-
env: { BUN_VERSION },
|
|
330
|
-
outputs: {
|
|
331
|
-
releases_created: { stepId: "release", outputName: "releases_created" },
|
|
332
|
-
prs_created: { stepId: "release", outputName: "prs_created" },
|
|
333
|
-
releases: { stepId: "release", outputName: "releases" },
|
|
334
|
-
prs: { stepId: "release", outputName: "prs" },
|
|
335
|
-
docs_changed: { stepId: "changes", outputName: "docs_changed" },
|
|
336
|
-
recovery_requested: { stepId: "recovery", outputName: "requested" },
|
|
337
|
-
recovery_component: { stepId: "recovery", outputName: "component" },
|
|
338
|
-
recovery_version: { stepId: "recovery", outputName: "version" },
|
|
339
|
-
},
|
|
340
|
-
steps: [
|
|
341
|
-
{
|
|
342
|
-
name: "Checkout source",
|
|
343
|
-
uses: "actions/checkout@v6",
|
|
344
|
-
with: { "fetch-depth": 2 },
|
|
345
|
-
},
|
|
346
|
-
...bunCacheRestoreSteps(project, {
|
|
347
|
-
ignorePaths: project.workflowCacheIgnorePaths,
|
|
348
|
-
}),
|
|
349
|
-
{ name: "Install dependencies", run: "bun install" },
|
|
350
|
-
{
|
|
351
|
-
name: "Refresh Release Please state",
|
|
352
|
-
id: "release",
|
|
353
|
-
env: { GITHUB_TOKEN: "${{ github.token }}" },
|
|
354
|
-
run: [
|
|
355
|
-
'OWNER="${GITHUB_REPOSITORY%%/*}"',
|
|
356
|
-
'REPO="${GITHUB_REPOSITORY#*/}"',
|
|
357
|
-
`bun node_modules/@dbx-tools/projen/tasks/release-please.ts --owner "$OWNER" --repo "$REPO" --target-branch ${JSON.stringify(branch)}`,
|
|
358
|
-
].join("\n"),
|
|
359
|
-
},
|
|
360
|
-
{
|
|
361
|
-
name: "Detect documentation changes",
|
|
362
|
-
id: "changes",
|
|
363
|
-
shell: "bash",
|
|
364
|
-
run: [
|
|
365
|
-
"if git diff --quiet HEAD^ HEAD -- README.md AGENTS.md ':(glob)**/README.md' docs ':(exclude)docs/releases/**' ':(exclude).release-notes/**'; then",
|
|
366
|
-
' echo "docs_changed=false" >> "$GITHUB_OUTPUT"',
|
|
367
|
-
"else",
|
|
368
|
-
' echo "docs_changed=true" >> "$GITHUB_OUTPUT"',
|
|
369
|
-
"fi",
|
|
370
|
-
].join("\n"),
|
|
371
|
-
},
|
|
372
|
-
{
|
|
373
|
-
name: "Detect committed recovery request",
|
|
374
|
-
id: "recovery",
|
|
375
|
-
shell: "bash",
|
|
376
|
-
run: [
|
|
377
|
-
"if [ -f .release-recovery.json ]; then",
|
|
378
|
-
' echo "requested=true" >> "$GITHUB_OUTPUT"',
|
|
379
|
-
' echo "component=$(jq -r .component .release-recovery.json)" >> "$GITHUB_OUTPUT"',
|
|
380
|
-
' echo "version=$(jq -r .version .release-recovery.json)" >> "$GITHUB_OUTPUT"',
|
|
381
|
-
"else",
|
|
382
|
-
' echo "requested=false" >> "$GITHUB_OUTPUT"',
|
|
383
|
-
"fi",
|
|
384
|
-
].join("\n"),
|
|
385
|
-
},
|
|
386
|
-
{
|
|
387
|
-
name: "Reconcile generated release PR files",
|
|
388
|
-
if: "${{ steps.release.outputs.prs_created == 'true' }}",
|
|
389
|
-
env: {
|
|
390
|
-
GH_TOKEN: "${{ github.token }}",
|
|
391
|
-
RELEASE_BRANCH: branch,
|
|
392
|
-
RELEASE_PRS: "${{ steps.release.outputs.prs }}",
|
|
393
|
-
},
|
|
394
|
-
shell: "bash",
|
|
395
|
-
// prettier-ignore
|
|
396
|
-
run: stringUtils.dedent(
|
|
397
|
-
// ============================================================================
|
|
398
|
-
/*bash*/`
|
|
399
|
-
BRANCH="$(jq -r '.[0].headBranchName' <<<"$RELEASE_PRS")"
|
|
400
|
-
test -n "$BRANCH" && test "$BRANCH" != "null"
|
|
401
|
-
git fetch origin "$BRANCH"
|
|
402
|
-
git switch --force-create "$BRANCH" "origin/$BRANCH"
|
|
403
|
-
bun install
|
|
404
|
-
bunx projen
|
|
405
|
-
bun node_modules/@dbx-tools/projen/tasks/version-check.ts
|
|
406
|
-
bun node_modules/@dbx-tools/projen/tasks/release-plan.ts --base-ref origin/${branch}
|
|
407
|
-
bun node_modules/@dbx-tools/projen/tasks/release-summary-units.ts --from-ref origin/${branch}
|
|
408
|
-
if ! git diff --quiet || test -n "$(git ls-files --others --exclude-standard)"; then
|
|
409
|
-
git config user.name "github-actions[bot]"
|
|
410
|
-
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
411
|
-
git add -A
|
|
412
|
-
git commit -m "chore: reconcile release metadata"
|
|
413
|
-
git push origin "HEAD:$BRANCH"
|
|
414
|
-
fi
|
|
415
|
-
MERGED="false"
|
|
416
|
-
for ATTEMPT in $(seq 1 15); do
|
|
417
|
-
STATE="$(gh pr view "$BRANCH" --json state,mergeable --jq '[.state,.mergeable] | join(" ")')"
|
|
418
|
-
if [[ "$STATE" == MERGED* ]]; then MERGED="true"; break; fi
|
|
419
|
-
if [ "$STATE" = "OPEN CONFLICTING" ]; then
|
|
420
|
-
echo "::error::release pull request has merge conflicts"
|
|
421
|
-
exit 1
|
|
422
|
-
fi
|
|
423
|
-
if [ "$STATE" = "OPEN MERGEABLE" ] && gh pr merge "$BRANCH" --merge; then
|
|
424
|
-
MERGED="true"
|
|
425
|
-
break
|
|
426
|
-
fi
|
|
427
|
-
echo "release pull request is not mergeable yet (attempt $ATTEMPT/15)"
|
|
428
|
-
sleep 2
|
|
429
|
-
done
|
|
430
|
-
if [ "$MERGED" != "true" ]; then
|
|
431
|
-
echo "::error::release pull request did not become mergeable"
|
|
432
|
-
exit 1
|
|
433
|
-
fi
|
|
434
|
-
gh workflow run release.yml --ref "$RELEASE_BRANCH" -f automatic=true
|
|
435
|
-
`
|
|
436
|
-
// ============================================================================
|
|
437
|
-
),
|
|
438
|
-
},
|
|
439
|
-
],
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
function configureReleaseRequestWorkflow(
|
|
444
|
-
project: DBXToolsJavaScriptProject,
|
|
445
|
-
baseBranch: string,
|
|
446
|
-
): void {
|
|
447
|
-
if (!project.github) return;
|
|
448
|
-
const workflow = new GithubWorkflow(project.github, "release-request", {
|
|
449
|
-
fileName: "release-request.yml",
|
|
450
|
-
limitConcurrency: true,
|
|
451
|
-
concurrencyOptions: {
|
|
452
|
-
group: "release-request-${{ github.ref_name }}",
|
|
453
|
-
cancelInProgress: false,
|
|
454
|
-
},
|
|
455
|
-
});
|
|
456
|
-
workflow.runName = "release request ${{ github.ref_name }}";
|
|
457
|
-
workflow.on({ push: {} });
|
|
458
|
-
workflow.file?.addOverride("permissions.contents", "read");
|
|
459
|
-
workflow.addJob("request", {
|
|
460
|
-
if: `\${{ github.ref_name != '${baseBranch}' }}`,
|
|
461
|
-
runsOn: ["ubuntu-latest"],
|
|
462
|
-
permissions: {
|
|
463
|
-
actions: JobPermission.WRITE,
|
|
464
|
-
contents: JobPermission.WRITE,
|
|
465
|
-
pullRequests: JobPermission.WRITE,
|
|
466
|
-
},
|
|
467
|
-
steps: [
|
|
468
|
-
{
|
|
469
|
-
name: "Checkout source branch",
|
|
470
|
-
uses: "actions/checkout@v6",
|
|
471
|
-
with: { "fetch-depth": 2 },
|
|
472
|
-
},
|
|
473
|
-
{
|
|
474
|
-
name: "Read release request",
|
|
475
|
-
id: "request",
|
|
476
|
-
shell: "bash",
|
|
477
|
-
// prettier-ignore
|
|
478
|
-
run: stringUtils.dedent(
|
|
479
|
-
// ============================================================================
|
|
480
|
-
/*bash*/`
|
|
481
|
-
MESSAGE="$(git log -1 --format=%B)"
|
|
482
|
-
if ! grep -q "^Release-Request: true$" <<<"$MESSAGE"; then
|
|
483
|
-
echo "requested=false" >> "$GITHUB_OUTPUT"
|
|
484
|
-
exit 0
|
|
485
|
-
fi
|
|
486
|
-
git fetch origin ${JSON.stringify(baseBranch)}
|
|
487
|
-
if git diff --quiet "origin/${baseBranch}...HEAD"; then
|
|
488
|
-
echo "requested=false" >> "$GITHUB_OUTPUT"
|
|
489
|
-
exit 0
|
|
490
|
-
fi
|
|
491
|
-
echo "requested=true" >> "$GITHUB_OUTPUT"
|
|
492
|
-
echo "branch=$GITHUB_REF_NAME" >> "$GITHUB_OUTPUT"
|
|
493
|
-
echo "notes_path=$(sed -n 's/^Release-Notes-Path: //p' <<<"$MESSAGE" | tail -1)" >> "$GITHUB_OUTPUT"
|
|
494
|
-
echo "title=$(git log -1 --skip=1 --format=%s)" >> "$GITHUB_OUTPUT"
|
|
495
|
-
`
|
|
496
|
-
// ============================================================================
|
|
497
|
-
),
|
|
498
|
-
},
|
|
499
|
-
{
|
|
500
|
-
name: "Create, merge, and release source pull request",
|
|
501
|
-
if: "${{ steps.request.outputs.requested == 'true' }}",
|
|
502
|
-
env: {
|
|
503
|
-
GH_TOKEN: "${{ github.token }}",
|
|
504
|
-
SOURCE_BRANCH: "${{ steps.request.outputs.branch }}",
|
|
505
|
-
BASE_BRANCH: baseBranch,
|
|
506
|
-
TITLE: "${{ steps.request.outputs.title }}",
|
|
507
|
-
NOTES_PATH: "${{ steps.request.outputs.notes_path }}",
|
|
508
|
-
},
|
|
509
|
-
shell: "bash",
|
|
510
|
-
// prettier-ignore
|
|
511
|
-
run: stringUtils.dedent(
|
|
512
|
-
// ============================================================================
|
|
513
|
-
/*bash*/`
|
|
514
|
-
test -f "$NOTES_PATH"
|
|
515
|
-
NOTES="$(cat "$NOTES_PATH")"
|
|
516
|
-
BODY="$(printf '## Release notes\\n\\n%s\\n' "$NOTES")"
|
|
517
|
-
PR="$(gh pr list --head "$SOURCE_BRANCH" --base "$BASE_BRANCH" --state open --json number --jq '.[0].number // empty')"
|
|
518
|
-
if [ -n "$PR" ]; then
|
|
519
|
-
gh pr edit "$PR" --title "$TITLE" --body "$BODY"
|
|
520
|
-
else
|
|
521
|
-
gh pr create --head "$SOURCE_BRANCH" --base "$BASE_BRANCH" --title "$TITLE" --body "$BODY"
|
|
522
|
-
PR="$(gh pr list --head "$SOURCE_BRANCH" --base "$BASE_BRANCH" --state open --json number --jq '.[0].number')"
|
|
523
|
-
fi
|
|
524
|
-
gh pr merge "$PR" --merge
|
|
525
|
-
gh workflow run release.yml --repo "$GITHUB_REPOSITORY" --ref "$BASE_BRANCH" -f automatic=true
|
|
526
|
-
`
|
|
527
|
-
// ============================================================================
|
|
528
|
-
),
|
|
529
|
-
},
|
|
530
|
-
],
|
|
531
|
-
});
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
function independentReleasePlanJob(project: DBXToolsJavaScriptProject): Job {
|
|
535
|
-
return {
|
|
536
|
-
if: "${{ always() && ((github.event_name == 'workflow_dispatch' && inputs.automatic != true) || needs.release-please.outputs.releases_created == 'true' || (needs.release-please.outputs.docs_changed == 'true' && needs.release-please.outputs.prs_created != 'true') || needs.release-please.outputs.recovery_requested == 'true') }}",
|
|
537
|
-
needs: ["release-please"],
|
|
538
|
-
runsOn: ["ubuntu-latest"],
|
|
539
|
-
permissions: { contents: JobPermission.READ },
|
|
540
|
-
timeoutMinutes: 15,
|
|
541
|
-
env: { BUN_VERSION },
|
|
542
|
-
outputs: {
|
|
543
|
-
release_sha: { stepId: "plan", outputName: "release_sha" },
|
|
544
|
-
rust: { stepId: "plan", outputName: "rust" },
|
|
545
|
-
python: { stepId: "plan", outputName: "python" },
|
|
546
|
-
node: { stepId: "plan", outputName: "node" },
|
|
547
|
-
github: { stepId: "plan", outputName: "github" },
|
|
548
|
-
docs: { stepId: "plan", outputName: "docs" },
|
|
549
|
-
units: { stepId: "plan", outputName: "units" },
|
|
550
|
-
python_packages: { stepId: "plan", outputName: "python_packages" },
|
|
551
|
-
rust_packages: { stepId: "plan", outputName: "rust_packages" },
|
|
552
|
-
artifacts: { stepId: "plan", outputName: "artifacts" },
|
|
553
|
-
rust_targets: { stepId: "plan", outputName: "rust_targets" },
|
|
554
|
-
},
|
|
555
|
-
steps: [
|
|
556
|
-
{
|
|
557
|
-
name: "Checkout release source",
|
|
558
|
-
uses: "actions/checkout@v6",
|
|
559
|
-
with: { "fetch-depth": 2 },
|
|
560
|
-
},
|
|
561
|
-
...bunCacheRestoreSteps(project, {
|
|
562
|
-
ignorePaths: project.workflowCacheIgnorePaths,
|
|
563
|
-
}),
|
|
564
|
-
{ name: "Install dependencies", run: "bun install" },
|
|
565
|
-
{
|
|
566
|
-
name: "Build affected release plan",
|
|
567
|
-
id: "plan",
|
|
568
|
-
shell: "bash",
|
|
569
|
-
env: {
|
|
570
|
-
COMPONENT:
|
|
571
|
-
"${{ inputs.component || needs.release-please.outputs.recovery_component || '' }}",
|
|
572
|
-
VERSION: "${{ inputs.version || needs.release-please.outputs.recovery_version || '' }}",
|
|
573
|
-
DOCS_CHANGED: "${{ needs.release-please.outputs.docs_changed || 'false' }}",
|
|
574
|
-
},
|
|
575
|
-
run: [
|
|
576
|
-
"ARGS=()",
|
|
577
|
-
'if [ -n "$COMPONENT" ]; then ARGS+=(--component "$COMPONENT" --version "$VERSION"); fi',
|
|
578
|
-
'if [ "$DOCS_CHANGED" = "true" ]; then ARGS+=(--docs); fi',
|
|
579
|
-
'bun node_modules/@dbx-tools/projen/tasks/release-plan.ts "${ARGS[@]}"',
|
|
580
|
-
].join("\n"),
|
|
581
|
-
},
|
|
582
|
-
{
|
|
583
|
-
name: "Upload release plan",
|
|
584
|
-
uses: "actions/upload-artifact@v7",
|
|
585
|
-
with: {
|
|
586
|
-
name: "release-plan",
|
|
587
|
-
path: "dist/release-plan.json",
|
|
588
|
-
"if-no-files-found": "error",
|
|
589
|
-
},
|
|
590
|
-
},
|
|
591
|
-
],
|
|
592
|
-
};
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
/** Checkout and install the immutable source selected by an independent release plan. */
|
|
596
|
-
export function independentReleaseSetupSteps(
|
|
597
|
-
project: DBXToolsJavaScriptProject,
|
|
598
|
-
): readonly JobStep[] {
|
|
599
|
-
return [
|
|
600
|
-
{
|
|
601
|
-
name: "Checkout release source",
|
|
602
|
-
uses: "actions/checkout@v6",
|
|
603
|
-
with: { ref: "${{ needs.release-plan.outputs.release_sha }}", "fetch-depth": 1 },
|
|
604
|
-
},
|
|
605
|
-
...bunCacheRestoreSteps(project, {
|
|
606
|
-
ignorePaths: project.workflowCacheIgnorePaths,
|
|
607
|
-
}),
|
|
608
|
-
{ name: "Install dependencies", run: "bun install" },
|
|
609
|
-
{
|
|
610
|
-
name: "Download release plan",
|
|
611
|
-
uses: "actions/download-artifact@v8",
|
|
612
|
-
with: { name: "release-plan", path: "dist" },
|
|
613
|
-
},
|
|
614
|
-
];
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
function independentNodePublishJob(project: DBXToolsJavaScriptProject): Job {
|
|
618
|
-
return {
|
|
619
|
-
if: "${{ needs.release-plan.outputs.node == 'true' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'node') }}",
|
|
620
|
-
needs: ["release-plan"],
|
|
621
|
-
runsOn: ["ubuntu-latest"],
|
|
622
|
-
permissions: { contents: JobPermission.READ, idToken: JobPermission.WRITE },
|
|
623
|
-
timeoutMinutes: 60,
|
|
624
|
-
env: { BUN_VERSION, CI: "true" },
|
|
625
|
-
steps: [
|
|
626
|
-
...independentReleaseSetupSteps(project),
|
|
627
|
-
{
|
|
628
|
-
name: "Publish affected npm packages",
|
|
629
|
-
env: npmPublishEnvironment(),
|
|
630
|
-
run: "bun node_modules/@dbx-tools/projen/tasks/publish.ts --plan dist/release-plan.json",
|
|
631
|
-
},
|
|
632
|
-
],
|
|
633
|
-
};
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
function independentReleaseNotesJob(project: DBXToolsJavaScriptProject): Job {
|
|
637
|
-
return {
|
|
638
|
-
if: "${{ needs.release-plan.outputs.units != '[]' }}",
|
|
639
|
-
needs: ["release-plan"],
|
|
640
|
-
runsOn: ["ubuntu-latest"],
|
|
641
|
-
permissions: { contents: JobPermission.WRITE },
|
|
642
|
-
env: { BUN_VERSION },
|
|
643
|
-
steps: [
|
|
644
|
-
...independentReleaseSetupSteps(project),
|
|
645
|
-
{
|
|
646
|
-
name: "Publish component release notes",
|
|
647
|
-
env: { GH_TOKEN: "${{ github.token }}" },
|
|
648
|
-
shell: "bash",
|
|
649
|
-
run: [
|
|
650
|
-
"jq -c '.units[]' dist/release-plan.json | while read -r UNIT; do",
|
|
651
|
-
' COMPONENT="$(jq -r .component <<<"$UNIT")"',
|
|
652
|
-
' VERSION="$(jq -r .newVersion <<<"$UNIT")"',
|
|
653
|
-
' gh release edit "$COMPONENT-v$VERSION" --notes-file ".release-notes/final/$COMPONENT-v$VERSION.md"',
|
|
654
|
-
"done",
|
|
655
|
-
].join("\n"),
|
|
656
|
-
},
|
|
657
|
-
{
|
|
658
|
-
name: "Remove consumed release notes",
|
|
659
|
-
shell: "bash",
|
|
660
|
-
// prettier-ignore
|
|
661
|
-
run: stringUtils.dedent(
|
|
662
|
-
// ============================================================================
|
|
663
|
-
/*bash*/`
|
|
664
|
-
git fetch origin ${JSON.stringify(projectReleaseBranch(project))}
|
|
665
|
-
if [ "$(git rev-parse origin/${projectReleaseBranch(project)})" != "$(git rev-parse HEAD)" ]; then
|
|
666
|
-
echo "::warning::main advanced before release-note cleanup; leaving temporary notes for the next release"
|
|
667
|
-
exit 0
|
|
668
|
-
fi
|
|
669
|
-
git rm -r --ignore-unmatch .release-notes
|
|
670
|
-
if ! git diff --cached --quiet; then
|
|
671
|
-
git config user.name "github-actions[bot]"
|
|
672
|
-
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
673
|
-
git commit -m "chore: remove consumed release notes"
|
|
674
|
-
git push origin "HEAD:${projectReleaseBranch(project)}"
|
|
675
|
-
fi
|
|
676
|
-
`
|
|
677
|
-
// ============================================================================
|
|
678
|
-
),
|
|
679
|
-
},
|
|
680
|
-
],
|
|
681
|
-
};
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
function addIndependentDocsJobs(
|
|
685
|
-
workflow: GithubWorkflow,
|
|
686
|
-
project: DBXToolsJavaScriptProject,
|
|
687
|
-
options: ReleaseDocsOptions,
|
|
688
|
-
): void {
|
|
689
|
-
workflow.addJob("build-docs", {
|
|
690
|
-
if: "${{ always() && needs['release-plan'].outputs.docs == 'true' && needs['publication-complete'].result == 'success' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'docs') }}",
|
|
691
|
-
needs: ["release-plan", "publication-complete"],
|
|
692
|
-
runsOn: ["ubuntu-latest"],
|
|
693
|
-
permissions: {
|
|
694
|
-
contents: JobPermission.READ,
|
|
695
|
-
pages: JobPermission.WRITE,
|
|
696
|
-
idToken: JobPermission.WRITE,
|
|
697
|
-
},
|
|
698
|
-
timeoutMinutes: 30,
|
|
699
|
-
env: {
|
|
700
|
-
BUN_VERSION,
|
|
701
|
-
DOCS_SITE_URL: options.siteUrl,
|
|
702
|
-
DOCS_BASE: options.base ?? "/",
|
|
703
|
-
},
|
|
704
|
-
steps: [
|
|
705
|
-
...independentReleaseSetupSteps(project),
|
|
706
|
-
...options.prepareSteps,
|
|
707
|
-
bunCacheSaveStep(),
|
|
708
|
-
...options.buildSteps,
|
|
709
|
-
{
|
|
710
|
-
name: "Upload Pages artifact",
|
|
711
|
-
uses: "actions/upload-pages-artifact@v4",
|
|
712
|
-
with: { path: options.artifactPath },
|
|
713
|
-
},
|
|
714
|
-
],
|
|
715
|
-
});
|
|
716
|
-
workflow.addJob("deploy-docs", {
|
|
717
|
-
if: "${{ always() && needs['build-docs'].result == 'success' }}",
|
|
718
|
-
needs: ["build-docs"],
|
|
719
|
-
environment: {
|
|
720
|
-
name: "github-pages",
|
|
721
|
-
url: "${{ steps.deployment.outputs.page_url }}",
|
|
722
|
-
},
|
|
723
|
-
runsOn: ["ubuntu-latest"],
|
|
724
|
-
permissions: { pages: JobPermission.WRITE, idToken: JobPermission.WRITE },
|
|
725
|
-
timeoutMinutes: 15,
|
|
726
|
-
steps: [
|
|
727
|
-
{
|
|
728
|
-
name: "Deploy to GitHub Pages",
|
|
729
|
-
id: "deployment",
|
|
730
|
-
uses: "actions/deploy-pages@v4",
|
|
731
|
-
},
|
|
732
|
-
],
|
|
733
|
-
});
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
function addIndependentBranchSyncJob(
|
|
737
|
-
workflow: GithubWorkflow,
|
|
738
|
-
project: DBXToolsJavaScriptProject,
|
|
739
|
-
branch: string,
|
|
740
|
-
): void {
|
|
741
|
-
const releaseBranch = projectReleaseBranch(project);
|
|
742
|
-
workflow.addJob("sync-release-branch", {
|
|
743
|
-
if: "${{ always() && (github.event_name == 'push' || inputs.automatic == true) && needs['publication-complete'].result == 'success' }}",
|
|
744
|
-
needs: ["publication-complete"],
|
|
745
|
-
runsOn: ["ubuntu-latest"],
|
|
746
|
-
permissions: { contents: JobPermission.WRITE },
|
|
747
|
-
env: { BUN_VERSION },
|
|
748
|
-
steps: [
|
|
749
|
-
{
|
|
750
|
-
name: "Checkout released main",
|
|
751
|
-
uses: "actions/checkout@v6",
|
|
752
|
-
with: { "fetch-depth": 0 },
|
|
753
|
-
},
|
|
754
|
-
...bunCacheRestoreSteps(project, {
|
|
755
|
-
ignorePaths: project.workflowCacheIgnorePaths,
|
|
756
|
-
}),
|
|
757
|
-
{ name: "Install dependencies", run: "bun install" },
|
|
758
|
-
{
|
|
759
|
-
name: `Safely sync ${branch}`,
|
|
760
|
-
shell: "bash",
|
|
761
|
-
env: { SOURCE_BRANCH: branch, RELEASE_BRANCH: releaseBranch },
|
|
762
|
-
// prettier-ignore
|
|
763
|
-
run: stringUtils.dedent(
|
|
764
|
-
// ============================================================================
|
|
765
|
-
/*bash*/`
|
|
766
|
-
if ! git ls-remote --exit-code --heads origin "refs/heads/$SOURCE_BRANCH" >/dev/null 2>&1; then
|
|
767
|
-
echo "source branch $SOURCE_BRANCH no longer exists; nothing to sync"
|
|
768
|
-
exit 0
|
|
769
|
-
fi
|
|
770
|
-
git fetch origin "refs/heads/$SOURCE_BRANCH:refs/remotes/origin/$SOURCE_BRANCH"
|
|
771
|
-
git fetch origin "refs/heads/$RELEASE_BRANCH:refs/remotes/origin/$RELEASE_BRANCH"
|
|
772
|
-
if git merge-base --is-ancestor "origin/$SOURCE_BRANCH" "origin/$RELEASE_BRANCH"; then
|
|
773
|
-
git push origin "refs/remotes/origin/$RELEASE_BRANCH:refs/heads/$SOURCE_BRANCH"
|
|
774
|
-
elif git merge-base --is-ancestor "origin/$RELEASE_BRANCH" "origin/$SOURCE_BRANCH"; then
|
|
775
|
-
echo "source branch $SOURCE_BRANCH already contains released main"
|
|
776
|
-
else
|
|
777
|
-
git config user.name "github-actions[bot]"
|
|
778
|
-
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
779
|
-
git switch --force-create "$SOURCE_BRANCH" "origin/$SOURCE_BRANCH"
|
|
780
|
-
if git merge --no-ff --no-edit "origin/$RELEASE_BRANCH"; then
|
|
781
|
-
git push origin "HEAD:refs/heads/$SOURCE_BRANCH"
|
|
782
|
-
else
|
|
783
|
-
SAFE_GENERATED="true"
|
|
784
|
-
while IFS= read -r FILE; do
|
|
785
|
-
case "$FILE" in
|
|
786
|
-
.projen/release-units.json|.release-please-manifest.json|.release-units/*/source.json|.release-units/*/version.txt|.release-units/*/CHANGELOG.md|Cargo.lock|*/Cargo.toml|*/package.json|*/pyproject.toml|*/index.ts) ;;
|
|
787
|
-
*) SAFE_GENERATED="false" ;;
|
|
788
|
-
esac
|
|
789
|
-
done < <(git diff --name-only --diff-filter=U)
|
|
790
|
-
if [ "$SAFE_GENERATED" = "true" ] && bunx projen; then
|
|
791
|
-
git add -A
|
|
792
|
-
if [ -z "$(git diff --name-only --diff-filter=U)" ]; then
|
|
793
|
-
git commit --no-edit
|
|
794
|
-
git push origin "HEAD:refs/heads/$SOURCE_BRANCH"
|
|
795
|
-
exit 0
|
|
796
|
-
fi
|
|
797
|
-
fi
|
|
798
|
-
git merge --abort
|
|
799
|
-
echo "::warning::source branch $SOURCE_BRANCH has handwritten or unresolved conflicts with released main; leaving it untouched"
|
|
800
|
-
fi
|
|
801
|
-
fi
|
|
802
|
-
`
|
|
803
|
-
// ============================================================================
|
|
804
|
-
),
|
|
805
|
-
},
|
|
806
|
-
],
|
|
807
|
-
});
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
class IndependentReleaseFinalizer extends Component {
|
|
811
|
-
private finalized = false;
|
|
812
|
-
|
|
813
|
-
constructor(
|
|
814
|
-
project: DBXToolsJavaScriptProject,
|
|
815
|
-
private readonly workflow: GithubWorkflow,
|
|
816
|
-
private readonly docs: ReleaseDocsOptions | undefined,
|
|
817
|
-
private readonly syncBranch: string | undefined,
|
|
818
|
-
) {
|
|
819
|
-
super(project);
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
public override preSynthesize(): void {
|
|
823
|
-
if (this.finalized) return;
|
|
824
|
-
this.finalized = true;
|
|
825
|
-
const jobs = [...(independentPublicationJobs.get(this.workflow) ?? [])].sort();
|
|
826
|
-
const successful = jobs
|
|
827
|
-
.map((job) => `needs.${job}.result != 'failure' && needs.${job}.result != 'cancelled'`)
|
|
828
|
-
.join(" && ");
|
|
829
|
-
this.workflow.addJob("publication-complete", {
|
|
830
|
-
if: `\${{ always()${successful ? ` && ${successful}` : ""} }}`,
|
|
831
|
-
needs: jobs,
|
|
832
|
-
runsOn: ["ubuntu-latest"],
|
|
833
|
-
permissions: { contents: JobPermission.READ },
|
|
834
|
-
steps: [{ name: "Confirm publication stages", run: "true" }],
|
|
835
|
-
});
|
|
836
|
-
if (this.docs) {
|
|
837
|
-
addIndependentDocsJobs(this.workflow, this.project as DBXToolsJavaScriptProject, this.docs);
|
|
838
|
-
}
|
|
839
|
-
if (this.syncBranch) {
|
|
840
|
-
addIndependentBranchSyncJob(
|
|
841
|
-
this.workflow,
|
|
842
|
-
this.project as DBXToolsJavaScriptProject,
|
|
843
|
-
this.syncBranch,
|
|
844
|
-
);
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
function configureIndependentRelease(
|
|
850
|
-
project: DBXToolsJavaScriptProject,
|
|
851
|
-
options: DBXToolsReleaseOptions,
|
|
852
|
-
): void {
|
|
853
|
-
const branch = projectReleaseBranch(project);
|
|
854
|
-
if (options.nodeRelease !== false) nodeReleaseProjects.add(project);
|
|
855
|
-
applyTasks(project, {
|
|
856
|
-
"version:check": {
|
|
857
|
-
exec: taskScript(project, "version-check.ts"),
|
|
858
|
-
description: "Verify every package against its release unit",
|
|
859
|
-
},
|
|
860
|
-
"release:plan": {
|
|
861
|
-
exec: taskScript(project, "release-plan.ts"),
|
|
862
|
-
receiveArgs: true,
|
|
863
|
-
description: "Build the affected release plan",
|
|
864
|
-
},
|
|
865
|
-
release: {
|
|
866
|
-
exec: taskScript(project, "release-request.ts", `--base ${JSON.stringify(branch)}`),
|
|
867
|
-
receiveArgs: true,
|
|
868
|
-
description: "Commit, annotate, and push a source release request",
|
|
869
|
-
},
|
|
870
|
-
"release:refresh": {
|
|
871
|
-
exec: taskScript(project, "release-please.ts", `--target-branch ${JSON.stringify(branch)}`),
|
|
872
|
-
receiveArgs: true,
|
|
873
|
-
description: "Refresh Release Please PRs, tags, and GitHub Releases",
|
|
874
|
-
},
|
|
875
|
-
});
|
|
876
|
-
if (!project.github) return;
|
|
877
|
-
const workflow = new GithubWorkflow(project.github, "release", {
|
|
878
|
-
fileName: "release.yml",
|
|
879
|
-
limitConcurrency: true,
|
|
880
|
-
concurrencyOptions: { group: "release", cancelInProgress: false },
|
|
881
|
-
});
|
|
882
|
-
workflow.runName =
|
|
883
|
-
"release units " +
|
|
884
|
-
"${{ github.event_name == 'push' && github.sha || inputs.automatic && 'automatic' || inputs.component }}";
|
|
885
|
-
workflow.on({
|
|
886
|
-
push: { branches: [branch] },
|
|
887
|
-
workflowDispatch: {
|
|
888
|
-
inputs: {
|
|
889
|
-
automatic: {
|
|
890
|
-
description: "Continue an automatically merged Release Please pull request",
|
|
891
|
-
type: "boolean",
|
|
892
|
-
default: "false",
|
|
893
|
-
required: false,
|
|
894
|
-
},
|
|
895
|
-
component: {
|
|
896
|
-
description: "Component to recover",
|
|
897
|
-
type: "string",
|
|
898
|
-
default: "",
|
|
899
|
-
required: false,
|
|
900
|
-
},
|
|
901
|
-
version: {
|
|
902
|
-
description: "Component version to recover",
|
|
903
|
-
type: "string",
|
|
904
|
-
default: "",
|
|
905
|
-
required: false,
|
|
906
|
-
},
|
|
907
|
-
stage: {
|
|
908
|
-
description: "Publication target to recover",
|
|
909
|
-
type: "choice",
|
|
910
|
-
options: ["all", "node", "python", "rust", "github", "docs"],
|
|
911
|
-
default: "all",
|
|
912
|
-
required: true,
|
|
913
|
-
},
|
|
914
|
-
},
|
|
915
|
-
},
|
|
916
|
-
});
|
|
917
|
-
workflow.file?.addOverride("on.workflow_dispatch.inputs.automatic.default", false);
|
|
918
|
-
workflow.file?.addOverride("permissions.contents", "read");
|
|
919
|
-
workflow.addJob("release-please", independentReleasePleaseJob(project, branch));
|
|
920
|
-
workflow.addJob("release-plan", independentReleasePlanJob(project));
|
|
921
|
-
workflow.addJob("publish-release-notes", independentReleaseNotesJob(project));
|
|
922
|
-
registerIndependentPublicationJob(workflow, "publish-release-notes");
|
|
923
|
-
if (options.nodeRelease !== false) {
|
|
924
|
-
workflow.addJob("publish-node", independentNodePublishJob(project));
|
|
925
|
-
registerIndependentPublicationJob(workflow, "publish-node");
|
|
926
|
-
}
|
|
927
|
-
new IndependentReleaseFinalizer(project, workflow, options.docs, options.syncBranch || undefined);
|
|
928
|
-
configureReleaseRequestWorkflow(project, branch);
|
|
929
|
-
releaseWorkflows.set(project, workflow);
|
|
279
|
+
refreshDocsRegistryDependencies(workflow);
|
|
930
280
|
}
|
|
931
281
|
|
|
932
282
|
/** Owns the single release workflow and local release preparation tasks. */
|
|
933
283
|
export class DBXToolsRelease extends Component {
|
|
934
284
|
constructor(project: DBXToolsJavaScriptProject, options: DBXToolsReleaseOptions = {}) {
|
|
935
285
|
super(project);
|
|
936
|
-
if (project.releaseCatalog.mode === "independent") {
|
|
937
|
-
configureIndependentRelease(project, options);
|
|
938
|
-
return;
|
|
939
|
-
}
|
|
940
286
|
const tagPrefix = options.tagPrefix ?? "v";
|
|
941
287
|
const releaseBranch = projectReleaseBranch(project);
|
|
942
|
-
const summary = options.summary ?? true;
|
|
943
|
-
const summaryArgs =
|
|
944
|
-
summary === false || (typeof summary === "object" && summary.providers?.length === 0)
|
|
945
|
-
? ["--no-release-summary"]
|
|
946
|
-
: typeof summary === "object" && summary.providers
|
|
947
|
-
? [`--release-summary-providers ${summary.providers.join(",")}`]
|
|
948
|
-
: [];
|
|
949
288
|
releaseTagPrefixes.set(project, tagPrefix);
|
|
950
289
|
if (options.nodeRelease !== false) nodeReleaseProjects.add(project);
|
|
951
290
|
applyTasks(project, {
|
|
952
291
|
bump: {
|
|
953
|
-
exec: taskScript(project, "bump.ts"
|
|
292
|
+
exec: taskScript(project, "bump.ts"),
|
|
954
293
|
receiveArgs: true,
|
|
955
294
|
description: "Increment VERSION and synchronize generated workspace versions",
|
|
956
295
|
},
|
|
@@ -963,27 +302,11 @@ export class DBXToolsRelease extends Component {
|
|
|
963
302
|
release: {
|
|
964
303
|
exec: taskScript(
|
|
965
304
|
project,
|
|
966
|
-
"release
|
|
967
|
-
|
|
968
|
-
`--prefix ${tagPrefix}`,
|
|
969
|
-
`--base ${releaseBranch}`,
|
|
970
|
-
...(options.pythonRoot
|
|
971
|
-
? [`--python-root ${JSON.stringify(options.pythonRoot)}`]
|
|
972
|
-
: []),
|
|
973
|
-
...(options.validationTasks ?? []).map(
|
|
974
|
-
(task) => `--validate-task ${JSON.stringify(task)}`,
|
|
975
|
-
),
|
|
976
|
-
...summaryArgs,
|
|
977
|
-
].join(" "),
|
|
305
|
+
"release.ts",
|
|
306
|
+
`--prefix ${tagPrefix} --branch ${releaseBranch}`,
|
|
978
307
|
),
|
|
979
308
|
receiveArgs: true,
|
|
980
|
-
description:
|
|
981
|
-
"Prepare, validate, locally publish, and upload a draft release candidate",
|
|
982
|
-
},
|
|
983
|
-
"release:assets": {
|
|
984
|
-
exec: taskScript(project, "release-candidate.ts"),
|
|
985
|
-
receiveArgs: true,
|
|
986
|
-
description: "Rebuild and upload a complete draft release candidate",
|
|
309
|
+
description: "Run bump, commit it, and push an annotated release tag",
|
|
987
310
|
},
|
|
988
311
|
}
|
|
989
312
|
: {}),
|
|
@@ -994,49 +317,22 @@ export class DBXToolsRelease extends Component {
|
|
|
994
317
|
fileName: "release.yml",
|
|
995
318
|
limitConcurrency: true,
|
|
996
319
|
concurrencyOptions: {
|
|
997
|
-
group:
|
|
998
|
-
"release-${{ github.event_name == 'release' && github.event.release.tag_name || inputs.release_tag }}",
|
|
320
|
+
group: "release-${{ github.ref_name }}",
|
|
999
321
|
cancelInProgress: false,
|
|
1000
322
|
},
|
|
1001
323
|
});
|
|
1002
|
-
|
|
1003
|
-
workflow.runName =
|
|
1004
|
-
`release ${version} ` +
|
|
1005
|
-
"${{ github.event_name == 'release' && github.event.release.tag_name || inputs.release_tag }}";
|
|
324
|
+
releaseWorkflows.set(project, workflow);
|
|
325
|
+
workflow.runName = "release ${{ github.ref_name }}";
|
|
1006
326
|
workflow.on({
|
|
1007
|
-
|
|
1008
|
-
workflowDispatch: {
|
|
1009
|
-
inputs: {
|
|
1010
|
-
release_tag: {
|
|
1011
|
-
description: "Annotated release tag to validate",
|
|
1012
|
-
type: "string",
|
|
1013
|
-
required: true,
|
|
1014
|
-
},
|
|
1015
|
-
stage: {
|
|
1016
|
-
description: "Published release stage to validate or recover",
|
|
1017
|
-
type: "choice",
|
|
1018
|
-
options: ["all", "node", "python", "cargo", "docs"],
|
|
1019
|
-
default: "all",
|
|
1020
|
-
required: true,
|
|
1021
|
-
},
|
|
1022
|
-
dry_run: {
|
|
1023
|
-
description: "Build and validate without publishing",
|
|
1024
|
-
type: "boolean",
|
|
1025
|
-
default: "true",
|
|
1026
|
-
required: true,
|
|
1027
|
-
},
|
|
1028
|
-
},
|
|
1029
|
-
},
|
|
327
|
+
push: { tags: [`${tagPrefix}*`] },
|
|
1030
328
|
});
|
|
1031
329
|
workflow.file?.addOverride("permissions.contents", "read");
|
|
1032
|
-
workflow.
|
|
1033
|
-
workflow.addJob("verify-context", verifyContextJob(tagPrefix, releaseBranch));
|
|
330
|
+
workflow.addJob("verify-context", verifyContextJob(project, tagPrefix, releaseBranch, options));
|
|
1034
331
|
if (options.nodeRelease !== false) {
|
|
1035
332
|
workflow.addJob("publish-node", nodePublishJob(project));
|
|
1036
333
|
}
|
|
1037
334
|
if (options.docs) {
|
|
1038
335
|
addDocsJobs(workflow, project, options.docs);
|
|
1039
336
|
}
|
|
1040
|
-
releaseWorkflows.set(project, workflow);
|
|
1041
337
|
}
|
|
1042
338
|
}
|