@dbx-tools/projen 0.9.19 → 0.9.21
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/._LICENSE +0 -0
- package/._README.md +0 -0
- package/._index.ts +0 -0
- package/._package.json +0 -0
- package/README.md +67 -83
- package/index.ts +2 -7
- package/package.json +9 -5
- package/src/.__lazy-require.ts +0 -0
- package/src/.__release-platform.ts +0 -0
- package/src/.__rust-project.ts +0 -0
- package/src/.__rust-release-workflow.ts +0 -0
- package/src/.__task-command.ts +0 -0
- package/src/._barrels.ts +0 -0
- package/src/._bun-app.ts +0 -0
- package/src/._bun-workflow.ts +0 -0
- package/src/._clean.ts +0 -0
- package/src/._codegen.ts +0 -0
- package/src/._dbx-tools-config.ts +0 -0
- package/src/._engine-root.ts +0 -0
- package/src/._generated.ts +0 -0
- package/src/._mixin.ts +0 -0
- package/src/._module-exports.ts +0 -0
- package/src/._openapi.ts +0 -0
- package/src/._packages.ts +0 -0
- package/src/._pnpm-workspace.ts +0 -0
- package/src/._project-js.ts +0 -0
- package/src/._project-predicate.ts +0 -0
- package/src/._project-py.ts +0 -0
- package/src/._project-rs.ts +0 -0
- package/src/._project.ts +0 -0
- package/src/._projen-version.ts +0 -0
- package/src/._publish.ts +0 -0
- package/src/._release-catalog.ts +0 -0
- package/src/._release-dispatch.ts +0 -0
- package/src/._release-github.ts +0 -0
- package/src/._release.ts +0 -0
- package/src/._scaffold.ts +0 -0
- package/src/._tags.ts +0 -0
- package/src/._tsconfig.ts +0 -0
- package/src/._uniffi.ts +0 -0
- package/src/._vscode.ts +0 -0
- package/src/._watch.ts +0 -0
- package/src/._workspace-lock.ts +0 -0
- package/src/._workspace-version.ts +0 -0
- package/src/_rust-release-workflow.ts +4 -391
- package/src/_task-command.ts +75 -0
- package/src/project-js.ts +2 -42
- package/src/project-py.ts +5 -96
- package/src/project-rs.ts +3 -76
- package/src/release-catalog.ts +37 -203
- package/src/release.ts +12 -634
- package/src/scaffold.ts +2 -6
- package/src/workspace-version.ts +9 -121
- package/tasks/._barrels.ts +0 -0
- package/tasks/._bump.ts +0 -0
- package/tasks/._clean.ts +0 -0
- package/tasks/._compile-workspace.ts +0 -0
- package/tasks/._local-publish.ts +0 -0
- package/tasks/._openapi.ts +0 -0
- package/tasks/._projenrc.ts +0 -0
- package/tasks/._publish-npm.ts +0 -0
- package/tasks/._publish-python.ts +0 -0
- package/tasks/._publish-uniffi-local.ts +0 -0
- package/tasks/._publish.ts +0 -0
- package/tasks/._python-registry.ts +0 -0
- package/tasks/._release-assets.ts +0 -0
- package/tasks/._release-candidate.ts +0 -0
- package/tasks/._release-manifest.ts +0 -0
- package/tasks/._release-pr.ts +0 -0
- package/tasks/._release-summary.ts +0 -0
- package/tasks/._release-version.ts +0 -0
- package/tasks/._rust.ts +0 -0
- package/tasks/._sync.ts +0 -0
- package/tasks/._uniffi-python.d.ts +0 -0
- package/tasks/._uniffi-python.js +0 -0
- package/tasks/._uniffi-release.mjs +0 -0
- package/tasks/._uniffi.ts +0 -0
- package/tasks/._version-check.ts +0 -0
- package/tasks/bump.ts +17 -34
- package/tasks/local-publish.ts +124 -117
- package/tasks/publish-npm.ts +62 -30
- package/tasks/publish-python.ts +38 -140
- package/tasks/publish-uniffi-local.ts +129 -84
- package/tasks/publish.ts +25 -139
- package/tasks/python-registry.ts +2 -9
- package/tasks/release-assets.ts +56 -65
- package/tasks/release-candidate.ts +123 -90
- package/tasks/release-pr.ts +266 -240
- package/tasks/release-summary.ts +8 -10
- package/tasks/release-version.ts +23 -2
- package/tasks/rust.ts +8 -13
- package/tasks/uniffi-python.d.ts +2 -3
- package/tasks/uniffi-python.js +7 -20
- package/tasks/uniffi-release.mjs +19 -13
- package/tasks/uniffi.ts +15 -24
- package/tasks/version-check.ts +24 -56
- package/src/release-plan.ts +0 -269
- package/src/release-please.ts +0 -242
- package/tasks/release-bootstrap.ts +0 -59
- package/tasks/release-plan.ts +0 -89
- package/tasks/release-please.ts +0 -162
- package/tasks/release-request.ts +0 -166
- package/tasks/release-summary-units.ts +0 -75
- package/tasks/stamp-python.ts +0 -13
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env -S bun
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
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";
|
|
5
15
|
import { fileURLToPath } from "node:url";
|
|
6
16
|
import { parseArgs } from "node:util";
|
|
7
17
|
import { json, log, object } from "@dbx-tools/shared-core";
|
|
@@ -12,33 +22,21 @@ import type { RustBindingMapping, RustWorkspaceMapping } from "../src/project-rs
|
|
|
12
22
|
|
|
13
23
|
const logger = log.logger("projen:publish-uniffi-local");
|
|
14
24
|
|
|
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
25
|
function commandAvailable(command: string): boolean {
|
|
31
26
|
return taskCommandSucceeds(repoRoot, command, ["--version"]);
|
|
32
27
|
}
|
|
33
28
|
|
|
34
29
|
function rustHost(): string {
|
|
35
|
-
const host =
|
|
30
|
+
const host = captureTaskCommand(repoRoot, "rustc", ["-vV"], { check: true }).match(
|
|
31
|
+
/^host:\s+(.+)$/m,
|
|
32
|
+
)?.[1];
|
|
36
33
|
if (!host) throw new Error("Unable to detect the local Rust target from rustc -vV");
|
|
37
34
|
return host;
|
|
38
35
|
}
|
|
39
36
|
|
|
40
37
|
function pythonTag(): string {
|
|
41
|
-
const value =
|
|
38
|
+
const value = captureTaskCommand(
|
|
39
|
+
repoRoot,
|
|
42
40
|
"uv",
|
|
43
41
|
[
|
|
44
42
|
"run",
|
|
@@ -47,7 +45,7 @@ function pythonTag(): string {
|
|
|
47
45
|
"-c",
|
|
48
46
|
"import sysconfig; print(sysconfig.get_platform().replace('-', '_').replace('.', '_'))",
|
|
49
47
|
],
|
|
50
|
-
true,
|
|
48
|
+
{ check: true },
|
|
51
49
|
);
|
|
52
50
|
if (!value) throw new Error("Unable to detect the local Python wheel platform tag");
|
|
53
51
|
return value;
|
|
@@ -99,12 +97,18 @@ interface CargoMetadataPackage {
|
|
|
99
97
|
dependencies: Array<{ name: string; source?: string | null }>;
|
|
100
98
|
manifest_path: string;
|
|
101
99
|
name: string;
|
|
100
|
+
version: string;
|
|
102
101
|
}
|
|
103
102
|
|
|
104
|
-
function orderedCargoManifests(config: RustWorkspaceMapping):
|
|
105
|
-
const configured = new Set(config.crates.map((crate) => resolve(
|
|
103
|
+
function orderedCargoManifests(root: string, config: RustWorkspaceMapping): CargoMetadataPackage[] {
|
|
104
|
+
const configured = new Set(config.crates.map((crate) => resolve(root, crate, "Cargo.toml")));
|
|
106
105
|
const metadata = json.parseRecord(
|
|
107
|
-
|
|
106
|
+
captureTaskCommand(
|
|
107
|
+
root,
|
|
108
|
+
"cargo",
|
|
109
|
+
["metadata", "--format-version", "1", "--no-deps", "--locked"],
|
|
110
|
+
{ check: true },
|
|
111
|
+
),
|
|
108
112
|
);
|
|
109
113
|
if (!metadata || !Array.isArray(metadata.packages)) {
|
|
110
114
|
throw new Error("Cargo metadata did not return workspace packages");
|
|
@@ -112,6 +116,7 @@ function orderedCargoManifests(config: RustWorkspaceMapping): string[] {
|
|
|
112
116
|
const packages = metadata.packages.filter(object.isRecord).map((pkg) => {
|
|
113
117
|
if (
|
|
114
118
|
typeof pkg.name !== "string" ||
|
|
119
|
+
typeof pkg.version !== "string" ||
|
|
115
120
|
typeof pkg.manifest_path !== "string" ||
|
|
116
121
|
!Array.isArray(pkg.dependencies)
|
|
117
122
|
) {
|
|
@@ -119,6 +124,7 @@ function orderedCargoManifests(config: RustWorkspaceMapping): string[] {
|
|
|
119
124
|
}
|
|
120
125
|
return {
|
|
121
126
|
name: pkg.name,
|
|
127
|
+
version: pkg.version,
|
|
122
128
|
manifest_path: resolve(pkg.manifest_path),
|
|
123
129
|
dependencies: pkg.dependencies.filter(object.isRecord).map((dependency) => {
|
|
124
130
|
if (typeof dependency.name !== "string") {
|
|
@@ -161,79 +167,103 @@ function orderedCargoManifests(config: RustWorkspaceMapping): string[] {
|
|
|
161
167
|
ordered.push(pkg);
|
|
162
168
|
};
|
|
163
169
|
for (const pkg of publishable.values()) visit(pkg);
|
|
164
|
-
return ordered
|
|
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
|
+
}
|
|
165
216
|
}
|
|
166
217
|
|
|
167
218
|
function publishCargo(config: RustWorkspaceMapping, registry: string, version: string): void {
|
|
168
|
-
const
|
|
169
|
-
const originals = new Map(
|
|
170
|
-
manifests.map((manifest) => [manifest, readFileSync(manifest, "utf8")]),
|
|
171
|
-
);
|
|
219
|
+
const root = copyCargoWorkspace(config);
|
|
172
220
|
try {
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
throw new Error(`Cargo manifest has no package name: ${manifest}`);
|
|
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}`);
|
|
179
226
|
}
|
|
180
|
-
|
|
181
|
-
|
|
227
|
+
}
|
|
228
|
+
const manifests = [join(root, "Cargo.toml"), ...packages.map((pkg) => pkg.manifest_path)];
|
|
182
229
|
for (const manifest of manifests) {
|
|
183
|
-
const mode = statSync(manifest).mode;
|
|
184
230
|
const document = parse(readFileSync(manifest, "utf8")) as Record<string, unknown>;
|
|
185
|
-
|
|
186
|
-
|
|
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);
|
|
231
|
+
projectCargoRegistry(document, crateNames, registry);
|
|
232
|
+
chmodSync(manifest, 0o644);
|
|
198
233
|
writeFileSync(manifest, `${stringify(document).trimEnd()}\n`);
|
|
199
|
-
chmodSync(manifest, mode);
|
|
200
234
|
}
|
|
201
|
-
for (const
|
|
202
|
-
if (
|
|
203
|
-
|
|
204
|
-
logger.info(`skip published ${crateName} @ ${version}`);
|
|
235
|
+
for (const pkg of packages) {
|
|
236
|
+
if (cargoVersionExists(pkg.name, version, registry)) {
|
|
237
|
+
logger.info(`skip published ${pkg.name} @ ${version}`);
|
|
205
238
|
continue;
|
|
206
239
|
}
|
|
207
|
-
|
|
240
|
+
runTaskCommand(root, "cargo", [
|
|
208
241
|
"publish",
|
|
209
|
-
"--
|
|
210
|
-
|
|
242
|
+
"--manifest-path",
|
|
243
|
+
pkg.manifest_path,
|
|
211
244
|
"--registry",
|
|
212
245
|
registry,
|
|
213
|
-
"--
|
|
214
|
-
"--no-verify",
|
|
246
|
+
"--locked",
|
|
215
247
|
]);
|
|
216
248
|
}
|
|
217
249
|
} finally {
|
|
218
|
-
|
|
219
|
-
const mode = statSync(manifest).mode;
|
|
220
|
-
chmodSync(manifest, mode | 0o200);
|
|
221
|
-
writeFileSync(manifest, source);
|
|
222
|
-
chmodSync(manifest, mode);
|
|
223
|
-
}
|
|
250
|
+
rmSync(root, { recursive: true, force: true });
|
|
224
251
|
}
|
|
225
252
|
}
|
|
226
253
|
|
|
227
|
-
function buildAndPublish(
|
|
228
|
-
|
|
229
|
-
|
|
254
|
+
function buildAndPublish(
|
|
255
|
+
binding: RustBindingMapping,
|
|
256
|
+
version: string,
|
|
257
|
+
registry: string | undefined,
|
|
258
|
+
pypiPublishUrl: string | undefined,
|
|
259
|
+
): void {
|
|
230
260
|
const includeNode = Boolean(registry && binding.node && binding.nodePackage);
|
|
231
261
|
const includePython = Boolean(pypiPublishUrl && binding.python && binding.pythonPackage);
|
|
232
262
|
if (!includeNode && !includePython) return;
|
|
233
263
|
|
|
234
264
|
const target = nativeTarget();
|
|
235
265
|
const output = resolve(repoRoot, "dist/uniffi");
|
|
236
|
-
|
|
266
|
+
runTaskCommand(repoRoot, "node", [
|
|
237
267
|
resolve(dirname(fileURLToPath(import.meta.url)), "uniffi-release.mjs"),
|
|
238
268
|
"build",
|
|
239
269
|
"--crate",
|
|
@@ -272,7 +302,7 @@ function buildAndPublish(binding: RustBindingMapping, version: string): void {
|
|
|
272
302
|
const publishNpmScript = resolve(dirname(fileURLToPath(import.meta.url)), "publish-npm.ts");
|
|
273
303
|
for (const directory of [join(output, "npm"), join(output, "npm-facade")]) {
|
|
274
304
|
if (artifacts(directory, ".tgz").length === 0) continue;
|
|
275
|
-
|
|
305
|
+
runTaskCommand(repoRoot, process.execPath, [
|
|
276
306
|
publishNpmScript,
|
|
277
307
|
"--directory",
|
|
278
308
|
directory,
|
|
@@ -286,7 +316,7 @@ function buildAndPublish(binding: RustBindingMapping, version: string): void {
|
|
|
286
316
|
if (includePython) {
|
|
287
317
|
const wheels = artifacts(join(output, "python"), ".whl");
|
|
288
318
|
if (wheels.length === 0) throw new Error(`No wheel produced for ${binding.crate}`);
|
|
289
|
-
|
|
319
|
+
runTaskCommand(repoRoot, "uvx", [
|
|
290
320
|
"--from",
|
|
291
321
|
"devpi-client",
|
|
292
322
|
"devpi",
|
|
@@ -299,16 +329,31 @@ function buildAndPublish(binding: RustBindingMapping, version: string): void {
|
|
|
299
329
|
}
|
|
300
330
|
}
|
|
301
331
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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);
|
|
308
356
|
}
|
|
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
357
|
}
|
|
358
|
+
|
|
359
|
+
if (import.meta.main) main();
|
package/tasks/publish.ts
CHANGED
|
@@ -18,14 +18,14 @@
|
|
|
18
18
|
* version. When Bun's workspace lock already carries the version, its
|
|
19
19
|
* refresh is skipped;
|
|
20
20
|
* - **`publishConfig` substitution** (compiled `lib/` entry points) is done
|
|
21
|
-
*
|
|
21
|
+
* inside the temporary archive, NOT in the checkout: unlike pnpm/npm, `bun
|
|
22
22
|
* publish`/`bun pm pack` do NOT fold `publishConfig`'s `main`/`types`/`bin`/
|
|
23
23
|
* `exports` into the packed manifest (verified: the packed manifest keeps the
|
|
24
24
|
* raw `.ts` source paths and an inert `publishConfig`). Left unsubstituted, a
|
|
25
25
|
* published CLI's `bin` points at `./bin/x.ts`, and because the bin runs via
|
|
26
26
|
* its `#!/usr/bin/env node` shebang, node chokes on the `.ts`
|
|
27
27
|
* (ERR_UNKNOWN_FILE_EXTENSION). We merge `publishConfig` onto the top-level
|
|
28
|
-
* manifest
|
|
28
|
+
* manifest after packing so the tarball advertises the compiled `lib/` tree;
|
|
29
29
|
* - **compiled output** is emitted once, before packing, by one root-level
|
|
30
30
|
* filtered `bun run` that fans out to every publishable member in parallel.
|
|
31
31
|
* Every package is then packed once with lifecycle scripts disabled. The
|
|
@@ -42,28 +42,10 @@
|
|
|
42
42
|
* non-default registry (a local verdaccio); `--exclude <dir>` (repeatable,
|
|
43
43
|
* repo-relative) skips a member owned by another publication flow.
|
|
44
44
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* The disk manifests carry the workspace `VERSION` (projen owns them, read-only);
|
|
49
|
-
* this unlocks each only long enough to fold in the `publishConfig` entry points
|
|
50
|
-
* and publish, then RESTORES every one it touched
|
|
51
|
-
* byte-for-byte (and re-locks the mode) on the way out - see
|
|
52
|
-
* {@link restoreManifests}. Restore returns each manifest to its committed
|
|
53
|
-
* content, which already equals the release version, so the worktree is never
|
|
54
|
-
* left regressed.
|
|
45
|
+
* The checkout remains byte-for-byte unchanged. A stale manifest or lockfile is
|
|
46
|
+
* a release error rather than something publication repairs.
|
|
55
47
|
*/
|
|
56
|
-
import {
|
|
57
|
-
chmodSync,
|
|
58
|
-
copyFileSync,
|
|
59
|
-
existsSync,
|
|
60
|
-
mkdirSync,
|
|
61
|
-
mkdtempSync,
|
|
62
|
-
readFileSync,
|
|
63
|
-
rmSync,
|
|
64
|
-
statSync,
|
|
65
|
-
writeFileSync,
|
|
66
|
-
} from "node:fs";
|
|
48
|
+
import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
67
49
|
import { tmpdir } from "node:os";
|
|
68
50
|
import { delimiter, dirname, join, resolve } from "node:path";
|
|
69
51
|
import { asyncUtils, log } from "@dbx-tools/shared-core";
|
|
@@ -77,25 +59,9 @@ import {
|
|
|
77
59
|
} from "./publish-npm.ts";
|
|
78
60
|
import { runTaskCommand, runTaskCommandAsync } from "../src/_task-command.ts";
|
|
79
61
|
import { toPosix } from "../src/packages.ts";
|
|
80
|
-
import type { ReleasePlan } from "../src/release-plan.ts";
|
|
81
62
|
|
|
82
63
|
const logger = log.logger("projen:publish");
|
|
83
64
|
|
|
84
|
-
/** A manifest's on-disk bytes + mode, captured before this task edits it. */
|
|
85
|
-
interface ManifestBackup {
|
|
86
|
-
readonly content: string;
|
|
87
|
-
readonly mode: number;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Original state of every manifest this task has unlocked, keyed by path.
|
|
92
|
-
*
|
|
93
|
-
* Bun does not apply `publishConfig` while packing, so publication temporarily
|
|
94
|
-
* projects those entry points onto projen-owned manifests. The projection is
|
|
95
|
-
* only meant for the packed tarball and is restored before the process exits.
|
|
96
|
-
*/
|
|
97
|
-
const manifestBackups = new Map<string, ManifestBackup>();
|
|
98
|
-
|
|
99
65
|
/**
|
|
100
66
|
* Workspace member dirs (absolute), read from the root `pnpm-workspace.yaml` - the
|
|
101
67
|
* file the engine keeps for the Databricks Apps pnpm deploy, which also lists every
|
|
@@ -109,18 +75,13 @@ function workspaceMembers(root: string): string[] {
|
|
|
109
75
|
return (doc?.packages ?? []).map((m) => resolve(root, m));
|
|
110
76
|
}
|
|
111
77
|
|
|
112
|
-
/** Whether selected workspace members carry
|
|
113
|
-
function
|
|
114
|
-
root: string,
|
|
115
|
-
members: readonly string[],
|
|
116
|
-
expected: ReadonlyMap<string, string>,
|
|
117
|
-
): boolean {
|
|
78
|
+
/** Whether selected workspace members carry the reviewed workspace version. */
|
|
79
|
+
function manifestsMatchVersion(members: readonly string[], version: string): boolean {
|
|
118
80
|
return members.every((dir) => {
|
|
119
81
|
const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8")) as {
|
|
120
82
|
version?: string;
|
|
121
83
|
};
|
|
122
|
-
|
|
123
|
-
return pkg.version === expected.get(path);
|
|
84
|
+
return pkg.version === version;
|
|
124
85
|
});
|
|
125
86
|
}
|
|
126
87
|
|
|
@@ -146,41 +107,6 @@ function lockfileMatchesManifestVersions(root: string, members: readonly string[
|
|
|
146
107
|
}
|
|
147
108
|
}
|
|
148
109
|
|
|
149
|
-
/**
|
|
150
|
-
* Make a projen-readonly manifest writable so the publishConfig projection can
|
|
151
|
-
* edit it, recording its original bytes and mode for {@link restoreManifests}
|
|
152
|
-
* the first time it is seen.
|
|
153
|
-
*/
|
|
154
|
-
function unlockManifest(pkgPath: string): void {
|
|
155
|
-
const mode = statSync(pkgPath).mode;
|
|
156
|
-
if (!manifestBackups.has(pkgPath)) {
|
|
157
|
-
manifestBackups.set(pkgPath, { content: readFileSync(pkgPath, "utf8"), mode });
|
|
158
|
-
}
|
|
159
|
-
chmodSync(pkgPath, mode | 0o200);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Put every manifest this task edited back exactly as it was found, mode included.
|
|
164
|
-
*
|
|
165
|
-
* Runs from an `exit` handler so a clean finish and a failure partway through the
|
|
166
|
-
* publish loop are covered alike - a `bun publish` that dies on package 12 of 34
|
|
167
|
-
* must not leave the first 11 projected. Best-effort per file: one unwritable
|
|
168
|
-
* manifest is logged and the rest are still restored, since a partial restore
|
|
169
|
-
* beats none.
|
|
170
|
-
*/
|
|
171
|
-
function restoreManifests(): void {
|
|
172
|
-
for (const [pkgPath, backup] of manifestBackups) {
|
|
173
|
-
try {
|
|
174
|
-
chmodSync(pkgPath, backup.mode | 0o200);
|
|
175
|
-
writeFileSync(pkgPath, backup.content);
|
|
176
|
-
chmodSync(pkgPath, backup.mode);
|
|
177
|
-
} catch (cause) {
|
|
178
|
-
logger.warn(`could not restore ${pkgPath}`, { cause });
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
manifestBackups.clear();
|
|
182
|
-
}
|
|
183
|
-
|
|
184
110
|
/** Entry-point fields projen writes as `.ts` source in-repo and rewrites to `lib/` for publish. */
|
|
185
111
|
const PUBLISH_CONFIG_ENTRY_FIELDS = ["main", "types", "bin", "exports"] as const;
|
|
186
112
|
|
|
@@ -195,23 +121,18 @@ function compiledPublishTargets(value: unknown): string[] {
|
|
|
195
121
|
/**
|
|
196
122
|
* Fold a package's `publishConfig` entry-point fields onto the top-level manifest,
|
|
197
123
|
* the way pnpm/npm do at pack time but `bun publish` does NOT (see the module
|
|
198
|
-
* doc).
|
|
199
|
-
* in place (npm ignores it once the top-level fields already point at `lib/`). The
|
|
200
|
-
* manifest must already be unlocked. {@link restoreManifests} puts the `.ts` entry
|
|
201
|
-
* points back at exit, so this only ever affects the packed tarball.
|
|
124
|
+
* doc). The input is the temporary archive manifest, never the checkout.
|
|
202
125
|
*/
|
|
203
|
-
function applyPublishConfig(
|
|
204
|
-
|
|
126
|
+
export function applyPublishConfig(
|
|
127
|
+
source: Readonly<Record<string, unknown>>,
|
|
128
|
+
): Readonly<Record<string, unknown>> {
|
|
129
|
+
const pkg = { ...source };
|
|
205
130
|
const publishConfig = pkg.publishConfig as Record<string, unknown> | undefined;
|
|
206
|
-
if (!publishConfig) return;
|
|
207
|
-
let changed = false;
|
|
131
|
+
if (!publishConfig) return pkg;
|
|
208
132
|
for (const field of PUBLISH_CONFIG_ENTRY_FIELDS) {
|
|
209
|
-
if (field in publishConfig)
|
|
210
|
-
pkg[field] = publishConfig[field];
|
|
211
|
-
changed = true;
|
|
212
|
-
}
|
|
133
|
+
if (field in publishConfig) pkg[field] = publishConfig[field];
|
|
213
134
|
}
|
|
214
|
-
|
|
135
|
+
return pkg;
|
|
215
136
|
}
|
|
216
137
|
|
|
217
138
|
/**
|
|
@@ -229,14 +150,9 @@ const argv = process.argv.slice(2);
|
|
|
229
150
|
const version = argv[0] && !argv[0].startsWith("--") ? argv[0] : undefined;
|
|
230
151
|
const rest = version ? argv.slice(1) : argv;
|
|
231
152
|
const root = process.cwd();
|
|
232
|
-
|
|
233
|
-
const plan =
|
|
234
|
-
planIdx >= 0
|
|
235
|
-
? (JSON.parse(readFileSync(resolve(root, rest[planIdx + 1]), "utf8")) as ReleasePlan)
|
|
236
|
-
: undefined;
|
|
237
|
-
if (!version && !plan) {
|
|
153
|
+
if (!version) {
|
|
238
154
|
logger.error(
|
|
239
|
-
"usage: bun tasks/publish.ts <version> [--
|
|
155
|
+
"usage: bun tasks/publish.ts <version> [--registry <url>] [--output <dir>] [--exclude <dir>] [--dry-run] [--skip-compile]",
|
|
240
156
|
);
|
|
241
157
|
process.exit(1);
|
|
242
158
|
}
|
|
@@ -252,46 +168,25 @@ if (!Number.isInteger(parsedConcurrency) || parsedConcurrency < 1) {
|
|
|
252
168
|
throw new Error(`--concurrency must be a positive integer, got ${String(parsedConcurrency)}`);
|
|
253
169
|
}
|
|
254
170
|
const concurrency = parsedConcurrency;
|
|
255
|
-
// Undo transient publishConfig edits at exit.
|
|
256
|
-
const restore = !rest.includes("--no-restore");
|
|
257
171
|
const excluded = new Set(
|
|
258
172
|
rest.reduce<string[]>((acc, arg, i) => (arg === "--exclude" ? [...acc, rest[i + 1]] : acc), []),
|
|
259
173
|
);
|
|
260
174
|
|
|
261
175
|
const path = enrichedPath(root);
|
|
262
|
-
// Registered BEFORE the first manifest edit so no exit path can skip it: a clean
|
|
263
|
-
// finish and a `bun publish` failure mid-loop both land here, so a publish that
|
|
264
|
-
// dies partway does not leave half the workspace stamped.
|
|
265
|
-
if (restore) process.on("exit", restoreManifests);
|
|
266
176
|
const allMembers = workspaceMembers(root)
|
|
267
177
|
.filter((dir) => existsSync(join(dir, "package.json")))
|
|
268
178
|
.filter((dir) => !excluded.has(resolve(root, dir).replace(`${resolve(root)}/`, "")));
|
|
269
|
-
const
|
|
270
|
-
plan
|
|
271
|
-
? plan.nodePackages.map((pkg) => [pkg.path, pkg.version] as const)
|
|
272
|
-
: allMembers.map((dir) => [toPosix(dir.slice(resolve(root).length + 1)), version!]),
|
|
273
|
-
);
|
|
274
|
-
const members = plan
|
|
275
|
-
? allMembers.filter((dir) => expectedVersions.has(toPosix(dir.slice(resolve(root).length + 1))))
|
|
276
|
-
: allMembers;
|
|
179
|
+
const members = allMembers;
|
|
277
180
|
|
|
278
|
-
if (!
|
|
279
|
-
throw new Error(
|
|
181
|
+
if (!manifestsMatchVersion(members, version)) {
|
|
182
|
+
throw new Error(`workspace manifests do not match release ${version}; run projen`);
|
|
280
183
|
}
|
|
281
184
|
logger.info(`validated ${members.length} selected member manifests`);
|
|
282
185
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
// live manifests. A normal bump's synth/install already makes it current; after
|
|
286
|
-
// fallback stamping, deleting it before install is what forces re-resolution.
|
|
287
|
-
const lockfile = join(root, "bun.lock");
|
|
288
|
-
if (lockfileMatchesManifestVersions(root, allMembers)) {
|
|
289
|
-
logger.info("workspace lock already resolves live member versions");
|
|
290
|
-
} else {
|
|
291
|
-
if (existsSync(lockfile)) rmSync(lockfile);
|
|
292
|
-
logger.info("refreshing lockfile so workspace deps resolve to the release version");
|
|
293
|
-
runTaskCommand(root, "bun", ["install"], { env: { ...process.env, PATH: path } });
|
|
186
|
+
if (!lockfileMatchesManifestVersions(root, allMembers)) {
|
|
187
|
+
throw new Error("bun.lock does not match workspace versions; run bun install and commit it");
|
|
294
188
|
}
|
|
189
|
+
logger.info("validated workspace lock versions");
|
|
295
190
|
|
|
296
191
|
const publishArgs = [
|
|
297
192
|
"--ignore-scripts",
|
|
@@ -365,15 +260,6 @@ if (compiled.length > 0) {
|
|
|
365
260
|
}
|
|
366
261
|
}
|
|
367
262
|
|
|
368
|
-
// Compile against workspace source exports first. Switching manifests to their
|
|
369
|
-
// publishConfig entries before compilation makes consumers resolve sibling
|
|
370
|
-
// `lib/*.d.ts` files that have not been emitted yet in a clean checkout.
|
|
371
|
-
for (const { dir } of publishable) {
|
|
372
|
-
const manifestPath = join(dir, "package.json");
|
|
373
|
-
unlockManifest(manifestPath);
|
|
374
|
-
applyPublishConfig(manifestPath);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
263
|
logger.info(
|
|
378
264
|
`${output ? "packing" : dryRun ? "dry-run packing" : "publishing"} ${publishable.length} packages with concurrency ${concurrency}`,
|
|
379
265
|
);
|
|
@@ -386,7 +272,7 @@ await asyncUtils.mapConcurrent(
|
|
|
386
272
|
async ({ dir, name, version: packageVersion, access }) => {
|
|
387
273
|
const packed = mkdtempSync(join(tmpdir(), "projen-npm-release-"));
|
|
388
274
|
try {
|
|
389
|
-
const archive = packNpmPackage(dir, packed, path);
|
|
275
|
+
const archive = packNpmPackage(dir, packed, path, applyPublishConfig);
|
|
390
276
|
const local = readNpmArchiveIdentity(archive);
|
|
391
277
|
if (local.name !== name || local.version !== packageVersion) {
|
|
392
278
|
throw new Error(
|
package/tasks/python-registry.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
|
-
import * as exec from "@dbx-tools/core/exec";
|
|
5
4
|
import { net } from "@dbx-tools/shared-core";
|
|
5
|
+
import { captureTaskCommand } from "../src/_task-command.ts";
|
|
6
6
|
|
|
7
7
|
export interface LocalPythonRegistry {
|
|
8
8
|
readonly indexUrl: string;
|
|
@@ -126,14 +126,7 @@ function splitIndexList(value: string | undefined): string[] {
|
|
|
126
126
|
|
|
127
127
|
/** Read a `pip config get <key>`, treating pip's literal "undefined" as unset. */
|
|
128
128
|
function pipConfig(key: string): string | undefined {
|
|
129
|
-
const
|
|
130
|
-
cwd: process.cwd(),
|
|
131
|
-
stdout: "capture",
|
|
132
|
-
stderr: "ignore",
|
|
133
|
-
stdin: "ignore",
|
|
134
|
-
check: false,
|
|
135
|
-
});
|
|
136
|
-
const out = res.stdout?.trim();
|
|
129
|
+
const out = captureTaskCommand(process.cwd(), "python", ["-m", "pip", "config", "get", key]);
|
|
137
130
|
return out && out !== "undefined" ? out : undefined;
|
|
138
131
|
}
|
|
139
132
|
|