@dbx-tools/projen 0.9.18 → 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 +72 -88
- 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 +18 -582
- package/src/_task-command.ts +75 -0
- package/src/project-js.ts +2 -42
- package/src/project-py.ts +60 -162
- package/src/project-rs.ts +4 -100
- package/src/release-catalog.ts +37 -203
- package/src/release.ts +96 -775
- 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 +125 -41
- package/tasks/publish-python.ts +79 -106
- package/tasks/publish-uniffi-local.ts +129 -84
- package/tasks/publish.ts +44 -139
- package/tasks/python-registry.ts +2 -9
- package/tasks/release-assets.ts +56 -65
- package/tasks/release-candidate.ts +416 -0
- package/tasks/release-manifest.ts +273 -0
- package/tasks/release-pr.ts +312 -369
- 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
package/tasks/publish-npm.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/** Idempotent npm archive publication for release recovery. */
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
4
|
import {
|
|
5
|
+
chmodSync,
|
|
5
6
|
lstatSync,
|
|
6
7
|
mkdtempSync,
|
|
7
8
|
readFileSync,
|
|
@@ -15,6 +16,7 @@ import { join, relative, resolve } from "node:path";
|
|
|
15
16
|
import * as exec from "@dbx-tools/core/exec";
|
|
16
17
|
import { log } from "@dbx-tools/shared-core";
|
|
17
18
|
import { Command } from "commander";
|
|
19
|
+
import { runTaskCommand } from "../src/_task-command.ts";
|
|
18
20
|
|
|
19
21
|
const DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
|
20
22
|
const logger = log.logger("projen:publish-npm");
|
|
@@ -28,6 +30,20 @@ export interface NpmReleaseIdentity {
|
|
|
28
30
|
readonly version: string;
|
|
29
31
|
}
|
|
30
32
|
|
|
33
|
+
interface NpmArchiveManifest {
|
|
34
|
+
readonly [key: string]: unknown;
|
|
35
|
+
readonly dependencies?: Readonly<Record<string, string>>;
|
|
36
|
+
readonly optionalDependencies?: Readonly<Record<string, string>>;
|
|
37
|
+
readonly name?: string;
|
|
38
|
+
readonly publishConfig?: { readonly access?: unknown };
|
|
39
|
+
readonly repository?: unknown;
|
|
40
|
+
readonly version?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type NpmArchiveManifestTransform = (
|
|
44
|
+
manifest: NpmArchiveManifest,
|
|
45
|
+
) => Readonly<Record<string, unknown>>;
|
|
46
|
+
|
|
31
47
|
function normalizedRepository(value: unknown): string | undefined {
|
|
32
48
|
const url =
|
|
33
49
|
typeof value === "string"
|
|
@@ -120,7 +136,7 @@ export async function publishedNpmRelease(
|
|
|
120
136
|
};
|
|
121
137
|
}
|
|
122
138
|
|
|
123
|
-
|
|
139
|
+
function readNpmArchiveManifest(path: string): NpmArchiveManifest {
|
|
124
140
|
const result = exec.spawnSync("tar", ["-xOf", path, "package/package.json"], {
|
|
125
141
|
cwd: process.cwd(),
|
|
126
142
|
stdout: "capture",
|
|
@@ -131,12 +147,11 @@ export function readNpmArchiveIdentity(path: string): NpmReleaseIdentity {
|
|
|
131
147
|
if (result.exitCode !== 0 || !result.stdout) {
|
|
132
148
|
throw new Error(`Cannot read npm package manifest from ${path}: ${result.stderr}`);
|
|
133
149
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
};
|
|
150
|
+
return JSON.parse(result.stdout) as NpmArchiveManifest;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function readNpmArchiveIdentity(path: string): NpmReleaseIdentity {
|
|
154
|
+
const manifest = readNpmArchiveManifest(path);
|
|
140
155
|
if (!manifest.name || !manifest.version) {
|
|
141
156
|
throw new Error(`npm archive has no package name or version: ${path}`);
|
|
142
157
|
}
|
|
@@ -154,6 +169,48 @@ export function readNpmArchiveIdentity(path: string): NpmReleaseIdentity {
|
|
|
154
169
|
};
|
|
155
170
|
}
|
|
156
171
|
|
|
172
|
+
function orderNpmArchives(archives: readonly string[]): string[] {
|
|
173
|
+
const byName = new Map(
|
|
174
|
+
archives.map((archive) => {
|
|
175
|
+
const manifest = readNpmArchiveManifest(archive);
|
|
176
|
+
if (!manifest.name) throw new Error(`npm archive has no package name: ${archive}`);
|
|
177
|
+
return [manifest.name, { archive, manifest }] as const;
|
|
178
|
+
}),
|
|
179
|
+
);
|
|
180
|
+
if (byName.size !== archives.length) throw new Error("Release contains duplicate npm packages");
|
|
181
|
+
const remaining = new Map(
|
|
182
|
+
[...byName].map(([name, value]) => [
|
|
183
|
+
name,
|
|
184
|
+
new Set(
|
|
185
|
+
[
|
|
186
|
+
...Object.keys(value.manifest.dependencies ?? {}),
|
|
187
|
+
...Object.keys(value.manifest.optionalDependencies ?? {}),
|
|
188
|
+
].filter((dependency) => byName.has(dependency)),
|
|
189
|
+
),
|
|
190
|
+
]),
|
|
191
|
+
);
|
|
192
|
+
const ordered: string[] = [];
|
|
193
|
+
while (remaining.size > 0) {
|
|
194
|
+
const ready = [...remaining]
|
|
195
|
+
.filter(([, dependencies]) => dependencies.size === 0)
|
|
196
|
+
.map(([name]) => name)
|
|
197
|
+
.sort();
|
|
198
|
+
if (ready.length === 0) {
|
|
199
|
+
throw new Error(
|
|
200
|
+
`Cyclic npm release dependencies: ${[...remaining.keys()].sort().join(", ")}`,
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
for (const name of ready) {
|
|
204
|
+
ordered.push(byName.get(name)!.archive);
|
|
205
|
+
remaining.delete(name);
|
|
206
|
+
}
|
|
207
|
+
for (const dependencies of remaining.values()) {
|
|
208
|
+
for (const name of ready) dependencies.delete(name);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return ordered;
|
|
212
|
+
}
|
|
213
|
+
|
|
157
214
|
/** Hash paths, executable bits, symlink targets, and bytes while ignoring tar metadata. */
|
|
158
215
|
export function npmArchiveContentDigest(path: string): string {
|
|
159
216
|
const temp = mkdtempSync(join(tmpdir(), "projen-npm-content-"));
|
|
@@ -191,25 +248,60 @@ export function packNpmPackage(
|
|
|
191
248
|
directory: string,
|
|
192
249
|
destination: string,
|
|
193
250
|
path = process.env.PATH ?? "",
|
|
251
|
+
transformManifest?: NpmArchiveManifestTransform,
|
|
194
252
|
): string {
|
|
195
253
|
const executable = process.versions.bun ? process.execPath : "bun";
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
254
|
+
const manifest = join(directory, "package.json");
|
|
255
|
+
const manifestMode = lstatSync(manifest).mode & 0o777;
|
|
256
|
+
const restoreManifestMode = (manifestMode & 0o200) === 0;
|
|
257
|
+
if (restoreManifestMode) chmodSync(manifest, manifestMode | 0o200);
|
|
258
|
+
try {
|
|
259
|
+
exec.spawnSync(
|
|
260
|
+
executable,
|
|
261
|
+
["pm", "pack", "--destination", destination, "--ignore-scripts", "--quiet"],
|
|
262
|
+
{
|
|
263
|
+
cwd: directory,
|
|
264
|
+
env: { ...process.env, PATH: path },
|
|
265
|
+
stdout: "inherit",
|
|
266
|
+
stderr: "inherit",
|
|
267
|
+
stdin: "ignore",
|
|
268
|
+
check: true,
|
|
269
|
+
},
|
|
270
|
+
);
|
|
271
|
+
} finally {
|
|
272
|
+
if (restoreManifestMode) chmodSync(manifest, manifestMode);
|
|
273
|
+
}
|
|
208
274
|
const archives = readdirSync(destination).filter((file) => file.endsWith(".tgz"));
|
|
209
275
|
if (archives.length !== 1) {
|
|
210
276
|
throw new Error(`Expected one packed npm archive, found ${archives.length}`);
|
|
211
277
|
}
|
|
212
|
-
|
|
278
|
+
const archive = join(destination, archives[0]);
|
|
279
|
+
if (!transformManifest) return archive;
|
|
280
|
+
|
|
281
|
+
const temp = mkdtempSync(join(tmpdir(), "projen-npm-project-"));
|
|
282
|
+
try {
|
|
283
|
+
exec.spawnSync("tar", ["-xzf", archive, "-C", temp], {
|
|
284
|
+
cwd: process.cwd(),
|
|
285
|
+
stdout: "ignore",
|
|
286
|
+
stderr: "inherit",
|
|
287
|
+
stdin: "ignore",
|
|
288
|
+
check: true,
|
|
289
|
+
});
|
|
290
|
+
const manifestPath = join(temp, "package", "package.json");
|
|
291
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf8")) as NpmArchiveManifest;
|
|
292
|
+
writeFileSync(manifestPath, `${JSON.stringify(transformManifest(manifest), null, 2)}\n`);
|
|
293
|
+
rmSync(archive, { force: true });
|
|
294
|
+
exec.spawnSync("tar", ["-czf", archive, "-C", temp, "package"], {
|
|
295
|
+
cwd: process.cwd(),
|
|
296
|
+
stdout: "ignore",
|
|
297
|
+
stderr: "inherit",
|
|
298
|
+
stdin: "ignore",
|
|
299
|
+
check: true,
|
|
300
|
+
});
|
|
301
|
+
return archive;
|
|
302
|
+
} finally {
|
|
303
|
+
rmSync(temp, { recursive: true, force: true });
|
|
304
|
+
}
|
|
213
305
|
}
|
|
214
306
|
|
|
215
307
|
export async function publishNpmArchives(options: {
|
|
@@ -219,10 +311,12 @@ export async function publishNpmArchives(options: {
|
|
|
219
311
|
readonly version?: string;
|
|
220
312
|
}): Promise<void> {
|
|
221
313
|
const directory = resolve(options.directory);
|
|
222
|
-
const archives =
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
314
|
+
const archives = orderNpmArchives(
|
|
315
|
+
readdirSync(directory)
|
|
316
|
+
.filter((file) => file.endsWith(".tgz"))
|
|
317
|
+
.sort()
|
|
318
|
+
.map((file) => join(directory, file)),
|
|
319
|
+
);
|
|
226
320
|
if (archives.length === 0) throw new Error(`No npm archives found in ${directory}`);
|
|
227
321
|
|
|
228
322
|
for (const archive of archives) {
|
|
@@ -239,23 +333,13 @@ export async function publishNpmArchives(options: {
|
|
|
239
333
|
continue;
|
|
240
334
|
}
|
|
241
335
|
}
|
|
242
|
-
|
|
243
|
-
"
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
...(options.dryRun ? ["--dry-run"] : []),
|
|
250
|
-
],
|
|
251
|
-
{
|
|
252
|
-
cwd: process.cwd(),
|
|
253
|
-
stdout: "inherit",
|
|
254
|
-
stderr: "inherit",
|
|
255
|
-
stdin: "ignore",
|
|
256
|
-
check: true,
|
|
257
|
-
},
|
|
258
|
-
);
|
|
336
|
+
runTaskCommand(process.cwd(), "npm", [
|
|
337
|
+
"publish",
|
|
338
|
+
archive,
|
|
339
|
+
...(local.access ? ["--access", local.access] : []),
|
|
340
|
+
...(options.registry ? ["--registry", options.registry] : []),
|
|
341
|
+
...(options.dryRun ? ["--dry-run"] : []),
|
|
342
|
+
]);
|
|
259
343
|
}
|
|
260
344
|
}
|
|
261
345
|
|
package/tasks/publish-python.ts
CHANGED
|
@@ -1,25 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env -S bun
|
|
2
2
|
import {
|
|
3
3
|
chmodSync,
|
|
4
|
+
cpSync,
|
|
4
5
|
existsSync,
|
|
6
|
+
mkdirSync,
|
|
5
7
|
mkdtempSync,
|
|
6
8
|
readFileSync,
|
|
7
9
|
readdirSync,
|
|
8
10
|
rmSync,
|
|
9
|
-
statSync,
|
|
10
11
|
writeFileSync,
|
|
11
12
|
} from "node:fs";
|
|
12
13
|
import { tmpdir } from "node:os";
|
|
13
|
-
import { basename, join, resolve } from "node:path";
|
|
14
|
-
import * as exec from "@dbx-tools/core/exec";
|
|
14
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
15
15
|
import { Command } from "commander";
|
|
16
16
|
import { parse, stringify } from "smol-toml";
|
|
17
|
-
import {
|
|
18
|
-
import
|
|
17
|
+
import { preparePythonProjectForPublication, pythonProjectInfo } from "./uniffi-python.js";
|
|
18
|
+
import { runTaskCommand } from "../src/_task-command.ts";
|
|
19
19
|
|
|
20
20
|
interface PythonProjectFile {
|
|
21
21
|
readonly directory: string;
|
|
22
|
-
readonly mode: number;
|
|
23
22
|
readonly name: string;
|
|
24
23
|
readonly version: string;
|
|
25
24
|
readonly path: string;
|
|
@@ -28,21 +27,15 @@ interface PythonProjectFile {
|
|
|
28
27
|
readonly uniffi: boolean;
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
/** Python wheel and source archives accepted by Twine and package indexes. */
|
|
31
|
+
export function pythonDistributionPaths(directory: string): string[] {
|
|
32
|
+
return readdirSync(directory)
|
|
33
|
+
.filter((file) => file.endsWith(".whl") || file.endsWith(".tar.gz"))
|
|
34
|
+
.sort()
|
|
35
|
+
.map((file) => join(directory, file));
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
(): void;
|
|
38
|
-
readonly paths: readonly string[];
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function stampPythonProjects(
|
|
42
|
-
root: string,
|
|
43
|
-
version: string,
|
|
44
|
-
options: StampPythonProjectsOptions = {},
|
|
45
|
-
): RestorePythonProjects {
|
|
38
|
+
function pythonProjects(root: string): PythonProjectFile[] {
|
|
46
39
|
const packageFiles = readdirSync(root, { withFileTypes: true })
|
|
47
40
|
.filter((entry) => entry.isDirectory())
|
|
48
41
|
.map((entry) => resolve(root, entry.name, "pyproject.toml"))
|
|
@@ -53,7 +46,6 @@ export function stampPythonProjects(
|
|
|
53
46
|
const info = pythonProjectInfo(source, { parse, stringify });
|
|
54
47
|
return {
|
|
55
48
|
directory: basename(resolve(path, "..")),
|
|
56
|
-
mode: statSync(path).mode,
|
|
57
49
|
name: info.name,
|
|
58
50
|
version: info.version,
|
|
59
51
|
path,
|
|
@@ -62,44 +54,7 @@ export function stampPythonProjects(
|
|
|
62
54
|
uniffi: info.uniffi,
|
|
63
55
|
};
|
|
64
56
|
});
|
|
65
|
-
|
|
66
|
-
(project) => !project.private && (!options.versions || options.versions.has(project.directory)),
|
|
67
|
-
);
|
|
68
|
-
if (projects.length === 0) throw new Error(`No Python packages found under ${root}`);
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
for (const project of projects) {
|
|
72
|
-
const stamped = stampPythonProject(project.source, {
|
|
73
|
-
packages: allProjects,
|
|
74
|
-
rewriteDependencies: options.rewriteDependencies,
|
|
75
|
-
usePackageVersions: options.versions !== undefined,
|
|
76
|
-
toml: { parse, stringify },
|
|
77
|
-
version: options.versions?.get(project.directory) ?? version,
|
|
78
|
-
});
|
|
79
|
-
chmodSync(project.path, project.mode | 0o200);
|
|
80
|
-
writeFileSync(project.path, stamped);
|
|
81
|
-
chmodSync(project.path, project.mode);
|
|
82
|
-
}
|
|
83
|
-
} catch (error) {
|
|
84
|
-
for (const project of projects) {
|
|
85
|
-
chmodSync(project.path, project.mode | 0o200);
|
|
86
|
-
writeFileSync(project.path, project.source);
|
|
87
|
-
chmodSync(project.path, project.mode);
|
|
88
|
-
}
|
|
89
|
-
throw error;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const restore = () => {
|
|
93
|
-
for (const project of projects) {
|
|
94
|
-
chmodSync(project.path, project.mode | 0o200);
|
|
95
|
-
writeFileSync(project.path, project.source);
|
|
96
|
-
chmodSync(project.path, project.mode);
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
Object.defineProperty(restore, "paths", {
|
|
100
|
-
value: projects.map((project) => project.path),
|
|
101
|
-
});
|
|
102
|
-
return restore as RestorePythonProjects;
|
|
57
|
+
return allProjects;
|
|
103
58
|
}
|
|
104
59
|
|
|
105
60
|
export function publishPythonProjects(options: {
|
|
@@ -108,38 +63,12 @@ export function publishPythonProjects(options: {
|
|
|
108
63
|
readonly publishUrl: string;
|
|
109
64
|
readonly root: string;
|
|
110
65
|
readonly version: string;
|
|
111
|
-
readonly plan?: ReleasePlan;
|
|
112
66
|
}): void {
|
|
113
|
-
const root = resolve(options.root);
|
|
114
67
|
const output = mkdtempSync(join(tmpdir(), "projen-python-publish-"));
|
|
115
|
-
const planned = new Map(
|
|
116
|
-
options.plan?.pythonPackages.map((pkg) => [pkg.path.replace(/^.*\//, ""), pkg.version]) ?? [],
|
|
117
|
-
);
|
|
118
|
-
const stamp = stampPythonProjects(root, options.version, {
|
|
119
|
-
...(options.plan ? { versions: planned } : {}),
|
|
120
|
-
});
|
|
121
68
|
try {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
exec.spawnSync("uv", ["build", "--package", pkg.identity, "--out-dir", output], {
|
|
126
|
-
cwd: process.cwd(),
|
|
127
|
-
stdout: "inherit",
|
|
128
|
-
stderr: "inherit",
|
|
129
|
-
stdin: "ignore",
|
|
130
|
-
check: true,
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
} else {
|
|
134
|
-
exec.spawnSync("uv", ["build", "--all-packages", "--out-dir", output], {
|
|
135
|
-
cwd: process.cwd(),
|
|
136
|
-
stdout: "inherit",
|
|
137
|
-
stderr: "inherit",
|
|
138
|
-
stdin: "ignore",
|
|
139
|
-
check: true,
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
exec.spawnSync(
|
|
69
|
+
buildPythonProjects({ ...options, output });
|
|
70
|
+
runTaskCommand(
|
|
71
|
+
process.cwd(),
|
|
143
72
|
"uvx",
|
|
144
73
|
[
|
|
145
74
|
"--from",
|
|
@@ -152,47 +81,91 @@ export function publishPythonProjects(options: {
|
|
|
152
81
|
...(options.dryRun ? ["--dry-run"] : []),
|
|
153
82
|
output,
|
|
154
83
|
],
|
|
155
|
-
{
|
|
156
|
-
cwd: process.cwd(),
|
|
157
|
-
env: { ...process.env, UV_DEFAULT_INDEX: options.indexUrl },
|
|
158
|
-
stdout: "inherit",
|
|
159
|
-
stderr: "inherit",
|
|
160
|
-
stdin: "ignore",
|
|
161
|
-
check: true,
|
|
162
|
-
},
|
|
84
|
+
{ env: { ...process.env, UV_DEFAULT_INDEX: options.indexUrl } },
|
|
163
85
|
);
|
|
164
86
|
} finally {
|
|
165
|
-
stamp();
|
|
166
87
|
rmSync(output, { recursive: true, force: true });
|
|
167
88
|
}
|
|
168
89
|
}
|
|
169
90
|
|
|
91
|
+
/** Build the exact Python release distributions without publishing them. */
|
|
92
|
+
export function buildPythonProjects(options: {
|
|
93
|
+
readonly allowEmpty?: boolean;
|
|
94
|
+
readonly excludeUniFFI?: boolean;
|
|
95
|
+
readonly output: string;
|
|
96
|
+
readonly root: string;
|
|
97
|
+
readonly version: string;
|
|
98
|
+
}): void {
|
|
99
|
+
const root = resolve(options.root);
|
|
100
|
+
const output = resolve(options.output);
|
|
101
|
+
rmSync(output, { recursive: true, force: true });
|
|
102
|
+
const allProjects = pythonProjects(root);
|
|
103
|
+
const projects = allProjects.filter(
|
|
104
|
+
(project) => !project.private && (!options.excludeUniFFI || !project.uniffi),
|
|
105
|
+
);
|
|
106
|
+
if (projects.length === 0 && options.allowEmpty) {
|
|
107
|
+
mkdirSync(output, { recursive: true });
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (projects.length === 0) throw new Error(`No Python packages found under ${root}`);
|
|
111
|
+
|
|
112
|
+
const temporaryRoot = mkdtempSync(join(tmpdir(), "projen-python-release-"));
|
|
113
|
+
try {
|
|
114
|
+
mkdirSync(output, { recursive: true });
|
|
115
|
+
for (const project of projects) {
|
|
116
|
+
const packageRoot = join(temporaryRoot, project.directory);
|
|
117
|
+
cpSync(dirname(project.path), packageRoot, { recursive: true });
|
|
118
|
+
const manifestPath = join(packageRoot, "pyproject.toml");
|
|
119
|
+
chmodSync(manifestPath, 0o644);
|
|
120
|
+
writeFileSync(
|
|
121
|
+
manifestPath,
|
|
122
|
+
preparePythonProjectForPublication(project.source, {
|
|
123
|
+
packages: allProjects,
|
|
124
|
+
toml: { parse, stringify },
|
|
125
|
+
version: options.version,
|
|
126
|
+
}),
|
|
127
|
+
);
|
|
128
|
+
runTaskCommand(root, "uv", ["build", "--out-dir", output, packageRoot]);
|
|
129
|
+
}
|
|
130
|
+
const distributions = pythonDistributionPaths(output);
|
|
131
|
+
if (distributions.length === 0) {
|
|
132
|
+
throw new Error(`No Python distributions found in ${output}`);
|
|
133
|
+
}
|
|
134
|
+
runTaskCommand(process.cwd(), "uvx", ["twine", "check", ...distributions]);
|
|
135
|
+
} finally {
|
|
136
|
+
rmSync(temporaryRoot, { recursive: true, force: true });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
170
140
|
if (import.meta.main) {
|
|
171
141
|
const program = new Command();
|
|
172
142
|
program
|
|
173
|
-
.argument("
|
|
143
|
+
.argument("<version>", "Python package version")
|
|
174
144
|
.requiredOption("--index-url <url>", "devpi Simple API URL")
|
|
175
145
|
.requiredOption("--publish-url <url>", "devpi writable index URL")
|
|
176
146
|
.option("--root <path>", "Python workspace package root", "packages/py")
|
|
177
|
-
.option("--
|
|
147
|
+
.option("--output <path>", "Build distributions into a directory without publishing")
|
|
178
148
|
.option("--dry-run", "build and inspect distributions without uploading")
|
|
179
149
|
.action(
|
|
180
150
|
(
|
|
181
|
-
version: string
|
|
151
|
+
version: string,
|
|
182
152
|
options: {
|
|
183
153
|
dryRun?: boolean;
|
|
184
154
|
indexUrl: string;
|
|
185
155
|
publishUrl: string;
|
|
186
156
|
root: string;
|
|
187
|
-
|
|
157
|
+
output?: string;
|
|
188
158
|
},
|
|
189
159
|
) => {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
160
|
+
if (options.output) {
|
|
161
|
+
buildPythonProjects({
|
|
162
|
+
output: options.output,
|
|
163
|
+
root: options.root,
|
|
164
|
+
version,
|
|
165
|
+
});
|
|
166
|
+
} else {
|
|
167
|
+
publishPythonProjects({ ...options, version });
|
|
168
|
+
}
|
|
196
169
|
},
|
|
197
170
|
);
|
|
198
171
|
await program.parseAsync();
|