@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.
Files changed (56) hide show
  1. package/README.md +83 -229
  2. package/index.ts +11 -18
  3. package/package.json +9 -5
  4. package/src/_task-command.ts +75 -0
  5. package/src/barrels.ts +1 -74
  6. package/src/module-exports.ts +175 -1
  7. package/src/openapi.ts +5 -72
  8. package/src/packages.ts +45 -0
  9. package/src/project-js.ts +5 -48
  10. package/src/project-predicate.ts +1 -2
  11. package/src/project-py.ts +87 -199
  12. package/src/project.ts +2 -2
  13. package/src/python-node-bindings.ts +138 -0
  14. package/src/python-node-bundle.ts +92 -0
  15. package/src/release-catalog.ts +40 -208
  16. package/src/{release-dispatch.ts → release-context.ts} +0 -11
  17. package/src/release.ts +74 -778
  18. package/src/scaffold.ts +2 -6
  19. package/src/workspace-version.ts +9 -121
  20. package/tasks/bump.ts +17 -34
  21. package/tasks/databricks-cli-assets.ts +129 -0
  22. package/tasks/local-publish.ts +61 -138
  23. package/tasks/publish-npm.ts +71 -30
  24. package/tasks/publish-python.ts +61 -148
  25. package/tasks/publish.ts +32 -182
  26. package/tasks/python-node-bindings-watch.ts +47 -0
  27. package/tasks/python-node-bindings.ts +1017 -0
  28. package/tasks/python-registry.ts +2 -9
  29. package/tasks/python-release.ts +74 -0
  30. package/tasks/release-version.ts +23 -2
  31. package/tasks/release.ts +99 -0
  32. package/tasks/sync.ts +13 -18
  33. package/tasks/version-check.ts +13 -72
  34. package/src/_rust-project.ts +0 -429
  35. package/src/_rust-release-workflow.ts +0 -698
  36. package/src/project-rs.ts +0 -1127
  37. package/src/release-plan.ts +0 -269
  38. package/src/release-please.ts +0 -242
  39. package/src/uniffi.ts +0 -65
  40. package/tasks/publish-uniffi-local.ts +0 -314
  41. package/tasks/release-assets.ts +0 -476
  42. package/tasks/release-bootstrap.ts +0 -59
  43. package/tasks/release-candidate.ts +0 -383
  44. package/tasks/release-manifest.ts +0 -273
  45. package/tasks/release-plan.ts +0 -89
  46. package/tasks/release-please.ts +0 -162
  47. package/tasks/release-pr.ts +0 -563
  48. package/tasks/release-request.ts +0 -166
  49. package/tasks/release-summary-units.ts +0 -75
  50. package/tasks/release-summary.ts +0 -381
  51. package/tasks/rust.ts +0 -178
  52. package/tasks/stamp-python.ts +0 -13
  53. package/tasks/uniffi-python.d.ts +0 -61
  54. package/tasks/uniffi-python.js +0 -191
  55. package/tasks/uniffi-release.mjs +0 -573
  56. package/tasks/uniffi.ts +0 -271
@@ -1,429 +0,0 @@
1
- /** Projen-native standalone and workspace-owned Rust project implementation. */
2
- import { existsSync, readFileSync, readdirSync } from "node:fs";
3
- import { basename, join, resolve } from "node:path";
4
- import * as projectUtils from "@dbx-tools/core/project-utils";
5
- import { object, stringUtils } from "@dbx-tools/shared-core";
6
- import { License, Project, TextFile, TomlFile, javascript, type Task } from "projen";
7
- import { DBX_TOOLS_LICENSE, projectRepositoryUrl } from "./project-js.ts";
8
- import type { RustReleaseOs } from "./project-rs.ts";
9
- import type { DBXToolsProject, DBXToolsProjectOptions } from "./project.ts";
10
- import { readWorkspaceVersion } from "./workspace-version.ts";
11
-
12
- export const UNIFFI_BINDGEN_FEATURE = "uniffi-bindgen";
13
-
14
- export interface CargoDependencyOptions {
15
- readonly version?: string;
16
- readonly workspace?: boolean;
17
- readonly path?: string;
18
- readonly optional?: boolean;
19
- readonly package?: string;
20
- readonly defaultFeatures?: boolean;
21
- readonly features?: readonly string[];
22
- }
23
-
24
- export type CargoDependency = string | CargoDependencyOptions;
25
-
26
- export interface RustCliOptions {
27
- /** `dbx` subcommand. Defaults to the Rust package directory. */
28
- readonly command?: string;
29
- /** Root CLI help text. Defaults to the Rust package description. */
30
- readonly description?: string;
31
- /** Keep the binary installable by registry lookup without exposing a root command. */
32
- readonly hidden?: boolean;
33
- }
34
-
35
- /** One explicit Cargo binary target and its optional release registration. */
36
- export interface RustBinaryOptions {
37
- readonly name: string;
38
- readonly path: string;
39
- readonly requiredFeatures?: readonly string[];
40
- readonly release?: boolean;
41
- readonly releaseExcludeOs?: readonly RustReleaseOs[];
42
- readonly description?: string;
43
- readonly cli?: boolean | RustCliOptions;
44
- }
45
-
46
- /** Explicit Cargo example target and the feature gate required to compile it. */
47
- export interface CargoExampleOptions {
48
- readonly name: string;
49
- /** Source path relative to the crate root. Defaults to Cargo's conventional path. */
50
- readonly path?: string;
51
- /** Features Cargo must enable before compiling this example. */
52
- readonly requiredFeatures?: readonly string[];
53
- }
54
-
55
- /** Rust code-first OpenAPI producer consumed by the shared generator. */
56
- export interface RustOpenApiOptions {
57
- /** Cargo binary that handles `--generate-spec` when the package has multiple binaries. */
58
- readonly binary?: string;
59
- /** Features enabled while running the OpenAPI export command. */
60
- readonly features?: readonly string[];
61
- /** Disable the crate's default features while running the export command. */
62
- readonly noDefaultFeatures?: boolean;
63
- }
64
-
65
- /** Cargo package, target, binding, and release behavior shared by every Rust project. */
66
- export interface RustCrateOptions {
67
- readonly description?: string;
68
- /** Keep this crate unpublished and out of public docs. */
69
- readonly private?: boolean;
70
- /** Build this crate's binary for each target and attach it to the GitHub release. */
71
- readonly release?: boolean;
72
- /** Omit this crate and any release binary artifact from these operating systems. */
73
- readonly releaseExcludeOs?: readonly RustReleaseOs[];
74
- /** Cargo features used only by the macOS cross-platform release builder. */
75
- readonly releaseLocalFeatures?: readonly string[];
76
- readonly dependencies?: Readonly<Record<string, CargoDependency>>;
77
- /** Cargo dependencies scoped by a target cfg expression. */
78
- readonly targetDependencies?: Readonly<Record<string, Readonly<Record<string, CargoDependency>>>>;
79
- readonly devDependencies?: Readonly<Record<string, CargoDependency>>;
80
- readonly features?: Readonly<Record<string, readonly string[]>>;
81
- readonly defaultFeatures?: readonly string[];
82
- /** Explicit example targets, including optional Cargo feature gates. */
83
- readonly examples?: readonly CargoExampleOptions[];
84
- /** Generate an OpenAPI client package from this crate's aide document. */
85
- readonly openapi?: boolean | RustOpenApiOptions;
86
- /** Cargo and release executable name. Defaults to the generated package name. */
87
- readonly binaryName?: string;
88
- /** Additional Cargo binary targets owned by this crate. */
89
- readonly binaries?: readonly RustBinaryOptions[];
90
- /** Cargo binary selected by `cargo run` when this crate has multiple binaries. */
91
- readonly defaultRun?: string;
92
- /** Publish this release binary through the generated `dbx` command registry. */
93
- readonly cli?: boolean | RustCliOptions;
94
- readonly bindings?: readonly ("node" | "python")[];
95
- readonly uniffiConfig?: Readonly<Record<string, unknown>>;
96
- }
97
-
98
- /** Options for a standalone or workspace-owned Projen-native Cargo project. */
99
- export interface DBXToolsRustProjectOptions extends DBXToolsProjectOptions, RustCrateOptions {
100
- /** Cargo package name. */
101
- readonly name: string;
102
- readonly parent?: Project;
103
- /** Package directory relative to its workspace root; inferred from `outdir` when omitted. */
104
- readonly directory?: string;
105
- /** Cargo workspace root used for generated workspace mappings. Defaults to `.`. */
106
- readonly workspaceRoot?: string;
107
- /** Inherit unspecified Cargo package metadata from `[workspace.package]`. */
108
- readonly workspace?: boolean;
109
- /** Crate-name scope retained for workspace package and binding naming. */
110
- readonly scope?: string;
111
- /** Concrete standalone package version. Defaults to `0.1.0`. */
112
- readonly version?: string;
113
- /** Rust edition. Defaults to `2021` for standalone projects. */
114
- readonly edition?: string;
115
- /** Minimum supported Rust version. Defaults to `1.82` for standalone projects. */
116
- readonly rustVersion?: string;
117
- /** SPDX license identifier. Defaults to Apache-2.0 for standalone projects. */
118
- readonly license?: string;
119
- /** Copyright owner written to a standalone license file. Defaults to the package name. */
120
- readonly copyrightOwner?: string;
121
- /** Source repository URL. Auto-detected for standalone projects when possible. */
122
- readonly repository?: string;
123
- }
124
-
125
- function* rustSources(directory: string): Generator<string> {
126
- if (!existsSync(directory)) return;
127
- for (const entry of readdirSync(directory, { withFileTypes: true })) {
128
- const path = join(directory, entry.name);
129
- if (entry.isDirectory()) yield* rustSources(path);
130
- else if (entry.isFile() && entry.name.endsWith(".rs")) yield path;
131
- }
132
- }
133
-
134
- /** Whether a Rust crate embeds the UniFFI proc-macro scaffolding marker. */
135
- export function hasUniFFIBindings(directory: string): boolean {
136
- return object
137
- .sequence(rustSources(join(directory, "src")))
138
- .some((path) => /\buniffi\s*::\s*setup_scaffolding\s*!\s*\(/.test(readFileSync(path, "utf8")));
139
- }
140
-
141
- function hasRustSources(directory: string): boolean {
142
- return object.sequence(rustSources(directory)).some(() => true);
143
- }
144
-
145
- export function discoverRustCrates(root: string): string[] {
146
- if (!existsSync(root)) return [];
147
- return readdirSync(root, { withFileTypes: true })
148
- .filter((entry) => entry.isDirectory())
149
- .filter((entry) => hasRustSources(join(root, entry.name, "src")))
150
- .map((entry) => entry.name)
151
- .sort();
152
- }
153
-
154
- export function cargoDependency(
155
- value: CargoDependency,
156
- workspaceVersion?: string,
157
- ): string | Record<string, unknown> {
158
- if (typeof value === "string") return value;
159
- return {
160
- ...(value.version ? { version: value.version } : {}),
161
- ...(!value.version && value.path && workspaceVersion ? { version: workspaceVersion } : {}),
162
- ...(value.workspace ? { workspace: true } : {}),
163
- ...(value.path ? { path: value.path } : {}),
164
- ...(value.package ? { package: value.package } : {}),
165
- ...(value.optional ? { optional: true } : {}),
166
- ...(value.defaultFeatures === false ? { "default-features": false } : {}),
167
- ...(value.features?.length ? { features: [...value.features] } : {}),
168
- };
169
- }
170
-
171
- export function structuredTomlSections(
172
- value: Readonly<Record<string, unknown>>,
173
- ): Record<string, unknown> {
174
- const result: Record<string, unknown> = {};
175
- for (const [section, contents] of Object.entries(value)) {
176
- const path = section.split(".");
177
- let target = result;
178
- for (const part of path.slice(0, -1)) {
179
- const current = target[part];
180
- if (current === undefined) {
181
- target[part] = {};
182
- } else if (!current || typeof current !== "object" || Array.isArray(current)) {
183
- throw new Error(`Conflicting TOML section ${section}`);
184
- }
185
- target = target[part] as Record<string, unknown>;
186
- }
187
- target[path.at(-1)!] = contents;
188
- }
189
- return result;
190
- }
191
-
192
- interface ResolvedRustProjectOptions extends DBXToolsRustProjectOptions {
193
- readonly directory: string;
194
- readonly outdir: string;
195
- readonly workspaceRoot: string;
196
- }
197
-
198
- function resolveRustProjectOptions(
199
- options: DBXToolsRustProjectOptions,
200
- ): ResolvedRustProjectOptions {
201
- if (!/^[A-Za-z0-9_][A-Za-z0-9_-]*$/.test(options.name)) {
202
- throw new Error(`Invalid Cargo package name: ${options.name}`);
203
- }
204
- const directory =
205
- options.directory ??
206
- (options.outdir && options.outdir !== "."
207
- ? basename(options.outdir)
208
- : stringUtils.toSlug(options.name));
209
- return {
210
- ...options,
211
- directory,
212
- outdir: options.outdir ?? (options.parent ? directory : "."),
213
- workspaceRoot: options.workspaceRoot ?? ".",
214
- };
215
- }
216
-
217
- function packageOptions(
218
- options: ResolvedRustProjectOptions,
219
- ): RustCrateOptions & { readonly directory: string } {
220
- return {
221
- directory: options.directory,
222
- ...(options.description !== undefined ? { description: options.description } : {}),
223
- ...(options.private !== undefined ? { private: options.private } : {}),
224
- ...(options.release !== undefined ? { release: options.release } : {}),
225
- ...(options.releaseExcludeOs ? { releaseExcludeOs: options.releaseExcludeOs } : {}),
226
- ...(options.releaseLocalFeatures ? { releaseLocalFeatures: options.releaseLocalFeatures } : {}),
227
- ...(options.dependencies ? { dependencies: options.dependencies } : {}),
228
- ...(options.targetDependencies ? { targetDependencies: options.targetDependencies } : {}),
229
- ...(options.devDependencies ? { devDependencies: options.devDependencies } : {}),
230
- ...(options.features ? { features: options.features } : {}),
231
- ...(options.defaultFeatures ? { defaultFeatures: options.defaultFeatures } : {}),
232
- ...(options.openapi !== undefined ? { openapi: options.openapi } : {}),
233
- ...(options.binaryName ? { binaryName: options.binaryName } : {}),
234
- ...(options.binaries ? { binaries: options.binaries } : {}),
235
- ...(options.cli !== undefined ? { cli: options.cli } : {}),
236
- ...(options.bindings ? { bindings: options.bindings } : {}),
237
- ...(options.uniffiConfig ? { uniffiConfig: options.uniffiConfig } : {}),
238
- };
239
- }
240
-
241
- function inheritedCargoValue(
242
- workspace: boolean,
243
- configured: string | undefined,
244
- fallback: string,
245
- ): string | { workspace: true } {
246
- return workspace && configured === undefined ? { workspace: true } : (configured ?? fallback);
247
- }
248
-
249
- /** One standalone or workspace-owned Cargo project. */
250
- export class RustProject extends Project implements DBXToolsProject {
251
- readonly language = "rust" as const;
252
- readonly crateName: string;
253
- readonly workspaceRoot: string;
254
- readonly scope: string;
255
- readonly packageOptions: RustCrateOptions & { readonly directory: string };
256
- readonly uniffi: boolean;
257
- readonly manifestFile: TomlFile;
258
- readonly lintTask: Task;
259
- readonly formatTask: Task;
260
- readonly formatCheckTask: Task;
261
-
262
- constructor(projectOptions: DBXToolsRustProjectOptions) {
263
- const options = resolveRustProjectOptions(projectOptions);
264
- super({ parent: options.parent, outdir: options.outdir, name: options.name });
265
- this.crateName = options.name;
266
- this.workspaceRoot = options.workspaceRoot;
267
- this.scope = stringUtils.toSlug(options.scope ?? options.name.split("-")[0]!);
268
- this.packageOptions = packageOptions(options);
269
- this.uniffi = hasUniFFIBindings(this.outdir);
270
- const library = existsSync(join(this.outdir, "src/lib.rs"));
271
- const binary = existsSync(join(this.outdir, "src/main.rs"));
272
- const binaryName = options.binaryName ?? this.crateName;
273
- const workspace = options.workspace === true;
274
- const projectDirectory = options.parent
275
- ? resolve(options.parent.outdir, options.outdir)
276
- : resolve(options.outdir);
277
- const repository =
278
- options.repository ??
279
- (options.parent instanceof javascript.NodeProject
280
- ? projectRepositoryUrl(options.parent)
281
- : projectUtils.repositoryUrl(projectDirectory));
282
- const dependencyVersion =
283
- options.version ?? (options.parent ? readWorkspaceVersion(options.parent.outdir) : "0.1.0");
284
- const manifest: Record<string, unknown> = {
285
- ...(!workspace ? { workspace: {} } : {}),
286
- package: {
287
- name: this.crateName,
288
- ...(options.defaultRun ? { "default-run": options.defaultRun } : {}),
289
- version: inheritedCargoValue(workspace, options.version, "0.1.0"),
290
- edition: inheritedCargoValue(workspace, options.edition, "2021"),
291
- "rust-version": inheritedCargoValue(workspace, options.rustVersion, "1.82"),
292
- description: options.description ?? this.crateName,
293
- license: inheritedCargoValue(workspace, options.license, DBX_TOOLS_LICENSE),
294
- ...(workspace && options.repository === undefined
295
- ? { repository: { workspace: true } }
296
- : repository
297
- ? { repository }
298
- : {}),
299
- ...(options.private ? { publish: false } : {}),
300
- },
301
- ...(library
302
- ? {
303
- lib: {
304
- name: this.crateName.replaceAll("-", "_"),
305
- path: "src/lib.rs",
306
- ...(this.uniffi ? { "crate-type": ["lib", "cdylib"] } : {}),
307
- },
308
- }
309
- : {}),
310
- ...(this.uniffi || binary
311
- ? {
312
- bin: [
313
- ...(this.uniffi
314
- ? [
315
- {
316
- name: `${this.crateName}-uniffi-bindgen`,
317
- path: "uniffi-bindgen.rs",
318
- "required-features": [UNIFFI_BINDGEN_FEATURE],
319
- },
320
- ]
321
- : []),
322
- ...(binary ? [{ name: binaryName, path: "src/main.rs" }] : []),
323
- ...(options.binaries ?? []).map((target) => ({
324
- name: target.name,
325
- path: target.path,
326
- ...(target.requiredFeatures?.length
327
- ? { "required-features": [...target.requiredFeatures] }
328
- : {}),
329
- })),
330
- ],
331
- }
332
- : {}),
333
- ...(this.uniffi || options.features || options.defaultFeatures
334
- ? {
335
- features: {
336
- ...(options.defaultFeatures ? { default: [...options.defaultFeatures] } : {}),
337
- ...options.features,
338
- ...(this.uniffi ? { [UNIFFI_BINDGEN_FEATURE]: ["uniffi/cli"] } : {}),
339
- },
340
- }
341
- : {}),
342
- ...(options.examples?.length
343
- ? {
344
- example: options.examples.map((example) => ({
345
- name: example.name,
346
- ...(example.path ? { path: example.path } : {}),
347
- ...(example.requiredFeatures?.length
348
- ? { "required-features": [...example.requiredFeatures] }
349
- : {}),
350
- })),
351
- }
352
- : {}),
353
- ...(options.dependencies
354
- ? {
355
- dependencies: Object.fromEntries(
356
- Object.entries(options.dependencies).map(([name, value]) => [
357
- name,
358
- cargoDependency(value, dependencyVersion),
359
- ]),
360
- ),
361
- }
362
- : {}),
363
- ...(options.targetDependencies
364
- ? {
365
- target: Object.fromEntries(
366
- Object.entries(options.targetDependencies).map(([target, dependencies]) => [
367
- target,
368
- {
369
- dependencies: Object.fromEntries(
370
- Object.entries(dependencies).map(([name, value]) => [
371
- name,
372
- cargoDependency(value, dependencyVersion),
373
- ]),
374
- ),
375
- },
376
- ]),
377
- ),
378
- }
379
- : {}),
380
- ...(options.devDependencies
381
- ? {
382
- "dev-dependencies": Object.fromEntries(
383
- Object.entries(options.devDependencies).map(([name, value]) => [
384
- name,
385
- cargoDependency(value, dependencyVersion),
386
- ]),
387
- ),
388
- }
389
- : {}),
390
- };
391
- this.manifestFile = new TomlFile(this, "Cargo.toml", { marker: false, obj: manifest });
392
- if (this.uniffi) {
393
- new TextFile(this, "uniffi-bindgen.rs", {
394
- lines: ["fn main() {", " uniffi::uniffi_bindgen_main();", "}", ""],
395
- });
396
- new TomlFile(this, "uniffi.toml", {
397
- marker: false,
398
- obj: structuredTomlSections(
399
- options.uniffiConfig ?? {
400
- "bindings.python": { cdylib_name: this.crateName.replaceAll("-", "_") },
401
- "bindings.typescript": { strictTypeChecking: true },
402
- },
403
- ),
404
- });
405
- }
406
- this.compileTask.reset("cargo build");
407
- this.testTask.reset("cargo test");
408
- this.packageTask.reset("cargo package");
409
- this.lintTask = this.addTask("lint", {
410
- description: "Lint every Rust target and feature",
411
- exec: "cargo clippy --all-targets --all-features",
412
- });
413
- this.formatTask = this.addTask("format", {
414
- description: "Format Rust sources",
415
- exec: "cargo fmt",
416
- });
417
- this.formatCheckTask = this.addTask("format:check", {
418
- description: "Check Rust source formatting",
419
- exec: "cargo fmt -- --check",
420
- });
421
- if (!workspace) {
422
- new License(this, {
423
- spdx: options.license ?? DBX_TOOLS_LICENSE,
424
- copyrightOwner: options.copyrightOwner ?? options.name,
425
- });
426
- this.gitignore.addPatterns("target/");
427
- }
428
- }
429
- }