@dbx-tools/projen 0.9.17 → 0.9.19
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/README.md +39 -36
- package/index.ts +2 -2
- package/package.json +4 -5
- package/src/_release-platform.ts +10 -0
- package/src/_rust-project.ts +3 -0
- package/src/_rust-release-workflow.ts +117 -528
- package/src/project-js.ts +8 -13
- package/src/project-py.ts +62 -54
- package/src/project-rs.ts +7 -37
- package/src/release-github.ts +23 -0
- package/src/release-plan.ts +32 -40
- package/src/release.ts +91 -135
- package/tasks/compile-workspace.ts +121 -0
- package/tasks/publish-npm.ts +63 -11
- package/tasks/publish-python.ts +101 -26
- package/tasks/publish.ts +22 -3
- package/tasks/release-assets.ts +476 -0
- package/tasks/release-candidate.ts +383 -0
- package/tasks/release-manifest.ts +273 -0
- package/tasks/release-plan.ts +1 -2
- package/tasks/release-pr.ts +86 -60
- package/tasks/uniffi-python.js +24 -12
- package/tasks/uniffi-release.mjs +35 -2
- package/src/_rust-release.ts +0 -633
- package/tasks/build-rust-release.ts +0 -68
- package/tasks/rust-release.d.ts +0 -45
- package/tasks/rust-release.mjs +0 -1726
package/tasks/publish-python.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
chmodSync,
|
|
4
4
|
existsSync,
|
|
5
|
+
mkdirSync,
|
|
5
6
|
mkdtempSync,
|
|
6
7
|
readFileSync,
|
|
7
8
|
readdirSync,
|
|
@@ -29,10 +30,19 @@ interface PythonProjectFile {
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
export interface StampPythonProjectsOptions {
|
|
33
|
+
readonly excludeUniFFI?: boolean;
|
|
32
34
|
readonly rewriteDependencies?: boolean;
|
|
33
35
|
readonly versions?: ReadonlyMap<string, string>;
|
|
34
36
|
}
|
|
35
37
|
|
|
38
|
+
/** Python wheel and source archives accepted by Twine and package indexes. */
|
|
39
|
+
export function pythonDistributionPaths(directory: string): string[] {
|
|
40
|
+
return readdirSync(directory)
|
|
41
|
+
.filter((file) => file.endsWith(".whl") || file.endsWith(".tar.gz"))
|
|
42
|
+
.sort()
|
|
43
|
+
.map((file) => join(directory, file));
|
|
44
|
+
}
|
|
45
|
+
|
|
36
46
|
export interface RestorePythonProjects {
|
|
37
47
|
(): void;
|
|
38
48
|
readonly paths: readonly string[];
|
|
@@ -63,7 +73,10 @@ export function stampPythonProjects(
|
|
|
63
73
|
};
|
|
64
74
|
});
|
|
65
75
|
const projects = allProjects.filter(
|
|
66
|
-
(project) =>
|
|
76
|
+
(project) =>
|
|
77
|
+
!project.private &&
|
|
78
|
+
(!options.excludeUniFFI || !project.uniffi) &&
|
|
79
|
+
(!options.versions || options.versions.has(project.directory)),
|
|
67
80
|
);
|
|
68
81
|
if (projects.length === 0) throw new Error(`No Python packages found under ${root}`);
|
|
69
82
|
|
|
@@ -110,12 +123,65 @@ export function publishPythonProjects(options: {
|
|
|
110
123
|
readonly version: string;
|
|
111
124
|
readonly plan?: ReleasePlan;
|
|
112
125
|
}): void {
|
|
113
|
-
const root = resolve(options.root);
|
|
114
126
|
const output = mkdtempSync(join(tmpdir(), "projen-python-publish-"));
|
|
127
|
+
try {
|
|
128
|
+
buildPythonProjects({ ...options, output });
|
|
129
|
+
exec.spawnSync(
|
|
130
|
+
"uvx",
|
|
131
|
+
[
|
|
132
|
+
"--from",
|
|
133
|
+
"devpi-client",
|
|
134
|
+
"devpi",
|
|
135
|
+
"upload",
|
|
136
|
+
"--index",
|
|
137
|
+
options.publishUrl,
|
|
138
|
+
"--from-dir",
|
|
139
|
+
...(options.dryRun ? ["--dry-run"] : []),
|
|
140
|
+
output,
|
|
141
|
+
],
|
|
142
|
+
{
|
|
143
|
+
cwd: process.cwd(),
|
|
144
|
+
env: { ...process.env, UV_DEFAULT_INDEX: options.indexUrl },
|
|
145
|
+
stdout: "inherit",
|
|
146
|
+
stderr: "inherit",
|
|
147
|
+
stdin: "ignore",
|
|
148
|
+
check: true,
|
|
149
|
+
},
|
|
150
|
+
);
|
|
151
|
+
} finally {
|
|
152
|
+
rmSync(output, { recursive: true, force: true });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Build the exact Python release distributions without publishing them. */
|
|
157
|
+
export function buildPythonProjects(options: {
|
|
158
|
+
readonly allowEmpty?: boolean;
|
|
159
|
+
readonly excludeUniFFI?: boolean;
|
|
160
|
+
readonly output: string;
|
|
161
|
+
readonly root: string;
|
|
162
|
+
readonly version: string;
|
|
163
|
+
readonly plan?: ReleasePlan;
|
|
164
|
+
}): void {
|
|
165
|
+
const root = resolve(options.root);
|
|
166
|
+
const output = resolve(options.output);
|
|
167
|
+
rmSync(output, { recursive: true, force: true });
|
|
168
|
+
const standardProjects = options.excludeUniFFI
|
|
169
|
+
? readdirSync(root, { withFileTypes: true })
|
|
170
|
+
.filter((entry) => entry.isDirectory())
|
|
171
|
+
.map((entry) => resolve(root, entry.name, "pyproject.toml"))
|
|
172
|
+
.filter(existsSync)
|
|
173
|
+
.map((path) => pythonProjectInfo(readFileSync(path, "utf8"), { parse, stringify }))
|
|
174
|
+
.filter((project) => !project.private && !project.uniffi)
|
|
175
|
+
: undefined;
|
|
176
|
+
if (standardProjects?.length === 0 && options.allowEmpty) {
|
|
177
|
+
mkdirSync(output, { recursive: true });
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
115
180
|
const planned = new Map(
|
|
116
181
|
options.plan?.pythonPackages.map((pkg) => [pkg.path.replace(/^.*\//, ""), pkg.version]) ?? [],
|
|
117
182
|
);
|
|
118
183
|
const stamp = stampPythonProjects(root, options.version, {
|
|
184
|
+
excludeUniFFI: options.excludeUniFFI,
|
|
119
185
|
...(options.plan ? { versions: planned } : {}),
|
|
120
186
|
});
|
|
121
187
|
try {
|
|
@@ -130,6 +196,16 @@ export function publishPythonProjects(options: {
|
|
|
130
196
|
check: true,
|
|
131
197
|
});
|
|
132
198
|
}
|
|
199
|
+
} else if (options.excludeUniFFI) {
|
|
200
|
+
for (const project of standardProjects ?? []) {
|
|
201
|
+
exec.spawnSync("uv", ["build", "--package", project.name, "--out-dir", output], {
|
|
202
|
+
cwd: process.cwd(),
|
|
203
|
+
stdout: "inherit",
|
|
204
|
+
stderr: "inherit",
|
|
205
|
+
stdin: "ignore",
|
|
206
|
+
check: true,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
133
209
|
} else {
|
|
134
210
|
exec.spawnSync("uv", ["build", "--all-packages", "--out-dir", output], {
|
|
135
211
|
cwd: process.cwd(),
|
|
@@ -139,31 +215,19 @@ export function publishPythonProjects(options: {
|
|
|
139
215
|
check: true,
|
|
140
216
|
});
|
|
141
217
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
output,
|
|
154
|
-
],
|
|
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
|
-
},
|
|
163
|
-
);
|
|
218
|
+
const distributions = pythonDistributionPaths(output);
|
|
219
|
+
if (distributions.length === 0) {
|
|
220
|
+
throw new Error(`No Python distributions found in ${output}`);
|
|
221
|
+
}
|
|
222
|
+
exec.spawnSync("uvx", ["twine", "check", ...distributions], {
|
|
223
|
+
cwd: process.cwd(),
|
|
224
|
+
stdout: "inherit",
|
|
225
|
+
stderr: "inherit",
|
|
226
|
+
stdin: "ignore",
|
|
227
|
+
check: true,
|
|
228
|
+
});
|
|
164
229
|
} finally {
|
|
165
230
|
stamp();
|
|
166
|
-
rmSync(output, { recursive: true, force: true });
|
|
167
231
|
}
|
|
168
232
|
}
|
|
169
233
|
|
|
@@ -175,6 +239,7 @@ if (import.meta.main) {
|
|
|
175
239
|
.requiredOption("--publish-url <url>", "devpi writable index URL")
|
|
176
240
|
.option("--root <path>", "Python workspace package root", "packages/py")
|
|
177
241
|
.option("--plan <path>", "affected release plan")
|
|
242
|
+
.option("--output <path>", "Build distributions into a directory without publishing")
|
|
178
243
|
.option("--dry-run", "build and inspect distributions without uploading")
|
|
179
244
|
.action(
|
|
180
245
|
(
|
|
@@ -185,6 +250,7 @@ if (import.meta.main) {
|
|
|
185
250
|
publishUrl: string;
|
|
186
251
|
root: string;
|
|
187
252
|
plan?: string;
|
|
253
|
+
output?: string;
|
|
188
254
|
},
|
|
189
255
|
) => {
|
|
190
256
|
const plan = options.plan
|
|
@@ -192,7 +258,16 @@ if (import.meta.main) {
|
|
|
192
258
|
: undefined;
|
|
193
259
|
const fallbackVersion = version ?? plan?.pythonPackages[0]?.version;
|
|
194
260
|
if (!fallbackVersion) throw new Error("Python publication requires a version or plan");
|
|
195
|
-
|
|
261
|
+
if (options.output) {
|
|
262
|
+
buildPythonProjects({
|
|
263
|
+
output: options.output,
|
|
264
|
+
root: options.root,
|
|
265
|
+
version: fallbackVersion,
|
|
266
|
+
plan,
|
|
267
|
+
});
|
|
268
|
+
} else {
|
|
269
|
+
publishPythonProjects({ ...options, version: fallbackVersion, plan });
|
|
270
|
+
}
|
|
196
271
|
},
|
|
197
272
|
);
|
|
198
273
|
await program.parseAsync();
|
package/tasks/publish.ts
CHANGED
|
@@ -55,7 +55,9 @@
|
|
|
55
55
|
*/
|
|
56
56
|
import {
|
|
57
57
|
chmodSync,
|
|
58
|
+
copyFileSync,
|
|
58
59
|
existsSync,
|
|
60
|
+
mkdirSync,
|
|
59
61
|
mkdtempSync,
|
|
60
62
|
readFileSync,
|
|
61
63
|
rmSync,
|
|
@@ -234,12 +236,14 @@ const plan =
|
|
|
234
236
|
: undefined;
|
|
235
237
|
if (!version && !plan) {
|
|
236
238
|
logger.error(
|
|
237
|
-
"usage: bun tasks/publish.ts <version> [--plan <path>] [--registry <url>] [--exclude <dir>] [--dry-run] [--skip-compile]",
|
|
239
|
+
"usage: bun tasks/publish.ts <version> [--plan <path>] [--registry <url>] [--output <dir>] [--exclude <dir>] [--dry-run] [--skip-compile]",
|
|
238
240
|
);
|
|
239
241
|
process.exit(1);
|
|
240
242
|
}
|
|
241
243
|
const registryIdx = rest.indexOf("--registry");
|
|
242
244
|
const registry = registryIdx >= 0 ? rest[registryIdx + 1] : undefined;
|
|
245
|
+
const outputIdx = rest.indexOf("--output");
|
|
246
|
+
const output = outputIdx >= 0 ? resolve(root, rest[outputIdx + 1]) : undefined;
|
|
243
247
|
const dryRun = rest.includes("--dry-run");
|
|
244
248
|
const skipCompile = rest.includes("--skip-compile");
|
|
245
249
|
const concurrencyIdx = rest.indexOf("--concurrency");
|
|
@@ -371,8 +375,12 @@ for (const { dir } of publishable) {
|
|
|
371
375
|
}
|
|
372
376
|
|
|
373
377
|
logger.info(
|
|
374
|
-
`${dryRun ? "dry-run packing" : "publishing"} ${publishable.length} packages with concurrency ${concurrency}`,
|
|
378
|
+
`${output ? "packing" : dryRun ? "dry-run packing" : "publishing"} ${publishable.length} packages with concurrency ${concurrency}`,
|
|
375
379
|
);
|
|
380
|
+
if (output) {
|
|
381
|
+
rmSync(output, { recursive: true, force: true });
|
|
382
|
+
mkdirSync(output, { recursive: true });
|
|
383
|
+
}
|
|
376
384
|
await asyncUtils.mapConcurrent(
|
|
377
385
|
publishable,
|
|
378
386
|
async ({ dir, name, version: packageVersion, access }) => {
|
|
@@ -390,6 +398,15 @@ await asyncUtils.mapConcurrent(
|
|
|
390
398
|
`Packed npm access ${String(local.access)} does not match ${String(access)} for ${name}`,
|
|
391
399
|
);
|
|
392
400
|
}
|
|
401
|
+
if (output) {
|
|
402
|
+
const destination = join(output, archive.split(/[\\/]/).at(-1)!);
|
|
403
|
+
if (existsSync(destination)) {
|
|
404
|
+
throw new Error(`Duplicate npm archive name: ${destination}`);
|
|
405
|
+
}
|
|
406
|
+
copyFileSync(archive, destination);
|
|
407
|
+
logger.info(`packed ${name} @ ${packageVersion}`);
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
393
410
|
if (!dryRun) {
|
|
394
411
|
const published = await publishedNpmRelease(local.name, local.version, registry);
|
|
395
412
|
if (npmReleaseMatches(local, published)) {
|
|
@@ -410,4 +427,6 @@ await asyncUtils.mapConcurrent(
|
|
|
410
427
|
},
|
|
411
428
|
{ concurrency, errorMode: "settle" },
|
|
412
429
|
);
|
|
413
|
-
logger.success(
|
|
430
|
+
logger.success(
|
|
431
|
+
`${output || dryRun ? "packed" : "published"} ${publishable.length} packages${output ? ` to ${output}` : ""}`,
|
|
432
|
+
);
|