@dbx-tools/projen 0.9.12 → 0.9.17
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 +20 -15
- package/index.ts +4 -3
- package/package.json +4 -4
- package/src/_rust-project.ts +37 -13
- package/src/_rust-release-workflow.ts +267 -164
- package/src/_rust-release.ts +38 -10
- package/src/openapi.ts +191 -79
- package/src/project-py.ts +4 -4
- package/src/project-rs.ts +57 -6
- package/src/release-plan.ts +57 -16
- package/src/release.ts +2 -1
- package/src/watch.ts +19 -2
- package/src/workspace-lock.ts +32 -6
- package/tasks/barrels.ts +47 -30
- package/tasks/openapi.ts +15 -11
- package/tasks/release-plan.ts +2 -1
- package/tasks/release-pr.ts +1 -1
- package/tasks/rust-release.d.ts +9 -5
- package/tasks/rust-release.mjs +20 -11
- package/tasks/rust.ts +62 -33
- package/tasks/uniffi-release.mjs +5 -1
- package/tasks/uniffi.ts +2 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/** Rust release planning and generated GitHub workflow jobs. */
|
|
2
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
|
-
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { stringUtils } from "@dbx-tools/shared-core";
|
|
7
7
|
import { TextFile, javascript } from "projen";
|
|
8
8
|
import { JobPermission, type Job, type JobStep } from "projen/lib/github/workflows-model";
|
|
9
|
-
import type
|
|
9
|
+
import { UNIFFI_BINDGEN_FEATURE, type RustProject } from "./_rust-project.ts";
|
|
10
10
|
import { BUN_VERSION } from "./bun-workflow.ts";
|
|
11
11
|
import type { DBXToolsJavaScriptProject } from "./project-js.ts";
|
|
12
12
|
import type {
|
|
@@ -131,25 +131,88 @@ interface RustReleaseBinaryPlan {
|
|
|
131
131
|
readonly requiredFeatures: readonly string[];
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
|
|
134
|
+
interface RustReleaseTargetPlan {
|
|
135
|
+
readonly runner: string;
|
|
136
|
+
readonly cargo: string;
|
|
137
|
+
readonly node: string;
|
|
138
|
+
readonly python: string;
|
|
139
|
+
readonly os: RustReleaseOs;
|
|
140
|
+
readonly cpu: string;
|
|
141
|
+
readonly libc: string;
|
|
142
|
+
readonly packages: readonly string[];
|
|
143
|
+
readonly sources: readonly string[];
|
|
144
|
+
readonly binaries: readonly string[];
|
|
145
|
+
readonly features: readonly string[];
|
|
146
|
+
readonly fingerprintConfig: string;
|
|
147
|
+
}
|
|
135
148
|
|
|
136
149
|
export interface RustReleasePlan {
|
|
137
150
|
readonly releaseRustVersion: string;
|
|
138
151
|
readonly releaseTask: string;
|
|
139
152
|
readonly releaseHelper: string;
|
|
140
|
-
readonly rustRoot: string;
|
|
141
153
|
readonly bindings: readonly RustReleaseBinding[];
|
|
142
154
|
readonly nodeBindings: readonly RustReleaseBinding[];
|
|
143
155
|
readonly releaseBinaries: readonly RustReleaseBinaryPlan[];
|
|
144
156
|
readonly publicCrates: readonly string[];
|
|
145
|
-
readonly
|
|
146
|
-
readonly
|
|
157
|
+
readonly uniffiTargets: readonly RustReleaseTargetPlan[];
|
|
158
|
+
readonly binaryTargets: readonly RustReleaseTargetPlan[];
|
|
147
159
|
readonly hasPythonBindings: boolean;
|
|
148
160
|
readonly usesCargoLock: boolean;
|
|
149
161
|
readonly usePreinstalledWindowsRust: boolean;
|
|
150
162
|
readonly hasTargetOutputs: boolean;
|
|
151
163
|
}
|
|
152
164
|
|
|
165
|
+
function packageClosure(
|
|
166
|
+
roots: readonly RustProject[],
|
|
167
|
+
packageDependencies: RustPackageDependencyResolver,
|
|
168
|
+
): RustProject[] {
|
|
169
|
+
const found = new Set<RustProject>();
|
|
170
|
+
const visit = (pkg: RustProject): void => {
|
|
171
|
+
if (found.has(pkg)) return;
|
|
172
|
+
found.add(pkg);
|
|
173
|
+
for (const dependency of packageDependencies(pkg)) visit(dependency);
|
|
174
|
+
};
|
|
175
|
+
for (const pkg of roots) visit(pkg);
|
|
176
|
+
return [...found].sort((left, right) => left.crateName.localeCompare(right.crateName));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function packageSources(
|
|
180
|
+
project: javascript.NodeProject,
|
|
181
|
+
packages: readonly RustProject[],
|
|
182
|
+
): string[] {
|
|
183
|
+
return packages.map((pkg) => relative(project.outdir, pkg.outdir).replaceAll("\\", "/")).sort();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function releaseTargetPlan(
|
|
187
|
+
target: UniFFIReleaseTarget,
|
|
188
|
+
packages: readonly RustProject[],
|
|
189
|
+
sources: readonly string[],
|
|
190
|
+
binaries: readonly string[],
|
|
191
|
+
features: readonly string[],
|
|
192
|
+
): RustReleaseTargetPlan {
|
|
193
|
+
const packageNames = packages.map((pkg) => pkg.crateName).sort();
|
|
194
|
+
const sortedBinaries = [...new Set(binaries)].sort();
|
|
195
|
+
const sortedFeatures = [...new Set(features)].sort();
|
|
196
|
+
return {
|
|
197
|
+
runner: target.runner,
|
|
198
|
+
cargo: target.cargo,
|
|
199
|
+
node: target.node,
|
|
200
|
+
python: target.python,
|
|
201
|
+
os: target.os,
|
|
202
|
+
cpu: target.cpu,
|
|
203
|
+
libc: target.libc ?? "",
|
|
204
|
+
packages: packageNames,
|
|
205
|
+
sources,
|
|
206
|
+
binaries: sortedBinaries,
|
|
207
|
+
features: sortedFeatures,
|
|
208
|
+
fingerprintConfig: JSON.stringify({
|
|
209
|
+
packages: packageNames,
|
|
210
|
+
binaries: sortedBinaries,
|
|
211
|
+
features: sortedFeatures,
|
|
212
|
+
}),
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
153
216
|
export function planRustRelease(
|
|
154
217
|
project: javascript.NodeProject,
|
|
155
218
|
options: DBXToolsRustWorkspaceOptions,
|
|
@@ -195,39 +258,58 @@ export function planRustRelease(
|
|
|
195
258
|
dependencies: packageDependencies(pkg).map((dependency) => dependency.crateName),
|
|
196
259
|
})),
|
|
197
260
|
).map((pkg) => pkg.crate);
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
261
|
+
const bindingPackages = packages.filter((pkg) =>
|
|
262
|
+
bindings.some((binding) => binding.crate === pkg.crateName),
|
|
263
|
+
);
|
|
264
|
+
const bindingClosure = packageClosure(bindingPackages, packageDependencies);
|
|
265
|
+
const bindingSources = packageSources(project, bindingClosure);
|
|
266
|
+
const uniffiTargets = bindings.length
|
|
267
|
+
? targets.map((target) =>
|
|
268
|
+
releaseTargetPlan(
|
|
269
|
+
target,
|
|
270
|
+
bindingPackages,
|
|
271
|
+
bindingSources,
|
|
272
|
+
[],
|
|
273
|
+
bindingPackages.map((pkg) => `${pkg.crateName}/${UNIFFI_BINDGEN_FEATURE}`),
|
|
274
|
+
),
|
|
275
|
+
)
|
|
276
|
+
: [];
|
|
277
|
+
const binaryTargets = targets.flatMap((target) => {
|
|
278
|
+
const selectedBinaries = releaseBinaries.filter(
|
|
279
|
+
(binary) => !binary.excludedOs.includes(target.os),
|
|
280
|
+
);
|
|
281
|
+
const binaryPackages = packages.filter((pkg) =>
|
|
282
|
+
selectedBinaries.some((binary) => binary.crate === pkg.crateName),
|
|
283
|
+
);
|
|
284
|
+
if (!binaryPackages.length) return [];
|
|
285
|
+
const binaryClosure = packageClosure(binaryPackages, packageDependencies);
|
|
286
|
+
const features = selectedBinaries.flatMap((binary) =>
|
|
287
|
+
binary.requiredFeatures.map((feature) => `${binary.crate}/${feature}`),
|
|
288
|
+
);
|
|
289
|
+
return [
|
|
290
|
+
releaseTargetPlan(
|
|
291
|
+
target,
|
|
292
|
+
binaryPackages,
|
|
293
|
+
packageSources(project, binaryClosure),
|
|
294
|
+
selectedBinaries.map((binary) => binary.binary),
|
|
295
|
+
features,
|
|
296
|
+
),
|
|
297
|
+
];
|
|
214
298
|
});
|
|
215
|
-
const hasTargetOutputs =
|
|
216
|
-
|
|
217
|
-
if (hasTargetOutputs && plannedTargets.length === 0) {
|
|
299
|
+
const hasTargetOutputs = uniffiTargets.length > 0 || binaryTargets.length > 0;
|
|
300
|
+
if (hasTargetOutputs && targets.length === 0) {
|
|
218
301
|
throw new Error("Rust release requires at least one target");
|
|
219
302
|
}
|
|
220
303
|
return {
|
|
221
304
|
releaseRustVersion,
|
|
222
305
|
releaseTask: ".projen/uniffi-release.mjs",
|
|
223
306
|
releaseHelper: ".projen/rust-release.mjs",
|
|
224
|
-
rustRoot: options.root ?? "packages/rs",
|
|
225
307
|
bindings,
|
|
226
308
|
nodeBindings: bindings.filter((binding) => Boolean(binding.node && binding.nodePackage)),
|
|
227
309
|
releaseBinaries,
|
|
228
310
|
publicCrates,
|
|
229
|
-
|
|
230
|
-
|
|
311
|
+
uniffiTargets,
|
|
312
|
+
binaryTargets,
|
|
231
313
|
hasPythonBindings: bindings.some((binding) => binding.python),
|
|
232
314
|
usesCargoLock: existsSync(join(project.outdir, "Cargo.lock")),
|
|
233
315
|
usePreinstalledWindowsRust: releaseRustVersion === "stable",
|
|
@@ -235,6 +317,19 @@ export function planRustRelease(
|
|
|
235
317
|
};
|
|
236
318
|
}
|
|
237
319
|
|
|
320
|
+
function rustBuildJobIds(plan: RustReleasePlan): string[] {
|
|
321
|
+
return [
|
|
322
|
+
...(plan.uniffiTargets.length ? ["rust-uniffi"] : []),
|
|
323
|
+
...(plan.binaryTargets.length ? ["rust-binaries"] : []),
|
|
324
|
+
];
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function rustBuildResultCondition(plan: RustReleasePlan): string {
|
|
328
|
+
return rustBuildJobIds(plan)
|
|
329
|
+
.map((job) => `needs.${job}.result != 'failure' && needs.${job}.result != 'cancelled'`)
|
|
330
|
+
.join(" && ");
|
|
331
|
+
}
|
|
332
|
+
|
|
238
333
|
export function configureRustReleaseTask(
|
|
239
334
|
project: javascript.NodeProject,
|
|
240
335
|
plan: RustReleasePlan,
|
|
@@ -264,20 +359,31 @@ export function configureRustReleaseTask(
|
|
|
264
359
|
} else {
|
|
265
360
|
for (const path of bindingSupportFiles) project.tryRemoveFile(path);
|
|
266
361
|
}
|
|
267
|
-
project.addTask("rs:release-fingerprint", {
|
|
362
|
+
const fingerprintTask = project.addTask("rs:release-fingerprint", {
|
|
268
363
|
description: "Write the version-independent Rust release build manifest",
|
|
269
|
-
exec: [
|
|
270
|
-
`node ${plan.releaseHelper} fingerprint`,
|
|
271
|
-
"--root .",
|
|
272
|
-
...plan.targets.map(
|
|
273
|
-
(target) =>
|
|
274
|
-
`--target ${JSON.stringify(`${String(target.cargo)}|${String(target.cargoExcludes ?? "")}`)}`,
|
|
275
|
-
),
|
|
276
|
-
`--toolchain ${JSON.stringify(plan.releaseRustVersion)}`,
|
|
277
|
-
`--source ${JSON.stringify(plan.rustRoot)}`,
|
|
278
|
-
"--portable",
|
|
279
|
-
].join(" "),
|
|
280
364
|
});
|
|
365
|
+
for (const [namespace, targets] of [
|
|
366
|
+
["uniffi", plan.uniffiTargets],
|
|
367
|
+
["binaries", plan.binaryTargets],
|
|
368
|
+
] as const) {
|
|
369
|
+
if (!targets.length) continue;
|
|
370
|
+
const sources = [...new Set(targets.flatMap((target) => target.sources))].sort();
|
|
371
|
+
fingerprintTask.exec(
|
|
372
|
+
[
|
|
373
|
+
`node ${plan.releaseHelper} fingerprint`,
|
|
374
|
+
"--root .",
|
|
375
|
+
`--output ${JSON.stringify(`.release/rust-${namespace}.json`)}`,
|
|
376
|
+
`--namespace ${JSON.stringify(namespace)}`,
|
|
377
|
+
...targets.map(
|
|
378
|
+
(target) => `--target ${JSON.stringify(`${target.cargo}|${target.fingerprintConfig}`)}`,
|
|
379
|
+
),
|
|
380
|
+
`--toolchain ${JSON.stringify(plan.releaseRustVersion)}`,
|
|
381
|
+
...sources.map((source) => `--source ${JSON.stringify(source)}`),
|
|
382
|
+
"--source-only",
|
|
383
|
+
"--portable",
|
|
384
|
+
].join(" "),
|
|
385
|
+
);
|
|
386
|
+
}
|
|
281
387
|
}
|
|
282
388
|
|
|
283
389
|
function rustBindingCommands(plan: RustReleasePlan, independent: boolean): string[] {
|
|
@@ -352,61 +458,42 @@ function rustBinaryCommands(plan: RustReleasePlan, independent: boolean): string
|
|
|
352
458
|
});
|
|
353
459
|
}
|
|
354
460
|
|
|
355
|
-
|
|
356
|
-
return plan.releaseBinaries
|
|
357
|
-
.filter((binary) => binary.requiredFeatures.length)
|
|
358
|
-
.flatMap((binary) => {
|
|
359
|
-
const command = `cargo build --release --package "${binary.crate}" --bin "${binary.binary}" --features "${binary.requiredFeatures.join(",")}"${
|
|
360
|
-
plan.usesCargoLock ? " --locked" : ""
|
|
361
|
-
} --target "\${{ matrix.cargo }}"`;
|
|
362
|
-
const excludedCondition = binary.excludedOs
|
|
363
|
-
.map((os) => `[ "\${{ matrix.os }}" != "${os}" ]`)
|
|
364
|
-
.join(" && ");
|
|
365
|
-
const platformCommands = excludedCondition
|
|
366
|
-
? [`if ${excludedCondition}; then`, ` ${command}`, "fi"]
|
|
367
|
-
: [command];
|
|
368
|
-
if (!independent) return platformCommands;
|
|
369
|
-
const unit = defaultReleaseUnitId("rust", binary.crate);
|
|
370
|
-
return [
|
|
371
|
-
`if jq -e --arg unit "${unit}" '.units[] | select(.id == $unit)' dist/release-plan.json >/dev/null; then`,
|
|
372
|
-
...platformCommands.map((line) => ` ${line}`),
|
|
373
|
-
"fi",
|
|
374
|
-
];
|
|
375
|
-
});
|
|
376
|
-
}
|
|
461
|
+
type RustBuildKind = "uniffi" | "binaries";
|
|
377
462
|
|
|
378
|
-
function rustArtifactSteps(plan: RustReleasePlan): JobStep[] {
|
|
463
|
+
function rustArtifactSteps(plan: RustReleasePlan, kind: RustBuildKind): JobStep[] {
|
|
379
464
|
const binaryCrates = [...new Set(plan.releaseBinaries.map((binary) => binary.crate))];
|
|
380
465
|
return [
|
|
381
|
-
...
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
466
|
+
...(kind === "uniffi"
|
|
467
|
+
? plan.bindings.flatMap((binding) => [
|
|
468
|
+
...(binding.node
|
|
469
|
+
? [
|
|
470
|
+
{
|
|
471
|
+
name: `Upload ${binding.crate} native npm package`,
|
|
472
|
+
uses: "actions/upload-artifact@v7",
|
|
473
|
+
with: {
|
|
474
|
+
name: `${binding.crate}-\${{ matrix.node }}-npm`,
|
|
475
|
+
path: `dist/release/${binding.crate}/\${{ matrix.node }}/npm/*.tgz`,
|
|
476
|
+
"retention-days": 7,
|
|
477
|
+
},
|
|
478
|
+
},
|
|
479
|
+
]
|
|
480
|
+
: []),
|
|
481
|
+
...(binding.python
|
|
482
|
+
? [
|
|
483
|
+
{
|
|
484
|
+
name: `Upload ${binding.crate} Python wheel`,
|
|
485
|
+
uses: "actions/upload-artifact@v7",
|
|
486
|
+
with: {
|
|
487
|
+
name: `${binding.pythonPackage}--\${{ matrix.python }}--python-wheel`,
|
|
488
|
+
path: `dist/release/${binding.crate}/\${{ matrix.node }}/python/*.whl`,
|
|
489
|
+
"retention-days": 7,
|
|
490
|
+
},
|
|
491
|
+
},
|
|
492
|
+
]
|
|
493
|
+
: []),
|
|
494
|
+
])
|
|
495
|
+
: []),
|
|
496
|
+
...(kind === "binaries" ? binaryCrates : []).map((crate) => {
|
|
410
497
|
const binaries = plan.releaseBinaries.filter((binary) => binary.crate === crate);
|
|
411
498
|
const excludedOs =
|
|
412
499
|
binaries[0]?.excludedOs.filter((os) =>
|
|
@@ -428,18 +515,43 @@ function rustArtifactSteps(plan: RustReleasePlan): JobStep[] {
|
|
|
428
515
|
];
|
|
429
516
|
}
|
|
430
517
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
518
|
+
/** Build a Bash loop that preserves matrix values across LF and CRLF runners. */
|
|
519
|
+
function matrixArgumentLoop(variable: string, field: string, flag: string): string {
|
|
520
|
+
return `while IFS= read -r ${variable}; do ${variable}="\${${variable}%$'\\r'}"; ${variable}_ARGS+=(${flag} "$${variable}"); done < <(jq -r '.[]' <<<'\${{ toJSON(matrix.${field}) }}')`;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function rustCargoBuildCommand(plan: RustReleasePlan, kind: RustBuildKind): string {
|
|
524
|
+
return [
|
|
525
|
+
"PACKAGE_ARGS=()",
|
|
526
|
+
"FEATURE_ARGS=()",
|
|
527
|
+
matrixArgumentLoop("PACKAGE", "packages", "--package"),
|
|
528
|
+
matrixArgumentLoop("FEATURE", "features", "--features"),
|
|
529
|
+
...(kind === "binaries"
|
|
530
|
+
? ["BINARY_ARGS=()", matrixArgumentLoop("BINARY", "binaries", "--bin")]
|
|
531
|
+
: ["BINARY_ARGS=()"]),
|
|
532
|
+
`cargo build --release --timings "\${PACKAGE_ARGS[@]}" "\${BINARY_ARGS[@]}" "\${FEATURE_ARGS[@]}"${
|
|
533
|
+
plan.usesCargoLock ? " --locked" : ""
|
|
534
|
+
} --target "\${{ matrix.cargo }}"`,
|
|
535
|
+
].join("\n");
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
export function rustBuildJob(
|
|
539
|
+
plan: RustReleasePlan,
|
|
540
|
+
kind: RustBuildKind,
|
|
541
|
+
independentSetup?: readonly JobStep[],
|
|
542
|
+
): Job {
|
|
543
|
+
const independent = Boolean(independentSetup);
|
|
544
|
+
const targets = kind === "uniffi" ? plan.uniffiTargets : plan.binaryTargets;
|
|
545
|
+
const outputName = kind === "uniffi" ? "rust_uniffi_targets" : "rust_binary_targets";
|
|
546
|
+
const title = kind === "uniffi" ? "UniFFI" : "binaries";
|
|
547
|
+
const bindingCommands = kind === "uniffi" ? rustBindingCommands(plan, independent) : [];
|
|
548
|
+
const binaryCommands = kind === "binaries" ? rustBinaryCommands(plan, independent) : [];
|
|
549
|
+
const fingerprintSources = [...new Set(targets.flatMap((target) => target.sources))].sort();
|
|
438
550
|
return {
|
|
439
551
|
if: independentSetup
|
|
440
|
-
?
|
|
552
|
+
? `\${{ needs.release-plan.outputs.${outputName} != '[]' && (github.event_name == 'push' || inputs.stage != 'docs') }}`
|
|
441
553
|
: "${{ github.event_name == 'push' || inputs.stage == 'all' }}",
|
|
442
|
-
name:
|
|
554
|
+
name: `${title} / \${{ matrix.node }}`,
|
|
443
555
|
needs: [independentSetup ? "release-plan" : "verify-context"],
|
|
444
556
|
runsOn: ["${{ matrix.runner }}"],
|
|
445
557
|
permissions: { contents: JobPermission.READ },
|
|
@@ -451,13 +563,15 @@ export function rustBuildJob(plan: RustReleasePlan, independentSetup?: readonly
|
|
|
451
563
|
failFast: false,
|
|
452
564
|
matrix: {
|
|
453
565
|
include: independentSetup
|
|
454
|
-
? (
|
|
455
|
-
: [...
|
|
566
|
+
? (`\${{ fromJSON(needs.release-plan.outputs.${outputName}) }}` as never)
|
|
567
|
+
: ([...targets] as never),
|
|
456
568
|
},
|
|
457
569
|
},
|
|
458
570
|
steps: [
|
|
459
571
|
...(independentSetup ?? releaseSourceSteps()),
|
|
460
|
-
...(
|
|
572
|
+
...(kind === "uniffi" && plan.hasPythonBindings
|
|
573
|
+
? [{ name: "Setup uv", uses: "astral-sh/setup-uv@v7" }]
|
|
574
|
+
: []),
|
|
461
575
|
{
|
|
462
576
|
name: "Setup Rust",
|
|
463
577
|
...(plan.usePreinstalledWindowsRust ? { if: "${{ matrix.os != 'win32' }}" } : {}),
|
|
@@ -484,15 +598,20 @@ export function rustBuildJob(plan: RustReleasePlan, independentSetup?: readonly
|
|
|
484
598
|
shell: "bash",
|
|
485
599
|
run: "rustup component add llvm-tools-preview",
|
|
486
600
|
},
|
|
487
|
-
...rustCacheSteps(`release-\${{ matrix.cargo }}-rust-${plan.releaseRustVersion}`),
|
|
601
|
+
...rustCacheSteps(`release-${kind}-\${{ matrix.cargo }}-rust-${plan.releaseRustVersion}`),
|
|
488
602
|
{
|
|
489
603
|
name: "Install Linux native dependencies",
|
|
490
604
|
if: "${{ matrix.os == 'linux' }}",
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
605
|
+
// prettier-ignore
|
|
606
|
+
run: stringUtils.dedent(
|
|
607
|
+
// ============================================================================
|
|
608
|
+
/*bash*/`
|
|
609
|
+
sudo rm -f /etc/apt/sources.list.d/google-chrome.list
|
|
610
|
+
sudo apt-get update
|
|
611
|
+
sudo apt-get install --yes libdbus-1-dev pkg-config
|
|
612
|
+
`
|
|
613
|
+
// ============================================================================
|
|
614
|
+
),
|
|
496
615
|
},
|
|
497
616
|
{
|
|
498
617
|
name: "Verify Rust build fingerprint",
|
|
@@ -501,29 +620,25 @@ export function rustBuildJob(plan: RustReleasePlan, independentSetup?: readonly
|
|
|
501
620
|
: "${{ github.event_name == 'push' || inputs.stage == 'all' }}",
|
|
502
621
|
...(!independentSetup ? { id: "rust-fingerprint" } : {}),
|
|
503
622
|
shell: "bash",
|
|
504
|
-
run:
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
'KEY="$(jq -r --arg target "${{ matrix.cargo }}" \'.targets[$target] // empty\' "$RUNTIME_MANIFEST")"',
|
|
524
|
-
'test -n "$KEY"',
|
|
525
|
-
'echo "key=$KEY" >> "$GITHUB_OUTPUT"',
|
|
526
|
-
].join("\n"),
|
|
623
|
+
run: [
|
|
624
|
+
`RUNTIME_MANIFEST="dist/rust-raw/rust-${kind}-build-\${{ matrix.node }}.json"`,
|
|
625
|
+
'mkdir -p "$(dirname "$RUNTIME_MANIFEST")"',
|
|
626
|
+
[
|
|
627
|
+
`node ${plan.releaseHelper} fingerprint --root .`,
|
|
628
|
+
'--output "$RUNTIME_MANIFEST"',
|
|
629
|
+
`--namespace ${JSON.stringify(kind)}`,
|
|
630
|
+
'--target "${{ matrix.cargo }}|${{ matrix.fingerprintConfig }}"',
|
|
631
|
+
`--toolchain ${JSON.stringify(plan.releaseRustVersion)}`,
|
|
632
|
+
...fingerprintSources.map((source) => `--source ${JSON.stringify(source)}`),
|
|
633
|
+
"--source-only",
|
|
634
|
+
...(independentSetup ? ["--portable"] : []),
|
|
635
|
+
].join(" "),
|
|
636
|
+
`test "$(jq -r .namespace "$RUNTIME_MANIFEST")" = "$(jq -r .namespace .release/rust-${kind}.json)"`,
|
|
637
|
+
`test "$(jq -r .rustSourceHash "$RUNTIME_MANIFEST")" = "$(jq -r .rustSourceHash .release/rust-${kind}.json)"`,
|
|
638
|
+
'KEY="$(jq -r --arg target "${{ matrix.cargo }}" \'.targets[$target] // empty\' "$RUNTIME_MANIFEST")"',
|
|
639
|
+
'test -n "$KEY"',
|
|
640
|
+
'echo "key=$KEY" >> "$GITHUB_OUTPUT"',
|
|
641
|
+
].join("\n"),
|
|
527
642
|
},
|
|
528
643
|
...(!independentSetup
|
|
529
644
|
? [
|
|
@@ -538,7 +653,7 @@ export function rustBuildJob(plan: RustReleasePlan, independentSetup?: readonly
|
|
|
538
653
|
/*bash*/`
|
|
539
654
|
KEY="\${{ steps.rust-fingerprint.outputs.key }}"
|
|
540
655
|
test -n "$KEY"
|
|
541
|
-
ASSET="rust-raw-\${{ matrix.node }}-$KEY.tar.gz"
|
|
656
|
+
ASSET="rust-${kind}-raw-\${{ matrix.node }}-$KEY.tar.gz"
|
|
542
657
|
mkdir -p dist/rust-raw
|
|
543
658
|
MATCH="$(gh api --paginate "repos/\${{ github.repository }}/releases?per_page=100" --jq '.[] | select(.draft == false) | . as $release | .assets[] | select(.name == "'"$ASSET"'") | [$release.tag_name, .url] | @tsv' | head -n 1)"
|
|
544
659
|
if [ -n "$MATCH" ]; then
|
|
@@ -564,29 +679,14 @@ export function rustBuildJob(plan: RustReleasePlan, independentSetup?: readonly
|
|
|
564
679
|
]
|
|
565
680
|
: []),
|
|
566
681
|
{
|
|
567
|
-
name:
|
|
682
|
+
name: `Build Rust ${title} outputs`,
|
|
568
683
|
...(independentSetup ? {} : { if: "${{ steps.raw-native.outputs.hit != 'true' }}" }),
|
|
569
684
|
shell: "bash",
|
|
570
685
|
env: {
|
|
571
686
|
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER:
|
|
572
687
|
"${{ matrix.os == 'win32' && 'rust-lld' || '' }}",
|
|
573
688
|
},
|
|
574
|
-
run: timedBash(
|
|
575
|
-
"rust_workspace",
|
|
576
|
-
[
|
|
577
|
-
independentSetup
|
|
578
|
-
? [
|
|
579
|
-
"mapfile -t PACKAGES < <(jq -r '.[]' <<<'${{ toJSON(matrix.packages) }}')",
|
|
580
|
-
"PACKAGE_ARGS=()",
|
|
581
|
-
'for PACKAGE in "${PACKAGES[@]}"; do PACKAGE_ARGS+=(--package "$PACKAGE"); done',
|
|
582
|
-
`cargo build --release --timings "\${PACKAGE_ARGS[@]}"${plan.usesCargoLock ? " --locked" : ""} --target "\${{ matrix.cargo }}"`,
|
|
583
|
-
].join("\n")
|
|
584
|
-
: `cargo build --release --timings --workspace${plan.usesCargoLock ? " --locked" : ""} --target "\${{ matrix.cargo }}"${
|
|
585
|
-
plan.hasReleaseExclusions ? " ${{ matrix.cargoExcludes }}" : ""
|
|
586
|
-
}`,
|
|
587
|
-
...additionalBinaryBuildCommands,
|
|
588
|
-
].join("\n"),
|
|
589
|
-
),
|
|
689
|
+
run: timedBash(`rust_${kind}`, rustCargoBuildCommand(plan, kind)),
|
|
590
690
|
},
|
|
591
691
|
...(!independentSetup
|
|
592
692
|
? [
|
|
@@ -595,7 +695,7 @@ export function rustBuildJob(plan: RustReleasePlan, independentSetup?: readonly
|
|
|
595
695
|
if: "${{ steps.raw-native.outputs.hit != 'true' }}",
|
|
596
696
|
uses: "actions/upload-artifact@v7",
|
|
597
697
|
with: {
|
|
598
|
-
name:
|
|
698
|
+
name: `rust-${kind}-\${{ matrix.node }}-cargo-timings`,
|
|
599
699
|
path: "target/cargo-timings/*",
|
|
600
700
|
"retention-days": 14,
|
|
601
701
|
},
|
|
@@ -647,14 +747,14 @@ export function rustBuildJob(plan: RustReleasePlan, independentSetup?: readonly
|
|
|
647
747
|
name: "Upload reusable raw Rust outputs",
|
|
648
748
|
uses: "actions/upload-artifact@v7",
|
|
649
749
|
with: {
|
|
650
|
-
name:
|
|
750
|
+
name: `rust-${kind}-\${{ matrix.node }}-raw`,
|
|
651
751
|
path: "dist/rust-raw/*",
|
|
652
752
|
"retention-days": 7,
|
|
653
753
|
},
|
|
654
754
|
},
|
|
655
755
|
]
|
|
656
756
|
: []),
|
|
657
|
-
...rustArtifactSteps(plan),
|
|
757
|
+
...rustArtifactSteps(plan, kind),
|
|
658
758
|
],
|
|
659
759
|
};
|
|
660
760
|
}
|
|
@@ -665,7 +765,7 @@ export function rustCargoPublishJob(plan: RustReleasePlan, local: boolean): Job
|
|
|
665
765
|
if: local
|
|
666
766
|
? "${{ github.event_name == 'push' && vars.LOCAL_REPOSITORIES == 'true' }}"
|
|
667
767
|
: "${{ github.event_name == 'push' }}",
|
|
668
|
-
needs: ["verify-context",
|
|
768
|
+
needs: ["verify-context", ...rustBuildJobIds(plan)],
|
|
669
769
|
runsOn: [local ? "self-hosted" : "ubuntu-latest"],
|
|
670
770
|
permissions: { contents: JobPermission.READ },
|
|
671
771
|
steps: [
|
|
@@ -705,11 +805,12 @@ export function independentRustCargoPublishJob(
|
|
|
705
805
|
"fi",
|
|
706
806
|
].join("\n"),
|
|
707
807
|
);
|
|
808
|
+
const buildCondition = rustBuildResultCondition(plan);
|
|
708
809
|
return {
|
|
709
810
|
if: plan.hasTargetOutputs
|
|
710
|
-
?
|
|
811
|
+
? `\${{ always() && needs.release-plan.outputs.rust == 'true' && ${buildCondition} && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'rust') }}`
|
|
711
812
|
: "${{ needs.release-plan.outputs.rust == 'true' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'rust') }}",
|
|
712
|
-
needs: ["release-plan", ...(plan
|
|
813
|
+
needs: ["release-plan", ...rustBuildJobIds(plan)],
|
|
713
814
|
runsOn: ["ubuntu-latest"],
|
|
714
815
|
permissions: { contents: JobPermission.READ },
|
|
715
816
|
env: { BUN_VERSION },
|
|
@@ -734,7 +835,7 @@ export function independentRustGitHubReleaseJob(
|
|
|
734
835
|
): Job {
|
|
735
836
|
return {
|
|
736
837
|
if: "${{ needs.release-plan.outputs.github == 'true' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'github') }}",
|
|
737
|
-
needs: ["release-plan",
|
|
838
|
+
needs: ["release-plan", ...rustBuildJobIds(plan)],
|
|
738
839
|
runsOn: ["ubuntu-latest"],
|
|
739
840
|
permissions: { contents: JobPermission.WRITE },
|
|
740
841
|
env: { BUN_VERSION },
|
|
@@ -778,10 +879,10 @@ export function independentRustGitHubReleaseJob(
|
|
|
778
879
|
};
|
|
779
880
|
}
|
|
780
881
|
|
|
781
|
-
export function rustGitHubReleaseJob(): Job {
|
|
882
|
+
export function rustGitHubReleaseJob(plan: RustReleasePlan): Job {
|
|
782
883
|
return {
|
|
783
884
|
if: "${{ github.event_name == 'push' }}",
|
|
784
|
-
needs: ["verify-context",
|
|
885
|
+
needs: ["verify-context", ...rustBuildJobIds(plan)],
|
|
785
886
|
runsOn: ["ubuntu-latest"],
|
|
786
887
|
permissions: { contents: JobPermission.WRITE },
|
|
787
888
|
steps: [
|
|
@@ -847,7 +948,9 @@ export function rustGitHubReleaseJob(): Job {
|
|
|
847
948
|
if (release.draft || !version || compareVersions(version, currentVersion) >= 0) {
|
|
848
949
|
return [];
|
|
849
950
|
}
|
|
850
|
-
return release.assets.filter((asset) =>
|
|
951
|
+
return release.assets.filter((asset) =>
|
|
952
|
+
/^rust-(?:(?:uniffi|binaries)-(?:raw|build)|(?:raw|build))-/.test(asset.name)
|
|
953
|
+
);
|
|
851
954
|
});
|
|
852
955
|
for (const asset of assets) {
|
|
853
956
|
core.info(\`Deleting \${asset.name} (\${asset.id})\`);
|
|
@@ -869,7 +972,7 @@ export function rustGitHubReleaseJob(): Job {
|
|
|
869
972
|
export function independentRustNativeNpmPublishJob(project: DBXToolsJavaScriptProject): Job {
|
|
870
973
|
return {
|
|
871
974
|
if: "${{ needs.release-plan.outputs.node == 'true' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'node') }}",
|
|
872
|
-
needs: ["release-plan", "rust-
|
|
975
|
+
needs: ["release-plan", "rust-uniffi"],
|
|
873
976
|
runsOn: ["ubuntu-latest"],
|
|
874
977
|
permissions: { contents: JobPermission.READ, idToken: JobPermission.WRITE },
|
|
875
978
|
timeoutMinutes: 15,
|
|
@@ -932,8 +1035,8 @@ export function independentRustNodeFacadePublishJob(
|
|
|
932
1035
|
|
|
933
1036
|
export function rustNativeNpmPublishJob(project: DBXToolsJavaScriptProject): Job {
|
|
934
1037
|
return {
|
|
935
|
-
if: "${{ always() && needs.verify-context.result == 'success' && needs.rust-
|
|
936
|
-
needs: ["verify-context", "rust-
|
|
1038
|
+
if: "${{ always() && needs.verify-context.result == 'success' && needs.rust-uniffi.result != 'failure' && needs.rust-uniffi.result != 'cancelled' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'node') }}",
|
|
1039
|
+
needs: ["verify-context", "rust-uniffi"],
|
|
937
1040
|
runsOn: ["ubuntu-latest"],
|
|
938
1041
|
permissions: {
|
|
939
1042
|
actions: JobPermission.READ,
|