@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,314 +0,0 @@
1
- #!/usr/bin/env -S bun
2
- import { chmodSync, existsSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
3
- import { arch, platform } from "node:os";
4
- import { dirname, join, resolve } from "node:path";
5
- import { fileURLToPath } from "node:url";
6
- import { parseArgs } from "node:util";
7
- import { json, log, object } from "@dbx-tools/shared-core";
8
- import { parse, stringify } from "smol-toml";
9
- import { captureTaskCommand, runTaskCommand, taskCommandSucceeds } from "../src/_task-command.ts";
10
- import { readDbxToolsConfig, repoRoot } from "../src/packages.ts";
11
- import type { RustBindingMapping, RustWorkspaceMapping } from "../src/project-rs.ts";
12
-
13
- const logger = log.logger("projen:publish-uniffi-local");
14
-
15
- const parsed = parseArgs({
16
- options: {
17
- version: { type: "string" },
18
- registry: { type: "string" },
19
- "pypi-publish-url": { type: "string" },
20
- "cargo-registry": { type: "string" },
21
- },
22
- });
23
-
24
- function run(command: string, args: string[], capture = false): string {
25
- if (capture) return captureTaskCommand(repoRoot, command, args, { check: true });
26
- runTaskCommand(repoRoot, command, args);
27
- return "";
28
- }
29
-
30
- function commandAvailable(command: string): boolean {
31
- return taskCommandSucceeds(repoRoot, command, ["--version"]);
32
- }
33
-
34
- function rustHost(): string {
35
- const host = run("rustc", ["-vV"], true).match(/^host:\s+(.+)$/m)?.[1];
36
- if (!host) throw new Error("Unable to detect the local Rust target from rustc -vV");
37
- return host;
38
- }
39
-
40
- function pythonTag(): string {
41
- const value = run(
42
- "uv",
43
- [
44
- "run",
45
- "--no-project",
46
- "python",
47
- "-c",
48
- "import sysconfig; print(sysconfig.get_platform().replace('-', '_').replace('.', '_'))",
49
- ],
50
- true,
51
- );
52
- if (!value) throw new Error("Unable to detect the local Python wheel platform tag");
53
- return value;
54
- }
55
-
56
- function nativeTarget(): {
57
- cpu: "arm64" | "x64";
58
- libc?: "glibc";
59
- node: string;
60
- os: "darwin" | "linux" | "win32";
61
- } {
62
- const os = platform();
63
- const machine = arch();
64
- const cpu = machine === "arm64" ? "arm64" : machine === "x64" ? "x64" : undefined;
65
- if (!cpu) throw new Error(`Unsupported local architecture: ${machine}`);
66
- if (os === "darwin") return { os, cpu, node: `darwin-${cpu}` };
67
- if (os === "win32") return { os, cpu, node: `win32-${cpu}-msvc` };
68
- if (os === "linux") {
69
- const libc = rustHost().includes("musl") ? undefined : "glibc";
70
- return { os, cpu, node: `linux-${cpu}-${libc ? "gnu" : "musl"}`, ...(libc ? { libc } : {}) };
71
- }
72
- throw new Error(`Unsupported local platform: ${os}`);
73
- }
74
-
75
- function rustConfig(): RustWorkspaceMapping | undefined {
76
- const value = readDbxToolsConfig(repoRoot)?.rust;
77
- if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
78
- const config = value as Partial<RustWorkspaceMapping>;
79
- return Array.isArray(config.bindings) ? (config as RustWorkspaceMapping) : undefined;
80
- }
81
-
82
- function artifacts(directory: string, suffix: string): string[] {
83
- if (!existsSync(directory)) return [];
84
- return readdirSync(directory)
85
- .filter((name) => name.endsWith(suffix))
86
- .map((name) => join(directory, name));
87
- }
88
-
89
- function cargoVersionExists(crateName: string, version: string, registry: string): boolean {
90
- return taskCommandSucceeds(repoRoot, "cargo", [
91
- "info",
92
- `${crateName}@${version}`,
93
- "--registry",
94
- registry,
95
- ]);
96
- }
97
-
98
- interface CargoMetadataPackage {
99
- dependencies: Array<{ name: string; source?: string | null }>;
100
- manifest_path: string;
101
- name: string;
102
- }
103
-
104
- function orderedCargoManifests(config: RustWorkspaceMapping): string[] {
105
- const configured = new Set(config.crates.map((crate) => resolve(repoRoot, crate, "Cargo.toml")));
106
- const metadata = json.parseRecord(
107
- run("cargo", ["metadata", "--format-version", "1", "--no-deps", "--locked"], true),
108
- );
109
- if (!metadata || !Array.isArray(metadata.packages)) {
110
- throw new Error("Cargo metadata did not return workspace packages");
111
- }
112
- const packages = metadata.packages.filter(object.isRecord).map((pkg) => {
113
- if (
114
- typeof pkg.name !== "string" ||
115
- typeof pkg.manifest_path !== "string" ||
116
- !Array.isArray(pkg.dependencies)
117
- ) {
118
- throw new Error("Cargo metadata returned an invalid package");
119
- }
120
- return {
121
- name: pkg.name,
122
- manifest_path: resolve(pkg.manifest_path),
123
- dependencies: pkg.dependencies.filter(object.isRecord).map((dependency) => {
124
- if (typeof dependency.name !== "string") {
125
- throw new Error(`Cargo metadata returned an invalid dependency for ${pkg.name}`);
126
- }
127
- return {
128
- name: dependency.name,
129
- source: typeof dependency.source === "string" ? dependency.source : null,
130
- };
131
- }),
132
- } satisfies CargoMetadataPackage;
133
- });
134
- const publishable = new Map(
135
- packages
136
- .filter((pkg) => configured.has(pkg.manifest_path))
137
- .filter((pkg) => {
138
- const manifest = parse(readFileSync(pkg.manifest_path, "utf8")) as {
139
- package?: { publish?: boolean };
140
- };
141
- return manifest.package?.publish !== false;
142
- })
143
- .map((pkg) => [pkg.name, pkg]),
144
- );
145
- const ordered: CargoMetadataPackage[] = [];
146
- const visiting = new Set<string>();
147
- const completed = new Set<string>();
148
- const visit = (pkg: CargoMetadataPackage): void => {
149
- if (completed.has(pkg.name)) return;
150
- if (visiting.has(pkg.name)) {
151
- throw new Error(`Cyclic Cargo publication dependency: ${pkg.name}`);
152
- }
153
- visiting.add(pkg.name);
154
- for (const dependency of pkg.dependencies) {
155
- if (dependency.source) continue;
156
- const workspaceDependency = publishable.get(dependency.name);
157
- if (workspaceDependency) visit(workspaceDependency);
158
- }
159
- visiting.delete(pkg.name);
160
- completed.add(pkg.name);
161
- ordered.push(pkg);
162
- };
163
- for (const pkg of publishable.values()) visit(pkg);
164
- return ordered.map((pkg) => pkg.manifest_path);
165
- }
166
-
167
- function publishCargo(config: RustWorkspaceMapping, registry: string, version: string): void {
168
- const manifests = orderedCargoManifests(config);
169
- const originals = new Map(
170
- manifests.map((manifest) => [manifest, readFileSync(manifest, "utf8")]),
171
- );
172
- try {
173
- const crateNames = manifests.map((manifest) => {
174
- const document = parse(readFileSync(manifest, "utf8")) as {
175
- package?: { name?: string };
176
- };
177
- if (!document.package?.name) {
178
- throw new Error(`Cargo manifest has no package name: ${manifest}`);
179
- }
180
- return document.package.name;
181
- });
182
- for (const manifest of manifests) {
183
- const mode = statSync(manifest).mode;
184
- const document = parse(readFileSync(manifest, "utf8")) as Record<string, unknown>;
185
- for (const crateName of crateNames) {
186
- if (!crateName) continue;
187
- for (const sectionName of ["dependencies", "dev-dependencies", "build-dependencies"]) {
188
- const section = document[sectionName];
189
- if (!section || typeof section !== "object" || Array.isArray(section)) continue;
190
- const dependency = (section as Record<string, unknown>)[crateName];
191
- if (!dependency || typeof dependency !== "object" || Array.isArray(dependency)) {
192
- continue;
193
- }
194
- (dependency as Record<string, unknown>).registry = registry;
195
- }
196
- }
197
- chmodSync(manifest, mode | 0o200);
198
- writeFileSync(manifest, `${stringify(document).trimEnd()}\n`);
199
- chmodSync(manifest, mode);
200
- }
201
- for (const crateName of crateNames) {
202
- if (!crateName) continue;
203
- if (cargoVersionExists(crateName, version, registry)) {
204
- logger.info(`skip published ${crateName} @ ${version}`);
205
- continue;
206
- }
207
- run("cargo", [
208
- "publish",
209
- "--package",
210
- crateName,
211
- "--registry",
212
- registry,
213
- "--allow-dirty",
214
- "--no-verify",
215
- ]);
216
- }
217
- } finally {
218
- for (const [manifest, source] of originals) {
219
- const mode = statSync(manifest).mode;
220
- chmodSync(manifest, mode | 0o200);
221
- writeFileSync(manifest, source);
222
- chmodSync(manifest, mode);
223
- }
224
- }
225
- }
226
-
227
- function buildAndPublish(binding: RustBindingMapping, version: string): void {
228
- const registry = parsed.values.registry;
229
- const pypiPublishUrl = parsed.values["pypi-publish-url"];
230
- const includeNode = Boolean(registry && binding.node && binding.nodePackage);
231
- const includePython = Boolean(pypiPublishUrl && binding.python && binding.pythonPackage);
232
- if (!includeNode && !includePython) return;
233
-
234
- const target = nativeTarget();
235
- const output = resolve(repoRoot, "dist/uniffi");
236
- run("node", [
237
- resolve(dirname(fileURLToPath(import.meta.url)), "uniffi-release.mjs"),
238
- "build",
239
- "--crate",
240
- binding.crate,
241
- "--node",
242
- includeNode ? binding.node! : "",
243
- "--python",
244
- includePython ? binding.python! : "",
245
- "--node-package",
246
- includeNode ? binding.nodePackage! : "",
247
- "--python-package",
248
- includePython ? binding.pythonPackage! : "",
249
- "--python-module",
250
- includePython ? binding.pythonModule! : "",
251
- "--cargo-target",
252
- rustHost(),
253
- "--node-triple",
254
- target.node,
255
- "--python-tag",
256
- pythonTag(),
257
- "--os",
258
- target.os,
259
- "--cpu",
260
- target.cpu,
261
- "--libc",
262
- target.libc ?? "",
263
- "--facade",
264
- "true",
265
- "--version",
266
- version,
267
- "--output",
268
- "dist/uniffi",
269
- ]);
270
-
271
- if (includeNode) {
272
- const publishNpmScript = resolve(dirname(fileURLToPath(import.meta.url)), "publish-npm.ts");
273
- for (const directory of [join(output, "npm"), join(output, "npm-facade")]) {
274
- if (artifacts(directory, ".tgz").length === 0) continue;
275
- run(process.execPath, [
276
- publishNpmScript,
277
- "--directory",
278
- directory,
279
- "--version",
280
- version,
281
- "--registry",
282
- registry!,
283
- ]);
284
- }
285
- }
286
- if (includePython) {
287
- const wheels = artifacts(join(output, "python"), ".whl");
288
- if (wheels.length === 0) throw new Error(`No wheel produced for ${binding.crate}`);
289
- run("uvx", [
290
- "--from",
291
- "devpi-client",
292
- "devpi",
293
- "upload",
294
- "--index",
295
- pypiPublishUrl!,
296
- "--from-dir",
297
- join(output, "python"),
298
- ]);
299
- }
300
- }
301
-
302
- const version = parsed.values.version;
303
- if (!version) throw new Error("Missing --version");
304
- const config = rustConfig();
305
- if (config?.bindings.length) {
306
- if (!commandAvailable("cargo") || !commandAvailable("rustc")) {
307
- throw new Error("Cargo and rustc are required because UniFFI Rust projects were detected");
308
- }
309
- for (const binding of config.bindings) buildAndPublish(binding, version);
310
- }
311
- const cargoRegistry = parsed.values["cargo-registry"];
312
- if (cargoRegistry && config?.crates.length) {
313
- publishCargo(config, cargoRegistry, version);
314
- }