@dbx-tools/projen 0.9.12 → 0.9.18
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 +33 -25
- package/index.ts +5 -4
- package/package.json +4 -5
- package/src/_release-platform.ts +10 -0
- package/src/_rust-project.ts +40 -13
- package/src/_rust-release-workflow.ts +213 -344
- package/src/openapi.ts +191 -79
- package/src/project-js.ts +8 -13
- package/src/project-py.ts +22 -3
- package/src/project-rs.ts +50 -6
- package/src/release-github.ts +23 -0
- package/src/release-plan.ts +48 -15
- package/src/release.ts +17 -3
- package/src/watch.ts +19 -2
- package/src/workspace-lock.ts +32 -6
- package/tasks/barrels.ts +47 -30
- package/tasks/compile-workspace.ts +121 -0
- package/tasks/openapi.ts +15 -11
- package/tasks/release-assets.ts +476 -0
- package/tasks/release-pr.ts +123 -14
- package/tasks/rust.ts +62 -33
- package/tasks/uniffi-python.js +24 -12
- package/tasks/uniffi-release.mjs +40 -3
- package/tasks/uniffi.ts +2 -0
- package/src/_rust-release.ts +0 -605
- package/tasks/build-rust-release.ts +0 -68
- package/tasks/rust-release.d.ts +0 -41
- package/tasks/rust-release.mjs +0 -1717
|
@@ -6,7 +6,8 @@ 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
|
|
9
|
+
import { releaseBinaryAssetName } from "./_release-platform.ts";
|
|
10
|
+
import { UNIFFI_BINDGEN_FEATURE, type RustProject } from "./_rust-project.ts";
|
|
10
11
|
import { BUN_VERSION } from "./bun-workflow.ts";
|
|
11
12
|
import type { DBXToolsJavaScriptProject } from "./project-js.ts";
|
|
12
13
|
import type {
|
|
@@ -35,29 +36,11 @@ const require = createRequire(import.meta.url);
|
|
|
35
36
|
|
|
36
37
|
type RustPackageDependencyResolver = (pkg: RustProject) => RustProject[];
|
|
37
38
|
|
|
38
|
-
const
|
|
39
|
+
const RUST_BUILD_ENV = {
|
|
39
40
|
CARGO_INCREMENTAL: "0",
|
|
40
41
|
CARGO_TERM_COLOR: "always",
|
|
41
42
|
} as const;
|
|
42
43
|
|
|
43
|
-
function rustCacheSteps(sharedKey: string): readonly JobStep[] {
|
|
44
|
-
return [
|
|
45
|
-
{
|
|
46
|
-
name: "Cache Cargo registry and dependencies",
|
|
47
|
-
id: "cargo_cache",
|
|
48
|
-
uses: "Swatinem/rust-cache@v2.9.2",
|
|
49
|
-
with: {
|
|
50
|
-
"cache-targets": true,
|
|
51
|
-
"cache-workspace-crates": true,
|
|
52
|
-
"add-job-id-key": false,
|
|
53
|
-
"add-rust-environment-hash-key": true,
|
|
54
|
-
"shared-key": sharedKey,
|
|
55
|
-
"save-if": "${{ github.event_name == 'push' }}",
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
];
|
|
59
|
-
}
|
|
60
|
-
|
|
61
44
|
function timedBash(phase: string, command: string): string {
|
|
62
45
|
return [
|
|
63
46
|
"SECONDS=0",
|
|
@@ -66,15 +49,6 @@ function timedBash(phase: string, command: string): string {
|
|
|
66
49
|
].join("\n");
|
|
67
50
|
}
|
|
68
51
|
|
|
69
|
-
export function releaseBinaryAssetName(
|
|
70
|
-
binaryName: string,
|
|
71
|
-
nodeTarget: string,
|
|
72
|
-
os: RustReleaseOs | "darwin" | "linux" | "win32",
|
|
73
|
-
): string {
|
|
74
|
-
const extension = os === "win32" ? "zip" : "tar.gz";
|
|
75
|
-
return `${binaryName}-${nodeTarget}.${extension}`;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
52
|
function taskSource(name: string): string {
|
|
79
53
|
const sourceDirectory = dirname(fileURLToPath(import.meta.url));
|
|
80
54
|
const candidates = [
|
|
@@ -117,39 +91,84 @@ export function orderRustBindings(bindings: readonly RustBindingMapping[]): Rust
|
|
|
117
91
|
return ordered;
|
|
118
92
|
}
|
|
119
93
|
|
|
120
|
-
interface RustReleaseBinding extends RustBindingMapping {
|
|
94
|
+
export interface RustReleaseBinding extends RustBindingMapping {
|
|
121
95
|
readonly node: string;
|
|
122
96
|
readonly python: string;
|
|
123
97
|
readonly nodePackage: string;
|
|
124
98
|
readonly pythonPackage: string;
|
|
125
99
|
}
|
|
126
100
|
|
|
127
|
-
interface RustReleaseBinaryPlan {
|
|
101
|
+
export interface RustReleaseBinaryPlan {
|
|
128
102
|
readonly crate: string;
|
|
129
103
|
readonly binary: string;
|
|
130
104
|
readonly excludedOs: readonly RustReleaseOs[];
|
|
131
105
|
readonly requiredFeatures: readonly string[];
|
|
132
106
|
}
|
|
133
107
|
|
|
134
|
-
|
|
108
|
+
export interface RustReleaseTargetPlan {
|
|
109
|
+
readonly runner: string;
|
|
110
|
+
readonly cargo: string;
|
|
111
|
+
readonly node: string;
|
|
112
|
+
readonly python: string;
|
|
113
|
+
readonly os: RustReleaseOs;
|
|
114
|
+
readonly cpu: string;
|
|
115
|
+
readonly libc: string;
|
|
116
|
+
readonly glibcVersion: string;
|
|
117
|
+
readonly packages: readonly string[];
|
|
118
|
+
readonly binaries: readonly string[];
|
|
119
|
+
readonly features: readonly string[];
|
|
120
|
+
readonly localFeatures: readonly string[];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Generated release inputs shared by GitHub-hosted and local target builds. */
|
|
124
|
+
export interface RustReleaseConfiguration {
|
|
125
|
+
readonly releaseRustVersion: string;
|
|
126
|
+
readonly usesCargoLock: boolean;
|
|
127
|
+
readonly bindings: readonly RustReleaseBinding[];
|
|
128
|
+
readonly binaries: readonly RustReleaseBinaryPlan[];
|
|
129
|
+
readonly targets: readonly RustReleaseTargetPlan[];
|
|
130
|
+
}
|
|
135
131
|
|
|
136
132
|
export interface RustReleasePlan {
|
|
137
133
|
readonly releaseRustVersion: string;
|
|
138
134
|
readonly releaseTask: string;
|
|
139
|
-
readonly releaseHelper: string;
|
|
140
|
-
readonly rustRoot: string;
|
|
141
135
|
readonly bindings: readonly RustReleaseBinding[];
|
|
142
136
|
readonly nodeBindings: readonly RustReleaseBinding[];
|
|
143
137
|
readonly releaseBinaries: readonly RustReleaseBinaryPlan[];
|
|
144
138
|
readonly publicCrates: readonly string[];
|
|
145
139
|
readonly targets: readonly RustReleaseTargetPlan[];
|
|
146
|
-
readonly hasReleaseExclusions: boolean;
|
|
147
140
|
readonly hasPythonBindings: boolean;
|
|
148
141
|
readonly usesCargoLock: boolean;
|
|
149
142
|
readonly usePreinstalledWindowsRust: boolean;
|
|
150
143
|
readonly hasTargetOutputs: boolean;
|
|
151
144
|
}
|
|
152
145
|
|
|
146
|
+
function releaseTargetPlan(
|
|
147
|
+
target: UniFFIReleaseTarget,
|
|
148
|
+
packages: readonly RustProject[],
|
|
149
|
+
binaries: readonly string[],
|
|
150
|
+
features: readonly string[],
|
|
151
|
+
localFeatures: readonly string[],
|
|
152
|
+
): RustReleaseTargetPlan {
|
|
153
|
+
const packageNames = packages.map((pkg) => pkg.crateName).sort();
|
|
154
|
+
const sortedBinaries = [...new Set(binaries)].sort();
|
|
155
|
+
const sortedFeatures = [...new Set(features)].sort();
|
|
156
|
+
return {
|
|
157
|
+
runner: target.runner,
|
|
158
|
+
cargo: target.cargo,
|
|
159
|
+
node: target.node,
|
|
160
|
+
python: target.python,
|
|
161
|
+
os: target.os,
|
|
162
|
+
cpu: target.cpu,
|
|
163
|
+
libc: target.libc ?? "",
|
|
164
|
+
glibcVersion: target.glibcVersion ?? "",
|
|
165
|
+
packages: packageNames,
|
|
166
|
+
binaries: sortedBinaries,
|
|
167
|
+
features: sortedFeatures,
|
|
168
|
+
localFeatures: [...new Set(localFeatures)].sort(),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
153
172
|
export function planRustRelease(
|
|
154
173
|
project: javascript.NodeProject,
|
|
155
174
|
options: DBXToolsRustWorkspaceOptions,
|
|
@@ -195,39 +214,51 @@ export function planRustRelease(
|
|
|
195
214
|
dependencies: packageDependencies(pkg).map((dependency) => dependency.crateName),
|
|
196
215
|
})),
|
|
197
216
|
).map((pkg) => pkg.crate);
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
...(
|
|
212
|
-
...(
|
|
213
|
-
|
|
217
|
+
const bindingPackages = packages.filter((pkg) =>
|
|
218
|
+
bindings.some((binding) => binding.crate === pkg.crateName),
|
|
219
|
+
);
|
|
220
|
+
const releaseTargets = targets.flatMap((target) => {
|
|
221
|
+
const selectedBinaries = releaseBinaries.filter(
|
|
222
|
+
(binary) => !binary.excludedOs.includes(target.os),
|
|
223
|
+
);
|
|
224
|
+
const binaryPackages = packages.filter((pkg) =>
|
|
225
|
+
selectedBinaries.some((binary) => binary.crate === pkg.crateName),
|
|
226
|
+
);
|
|
227
|
+
const selectedPackages = [...new Set([...bindingPackages, ...binaryPackages])];
|
|
228
|
+
if (!selectedPackages.length) return [];
|
|
229
|
+
const features = [
|
|
230
|
+
...bindingPackages.map((pkg) => `${pkg.crateName}/${UNIFFI_BINDGEN_FEATURE}`),
|
|
231
|
+
...selectedBinaries.flatMap((binary) =>
|
|
232
|
+
binary.requiredFeatures.map((feature) => `${binary.crate}/${feature}`),
|
|
233
|
+
),
|
|
234
|
+
];
|
|
235
|
+
const localFeatures = selectedPackages.flatMap((pkg) =>
|
|
236
|
+
(pkg.packageOptions.releaseLocalFeatures ?? []).map(
|
|
237
|
+
(feature) => `${pkg.crateName}/${feature}`,
|
|
238
|
+
),
|
|
239
|
+
);
|
|
240
|
+
return [
|
|
241
|
+
releaseTargetPlan(
|
|
242
|
+
target,
|
|
243
|
+
selectedPackages,
|
|
244
|
+
selectedBinaries.map((binary) => binary.binary),
|
|
245
|
+
features,
|
|
246
|
+
localFeatures,
|
|
247
|
+
),
|
|
248
|
+
];
|
|
214
249
|
});
|
|
215
|
-
const hasTargetOutputs =
|
|
216
|
-
|
|
217
|
-
if (hasTargetOutputs && plannedTargets.length === 0) {
|
|
250
|
+
const hasTargetOutputs = releaseTargets.length > 0;
|
|
251
|
+
if (hasTargetOutputs && targets.length === 0) {
|
|
218
252
|
throw new Error("Rust release requires at least one target");
|
|
219
253
|
}
|
|
220
254
|
return {
|
|
221
255
|
releaseRustVersion,
|
|
222
256
|
releaseTask: ".projen/uniffi-release.mjs",
|
|
223
|
-
releaseHelper: ".projen/rust-release.mjs",
|
|
224
|
-
rustRoot: options.root ?? "packages/rs",
|
|
225
257
|
bindings,
|
|
226
258
|
nodeBindings: bindings.filter((binding) => Boolean(binding.node && binding.nodePackage)),
|
|
227
259
|
releaseBinaries,
|
|
228
260
|
publicCrates,
|
|
229
|
-
targets:
|
|
230
|
-
hasReleaseExclusions,
|
|
261
|
+
targets: releaseTargets,
|
|
231
262
|
hasPythonBindings: bindings.some((binding) => binding.python),
|
|
232
263
|
usesCargoLock: existsSync(join(project.outdir, "Cargo.lock")),
|
|
233
264
|
usePreinstalledWindowsRust: releaseRustVersion === "stable",
|
|
@@ -235,19 +266,39 @@ export function planRustRelease(
|
|
|
235
266
|
};
|
|
236
267
|
}
|
|
237
268
|
|
|
269
|
+
function rustBuildJobIds(plan: RustReleasePlan): string[] {
|
|
270
|
+
return plan.targets.length ? ["rust-build"] : [];
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function rustBuildResultCondition(plan: RustReleasePlan): string {
|
|
274
|
+
return rustBuildJobIds(plan)
|
|
275
|
+
.map((job) => `needs.${job}.result != 'failure' && needs.${job}.result != 'cancelled'`)
|
|
276
|
+
.join(" && ");
|
|
277
|
+
}
|
|
278
|
+
|
|
238
279
|
export function configureRustReleaseTask(
|
|
239
280
|
project: javascript.NodeProject,
|
|
240
281
|
plan: RustReleasePlan,
|
|
241
282
|
): void {
|
|
283
|
+
new TextFile(project, ".projen/rust-release.json", {
|
|
284
|
+
lines: JSON.stringify(
|
|
285
|
+
{
|
|
286
|
+
releaseRustVersion: plan.releaseRustVersion,
|
|
287
|
+
usesCargoLock: plan.usesCargoLock,
|
|
288
|
+
bindings: plan.bindings,
|
|
289
|
+
binaries: plan.releaseBinaries,
|
|
290
|
+
targets: plan.targets,
|
|
291
|
+
} satisfies RustReleaseConfiguration,
|
|
292
|
+
null,
|
|
293
|
+
2,
|
|
294
|
+
).split("\n"),
|
|
295
|
+
});
|
|
242
296
|
const bindingSupportFiles = [
|
|
243
297
|
plan.releaseTask,
|
|
244
298
|
".projen/uniffi-python.js",
|
|
245
299
|
".projen/smol-toml.cjs",
|
|
246
300
|
".projen/smol-toml.LICENSE",
|
|
247
301
|
] as const;
|
|
248
|
-
new TextFile(project, plan.releaseHelper, {
|
|
249
|
-
lines: taskSource("rust-release.mjs").trimEnd().split("\n"),
|
|
250
|
-
});
|
|
251
302
|
if (plan.bindings.length) {
|
|
252
303
|
new TextFile(project, plan.releaseTask, {
|
|
253
304
|
lines: taskSource("uniffi-release.mjs").trimEnd().split("\n"),
|
|
@@ -264,20 +315,7 @@ export function configureRustReleaseTask(
|
|
|
264
315
|
} else {
|
|
265
316
|
for (const path of bindingSupportFiles) project.tryRemoveFile(path);
|
|
266
317
|
}
|
|
267
|
-
project.
|
|
268
|
-
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
|
-
});
|
|
318
|
+
project.tryRemoveFile(".projen/rust-release.mjs");
|
|
281
319
|
}
|
|
282
320
|
|
|
283
321
|
function rustBindingCommands(plan: RustReleasePlan, independent: boolean): string[] {
|
|
@@ -352,29 +390,6 @@ function rustBinaryCommands(plan: RustReleasePlan, independent: boolean): string
|
|
|
352
390
|
});
|
|
353
391
|
}
|
|
354
392
|
|
|
355
|
-
function rustAdditionalBinaryBuildCommands(plan: RustReleasePlan, independent: boolean): string[] {
|
|
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
|
-
}
|
|
377
|
-
|
|
378
393
|
function rustArtifactSteps(plan: RustReleasePlan): JobStep[] {
|
|
379
394
|
const binaryCrates = [...new Set(plan.releaseBinaries.map((binary) => binary.crate))];
|
|
380
395
|
return [
|
|
@@ -428,23 +443,37 @@ function rustArtifactSteps(plan: RustReleasePlan): JobStep[] {
|
|
|
428
443
|
];
|
|
429
444
|
}
|
|
430
445
|
|
|
446
|
+
/** Build a Bash loop that preserves matrix values across LF and CRLF runners. */
|
|
447
|
+
function matrixArgumentLoop(variable: string, field: string, flag: string): string {
|
|
448
|
+
return `while IFS= read -r ${variable}; do ${variable}="\${${variable}%$'\\r'}"; ${variable}_ARGS+=(${flag} "$${variable}"); done < <(jq -r '.[]' <<<'\${{ toJSON(matrix.${field}) }}')`;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function rustCargoBuildCommand(plan: RustReleasePlan): string {
|
|
452
|
+
return [
|
|
453
|
+
"PACKAGE_ARGS=()",
|
|
454
|
+
"FEATURE_ARGS=()",
|
|
455
|
+
matrixArgumentLoop("PACKAGE", "packages", "--package"),
|
|
456
|
+
matrixArgumentLoop("FEATURE", "features", "--features"),
|
|
457
|
+
`cargo build --release --timings "\${PACKAGE_ARGS[@]}" "\${FEATURE_ARGS[@]}"${
|
|
458
|
+
plan.usesCargoLock ? " --locked" : ""
|
|
459
|
+
} --target "\${{ matrix.cargo }}"`,
|
|
460
|
+
].join("\n");
|
|
461
|
+
}
|
|
462
|
+
|
|
431
463
|
export function rustBuildJob(plan: RustReleasePlan, independentSetup?: readonly JobStep[]): Job {
|
|
432
|
-
const
|
|
433
|
-
const
|
|
434
|
-
const
|
|
435
|
-
plan,
|
|
436
|
-
Boolean(independentSetup),
|
|
437
|
-
);
|
|
464
|
+
const independent = Boolean(independentSetup);
|
|
465
|
+
const bindingCommands = rustBindingCommands(plan, independent);
|
|
466
|
+
const binaryCommands = rustBinaryCommands(plan, independent);
|
|
438
467
|
return {
|
|
439
468
|
if: independentSetup
|
|
440
469
|
? "${{ needs.release-plan.outputs.rust_targets != '[]' && (github.event_name == 'push' || inputs.stage != 'docs') }}"
|
|
441
|
-
: "${{ github.event_name == 'push' || inputs.stage == 'all' }}",
|
|
442
|
-
name: "${{ matrix.node }}",
|
|
470
|
+
: "${{ needs.verify-context.outputs.build_mode == 'remote' && (github.event_name == 'push' || inputs.stage == 'all') }}",
|
|
471
|
+
name: "Rust / ${{ matrix.node }}",
|
|
443
472
|
needs: [independentSetup ? "release-plan" : "verify-context"],
|
|
444
473
|
runsOn: ["${{ matrix.runner }}"],
|
|
445
474
|
permissions: { contents: JobPermission.READ },
|
|
446
475
|
env: {
|
|
447
|
-
...
|
|
476
|
+
...RUST_BUILD_ENV,
|
|
448
477
|
...(independentSetup ? { BUN_VERSION } : {}),
|
|
449
478
|
},
|
|
450
479
|
strategy: {
|
|
@@ -452,7 +481,7 @@ export function rustBuildJob(plan: RustReleasePlan, independentSetup?: readonly
|
|
|
452
481
|
matrix: {
|
|
453
482
|
include: independentSetup
|
|
454
483
|
? ("${{ fromJSON(needs.release-plan.outputs.rust_targets) }}" as never)
|
|
455
|
-
: [...plan.targets],
|
|
484
|
+
: ([...plan.targets] as never),
|
|
456
485
|
},
|
|
457
486
|
},
|
|
458
487
|
steps: [
|
|
@@ -460,168 +489,28 @@ export function rustBuildJob(plan: RustReleasePlan, independentSetup?: readonly
|
|
|
460
489
|
...(plan.hasPythonBindings ? [{ name: "Setup uv", uses: "astral-sh/setup-uv@v7" }] : []),
|
|
461
490
|
{
|
|
462
491
|
name: "Setup Rust",
|
|
463
|
-
...(plan.usePreinstalledWindowsRust ? { if: "${{ matrix.os != 'win32' }}" } : {}),
|
|
464
492
|
uses: `dtolnay/rust-toolchain@${plan.releaseRustVersion}`,
|
|
465
493
|
with: { targets: "${{ matrix.cargo }}" },
|
|
466
494
|
},
|
|
467
|
-
...(plan.usePreinstalledWindowsRust
|
|
468
|
-
? [
|
|
469
|
-
{
|
|
470
|
-
name: "Verify preinstalled Windows Rust",
|
|
471
|
-
if: "${{ matrix.os == 'win32' }}",
|
|
472
|
-
shell: "bash",
|
|
473
|
-
run: [
|
|
474
|
-
"rustc --version --verbose",
|
|
475
|
-
"cargo --version",
|
|
476
|
-
'rustup target list --installed | grep -Fx "${{ matrix.cargo }}"',
|
|
477
|
-
'test -f "$(rustc --print sysroot)/lib/rustlib/${{ matrix.cargo }}/bin/rust-lld.exe"',
|
|
478
|
-
].join("\n"),
|
|
479
|
-
},
|
|
480
|
-
]
|
|
481
|
-
: []),
|
|
482
|
-
{
|
|
483
|
-
name: "Install LLVM release tools",
|
|
484
|
-
shell: "bash",
|
|
485
|
-
run: "rustup component add llvm-tools-preview",
|
|
486
|
-
},
|
|
487
|
-
...rustCacheSteps(`release-\${{ matrix.cargo }}-rust-${plan.releaseRustVersion}`),
|
|
488
495
|
{
|
|
489
496
|
name: "Install Linux native dependencies",
|
|
490
497
|
if: "${{ matrix.os == 'linux' }}",
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
498
|
+
// prettier-ignore
|
|
499
|
+
run: stringUtils.dedent(
|
|
500
|
+
// ============================================================================
|
|
501
|
+
/*bash*/`
|
|
502
|
+
sudo rm -f /etc/apt/sources.list.d/google-chrome.list
|
|
503
|
+
sudo apt-get update
|
|
504
|
+
sudo apt-get install --yes libdbus-1-dev pkg-config
|
|
505
|
+
`
|
|
506
|
+
// ============================================================================
|
|
507
|
+
),
|
|
496
508
|
},
|
|
497
509
|
{
|
|
498
|
-
name: "
|
|
499
|
-
if: independentSetup
|
|
500
|
-
? undefined
|
|
501
|
-
: "${{ github.event_name == 'push' || inputs.stage == 'all' }}",
|
|
502
|
-
...(!independentSetup ? { id: "rust-fingerprint" } : {}),
|
|
510
|
+
name: "Build Rust release outputs",
|
|
503
511
|
shell: "bash",
|
|
504
|
-
run:
|
|
505
|
-
? [
|
|
506
|
-
`node ${plan.releaseHelper} fingerprint --check --root .`,
|
|
507
|
-
'--target "${{ matrix.cargo }}|${{ matrix.cargoExcludes }}"',
|
|
508
|
-
`--toolchain ${JSON.stringify(plan.releaseRustVersion)}`,
|
|
509
|
-
`--source ${JSON.stringify(plan.rustRoot)}`,
|
|
510
|
-
"--portable",
|
|
511
|
-
].join(" ")
|
|
512
|
-
: [
|
|
513
|
-
'RUNTIME_MANIFEST="dist/rust-raw/rust-build-${{ matrix.node }}.json"',
|
|
514
|
-
'mkdir -p "$(dirname "$RUNTIME_MANIFEST")"',
|
|
515
|
-
[
|
|
516
|
-
`node ${plan.releaseHelper} fingerprint --root .`,
|
|
517
|
-
'--output "$RUNTIME_MANIFEST"',
|
|
518
|
-
'--target "${{ matrix.cargo }}|${{ matrix.cargoExcludes }}"',
|
|
519
|
-
`--toolchain ${JSON.stringify(plan.releaseRustVersion)}`,
|
|
520
|
-
`--source ${JSON.stringify(plan.rustRoot)}`,
|
|
521
|
-
].join(" "),
|
|
522
|
-
'test "$(jq -r .rustSourceHash "$RUNTIME_MANIFEST")" = "$(jq -r .rustSourceHash .release/rust-build.json)"',
|
|
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"),
|
|
512
|
+
run: timedBash("rust", rustCargoBuildCommand(plan)),
|
|
527
513
|
},
|
|
528
|
-
...(!independentSetup
|
|
529
|
-
? [
|
|
530
|
-
{
|
|
531
|
-
name: "Reuse matching raw Rust outputs",
|
|
532
|
-
id: "raw-native",
|
|
533
|
-
shell: "bash",
|
|
534
|
-
env: { GH_TOKEN: "${{ github.token }}" },
|
|
535
|
-
// prettier-ignore
|
|
536
|
-
run: stringUtils.dedent(
|
|
537
|
-
// ============================================================================
|
|
538
|
-
/*bash*/`
|
|
539
|
-
KEY="\${{ steps.rust-fingerprint.outputs.key }}"
|
|
540
|
-
test -n "$KEY"
|
|
541
|
-
ASSET="rust-raw-\${{ matrix.node }}-$KEY.tar.gz"
|
|
542
|
-
mkdir -p dist/rust-raw
|
|
543
|
-
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
|
-
if [ -n "$MATCH" ]; then
|
|
545
|
-
TAG="\${MATCH%%$'\\t'*}"
|
|
546
|
-
URL="\${MATCH#*$'\\t'}"
|
|
547
|
-
CHECKSUM_URL="$(gh api "repos/\${{ github.repository }}/releases/tags/$TAG" --jq '.assets[] | select(.name == "'"$ASSET.sha256"'") | .url')"
|
|
548
|
-
rm -rf "target/\${{ matrix.cargo }}/release"
|
|
549
|
-
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
|
|
550
|
-
echo "hit=true" >> "$GITHUB_OUTPUT"
|
|
551
|
-
else
|
|
552
|
-
rm -rf "target/\${{ matrix.cargo }}/release" "dist/rust-raw/$ASSET" "dist/rust-raw/$ASSET.sha256"
|
|
553
|
-
echo "::warning::matching raw Rust bundle failed validation; rebuilding"
|
|
554
|
-
echo "hit=false" >> "$GITHUB_OUTPUT"
|
|
555
|
-
fi
|
|
556
|
-
else
|
|
557
|
-
echo "hit=false" >> "$GITHUB_OUTPUT"
|
|
558
|
-
fi
|
|
559
|
-
echo "asset=$ASSET" >> "$GITHUB_OUTPUT"
|
|
560
|
-
`
|
|
561
|
-
// ============================================================================
|
|
562
|
-
),
|
|
563
|
-
},
|
|
564
|
-
]
|
|
565
|
-
: []),
|
|
566
|
-
{
|
|
567
|
-
name: "Build Rust outputs",
|
|
568
|
-
...(independentSetup ? {} : { if: "${{ steps.raw-native.outputs.hit != 'true' }}" }),
|
|
569
|
-
shell: "bash",
|
|
570
|
-
env: {
|
|
571
|
-
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER:
|
|
572
|
-
"${{ matrix.os == 'win32' && 'rust-lld' || '' }}",
|
|
573
|
-
},
|
|
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
|
-
),
|
|
590
|
-
},
|
|
591
|
-
...(!independentSetup
|
|
592
|
-
? [
|
|
593
|
-
{
|
|
594
|
-
name: "Upload Cargo timings",
|
|
595
|
-
if: "${{ steps.raw-native.outputs.hit != 'true' }}",
|
|
596
|
-
uses: "actions/upload-artifact@v7",
|
|
597
|
-
with: {
|
|
598
|
-
name: "rust-${{ matrix.node }}-cargo-timings",
|
|
599
|
-
path: "target/cargo-timings/*",
|
|
600
|
-
"retention-days": 14,
|
|
601
|
-
},
|
|
602
|
-
},
|
|
603
|
-
]
|
|
604
|
-
: []),
|
|
605
|
-
...(!independentSetup
|
|
606
|
-
? [
|
|
607
|
-
{
|
|
608
|
-
name: "Archive raw Rust outputs",
|
|
609
|
-
if: "${{ steps.raw-native.outputs.hit != 'true' }}",
|
|
610
|
-
shell: "bash",
|
|
611
|
-
run: [
|
|
612
|
-
"mkdir -p dist/rust-raw",
|
|
613
|
-
'tar -czf "dist/rust-raw/${{ steps.raw-native.outputs.asset }}" "target/${{ matrix.cargo }}/release"',
|
|
614
|
-
'(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")',
|
|
615
|
-
].join("\n"),
|
|
616
|
-
},
|
|
617
|
-
{
|
|
618
|
-
name: "Stamp native release version",
|
|
619
|
-
shell: "bash",
|
|
620
|
-
env: { VERSION: RELEASE_VERSION },
|
|
621
|
-
run: `node ${plan.releaseHelper} stamp-tree --root "target/\${{ matrix.cargo }}/release" --version "$VERSION"`,
|
|
622
|
-
},
|
|
623
|
-
]
|
|
624
|
-
: []),
|
|
625
514
|
...(bindingCommands.length
|
|
626
515
|
? [
|
|
627
516
|
{
|
|
@@ -641,31 +530,53 @@ export function rustBuildJob(plan: RustReleasePlan, independentSetup?: readonly
|
|
|
641
530
|
},
|
|
642
531
|
]
|
|
643
532
|
: []),
|
|
644
|
-
...(!independentSetup
|
|
645
|
-
? [
|
|
646
|
-
{
|
|
647
|
-
name: "Upload reusable raw Rust outputs",
|
|
648
|
-
uses: "actions/upload-artifact@v7",
|
|
649
|
-
with: {
|
|
650
|
-
name: "rust-${{ matrix.node }}-raw",
|
|
651
|
-
path: "dist/rust-raw/*",
|
|
652
|
-
"retention-days": 7,
|
|
653
|
-
},
|
|
654
|
-
},
|
|
655
|
-
]
|
|
656
|
-
: []),
|
|
657
533
|
...rustArtifactSteps(plan),
|
|
658
534
|
],
|
|
659
535
|
};
|
|
660
536
|
}
|
|
661
537
|
|
|
538
|
+
/** Wait for the selected local or GitHub-hosted native build source. */
|
|
539
|
+
export function rustAssetsJob(): Job {
|
|
540
|
+
return {
|
|
541
|
+
if: "${{ always() && needs.verify-context.result == 'success' && (needs.verify-context.outputs.build_mode == 'local' || needs.rust-build.result == 'success') }}",
|
|
542
|
+
needs: ["verify-context", "rust-build"],
|
|
543
|
+
runsOn: ["ubuntu-latest"],
|
|
544
|
+
permissions: { contents: JobPermission.READ },
|
|
545
|
+
steps: [
|
|
546
|
+
{
|
|
547
|
+
name: "Wait for locally uploaded release assets",
|
|
548
|
+
if: "${{ needs.verify-context.outputs.build_mode == 'local' }}",
|
|
549
|
+
env: {
|
|
550
|
+
GH_TOKEN: "${{ github.token }}",
|
|
551
|
+
RELEASE_TAG: RELEASE_TAG,
|
|
552
|
+
},
|
|
553
|
+
shell: "bash",
|
|
554
|
+
run: [
|
|
555
|
+
"mkdir -p dist/local-release",
|
|
556
|
+
"for ATTEMPT in $(seq 1 360); do",
|
|
557
|
+
' if gh release download "$RELEASE_TAG" --pattern rust-assets.json --dir dist/local-release --clobber; then exit 0; fi',
|
|
558
|
+
' echo "waiting for local release assets ($ATTEMPT/360)"',
|
|
559
|
+
" sleep 5",
|
|
560
|
+
"done",
|
|
561
|
+
'echo "::error::local release assets were not uploaded"',
|
|
562
|
+
"exit 1",
|
|
563
|
+
].join("\n"),
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
name: "Confirm native release assets",
|
|
567
|
+
run: "true",
|
|
568
|
+
},
|
|
569
|
+
],
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
|
|
662
573
|
export function rustCargoPublishJob(plan: RustReleasePlan, local: boolean): Job {
|
|
663
574
|
const registry = local ? '"${{ vars.LOCAL_CARGO_REGISTRY }}"' : "crates-io";
|
|
664
575
|
return {
|
|
665
576
|
if: local
|
|
666
577
|
? "${{ github.event_name == 'push' && vars.LOCAL_REPOSITORIES == 'true' }}"
|
|
667
578
|
: "${{ github.event_name == 'push' }}",
|
|
668
|
-
needs: ["verify-context", "rust-
|
|
579
|
+
needs: ["verify-context", ...(plan.hasTargetOutputs ? ["rust-assets"] : [])],
|
|
669
580
|
runsOn: [local ? "self-hosted" : "ubuntu-latest"],
|
|
670
581
|
permissions: { contents: JobPermission.READ },
|
|
671
582
|
steps: [
|
|
@@ -705,11 +616,12 @@ export function independentRustCargoPublishJob(
|
|
|
705
616
|
"fi",
|
|
706
617
|
].join("\n"),
|
|
707
618
|
);
|
|
619
|
+
const buildCondition = rustBuildResultCondition(plan);
|
|
708
620
|
return {
|
|
709
621
|
if: plan.hasTargetOutputs
|
|
710
|
-
?
|
|
622
|
+
? `\${{ always() && needs.release-plan.outputs.rust == 'true' && ${buildCondition} && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'rust') }}`
|
|
711
623
|
: "${{ needs.release-plan.outputs.rust == 'true' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'rust') }}",
|
|
712
|
-
needs: ["release-plan", ...(plan
|
|
624
|
+
needs: ["release-plan", ...rustBuildJobIds(plan)],
|
|
713
625
|
runsOn: ["ubuntu-latest"],
|
|
714
626
|
permissions: { contents: JobPermission.READ },
|
|
715
627
|
env: { BUN_VERSION },
|
|
@@ -734,7 +646,7 @@ export function independentRustGitHubReleaseJob(
|
|
|
734
646
|
): Job {
|
|
735
647
|
return {
|
|
736
648
|
if: "${{ needs.release-plan.outputs.github == 'true' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'github') }}",
|
|
737
|
-
needs: ["release-plan",
|
|
649
|
+
needs: ["release-plan", ...rustBuildJobIds(plan)],
|
|
738
650
|
runsOn: ["ubuntu-latest"],
|
|
739
651
|
permissions: { contents: JobPermission.WRITE },
|
|
740
652
|
env: { BUN_VERSION },
|
|
@@ -749,15 +661,6 @@ export function independentRustGitHubReleaseJob(
|
|
|
749
661
|
"merge-multiple": true,
|
|
750
662
|
},
|
|
751
663
|
},
|
|
752
|
-
{
|
|
753
|
-
name: "Download reusable raw Rust outputs",
|
|
754
|
-
uses: "actions/download-artifact@v8",
|
|
755
|
-
with: {
|
|
756
|
-
pattern: "*-raw",
|
|
757
|
-
path: "dist/rust-raw",
|
|
758
|
-
"merge-multiple": true,
|
|
759
|
-
},
|
|
760
|
-
},
|
|
761
664
|
{
|
|
762
665
|
name: "Upload affected GitHub release assets",
|
|
763
666
|
env: { GH_TOKEN: "${{ github.token }}" },
|
|
@@ -778,16 +681,17 @@ export function independentRustGitHubReleaseJob(
|
|
|
778
681
|
};
|
|
779
682
|
}
|
|
780
683
|
|
|
781
|
-
export function rustGitHubReleaseJob(): Job {
|
|
684
|
+
export function rustGitHubReleaseJob(plan: RustReleasePlan): Job {
|
|
782
685
|
return {
|
|
783
686
|
if: "${{ github.event_name == 'push' }}",
|
|
784
|
-
needs: ["verify-context", "rust-
|
|
687
|
+
needs: ["verify-context", ...(plan.hasTargetOutputs ? ["rust-assets"] : [])],
|
|
785
688
|
runsOn: ["ubuntu-latest"],
|
|
786
689
|
permissions: { contents: JobPermission.WRITE },
|
|
787
690
|
steps: [
|
|
788
691
|
...releaseSourceSteps(),
|
|
789
692
|
{
|
|
790
693
|
name: "Download release binaries",
|
|
694
|
+
if: "${{ needs.verify-context.outputs.build_mode == 'remote' }}",
|
|
791
695
|
uses: "actions/download-artifact@v8",
|
|
792
696
|
with: {
|
|
793
697
|
pattern: "*-binary",
|
|
@@ -795,20 +699,12 @@ export function rustGitHubReleaseJob(): Job {
|
|
|
795
699
|
"merge-multiple": true,
|
|
796
700
|
},
|
|
797
701
|
},
|
|
798
|
-
{
|
|
799
|
-
name: "Download reusable raw Rust outputs",
|
|
800
|
-
uses: "actions/download-artifact@v8",
|
|
801
|
-
with: {
|
|
802
|
-
pattern: "*-raw",
|
|
803
|
-
path: "dist/rust-raw",
|
|
804
|
-
"merge-multiple": true,
|
|
805
|
-
},
|
|
806
|
-
},
|
|
807
702
|
{
|
|
808
703
|
name: "Publish GitHub release assets",
|
|
704
|
+
if: "${{ needs.verify-context.outputs.build_mode == 'remote' }}",
|
|
809
705
|
uses: "softprops/action-gh-release@v2",
|
|
810
706
|
with: {
|
|
811
|
-
files:
|
|
707
|
+
files: "dist/rust-release/*",
|
|
812
708
|
body_path: RELEASE_SUMMARY_FILE,
|
|
813
709
|
generate_release_notes: true,
|
|
814
710
|
tag_name: RELEASE_TAG,
|
|
@@ -816,51 +712,14 @@ export function rustGitHubReleaseJob(): Job {
|
|
|
816
712
|
},
|
|
817
713
|
},
|
|
818
714
|
{
|
|
819
|
-
name: "
|
|
820
|
-
|
|
821
|
-
env: {
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
// ============================================================================
|
|
826
|
-
/*js*/`
|
|
827
|
-
const currentTag = process.env.CURRENT_RELEASE_TAG;
|
|
828
|
-
if (!currentTag) throw new Error("CURRENT_RELEASE_TAG is required");
|
|
829
|
-
const parseVersion = (tag) => {
|
|
830
|
-
const match = /^v(\\d+)\\.(\\d+)\\.(\\d+)$/.exec(tag);
|
|
831
|
-
return match ? match.slice(1).map(Number) : undefined;
|
|
832
|
-
};
|
|
833
|
-
const compareVersions = (left, right) => {
|
|
834
|
-
for (let index = 0; index < 3; index += 1) {
|
|
835
|
-
if (left[index] !== right[index]) return left[index] - right[index];
|
|
836
|
-
}
|
|
837
|
-
return 0;
|
|
838
|
-
};
|
|
839
|
-
const currentVersion = parseVersion(currentTag);
|
|
840
|
-
if (!currentVersion) throw new Error(\`Unsupported release tag: \${currentTag}\`);
|
|
841
|
-
const releases = await github.paginate(github.rest.repos.listReleases, {
|
|
842
|
-
...context.repo,
|
|
843
|
-
per_page: 100,
|
|
844
|
-
});
|
|
845
|
-
const assets = releases.flatMap((release) => {
|
|
846
|
-
const version = parseVersion(release.tag_name);
|
|
847
|
-
if (release.draft || !version || compareVersions(version, currentVersion) >= 0) {
|
|
848
|
-
return [];
|
|
849
|
-
}
|
|
850
|
-
return release.assets.filter((asset) => /^rust-(?:raw|build)-/.test(asset.name));
|
|
851
|
-
});
|
|
852
|
-
for (const asset of assets) {
|
|
853
|
-
core.info(\`Deleting \${asset.name} (\${asset.id})\`);
|
|
854
|
-
await github.rest.repos.deleteReleaseAsset({
|
|
855
|
-
...context.repo,
|
|
856
|
-
asset_id: asset.id,
|
|
857
|
-
});
|
|
858
|
-
}
|
|
859
|
-
core.info(\`Deleted \${assets.length} superseded raw Rust release assets\`);
|
|
860
|
-
`
|
|
861
|
-
// ============================================================================
|
|
862
|
-
),
|
|
715
|
+
name: "Publish locally built GitHub release",
|
|
716
|
+
if: "${{ needs.verify-context.outputs.build_mode == 'local' }}",
|
|
717
|
+
env: {
|
|
718
|
+
GH_TOKEN: "${{ github.token }}",
|
|
719
|
+
RELEASE_NOTES: RELEASE_SUMMARY_FILE,
|
|
720
|
+
RELEASE_TAG,
|
|
863
721
|
},
|
|
722
|
+
run: 'gh release edit "$RELEASE_TAG" --draft=false --latest --notes-file "$RELEASE_NOTES"',
|
|
864
723
|
},
|
|
865
724
|
],
|
|
866
725
|
};
|
|
@@ -932,8 +791,8 @@ export function independentRustNodeFacadePublishJob(
|
|
|
932
791
|
|
|
933
792
|
export function rustNativeNpmPublishJob(project: DBXToolsJavaScriptProject): Job {
|
|
934
793
|
return {
|
|
935
|
-
if: "${{ always() && needs.verify-context.result == 'success' && needs.rust-
|
|
936
|
-
needs: ["verify-context", "rust-
|
|
794
|
+
if: "${{ always() && needs.verify-context.result == 'success' && needs.rust-assets.result == 'success' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'node') }}",
|
|
795
|
+
needs: ["verify-context", "rust-assets"],
|
|
937
796
|
runsOn: ["ubuntu-latest"],
|
|
938
797
|
permissions: {
|
|
939
798
|
actions: JobPermission.READ,
|
|
@@ -949,7 +808,17 @@ export function rustNativeNpmPublishJob(project: DBXToolsJavaScriptProject): Job
|
|
|
949
808
|
recoveredName: "Download recovered native npm packages",
|
|
950
809
|
pattern: "*-npm",
|
|
951
810
|
path: "dist/uniffi/native",
|
|
811
|
+
condition: "needs.verify-context.outputs.build_mode == 'remote'",
|
|
952
812
|
}),
|
|
813
|
+
{
|
|
814
|
+
name: "Download locally built native npm packages",
|
|
815
|
+
if: "${{ needs.verify-context.outputs.build_mode == 'local' }}",
|
|
816
|
+
env: { GH_TOKEN: "${{ github.token }}", RELEASE_TAG },
|
|
817
|
+
run: [
|
|
818
|
+
"mkdir -p dist/uniffi/native",
|
|
819
|
+
'gh release download "$RELEASE_TAG" --pattern "*.tgz" --dir dist/uniffi/native',
|
|
820
|
+
].join("\n"),
|
|
821
|
+
},
|
|
953
822
|
{
|
|
954
823
|
name: "Publish native npm packages",
|
|
955
824
|
env: { RELEASE_VERSION, ...npmPublishEnvironment() },
|