@dbx-tools/projen 0.9.21 → 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 (108) hide show
  1. package/README.md +77 -207
  2. package/index.ts +10 -12
  3. package/package.json +4 -4
  4. package/src/barrels.ts +1 -74
  5. package/src/module-exports.ts +175 -1
  6. package/src/openapi.ts +5 -72
  7. package/src/packages.ts +45 -0
  8. package/src/project-js.ts +3 -6
  9. package/src/project-predicate.ts +1 -2
  10. package/src/project-py.ts +82 -103
  11. package/src/project.ts +2 -2
  12. package/src/python-node-bindings.ts +138 -0
  13. package/src/python-node-bundle.ts +92 -0
  14. package/src/release-catalog.ts +3 -5
  15. package/src/{release-dispatch.ts → release-context.ts} +0 -11
  16. package/src/release.ts +70 -152
  17. package/tasks/databricks-cli-assets.ts +129 -0
  18. package/tasks/local-publish.ts +23 -107
  19. package/tasks/publish-npm.ts +10 -1
  20. package/tasks/publish-python.ts +26 -11
  21. package/tasks/publish.ts +12 -48
  22. package/tasks/python-node-bindings-watch.ts +47 -0
  23. package/tasks/python-node-bindings.ts +1017 -0
  24. package/tasks/python-release.ts +74 -0
  25. package/tasks/release.ts +99 -0
  26. package/tasks/sync.ts +13 -18
  27. package/tasks/version-check.ts +1 -28
  28. package/._LICENSE +0 -0
  29. package/._README.md +0 -0
  30. package/._index.ts +0 -0
  31. package/._package.json +0 -0
  32. package/src/.__lazy-require.ts +0 -0
  33. package/src/.__release-platform.ts +0 -0
  34. package/src/.__rust-project.ts +0 -0
  35. package/src/.__rust-release-workflow.ts +0 -0
  36. package/src/.__task-command.ts +0 -0
  37. package/src/._barrels.ts +0 -0
  38. package/src/._bun-app.ts +0 -0
  39. package/src/._bun-workflow.ts +0 -0
  40. package/src/._clean.ts +0 -0
  41. package/src/._codegen.ts +0 -0
  42. package/src/._dbx-tools-config.ts +0 -0
  43. package/src/._engine-root.ts +0 -0
  44. package/src/._generated.ts +0 -0
  45. package/src/._mixin.ts +0 -0
  46. package/src/._module-exports.ts +0 -0
  47. package/src/._openapi.ts +0 -0
  48. package/src/._packages.ts +0 -0
  49. package/src/._pnpm-workspace.ts +0 -0
  50. package/src/._project-js.ts +0 -0
  51. package/src/._project-predicate.ts +0 -0
  52. package/src/._project-py.ts +0 -0
  53. package/src/._project-rs.ts +0 -0
  54. package/src/._project.ts +0 -0
  55. package/src/._projen-version.ts +0 -0
  56. package/src/._publish.ts +0 -0
  57. package/src/._release-catalog.ts +0 -0
  58. package/src/._release-dispatch.ts +0 -0
  59. package/src/._release-github.ts +0 -0
  60. package/src/._release.ts +0 -0
  61. package/src/._scaffold.ts +0 -0
  62. package/src/._tags.ts +0 -0
  63. package/src/._tsconfig.ts +0 -0
  64. package/src/._uniffi.ts +0 -0
  65. package/src/._vscode.ts +0 -0
  66. package/src/._watch.ts +0 -0
  67. package/src/._workspace-lock.ts +0 -0
  68. package/src/._workspace-version.ts +0 -0
  69. package/src/_rust-project.ts +0 -429
  70. package/src/_rust-release-workflow.ts +0 -311
  71. package/src/project-rs.ts +0 -1054
  72. package/src/uniffi.ts +0 -65
  73. package/tasks/._barrels.ts +0 -0
  74. package/tasks/._bump.ts +0 -0
  75. package/tasks/._clean.ts +0 -0
  76. package/tasks/._compile-workspace.ts +0 -0
  77. package/tasks/._local-publish.ts +0 -0
  78. package/tasks/._openapi.ts +0 -0
  79. package/tasks/._projenrc.ts +0 -0
  80. package/tasks/._publish-npm.ts +0 -0
  81. package/tasks/._publish-python.ts +0 -0
  82. package/tasks/._publish-uniffi-local.ts +0 -0
  83. package/tasks/._publish.ts +0 -0
  84. package/tasks/._python-registry.ts +0 -0
  85. package/tasks/._release-assets.ts +0 -0
  86. package/tasks/._release-candidate.ts +0 -0
  87. package/tasks/._release-manifest.ts +0 -0
  88. package/tasks/._release-pr.ts +0 -0
  89. package/tasks/._release-summary.ts +0 -0
  90. package/tasks/._release-version.ts +0 -0
  91. package/tasks/._rust.ts +0 -0
  92. package/tasks/._sync.ts +0 -0
  93. package/tasks/._uniffi-python.d.ts +0 -0
  94. package/tasks/._uniffi-python.js +0 -0
  95. package/tasks/._uniffi-release.mjs +0 -0
  96. package/tasks/._uniffi.ts +0 -0
  97. package/tasks/._version-check.ts +0 -0
  98. package/tasks/publish-uniffi-local.ts +0 -359
  99. package/tasks/release-assets.ts +0 -467
  100. package/tasks/release-candidate.ts +0 -416
  101. package/tasks/release-manifest.ts +0 -273
  102. package/tasks/release-pr.ts +0 -589
  103. package/tasks/release-summary.ts +0 -379
  104. package/tasks/rust.ts +0 -173
  105. package/tasks/uniffi-python.d.ts +0 -60
  106. package/tasks/uniffi-python.js +0 -178
  107. package/tasks/uniffi-release.mjs +0 -579
  108. package/tasks/uniffi.ts +0 -262
package/src/uniffi.ts DELETED
@@ -1,65 +0,0 @@
1
- const defaultedParameters = (source: string): Map<string, Set<string>> => {
2
- const methods = new Map<string, Set<string>>();
3
- for (const match of source.matchAll(/^\s*(?:async\s+)?(\w+)\(([^)]*)\)/gm)) {
4
- const parameters = match[2]
5
- .split(",")
6
- .map((parameter) => parameter.trim())
7
- .filter((parameter) => parameter.includes("="))
8
- .map((parameter) => parameter.match(/^(\w+)\s*:/)?.[1])
9
- .filter((parameter): parameter is string => parameter !== undefined);
10
- if (parameters.length > 0) methods.set(match[1], new Set(parameters));
11
- }
12
- return methods;
13
- };
14
-
15
- export const makeDefaultedInterfaceParametersOptional = (source: string): string => {
16
- const defaults = defaultedParameters(source);
17
- return source.replace(/export interface \w+Like \{[\s\S]*?^\}/gm, (block) =>
18
- block.replace(/^(\s*)(\w+)\(([^)]*)\)/gm, (signature, indent, method, parameters) => {
19
- const defaulted = defaults.get(method);
20
- if (!defaulted) return signature;
21
- const repaired = parameters
22
- .split(",")
23
- .map((parameter: string) => {
24
- const match = parameter.match(/^(\s*)(\w+)(\s*:\s*.*)$/);
25
- if (!match || !defaulted.has(match[2])) return parameter;
26
- return `${match[1]}${match[2]}?${match[3]}`;
27
- })
28
- .join(",");
29
- return `${indent}${method}(${repaired})`;
30
- }),
31
- );
32
- };
33
-
34
- /** Remove UBRN's obsolete `*Interface` aliases once the `*Like` owner exists. */
35
- export const removeObsoleteInterfaceAliases = (source: string): string =>
36
- source.replace(
37
- /\n\/\*\*\n \* @deprecated Use `\w+Like` instead\.\n \*\/\nexport type \w+Interface = \w+Like;\n/g,
38
- "\n",
39
- );
40
-
41
- export interface TypeScriptBindingModule {
42
- readonly specifier: string;
43
- readonly source: string;
44
- }
45
-
46
- /** Add source extensions to UBRN's generated relative binding imports. */
47
- export const addTypeScriptExtensionsToBindingImports = (source: string): string =>
48
- source.replace(
49
- /(["'])\.\/_bindings(-ffi)?\1/g,
50
- (_specifier, quote: string, suffix: string | undefined) =>
51
- `${quote}./_bindings${suffix ?? ""}.ts${quote}`,
52
- );
53
-
54
- /** Add explicit type exports for interfaces that TypeScript misses through UBRN's star exports. */
55
- export const addExplicitInterfaceReexports = (
56
- facade: string,
57
- modules: readonly TypeScriptBindingModule[],
58
- ): string => {
59
- const exports = modules.flatMap(({ specifier, source }) => {
60
- const names = [...source.matchAll(/^export interface (\w+)/gm)].map((match) => match[1]!);
61
- return names.length > 0 ? [`export type { ${names.join(", ")} } from '${specifier}';`] : [];
62
- });
63
- if (exports.length === 0) return facade;
64
- return `${facade.trimEnd()}\n${exports.join("\n")}\n`;
65
- };
Binary file
package/tasks/._bump.ts DELETED
Binary file
package/tasks/._clean.ts DELETED
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/tasks/._rust.ts DELETED
Binary file
package/tasks/._sync.ts DELETED
Binary file
Binary file
Binary file
Binary file
package/tasks/._uniffi.ts DELETED
Binary file
Binary file
@@ -1,359 +0,0 @@
1
- #!/usr/bin/env -S bun
2
- import {
3
- cpSync,
4
- chmodSync,
5
- existsSync,
6
- mkdirSync,
7
- mkdtempSync,
8
- readFileSync,
9
- readdirSync,
10
- rmSync,
11
- writeFileSync,
12
- } from "node:fs";
13
- import { arch, platform, tmpdir } from "node:os";
14
- import { dirname, join, relative, resolve } from "node:path";
15
- import { fileURLToPath } from "node:url";
16
- import { parseArgs } from "node:util";
17
- import { json, log, object } from "@dbx-tools/shared-core";
18
- import { parse, stringify } from "smol-toml";
19
- import { captureTaskCommand, runTaskCommand, taskCommandSucceeds } from "../src/_task-command.ts";
20
- import { readDbxToolsConfig, repoRoot } from "../src/packages.ts";
21
- import type { RustBindingMapping, RustWorkspaceMapping } from "../src/project-rs.ts";
22
-
23
- const logger = log.logger("projen:publish-uniffi-local");
24
-
25
- function commandAvailable(command: string): boolean {
26
- return taskCommandSucceeds(repoRoot, command, ["--version"]);
27
- }
28
-
29
- function rustHost(): string {
30
- const host = captureTaskCommand(repoRoot, "rustc", ["-vV"], { check: true }).match(
31
- /^host:\s+(.+)$/m,
32
- )?.[1];
33
- if (!host) throw new Error("Unable to detect the local Rust target from rustc -vV");
34
- return host;
35
- }
36
-
37
- function pythonTag(): string {
38
- const value = captureTaskCommand(
39
- repoRoot,
40
- "uv",
41
- [
42
- "run",
43
- "--no-project",
44
- "python",
45
- "-c",
46
- "import sysconfig; print(sysconfig.get_platform().replace('-', '_').replace('.', '_'))",
47
- ],
48
- { check: true },
49
- );
50
- if (!value) throw new Error("Unable to detect the local Python wheel platform tag");
51
- return value;
52
- }
53
-
54
- function nativeTarget(): {
55
- cpu: "arm64" | "x64";
56
- libc?: "glibc";
57
- node: string;
58
- os: "darwin" | "linux" | "win32";
59
- } {
60
- const os = platform();
61
- const machine = arch();
62
- const cpu = machine === "arm64" ? "arm64" : machine === "x64" ? "x64" : undefined;
63
- if (!cpu) throw new Error(`Unsupported local architecture: ${machine}`);
64
- if (os === "darwin") return { os, cpu, node: `darwin-${cpu}` };
65
- if (os === "win32") return { os, cpu, node: `win32-${cpu}-msvc` };
66
- if (os === "linux") {
67
- const libc = rustHost().includes("musl") ? undefined : "glibc";
68
- return { os, cpu, node: `linux-${cpu}-${libc ? "gnu" : "musl"}`, ...(libc ? { libc } : {}) };
69
- }
70
- throw new Error(`Unsupported local platform: ${os}`);
71
- }
72
-
73
- function rustConfig(): RustWorkspaceMapping | undefined {
74
- const value = readDbxToolsConfig(repoRoot)?.rust;
75
- if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
76
- const config = value as Partial<RustWorkspaceMapping>;
77
- return Array.isArray(config.bindings) ? (config as RustWorkspaceMapping) : undefined;
78
- }
79
-
80
- function artifacts(directory: string, suffix: string): string[] {
81
- if (!existsSync(directory)) return [];
82
- return readdirSync(directory)
83
- .filter((name) => name.endsWith(suffix))
84
- .map((name) => join(directory, name));
85
- }
86
-
87
- function cargoVersionExists(crateName: string, version: string, registry: string): boolean {
88
- return taskCommandSucceeds(repoRoot, "cargo", [
89
- "info",
90
- `${crateName}@${version}`,
91
- "--registry",
92
- registry,
93
- ]);
94
- }
95
-
96
- interface CargoMetadataPackage {
97
- dependencies: Array<{ name: string; source?: string | null }>;
98
- manifest_path: string;
99
- name: string;
100
- version: string;
101
- }
102
-
103
- function orderedCargoManifests(root: string, config: RustWorkspaceMapping): CargoMetadataPackage[] {
104
- const configured = new Set(config.crates.map((crate) => resolve(root, crate, "Cargo.toml")));
105
- const metadata = json.parseRecord(
106
- captureTaskCommand(
107
- root,
108
- "cargo",
109
- ["metadata", "--format-version", "1", "--no-deps", "--locked"],
110
- { check: true },
111
- ),
112
- );
113
- if (!metadata || !Array.isArray(metadata.packages)) {
114
- throw new Error("Cargo metadata did not return workspace packages");
115
- }
116
- const packages = metadata.packages.filter(object.isRecord).map((pkg) => {
117
- if (
118
- typeof pkg.name !== "string" ||
119
- typeof pkg.version !== "string" ||
120
- typeof pkg.manifest_path !== "string" ||
121
- !Array.isArray(pkg.dependencies)
122
- ) {
123
- throw new Error("Cargo metadata returned an invalid package");
124
- }
125
- return {
126
- name: pkg.name,
127
- version: pkg.version,
128
- manifest_path: resolve(pkg.manifest_path),
129
- dependencies: pkg.dependencies.filter(object.isRecord).map((dependency) => {
130
- if (typeof dependency.name !== "string") {
131
- throw new Error(`Cargo metadata returned an invalid dependency for ${pkg.name}`);
132
- }
133
- return {
134
- name: dependency.name,
135
- source: typeof dependency.source === "string" ? dependency.source : null,
136
- };
137
- }),
138
- } satisfies CargoMetadataPackage;
139
- });
140
- const publishable = new Map(
141
- packages
142
- .filter((pkg) => configured.has(pkg.manifest_path))
143
- .filter((pkg) => {
144
- const manifest = parse(readFileSync(pkg.manifest_path, "utf8")) as {
145
- package?: { publish?: boolean };
146
- };
147
- return manifest.package?.publish !== false;
148
- })
149
- .map((pkg) => [pkg.name, pkg]),
150
- );
151
- const ordered: CargoMetadataPackage[] = [];
152
- const visiting = new Set<string>();
153
- const completed = new Set<string>();
154
- const visit = (pkg: CargoMetadataPackage): void => {
155
- if (completed.has(pkg.name)) return;
156
- if (visiting.has(pkg.name)) {
157
- throw new Error(`Cyclic Cargo publication dependency: ${pkg.name}`);
158
- }
159
- visiting.add(pkg.name);
160
- for (const dependency of pkg.dependencies) {
161
- if (dependency.source) continue;
162
- const workspaceDependency = publishable.get(dependency.name);
163
- if (workspaceDependency) visit(workspaceDependency);
164
- }
165
- visiting.delete(pkg.name);
166
- completed.add(pkg.name);
167
- ordered.push(pkg);
168
- };
169
- for (const pkg of publishable.values()) visit(pkg);
170
- return ordered;
171
- }
172
-
173
- function copyCargoWorkspace(config: RustWorkspaceMapping): string {
174
- const root = mkdtempSync(join(tmpdir(), "dbx-tools-local-cargo-"));
175
- for (const path of ["Cargo.toml", "Cargo.lock", "LICENSE", "README.md", "rust-toolchain.toml"]) {
176
- const source = join(repoRoot, path);
177
- if (!existsSync(source)) continue;
178
- cpSync(source, join(root, path));
179
- }
180
- const cargoConfig = join(repoRoot, ".cargo");
181
- if (existsSync(cargoConfig)) cpSync(cargoConfig, join(root, ".cargo"), { recursive: true });
182
- for (const crate of config.crates) {
183
- const source = resolve(repoRoot, crate);
184
- const destination = resolve(root, relative(repoRoot, source));
185
- mkdirSync(dirname(destination), { recursive: true });
186
- cpSync(source, destination, { recursive: true });
187
- }
188
- return root;
189
- }
190
-
191
- export function projectCargoRegistry(
192
- document: Record<string, unknown>,
193
- crateNames: ReadonlySet<string>,
194
- registry: string,
195
- ): void {
196
- const sections: unknown[] = [
197
- document.dependencies,
198
- document["dev-dependencies"],
199
- document["build-dependencies"],
200
- ];
201
- const workspace = document.workspace;
202
- if (workspace && typeof workspace === "object" && !Array.isArray(workspace)) {
203
- sections.push((workspace as Record<string, unknown>).dependencies);
204
- }
205
- for (const value of sections) {
206
- if (!value || typeof value !== "object" || Array.isArray(value)) continue;
207
- const section = value as Record<string, unknown>;
208
- for (const crateName of crateNames) {
209
- const dependency = section[crateName];
210
- if (!dependency || typeof dependency !== "object" || Array.isArray(dependency)) continue;
211
- const record = dependency as Record<string, unknown>;
212
- if (record.workspace === true) continue;
213
- record.registry = registry;
214
- }
215
- }
216
- }
217
-
218
- function publishCargo(config: RustWorkspaceMapping, registry: string, version: string): void {
219
- const root = copyCargoWorkspace(config);
220
- try {
221
- const packages = orderedCargoManifests(root, config);
222
- const crateNames = new Set(packages.map((pkg) => pkg.name));
223
- for (const pkg of packages) {
224
- if (pkg.version !== version) {
225
- throw new Error(`${pkg.name} version ${pkg.version} does not match release ${version}`);
226
- }
227
- }
228
- const manifests = [join(root, "Cargo.toml"), ...packages.map((pkg) => pkg.manifest_path)];
229
- for (const manifest of manifests) {
230
- const document = parse(readFileSync(manifest, "utf8")) as Record<string, unknown>;
231
- projectCargoRegistry(document, crateNames, registry);
232
- chmodSync(manifest, 0o644);
233
- writeFileSync(manifest, `${stringify(document).trimEnd()}\n`);
234
- }
235
- for (const pkg of packages) {
236
- if (cargoVersionExists(pkg.name, version, registry)) {
237
- logger.info(`skip published ${pkg.name} @ ${version}`);
238
- continue;
239
- }
240
- runTaskCommand(root, "cargo", [
241
- "publish",
242
- "--manifest-path",
243
- pkg.manifest_path,
244
- "--registry",
245
- registry,
246
- "--locked",
247
- ]);
248
- }
249
- } finally {
250
- rmSync(root, { recursive: true, force: true });
251
- }
252
- }
253
-
254
- function buildAndPublish(
255
- binding: RustBindingMapping,
256
- version: string,
257
- registry: string | undefined,
258
- pypiPublishUrl: string | undefined,
259
- ): void {
260
- const includeNode = Boolean(registry && binding.node && binding.nodePackage);
261
- const includePython = Boolean(pypiPublishUrl && binding.python && binding.pythonPackage);
262
- if (!includeNode && !includePython) return;
263
-
264
- const target = nativeTarget();
265
- const output = resolve(repoRoot, "dist/uniffi");
266
- runTaskCommand(repoRoot, "node", [
267
- resolve(dirname(fileURLToPath(import.meta.url)), "uniffi-release.mjs"),
268
- "build",
269
- "--crate",
270
- binding.crate,
271
- "--node",
272
- includeNode ? binding.node! : "",
273
- "--python",
274
- includePython ? binding.python! : "",
275
- "--node-package",
276
- includeNode ? binding.nodePackage! : "",
277
- "--python-package",
278
- includePython ? binding.pythonPackage! : "",
279
- "--python-module",
280
- includePython ? binding.pythonModule! : "",
281
- "--cargo-target",
282
- rustHost(),
283
- "--node-triple",
284
- target.node,
285
- "--python-tag",
286
- pythonTag(),
287
- "--os",
288
- target.os,
289
- "--cpu",
290
- target.cpu,
291
- "--libc",
292
- target.libc ?? "",
293
- "--facade",
294
- "true",
295
- "--version",
296
- version,
297
- "--output",
298
- "dist/uniffi",
299
- ]);
300
-
301
- if (includeNode) {
302
- const publishNpmScript = resolve(dirname(fileURLToPath(import.meta.url)), "publish-npm.ts");
303
- for (const directory of [join(output, "npm"), join(output, "npm-facade")]) {
304
- if (artifacts(directory, ".tgz").length === 0) continue;
305
- runTaskCommand(repoRoot, process.execPath, [
306
- publishNpmScript,
307
- "--directory",
308
- directory,
309
- "--version",
310
- version,
311
- "--registry",
312
- registry!,
313
- ]);
314
- }
315
- }
316
- if (includePython) {
317
- const wheels = artifacts(join(output, "python"), ".whl");
318
- if (wheels.length === 0) throw new Error(`No wheel produced for ${binding.crate}`);
319
- runTaskCommand(repoRoot, "uvx", [
320
- "--from",
321
- "devpi-client",
322
- "devpi",
323
- "upload",
324
- "--index",
325
- pypiPublishUrl!,
326
- "--from-dir",
327
- join(output, "python"),
328
- ]);
329
- }
330
- }
331
-
332
- function main(args: string[] = process.argv.slice(2)): void {
333
- const parsed = parseArgs({
334
- args,
335
- options: {
336
- version: { type: "string" },
337
- registry: { type: "string" },
338
- "pypi-publish-url": { type: "string" },
339
- "cargo-registry": { type: "string" },
340
- },
341
- });
342
- const version = parsed.values.version;
343
- if (!version) throw new Error("Missing --version");
344
- const config = rustConfig();
345
- if (config?.bindings.length) {
346
- if (!commandAvailable("cargo") || !commandAvailable("rustc")) {
347
- throw new Error("Cargo and rustc are required because UniFFI Rust projects were detected");
348
- }
349
- for (const binding of config.bindings) {
350
- buildAndPublish(binding, version, parsed.values.registry, parsed.values["pypi-publish-url"]);
351
- }
352
- }
353
- const cargoRegistry = parsed.values["cargo-registry"];
354
- if (cargoRegistry && config?.crates.length) {
355
- publishCargo(config, cargoRegistry, version);
356
- }
357
- }
358
-
359
- if (import.meta.main) main();