@dbx-tools/projen 0.9.19 → 0.9.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -229
- package/index.ts +11 -18
- package/package.json +9 -5
- package/src/_task-command.ts +75 -0
- package/src/barrels.ts +1 -74
- package/src/module-exports.ts +175 -1
- package/src/openapi.ts +5 -72
- package/src/packages.ts +45 -0
- package/src/project-js.ts +5 -48
- package/src/project-predicate.ts +1 -2
- package/src/project-py.ts +87 -199
- package/src/project.ts +2 -2
- package/src/python-node-bindings.ts +138 -0
- package/src/python-node-bundle.ts +92 -0
- package/src/release-catalog.ts +40 -208
- package/src/{release-dispatch.ts → release-context.ts} +0 -11
- package/src/release.ts +74 -778
- package/src/scaffold.ts +2 -6
- package/src/workspace-version.ts +9 -121
- package/tasks/bump.ts +17 -34
- package/tasks/databricks-cli-assets.ts +129 -0
- package/tasks/local-publish.ts +61 -138
- package/tasks/publish-npm.ts +71 -30
- package/tasks/publish-python.ts +61 -148
- package/tasks/publish.ts +32 -182
- package/tasks/python-node-bindings-watch.ts +47 -0
- package/tasks/python-node-bindings.ts +1017 -0
- package/tasks/python-registry.ts +2 -9
- package/tasks/python-release.ts +74 -0
- package/tasks/release-version.ts +23 -2
- package/tasks/release.ts +99 -0
- package/tasks/sync.ts +13 -18
- package/tasks/version-check.ts +13 -72
- package/src/_rust-project.ts +0 -429
- package/src/_rust-release-workflow.ts +0 -698
- package/src/project-rs.ts +0 -1127
- package/src/release-plan.ts +0 -269
- package/src/release-please.ts +0 -242
- package/src/uniffi.ts +0 -65
- package/tasks/publish-uniffi-local.ts +0 -314
- package/tasks/release-assets.ts +0 -476
- package/tasks/release-bootstrap.ts +0 -59
- package/tasks/release-candidate.ts +0 -383
- package/tasks/release-manifest.ts +0 -273
- package/tasks/release-plan.ts +0 -89
- package/tasks/release-please.ts +0 -162
- package/tasks/release-pr.ts +0 -563
- package/tasks/release-request.ts +0 -166
- package/tasks/release-summary-units.ts +0 -75
- package/tasks/release-summary.ts +0 -381
- package/tasks/rust.ts +0 -178
- package/tasks/stamp-python.ts +0 -13
- package/tasks/uniffi-python.d.ts +0 -61
- package/tasks/uniffi-python.js +0 -191
- package/tasks/uniffi-release.mjs +0 -573
- package/tasks/uniffi.ts +0 -271
package/src/project-rs.ts
DELETED
|
@@ -1,1127 +0,0 @@
|
|
|
1
|
-
/** Filesystem-discovered Rust workspaces and UniFFI binding package wiring. */
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { dirname, join, relative, resolve } from "node:path";
|
|
4
|
-
import * as projectUtils from "@dbx-tools/core/project-utils";
|
|
5
|
-
import { stringUtils } from "@dbx-tools/shared-core";
|
|
6
|
-
import { Component, Project, TextFile, TomlFile, javascript } from "projen";
|
|
7
|
-
import { releaseBinaryAssetName } from "./_release-platform.ts";
|
|
8
|
-
import {
|
|
9
|
-
RustProject,
|
|
10
|
-
type CargoExampleOptions as RustCargoExampleOptions,
|
|
11
|
-
cargoDependency,
|
|
12
|
-
discoverRustCrates,
|
|
13
|
-
structuredTomlSections,
|
|
14
|
-
type CargoDependency as RustCargoDependency,
|
|
15
|
-
type CargoDependencyOptions as RustCargoDependencyOptions,
|
|
16
|
-
type DBXToolsRustProjectOptions as RustProjectOptions,
|
|
17
|
-
type RustCrateOptions as RustCrateConfiguration,
|
|
18
|
-
type RustCliOptions as RustCliConfiguration,
|
|
19
|
-
type RustOpenApiOptions as RustOpenApiConfiguration,
|
|
20
|
-
} from "./_rust-project.ts";
|
|
21
|
-
import {
|
|
22
|
-
configureRustReleaseTask,
|
|
23
|
-
independentRustCargoPublishJob,
|
|
24
|
-
independentRustGitHubReleaseJob,
|
|
25
|
-
independentRustNativeNpmPublishJob,
|
|
26
|
-
independentRustNodeFacadePublishJob,
|
|
27
|
-
orderRustBindings,
|
|
28
|
-
planRustRelease,
|
|
29
|
-
rustBuildJob,
|
|
30
|
-
rustCargoPublishJob,
|
|
31
|
-
} from "./_rust-release-workflow.ts";
|
|
32
|
-
import { toPosix } from "./packages.ts";
|
|
33
|
-
import {
|
|
34
|
-
DBX_TOOLS_LICENSE,
|
|
35
|
-
type DBXToolsJavaScriptProject,
|
|
36
|
-
DBXToolsTypeScriptProject,
|
|
37
|
-
projectRepositoryUrl,
|
|
38
|
-
} from "./project-js.ts";
|
|
39
|
-
import { isDBXToolsJavaScriptProject } from "./project-predicate.ts";
|
|
40
|
-
import { pythonModuleName, type PythonPackageOptions } from "./project-py.ts";
|
|
41
|
-
import { defaultReleaseUnitId, type ReleaseDependencyInput } from "./release-catalog.ts";
|
|
42
|
-
import {
|
|
43
|
-
hasNodeRelease,
|
|
44
|
-
independentReleaseSetupSteps,
|
|
45
|
-
registerIndependentPublicationJob,
|
|
46
|
-
tryReleaseWorkflow,
|
|
47
|
-
} from "./release.ts";
|
|
48
|
-
import { readWorkspaceVersion } from "./workspace-version.ts";
|
|
49
|
-
export { discoverRustCrates, hasUniFFIBindings } from "./_rust-project.ts";
|
|
50
|
-
export { orderRustBindings } from "./_rust-release-workflow.ts";
|
|
51
|
-
|
|
52
|
-
export type CargoDependency = RustCargoDependency;
|
|
53
|
-
export type CargoDependencyOptions = RustCargoDependencyOptions;
|
|
54
|
-
export type CargoExampleOptions = RustCargoExampleOptions;
|
|
55
|
-
export type DBXToolsRustProjectOptions = RustProjectOptions;
|
|
56
|
-
export type RustCrateOptions = RustCrateConfiguration;
|
|
57
|
-
export type RustCliOptions = RustCliConfiguration;
|
|
58
|
-
export type RustOpenApiOptions = RustOpenApiConfiguration;
|
|
59
|
-
|
|
60
|
-
/** Public Rust project facade; implementation lives apart from workspace/release coordination. */
|
|
61
|
-
export class DBXToolsRustProject extends RustProject {
|
|
62
|
-
constructor(options: DBXToolsRustProjectOptions) {
|
|
63
|
-
super(options);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export interface DBXToolsRustWorkspaceOptions {
|
|
68
|
-
readonly root?: string;
|
|
69
|
-
readonly scope?: string;
|
|
70
|
-
readonly edition?: string;
|
|
71
|
-
/** Minimum supported Rust version recorded in Cargo manifests. */
|
|
72
|
-
readonly rustVersion?: string;
|
|
73
|
-
/** Rust toolchain used by release builds. Defaults to `stable`. */
|
|
74
|
-
readonly releaseRustVersion?: string;
|
|
75
|
-
readonly license?: string;
|
|
76
|
-
readonly repository?: string;
|
|
77
|
-
readonly workspaceDependencies?: Readonly<Record<string, CargoDependency>>;
|
|
78
|
-
readonly packages?: Readonly<Record<string, RustCrateOptions>>;
|
|
79
|
-
readonly nodeRoot?: string;
|
|
80
|
-
readonly pythonRoot?: string;
|
|
81
|
-
readonly pythonModulePrefix?: string;
|
|
82
|
-
/** Default publication policy for discovered crates. Individual packages can override it. */
|
|
83
|
-
readonly private?: boolean;
|
|
84
|
-
/** Generate release-branch-driven cross-platform UniFFI package releases. */
|
|
85
|
-
readonly release?: boolean;
|
|
86
|
-
/** Native release targets; defaults to the maintained GitHub-hosted matrix. */
|
|
87
|
-
readonly releaseTargets?: readonly UniFFIReleaseTarget[];
|
|
88
|
-
/** Maintained OS/CPU combinations to release. Defaults to every supported target. */
|
|
89
|
-
readonly releasePlatforms?: readonly RustReleasePlatform[];
|
|
90
|
-
/** Optional generated TypeScript registry for `cli`-enabled release binaries. */
|
|
91
|
-
readonly cliRegistryPath?: string;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export enum RustReleaseOs {
|
|
95
|
-
DARWIN = "darwin",
|
|
96
|
-
LINUX = "linux",
|
|
97
|
-
WINDOWS = "win32",
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export enum RustReleaseCpu {
|
|
101
|
-
ARM64 = "arm64",
|
|
102
|
-
X64 = "x64",
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export interface RustReleasePlatform {
|
|
106
|
-
readonly os: RustReleaseOs;
|
|
107
|
-
readonly cpu: RustReleaseCpu;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export interface UniFFIReleaseTarget {
|
|
111
|
-
readonly runner: string;
|
|
112
|
-
readonly cargo: string;
|
|
113
|
-
readonly node: string;
|
|
114
|
-
readonly python: string;
|
|
115
|
-
readonly os: RustReleaseOs;
|
|
116
|
-
readonly cpu: RustReleaseCpu;
|
|
117
|
-
readonly libc?: "glibc";
|
|
118
|
-
readonly glibcVersion?: string;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/** Native targets built on matching GitHub-hosted runners. */
|
|
122
|
-
export const UNIFFI_RELEASE_TARGETS: readonly UniFFIReleaseTarget[] = [
|
|
123
|
-
{
|
|
124
|
-
runner: "ubuntu-22.04",
|
|
125
|
-
cargo: "x86_64-unknown-linux-gnu",
|
|
126
|
-
node: "linux-x64-gnu",
|
|
127
|
-
python: "manylinux_2_35_x86_64",
|
|
128
|
-
os: RustReleaseOs.LINUX,
|
|
129
|
-
cpu: RustReleaseCpu.X64,
|
|
130
|
-
libc: "glibc",
|
|
131
|
-
glibcVersion: "2.35",
|
|
132
|
-
},
|
|
133
|
-
{
|
|
134
|
-
runner: "ubuntu-24.04-arm",
|
|
135
|
-
cargo: "aarch64-unknown-linux-gnu",
|
|
136
|
-
node: "linux-arm64-gnu",
|
|
137
|
-
python: "manylinux_2_39_aarch64",
|
|
138
|
-
os: RustReleaseOs.LINUX,
|
|
139
|
-
cpu: RustReleaseCpu.ARM64,
|
|
140
|
-
libc: "glibc",
|
|
141
|
-
glibcVersion: "2.39",
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
runner: "macos-15-intel",
|
|
145
|
-
cargo: "x86_64-apple-darwin",
|
|
146
|
-
node: "darwin-x64",
|
|
147
|
-
python: "macosx_13_0_x86_64",
|
|
148
|
-
os: RustReleaseOs.DARWIN,
|
|
149
|
-
cpu: RustReleaseCpu.X64,
|
|
150
|
-
},
|
|
151
|
-
{
|
|
152
|
-
runner: "macos-14",
|
|
153
|
-
cargo: "aarch64-apple-darwin",
|
|
154
|
-
node: "darwin-arm64",
|
|
155
|
-
python: "macosx_11_0_arm64",
|
|
156
|
-
os: RustReleaseOs.DARWIN,
|
|
157
|
-
cpu: RustReleaseCpu.ARM64,
|
|
158
|
-
},
|
|
159
|
-
{
|
|
160
|
-
runner: "windows-latest",
|
|
161
|
-
cargo: "x86_64-pc-windows-msvc",
|
|
162
|
-
node: "win32-x64-msvc",
|
|
163
|
-
python: "win_amd64",
|
|
164
|
-
os: RustReleaseOs.WINDOWS,
|
|
165
|
-
cpu: RustReleaseCpu.X64,
|
|
166
|
-
},
|
|
167
|
-
{
|
|
168
|
-
runner: "windows-11-vs2026-arm",
|
|
169
|
-
cargo: "aarch64-pc-windows-msvc",
|
|
170
|
-
node: "win32-arm64-msvc",
|
|
171
|
-
python: "win_arm64",
|
|
172
|
-
os: RustReleaseOs.WINDOWS,
|
|
173
|
-
cpu: RustReleaseCpu.ARM64,
|
|
174
|
-
},
|
|
175
|
-
] as const;
|
|
176
|
-
|
|
177
|
-
const UBRN_VERSION = "0.31.0-5";
|
|
178
|
-
const RELEASE_PLATFORMS_ENV = "DBX_TOOLS_RELEASE_PLATFORMS";
|
|
179
|
-
function releaseTargets(options: DBXToolsRustWorkspaceOptions): readonly UniFFIReleaseTarget[] {
|
|
180
|
-
if (options.releaseTargets && options.releasePlatforms) {
|
|
181
|
-
throw new Error("releaseTargets and releasePlatforms are mutually exclusive");
|
|
182
|
-
}
|
|
183
|
-
if (options.releaseTargets) return options.releaseTargets;
|
|
184
|
-
const releasePlatforms =
|
|
185
|
-
options.releasePlatforms ?? releasePlatformsFromEnvironment(process.env[RELEASE_PLATFORMS_ENV]);
|
|
186
|
-
if (!releasePlatforms) return UNIFFI_RELEASE_TARGETS;
|
|
187
|
-
return releasePlatforms.map((platform) => {
|
|
188
|
-
const target = UNIFFI_RELEASE_TARGETS.find(
|
|
189
|
-
(candidate) => candidate.os === platform.os && candidate.cpu === platform.cpu,
|
|
190
|
-
);
|
|
191
|
-
if (!target) {
|
|
192
|
-
throw new Error(`Unsupported Rust release platform: ${platform.os}-${platform.cpu}`);
|
|
193
|
-
}
|
|
194
|
-
return target;
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function releasePlatformsFromEnvironment(
|
|
199
|
-
value: string | undefined,
|
|
200
|
-
): RustReleasePlatform[] | undefined {
|
|
201
|
-
if (!value?.trim()) return undefined;
|
|
202
|
-
return value.split(",").map((entry) => {
|
|
203
|
-
const [os, cpu, ...extra] = entry.split(":").map((part) => part.trim());
|
|
204
|
-
if (
|
|
205
|
-
extra.length ||
|
|
206
|
-
!Object.values(RustReleaseOs).includes(os as RustReleaseOs) ||
|
|
207
|
-
!Object.values(RustReleaseCpu).includes(cpu as RustReleaseCpu)
|
|
208
|
-
) {
|
|
209
|
-
throw new Error(`Invalid ${RELEASE_PLATFORMS_ENV} entry: ${entry}`);
|
|
210
|
-
}
|
|
211
|
-
return { os: os as RustReleaseOs, cpu: cpu as RustReleaseCpu };
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
function rustCliRegistrySource(binaries: readonly RustReleaseBinaryMapping[]): string {
|
|
216
|
-
return [
|
|
217
|
-
"// GENERATED by projen - DO NOT EDIT.",
|
|
218
|
-
"// Rust release package options are the source of truth.",
|
|
219
|
-
"",
|
|
220
|
-
`export const RELEASE_BINARY_COMMANDS = ${JSON.stringify(binaries, null, 2)} as const;`,
|
|
221
|
-
"",
|
|
222
|
-
].join("\n");
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/** Update only source-less workspace package entries in Cargo.lock. */
|
|
226
|
-
export function synchronizeCargoLockVersions(
|
|
227
|
-
content: string,
|
|
228
|
-
packageNames: ReadonlySet<string>,
|
|
229
|
-
version: string,
|
|
230
|
-
): string {
|
|
231
|
-
return content
|
|
232
|
-
.split("[[package]]")
|
|
233
|
-
.map((block, index) => {
|
|
234
|
-
if (index === 0 || /^source = /m.test(block)) return block;
|
|
235
|
-
const name = /^name = "([^"]+)"$/m.exec(block)?.[1];
|
|
236
|
-
if (!name || !packageNames.has(name)) return block;
|
|
237
|
-
return block.replace(/^version = "[^"]+"$/m, `version = "${version}"`);
|
|
238
|
-
})
|
|
239
|
-
.join("[[package]]");
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/** Keep tracked workspace package versions in Cargo.lock aligned with VERSION. */
|
|
243
|
-
class RustWorkspaceVersionLock extends Component {
|
|
244
|
-
constructor(
|
|
245
|
-
project: Project,
|
|
246
|
-
private readonly packageNames: ReadonlySet<string>,
|
|
247
|
-
private readonly version: string,
|
|
248
|
-
) {
|
|
249
|
-
super(project);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
public override postSynthesize(): void {
|
|
253
|
-
const path = join(this.project.outdir, "Cargo.lock");
|
|
254
|
-
if (!existsSync(path)) return;
|
|
255
|
-
const content = readFileSync(path, "utf8");
|
|
256
|
-
const next = synchronizeCargoLockVersions(content, this.packageNames, this.version);
|
|
257
|
-
if (next !== content) writeFileSync(path, next);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/** Persisted mapping consumed by the focused Rust source watcher. */
|
|
262
|
-
export interface RustBindingMapping {
|
|
263
|
-
readonly crate: string;
|
|
264
|
-
readonly rust: string;
|
|
265
|
-
readonly node?: string;
|
|
266
|
-
readonly python?: string;
|
|
267
|
-
readonly nodePackage?: string;
|
|
268
|
-
readonly pythonPackage?: string;
|
|
269
|
-
readonly pythonModule?: string;
|
|
270
|
-
readonly facadeTarget?: boolean;
|
|
271
|
-
readonly dependencies?: readonly string[];
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/** Persisted Rust OpenAPI producer consumed by the shared OpenAPI task. */
|
|
275
|
-
export interface RustOpenApiMapping {
|
|
276
|
-
readonly crate: string;
|
|
277
|
-
readonly rust: string;
|
|
278
|
-
readonly output: string;
|
|
279
|
-
readonly binary?: string;
|
|
280
|
-
readonly features: readonly string[];
|
|
281
|
-
readonly noDefaultFeatures: boolean;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/** Persisted Rust workspace state consumed by `sync --watch`. */
|
|
285
|
-
export interface RustWorkspaceMapping {
|
|
286
|
-
readonly root: string;
|
|
287
|
-
readonly crates: readonly string[];
|
|
288
|
-
readonly bindings: readonly RustBindingMapping[];
|
|
289
|
-
readonly binaries: readonly RustReleaseBinaryMapping[];
|
|
290
|
-
readonly openapi: readonly RustOpenApiMapping[];
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/** One platform archive published for a Rust CLI binary. */
|
|
294
|
-
export interface RustReleaseBinaryAssetMapping {
|
|
295
|
-
readonly os: RustReleaseOs;
|
|
296
|
-
readonly cpu: RustReleaseCpu;
|
|
297
|
-
readonly name: string;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/** Runtime metadata for one lazily installed Rust CLI binary. */
|
|
301
|
-
export interface RustReleaseBinaryMapping {
|
|
302
|
-
readonly command: string;
|
|
303
|
-
readonly description: string;
|
|
304
|
-
readonly binaryName: string;
|
|
305
|
-
readonly hidden: boolean;
|
|
306
|
-
readonly unit: string;
|
|
307
|
-
readonly component: string;
|
|
308
|
-
readonly version: string;
|
|
309
|
-
readonly tagPrefix: string;
|
|
310
|
-
readonly tag: string;
|
|
311
|
-
readonly repository: string;
|
|
312
|
-
/** Cargo crate published or mirrored for `cargo install` when GitHub has no archive. */
|
|
313
|
-
readonly crateName: string;
|
|
314
|
-
/** Feature flags passed to `cargo install --features` for this binary target. */
|
|
315
|
-
readonly cargoFeatures: readonly string[];
|
|
316
|
-
readonly assets: readonly RustReleaseBinaryAssetMapping[];
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
function rustPackageDependencies(
|
|
320
|
-
packages: readonly DBXToolsRustProject[],
|
|
321
|
-
pkg: DBXToolsRustProject,
|
|
322
|
-
root: string,
|
|
323
|
-
workspaceDependencies: Readonly<Record<string, CargoDependency>> | undefined,
|
|
324
|
-
): DBXToolsRustProject[] {
|
|
325
|
-
return packages.filter(
|
|
326
|
-
(dependency) =>
|
|
327
|
-
dependency !== pkg &&
|
|
328
|
-
Object.entries(pkg.packageOptions.dependencies ?? {}).some(([name, value]) => {
|
|
329
|
-
const resolved =
|
|
330
|
-
typeof value === "object" && value.workspace
|
|
331
|
-
? (workspaceDependencies?.[name] ?? value)
|
|
332
|
-
: value;
|
|
333
|
-
return (
|
|
334
|
-
name === dependency.crateName ||
|
|
335
|
-
(typeof resolved === "object" &&
|
|
336
|
-
(resolved.package === dependency.crateName ||
|
|
337
|
-
(resolved.path !== undefined &&
|
|
338
|
-
resolve(
|
|
339
|
-
typeof value === "object" && value.workspace ? root : pkg.outdir,
|
|
340
|
-
resolved.path,
|
|
341
|
-
) === dependency.outdir)))
|
|
342
|
-
);
|
|
343
|
-
}),
|
|
344
|
-
);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
interface ResolvedRustWorkspaceOptions {
|
|
348
|
-
readonly root: string;
|
|
349
|
-
readonly nodeRoot: string;
|
|
350
|
-
readonly pythonRoot: string;
|
|
351
|
-
readonly scope: string;
|
|
352
|
-
readonly repository: string;
|
|
353
|
-
readonly nativeTargets: readonly UniFFIReleaseTarget[];
|
|
354
|
-
readonly pythonModulePrefix: string;
|
|
355
|
-
readonly packageOptions: Readonly<Record<string, RustCrateOptions>>;
|
|
356
|
-
readonly private?: boolean;
|
|
357
|
-
readonly release: boolean;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
function resolveRustWorkspaceOptions(
|
|
361
|
-
project: javascript.NodeProject,
|
|
362
|
-
options: DBXToolsRustWorkspaceOptions,
|
|
363
|
-
): ResolvedRustWorkspaceOptions {
|
|
364
|
-
const dbxToolsProject = isDBXToolsJavaScriptProject()(project) ? project : undefined;
|
|
365
|
-
const scope = stringUtils.toSlug(options.scope ?? dbxToolsProject?.scope ?? project.name);
|
|
366
|
-
return {
|
|
367
|
-
root: options.root ?? "packages/rs",
|
|
368
|
-
nodeRoot: options.nodeRoot ?? "packages/js/node",
|
|
369
|
-
pythonRoot: options.pythonRoot ?? "packages/py",
|
|
370
|
-
scope,
|
|
371
|
-
repository:
|
|
372
|
-
options.repository ??
|
|
373
|
-
projectRepositoryUrl(project) ??
|
|
374
|
-
projectUtils.repositoryUrl(project.outdir) ??
|
|
375
|
-
"",
|
|
376
|
-
nativeTargets: releaseTargets(options),
|
|
377
|
-
pythonModulePrefix: options.pythonModulePrefix ?? scope.replaceAll("-", "_"),
|
|
378
|
-
packageOptions: options.packages ?? {},
|
|
379
|
-
private: options.private,
|
|
380
|
-
release: options.release ?? true,
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
type RustPackageDependencyResolver = (pkg: DBXToolsRustProject) => DBXToolsRustProject[];
|
|
385
|
-
|
|
386
|
-
function createRustPackageDependencyResolver(
|
|
387
|
-
packages: readonly DBXToolsRustProject[],
|
|
388
|
-
project: javascript.NodeProject,
|
|
389
|
-
options: DBXToolsRustWorkspaceOptions,
|
|
390
|
-
): RustPackageDependencyResolver {
|
|
391
|
-
return (pkg) =>
|
|
392
|
-
rustPackageDependencies(packages, pkg, project.outdir, options.workspaceDependencies);
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
function planRustReleaseBinaries(
|
|
396
|
-
project: javascript.NodeProject,
|
|
397
|
-
packages: readonly DBXToolsRustProject[],
|
|
398
|
-
resolved: ResolvedRustWorkspaceOptions,
|
|
399
|
-
): RustReleaseBinaryMapping[] {
|
|
400
|
-
const commands = new Set<string>();
|
|
401
|
-
const binaries = packages.flatMap((pkg) => {
|
|
402
|
-
const configuredBinaries = [
|
|
403
|
-
...(pkg.packageOptions.cli
|
|
404
|
-
? [
|
|
405
|
-
{
|
|
406
|
-
name: pkg.packageOptions.binaryName ?? pkg.crateName,
|
|
407
|
-
defaultCommand: pkg.packageOptions.directory,
|
|
408
|
-
description: pkg.packageOptions.description,
|
|
409
|
-
release: pkg.packageOptions.release,
|
|
410
|
-
excludedOs: pkg.packageOptions.releaseExcludeOs,
|
|
411
|
-
cli: pkg.packageOptions.cli,
|
|
412
|
-
requiredFeatures: undefined,
|
|
413
|
-
},
|
|
414
|
-
]
|
|
415
|
-
: []),
|
|
416
|
-
...(pkg.packageOptions.binaries ?? [])
|
|
417
|
-
.filter((binary) => Boolean(binary.cli))
|
|
418
|
-
.map((binary) => ({
|
|
419
|
-
name: binary.name,
|
|
420
|
-
defaultCommand: binary.name,
|
|
421
|
-
description: binary.description ?? pkg.packageOptions.description,
|
|
422
|
-
release: binary.release,
|
|
423
|
-
excludedOs: binary.releaseExcludeOs,
|
|
424
|
-
cli: binary.cli!,
|
|
425
|
-
requiredFeatures: binary.requiredFeatures,
|
|
426
|
-
})),
|
|
427
|
-
];
|
|
428
|
-
return configuredBinaries.map((binary) => {
|
|
429
|
-
if (!binary.release) {
|
|
430
|
-
throw new Error(`${pkg.crateName} must set release when cli is configured`);
|
|
431
|
-
}
|
|
432
|
-
const configured = binary.cli;
|
|
433
|
-
const command =
|
|
434
|
-
typeof configured === "object" && configured.command
|
|
435
|
-
? configured.command
|
|
436
|
-
: binary.defaultCommand;
|
|
437
|
-
if (!/^[a-z0-9][a-z0-9-]*$/.test(command)) {
|
|
438
|
-
throw new Error(`Invalid Rust CLI command: ${command}`);
|
|
439
|
-
}
|
|
440
|
-
if (commands.has(command)) {
|
|
441
|
-
throw new Error(`Duplicate Rust CLI command: ${command}`);
|
|
442
|
-
}
|
|
443
|
-
commands.add(command);
|
|
444
|
-
const description =
|
|
445
|
-
(typeof configured === "object" ? configured.description : undefined) ??
|
|
446
|
-
binary.description ??
|
|
447
|
-
pkg.crateName;
|
|
448
|
-
const hidden = typeof configured === "object" && configured.hidden === true;
|
|
449
|
-
const excludedOs = new Set(binary.excludedOs ?? []);
|
|
450
|
-
const binaryName = binary.name;
|
|
451
|
-
const identity = isDBXToolsJavaScriptProject()(project)
|
|
452
|
-
? project.releaseCatalog.releaseIdentityFor(pkg)
|
|
453
|
-
: {
|
|
454
|
-
component: defaultReleaseUnitId("rust", pkg.crateName),
|
|
455
|
-
version: readWorkspaceVersion(project.outdir),
|
|
456
|
-
};
|
|
457
|
-
const tagPrefix =
|
|
458
|
-
isDBXToolsJavaScriptProject()(project) && project.releaseCatalog.mode === "independent"
|
|
459
|
-
? `${identity.component}-v`
|
|
460
|
-
: "v";
|
|
461
|
-
return {
|
|
462
|
-
command,
|
|
463
|
-
description,
|
|
464
|
-
binaryName,
|
|
465
|
-
hidden,
|
|
466
|
-
unit: "id" in identity ? identity.id : identity.component,
|
|
467
|
-
component: identity.component,
|
|
468
|
-
version: identity.version,
|
|
469
|
-
tagPrefix,
|
|
470
|
-
tag: `${tagPrefix}${identity.version}`,
|
|
471
|
-
repository: resolved.repository,
|
|
472
|
-
crateName: pkg.crateName,
|
|
473
|
-
cargoFeatures: [...(binary.requiredFeatures ?? [])],
|
|
474
|
-
assets: resolved.nativeTargets
|
|
475
|
-
.filter((target) => !excludedOs.has(target.os))
|
|
476
|
-
.map((target) => ({
|
|
477
|
-
os: target.os,
|
|
478
|
-
cpu: target.cpu,
|
|
479
|
-
name: releaseBinaryAssetName(binaryName, target.node, target.os),
|
|
480
|
-
})),
|
|
481
|
-
};
|
|
482
|
-
});
|
|
483
|
-
});
|
|
484
|
-
if (binaries.length && !resolved.repository) {
|
|
485
|
-
throw new Error("A repository URL is required when Rust CLI commands are configured");
|
|
486
|
-
}
|
|
487
|
-
return binaries;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
function validateRustReleaseGraph(
|
|
491
|
-
packages: readonly DBXToolsRustProject[],
|
|
492
|
-
packageDependencies: RustPackageDependencyResolver,
|
|
493
|
-
): void {
|
|
494
|
-
for (const pkg of packages) {
|
|
495
|
-
const excludedOs = new Set(pkg.packageOptions.releaseExcludeOs ?? []);
|
|
496
|
-
if (excludedOs.size && pkg.uniffi) {
|
|
497
|
-
throw new Error(
|
|
498
|
-
`${pkg.crateName} cannot set releaseExcludeOs because UniFFI requires every configured target`,
|
|
499
|
-
);
|
|
500
|
-
}
|
|
501
|
-
for (const dependency of packageDependencies(pkg)) {
|
|
502
|
-
for (const os of dependency.packageOptions.releaseExcludeOs ?? []) {
|
|
503
|
-
if (!excludedOs.has(os)) {
|
|
504
|
-
throw new Error(
|
|
505
|
-
`${pkg.crateName} must exclude ${os} release builds because it depends on ${dependency.crateName}`,
|
|
506
|
-
);
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
interface RustBindingPlan {
|
|
514
|
-
readonly packages: readonly DBXToolsRustProject[];
|
|
515
|
-
readonly mappings: readonly RustBindingMapping[];
|
|
516
|
-
readonly dependencies: ReadonlyMap<
|
|
517
|
-
DBXToolsRustProject,
|
|
518
|
-
Readonly<Record<"node" | "python", readonly DBXToolsRustProject[]>>
|
|
519
|
-
>;
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
function planRustBindings(
|
|
523
|
-
packages: readonly DBXToolsRustProject[],
|
|
524
|
-
packageDependencies: RustPackageDependencyResolver,
|
|
525
|
-
resolved: ResolvedRustWorkspaceOptions,
|
|
526
|
-
): RustBindingPlan {
|
|
527
|
-
const bindings = packages.filter((pkg) => pkg.uniffi);
|
|
528
|
-
const dependencies = new Map<
|
|
529
|
-
DBXToolsRustProject,
|
|
530
|
-
Readonly<Record<"node" | "python", readonly DBXToolsRustProject[]>>
|
|
531
|
-
>();
|
|
532
|
-
for (const pkg of bindings) {
|
|
533
|
-
const direct = packageDependencies(pkg);
|
|
534
|
-
dependencies.set(pkg, {
|
|
535
|
-
node: bindings.filter(
|
|
536
|
-
(dependency) =>
|
|
537
|
-
(dependency.packageOptions.bindings ?? ["node", "python"]).includes("node") &&
|
|
538
|
-
direct.includes(dependency),
|
|
539
|
-
),
|
|
540
|
-
python: bindings.filter(
|
|
541
|
-
(dependency) =>
|
|
542
|
-
(dependency.packageOptions.bindings ?? ["node", "python"]).includes("python") &&
|
|
543
|
-
direct.includes(dependency),
|
|
544
|
-
),
|
|
545
|
-
});
|
|
546
|
-
}
|
|
547
|
-
const mappings = orderRustBindings(
|
|
548
|
-
bindings.map((pkg) => {
|
|
549
|
-
const targets = pkg.packageOptions.bindings ?? ["node", "python"];
|
|
550
|
-
const packageDirectory = `${stringUtils.toSlug(pkg.packageOptions.directory)}-rs`;
|
|
551
|
-
const direct = dependencies.get(pkg);
|
|
552
|
-
const dependencyCrates = [
|
|
553
|
-
...new Set([...(direct?.node ?? []), ...(direct?.python ?? [])]),
|
|
554
|
-
].map((dependency) => dependency.crateName);
|
|
555
|
-
return {
|
|
556
|
-
crate: pkg.crateName,
|
|
557
|
-
...(dependencyCrates.length ? { dependencies: dependencyCrates } : {}),
|
|
558
|
-
rust: `${resolved.root}/${pkg.packageOptions.directory}`,
|
|
559
|
-
...(targets.includes("node")
|
|
560
|
-
? {
|
|
561
|
-
node: `${resolved.nodeRoot}/${packageDirectory}`,
|
|
562
|
-
nodePackage: `@${resolved.scope}/${packageDirectory}`,
|
|
563
|
-
}
|
|
564
|
-
: {}),
|
|
565
|
-
...(targets.includes("python")
|
|
566
|
-
? {
|
|
567
|
-
python: `${resolved.pythonRoot}/${packageDirectory}`,
|
|
568
|
-
pythonPackage: `${resolved.scope}-${packageDirectory}`,
|
|
569
|
-
pythonModule: pythonModuleName(resolved.pythonModulePrefix, packageDirectory),
|
|
570
|
-
}
|
|
571
|
-
: {}),
|
|
572
|
-
};
|
|
573
|
-
}),
|
|
574
|
-
);
|
|
575
|
-
return { packages: bindings, mappings, dependencies };
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
function bindingDependencies(
|
|
579
|
-
plan: RustBindingPlan,
|
|
580
|
-
pkg: DBXToolsRustProject,
|
|
581
|
-
language: "node" | "python",
|
|
582
|
-
): readonly DBXToolsRustProject[] {
|
|
583
|
-
return plan.dependencies.get(pkg)?.[language] ?? [];
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
function planPythonBindingPackages(
|
|
587
|
-
plan: RustBindingPlan,
|
|
588
|
-
resolved: ResolvedRustWorkspaceOptions,
|
|
589
|
-
): PythonPackageOptions[] {
|
|
590
|
-
return plan.packages
|
|
591
|
-
.filter((pkg) => (pkg.packageOptions.bindings ?? ["node", "python"]).includes("python"))
|
|
592
|
-
.map((pkg) => {
|
|
593
|
-
const directory = `${stringUtils.toSlug(pkg.packageOptions.directory)}-rs`;
|
|
594
|
-
const module = pythonModuleName(resolved.pythonModulePrefix, directory);
|
|
595
|
-
const name = `${resolved.scope}-${directory}`;
|
|
596
|
-
return {
|
|
597
|
-
directory,
|
|
598
|
-
name,
|
|
599
|
-
module,
|
|
600
|
-
description: `Python bindings for ${pkg.crateName}`,
|
|
601
|
-
uniffi: true,
|
|
602
|
-
internalDependencies: bindingDependencies(plan, pkg, "python").map(
|
|
603
|
-
(dependency) => `${stringUtils.toSlug(dependency.packageOptions.directory)}-rs`,
|
|
604
|
-
),
|
|
605
|
-
generatedSources: [
|
|
606
|
-
`src/${module.replaceAll(".", "/")}/bindings.py`,
|
|
607
|
-
`src/${module.replaceAll(".", "/")}/__init__.py`,
|
|
608
|
-
],
|
|
609
|
-
trustedPublisher: {
|
|
610
|
-
environment: `pypi-${name}`,
|
|
611
|
-
artifacts: `platform-specific wheels for ${resolved.nativeTargets
|
|
612
|
-
.map((target) => `${target.os}-${target.cpu}`)
|
|
613
|
-
.join(", ")}; all architectures publish to this one PyPI project`,
|
|
614
|
-
},
|
|
615
|
-
};
|
|
616
|
-
});
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
interface RustNodeBindingPackagePlan {
|
|
620
|
-
readonly binding: DBXToolsRustProject;
|
|
621
|
-
readonly memberPath: string;
|
|
622
|
-
readonly name: string;
|
|
623
|
-
readonly optionalDependencies: Readonly<Record<string, string>>;
|
|
624
|
-
readonly internalDependencies: readonly string[];
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
function planNodeBindingPackages(
|
|
628
|
-
project: javascript.NodeProject,
|
|
629
|
-
plan: RustBindingPlan,
|
|
630
|
-
resolved: ResolvedRustWorkspaceOptions,
|
|
631
|
-
): RustNodeBindingPackagePlan[] {
|
|
632
|
-
return plan.packages
|
|
633
|
-
.filter((pkg) => (pkg.packageOptions.bindings ?? ["node", "python"]).includes("node"))
|
|
634
|
-
.map((binding) => {
|
|
635
|
-
const directory = `${stringUtils.toSlug(binding.packageOptions.directory)}-rs`;
|
|
636
|
-
const version = isDBXToolsJavaScriptProject()(project)
|
|
637
|
-
? project.releaseCatalog.versionFor(binding)
|
|
638
|
-
: readWorkspaceVersion(project.outdir);
|
|
639
|
-
return {
|
|
640
|
-
binding,
|
|
641
|
-
memberPath: `${resolved.nodeRoot}/${directory}`,
|
|
642
|
-
name: `@${resolved.scope}/${directory.toLowerCase().replace(/[^a-z0-9-]+/g, "-")}`,
|
|
643
|
-
optionalDependencies: resolved.release
|
|
644
|
-
? Object.fromEntries(
|
|
645
|
-
resolved.nativeTargets.map((target) => [
|
|
646
|
-
`@${resolved.scope}/${directory}-${target.node}`,
|
|
647
|
-
version,
|
|
648
|
-
]),
|
|
649
|
-
)
|
|
650
|
-
: {},
|
|
651
|
-
internalDependencies: bindingDependencies(plan, binding, "node").map(
|
|
652
|
-
(dependency) =>
|
|
653
|
-
`@${resolved.scope}/${stringUtils.toSlug(dependency.packageOptions.directory)}-rs@workspace:*`,
|
|
654
|
-
),
|
|
655
|
-
};
|
|
656
|
-
});
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
function createRustPackages(
|
|
660
|
-
project: javascript.NodeProject,
|
|
661
|
-
resolved: ResolvedRustWorkspaceOptions,
|
|
662
|
-
): DBXToolsRustProject[] {
|
|
663
|
-
return discoverRustCrates(resolve(project.outdir, resolved.root)).map((directory) => {
|
|
664
|
-
const configured = {
|
|
665
|
-
private: resolved.private,
|
|
666
|
-
...resolved.packageOptions[directory],
|
|
667
|
-
};
|
|
668
|
-
const name = `${resolved.scope}-${directory.toLowerCase().replace(/[^a-z0-9-]+/g, "-")}`;
|
|
669
|
-
const version =
|
|
670
|
-
isDBXToolsJavaScriptProject()(project) && project.releaseCatalog.mode === "independent"
|
|
671
|
-
? project.releaseCatalog.versionForRegistration(
|
|
672
|
-
"rust",
|
|
673
|
-
name,
|
|
674
|
-
`${resolved.root}/${directory}`,
|
|
675
|
-
)
|
|
676
|
-
: undefined;
|
|
677
|
-
return new DBXToolsRustProject({
|
|
678
|
-
parent: project,
|
|
679
|
-
outdir: `${resolved.root}/${directory}`,
|
|
680
|
-
name,
|
|
681
|
-
directory,
|
|
682
|
-
workspaceRoot: resolved.root,
|
|
683
|
-
workspace: true,
|
|
684
|
-
scope: resolved.scope,
|
|
685
|
-
...(version ? { version } : {}),
|
|
686
|
-
...configured,
|
|
687
|
-
});
|
|
688
|
-
});
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
function configureRustCliRegistry(
|
|
692
|
-
project: javascript.NodeProject,
|
|
693
|
-
path: string | undefined,
|
|
694
|
-
binaries: readonly RustReleaseBinaryMapping[],
|
|
695
|
-
): void {
|
|
696
|
-
if (binaries.length && !path) {
|
|
697
|
-
throw new Error("cliRegistryPath is required when Rust CLI commands are configured");
|
|
698
|
-
}
|
|
699
|
-
if (!path) return;
|
|
700
|
-
new TextFile(project, path, {
|
|
701
|
-
lines: rustCliRegistrySource(binaries).trimEnd().split("\n"),
|
|
702
|
-
});
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
function configureRustBindingFiles(
|
|
706
|
-
plan: RustBindingPlan,
|
|
707
|
-
resolved: ResolvedRustWorkspaceOptions,
|
|
708
|
-
): void {
|
|
709
|
-
for (const pkg of plan.packages) {
|
|
710
|
-
const dependencies = bindingDependencies(plan, pkg, "python");
|
|
711
|
-
if (dependencies.length === 0) continue;
|
|
712
|
-
pkg.tryRemoveFile("uniffi.toml");
|
|
713
|
-
new TomlFile(pkg, "uniffi.toml", {
|
|
714
|
-
marker: false,
|
|
715
|
-
obj: structuredTomlSections({
|
|
716
|
-
"bindings.python": { cdylib_name: pkg.crateName.replaceAll("-", "_") },
|
|
717
|
-
"bindings.typescript": { strictTypeChecking: true },
|
|
718
|
-
...pkg.packageOptions.uniffiConfig,
|
|
719
|
-
"bindings.python.external_packages": Object.fromEntries(
|
|
720
|
-
dependencies.map((dependency) => [
|
|
721
|
-
dependency.crateName.replaceAll("-", "_"),
|
|
722
|
-
`${pythonModuleName(
|
|
723
|
-
resolved.pythonModulePrefix,
|
|
724
|
-
`${stringUtils.toSlug(dependency.packageOptions.directory)}-rs`,
|
|
725
|
-
)}.bindings`,
|
|
726
|
-
]),
|
|
727
|
-
),
|
|
728
|
-
}),
|
|
729
|
-
});
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
function configureRustBindingIgnores(
|
|
734
|
-
project: javascript.NodeProject,
|
|
735
|
-
bindings: readonly RustBindingMapping[],
|
|
736
|
-
): void {
|
|
737
|
-
project.gitignore.addPatterns(
|
|
738
|
-
"!/Cargo.lock",
|
|
739
|
-
"target/",
|
|
740
|
-
...bindings.flatMap((binding) => [
|
|
741
|
-
...(binding.node ? [`${binding.node}/src/*${binding.crate.replaceAll("-", "_")}.*`] : []),
|
|
742
|
-
...(binding.python && binding.pythonModule
|
|
743
|
-
? [
|
|
744
|
-
`${binding.python}/src/${binding.pythonModule.replaceAll(".", "/")}/bindings.py`,
|
|
745
|
-
`${binding.python}/src/${binding.pythonModule.replaceAll(".", "/")}/*${binding.crate.replaceAll("-", "_")}.*`,
|
|
746
|
-
]
|
|
747
|
-
: []),
|
|
748
|
-
]),
|
|
749
|
-
);
|
|
750
|
-
for (const binding of bindings) {
|
|
751
|
-
if (!binding.node) continue;
|
|
752
|
-
project.prettier?.addIgnorePattern(`${binding.node}/src/bindings.ts`);
|
|
753
|
-
project.prettier?.addIgnorePattern(`${binding.node}/src/_bindings*.ts`);
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
function createRustNodeBindingPackages(
|
|
758
|
-
project: javascript.NodeProject,
|
|
759
|
-
plans: readonly RustNodeBindingPackagePlan[],
|
|
760
|
-
): DBXToolsTypeScriptProject[] {
|
|
761
|
-
const existing = new Map(
|
|
762
|
-
project.subprojects.map((child) => [relative(project.outdir, child.outdir), child]),
|
|
763
|
-
);
|
|
764
|
-
return plans.map((plan) => {
|
|
765
|
-
const found = existing.get(plan.memberPath);
|
|
766
|
-
const existingNode = found instanceof DBXToolsTypeScriptProject ? found : undefined;
|
|
767
|
-
const node =
|
|
768
|
-
existingNode ??
|
|
769
|
-
new DBXToolsTypeScriptProject({
|
|
770
|
-
parent: project,
|
|
771
|
-
outdir: plan.memberPath,
|
|
772
|
-
name: plan.name,
|
|
773
|
-
tags: ["node"],
|
|
774
|
-
});
|
|
775
|
-
node.package.addField("name", plan.name);
|
|
776
|
-
node.dbxToolsConfig.uniffi = true;
|
|
777
|
-
node.package.addField("description", `Node bindings for ${plan.binding.crateName}`);
|
|
778
|
-
if (Object.keys(plan.optionalDependencies).length) {
|
|
779
|
-
node.package.addField("optionalDependencies", plan.optionalDependencies);
|
|
780
|
-
}
|
|
781
|
-
node.addDeps("@ubjs/core@0.31.0-5", "@ubjs/node@0.31.0-5");
|
|
782
|
-
node.addDeps(...plan.internalDependencies);
|
|
783
|
-
node.addDevDeps(`uniffi-bindgen-react-native@${UBRN_VERSION}`);
|
|
784
|
-
return node;
|
|
785
|
-
});
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
function createRustWorkspaceMapping(
|
|
789
|
-
packages: readonly DBXToolsRustProject[],
|
|
790
|
-
bindings: readonly RustBindingMapping[],
|
|
791
|
-
binaries: readonly RustReleaseBinaryMapping[],
|
|
792
|
-
resolved: ResolvedRustWorkspaceOptions,
|
|
793
|
-
): RustWorkspaceMapping {
|
|
794
|
-
const openapiRoot = join(dirname(resolved.nodeRoot), "openapi");
|
|
795
|
-
return {
|
|
796
|
-
root: resolved.root,
|
|
797
|
-
crates: packages.map((pkg) => `${resolved.root}/${pkg.packageOptions.directory}`),
|
|
798
|
-
bindings,
|
|
799
|
-
binaries,
|
|
800
|
-
openapi: packages.flatMap((pkg) => {
|
|
801
|
-
if (!pkg.packageOptions.openapi) return [];
|
|
802
|
-
const options = pkg.packageOptions.openapi === true ? {} : pkg.packageOptions.openapi;
|
|
803
|
-
return [
|
|
804
|
-
{
|
|
805
|
-
crate: pkg.crateName,
|
|
806
|
-
rust: `${resolved.root}/${pkg.packageOptions.directory}`,
|
|
807
|
-
output: toPosix(join(openapiRoot, pkg.packageOptions.directory)),
|
|
808
|
-
...(options.binary ? { binary: options.binary } : {}),
|
|
809
|
-
features: [...(options.features ?? [])],
|
|
810
|
-
noDefaultFeatures: options.noDefaultFeatures ?? false,
|
|
811
|
-
},
|
|
812
|
-
];
|
|
813
|
-
}),
|
|
814
|
-
};
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
function configureRustWorkspaceFiles(
|
|
818
|
-
project: javascript.NodeProject,
|
|
819
|
-
packages: readonly DBXToolsRustProject[],
|
|
820
|
-
options: DBXToolsRustWorkspaceOptions,
|
|
821
|
-
resolved: ResolvedRustWorkspaceOptions,
|
|
822
|
-
): void {
|
|
823
|
-
const manifest: Record<string, unknown> = {
|
|
824
|
-
workspace: {
|
|
825
|
-
members: packages.map((pkg) => `${resolved.root}/${pkg.packageOptions.directory}`),
|
|
826
|
-
"default-members": packages
|
|
827
|
-
.filter((pkg) => !pkg.uniffi)
|
|
828
|
-
.map((pkg) => `${resolved.root}/${pkg.packageOptions.directory}`),
|
|
829
|
-
resolver: "2",
|
|
830
|
-
package: {
|
|
831
|
-
...(!isDBXToolsJavaScriptProject()(project) || project.releaseCatalog.mode === "fixed"
|
|
832
|
-
? { version: readWorkspaceVersion(project.outdir) }
|
|
833
|
-
: {}),
|
|
834
|
-
edition: options.edition ?? "2021",
|
|
835
|
-
"rust-version": options.rustVersion ?? "1.82",
|
|
836
|
-
license: options.license ?? DBX_TOOLS_LICENSE,
|
|
837
|
-
repository: resolved.repository,
|
|
838
|
-
},
|
|
839
|
-
...(options.workspaceDependencies
|
|
840
|
-
? {
|
|
841
|
-
dependencies: Object.fromEntries(
|
|
842
|
-
Object.entries(options.workspaceDependencies).map(([name, value]) => [
|
|
843
|
-
name,
|
|
844
|
-
cargoDependency(value),
|
|
845
|
-
]),
|
|
846
|
-
),
|
|
847
|
-
}
|
|
848
|
-
: {}),
|
|
849
|
-
},
|
|
850
|
-
};
|
|
851
|
-
new TomlFile(project, "Cargo.toml", {
|
|
852
|
-
marker: false,
|
|
853
|
-
obj: manifest,
|
|
854
|
-
});
|
|
855
|
-
new TomlFile(project, ".cargo/config.toml", {
|
|
856
|
-
marker: false,
|
|
857
|
-
obj: {
|
|
858
|
-
target: {
|
|
859
|
-
"x86_64-pc-windows-msvc": {
|
|
860
|
-
rustflags: ["-C", "target-feature=+crt-static"],
|
|
861
|
-
},
|
|
862
|
-
"aarch64-pc-windows-msvc": {
|
|
863
|
-
rustflags: ["-C", "target-feature=+crt-static"],
|
|
864
|
-
},
|
|
865
|
-
},
|
|
866
|
-
},
|
|
867
|
-
});
|
|
868
|
-
new RustWorkspaceVersionLock(
|
|
869
|
-
project,
|
|
870
|
-
new Set(packages.map((pkg) => pkg.crateName)),
|
|
871
|
-
readWorkspaceVersion(project.outdir),
|
|
872
|
-
);
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
function configureRustWorkspaceTasks(project: javascript.NodeProject): void {
|
|
876
|
-
project.addTask("rs:format", { exec: "cargo fmt --all" });
|
|
877
|
-
project.addTask("rs:lint", { exec: "cargo clippy --workspace --all-targets --all-features" });
|
|
878
|
-
project.addTask("rs:test", { exec: "cargo test --workspace" });
|
|
879
|
-
project.addTask("rs:build", { exec: "cargo build --workspace" });
|
|
880
|
-
project.addTask("rs:bindings", {
|
|
881
|
-
exec: "bun node_modules/@dbx-tools/projen/tasks/rust.ts",
|
|
882
|
-
description: "Generate language bindings for UniFFI-enabled Rust crates",
|
|
883
|
-
});
|
|
884
|
-
project.removeTask("rs:bindings:demo");
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
function rustReleaseDependencies(
|
|
888
|
-
root: DBXToolsJavaScriptProject,
|
|
889
|
-
project: DBXToolsRustProject,
|
|
890
|
-
packages: readonly DBXToolsRustProject[],
|
|
891
|
-
dependencies: Readonly<Record<string, CargoDependency>> | undefined,
|
|
892
|
-
development: boolean,
|
|
893
|
-
): ReleaseDependencyInput[] {
|
|
894
|
-
return Object.values(dependencies ?? {}).flatMap((dependency) => {
|
|
895
|
-
if (typeof dependency === "string" || !dependency.path) return [];
|
|
896
|
-
const dependencyPath = resolve(project.outdir, dependency.path);
|
|
897
|
-
const target = packages.find((candidate) => resolve(candidate.outdir) === dependencyPath);
|
|
898
|
-
if (!target) return [];
|
|
899
|
-
const configuredRequirement = dependency.version;
|
|
900
|
-
const requirement = configuredRequirement
|
|
901
|
-
? /^[0-9]/.test(configuredRequirement)
|
|
902
|
-
? `^${configuredRequirement}`
|
|
903
|
-
: configuredRequirement
|
|
904
|
-
: `^${root.releaseCatalog.versionFor(target)}`;
|
|
905
|
-
return [
|
|
906
|
-
{
|
|
907
|
-
target: target.crateName,
|
|
908
|
-
kind: development ? ("development" as const) : ("runtime" as const),
|
|
909
|
-
requirement,
|
|
910
|
-
propagation: development ? ("never" as const) : ("outside-range" as const),
|
|
911
|
-
publishOrder: !development,
|
|
912
|
-
internal: true,
|
|
913
|
-
},
|
|
914
|
-
];
|
|
915
|
-
});
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
function configureIndependentRustVersions(
|
|
919
|
-
project: DBXToolsJavaScriptProject,
|
|
920
|
-
packages: readonly DBXToolsRustProject[],
|
|
921
|
-
): void {
|
|
922
|
-
for (const pkg of packages) {
|
|
923
|
-
for (const [section, dependencies] of [
|
|
924
|
-
["dependencies", pkg.packageOptions.dependencies],
|
|
925
|
-
["dev-dependencies", pkg.packageOptions.devDependencies],
|
|
926
|
-
] as const) {
|
|
927
|
-
for (const [name, dependency] of Object.entries(dependencies ?? {})) {
|
|
928
|
-
if (typeof dependency === "string" || !dependency.path) continue;
|
|
929
|
-
const dependencyPath = resolve(pkg.outdir, dependency.path);
|
|
930
|
-
const target = packages.find((candidate) => resolve(candidate.outdir) === dependencyPath);
|
|
931
|
-
if (!target) continue;
|
|
932
|
-
pkg.manifestFile.addOverride(
|
|
933
|
-
`${section}.${name}.version`,
|
|
934
|
-
project.releaseCatalog.versionFor(target),
|
|
935
|
-
);
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
|
|
941
|
-
/** Generated Rust workspace plus convention-derived UniFFI binding packages. */
|
|
942
|
-
export class DBXToolsRustWorkspace extends Component {
|
|
943
|
-
readonly packages: readonly DBXToolsRustProject[];
|
|
944
|
-
readonly nodePackages: readonly DBXToolsTypeScriptProject[];
|
|
945
|
-
readonly pythonPackages: readonly PythonPackageOptions[];
|
|
946
|
-
readonly bindingMappings: readonly RustBindingMapping[];
|
|
947
|
-
readonly releaseBinaries: readonly RustReleaseBinaryMapping[];
|
|
948
|
-
readonly workspaceMapping: RustWorkspaceMapping;
|
|
949
|
-
|
|
950
|
-
constructor(project: javascript.NodeProject, options: DBXToolsRustWorkspaceOptions) {
|
|
951
|
-
super(project);
|
|
952
|
-
const resolved = resolveRustWorkspaceOptions(project, options);
|
|
953
|
-
this.packages = createRustPackages(project, resolved);
|
|
954
|
-
const packageDependencies = createRustPackageDependencyResolver(
|
|
955
|
-
this.packages,
|
|
956
|
-
project,
|
|
957
|
-
options,
|
|
958
|
-
);
|
|
959
|
-
if (isDBXToolsJavaScriptProject()(project)) {
|
|
960
|
-
for (const pkg of this.packages) {
|
|
961
|
-
project.releaseCatalog.registerProject(pkg, {
|
|
962
|
-
language: "rust",
|
|
963
|
-
identity: pkg.crateName,
|
|
964
|
-
publish: !pkg.packageOptions.private || pkg.packageOptions.release === true,
|
|
965
|
-
sourcePaths: [`${toPosix(relative(project.outdir, pkg.outdir))}/src`],
|
|
966
|
-
});
|
|
967
|
-
}
|
|
968
|
-
for (const pkg of this.packages) {
|
|
969
|
-
project.releaseCatalog.configureProject(pkg, {
|
|
970
|
-
dependencies: [
|
|
971
|
-
...rustReleaseDependencies(
|
|
972
|
-
project,
|
|
973
|
-
pkg,
|
|
974
|
-
this.packages,
|
|
975
|
-
pkg.packageOptions.dependencies,
|
|
976
|
-
false,
|
|
977
|
-
),
|
|
978
|
-
...rustReleaseDependencies(
|
|
979
|
-
project,
|
|
980
|
-
pkg,
|
|
981
|
-
this.packages,
|
|
982
|
-
pkg.packageOptions.devDependencies,
|
|
983
|
-
true,
|
|
984
|
-
),
|
|
985
|
-
],
|
|
986
|
-
});
|
|
987
|
-
}
|
|
988
|
-
if (project.releaseCatalog.mode === "independent") {
|
|
989
|
-
configureIndependentRustVersions(project, this.packages);
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
this.releaseBinaries = planRustReleaseBinaries(project, this.packages, resolved);
|
|
993
|
-
configureRustCliRegistry(project, options.cliRegistryPath, this.releaseBinaries);
|
|
994
|
-
if (isDBXToolsJavaScriptProject()(project)) {
|
|
995
|
-
for (const binary of this.releaseBinaries) {
|
|
996
|
-
project.releaseCatalog.registerArtifact(binary.unit, {
|
|
997
|
-
id: `${binary.unit}:github-binary:${binary.binaryName}`,
|
|
998
|
-
kind: "github-binary",
|
|
999
|
-
name: binary.binaryName,
|
|
1000
|
-
generated: true,
|
|
1001
|
-
data: {
|
|
1002
|
-
binary: binary.binaryName,
|
|
1003
|
-
crate: binary.crateName,
|
|
1004
|
-
features: binary.cargoFeatures,
|
|
1005
|
-
targets: resolved.nativeTargets.filter((target) =>
|
|
1006
|
-
binary.assets.some((asset) => asset.os === target.os && asset.cpu === target.cpu),
|
|
1007
|
-
),
|
|
1008
|
-
},
|
|
1009
|
-
});
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
const bindingPlan = planRustBindings(this.packages, packageDependencies, resolved);
|
|
1014
|
-
validateRustReleaseGraph(this.packages, packageDependencies);
|
|
1015
|
-
this.bindingMappings = bindingPlan.mappings;
|
|
1016
|
-
configureRustBindingFiles(bindingPlan, resolved);
|
|
1017
|
-
const releaseEnabled =
|
|
1018
|
-
resolved.release &&
|
|
1019
|
-
(this.bindingMappings.length > 0 ||
|
|
1020
|
-
this.packages.some((pkg) => pkg.packageOptions.release || !pkg.packageOptions.private));
|
|
1021
|
-
this.workspaceMapping = createRustWorkspaceMapping(
|
|
1022
|
-
this.packages,
|
|
1023
|
-
this.bindingMappings,
|
|
1024
|
-
this.releaseBinaries,
|
|
1025
|
-
resolved,
|
|
1026
|
-
);
|
|
1027
|
-
if (isDBXToolsJavaScriptProject()(project)) {
|
|
1028
|
-
project.dbxToolsConfig.rust = this.workspaceMapping;
|
|
1029
|
-
}
|
|
1030
|
-
configureRustBindingIgnores(project, this.bindingMappings);
|
|
1031
|
-
this.pythonPackages = planPythonBindingPackages(bindingPlan, resolved);
|
|
1032
|
-
this.nodePackages = createRustNodeBindingPackages(
|
|
1033
|
-
project,
|
|
1034
|
-
planNodeBindingPackages(project, bindingPlan, resolved),
|
|
1035
|
-
);
|
|
1036
|
-
if (isDBXToolsJavaScriptProject()(project)) {
|
|
1037
|
-
for (const mapping of this.bindingMappings) {
|
|
1038
|
-
const unit = defaultReleaseUnitId("rust", mapping.crate);
|
|
1039
|
-
project.releaseCatalog.addUnit({
|
|
1040
|
-
id: unit,
|
|
1041
|
-
projectPaths: [mapping.rust, mapping.node, mapping.python].filter(
|
|
1042
|
-
(path): path is string => Boolean(path),
|
|
1043
|
-
),
|
|
1044
|
-
});
|
|
1045
|
-
if (mapping.nodePackage) {
|
|
1046
|
-
project.releaseCatalog.registerArtifact(unit, {
|
|
1047
|
-
id: `${unit}:npm-facade`,
|
|
1048
|
-
kind: "npm",
|
|
1049
|
-
name: mapping.nodePackage,
|
|
1050
|
-
path: mapping.node,
|
|
1051
|
-
generated: true,
|
|
1052
|
-
});
|
|
1053
|
-
project.releaseCatalog.registerArtifact(unit, {
|
|
1054
|
-
id: `${unit}:npm-native`,
|
|
1055
|
-
kind: "npm",
|
|
1056
|
-
name: `${mapping.nodePackage}-native`,
|
|
1057
|
-
generated: true,
|
|
1058
|
-
data: { targets: resolved.nativeTargets },
|
|
1059
|
-
});
|
|
1060
|
-
}
|
|
1061
|
-
if (mapping.pythonPackage) {
|
|
1062
|
-
project.releaseCatalog.registerArtifact(unit, {
|
|
1063
|
-
id: `${unit}:python-wheel`,
|
|
1064
|
-
kind: "pypi",
|
|
1065
|
-
name: mapping.pythonPackage,
|
|
1066
|
-
path: mapping.python,
|
|
1067
|
-
generated: true,
|
|
1068
|
-
data: { targets: resolved.nativeTargets },
|
|
1069
|
-
});
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
configureRustWorkspaceFiles(project, this.packages, options, resolved);
|
|
1074
|
-
configureRustWorkspaceTasks(project);
|
|
1075
|
-
if (releaseEnabled) {
|
|
1076
|
-
this.addReleaseWorkflow(project, options, resolved.nativeTargets, packageDependencies);
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
private addReleaseWorkflow(
|
|
1081
|
-
project: javascript.NodeProject,
|
|
1082
|
-
options: DBXToolsRustWorkspaceOptions,
|
|
1083
|
-
targets: readonly UniFFIReleaseTarget[],
|
|
1084
|
-
packageDependencies: RustPackageDependencyResolver,
|
|
1085
|
-
): void {
|
|
1086
|
-
if (!project.github || !isDBXToolsJavaScriptProject()(project)) return;
|
|
1087
|
-
const plan = planRustRelease(
|
|
1088
|
-
project,
|
|
1089
|
-
options,
|
|
1090
|
-
targets,
|
|
1091
|
-
this.packages,
|
|
1092
|
-
this.bindingMappings,
|
|
1093
|
-
packageDependencies,
|
|
1094
|
-
);
|
|
1095
|
-
const workflow = tryReleaseWorkflow(project);
|
|
1096
|
-
if (!workflow) {
|
|
1097
|
-
throw new Error("Rust release requires the root dbx-tools release mode");
|
|
1098
|
-
}
|
|
1099
|
-
configureRustReleaseTask(project, plan);
|
|
1100
|
-
if (project.releaseCatalog.mode === "independent") {
|
|
1101
|
-
if (plan.targets.length) {
|
|
1102
|
-
workflow.addJob("rust-build", rustBuildJob(plan, independentReleaseSetupSteps(project)));
|
|
1103
|
-
}
|
|
1104
|
-
if (plan.publicCrates.length) {
|
|
1105
|
-
workflow.addJob("publish-cargo", independentRustCargoPublishJob(project, plan));
|
|
1106
|
-
registerIndependentPublicationJob(workflow, "publish-cargo");
|
|
1107
|
-
}
|
|
1108
|
-
if (plan.releaseBinaries.length) {
|
|
1109
|
-
workflow.addJob("publish-github-release", independentRustGitHubReleaseJob(project, plan));
|
|
1110
|
-
registerIndependentPublicationJob(workflow, "publish-github-release");
|
|
1111
|
-
}
|
|
1112
|
-
if (plan.nodeBindings.length && hasNodeRelease(project)) {
|
|
1113
|
-
workflow.addJob("publish-native-npm", independentRustNativeNpmPublishJob(project));
|
|
1114
|
-
registerIndependentPublicationJob(workflow, "publish-native-npm");
|
|
1115
|
-
workflow.addJob(
|
|
1116
|
-
"publish-node-facades",
|
|
1117
|
-
independentRustNodeFacadePublishJob(project, plan.nodeBindings),
|
|
1118
|
-
);
|
|
1119
|
-
registerIndependentPublicationJob(workflow, "publish-node-facades");
|
|
1120
|
-
}
|
|
1121
|
-
return;
|
|
1122
|
-
}
|
|
1123
|
-
if (plan.publicCrates.length) {
|
|
1124
|
-
workflow.addJob("publish-cargo", rustCargoPublishJob(plan));
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
}
|