@dbx-tools/projen 0.9.19 → 0.9.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -229
- package/index.ts +11 -18
- package/package.json +9 -5
- package/src/_task-command.ts +75 -0
- package/src/barrels.ts +1 -74
- package/src/module-exports.ts +175 -1
- package/src/openapi.ts +5 -72
- package/src/packages.ts +45 -0
- package/src/project-js.ts +5 -48
- package/src/project-predicate.ts +1 -2
- package/src/project-py.ts +87 -199
- package/src/project.ts +2 -2
- package/src/python-node-bindings.ts +138 -0
- package/src/python-node-bundle.ts +92 -0
- package/src/release-catalog.ts +40 -208
- package/src/{release-dispatch.ts → release-context.ts} +0 -11
- package/src/release.ts +74 -778
- package/src/scaffold.ts +2 -6
- package/src/workspace-version.ts +9 -121
- package/tasks/bump.ts +17 -34
- package/tasks/databricks-cli-assets.ts +129 -0
- package/tasks/local-publish.ts +61 -138
- package/tasks/publish-npm.ts +71 -30
- package/tasks/publish-python.ts +61 -148
- package/tasks/publish.ts +32 -182
- package/tasks/python-node-bindings-watch.ts +47 -0
- package/tasks/python-node-bindings.ts +1017 -0
- package/tasks/python-registry.ts +2 -9
- package/tasks/python-release.ts +74 -0
- package/tasks/release-version.ts +23 -2
- package/tasks/release.ts +99 -0
- package/tasks/sync.ts +13 -18
- package/tasks/version-check.ts +13 -72
- package/src/_rust-project.ts +0 -429
- package/src/_rust-release-workflow.ts +0 -698
- package/src/project-rs.ts +0 -1127
- package/src/release-plan.ts +0 -269
- package/src/release-please.ts +0 -242
- package/src/uniffi.ts +0 -65
- package/tasks/publish-uniffi-local.ts +0 -314
- package/tasks/release-assets.ts +0 -476
- package/tasks/release-bootstrap.ts +0 -59
- package/tasks/release-candidate.ts +0 -383
- package/tasks/release-manifest.ts +0 -273
- package/tasks/release-plan.ts +0 -89
- package/tasks/release-please.ts +0 -162
- package/tasks/release-pr.ts +0 -563
- package/tasks/release-request.ts +0 -166
- package/tasks/release-summary-units.ts +0 -75
- package/tasks/release-summary.ts +0 -381
- package/tasks/rust.ts +0 -178
- package/tasks/stamp-python.ts +0 -13
- package/tasks/uniffi-python.d.ts +0 -61
- package/tasks/uniffi-python.js +0 -191
- package/tasks/uniffi-release.mjs +0 -573
- package/tasks/uniffi.ts +0 -271
|
@@ -1,698 +0,0 @@
|
|
|
1
|
-
/** Rust release planning and generated GitHub workflow jobs. */
|
|
2
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
-
import { createRequire } from "node:module";
|
|
4
|
-
import { dirname, join, resolve } from "node:path";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
-
import { stringUtils } from "@dbx-tools/shared-core";
|
|
7
|
-
import { TextFile, javascript } from "projen";
|
|
8
|
-
import { JobPermission, type Job, type JobStep } from "projen/lib/github/workflows-model";
|
|
9
|
-
import { releaseBinaryAssetName } from "./_release-platform.ts";
|
|
10
|
-
import { UNIFFI_BINDGEN_FEATURE, type RustProject } from "./_rust-project.ts";
|
|
11
|
-
import { BUN_VERSION } from "./bun-workflow.ts";
|
|
12
|
-
import type { DBXToolsJavaScriptProject } from "./project-js.ts";
|
|
13
|
-
import type {
|
|
14
|
-
DBXToolsRustWorkspaceOptions,
|
|
15
|
-
RustBindingMapping,
|
|
16
|
-
RustReleaseOs,
|
|
17
|
-
UniFFIReleaseTarget,
|
|
18
|
-
} from "./project-rs.ts";
|
|
19
|
-
import { defaultReleaseUnitId } from "./release-catalog.ts";
|
|
20
|
-
import { RELEASE_VERSION, releaseSourceSteps } from "./release-dispatch.ts";
|
|
21
|
-
import {
|
|
22
|
-
independentReleaseSetupSteps,
|
|
23
|
-
npmPublishEnvironment,
|
|
24
|
-
releasePublishCondition,
|
|
25
|
-
} from "./release.ts";
|
|
26
|
-
|
|
27
|
-
const require = createRequire(import.meta.url);
|
|
28
|
-
|
|
29
|
-
type RustPackageDependencyResolver = (pkg: RustProject) => RustProject[];
|
|
30
|
-
|
|
31
|
-
const RUST_BUILD_ENV = {
|
|
32
|
-
CARGO_INCREMENTAL: "0",
|
|
33
|
-
CARGO_TERM_COLOR: "always",
|
|
34
|
-
} as const;
|
|
35
|
-
|
|
36
|
-
function timedBash(phase: string, command: string): string {
|
|
37
|
-
return [
|
|
38
|
-
"SECONDS=0",
|
|
39
|
-
`trap 'status=$?; echo "phase=${phase} duration_seconds=$SECONDS status=$status"; exit "$status"' EXIT`,
|
|
40
|
-
command,
|
|
41
|
-
].join("\n");
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function taskSource(name: string): string {
|
|
45
|
-
const sourceDirectory = dirname(fileURLToPath(import.meta.url));
|
|
46
|
-
const candidates = [
|
|
47
|
-
resolve(sourceDirectory, `../tasks/${name}`),
|
|
48
|
-
resolve(sourceDirectory, `../../tasks/${name}`),
|
|
49
|
-
];
|
|
50
|
-
const source = candidates.find(existsSync);
|
|
51
|
-
if (!source) throw new Error(`Could not locate tasks/${name}`);
|
|
52
|
-
return readFileSync(source, "utf8");
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function smolTomlSource(name: "dist/index.cjs" | "LICENSE"): string {
|
|
56
|
-
const entry = require.resolve("smol-toml");
|
|
57
|
-
return readFileSync(
|
|
58
|
-
resolve(dirname(entry), name === "LICENSE" ? "../LICENSE" : "index.cjs"),
|
|
59
|
-
"utf8",
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function orderRustBindings(bindings: readonly RustBindingMapping[]): RustBindingMapping[] {
|
|
64
|
-
const ordered: RustBindingMapping[] = [];
|
|
65
|
-
const visiting = new Set<string>();
|
|
66
|
-
const completed = new Set<string>();
|
|
67
|
-
const visit = (binding: RustBindingMapping): void => {
|
|
68
|
-
if (completed.has(binding.crate)) return;
|
|
69
|
-
if (visiting.has(binding.crate)) {
|
|
70
|
-
throw new Error(`Cyclic Rust binding dependency: ${binding.crate}`);
|
|
71
|
-
}
|
|
72
|
-
visiting.add(binding.crate);
|
|
73
|
-
for (const name of binding.dependencies ?? []) {
|
|
74
|
-
const dependency = bindings.find((candidate) => candidate.crate === name);
|
|
75
|
-
if (!dependency) throw new Error(`Missing Rust binding dependency: ${name}`);
|
|
76
|
-
visit(dependency);
|
|
77
|
-
}
|
|
78
|
-
visiting.delete(binding.crate);
|
|
79
|
-
completed.add(binding.crate);
|
|
80
|
-
ordered.push(binding);
|
|
81
|
-
};
|
|
82
|
-
for (const binding of bindings) visit(binding);
|
|
83
|
-
return ordered;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export interface RustReleaseBinding extends RustBindingMapping {
|
|
87
|
-
readonly node: string;
|
|
88
|
-
readonly python: string;
|
|
89
|
-
readonly nodePackage: string;
|
|
90
|
-
readonly pythonPackage: string;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export interface RustReleaseBinaryPlan {
|
|
94
|
-
readonly crate: string;
|
|
95
|
-
readonly binary: string;
|
|
96
|
-
readonly excludedOs: readonly RustReleaseOs[];
|
|
97
|
-
readonly requiredFeatures: readonly string[];
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export interface RustReleaseTargetPlan {
|
|
101
|
-
readonly runner: string;
|
|
102
|
-
readonly cargo: string;
|
|
103
|
-
readonly node: string;
|
|
104
|
-
readonly python: string;
|
|
105
|
-
readonly os: RustReleaseOs;
|
|
106
|
-
readonly cpu: string;
|
|
107
|
-
readonly libc: string;
|
|
108
|
-
readonly glibcVersion: string;
|
|
109
|
-
readonly packages: readonly string[];
|
|
110
|
-
readonly binaries: readonly string[];
|
|
111
|
-
readonly features: readonly string[];
|
|
112
|
-
readonly localFeatures: readonly string[];
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/** Generated release inputs shared by GitHub-hosted and local target builds. */
|
|
116
|
-
export interface RustReleaseConfiguration {
|
|
117
|
-
readonly releaseRustVersion: string;
|
|
118
|
-
readonly usesCargoLock: boolean;
|
|
119
|
-
readonly bindings: readonly RustReleaseBinding[];
|
|
120
|
-
readonly binaries: readonly RustReleaseBinaryPlan[];
|
|
121
|
-
readonly targets: readonly RustReleaseTargetPlan[];
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export interface RustReleasePlan {
|
|
125
|
-
readonly releaseRustVersion: string;
|
|
126
|
-
readonly releaseTask: string;
|
|
127
|
-
readonly bindings: readonly RustReleaseBinding[];
|
|
128
|
-
readonly nodeBindings: readonly RustReleaseBinding[];
|
|
129
|
-
readonly releaseBinaries: readonly RustReleaseBinaryPlan[];
|
|
130
|
-
readonly publicCrates: readonly string[];
|
|
131
|
-
readonly targets: readonly RustReleaseTargetPlan[];
|
|
132
|
-
readonly hasPythonBindings: boolean;
|
|
133
|
-
readonly usesCargoLock: boolean;
|
|
134
|
-
readonly usePreinstalledWindowsRust: boolean;
|
|
135
|
-
readonly hasTargetOutputs: boolean;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function releaseTargetPlan(
|
|
139
|
-
target: UniFFIReleaseTarget,
|
|
140
|
-
packages: readonly RustProject[],
|
|
141
|
-
binaries: readonly string[],
|
|
142
|
-
features: readonly string[],
|
|
143
|
-
localFeatures: readonly string[],
|
|
144
|
-
): RustReleaseTargetPlan {
|
|
145
|
-
const packageNames = packages.map((pkg) => pkg.crateName).sort();
|
|
146
|
-
const sortedBinaries = [...new Set(binaries)].sort();
|
|
147
|
-
const sortedFeatures = [...new Set(features)].sort();
|
|
148
|
-
return {
|
|
149
|
-
runner: target.runner,
|
|
150
|
-
cargo: target.cargo,
|
|
151
|
-
node: target.node,
|
|
152
|
-
python: target.python,
|
|
153
|
-
os: target.os,
|
|
154
|
-
cpu: target.cpu,
|
|
155
|
-
libc: target.libc ?? "",
|
|
156
|
-
glibcVersion: target.glibcVersion ?? "",
|
|
157
|
-
packages: packageNames,
|
|
158
|
-
binaries: sortedBinaries,
|
|
159
|
-
features: sortedFeatures,
|
|
160
|
-
localFeatures: [...new Set(localFeatures)].sort(),
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
export function planRustRelease(
|
|
165
|
-
project: javascript.NodeProject,
|
|
166
|
-
options: DBXToolsRustWorkspaceOptions,
|
|
167
|
-
targets: readonly UniFFIReleaseTarget[],
|
|
168
|
-
packages: readonly RustProject[],
|
|
169
|
-
bindingMappings: readonly RustBindingMapping[],
|
|
170
|
-
packageDependencies: RustPackageDependencyResolver,
|
|
171
|
-
): RustReleasePlan {
|
|
172
|
-
const releaseRustVersion = options.releaseRustVersion ?? "stable";
|
|
173
|
-
const bindings = bindingMappings.map((binding) => ({
|
|
174
|
-
...binding,
|
|
175
|
-
node: binding.node ?? "",
|
|
176
|
-
python: binding.python ?? "",
|
|
177
|
-
nodePackage: binding.nodePackage ?? "",
|
|
178
|
-
pythonPackage: binding.pythonPackage ?? "",
|
|
179
|
-
}));
|
|
180
|
-
const releaseBinaries = packages.flatMap((pkg) => [
|
|
181
|
-
...(pkg.packageOptions.release
|
|
182
|
-
? [
|
|
183
|
-
{
|
|
184
|
-
crate: pkg.crateName,
|
|
185
|
-
binary: pkg.packageOptions.binaryName ?? pkg.crateName,
|
|
186
|
-
excludedOs: pkg.packageOptions.releaseExcludeOs ?? [],
|
|
187
|
-
requiredFeatures: [],
|
|
188
|
-
},
|
|
189
|
-
]
|
|
190
|
-
: []),
|
|
191
|
-
...(pkg.packageOptions.binaries ?? [])
|
|
192
|
-
.filter((binary) => binary.release)
|
|
193
|
-
.map((binary) => ({
|
|
194
|
-
crate: pkg.crateName,
|
|
195
|
-
binary: binary.name,
|
|
196
|
-
excludedOs: binary.releaseExcludeOs ?? [],
|
|
197
|
-
requiredFeatures: binary.requiredFeatures ?? [],
|
|
198
|
-
})),
|
|
199
|
-
]);
|
|
200
|
-
const publicCrates = orderRustBindings(
|
|
201
|
-
packages
|
|
202
|
-
.filter((pkg) => !pkg.packageOptions.private)
|
|
203
|
-
.map((pkg) => ({
|
|
204
|
-
crate: pkg.crateName,
|
|
205
|
-
rust: pkg.outdir,
|
|
206
|
-
dependencies: packageDependencies(pkg).map((dependency) => dependency.crateName),
|
|
207
|
-
})),
|
|
208
|
-
).map((pkg) => pkg.crate);
|
|
209
|
-
const bindingPackages = packages.filter((pkg) =>
|
|
210
|
-
bindings.some((binding) => binding.crate === pkg.crateName),
|
|
211
|
-
);
|
|
212
|
-
const releaseTargets = targets.flatMap((target) => {
|
|
213
|
-
const selectedBinaries = releaseBinaries.filter(
|
|
214
|
-
(binary) => !binary.excludedOs.includes(target.os),
|
|
215
|
-
);
|
|
216
|
-
const binaryPackages = packages.filter((pkg) =>
|
|
217
|
-
selectedBinaries.some((binary) => binary.crate === pkg.crateName),
|
|
218
|
-
);
|
|
219
|
-
const selectedPackages = [...new Set([...bindingPackages, ...binaryPackages])];
|
|
220
|
-
if (!selectedPackages.length) return [];
|
|
221
|
-
const features = [
|
|
222
|
-
...bindingPackages.map((pkg) => `${pkg.crateName}/${UNIFFI_BINDGEN_FEATURE}`),
|
|
223
|
-
...selectedBinaries.flatMap((binary) =>
|
|
224
|
-
binary.requiredFeatures.map((feature) => `${binary.crate}/${feature}`),
|
|
225
|
-
),
|
|
226
|
-
];
|
|
227
|
-
const localFeatures = selectedPackages.flatMap((pkg) =>
|
|
228
|
-
(pkg.packageOptions.releaseLocalFeatures ?? []).map(
|
|
229
|
-
(feature) => `${pkg.crateName}/${feature}`,
|
|
230
|
-
),
|
|
231
|
-
);
|
|
232
|
-
return [
|
|
233
|
-
releaseTargetPlan(
|
|
234
|
-
target,
|
|
235
|
-
selectedPackages,
|
|
236
|
-
selectedBinaries.map((binary) => binary.binary),
|
|
237
|
-
features,
|
|
238
|
-
localFeatures,
|
|
239
|
-
),
|
|
240
|
-
];
|
|
241
|
-
});
|
|
242
|
-
const hasTargetOutputs = releaseTargets.length > 0;
|
|
243
|
-
if (hasTargetOutputs && targets.length === 0) {
|
|
244
|
-
throw new Error("Rust release requires at least one target");
|
|
245
|
-
}
|
|
246
|
-
return {
|
|
247
|
-
releaseRustVersion,
|
|
248
|
-
releaseTask: ".projen/uniffi-release.mjs",
|
|
249
|
-
bindings,
|
|
250
|
-
nodeBindings: bindings.filter((binding) => Boolean(binding.node && binding.nodePackage)),
|
|
251
|
-
releaseBinaries,
|
|
252
|
-
publicCrates,
|
|
253
|
-
targets: releaseTargets,
|
|
254
|
-
hasPythonBindings: bindings.some((binding) => binding.python),
|
|
255
|
-
usesCargoLock: existsSync(join(project.outdir, "Cargo.lock")),
|
|
256
|
-
usePreinstalledWindowsRust: releaseRustVersion === "stable",
|
|
257
|
-
hasTargetOutputs,
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
function rustBuildJobIds(plan: RustReleasePlan): string[] {
|
|
262
|
-
return plan.targets.length ? ["rust-build"] : [];
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
function rustBuildResultCondition(plan: RustReleasePlan): string {
|
|
266
|
-
return rustBuildJobIds(plan)
|
|
267
|
-
.map((job) => `needs.${job}.result != 'failure' && needs.${job}.result != 'cancelled'`)
|
|
268
|
-
.join(" && ");
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
export function configureRustReleaseTask(
|
|
272
|
-
project: javascript.NodeProject,
|
|
273
|
-
plan: RustReleasePlan,
|
|
274
|
-
): void {
|
|
275
|
-
new TextFile(project, ".projen/rust-release.json", {
|
|
276
|
-
lines: JSON.stringify(
|
|
277
|
-
{
|
|
278
|
-
releaseRustVersion: plan.releaseRustVersion,
|
|
279
|
-
usesCargoLock: plan.usesCargoLock,
|
|
280
|
-
bindings: plan.bindings,
|
|
281
|
-
binaries: plan.releaseBinaries,
|
|
282
|
-
targets: plan.targets,
|
|
283
|
-
} satisfies RustReleaseConfiguration,
|
|
284
|
-
null,
|
|
285
|
-
2,
|
|
286
|
-
).split("\n"),
|
|
287
|
-
});
|
|
288
|
-
const bindingSupportFiles = [
|
|
289
|
-
plan.releaseTask,
|
|
290
|
-
".projen/uniffi-python.js",
|
|
291
|
-
".projen/smol-toml.cjs",
|
|
292
|
-
".projen/smol-toml.LICENSE",
|
|
293
|
-
] as const;
|
|
294
|
-
if (plan.bindings.length) {
|
|
295
|
-
new TextFile(project, plan.releaseTask, {
|
|
296
|
-
lines: taskSource("uniffi-release.mjs").trimEnd().split("\n"),
|
|
297
|
-
});
|
|
298
|
-
new TextFile(project, bindingSupportFiles[1], {
|
|
299
|
-
lines: taskSource("uniffi-python.js").trimEnd().split("\n"),
|
|
300
|
-
});
|
|
301
|
-
new TextFile(project, bindingSupportFiles[2], {
|
|
302
|
-
lines: smolTomlSource("dist/index.cjs").trimEnd().split("\n"),
|
|
303
|
-
});
|
|
304
|
-
new TextFile(project, bindingSupportFiles[3], {
|
|
305
|
-
lines: smolTomlSource("LICENSE").trimEnd().split("\n"),
|
|
306
|
-
});
|
|
307
|
-
} else {
|
|
308
|
-
for (const path of bindingSupportFiles) project.tryRemoveFile(path);
|
|
309
|
-
}
|
|
310
|
-
project.tryRemoveFile(".projen/rust-release.mjs");
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
function rustBindingCommands(plan: RustReleasePlan, independent: boolean): string[] {
|
|
314
|
-
return plan.bindings.map((binding) => {
|
|
315
|
-
const unit = defaultReleaseUnitId("rust", binding.crate);
|
|
316
|
-
const command = [
|
|
317
|
-
`node ${plan.releaseTask} build`,
|
|
318
|
-
`--crate "${binding.crate}"`,
|
|
319
|
-
`--node "${binding.node}"`,
|
|
320
|
-
`--python "${binding.python}"`,
|
|
321
|
-
`--node-package "${binding.nodePackage}"`,
|
|
322
|
-
`--python-package "${binding.pythonPackage}"`,
|
|
323
|
-
`--python-module "${binding.pythonModule}"`,
|
|
324
|
-
'--cargo-target "${{ matrix.cargo }}"',
|
|
325
|
-
'--node-triple "${{ matrix.node }}"',
|
|
326
|
-
'--python-tag "${{ matrix.python }}"',
|
|
327
|
-
'--os "${{ matrix.os }}"',
|
|
328
|
-
'--cpu "${{ matrix.cpu }}"',
|
|
329
|
-
'--libc "${{ matrix.libc }}"',
|
|
330
|
-
independent
|
|
331
|
-
? `--version "$(jq -r --arg unit "${unit}" '.units[] | select(.id == $unit) | .newVersion' dist/release-plan.json)"`
|
|
332
|
-
: '--version "$VERSION"',
|
|
333
|
-
`--output "dist/release/${binding.crate}/\${{ matrix.node }}"`,
|
|
334
|
-
"--skip-build",
|
|
335
|
-
].join(" \\\n ");
|
|
336
|
-
return independent
|
|
337
|
-
? [
|
|
338
|
-
`if jq -e --arg unit "${unit}" '.units[] | select(.id == $unit)' dist/release-plan.json >/dev/null; then`,
|
|
339
|
-
` ${command.replaceAll("\n", "\n ")}`,
|
|
340
|
-
"fi",
|
|
341
|
-
].join("\n")
|
|
342
|
-
: command;
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
function rustReleaseBinaryCondition(excludedOs: readonly RustReleaseOs[]): string | undefined {
|
|
347
|
-
return excludedOs.length
|
|
348
|
-
? `\${{ ${excludedOs.map((os) => `matrix.os != '${os}'`).join(" && ")} }}`
|
|
349
|
-
: undefined;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
function rustBinaryCommands(plan: RustReleasePlan, independent: boolean): string[] {
|
|
353
|
-
return plan.releaseBinaries.flatMap((pkg) => {
|
|
354
|
-
const unit = defaultReleaseUnitId("rust", pkg.crate);
|
|
355
|
-
const windowsAsset = releaseBinaryAssetName(pkg.binary, "${{ matrix.node }}", "win32");
|
|
356
|
-
const unixAsset = releaseBinaryAssetName(pkg.binary, "${{ matrix.node }}", "linux");
|
|
357
|
-
const commands = [
|
|
358
|
-
`mkdir -p "dist/release/${pkg.crate}/\${{ matrix.node }}/binary/stage"`,
|
|
359
|
-
`SOURCE="target/\${{ matrix.cargo }}/release/${pkg.binary}\${{ matrix.os == 'win32' && '.exe' || '' }}"`,
|
|
360
|
-
`DESTINATION="dist/release/${pkg.crate}/\${{ matrix.node }}/binary/stage/${pkg.binary}\${{ matrix.os == 'win32' && '.exe' || '' }}"`,
|
|
361
|
-
'cp "$SOURCE" "$DESTINATION"',
|
|
362
|
-
'if [ "${{ matrix.os }}" = "win32" ]; then',
|
|
363
|
-
` 7z a "dist/release/${pkg.crate}/\${{ matrix.node }}/binary/${windowsAsset}" "$DESTINATION"`,
|
|
364
|
-
"else",
|
|
365
|
-
` tar -C "dist/release/${pkg.crate}/\${{ matrix.node }}/binary/stage" -czf "dist/release/${pkg.crate}/\${{ matrix.node }}/binary/${unixAsset}" "${pkg.binary}"`,
|
|
366
|
-
"fi",
|
|
367
|
-
`rm -rf "dist/release/${pkg.crate}/\${{ matrix.node }}/binary/stage"`,
|
|
368
|
-
];
|
|
369
|
-
const excludedCondition = pkg.excludedOs
|
|
370
|
-
.map((os) => `[ "\${{ matrix.os }}" != "${os}" ]`)
|
|
371
|
-
.join(" && ");
|
|
372
|
-
const platformCommands = excludedCondition
|
|
373
|
-
? [`if ${excludedCondition}; then`, ...commands.map((command) => ` ${command}`), "fi"]
|
|
374
|
-
: commands;
|
|
375
|
-
return independent
|
|
376
|
-
? [
|
|
377
|
-
`if jq -e --arg unit "${unit}" '.units[] | select(.id == $unit)' dist/release-plan.json >/dev/null; then`,
|
|
378
|
-
...platformCommands.map((command) => ` ${command}`),
|
|
379
|
-
"fi",
|
|
380
|
-
]
|
|
381
|
-
: platformCommands;
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
function rustArtifactSteps(plan: RustReleasePlan): JobStep[] {
|
|
386
|
-
const binaryCrates = [...new Set(plan.releaseBinaries.map((binary) => binary.crate))];
|
|
387
|
-
return [
|
|
388
|
-
...plan.bindings.flatMap((binding) => [
|
|
389
|
-
...(binding.node
|
|
390
|
-
? [
|
|
391
|
-
{
|
|
392
|
-
name: `Upload ${binding.crate} native npm package`,
|
|
393
|
-
uses: "actions/upload-artifact@v7",
|
|
394
|
-
with: {
|
|
395
|
-
name: `${binding.crate}-\${{ matrix.node }}-npm`,
|
|
396
|
-
path: `dist/release/${binding.crate}/\${{ matrix.node }}/npm/*.tgz`,
|
|
397
|
-
"retention-days": 7,
|
|
398
|
-
},
|
|
399
|
-
},
|
|
400
|
-
]
|
|
401
|
-
: []),
|
|
402
|
-
...(binding.python
|
|
403
|
-
? [
|
|
404
|
-
{
|
|
405
|
-
name: `Upload ${binding.crate} Python wheel`,
|
|
406
|
-
uses: "actions/upload-artifact@v7",
|
|
407
|
-
with: {
|
|
408
|
-
name: `${binding.pythonPackage}--\${{ matrix.python }}--python-wheel`,
|
|
409
|
-
path: `dist/release/${binding.crate}/\${{ matrix.node }}/python/*.whl`,
|
|
410
|
-
"retention-days": 7,
|
|
411
|
-
},
|
|
412
|
-
},
|
|
413
|
-
]
|
|
414
|
-
: []),
|
|
415
|
-
]),
|
|
416
|
-
...binaryCrates.map((crate) => {
|
|
417
|
-
const binaries = plan.releaseBinaries.filter((binary) => binary.crate === crate);
|
|
418
|
-
const excludedOs =
|
|
419
|
-
binaries[0]?.excludedOs.filter((os) =>
|
|
420
|
-
binaries.every((binary) => binary.excludedOs.includes(os)),
|
|
421
|
-
) ?? [];
|
|
422
|
-
return {
|
|
423
|
-
name: `Upload ${crate} release binary`,
|
|
424
|
-
uses: "actions/upload-artifact@v7",
|
|
425
|
-
...(rustReleaseBinaryCondition(excludedOs)
|
|
426
|
-
? { if: rustReleaseBinaryCondition(excludedOs) }
|
|
427
|
-
: {}),
|
|
428
|
-
with: {
|
|
429
|
-
name: `${crate}-\${{ matrix.node }}-binary`,
|
|
430
|
-
path: `dist/release/${crate}/\${{ matrix.node }}/binary/*`,
|
|
431
|
-
"retention-days": 7,
|
|
432
|
-
},
|
|
433
|
-
};
|
|
434
|
-
}),
|
|
435
|
-
];
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
/** Build a Bash loop that preserves matrix values across LF and CRLF runners. */
|
|
439
|
-
function matrixArgumentLoop(variable: string, field: string, flag: string): string {
|
|
440
|
-
return `while IFS= read -r ${variable}; do ${variable}="\${${variable}%$'\\r'}"; ${variable}_ARGS+=(${flag} "$${variable}"); done < <(jq -r '.[]' <<<'\${{ toJSON(matrix.${field}) }}')`;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
function rustCargoBuildCommand(plan: RustReleasePlan): string {
|
|
444
|
-
return [
|
|
445
|
-
"PACKAGE_ARGS=()",
|
|
446
|
-
"FEATURE_ARGS=()",
|
|
447
|
-
matrixArgumentLoop("PACKAGE", "packages", "--package"),
|
|
448
|
-
matrixArgumentLoop("FEATURE", "features", "--features"),
|
|
449
|
-
`cargo build --release --timings "\${PACKAGE_ARGS[@]}" "\${FEATURE_ARGS[@]}"${
|
|
450
|
-
plan.usesCargoLock ? " --locked" : ""
|
|
451
|
-
} --target "\${{ matrix.cargo }}"`,
|
|
452
|
-
].join("\n");
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
export function rustBuildJob(plan: RustReleasePlan, setup: readonly JobStep[]): Job {
|
|
456
|
-
const bindingCommands = rustBindingCommands(plan, true);
|
|
457
|
-
const binaryCommands = rustBinaryCommands(plan, true);
|
|
458
|
-
return {
|
|
459
|
-
if: "${{ needs.release-plan.outputs.rust_targets != '[]' && (github.event_name == 'push' || inputs.stage != 'docs') }}",
|
|
460
|
-
name: "Rust / ${{ matrix.node }}",
|
|
461
|
-
needs: ["release-plan"],
|
|
462
|
-
runsOn: ["${{ matrix.runner }}"],
|
|
463
|
-
permissions: { contents: JobPermission.READ },
|
|
464
|
-
env: { ...RUST_BUILD_ENV, BUN_VERSION },
|
|
465
|
-
strategy: {
|
|
466
|
-
failFast: false,
|
|
467
|
-
matrix: {
|
|
468
|
-
include: "${{ fromJSON(needs.release-plan.outputs.rust_targets) }}" as never,
|
|
469
|
-
},
|
|
470
|
-
},
|
|
471
|
-
steps: [
|
|
472
|
-
...setup,
|
|
473
|
-
...(plan.hasPythonBindings ? [{ name: "Setup uv", uses: "astral-sh/setup-uv@v7" }] : []),
|
|
474
|
-
{
|
|
475
|
-
name: "Setup Rust",
|
|
476
|
-
uses: `dtolnay/rust-toolchain@${plan.releaseRustVersion}`,
|
|
477
|
-
with: { targets: "${{ matrix.cargo }}" },
|
|
478
|
-
},
|
|
479
|
-
{
|
|
480
|
-
name: "Install Linux native dependencies",
|
|
481
|
-
if: "${{ matrix.os == 'linux' }}",
|
|
482
|
-
// prettier-ignore
|
|
483
|
-
run: stringUtils.dedent(
|
|
484
|
-
// ============================================================================
|
|
485
|
-
/*bash*/`
|
|
486
|
-
sudo rm -f /etc/apt/sources.list.d/google-chrome.list
|
|
487
|
-
sudo apt-get update
|
|
488
|
-
sudo apt-get install --yes libdbus-1-dev pkg-config
|
|
489
|
-
`
|
|
490
|
-
// ============================================================================
|
|
491
|
-
),
|
|
492
|
-
},
|
|
493
|
-
{
|
|
494
|
-
name: "Build Rust release outputs",
|
|
495
|
-
shell: "bash",
|
|
496
|
-
run: timedBash("rust", rustCargoBuildCommand(plan)),
|
|
497
|
-
},
|
|
498
|
-
...(bindingCommands.length
|
|
499
|
-
? [
|
|
500
|
-
{
|
|
501
|
-
name: "Package UniFFI outputs",
|
|
502
|
-
shell: "bash",
|
|
503
|
-
run: timedBash("uniffi_packaging", bindingCommands.join("\n")),
|
|
504
|
-
},
|
|
505
|
-
]
|
|
506
|
-
: []),
|
|
507
|
-
...(binaryCommands.length
|
|
508
|
-
? [
|
|
509
|
-
{
|
|
510
|
-
name: "Package release binaries",
|
|
511
|
-
shell: "bash",
|
|
512
|
-
run: timedBash("binary_packaging", binaryCommands.join("\n")),
|
|
513
|
-
},
|
|
514
|
-
]
|
|
515
|
-
: []),
|
|
516
|
-
...rustArtifactSteps(plan),
|
|
517
|
-
],
|
|
518
|
-
};
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
export function rustCargoPublishJob(plan: RustReleasePlan): Job {
|
|
522
|
-
return {
|
|
523
|
-
if: releasePublishCondition("cargo", ["needs.verify-context.result == 'success'"]),
|
|
524
|
-
needs: ["verify-context"],
|
|
525
|
-
runsOn: ["ubuntu-latest"],
|
|
526
|
-
permissions: { contents: JobPermission.READ },
|
|
527
|
-
steps: [
|
|
528
|
-
...releaseSourceSteps(),
|
|
529
|
-
{
|
|
530
|
-
name: "Setup Rust",
|
|
531
|
-
uses: `dtolnay/rust-toolchain@${plan.releaseRustVersion}`,
|
|
532
|
-
},
|
|
533
|
-
{
|
|
534
|
-
name: "Publish public crates",
|
|
535
|
-
env: {
|
|
536
|
-
RELEASE_VERSION,
|
|
537
|
-
CARGO_REGISTRY_TOKEN: "${{ secrets.CARGO_REGISTRY_TOKEN }}",
|
|
538
|
-
},
|
|
539
|
-
run: plan.publicCrates
|
|
540
|
-
.map((crate) =>
|
|
541
|
-
[
|
|
542
|
-
`if cargo info "${crate}@$RELEASE_VERSION" >/dev/null 2>&1; then`,
|
|
543
|
-
` echo "skip published ${crate}@$RELEASE_VERSION"`,
|
|
544
|
-
"else",
|
|
545
|
-
` cargo publish --package "${crate}" --registry crates-io --no-verify`,
|
|
546
|
-
"fi",
|
|
547
|
-
].join("\n"),
|
|
548
|
-
)
|
|
549
|
-
.join("\n"),
|
|
550
|
-
},
|
|
551
|
-
],
|
|
552
|
-
};
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
export function independentRustCargoPublishJob(
|
|
556
|
-
project: DBXToolsJavaScriptProject,
|
|
557
|
-
plan: RustReleasePlan,
|
|
558
|
-
): Job {
|
|
559
|
-
const commands = plan.publicCrates.map((crate) =>
|
|
560
|
-
[
|
|
561
|
-
`VERSION="$(jq -r --arg package "${crate}" '.rustPackages[] | select(.identity == $package) | .version' dist/release-plan.json)"`,
|
|
562
|
-
'if [ -n "$VERSION" ]; then',
|
|
563
|
-
` if cargo info "${crate}@$VERSION" >/dev/null 2>&1; then`,
|
|
564
|
-
` echo "skip published ${crate}@$VERSION"`,
|
|
565
|
-
" else",
|
|
566
|
-
` cargo publish --package "${crate}" --registry crates-io --no-verify`,
|
|
567
|
-
" fi",
|
|
568
|
-
"fi",
|
|
569
|
-
].join("\n"),
|
|
570
|
-
);
|
|
571
|
-
const buildCondition = rustBuildResultCondition(plan);
|
|
572
|
-
return {
|
|
573
|
-
if: plan.hasTargetOutputs
|
|
574
|
-
? `\${{ always() && needs.release-plan.outputs.rust == 'true' && ${buildCondition} && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'rust') }}`
|
|
575
|
-
: "${{ needs.release-plan.outputs.rust == 'true' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'rust') }}",
|
|
576
|
-
needs: ["release-plan", ...rustBuildJobIds(plan)],
|
|
577
|
-
runsOn: ["ubuntu-latest"],
|
|
578
|
-
permissions: { contents: JobPermission.READ },
|
|
579
|
-
env: { BUN_VERSION },
|
|
580
|
-
steps: [
|
|
581
|
-
...independentReleaseSetupSteps(project),
|
|
582
|
-
{
|
|
583
|
-
name: "Setup Rust",
|
|
584
|
-
uses: `dtolnay/rust-toolchain@${plan.releaseRustVersion}`,
|
|
585
|
-
},
|
|
586
|
-
{
|
|
587
|
-
name: "Publish affected Cargo crates",
|
|
588
|
-
env: { CARGO_REGISTRY_TOKEN: "${{ secrets.CARGO_REGISTRY_TOKEN }}" },
|
|
589
|
-
run: commands.join("\n"),
|
|
590
|
-
},
|
|
591
|
-
],
|
|
592
|
-
};
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
export function independentRustGitHubReleaseJob(
|
|
596
|
-
project: DBXToolsJavaScriptProject,
|
|
597
|
-
plan: RustReleasePlan,
|
|
598
|
-
): Job {
|
|
599
|
-
return {
|
|
600
|
-
if: "${{ needs.release-plan.outputs.github == 'true' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'github') }}",
|
|
601
|
-
needs: ["release-plan", ...rustBuildJobIds(plan)],
|
|
602
|
-
runsOn: ["ubuntu-latest"],
|
|
603
|
-
permissions: { contents: JobPermission.WRITE },
|
|
604
|
-
env: { BUN_VERSION },
|
|
605
|
-
steps: [
|
|
606
|
-
...independentReleaseSetupSteps(project),
|
|
607
|
-
{
|
|
608
|
-
name: "Download release binaries",
|
|
609
|
-
uses: "actions/download-artifact@v8",
|
|
610
|
-
with: {
|
|
611
|
-
pattern: "*-binary",
|
|
612
|
-
path: "dist/rust-release",
|
|
613
|
-
"merge-multiple": true,
|
|
614
|
-
},
|
|
615
|
-
},
|
|
616
|
-
{
|
|
617
|
-
name: "Upload affected GitHub release assets",
|
|
618
|
-
env: { GH_TOKEN: "${{ github.token }}" },
|
|
619
|
-
shell: "bash",
|
|
620
|
-
run: plan.releaseBinaries
|
|
621
|
-
.map((binary) => {
|
|
622
|
-
const unit = defaultReleaseUnitId("rust", binary.crate);
|
|
623
|
-
return [
|
|
624
|
-
`VERSION="$(jq -r --arg unit "${unit}" '.units[] | select(.id == $unit) | .newVersion' dist/release-plan.json)"`,
|
|
625
|
-
'if [ -n "$VERSION" ]; then',
|
|
626
|
-
` gh release upload "${unit}-v$VERSION" dist/rust-release/${binary.binary}-* --clobber`,
|
|
627
|
-
"fi",
|
|
628
|
-
].join("\n");
|
|
629
|
-
})
|
|
630
|
-
.join("\n"),
|
|
631
|
-
},
|
|
632
|
-
],
|
|
633
|
-
};
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
export function independentRustNativeNpmPublishJob(project: DBXToolsJavaScriptProject): Job {
|
|
637
|
-
return {
|
|
638
|
-
if: "${{ needs.release-plan.outputs.node == 'true' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'node') }}",
|
|
639
|
-
needs: ["release-plan", "rust-build"],
|
|
640
|
-
runsOn: ["ubuntu-latest"],
|
|
641
|
-
permissions: { contents: JobPermission.READ, idToken: JobPermission.WRITE },
|
|
642
|
-
timeoutMinutes: 15,
|
|
643
|
-
env: { BUN_VERSION, CI: "true" },
|
|
644
|
-
steps: [
|
|
645
|
-
...independentReleaseSetupSteps(project),
|
|
646
|
-
{
|
|
647
|
-
name: "Download native npm packages",
|
|
648
|
-
uses: "actions/download-artifact@v8",
|
|
649
|
-
with: {
|
|
650
|
-
pattern: "*-npm",
|
|
651
|
-
path: "dist/uniffi/native",
|
|
652
|
-
"merge-multiple": true,
|
|
653
|
-
},
|
|
654
|
-
},
|
|
655
|
-
{
|
|
656
|
-
name: "Publish affected native npm packages",
|
|
657
|
-
env: npmPublishEnvironment(),
|
|
658
|
-
run: "bun node_modules/@dbx-tools/projen/tasks/publish-npm.ts --directory dist/uniffi/native",
|
|
659
|
-
},
|
|
660
|
-
],
|
|
661
|
-
};
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
export function independentRustNodeFacadePublishJob(
|
|
665
|
-
project: DBXToolsJavaScriptProject,
|
|
666
|
-
bindings: readonly RustBindingMapping[],
|
|
667
|
-
): Job {
|
|
668
|
-
return {
|
|
669
|
-
if: "${{ needs.release-plan.outputs.node == 'true' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'node') }}",
|
|
670
|
-
needs: ["release-plan", "publish-node", "publish-native-npm"],
|
|
671
|
-
runsOn: ["ubuntu-latest"],
|
|
672
|
-
permissions: { contents: JobPermission.READ, idToken: JobPermission.WRITE },
|
|
673
|
-
timeoutMinutes: 30,
|
|
674
|
-
env: { BUN_VERSION, CI: "true" },
|
|
675
|
-
steps: [
|
|
676
|
-
...independentReleaseSetupSteps(project),
|
|
677
|
-
{
|
|
678
|
-
name: "Build and publish affected UniFFI npm facades",
|
|
679
|
-
env: npmPublishEnvironment(),
|
|
680
|
-
shell: "bash",
|
|
681
|
-
run: bindings
|
|
682
|
-
.flatMap((binding) => {
|
|
683
|
-
if (!binding.node || !binding.nodePackage) return [];
|
|
684
|
-
const unit = defaultReleaseUnitId("rust", binding.crate);
|
|
685
|
-
const output = `dist/uniffi/facades/${binding.crate}`;
|
|
686
|
-
return [
|
|
687
|
-
`VERSION="$(jq -r --arg unit "${unit}" '.units[] | select(.id == $unit) | .newVersion' dist/release-plan.json)"`,
|
|
688
|
-
'if [ -n "$VERSION" ]; then',
|
|
689
|
-
` node .projen/uniffi-release.mjs facade --node "${binding.node}" --node-package "${binding.nodePackage}" --node-triple "linux-x64-gnu" --version "$VERSION" --output "${output}"`,
|
|
690
|
-
` bun node_modules/@dbx-tools/projen/tasks/publish-npm.ts --directory "${output}/npm-facade" --version "$VERSION"`,
|
|
691
|
-
"fi",
|
|
692
|
-
];
|
|
693
|
-
})
|
|
694
|
-
.join("\n"),
|
|
695
|
-
},
|
|
696
|
-
],
|
|
697
|
-
};
|
|
698
|
-
}
|