@dbx-tools/projen 0.9.5 → 0.9.6
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 +64 -16
- package/index.ts +7 -5
- package/package.json +9 -6
- package/src/_rust-project.ts +356 -0
- package/src/_rust-release-workflow.ts +901 -0
- package/src/_rust-release.ts +605 -0
- package/src/_task-command.ts +89 -0
- package/src/barrels.ts +9 -7
- package/src/engine-root.ts +5 -5
- package/src/openapi.ts +1 -1
- package/src/packages.ts +9 -9
- package/src/pnpm-workspace.ts +1 -1
- package/src/project-js.ts +19 -25
- package/src/project-py.ts +6 -6
- package/src/project-rs.ts +100 -1098
- package/src/projen-version.ts +9 -0
- package/src/publish.ts +3 -4
- package/src/release-catalog.ts +1 -1
- package/src/release.ts +11 -12
- package/src/scaffold.ts +1 -1
- package/src/uniffi.ts +7 -0
- package/src/watch.ts +2 -2
- package/src/workspace-version.ts +20 -15
- package/tasks/barrels.ts +4 -4
- package/tasks/build-rust-release.ts +68 -0
- package/tasks/bump.ts +3 -2
- package/tasks/clean.ts +3 -3
- package/tasks/local-publish.ts +21 -3
- package/tasks/openapi.ts +2 -2
- package/tasks/publish-npm.ts +12 -6
- package/tasks/publish-python.ts +2 -2
- package/tasks/publish-uniffi-local.ts +11 -15
- package/tasks/publish.ts +88 -88
- package/tasks/python-registry.ts +1 -1
- package/tasks/release-bootstrap.ts +2 -2
- package/tasks/release-plan.ts +1 -1
- package/tasks/release-please.ts +1 -1
- package/tasks/release-pr.ts +60 -115
- package/tasks/release-request.ts +8 -29
- package/tasks/release-summary-units.ts +2 -9
- package/tasks/release-summary.ts +35 -24
- package/tasks/release-version.ts +44 -0
- package/tasks/rust-release.d.ts +41 -0
- package/tasks/rust-release.mjs +1717 -0
- package/tasks/uniffi-python.js +1 -1
- package/tasks/uniffi.ts +7 -1
- package/tasks/version-check.ts +6 -5
package/src/project-rs.ts
CHANGED
|
@@ -1,13 +1,37 @@
|
|
|
1
1
|
/** Filesystem-discovered Rust workspaces and UniFFI binding package wiring. */
|
|
2
|
-
import { existsSync, readFileSync,
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import { project as coreProject } from "@dbx-tools/core";
|
|
7
|
-
import { string } from "@dbx-tools/shared-core";
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join, relative, resolve } from "node:path";
|
|
4
|
+
import * as projectUtils from "@dbx-tools/core/project-utils";
|
|
5
|
+
import { stringUtils } from "@dbx-tools/shared-core";
|
|
8
6
|
import { Component, Project, TextFile, TomlFile, javascript } from "projen";
|
|
9
|
-
import {
|
|
10
|
-
|
|
7
|
+
import {
|
|
8
|
+
RustProject,
|
|
9
|
+
type CargoExampleOptions as RustCargoExampleOptions,
|
|
10
|
+
cargoDependency,
|
|
11
|
+
discoverRustCrates,
|
|
12
|
+
structuredTomlSections,
|
|
13
|
+
type CargoDependency as RustCargoDependency,
|
|
14
|
+
type CargoDependencyOptions as RustCargoDependencyOptions,
|
|
15
|
+
type DBXToolsRustProjectOptions as RustProjectOptions,
|
|
16
|
+
type RustCrateOptions as RustCrateConfiguration,
|
|
17
|
+
type RustCliOptions as RustCliConfiguration,
|
|
18
|
+
} from "./_rust-project.ts";
|
|
19
|
+
import {
|
|
20
|
+
configureRustReleaseTask,
|
|
21
|
+
independentRustCargoPublishJob,
|
|
22
|
+
independentRustGitHubReleaseJob,
|
|
23
|
+
independentRustNativeNpmPublishJob,
|
|
24
|
+
independentRustNodeFacadePublishJob,
|
|
25
|
+
orderRustBindings,
|
|
26
|
+
planRustRelease,
|
|
27
|
+
releaseBinaryAssetName,
|
|
28
|
+
rustBuildJob,
|
|
29
|
+
rustCargoPublishJob,
|
|
30
|
+
rustGitHubReleaseJob,
|
|
31
|
+
rustNativeNpmPublishJob,
|
|
32
|
+
rustNodeFacadePublishJob,
|
|
33
|
+
} from "./_rust-release-workflow.ts";
|
|
34
|
+
import { toPosix } from "./packages.ts";
|
|
11
35
|
import {
|
|
12
36
|
DBX_TOOLS_LICENSE,
|
|
13
37
|
type DBXToolsJavaScriptProject,
|
|
@@ -16,65 +40,29 @@ import {
|
|
|
16
40
|
} from "./project-js.ts";
|
|
17
41
|
import { isDBXToolsJavaScriptProject } from "./project-predicate.ts";
|
|
18
42
|
import { pythonModuleName, type PythonPackageOptions } from "./project-py.ts";
|
|
19
|
-
import type { DBXToolsProject } from "./project.ts";
|
|
20
43
|
import { defaultReleaseUnitId, type ReleaseDependencyInput } from "./release-catalog.ts";
|
|
21
|
-
import {
|
|
22
|
-
RELEASE_SHA,
|
|
23
|
-
RELEASE_SUMMARY_FILE,
|
|
24
|
-
RELEASE_TAG,
|
|
25
|
-
RELEASE_VERSION,
|
|
26
|
-
releaseSourceSteps,
|
|
27
|
-
} from "./release-dispatch.ts";
|
|
28
44
|
import {
|
|
29
45
|
hasNodeRelease,
|
|
30
46
|
independentReleaseSetupSteps,
|
|
31
|
-
npmPublishEnvironment,
|
|
32
|
-
nodeReleaseSetupSteps,
|
|
33
47
|
registerIndependentPublicationJob,
|
|
34
|
-
releaseArtifactSteps,
|
|
35
|
-
releaseStageCondition,
|
|
36
48
|
tryReleaseWorkflow,
|
|
37
49
|
} from "./release.ts";
|
|
38
50
|
import { readWorkspaceVersion } from "./workspace-version.ts";
|
|
39
|
-
|
|
40
|
-
export
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
export
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
readonly command?: string;
|
|
55
|
-
/** Root CLI help text. Defaults to the Rust package description. */
|
|
56
|
-
readonly description?: string;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export interface RustPackageOptions {
|
|
60
|
-
readonly directory: string;
|
|
61
|
-
readonly description?: string;
|
|
62
|
-
/** Keep this crate unpublished and out of public docs. */
|
|
63
|
-
readonly private?: boolean;
|
|
64
|
-
/** Build this crate's binary for each target and attach it to the GitHub release. */
|
|
65
|
-
readonly release?: boolean;
|
|
66
|
-
/** Omit this crate and any release binary artifact from these operating systems. */
|
|
67
|
-
readonly releaseExcludeOs?: readonly RustReleaseOs[];
|
|
68
|
-
readonly dependencies?: Readonly<Record<string, CargoDependency>>;
|
|
69
|
-
readonly devDependencies?: Readonly<Record<string, CargoDependency>>;
|
|
70
|
-
readonly features?: Readonly<Record<string, readonly string[]>>;
|
|
71
|
-
readonly defaultFeatures?: readonly string[];
|
|
72
|
-
/** Cargo and release executable name. Defaults to the generated package name. */
|
|
73
|
-
readonly binaryName?: string;
|
|
74
|
-
/** Publish this release binary through the generated `dbx` command registry. */
|
|
75
|
-
readonly cli?: boolean | RustCliOptions;
|
|
76
|
-
readonly bindings?: readonly ("node" | "python")[];
|
|
77
|
-
readonly uniffiConfig?: Readonly<Record<string, unknown>>;
|
|
51
|
+
export { discoverRustCrates, hasUniFFIBindings } from "./_rust-project.ts";
|
|
52
|
+
export { orderRustBindings } from "./_rust-release-workflow.ts";
|
|
53
|
+
|
|
54
|
+
export type CargoDependency = RustCargoDependency;
|
|
55
|
+
export type CargoDependencyOptions = RustCargoDependencyOptions;
|
|
56
|
+
export type CargoExampleOptions = RustCargoExampleOptions;
|
|
57
|
+
export type DBXToolsRustProjectOptions = RustProjectOptions;
|
|
58
|
+
export type RustCrateOptions = RustCrateConfiguration;
|
|
59
|
+
export type RustCliOptions = RustCliConfiguration;
|
|
60
|
+
|
|
61
|
+
/** Public Rust project facade; implementation lives apart from workspace/release coordination. */
|
|
62
|
+
export class DBXToolsRustProject extends RustProject {
|
|
63
|
+
constructor(options: DBXToolsRustProjectOptions) {
|
|
64
|
+
super(options);
|
|
65
|
+
}
|
|
78
66
|
}
|
|
79
67
|
|
|
80
68
|
export interface DBXToolsRustWorkspaceOptions {
|
|
@@ -88,10 +76,11 @@ export interface DBXToolsRustWorkspaceOptions {
|
|
|
88
76
|
readonly license?: string;
|
|
89
77
|
readonly repository?: string;
|
|
90
78
|
readonly workspaceDependencies?: Readonly<Record<string, CargoDependency>>;
|
|
91
|
-
readonly packages?: Readonly<Record<string,
|
|
79
|
+
readonly packages?: Readonly<Record<string, RustCrateOptions>>;
|
|
92
80
|
readonly nodeRoot?: string;
|
|
93
81
|
readonly pythonRoot?: string;
|
|
94
82
|
readonly pythonModulePrefix?: string;
|
|
83
|
+
/** Default publication policy for discovered crates. Individual packages can override it. */
|
|
95
84
|
readonly private?: boolean;
|
|
96
85
|
/** Generate release-branch-driven cross-platform UniFFI package releases. */
|
|
97
86
|
readonly release?: boolean;
|
|
@@ -185,38 +174,6 @@ export const UNIFFI_RELEASE_TARGETS: readonly UniFFIReleaseTarget[] = [
|
|
|
185
174
|
|
|
186
175
|
const UBRN_VERSION = "0.31.0-5";
|
|
187
176
|
const RELEASE_PLATFORMS_ENV = "DBX_TOOLS_RELEASE_PLATFORMS";
|
|
188
|
-
const require = createRequire(import.meta.url);
|
|
189
|
-
const RUST_CACHE_ENV = {
|
|
190
|
-
CARGO_INCREMENTAL: "0",
|
|
191
|
-
CARGO_TERM_COLOR: "always",
|
|
192
|
-
} as const;
|
|
193
|
-
|
|
194
|
-
function rustCacheSteps(sharedKey: string): readonly JobStep[] {
|
|
195
|
-
return [
|
|
196
|
-
{
|
|
197
|
-
name: "Cache Cargo registry and dependencies",
|
|
198
|
-
id: "cargo_cache",
|
|
199
|
-
uses: "Swatinem/rust-cache@v2.9.2",
|
|
200
|
-
with: {
|
|
201
|
-
"cache-targets": true,
|
|
202
|
-
"cache-workspace-crates": true,
|
|
203
|
-
"add-job-id-key": false,
|
|
204
|
-
"add-rust-environment-hash-key": true,
|
|
205
|
-
"shared-key": sharedKey,
|
|
206
|
-
"save-if": "${{ github.event_name == 'push' }}",
|
|
207
|
-
},
|
|
208
|
-
},
|
|
209
|
-
];
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function timedBash(phase: string, command: string): string {
|
|
213
|
-
return [
|
|
214
|
-
"SECONDS=0",
|
|
215
|
-
`trap 'status=$?; echo "phase=${phase} duration_seconds=$SECONDS status=$status"; exit "$status"' EXIT`,
|
|
216
|
-
command,
|
|
217
|
-
].join("\n");
|
|
218
|
-
}
|
|
219
|
-
|
|
220
177
|
function releaseTargets(options: DBXToolsRustWorkspaceOptions): readonly UniFFIReleaseTarget[] {
|
|
221
178
|
if (options.releaseTargets && options.releasePlatforms) {
|
|
222
179
|
throw new Error("releaseTargets and releasePlatforms are mutually exclusive");
|
|
@@ -253,40 +210,16 @@ function releasePlatformsFromEnvironment(
|
|
|
253
210
|
});
|
|
254
211
|
}
|
|
255
212
|
|
|
256
|
-
function releaseBinaryAssetName(binaryName: string, nodeTarget: string, os: RustReleaseOs): string {
|
|
257
|
-
const extension = os === RustReleaseOs.WINDOWS ? "zip" : "tar.gz";
|
|
258
|
-
return `${binaryName}-${nodeTarget}.${extension}`;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
213
|
function rustCliRegistrySource(binaries: readonly RustReleaseBinaryMapping[]): string {
|
|
262
214
|
return [
|
|
263
215
|
"// GENERATED by projen - DO NOT EDIT.",
|
|
264
216
|
"// Rust release package options are the source of truth.",
|
|
265
217
|
"",
|
|
266
|
-
`export const
|
|
218
|
+
`export const RELEASE_BINARY_COMMANDS = ${JSON.stringify(binaries, null, 2)} as const;`,
|
|
267
219
|
"",
|
|
268
220
|
].join("\n");
|
|
269
221
|
}
|
|
270
222
|
|
|
271
|
-
function taskSource(name: string): string {
|
|
272
|
-
const sourceDirectory = dirname(fileURLToPath(import.meta.url));
|
|
273
|
-
const candidates = [
|
|
274
|
-
resolve(sourceDirectory, `../tasks/${name}`),
|
|
275
|
-
resolve(sourceDirectory, `../../tasks/${name}`),
|
|
276
|
-
];
|
|
277
|
-
const source = candidates.find(existsSync);
|
|
278
|
-
if (!source) throw new Error(`Could not locate tasks/${name}`);
|
|
279
|
-
return readFileSync(source, "utf8");
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
function smolTomlSource(name: "dist/index.cjs" | "LICENSE"): string {
|
|
283
|
-
const entry = require.resolve("smol-toml");
|
|
284
|
-
return readFileSync(
|
|
285
|
-
resolve(dirname(entry), name === "LICENSE" ? "../LICENSE" : "index.cjs"),
|
|
286
|
-
"utf8",
|
|
287
|
-
);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
223
|
/** Update only source-less workspace package entries in Cargo.lock. */
|
|
291
224
|
export function synchronizeCargoLockVersions(
|
|
292
225
|
content: string,
|
|
@@ -364,198 +297,6 @@ export interface RustReleaseBinaryMapping {
|
|
|
364
297
|
readonly assets: readonly RustReleaseBinaryAssetMapping[];
|
|
365
298
|
}
|
|
366
299
|
|
|
367
|
-
export function orderRustBindings(bindings: readonly RustBindingMapping[]): RustBindingMapping[] {
|
|
368
|
-
const ordered: RustBindingMapping[] = [];
|
|
369
|
-
const visiting = new Set<string>();
|
|
370
|
-
const completed = new Set<string>();
|
|
371
|
-
const visit = (binding: RustBindingMapping): void => {
|
|
372
|
-
if (completed.has(binding.crate)) return;
|
|
373
|
-
if (visiting.has(binding.crate)) {
|
|
374
|
-
throw new Error(`Cyclic Rust binding dependency: ${binding.crate}`);
|
|
375
|
-
}
|
|
376
|
-
visiting.add(binding.crate);
|
|
377
|
-
for (const name of binding.dependencies ?? []) {
|
|
378
|
-
const dependency = bindings.find((candidate) => candidate.crate === name);
|
|
379
|
-
if (!dependency) throw new Error(`Missing Rust binding dependency: ${name}`);
|
|
380
|
-
visit(dependency);
|
|
381
|
-
}
|
|
382
|
-
visiting.delete(binding.crate);
|
|
383
|
-
completed.add(binding.crate);
|
|
384
|
-
ordered.push(binding);
|
|
385
|
-
};
|
|
386
|
-
for (const binding of bindings) visit(binding);
|
|
387
|
-
return ordered;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
function rustSources(directory: string): string[] {
|
|
391
|
-
if (!existsSync(directory)) return [];
|
|
392
|
-
const files: string[] = [];
|
|
393
|
-
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
|
394
|
-
const path = join(directory, entry.name);
|
|
395
|
-
if (entry.isDirectory()) files.push(...rustSources(path));
|
|
396
|
-
else if (entry.isFile() && entry.name.endsWith(".rs")) files.push(path);
|
|
397
|
-
}
|
|
398
|
-
return files;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
/** Whether a Rust crate embeds the UniFFI proc-macro scaffolding marker. */
|
|
402
|
-
export function hasUniFFIBindings(directory: string): boolean {
|
|
403
|
-
return rustSources(join(directory, "src")).some((path) =>
|
|
404
|
-
/\buniffi\s*::\s*setup_scaffolding\s*!\s*\(/.test(readFileSync(path, "utf8")),
|
|
405
|
-
);
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
export function discoverRustCrates(root: string): string[] {
|
|
409
|
-
if (!existsSync(root)) return [];
|
|
410
|
-
return readdirSync(root, { withFileTypes: true })
|
|
411
|
-
.filter((entry) => entry.isDirectory())
|
|
412
|
-
.filter((entry) => rustSources(join(root, entry.name, "src")).length > 0)
|
|
413
|
-
.map((entry) => entry.name)
|
|
414
|
-
.sort();
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
function cargoDependency(
|
|
418
|
-
value: CargoDependency,
|
|
419
|
-
workspaceVersion?: string,
|
|
420
|
-
): string | Record<string, unknown> {
|
|
421
|
-
if (typeof value === "string") return value;
|
|
422
|
-
return {
|
|
423
|
-
...(value.version ? { version: value.version } : {}),
|
|
424
|
-
...(!value.version && value.path && workspaceVersion ? { version: workspaceVersion } : {}),
|
|
425
|
-
...(value.workspace ? { workspace: true } : {}),
|
|
426
|
-
...(value.path ? { path: value.path } : {}),
|
|
427
|
-
...(value.package ? { package: value.package } : {}),
|
|
428
|
-
...(value.optional ? { optional: true } : {}),
|
|
429
|
-
...(value.defaultFeatures === false ? { "default-features": false } : {}),
|
|
430
|
-
...(value.features?.length ? { features: [...value.features] } : {}),
|
|
431
|
-
};
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
function structuredTomlSections(value: Readonly<Record<string, unknown>>): Record<string, unknown> {
|
|
435
|
-
const result: Record<string, unknown> = {};
|
|
436
|
-
for (const [section, contents] of Object.entries(value)) {
|
|
437
|
-
const path = section.split(".");
|
|
438
|
-
let target = result;
|
|
439
|
-
for (const part of path.slice(0, -1)) {
|
|
440
|
-
const current = target[part];
|
|
441
|
-
if (current === undefined) {
|
|
442
|
-
target[part] = {};
|
|
443
|
-
} else if (!current || typeof current !== "object" || Array.isArray(current)) {
|
|
444
|
-
throw new Error(`Conflicting TOML section ${section}`);
|
|
445
|
-
}
|
|
446
|
-
target = target[part] as Record<string, unknown>;
|
|
447
|
-
}
|
|
448
|
-
target[path.at(-1)!] = contents;
|
|
449
|
-
}
|
|
450
|
-
return result;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
/** One generated Cargo workspace member. */
|
|
454
|
-
export class DBXToolsRustProject extends Project implements DBXToolsProject {
|
|
455
|
-
readonly language = "rust" as const;
|
|
456
|
-
readonly crateName: string;
|
|
457
|
-
readonly packageOptions: RustPackageOptions;
|
|
458
|
-
readonly uniffi: boolean;
|
|
459
|
-
readonly manifestFile: TomlFile;
|
|
460
|
-
|
|
461
|
-
constructor(
|
|
462
|
-
parent: javascript.NodeProject,
|
|
463
|
-
root: string,
|
|
464
|
-
scope: string,
|
|
465
|
-
options: RustPackageOptions,
|
|
466
|
-
) {
|
|
467
|
-
const crateName = `${string.toSlug(scope)}-${options.directory.toLowerCase().replace(/[^a-z0-9-]+/g, "-")}`;
|
|
468
|
-
super({ parent, outdir: `${root}/${options.directory}`, name: crateName });
|
|
469
|
-
this.crateName = crateName;
|
|
470
|
-
this.packageOptions = options;
|
|
471
|
-
this.uniffi = hasUniFFIBindings(this.outdir);
|
|
472
|
-
const library = existsSync(join(this.outdir, "src/lib.rs"));
|
|
473
|
-
const binary = existsSync(join(this.outdir, "src/main.rs"));
|
|
474
|
-
const binaryName = options.binaryName ?? crateName;
|
|
475
|
-
const packageVersion =
|
|
476
|
-
isDBXToolsJavaScriptProject()(parent) && parent.releaseCatalog.mode === "independent"
|
|
477
|
-
? parent.releaseCatalog.versionForRegistration(
|
|
478
|
-
"rust",
|
|
479
|
-
crateName,
|
|
480
|
-
`${root}/${options.directory}`,
|
|
481
|
-
)
|
|
482
|
-
: { workspace: true };
|
|
483
|
-
const manifest: Record<string, unknown> = {
|
|
484
|
-
package: {
|
|
485
|
-
name: crateName,
|
|
486
|
-
version: packageVersion,
|
|
487
|
-
edition: { workspace: true },
|
|
488
|
-
"rust-version": { workspace: true },
|
|
489
|
-
description: options.description ?? crateName,
|
|
490
|
-
license: { workspace: true },
|
|
491
|
-
repository: { workspace: true },
|
|
492
|
-
...(options.private ? { publish: false } : {}),
|
|
493
|
-
},
|
|
494
|
-
...(library
|
|
495
|
-
? {
|
|
496
|
-
lib: {
|
|
497
|
-
name: crateName.replaceAll("-", "_"),
|
|
498
|
-
path: "src/lib.rs",
|
|
499
|
-
...(this.uniffi ? { "crate-type": ["lib", "cdylib"] } : {}),
|
|
500
|
-
},
|
|
501
|
-
}
|
|
502
|
-
: {}),
|
|
503
|
-
...(this.uniffi || binary
|
|
504
|
-
? {
|
|
505
|
-
bin: [
|
|
506
|
-
this.uniffi
|
|
507
|
-
? { name: `${crateName}-uniffi-bindgen`, path: "uniffi-bindgen.rs" }
|
|
508
|
-
: { name: binaryName, path: "src/main.rs" },
|
|
509
|
-
],
|
|
510
|
-
}
|
|
511
|
-
: {}),
|
|
512
|
-
...(options.features || options.defaultFeatures
|
|
513
|
-
? {
|
|
514
|
-
features: {
|
|
515
|
-
...(options.defaultFeatures ? { default: [...options.defaultFeatures] } : {}),
|
|
516
|
-
...options.features,
|
|
517
|
-
},
|
|
518
|
-
}
|
|
519
|
-
: {}),
|
|
520
|
-
...(options.dependencies
|
|
521
|
-
? {
|
|
522
|
-
dependencies: Object.fromEntries(
|
|
523
|
-
Object.entries(options.dependencies).map(([name, value]) => [
|
|
524
|
-
name,
|
|
525
|
-
cargoDependency(value, readWorkspaceVersion(parent.outdir)),
|
|
526
|
-
]),
|
|
527
|
-
),
|
|
528
|
-
}
|
|
529
|
-
: {}),
|
|
530
|
-
...(options.devDependencies
|
|
531
|
-
? {
|
|
532
|
-
"dev-dependencies": Object.fromEntries(
|
|
533
|
-
Object.entries(options.devDependencies).map(([name, value]) => [
|
|
534
|
-
name,
|
|
535
|
-
cargoDependency(value, readWorkspaceVersion(parent.outdir)),
|
|
536
|
-
]),
|
|
537
|
-
),
|
|
538
|
-
}
|
|
539
|
-
: {}),
|
|
540
|
-
};
|
|
541
|
-
this.manifestFile = new TomlFile(this, "Cargo.toml", { marker: false, obj: manifest });
|
|
542
|
-
if (this.uniffi) {
|
|
543
|
-
new TextFile(this, "uniffi-bindgen.rs", {
|
|
544
|
-
lines: ["fn main() {", " uniffi::uniffi_bindgen_main();", "}", ""],
|
|
545
|
-
});
|
|
546
|
-
new TomlFile(this, "uniffi.toml", {
|
|
547
|
-
marker: false,
|
|
548
|
-
obj: structuredTomlSections(
|
|
549
|
-
options.uniffiConfig ?? {
|
|
550
|
-
"bindings.python": { cdylib_name: crateName.replaceAll("-", "_") },
|
|
551
|
-
"bindings.typescript": { strictTypeChecking: true },
|
|
552
|
-
},
|
|
553
|
-
),
|
|
554
|
-
});
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
|
|
559
300
|
function rustPackageDependencies(
|
|
560
301
|
packages: readonly DBXToolsRustProject[],
|
|
561
302
|
pkg: DBXToolsRustProject,
|
|
@@ -592,7 +333,8 @@ interface ResolvedRustWorkspaceOptions {
|
|
|
592
333
|
readonly repository: string;
|
|
593
334
|
readonly nativeTargets: readonly UniFFIReleaseTarget[];
|
|
594
335
|
readonly pythonModulePrefix: string;
|
|
595
|
-
readonly packageOptions: Readonly<Record<string,
|
|
336
|
+
readonly packageOptions: Readonly<Record<string, RustCrateOptions>>;
|
|
337
|
+
readonly private?: boolean;
|
|
596
338
|
readonly release: boolean;
|
|
597
339
|
}
|
|
598
340
|
|
|
@@ -601,7 +343,7 @@ function resolveRustWorkspaceOptions(
|
|
|
601
343
|
options: DBXToolsRustWorkspaceOptions,
|
|
602
344
|
): ResolvedRustWorkspaceOptions {
|
|
603
345
|
const dbxToolsProject = isDBXToolsJavaScriptProject()(project) ? project : undefined;
|
|
604
|
-
const scope =
|
|
346
|
+
const scope = stringUtils.toSlug(options.scope ?? dbxToolsProject?.scope ?? project.name);
|
|
605
347
|
return {
|
|
606
348
|
root: options.root ?? "packages/rs",
|
|
607
349
|
nodeRoot: options.nodeRoot ?? "packages/js/node",
|
|
@@ -610,11 +352,12 @@ function resolveRustWorkspaceOptions(
|
|
|
610
352
|
repository:
|
|
611
353
|
options.repository ??
|
|
612
354
|
projectRepositoryUrl(project) ??
|
|
613
|
-
|
|
355
|
+
projectUtils.repositoryUrl(project.outdir) ??
|
|
614
356
|
"",
|
|
615
357
|
nativeTargets: releaseTargets(options),
|
|
616
358
|
pythonModulePrefix: options.pythonModulePrefix ?? scope.replaceAll("-", "_"),
|
|
617
359
|
packageOptions: options.packages ?? {},
|
|
360
|
+
private: options.private,
|
|
618
361
|
release: options.release ?? true,
|
|
619
362
|
};
|
|
620
363
|
}
|
|
@@ -750,7 +493,7 @@ function planRustBindings(
|
|
|
750
493
|
const mappings = orderRustBindings(
|
|
751
494
|
bindings.map((pkg) => {
|
|
752
495
|
const targets = pkg.packageOptions.bindings ?? ["node", "python"];
|
|
753
|
-
const packageDirectory = `${
|
|
496
|
+
const packageDirectory = `${stringUtils.toSlug(pkg.packageOptions.directory)}-rs`;
|
|
754
497
|
const direct = dependencies.get(pkg);
|
|
755
498
|
const dependencyCrates = [
|
|
756
499
|
...new Set([...(direct?.node ?? []), ...(direct?.python ?? [])]),
|
|
@@ -793,7 +536,7 @@ function planPythonBindingPackages(
|
|
|
793
536
|
return plan.packages
|
|
794
537
|
.filter((pkg) => (pkg.packageOptions.bindings ?? ["node", "python"]).includes("python"))
|
|
795
538
|
.map((pkg) => {
|
|
796
|
-
const directory = `${
|
|
539
|
+
const directory = `${stringUtils.toSlug(pkg.packageOptions.directory)}-rs`;
|
|
797
540
|
const module = pythonModuleName(resolved.pythonModulePrefix, directory);
|
|
798
541
|
const name = `${resolved.scope}-${directory}`;
|
|
799
542
|
return {
|
|
@@ -803,7 +546,7 @@ function planPythonBindingPackages(
|
|
|
803
546
|
description: `Python bindings for ${pkg.crateName}`,
|
|
804
547
|
uniffi: true,
|
|
805
548
|
internalDependencies: bindingDependencies(plan, pkg, "python").map(
|
|
806
|
-
(dependency) => `${
|
|
549
|
+
(dependency) => `${stringUtils.toSlug(dependency.packageOptions.directory)}-rs`,
|
|
807
550
|
),
|
|
808
551
|
generatedSources: [
|
|
809
552
|
`src/${module.replaceAll(".", "/")}/bindings.py`,
|
|
@@ -835,7 +578,7 @@ function planNodeBindingPackages(
|
|
|
835
578
|
return plan.packages
|
|
836
579
|
.filter((pkg) => (pkg.packageOptions.bindings ?? ["node", "python"]).includes("node"))
|
|
837
580
|
.map((binding) => {
|
|
838
|
-
const directory = `${
|
|
581
|
+
const directory = `${stringUtils.toSlug(binding.packageOptions.directory)}-rs`;
|
|
839
582
|
const version = isDBXToolsJavaScriptProject()(project)
|
|
840
583
|
? project.releaseCatalog.versionFor(binding)
|
|
841
584
|
: readWorkspaceVersion(project.outdir);
|
|
@@ -853,7 +596,7 @@ function planNodeBindingPackages(
|
|
|
853
596
|
: {},
|
|
854
597
|
internalDependencies: bindingDependencies(plan, binding, "node").map(
|
|
855
598
|
(dependency) =>
|
|
856
|
-
`@${resolved.scope}/${
|
|
599
|
+
`@${resolved.scope}/${stringUtils.toSlug(dependency.packageOptions.directory)}-rs@workspace:*`,
|
|
857
600
|
),
|
|
858
601
|
};
|
|
859
602
|
});
|
|
@@ -863,13 +606,32 @@ function createRustPackages(
|
|
|
863
606
|
project: javascript.NodeProject,
|
|
864
607
|
resolved: ResolvedRustWorkspaceOptions,
|
|
865
608
|
): DBXToolsRustProject[] {
|
|
866
|
-
return discoverRustCrates(resolve(project.outdir, resolved.root)).map(
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
609
|
+
return discoverRustCrates(resolve(project.outdir, resolved.root)).map((directory) => {
|
|
610
|
+
const configured = {
|
|
611
|
+
private: resolved.private,
|
|
612
|
+
...resolved.packageOptions[directory],
|
|
613
|
+
};
|
|
614
|
+
const name = `${resolved.scope}-${directory.toLowerCase().replace(/[^a-z0-9-]+/g, "-")}`;
|
|
615
|
+
const version =
|
|
616
|
+
isDBXToolsJavaScriptProject()(project) && project.releaseCatalog.mode === "independent"
|
|
617
|
+
? project.releaseCatalog.versionForRegistration(
|
|
618
|
+
"rust",
|
|
619
|
+
name,
|
|
620
|
+
`${resolved.root}/${directory}`,
|
|
621
|
+
)
|
|
622
|
+
: undefined;
|
|
623
|
+
return new DBXToolsRustProject({
|
|
624
|
+
parent: project,
|
|
625
|
+
outdir: `${resolved.root}/${directory}`,
|
|
626
|
+
name,
|
|
627
|
+
directory,
|
|
628
|
+
workspaceRoot: resolved.root,
|
|
629
|
+
workspace: true,
|
|
630
|
+
scope: resolved.scope,
|
|
631
|
+
...(version ? { version } : {}),
|
|
632
|
+
...configured,
|
|
633
|
+
});
|
|
634
|
+
});
|
|
873
635
|
}
|
|
874
636
|
|
|
875
637
|
function configureRustCliRegistry(
|
|
@@ -905,7 +667,7 @@ function configureRustBindingFiles(
|
|
|
905
667
|
dependency.crateName.replaceAll("-", "_"),
|
|
906
668
|
`${pythonModuleName(
|
|
907
669
|
resolved.pythonModulePrefix,
|
|
908
|
-
`${
|
|
670
|
+
`${stringUtils.toSlug(dependency.packageOptions.directory)}-rs`,
|
|
909
671
|
)}.bindings`,
|
|
910
672
|
]),
|
|
911
673
|
),
|
|
@@ -1053,775 +815,6 @@ function configureRustWorkspaceTasks(project: javascript.NodeProject): void {
|
|
|
1053
815
|
project.removeTask("rs:bindings:demo");
|
|
1054
816
|
}
|
|
1055
817
|
|
|
1056
|
-
interface RustReleaseBinding extends RustBindingMapping {
|
|
1057
|
-
readonly node: string;
|
|
1058
|
-
readonly python: string;
|
|
1059
|
-
readonly nodePackage: string;
|
|
1060
|
-
readonly pythonPackage: string;
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
|
-
interface RustReleaseBinaryPlan {
|
|
1064
|
-
readonly crate: string;
|
|
1065
|
-
readonly binary: string;
|
|
1066
|
-
readonly excludedOs: readonly RustReleaseOs[];
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
type RustReleaseTargetPlan = Readonly<Record<string, string | boolean | number>>;
|
|
1070
|
-
|
|
1071
|
-
interface RustReleasePlan {
|
|
1072
|
-
readonly releaseRustVersion: string;
|
|
1073
|
-
readonly releaseTask: string;
|
|
1074
|
-
readonly bindings: readonly RustReleaseBinding[];
|
|
1075
|
-
readonly nodeBindings: readonly RustReleaseBinding[];
|
|
1076
|
-
readonly releaseBinaries: readonly RustReleaseBinaryPlan[];
|
|
1077
|
-
readonly publicCrates: readonly string[];
|
|
1078
|
-
readonly targets: readonly RustReleaseTargetPlan[];
|
|
1079
|
-
readonly hasReleaseExclusions: boolean;
|
|
1080
|
-
readonly hasPythonBindings: boolean;
|
|
1081
|
-
readonly usesCargoLock: boolean;
|
|
1082
|
-
readonly usePreinstalledWindowsRust: boolean;
|
|
1083
|
-
readonly hasTargetOutputs: boolean;
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
|
-
function planRustRelease(
|
|
1087
|
-
project: javascript.NodeProject,
|
|
1088
|
-
options: DBXToolsRustWorkspaceOptions,
|
|
1089
|
-
targets: readonly UniFFIReleaseTarget[],
|
|
1090
|
-
packages: readonly DBXToolsRustProject[],
|
|
1091
|
-
bindingMappings: readonly RustBindingMapping[],
|
|
1092
|
-
): RustReleasePlan {
|
|
1093
|
-
const releaseRustVersion = options.releaseRustVersion ?? "stable";
|
|
1094
|
-
const bindings = bindingMappings.map((binding) => ({
|
|
1095
|
-
...binding,
|
|
1096
|
-
node: binding.node ?? "",
|
|
1097
|
-
python: binding.python ?? "",
|
|
1098
|
-
nodePackage: binding.nodePackage ?? "",
|
|
1099
|
-
pythonPackage: binding.pythonPackage ?? "",
|
|
1100
|
-
}));
|
|
1101
|
-
const releaseBinaries = packages
|
|
1102
|
-
.filter((pkg) => pkg.packageOptions.release)
|
|
1103
|
-
.map((pkg) => ({
|
|
1104
|
-
crate: pkg.crateName,
|
|
1105
|
-
binary: pkg.packageOptions.binaryName ?? pkg.crateName,
|
|
1106
|
-
excludedOs: pkg.packageOptions.releaseExcludeOs ?? [],
|
|
1107
|
-
}));
|
|
1108
|
-
const packageDependencies = createRustPackageDependencyResolver(packages, project, options);
|
|
1109
|
-
const publicCrates = orderRustBindings(
|
|
1110
|
-
packages
|
|
1111
|
-
.filter((pkg) => !pkg.packageOptions.private)
|
|
1112
|
-
.map((pkg) => ({
|
|
1113
|
-
crate: pkg.crateName,
|
|
1114
|
-
rust: pkg.outdir,
|
|
1115
|
-
dependencies: packageDependencies(pkg).map((dependency) => dependency.crateName),
|
|
1116
|
-
})),
|
|
1117
|
-
).map((pkg) => pkg.crate);
|
|
1118
|
-
const hasReleaseExclusions = packages.some((pkg) => pkg.packageOptions.releaseExcludeOs?.length);
|
|
1119
|
-
const plannedTargets: RustReleaseTargetPlan[] = targets.map((target) => {
|
|
1120
|
-
const cargoExcludes = packages
|
|
1121
|
-
.filter((pkg) => pkg.packageOptions.releaseExcludeOs?.includes(target.os))
|
|
1122
|
-
.map((pkg) => `--exclude ${pkg.crateName}`)
|
|
1123
|
-
.join(" ");
|
|
1124
|
-
return {
|
|
1125
|
-
runner: target.runner,
|
|
1126
|
-
cargo: target.cargo,
|
|
1127
|
-
node: target.node,
|
|
1128
|
-
python: target.python,
|
|
1129
|
-
os: target.os,
|
|
1130
|
-
cpu: target.cpu,
|
|
1131
|
-
...(target.libc ? { libc: target.libc } : {}),
|
|
1132
|
-
...(hasReleaseExclusions ? { cargoExcludes } : {}),
|
|
1133
|
-
};
|
|
1134
|
-
});
|
|
1135
|
-
const hasTargetOutputs =
|
|
1136
|
-
bindings.length > 0 || releaseBinaries.length > 0 || publicCrates.length > 0;
|
|
1137
|
-
if (hasTargetOutputs && plannedTargets.length === 0) {
|
|
1138
|
-
throw new Error("Rust release requires at least one target");
|
|
1139
|
-
}
|
|
1140
|
-
return {
|
|
1141
|
-
releaseRustVersion,
|
|
1142
|
-
releaseTask: ".projen/uniffi-release.mjs",
|
|
1143
|
-
bindings,
|
|
1144
|
-
nodeBindings: bindings.filter((binding) => Boolean(binding.node && binding.nodePackage)),
|
|
1145
|
-
releaseBinaries,
|
|
1146
|
-
publicCrates,
|
|
1147
|
-
targets: plannedTargets,
|
|
1148
|
-
hasReleaseExclusions,
|
|
1149
|
-
hasPythonBindings: bindings.some((binding) => binding.python),
|
|
1150
|
-
usesCargoLock: existsSync(join(project.outdir, "Cargo.lock")),
|
|
1151
|
-
usePreinstalledWindowsRust: releaseRustVersion === "stable",
|
|
1152
|
-
hasTargetOutputs,
|
|
1153
|
-
};
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
|
-
function configureRustReleaseTask(project: javascript.NodeProject, plan: RustReleasePlan): void {
|
|
1157
|
-
const supportFiles = [
|
|
1158
|
-
plan.releaseTask,
|
|
1159
|
-
".projen/uniffi-python.js",
|
|
1160
|
-
".projen/smol-toml.cjs",
|
|
1161
|
-
".projen/smol-toml.LICENSE",
|
|
1162
|
-
] as const;
|
|
1163
|
-
if (plan.bindings.length) {
|
|
1164
|
-
new TextFile(project, plan.releaseTask, {
|
|
1165
|
-
lines: taskSource("uniffi-release.mjs").trimEnd().split("\n"),
|
|
1166
|
-
});
|
|
1167
|
-
new TextFile(project, supportFiles[1], {
|
|
1168
|
-
lines: taskSource("uniffi-python.js").trimEnd().split("\n"),
|
|
1169
|
-
});
|
|
1170
|
-
new TextFile(project, supportFiles[2], {
|
|
1171
|
-
lines: smolTomlSource("dist/index.cjs").trimEnd().split("\n"),
|
|
1172
|
-
});
|
|
1173
|
-
new TextFile(project, supportFiles[3], {
|
|
1174
|
-
lines: smolTomlSource("LICENSE").trimEnd().split("\n"),
|
|
1175
|
-
});
|
|
1176
|
-
} else {
|
|
1177
|
-
for (const path of supportFiles) project.tryRemoveFile(path);
|
|
1178
|
-
}
|
|
1179
|
-
project.addTask("rs:release-fingerprint", {
|
|
1180
|
-
description: "Write the version-independent Rust release build manifest",
|
|
1181
|
-
exec: [
|
|
1182
|
-
"cargo run --quiet --package dbx-tools-release-tools -- fingerprint",
|
|
1183
|
-
...plan.targets.map(
|
|
1184
|
-
(target) =>
|
|
1185
|
-
`--target ${JSON.stringify(`${String(target.cargo)}|${String(target.cargoExcludes ?? "")}`)}`,
|
|
1186
|
-
),
|
|
1187
|
-
`--toolchain ${JSON.stringify(plan.releaseRustVersion)}`,
|
|
1188
|
-
"--portable",
|
|
1189
|
-
].join(" "),
|
|
1190
|
-
});
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
function rustBindingCommands(plan: RustReleasePlan, independent: boolean): string[] {
|
|
1194
|
-
return plan.bindings.map((binding) => {
|
|
1195
|
-
const unit = defaultReleaseUnitId("rust", binding.crate);
|
|
1196
|
-
const command = [
|
|
1197
|
-
`node ${plan.releaseTask} build`,
|
|
1198
|
-
`--crate "${binding.crate}"`,
|
|
1199
|
-
`--node "${binding.node}"`,
|
|
1200
|
-
`--python "${binding.python}"`,
|
|
1201
|
-
`--node-package "${binding.nodePackage}"`,
|
|
1202
|
-
`--python-package "${binding.pythonPackage}"`,
|
|
1203
|
-
`--python-module "${binding.pythonModule}"`,
|
|
1204
|
-
'--cargo-target "${{ matrix.cargo }}"',
|
|
1205
|
-
'--node-triple "${{ matrix.node }}"',
|
|
1206
|
-
'--python-tag "${{ matrix.python }}"',
|
|
1207
|
-
'--os "${{ matrix.os }}"',
|
|
1208
|
-
'--cpu "${{ matrix.cpu }}"',
|
|
1209
|
-
'--libc "${{ matrix.libc }}"',
|
|
1210
|
-
independent
|
|
1211
|
-
? `--version "$(jq -r --arg unit "${unit}" '.units[] | select(.id == $unit) | .newVersion' dist/release-plan.json)"`
|
|
1212
|
-
: '--version "$VERSION"',
|
|
1213
|
-
`--output "dist/release/${binding.crate}/\${{ matrix.node }}"`,
|
|
1214
|
-
"--skip-build",
|
|
1215
|
-
].join(" \\\n ");
|
|
1216
|
-
return independent
|
|
1217
|
-
? [
|
|
1218
|
-
`if jq -e --arg unit "${unit}" '.units[] | select(.id == $unit)' dist/release-plan.json >/dev/null; then`,
|
|
1219
|
-
` ${command.replaceAll("\n", "\n ")}`,
|
|
1220
|
-
"fi",
|
|
1221
|
-
].join("\n")
|
|
1222
|
-
: command;
|
|
1223
|
-
});
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
function rustReleaseBinaryCondition(excludedOs: readonly RustReleaseOs[]): string | undefined {
|
|
1227
|
-
return excludedOs.length
|
|
1228
|
-
? `\${{ ${excludedOs.map((os) => `matrix.os != '${os}'`).join(" && ")} }}`
|
|
1229
|
-
: undefined;
|
|
1230
|
-
}
|
|
1231
|
-
|
|
1232
|
-
function rustBinaryCommands(plan: RustReleasePlan, independent: boolean): string[] {
|
|
1233
|
-
return plan.releaseBinaries.flatMap((pkg) => {
|
|
1234
|
-
const unit = defaultReleaseUnitId("rust", pkg.crate);
|
|
1235
|
-
const windowsAsset = releaseBinaryAssetName(
|
|
1236
|
-
pkg.binary,
|
|
1237
|
-
"${{ matrix.node }}",
|
|
1238
|
-
RustReleaseOs.WINDOWS,
|
|
1239
|
-
);
|
|
1240
|
-
const unixAsset = releaseBinaryAssetName(pkg.binary, "${{ matrix.node }}", RustReleaseOs.LINUX);
|
|
1241
|
-
const commands = [
|
|
1242
|
-
`mkdir -p "dist/release/${pkg.crate}/\${{ matrix.node }}/binary/stage"`,
|
|
1243
|
-
`SOURCE="target/\${{ matrix.cargo }}/release/${pkg.binary}\${{ matrix.os == 'win32' && '.exe' || '' }}"`,
|
|
1244
|
-
`DESTINATION="dist/release/${pkg.crate}/\${{ matrix.node }}/binary/stage/${pkg.binary}\${{ matrix.os == 'win32' && '.exe' || '' }}"`,
|
|
1245
|
-
'cp "$SOURCE" "$DESTINATION"',
|
|
1246
|
-
'if [ "${{ matrix.os }}" = "win32" ]; then',
|
|
1247
|
-
` 7z a "dist/release/${pkg.crate}/\${{ matrix.node }}/binary/${windowsAsset}" "$DESTINATION"`,
|
|
1248
|
-
"else",
|
|
1249
|
-
` tar -C "dist/release/${pkg.crate}/\${{ matrix.node }}/binary/stage" -czf "dist/release/${pkg.crate}/\${{ matrix.node }}/binary/${unixAsset}" "${pkg.binary}"`,
|
|
1250
|
-
"fi",
|
|
1251
|
-
`rm -rf "dist/release/${pkg.crate}/\${{ matrix.node }}/binary/stage"`,
|
|
1252
|
-
];
|
|
1253
|
-
const excludedCondition = pkg.excludedOs
|
|
1254
|
-
.map((os) => `[ "\${{ matrix.os }}" != "${os}" ]`)
|
|
1255
|
-
.join(" && ");
|
|
1256
|
-
const platformCommands = excludedCondition
|
|
1257
|
-
? [`if ${excludedCondition}; then`, ...commands.map((command) => ` ${command}`), "fi"]
|
|
1258
|
-
: commands;
|
|
1259
|
-
return independent
|
|
1260
|
-
? [
|
|
1261
|
-
`if jq -e --arg unit "${unit}" '.units[] | select(.id == $unit)' dist/release-plan.json >/dev/null; then`,
|
|
1262
|
-
...platformCommands.map((command) => ` ${command}`),
|
|
1263
|
-
"fi",
|
|
1264
|
-
]
|
|
1265
|
-
: platformCommands;
|
|
1266
|
-
});
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
|
-
function rustArtifactSteps(plan: RustReleasePlan): JobStep[] {
|
|
1270
|
-
return [
|
|
1271
|
-
...plan.bindings.flatMap((binding) => [
|
|
1272
|
-
...(binding.node
|
|
1273
|
-
? [
|
|
1274
|
-
{
|
|
1275
|
-
name: `Upload ${binding.crate} native npm package`,
|
|
1276
|
-
uses: "actions/upload-artifact@v7",
|
|
1277
|
-
with: {
|
|
1278
|
-
name: `${binding.crate}-\${{ matrix.node }}-npm`,
|
|
1279
|
-
path: `dist/release/${binding.crate}/\${{ matrix.node }}/npm/*.tgz`,
|
|
1280
|
-
"retention-days": 7,
|
|
1281
|
-
},
|
|
1282
|
-
},
|
|
1283
|
-
]
|
|
1284
|
-
: []),
|
|
1285
|
-
...(binding.python
|
|
1286
|
-
? [
|
|
1287
|
-
{
|
|
1288
|
-
name: `Upload ${binding.crate} Python wheel`,
|
|
1289
|
-
uses: "actions/upload-artifact@v7",
|
|
1290
|
-
with: {
|
|
1291
|
-
name: `${binding.pythonPackage}--\${{ matrix.python }}--python-wheel`,
|
|
1292
|
-
path: `dist/release/${binding.crate}/\${{ matrix.node }}/python/*.whl`,
|
|
1293
|
-
"retention-days": 7,
|
|
1294
|
-
},
|
|
1295
|
-
},
|
|
1296
|
-
]
|
|
1297
|
-
: []),
|
|
1298
|
-
]),
|
|
1299
|
-
...plan.releaseBinaries.map((pkg) => ({
|
|
1300
|
-
name: `Upload ${pkg.crate} release binary`,
|
|
1301
|
-
uses: "actions/upload-artifact@v7",
|
|
1302
|
-
...(rustReleaseBinaryCondition(pkg.excludedOs)
|
|
1303
|
-
? { if: rustReleaseBinaryCondition(pkg.excludedOs) }
|
|
1304
|
-
: {}),
|
|
1305
|
-
with: {
|
|
1306
|
-
name: `${pkg.crate}-\${{ matrix.node }}-binary`,
|
|
1307
|
-
path: `dist/release/${pkg.crate}/\${{ matrix.node }}/binary/*`,
|
|
1308
|
-
"retention-days": 7,
|
|
1309
|
-
},
|
|
1310
|
-
})),
|
|
1311
|
-
];
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
|
-
function rustBuildJob(plan: RustReleasePlan, independentSetup?: readonly JobStep[]): Job {
|
|
1315
|
-
const bindingCommands = rustBindingCommands(plan, Boolean(independentSetup));
|
|
1316
|
-
const binaryCommands = rustBinaryCommands(plan, Boolean(independentSetup));
|
|
1317
|
-
return {
|
|
1318
|
-
if: independentSetup
|
|
1319
|
-
? "${{ needs.release-plan.outputs.rust_targets != '[]' && (github.event_name == 'push' || inputs.stage != 'docs') }}"
|
|
1320
|
-
: "${{ github.event_name == 'push' || inputs.stage == 'all' }}",
|
|
1321
|
-
name: "${{ matrix.node }}",
|
|
1322
|
-
needs: [independentSetup ? "release-plan" : "verify-context"],
|
|
1323
|
-
runsOn: ["${{ matrix.runner }}"],
|
|
1324
|
-
permissions: { contents: JobPermission.READ },
|
|
1325
|
-
env: {
|
|
1326
|
-
...RUST_CACHE_ENV,
|
|
1327
|
-
...(independentSetup ? { BUN_VERSION } : {}),
|
|
1328
|
-
},
|
|
1329
|
-
strategy: {
|
|
1330
|
-
failFast: false,
|
|
1331
|
-
matrix: {
|
|
1332
|
-
include: independentSetup
|
|
1333
|
-
? ("${{ fromJSON(needs.release-plan.outputs.rust_targets) }}" as never)
|
|
1334
|
-
: [...plan.targets],
|
|
1335
|
-
},
|
|
1336
|
-
},
|
|
1337
|
-
steps: [
|
|
1338
|
-
...(independentSetup ?? releaseSourceSteps()),
|
|
1339
|
-
...(plan.hasPythonBindings ? [{ name: "Setup uv", uses: "astral-sh/setup-uv@v7" }] : []),
|
|
1340
|
-
{
|
|
1341
|
-
name: "Setup Rust",
|
|
1342
|
-
...(plan.usePreinstalledWindowsRust ? { if: "${{ matrix.os != 'win32' }}" } : {}),
|
|
1343
|
-
uses: `dtolnay/rust-toolchain@${plan.releaseRustVersion}`,
|
|
1344
|
-
with: { targets: "${{ matrix.cargo }}" },
|
|
1345
|
-
},
|
|
1346
|
-
...(plan.usePreinstalledWindowsRust
|
|
1347
|
-
? [
|
|
1348
|
-
{
|
|
1349
|
-
name: "Verify preinstalled Windows Rust",
|
|
1350
|
-
if: "${{ matrix.os == 'win32' }}",
|
|
1351
|
-
shell: "bash",
|
|
1352
|
-
run: [
|
|
1353
|
-
"rustc --version --verbose",
|
|
1354
|
-
"cargo --version",
|
|
1355
|
-
'rustup target list --installed | grep -Fx "${{ matrix.cargo }}"',
|
|
1356
|
-
'test -f "$(rustc --print sysroot)/lib/rustlib/${{ matrix.cargo }}/bin/rust-lld.exe"',
|
|
1357
|
-
].join("\n"),
|
|
1358
|
-
},
|
|
1359
|
-
]
|
|
1360
|
-
: []),
|
|
1361
|
-
...rustCacheSteps(`release-\${{ matrix.cargo }}-rust-${plan.releaseRustVersion}`),
|
|
1362
|
-
{
|
|
1363
|
-
name: "Install Linux native dependencies",
|
|
1364
|
-
if: "${{ matrix.os == 'linux' }}",
|
|
1365
|
-
run: [
|
|
1366
|
-
"sudo rm -f /etc/apt/sources.list.d/google-chrome.list",
|
|
1367
|
-
"sudo apt-get update",
|
|
1368
|
-
"sudo apt-get install --yes libdbus-1-dev pkg-config",
|
|
1369
|
-
].join("\n"),
|
|
1370
|
-
},
|
|
1371
|
-
{
|
|
1372
|
-
name: "Verify Rust build fingerprint",
|
|
1373
|
-
if: independentSetup
|
|
1374
|
-
? undefined
|
|
1375
|
-
: "${{ github.event_name == 'push' || inputs.stage == 'all' }}",
|
|
1376
|
-
...(!independentSetup ? { id: "rust-fingerprint" } : {}),
|
|
1377
|
-
shell: "bash",
|
|
1378
|
-
run: independentSetup
|
|
1379
|
-
? [
|
|
1380
|
-
"cargo run --quiet --package dbx-tools-release-tools -- fingerprint --check",
|
|
1381
|
-
'--target "${{ matrix.cargo }}|${{ matrix.cargoExcludes }}"',
|
|
1382
|
-
`--toolchain ${JSON.stringify(plan.releaseRustVersion)}`,
|
|
1383
|
-
"--portable",
|
|
1384
|
-
].join(" ")
|
|
1385
|
-
: [
|
|
1386
|
-
'RUNTIME_MANIFEST="dist/rust-raw/rust-build-${{ matrix.node }}.json"',
|
|
1387
|
-
'mkdir -p "$(dirname "$RUNTIME_MANIFEST")"',
|
|
1388
|
-
[
|
|
1389
|
-
"cargo run --quiet --package dbx-tools-release-tools -- fingerprint",
|
|
1390
|
-
'--output "$RUNTIME_MANIFEST"',
|
|
1391
|
-
'--target "${{ matrix.cargo }}|${{ matrix.cargoExcludes }}"',
|
|
1392
|
-
`--toolchain ${JSON.stringify(plan.releaseRustVersion)}`,
|
|
1393
|
-
].join(" "),
|
|
1394
|
-
'test "$(jq -r .rustSourceHash "$RUNTIME_MANIFEST")" = "$(jq -r .rustSourceHash .release/rust-build.json)"',
|
|
1395
|
-
'KEY="$(jq -r --arg target "${{ matrix.cargo }}" \'.targets[$target] // empty\' "$RUNTIME_MANIFEST")"',
|
|
1396
|
-
'test -n "$KEY"',
|
|
1397
|
-
'echo "key=$KEY" >> "$GITHUB_OUTPUT"',
|
|
1398
|
-
].join("\n"),
|
|
1399
|
-
},
|
|
1400
|
-
...(!independentSetup
|
|
1401
|
-
? [
|
|
1402
|
-
{
|
|
1403
|
-
name: "Reuse matching raw Rust outputs",
|
|
1404
|
-
id: "raw-native",
|
|
1405
|
-
shell: "bash",
|
|
1406
|
-
env: { GH_TOKEN: "${{ github.token }}" },
|
|
1407
|
-
run: [
|
|
1408
|
-
'KEY="${{ steps.rust-fingerprint.outputs.key }}"',
|
|
1409
|
-
'test -n "$KEY"',
|
|
1410
|
-
'ASSET="rust-raw-${{ matrix.node }}-$KEY.tar.gz"',
|
|
1411
|
-
"mkdir -p dist/rust-raw",
|
|
1412
|
-
'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)"',
|
|
1413
|
-
'if [ -n "$MATCH" ]; then',
|
|
1414
|
-
" TAG=\"${MATCH%%$'\\t'*}\"",
|
|
1415
|
-
" URL=\"${MATCH#*$'\\t'}\"",
|
|
1416
|
-
' CHECKSUM_URL="$(gh api "repos/${{ github.repository }}/releases/tags/$TAG" --jq \'.assets[] | select(.name == "\'"$ASSET.sha256"\'") | .url\')"',
|
|
1417
|
-
' rm -rf "target/${{ matrix.cargo }}/release"',
|
|
1418
|
-
' if test -n "$CHECKSUM_URL" && gh api "$URL" -H "Accept: application/octet-stream" > "dist/rust-raw/$ASSET" && gh api "$CHECKSUM_URL" -H "Accept: application/octet-stream" > "dist/rust-raw/$ASSET.sha256" && (cd dist/rust-raw && if command -v sha256sum >/dev/null 2>&1; then sha256sum --check "$ASSET.sha256"; else shasum -a 256 --check "$ASSET.sha256"; fi) && tar -xzf "dist/rust-raw/$ASSET"; then',
|
|
1419
|
-
' echo "hit=true" >> "$GITHUB_OUTPUT"',
|
|
1420
|
-
" else",
|
|
1421
|
-
' rm -rf "target/${{ matrix.cargo }}/release" "dist/rust-raw/$ASSET" "dist/rust-raw/$ASSET.sha256"',
|
|
1422
|
-
' echo "::warning::matching raw Rust bundle failed validation; rebuilding"',
|
|
1423
|
-
' echo "hit=false" >> "$GITHUB_OUTPUT"',
|
|
1424
|
-
" fi",
|
|
1425
|
-
"else",
|
|
1426
|
-
' echo "hit=false" >> "$GITHUB_OUTPUT"',
|
|
1427
|
-
"fi",
|
|
1428
|
-
'echo "asset=$ASSET" >> "$GITHUB_OUTPUT"',
|
|
1429
|
-
].join("\n"),
|
|
1430
|
-
},
|
|
1431
|
-
]
|
|
1432
|
-
: []),
|
|
1433
|
-
{
|
|
1434
|
-
name: "Build Rust outputs",
|
|
1435
|
-
...(independentSetup ? {} : { if: "${{ steps.raw-native.outputs.hit != 'true' }}" }),
|
|
1436
|
-
shell: "bash",
|
|
1437
|
-
env: {
|
|
1438
|
-
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER:
|
|
1439
|
-
"${{ matrix.os == 'win32' && 'rust-lld' || '' }}",
|
|
1440
|
-
},
|
|
1441
|
-
run: timedBash(
|
|
1442
|
-
"rust_workspace",
|
|
1443
|
-
independentSetup
|
|
1444
|
-
? [
|
|
1445
|
-
"mapfile -t PACKAGES < <(jq -r '.[]' <<<'${{ toJSON(matrix.packages) }}')",
|
|
1446
|
-
"PACKAGE_ARGS=()",
|
|
1447
|
-
'for PACKAGE in "${PACKAGES[@]}"; do PACKAGE_ARGS+=(--package "$PACKAGE"); done',
|
|
1448
|
-
`cargo build --release --timings "\${PACKAGE_ARGS[@]}"${plan.usesCargoLock ? " --locked" : ""} --target "\${{ matrix.cargo }}"`,
|
|
1449
|
-
].join("\n")
|
|
1450
|
-
: `cargo build --release --timings --workspace${plan.usesCargoLock ? " --locked" : ""} --target "\${{ matrix.cargo }}"${
|
|
1451
|
-
plan.hasReleaseExclusions ? " ${{ matrix.cargoExcludes }}" : ""
|
|
1452
|
-
}`,
|
|
1453
|
-
),
|
|
1454
|
-
},
|
|
1455
|
-
...(!independentSetup
|
|
1456
|
-
? [
|
|
1457
|
-
{
|
|
1458
|
-
name: "Upload Cargo timings",
|
|
1459
|
-
if: "${{ steps.raw-native.outputs.hit != 'true' }}",
|
|
1460
|
-
uses: "actions/upload-artifact@v7",
|
|
1461
|
-
with: {
|
|
1462
|
-
name: "rust-${{ matrix.node }}-cargo-timings",
|
|
1463
|
-
path: "target/cargo-timings/*",
|
|
1464
|
-
"retention-days": 14,
|
|
1465
|
-
},
|
|
1466
|
-
},
|
|
1467
|
-
]
|
|
1468
|
-
: []),
|
|
1469
|
-
...(!independentSetup
|
|
1470
|
-
? [
|
|
1471
|
-
{
|
|
1472
|
-
name: "Archive raw Rust outputs",
|
|
1473
|
-
if: "${{ steps.raw-native.outputs.hit != 'true' }}",
|
|
1474
|
-
shell: "bash",
|
|
1475
|
-
run: [
|
|
1476
|
-
"mkdir -p dist/rust-raw",
|
|
1477
|
-
'tar -czf "dist/rust-raw/${{ steps.raw-native.outputs.asset }}" "target/${{ matrix.cargo }}/release"',
|
|
1478
|
-
'(cd dist/rust-raw && if command -v sha256sum >/dev/null 2>&1; then sha256sum "${{ steps.raw-native.outputs.asset }}"; else shasum -a 256 "${{ steps.raw-native.outputs.asset }}"; fi > "${{ steps.raw-native.outputs.asset }}.sha256")',
|
|
1479
|
-
].join("\n"),
|
|
1480
|
-
},
|
|
1481
|
-
{
|
|
1482
|
-
name: "Stamp native release version",
|
|
1483
|
-
shell: "bash",
|
|
1484
|
-
env: { VERSION: RELEASE_VERSION },
|
|
1485
|
-
run: 'cargo run --quiet --package dbx-tools-release-tools -- stamp-tree --root "target/${{ matrix.cargo }}/release" --version "$VERSION"',
|
|
1486
|
-
},
|
|
1487
|
-
]
|
|
1488
|
-
: []),
|
|
1489
|
-
...(bindingCommands.length
|
|
1490
|
-
? [
|
|
1491
|
-
{
|
|
1492
|
-
name: "Package UniFFI outputs",
|
|
1493
|
-
shell: "bash",
|
|
1494
|
-
...(independentSetup ? {} : { env: { VERSION: RELEASE_VERSION } }),
|
|
1495
|
-
run: timedBash("uniffi_packaging", bindingCommands.join("\n")),
|
|
1496
|
-
},
|
|
1497
|
-
]
|
|
1498
|
-
: []),
|
|
1499
|
-
...(binaryCommands.length
|
|
1500
|
-
? [
|
|
1501
|
-
{
|
|
1502
|
-
name: "Package release binaries",
|
|
1503
|
-
shell: "bash",
|
|
1504
|
-
run: timedBash("binary_packaging", binaryCommands.join("\n")),
|
|
1505
|
-
},
|
|
1506
|
-
]
|
|
1507
|
-
: []),
|
|
1508
|
-
...(!independentSetup
|
|
1509
|
-
? [
|
|
1510
|
-
{
|
|
1511
|
-
name: "Upload reusable raw Rust outputs",
|
|
1512
|
-
uses: "actions/upload-artifact@v7",
|
|
1513
|
-
with: {
|
|
1514
|
-
name: "rust-${{ matrix.node }}-raw",
|
|
1515
|
-
path: "dist/rust-raw/*",
|
|
1516
|
-
"retention-days": 7,
|
|
1517
|
-
},
|
|
1518
|
-
},
|
|
1519
|
-
]
|
|
1520
|
-
: []),
|
|
1521
|
-
...rustArtifactSteps(plan),
|
|
1522
|
-
],
|
|
1523
|
-
};
|
|
1524
|
-
}
|
|
1525
|
-
|
|
1526
|
-
function rustCargoPublishJob(plan: RustReleasePlan, local: boolean): Job {
|
|
1527
|
-
const registry = local ? '"${{ vars.LOCAL_CARGO_REGISTRY }}"' : "crates-io";
|
|
1528
|
-
return {
|
|
1529
|
-
if: local
|
|
1530
|
-
? "${{ github.event_name == 'push' && vars.LOCAL_REPOSITORIES == 'true' }}"
|
|
1531
|
-
: "${{ github.event_name == 'push' }}",
|
|
1532
|
-
needs: ["verify-context", "rust-build"],
|
|
1533
|
-
runsOn: [local ? "self-hosted" : "ubuntu-latest"],
|
|
1534
|
-
permissions: { contents: JobPermission.READ },
|
|
1535
|
-
steps: [
|
|
1536
|
-
...releaseSourceSteps(),
|
|
1537
|
-
{
|
|
1538
|
-
name: "Setup Rust",
|
|
1539
|
-
uses: `dtolnay/rust-toolchain@${plan.releaseRustVersion}`,
|
|
1540
|
-
},
|
|
1541
|
-
{
|
|
1542
|
-
name: local ? "Publish Cargo crates locally" : "Publish public crates",
|
|
1543
|
-
env: {
|
|
1544
|
-
CARGO_REGISTRY_TOKEN: local
|
|
1545
|
-
? "${{ secrets.LOCAL_CARGO_TOKEN }}"
|
|
1546
|
-
: "${{ secrets.CARGO_REGISTRY_TOKEN }}",
|
|
1547
|
-
},
|
|
1548
|
-
run: plan.publicCrates
|
|
1549
|
-
.map((crate) => `cargo publish --package "${crate}" --registry ${registry} --no-verify`)
|
|
1550
|
-
.join("\n"),
|
|
1551
|
-
},
|
|
1552
|
-
],
|
|
1553
|
-
};
|
|
1554
|
-
}
|
|
1555
|
-
|
|
1556
|
-
function independentRustCargoPublishJob(
|
|
1557
|
-
project: DBXToolsJavaScriptProject,
|
|
1558
|
-
plan: RustReleasePlan,
|
|
1559
|
-
): Job {
|
|
1560
|
-
const commands = plan.publicCrates.map((crate) =>
|
|
1561
|
-
[
|
|
1562
|
-
`VERSION="$(jq -r --arg package "${crate}" '.rustPackages[] | select(.identity == $package) | .version' dist/release-plan.json)"`,
|
|
1563
|
-
'if [ -n "$VERSION" ]; then',
|
|
1564
|
-
` if cargo info "${crate}@$VERSION" >/dev/null 2>&1; then`,
|
|
1565
|
-
` echo "skip published ${crate}@$VERSION"`,
|
|
1566
|
-
" else",
|
|
1567
|
-
` cargo publish --package "${crate}" --registry crates-io --no-verify`,
|
|
1568
|
-
" fi",
|
|
1569
|
-
"fi",
|
|
1570
|
-
].join("\n"),
|
|
1571
|
-
);
|
|
1572
|
-
return {
|
|
1573
|
-
if: plan.hasTargetOutputs
|
|
1574
|
-
? "${{ always() && needs.release-plan.outputs.rust == 'true' && needs.rust-build.result != 'failure' && needs.rust-build.result != 'cancelled' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'rust') }}"
|
|
1575
|
-
: "${{ needs.release-plan.outputs.rust == 'true' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'rust') }}",
|
|
1576
|
-
needs: ["release-plan", ...(plan.hasTargetOutputs ? ["rust-build"] : [])],
|
|
1577
|
-
runsOn: ["ubuntu-latest"],
|
|
1578
|
-
permissions: { contents: JobPermission.READ },
|
|
1579
|
-
env: { BUN_VERSION },
|
|
1580
|
-
steps: [
|
|
1581
|
-
...independentReleaseSetupSteps(project),
|
|
1582
|
-
{
|
|
1583
|
-
name: "Setup Rust",
|
|
1584
|
-
uses: `dtolnay/rust-toolchain@${plan.releaseRustVersion}`,
|
|
1585
|
-
},
|
|
1586
|
-
{
|
|
1587
|
-
name: "Publish affected Cargo crates",
|
|
1588
|
-
env: { CARGO_REGISTRY_TOKEN: "${{ secrets.CARGO_REGISTRY_TOKEN }}" },
|
|
1589
|
-
run: commands.join("\n"),
|
|
1590
|
-
},
|
|
1591
|
-
],
|
|
1592
|
-
};
|
|
1593
|
-
}
|
|
1594
|
-
|
|
1595
|
-
function independentRustGitHubReleaseJob(
|
|
1596
|
-
project: DBXToolsJavaScriptProject,
|
|
1597
|
-
plan: RustReleasePlan,
|
|
1598
|
-
): Job {
|
|
1599
|
-
return {
|
|
1600
|
-
if: "${{ needs.release-plan.outputs.github == 'true' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'github') }}",
|
|
1601
|
-
needs: ["release-plan", "rust-build"],
|
|
1602
|
-
runsOn: ["ubuntu-latest"],
|
|
1603
|
-
permissions: { contents: JobPermission.WRITE },
|
|
1604
|
-
env: { BUN_VERSION },
|
|
1605
|
-
steps: [
|
|
1606
|
-
...independentReleaseSetupSteps(project),
|
|
1607
|
-
{
|
|
1608
|
-
name: "Download release binaries",
|
|
1609
|
-
uses: "actions/download-artifact@v8",
|
|
1610
|
-
with: {
|
|
1611
|
-
pattern: "*-binary",
|
|
1612
|
-
path: "dist/rust-release",
|
|
1613
|
-
"merge-multiple": true,
|
|
1614
|
-
},
|
|
1615
|
-
},
|
|
1616
|
-
{
|
|
1617
|
-
name: "Download reusable raw Rust outputs",
|
|
1618
|
-
uses: "actions/download-artifact@v8",
|
|
1619
|
-
with: {
|
|
1620
|
-
pattern: "*-raw",
|
|
1621
|
-
path: "dist/rust-raw",
|
|
1622
|
-
"merge-multiple": true,
|
|
1623
|
-
},
|
|
1624
|
-
},
|
|
1625
|
-
{
|
|
1626
|
-
name: "Upload affected GitHub release assets",
|
|
1627
|
-
env: { GH_TOKEN: "${{ github.token }}" },
|
|
1628
|
-
shell: "bash",
|
|
1629
|
-
run: plan.releaseBinaries
|
|
1630
|
-
.map((binary) => {
|
|
1631
|
-
const unit = defaultReleaseUnitId("rust", binary.crate);
|
|
1632
|
-
return [
|
|
1633
|
-
`VERSION="$(jq -r --arg unit "${unit}" '.units[] | select(.id == $unit) | .newVersion' dist/release-plan.json)"`,
|
|
1634
|
-
'if [ -n "$VERSION" ]; then',
|
|
1635
|
-
` gh release upload "${unit}-v$VERSION" dist/rust-release/${binary.binary}-* --clobber`,
|
|
1636
|
-
"fi",
|
|
1637
|
-
].join("\n");
|
|
1638
|
-
})
|
|
1639
|
-
.join("\n"),
|
|
1640
|
-
},
|
|
1641
|
-
],
|
|
1642
|
-
};
|
|
1643
|
-
}
|
|
1644
|
-
|
|
1645
|
-
function rustGitHubReleaseJob(): Job {
|
|
1646
|
-
return {
|
|
1647
|
-
if: "${{ github.event_name == 'push' }}",
|
|
1648
|
-
needs: ["verify-context", "rust-build"],
|
|
1649
|
-
runsOn: ["ubuntu-latest"],
|
|
1650
|
-
permissions: { contents: JobPermission.WRITE },
|
|
1651
|
-
steps: [
|
|
1652
|
-
...releaseSourceSteps(),
|
|
1653
|
-
{
|
|
1654
|
-
name: "Download release binaries",
|
|
1655
|
-
uses: "actions/download-artifact@v8",
|
|
1656
|
-
with: {
|
|
1657
|
-
pattern: "*-binary",
|
|
1658
|
-
path: "dist/rust-release",
|
|
1659
|
-
"merge-multiple": true,
|
|
1660
|
-
},
|
|
1661
|
-
},
|
|
1662
|
-
{
|
|
1663
|
-
name: "Download reusable raw Rust outputs",
|
|
1664
|
-
uses: "actions/download-artifact@v8",
|
|
1665
|
-
with: {
|
|
1666
|
-
pattern: "*-raw",
|
|
1667
|
-
path: "dist/rust-raw",
|
|
1668
|
-
"merge-multiple": true,
|
|
1669
|
-
},
|
|
1670
|
-
},
|
|
1671
|
-
{
|
|
1672
|
-
name: "Publish GitHub release assets",
|
|
1673
|
-
uses: "softprops/action-gh-release@v2",
|
|
1674
|
-
with: {
|
|
1675
|
-
files: ["dist/rust-release/*", "dist/rust-raw/*"].join("\n"),
|
|
1676
|
-
body_path: RELEASE_SUMMARY_FILE,
|
|
1677
|
-
generate_release_notes: true,
|
|
1678
|
-
tag_name: RELEASE_TAG,
|
|
1679
|
-
target_commitish: RELEASE_SHA,
|
|
1680
|
-
},
|
|
1681
|
-
},
|
|
1682
|
-
],
|
|
1683
|
-
};
|
|
1684
|
-
}
|
|
1685
|
-
|
|
1686
|
-
function independentRustNativeNpmPublishJob(project: DBXToolsJavaScriptProject): Job {
|
|
1687
|
-
return {
|
|
1688
|
-
if: "${{ needs.release-plan.outputs.node == 'true' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'node') }}",
|
|
1689
|
-
needs: ["release-plan", "rust-build"],
|
|
1690
|
-
runsOn: ["ubuntu-latest"],
|
|
1691
|
-
permissions: { contents: JobPermission.READ, idToken: JobPermission.WRITE },
|
|
1692
|
-
timeoutMinutes: 15,
|
|
1693
|
-
env: { BUN_VERSION, CI: "true" },
|
|
1694
|
-
steps: [
|
|
1695
|
-
...independentReleaseSetupSteps(project),
|
|
1696
|
-
{
|
|
1697
|
-
name: "Download native npm packages",
|
|
1698
|
-
uses: "actions/download-artifact@v8",
|
|
1699
|
-
with: {
|
|
1700
|
-
pattern: "*-npm",
|
|
1701
|
-
path: "dist/uniffi/native",
|
|
1702
|
-
"merge-multiple": true,
|
|
1703
|
-
},
|
|
1704
|
-
},
|
|
1705
|
-
{
|
|
1706
|
-
name: "Publish affected native npm packages",
|
|
1707
|
-
env: npmPublishEnvironment(),
|
|
1708
|
-
run: "bun node_modules/@dbx-tools/projen/tasks/publish-npm.ts --directory dist/uniffi/native",
|
|
1709
|
-
},
|
|
1710
|
-
],
|
|
1711
|
-
};
|
|
1712
|
-
}
|
|
1713
|
-
|
|
1714
|
-
function independentRustNodeFacadePublishJob(
|
|
1715
|
-
project: DBXToolsJavaScriptProject,
|
|
1716
|
-
bindings: readonly RustBindingMapping[],
|
|
1717
|
-
): Job {
|
|
1718
|
-
return {
|
|
1719
|
-
if: "${{ needs.release-plan.outputs.node == 'true' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'node') }}",
|
|
1720
|
-
needs: ["release-plan", "publish-node", "publish-native-npm"],
|
|
1721
|
-
runsOn: ["ubuntu-latest"],
|
|
1722
|
-
permissions: { contents: JobPermission.READ, idToken: JobPermission.WRITE },
|
|
1723
|
-
timeoutMinutes: 30,
|
|
1724
|
-
env: { BUN_VERSION, CI: "true" },
|
|
1725
|
-
steps: [
|
|
1726
|
-
...independentReleaseSetupSteps(project),
|
|
1727
|
-
{
|
|
1728
|
-
name: "Build and publish affected UniFFI npm facades",
|
|
1729
|
-
env: npmPublishEnvironment(),
|
|
1730
|
-
shell: "bash",
|
|
1731
|
-
run: bindings
|
|
1732
|
-
.flatMap((binding) => {
|
|
1733
|
-
if (!binding.node || !binding.nodePackage) return [];
|
|
1734
|
-
const unit = defaultReleaseUnitId("rust", binding.crate);
|
|
1735
|
-
const output = `dist/uniffi/facades/${binding.crate}`;
|
|
1736
|
-
return [
|
|
1737
|
-
`VERSION="$(jq -r --arg unit "${unit}" '.units[] | select(.id == $unit) | .newVersion' dist/release-plan.json)"`,
|
|
1738
|
-
'if [ -n "$VERSION" ]; then',
|
|
1739
|
-
` node .projen/uniffi-release.mjs facade --node "${binding.node}" --node-package "${binding.nodePackage}" --node-triple "linux-x64-gnu" --version "$VERSION" --output "${output}"`,
|
|
1740
|
-
` bun node_modules/@dbx-tools/projen/tasks/publish-npm.ts --directory "${output}/npm-facade" --version "$VERSION"`,
|
|
1741
|
-
"fi",
|
|
1742
|
-
];
|
|
1743
|
-
})
|
|
1744
|
-
.join("\n"),
|
|
1745
|
-
},
|
|
1746
|
-
],
|
|
1747
|
-
};
|
|
1748
|
-
}
|
|
1749
|
-
|
|
1750
|
-
function rustNativeNpmPublishJob(project: DBXToolsJavaScriptProject): Job {
|
|
1751
|
-
return {
|
|
1752
|
-
if: "${{ always() && needs.verify-context.result == 'success' && needs.rust-build.result != 'failure' && needs.rust-build.result != 'cancelled' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'node') }}",
|
|
1753
|
-
needs: ["verify-context", "rust-build"],
|
|
1754
|
-
runsOn: ["ubuntu-latest"],
|
|
1755
|
-
permissions: {
|
|
1756
|
-
actions: JobPermission.READ,
|
|
1757
|
-
contents: JobPermission.READ,
|
|
1758
|
-
idToken: JobPermission.WRITE,
|
|
1759
|
-
},
|
|
1760
|
-
timeoutMinutes: 15,
|
|
1761
|
-
env: { BUN_VERSION, CI: "true" },
|
|
1762
|
-
steps: [
|
|
1763
|
-
...nodeReleaseSetupSteps(project),
|
|
1764
|
-
...releaseArtifactSteps({
|
|
1765
|
-
currentName: "Download native npm packages",
|
|
1766
|
-
recoveredName: "Download recovered native npm packages",
|
|
1767
|
-
pattern: "*-npm",
|
|
1768
|
-
path: "dist/uniffi/native",
|
|
1769
|
-
}),
|
|
1770
|
-
{
|
|
1771
|
-
name: "Publish native npm packages",
|
|
1772
|
-
env: { RELEASE_VERSION, ...npmPublishEnvironment() },
|
|
1773
|
-
run: 'bun node_modules/@dbx-tools/projen/tasks/publish-npm.ts --directory dist/uniffi/native --version "$RELEASE_VERSION" $DRY_RUN',
|
|
1774
|
-
},
|
|
1775
|
-
],
|
|
1776
|
-
};
|
|
1777
|
-
}
|
|
1778
|
-
|
|
1779
|
-
function rustNodeFacadePublishJob(
|
|
1780
|
-
project: DBXToolsJavaScriptProject,
|
|
1781
|
-
bindings: readonly RustReleaseBinding[],
|
|
1782
|
-
): Job {
|
|
1783
|
-
return {
|
|
1784
|
-
if: releaseStageCondition("node"),
|
|
1785
|
-
needs: ["verify-context", "publish-node"],
|
|
1786
|
-
runsOn: ["ubuntu-latest"],
|
|
1787
|
-
permissions: { contents: JobPermission.READ, idToken: JobPermission.WRITE },
|
|
1788
|
-
timeoutMinutes: 30,
|
|
1789
|
-
env: { BUN_VERSION, CI: "true" },
|
|
1790
|
-
steps: [
|
|
1791
|
-
...nodeReleaseSetupSteps(project),
|
|
1792
|
-
{
|
|
1793
|
-
name: "Build and publish UniFFI npm facades",
|
|
1794
|
-
env: { RELEASE_VERSION, ...npmPublishEnvironment() },
|
|
1795
|
-
run: bindings
|
|
1796
|
-
.flatMap((binding) => {
|
|
1797
|
-
const output = `dist/uniffi/facades/${binding.crate}`;
|
|
1798
|
-
return [
|
|
1799
|
-
`node .projen/uniffi-release.mjs facade --node "${binding.node}" --node-package "${binding.nodePackage}" --node-triple "linux-x64-gnu" --version "$RELEASE_VERSION" --output "${output}"`,
|
|
1800
|
-
`bun node_modules/@dbx-tools/projen/tasks/publish-npm.ts --directory "${output}/npm-facade" --version "$RELEASE_VERSION" $DRY_RUN`,
|
|
1801
|
-
];
|
|
1802
|
-
})
|
|
1803
|
-
.join("\n"),
|
|
1804
|
-
},
|
|
1805
|
-
{
|
|
1806
|
-
name: "Smoke test published UniFFI npm facades",
|
|
1807
|
-
if: "${{ github.event_name == 'push' && vars.UNIFFI_FACADE_SMOKE == 'true' }}",
|
|
1808
|
-
continueOnError: true,
|
|
1809
|
-
env: { RELEASE_VERSION },
|
|
1810
|
-
run: [
|
|
1811
|
-
'SMOKE_DIR="$(mktemp -d)"',
|
|
1812
|
-
"trap 'rm -rf \"$SMOKE_DIR\"' EXIT",
|
|
1813
|
-
'cd "$SMOKE_DIR"',
|
|
1814
|
-
"npm init --yes >/dev/null",
|
|
1815
|
-
...bindings.flatMap((binding) => [
|
|
1816
|
-
`npm install --ignore-scripts --no-audit --no-fund --package-lock=false "${binding.nodePackage}@$RELEASE_VERSION"`,
|
|
1817
|
-
`node -e 'import("${binding.nodePackage}")'`,
|
|
1818
|
-
]),
|
|
1819
|
-
].join("\n"),
|
|
1820
|
-
},
|
|
1821
|
-
],
|
|
1822
|
-
};
|
|
1823
|
-
}
|
|
1824
|
-
|
|
1825
818
|
function rustReleaseDependencies(
|
|
1826
819
|
root: DBXToolsJavaScriptProject,
|
|
1827
820
|
project: DBXToolsRustProject,
|
|
@@ -1877,7 +870,7 @@ function configureIndependentRustVersions(
|
|
|
1877
870
|
}
|
|
1878
871
|
|
|
1879
872
|
/** Generated Rust workspace plus convention-derived UniFFI binding packages. */
|
|
1880
|
-
export class DBXToolsRustWorkspace {
|
|
873
|
+
export class DBXToolsRustWorkspace extends Component {
|
|
1881
874
|
readonly packages: readonly DBXToolsRustProject[];
|
|
1882
875
|
readonly nodePackages: readonly DBXToolsTypeScriptProject[];
|
|
1883
876
|
readonly pythonPackages: readonly PythonPackageOptions[];
|
|
@@ -1886,6 +879,7 @@ export class DBXToolsRustWorkspace {
|
|
|
1886
879
|
readonly workspaceMapping: RustWorkspaceMapping;
|
|
1887
880
|
|
|
1888
881
|
constructor(project: javascript.NodeProject, options: DBXToolsRustWorkspaceOptions) {
|
|
882
|
+
super(project);
|
|
1889
883
|
const resolved = resolveRustWorkspaceOptions(project, options);
|
|
1890
884
|
this.packages = createRustPackages(project, resolved);
|
|
1891
885
|
const packageDependencies = createRustPackageDependencyResolver(
|
|
@@ -1899,7 +893,7 @@ export class DBXToolsRustWorkspace {
|
|
|
1899
893
|
language: "rust",
|
|
1900
894
|
identity: pkg.crateName,
|
|
1901
895
|
publish: !pkg.packageOptions.private || pkg.packageOptions.release === true,
|
|
1902
|
-
sourcePaths: [`${relative(project.outdir, pkg.outdir)
|
|
896
|
+
sourcePaths: [`${toPosix(relative(project.outdir, pkg.outdir))}/src`],
|
|
1903
897
|
dependencies: [
|
|
1904
898
|
...rustReleaseDependencies(
|
|
1905
899
|
project,
|
|
@@ -2003,7 +997,7 @@ export class DBXToolsRustWorkspace {
|
|
|
2003
997
|
configureRustWorkspaceFiles(project, this.packages, options, resolved);
|
|
2004
998
|
configureRustWorkspaceTasks(project);
|
|
2005
999
|
if (releaseEnabled) {
|
|
2006
|
-
this.addReleaseWorkflow(project, options, resolved.nativeTargets);
|
|
1000
|
+
this.addReleaseWorkflow(project, options, resolved.nativeTargets, packageDependencies);
|
|
2007
1001
|
}
|
|
2008
1002
|
}
|
|
2009
1003
|
|
|
@@ -2011,9 +1005,17 @@ export class DBXToolsRustWorkspace {
|
|
|
2011
1005
|
project: javascript.NodeProject,
|
|
2012
1006
|
options: DBXToolsRustWorkspaceOptions,
|
|
2013
1007
|
targets: readonly UniFFIReleaseTarget[],
|
|
1008
|
+
packageDependencies: RustPackageDependencyResolver,
|
|
2014
1009
|
): void {
|
|
2015
1010
|
if (!project.github || !isDBXToolsJavaScriptProject()(project)) return;
|
|
2016
|
-
const plan = planRustRelease(
|
|
1011
|
+
const plan = planRustRelease(
|
|
1012
|
+
project,
|
|
1013
|
+
options,
|
|
1014
|
+
targets,
|
|
1015
|
+
this.packages,
|
|
1016
|
+
this.bindingMappings,
|
|
1017
|
+
packageDependencies,
|
|
1018
|
+
);
|
|
2017
1019
|
const workflow = tryReleaseWorkflow(project);
|
|
2018
1020
|
if (!workflow) {
|
|
2019
1021
|
throw new Error("Rust release requires the root dbx-tools release mode");
|