@dbx-tools/projen 0.9.21 → 0.9.29
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 +77 -207
- package/index.ts +10 -14
- package/package.json +4 -4
- package/src/barrels.ts +1 -74
- package/src/module-exports.ts +175 -1
- package/src/openapi.ts +5 -72
- package/src/packages.ts +45 -0
- package/src/project-js.ts +3 -6
- package/src/project-predicate.ts +1 -2
- package/src/project-py.ts +82 -103
- package/src/project.ts +2 -2
- package/src/python-node-bindings.ts +138 -0
- package/src/python-node-bundle.ts +92 -0
- package/src/release-catalog.ts +3 -5
- package/src/{release-dispatch.ts → release-context.ts} +0 -11
- package/src/release.ts +70 -152
- package/tasks/bump.ts +12 -21
- package/tasks/databricks-cli-assets.ts +129 -0
- package/tasks/local-publish.ts +23 -107
- package/tasks/publish-npm.ts +10 -1
- package/tasks/publish-python.ts +26 -11
- package/tasks/publish.ts +12 -48
- package/tasks/python-node-bindings-watch.ts +47 -0
- package/tasks/python-node-bindings.ts +1017 -0
- package/tasks/python-release.ts +74 -0
- package/tasks/release.ts +99 -0
- package/tasks/sync.ts +13 -18
- package/tasks/version-check.ts +1 -28
- package/._LICENSE +0 -0
- package/._README.md +0 -0
- package/._index.ts +0 -0
- package/._package.json +0 -0
- 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/_release-platform.ts +0 -78
- package/src/_rust-project.ts +0 -429
- package/src/_rust-release-workflow.ts +0 -311
- package/src/project-rs.ts +0 -1054
- package/src/release-github.ts +0 -109
- package/src/uniffi.ts +0 -65
- 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/publish-uniffi-local.ts +0 -359
- package/tasks/release-assets.ts +0 -467
- package/tasks/release-candidate.ts +0 -416
- package/tasks/release-manifest.ts +0 -273
- package/tasks/release-pr.ts +0 -589
- package/tasks/release-summary.ts +0 -379
- package/tasks/rust.ts +0 -173
- package/tasks/uniffi-python.d.ts +0 -60
- package/tasks/uniffi-python.js +0 -178
- package/tasks/uniffi-release.mjs +0 -579
- package/tasks/uniffi.ts +0 -262
package/tasks/local-publish.ts
CHANGED
|
@@ -1,27 +1,20 @@
|
|
|
1
|
-
/**
|
|
2
|
-
import {
|
|
3
|
-
import { homedir, tmpdir } from "node:os";
|
|
4
|
-
import { join, resolve } from "node:path";
|
|
1
|
+
/** Build and publish the current workspace to configured loopback registries. */
|
|
2
|
+
import { resolve } from "node:path";
|
|
5
3
|
import { fileURLToPath } from "node:url";
|
|
6
4
|
import * as projectUtils from "@dbx-tools/core/project-utils";
|
|
7
5
|
import { log, net } from "@dbx-tools/shared-core";
|
|
8
6
|
import { Command } from "commander";
|
|
9
7
|
import { activePythonIndexes, resolveLocalPypi } from "./python-registry.ts";
|
|
10
|
-
import { verifyReleaseManifest } from "./release-manifest.ts";
|
|
11
8
|
import { runTaskCommandAsync } from "../src/_task-command.ts";
|
|
12
|
-
import { readDbxToolsConfig } from "../src/packages.ts";
|
|
13
9
|
|
|
14
10
|
const logger = log.logger("projen:local-publish");
|
|
15
11
|
|
|
16
|
-
/** Options for publishing
|
|
12
|
+
/** Options for publishing the current workspace to local registries. */
|
|
17
13
|
export interface LocalPublishOptions {
|
|
18
|
-
readonly candidateDirectory: string;
|
|
19
|
-
readonly gitSha: string;
|
|
20
|
-
readonly localCargo: boolean;
|
|
21
14
|
readonly localPypi: string;
|
|
22
15
|
readonly localRegistry: string;
|
|
16
|
+
readonly pythonRoot: string;
|
|
23
17
|
readonly root: string;
|
|
24
|
-
readonly tag: string;
|
|
25
18
|
readonly version: string;
|
|
26
19
|
}
|
|
27
20
|
|
|
@@ -35,43 +28,9 @@ export function resolveLocalRegistry(value: string): string | undefined {
|
|
|
35
28
|
return trimmed;
|
|
36
29
|
}
|
|
37
30
|
|
|
38
|
-
|
|
39
|
-
if (process.env.LOCAL_CARGO_REGISTRY) return process.env.LOCAL_CARGO_REGISTRY;
|
|
40
|
-
const config = join(homedir(), ".cargo", "config.toml");
|
|
41
|
-
if (!existsSync(config)) return undefined;
|
|
42
|
-
const source = readFileSync(config, "utf8");
|
|
43
|
-
const sections = [
|
|
44
|
-
...source.matchAll(/^\[registries\.([^\]]+)\]\s*\n([\s\S]*?)(?=^\[|(?![\s\S]))/gm),
|
|
45
|
-
];
|
|
46
|
-
for (const section of sections) {
|
|
47
|
-
const index = section[2]?.match(/^\s*index\s*=\s*["']([^"']+)["']/m)?.[1];
|
|
48
|
-
if (index && net.isLoopbackHost(index.replace(/^sparse\+/, ""))) return section[1];
|
|
49
|
-
}
|
|
50
|
-
return undefined;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function hasRustCrates(root: string): boolean {
|
|
54
|
-
const rust = readDbxToolsConfig(root)?.rust;
|
|
55
|
-
return Boolean(
|
|
56
|
-
rust &&
|
|
57
|
-
typeof rust === "object" &&
|
|
58
|
-
!Array.isArray(rust) &&
|
|
59
|
-
Array.isArray((rust as { crates?: unknown }).crates) &&
|
|
60
|
-
(rust as { crates: unknown[] }).crates.length > 0,
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/** Publish the exact candidate archives before they are attached to the draft release. */
|
|
31
|
+
/** Build and publish the current workspace directly to local registries. */
|
|
65
32
|
export async function publishLocalRelease(options: LocalPublishOptions): Promise<void> {
|
|
66
33
|
const root = resolve(options.root);
|
|
67
|
-
const candidateDirectory = resolve(options.candidateDirectory);
|
|
68
|
-
verifyReleaseManifest({
|
|
69
|
-
directory: candidateDirectory,
|
|
70
|
-
gitSha: options.gitSha,
|
|
71
|
-
tag: options.tag,
|
|
72
|
-
version: options.version,
|
|
73
|
-
});
|
|
74
|
-
|
|
75
34
|
const localRegistry = resolveLocalRegistry(options.localRegistry);
|
|
76
35
|
const activeIndexes = activePythonIndexes();
|
|
77
36
|
const localPypi = resolveLocalPypi(options.localPypi, activeIndexes);
|
|
@@ -88,14 +47,11 @@ export async function publishLocalRelease(options: LocalPublishOptions): Promise
|
|
|
88
47
|
}
|
|
89
48
|
|
|
90
49
|
if (localRegistry) {
|
|
91
|
-
const publishNpmScript = fileURLToPath(new URL("./publish
|
|
92
|
-
logger.info(`publishing
|
|
50
|
+
const publishNpmScript = fileURLToPath(new URL("./publish.ts", import.meta.url));
|
|
51
|
+
logger.info(`building and publishing npm workspace to ${localRegistry}`);
|
|
93
52
|
publishes.push(
|
|
94
53
|
runTaskCommandAsync(root, process.execPath, [
|
|
95
54
|
publishNpmScript,
|
|
96
|
-
"--directory",
|
|
97
|
-
candidateDirectory,
|
|
98
|
-
"--version",
|
|
99
55
|
options.version,
|
|
100
56
|
"--registry",
|
|
101
57
|
localRegistry,
|
|
@@ -104,86 +60,46 @@ export async function publishLocalRelease(options: LocalPublishOptions): Promise
|
|
|
104
60
|
}
|
|
105
61
|
|
|
106
62
|
if (localPypi) {
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
directory: candidateDirectory,
|
|
110
|
-
gitSha: options.gitSha,
|
|
111
|
-
kind: "pypi",
|
|
112
|
-
output: pythonDirectory,
|
|
113
|
-
tag: options.tag,
|
|
114
|
-
version: options.version,
|
|
115
|
-
});
|
|
116
|
-
logger.info(`publishing approved Python distributions to ${localPypi.publishUrl}`);
|
|
63
|
+
const publishPythonScript = fileURLToPath(new URL("./publish-python.ts", import.meta.url));
|
|
64
|
+
logger.info(`building and publishing Python workspace to ${localPypi.publishUrl}`);
|
|
117
65
|
publishes.push(
|
|
118
|
-
runTaskCommandAsync(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
"--from-dir",
|
|
129
|
-
pythonDirectory,
|
|
130
|
-
],
|
|
131
|
-
{ env: { ...process.env, UV_DEFAULT_INDEX: localPypi.indexUrl } },
|
|
132
|
-
).finally(() => rmSync(pythonDirectory, { recursive: true, force: true })),
|
|
66
|
+
runTaskCommandAsync(root, process.execPath, [
|
|
67
|
+
publishPythonScript,
|
|
68
|
+
options.version,
|
|
69
|
+
"--root",
|
|
70
|
+
options.pythonRoot,
|
|
71
|
+
"--index-url",
|
|
72
|
+
localPypi.indexUrl,
|
|
73
|
+
"--publish-url",
|
|
74
|
+
localPypi.publishUrl,
|
|
75
|
+
]),
|
|
133
76
|
);
|
|
134
77
|
}
|
|
135
78
|
|
|
136
79
|
await Promise.all(publishes);
|
|
137
|
-
|
|
138
|
-
if (options.localCargo && hasRustCrates(root)) {
|
|
139
|
-
const registry = localCargoRegistry();
|
|
140
|
-
if (registry) {
|
|
141
|
-
const publishCargoScript = fileURLToPath(
|
|
142
|
-
new URL("./publish-uniffi-local.ts", import.meta.url),
|
|
143
|
-
);
|
|
144
|
-
await runTaskCommandAsync(root, process.execPath, [
|
|
145
|
-
publishCargoScript,
|
|
146
|
-
"--version",
|
|
147
|
-
options.version,
|
|
148
|
-
"--cargo-registry",
|
|
149
|
-
registry,
|
|
150
|
-
]);
|
|
151
|
-
logger.success(`published Rust ${options.version} to ${registry}`);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
logger.success(`published approved local candidate ${options.tag}`);
|
|
80
|
+
logger.success(`published local workspace ${options.version}`);
|
|
156
81
|
}
|
|
157
82
|
|
|
158
83
|
if (import.meta.main) {
|
|
159
84
|
await new Command()
|
|
160
|
-
.requiredOption("--candidate-directory <path>", "verified release candidate directory")
|
|
161
|
-
.requiredOption("--sha <sha>", "exact release commit")
|
|
162
|
-
.requiredOption("--tag <tag>", "annotated release tag")
|
|
163
85
|
.requiredOption("--version <version>", "release version")
|
|
164
86
|
.option("--root <path>", "repository root")
|
|
87
|
+
.option("--python-root <path>", "Python package root", "packages/py")
|
|
165
88
|
.option("--local-registry <url>", "local npm registry", "auto")
|
|
166
89
|
.option("--local-pypi <url>", "local devpi index", "auto")
|
|
167
|
-
.option("--local-cargo", "publish Cargo crates to a configured local registry")
|
|
168
90
|
.action(
|
|
169
91
|
(options: {
|
|
170
|
-
candidateDirectory: string;
|
|
171
|
-
sha: string;
|
|
172
|
-
tag: string;
|
|
173
92
|
version: string;
|
|
174
93
|
root?: string;
|
|
94
|
+
pythonRoot: string;
|
|
175
95
|
localRegistry: string;
|
|
176
96
|
localPypi: string;
|
|
177
|
-
localCargo?: boolean;
|
|
178
97
|
}) =>
|
|
179
98
|
publishLocalRelease({
|
|
180
|
-
candidateDirectory: options.candidateDirectory,
|
|
181
|
-
gitSha: options.sha,
|
|
182
|
-
localCargo: Boolean(options.localCargo),
|
|
183
99
|
localPypi: options.localPypi,
|
|
184
100
|
localRegistry: options.localRegistry,
|
|
101
|
+
pythonRoot: options.pythonRoot,
|
|
185
102
|
root: options.root ?? projectUtils.root() ?? process.cwd(),
|
|
186
|
-
tag: options.tag,
|
|
187
103
|
version: options.version,
|
|
188
104
|
}),
|
|
189
105
|
)
|
package/tasks/publish-npm.ts
CHANGED
|
@@ -44,6 +44,15 @@ export type NpmArchiveManifestTransform = (
|
|
|
44
44
|
manifest: NpmArchiveManifest,
|
|
45
45
|
) => Readonly<Record<string, unknown>>;
|
|
46
46
|
|
|
47
|
+
function archiveFileEntries(directory: string, prefix = ""): string[] {
|
|
48
|
+
return readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
49
|
+
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
50
|
+
return entry.isDirectory()
|
|
51
|
+
? archiveFileEntries(join(directory, entry.name), relativePath)
|
|
52
|
+
: [relativePath];
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
47
56
|
function normalizedRepository(value: unknown): string | undefined {
|
|
48
57
|
const url =
|
|
49
58
|
typeof value === "string"
|
|
@@ -291,7 +300,7 @@ export function packNpmPackage(
|
|
|
291
300
|
const manifest = JSON.parse(readFileSync(manifestPath, "utf8")) as NpmArchiveManifest;
|
|
292
301
|
writeFileSync(manifestPath, `${JSON.stringify(transformManifest(manifest), null, 2)}\n`);
|
|
293
302
|
rmSync(archive, { force: true });
|
|
294
|
-
exec.spawnSync("tar", ["-czf", archive, "-C", temp,
|
|
303
|
+
exec.spawnSync("tar", ["-czf", archive, "-C", temp, ...archiveFileEntries(temp)], {
|
|
295
304
|
cwd: process.cwd(),
|
|
296
305
|
stdout: "ignore",
|
|
297
306
|
stderr: "inherit",
|
package/tasks/publish-python.ts
CHANGED
|
@@ -14,7 +14,7 @@ import { tmpdir } from "node:os";
|
|
|
14
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 { preparePythonProjectForPublication, pythonProjectInfo } from "./
|
|
17
|
+
import { preparePythonProjectForPublication, pythonProjectInfo } from "./python-release.ts";
|
|
18
18
|
import { runTaskCommand } from "../src/_task-command.ts";
|
|
19
19
|
|
|
20
20
|
interface PythonProjectFile {
|
|
@@ -24,7 +24,6 @@ interface PythonProjectFile {
|
|
|
24
24
|
readonly path: string;
|
|
25
25
|
readonly private: boolean;
|
|
26
26
|
readonly source: string;
|
|
27
|
-
readonly uniffi: boolean;
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
/** Python wheel and source archives accepted by Twine and package indexes. */
|
|
@@ -43,7 +42,7 @@ function pythonProjects(root: string): PythonProjectFile[] {
|
|
|
43
42
|
.sort();
|
|
44
43
|
const allProjects: PythonProjectFile[] = packageFiles.map((path) => {
|
|
45
44
|
const source = readFileSync(path, "utf8");
|
|
46
|
-
const info = pythonProjectInfo(source, { parse
|
|
45
|
+
const info = pythonProjectInfo(source, { parse });
|
|
47
46
|
return {
|
|
48
47
|
directory: basename(resolve(path, "..")),
|
|
49
48
|
name: info.name,
|
|
@@ -51,7 +50,6 @@ function pythonProjects(root: string): PythonProjectFile[] {
|
|
|
51
50
|
path,
|
|
52
51
|
private: info.private,
|
|
53
52
|
source,
|
|
54
|
-
uniffi: info.uniffi,
|
|
55
53
|
};
|
|
56
54
|
});
|
|
57
55
|
return allProjects;
|
|
@@ -91,8 +89,8 @@ export function publishPythonProjects(options: {
|
|
|
91
89
|
/** Build the exact Python release distributions without publishing them. */
|
|
92
90
|
export function buildPythonProjects(options: {
|
|
93
91
|
readonly allowEmpty?: boolean;
|
|
94
|
-
readonly excludeUniFFI?: boolean;
|
|
95
92
|
readonly output: string;
|
|
93
|
+
readonly packages?: readonly string[];
|
|
96
94
|
readonly root: string;
|
|
97
95
|
readonly version: string;
|
|
98
96
|
}): void {
|
|
@@ -100,8 +98,13 @@ export function buildPythonProjects(options: {
|
|
|
100
98
|
const output = resolve(options.output);
|
|
101
99
|
rmSync(output, { recursive: true, force: true });
|
|
102
100
|
const allProjects = pythonProjects(root);
|
|
101
|
+
const requested = new Set(options.packages ?? []);
|
|
102
|
+
const missing = [...requested].filter(
|
|
103
|
+
(directory) => !allProjects.some((project) => project.directory === directory),
|
|
104
|
+
);
|
|
105
|
+
if (missing.length > 0) throw new Error(`Unknown Python packages: ${missing.join(", ")}`);
|
|
103
106
|
const projects = allProjects.filter(
|
|
104
|
-
(project) => !project.private && (
|
|
107
|
+
(project) => !project.private && (requested.size === 0 || requested.has(project.directory)),
|
|
105
108
|
);
|
|
106
109
|
if (projects.length === 0 && options.allowEmpty) {
|
|
107
110
|
mkdirSync(output, { recursive: true });
|
|
@@ -141,9 +144,10 @@ if (import.meta.main) {
|
|
|
141
144
|
const program = new Command();
|
|
142
145
|
program
|
|
143
146
|
.argument("<version>", "Python package version")
|
|
144
|
-
.
|
|
145
|
-
.
|
|
147
|
+
.option("--index-url <url>", "devpi Simple API URL")
|
|
148
|
+
.option("--publish-url <url>", "devpi writable index URL")
|
|
146
149
|
.option("--root <path>", "Python workspace package root", "packages/py")
|
|
150
|
+
.option("--package <directory...>", "Build only selected package directories")
|
|
147
151
|
.option("--output <path>", "Build distributions into a directory without publishing")
|
|
148
152
|
.option("--dry-run", "build and inspect distributions without uploading")
|
|
149
153
|
.action(
|
|
@@ -151,8 +155,9 @@ if (import.meta.main) {
|
|
|
151
155
|
version: string,
|
|
152
156
|
options: {
|
|
153
157
|
dryRun?: boolean;
|
|
154
|
-
indexUrl
|
|
155
|
-
|
|
158
|
+
indexUrl?: string;
|
|
159
|
+
package?: string[];
|
|
160
|
+
publishUrl?: string;
|
|
156
161
|
root: string;
|
|
157
162
|
output?: string;
|
|
158
163
|
},
|
|
@@ -160,11 +165,21 @@ if (import.meta.main) {
|
|
|
160
165
|
if (options.output) {
|
|
161
166
|
buildPythonProjects({
|
|
162
167
|
output: options.output,
|
|
168
|
+
packages: options.package,
|
|
163
169
|
root: options.root,
|
|
164
170
|
version,
|
|
165
171
|
});
|
|
166
172
|
} else {
|
|
167
|
-
|
|
173
|
+
if (!options.indexUrl || !options.publishUrl) {
|
|
174
|
+
throw new Error("--index-url and --publish-url are required when publishing");
|
|
175
|
+
}
|
|
176
|
+
publishPythonProjects({
|
|
177
|
+
dryRun: options.dryRun,
|
|
178
|
+
indexUrl: options.indexUrl,
|
|
179
|
+
publishUrl: options.publishUrl,
|
|
180
|
+
root: options.root,
|
|
181
|
+
version,
|
|
182
|
+
});
|
|
168
183
|
}
|
|
169
184
|
},
|
|
170
185
|
);
|
package/tasks/publish.ts
CHANGED
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env -S bun
|
|
2
2
|
/**
|
|
3
3
|
* `bun tasks/publish.ts <version> [--registry <url>] [--exclude <dir>] [--dry-run] [--skip-compile]`
|
|
4
|
-
* - verify every workspace member
|
|
5
|
-
*
|
|
4
|
+
* - verify every workspace member carries the release version, then pack and
|
|
5
|
+
* publish each package owned by the standard Node release.
|
|
6
6
|
*
|
|
7
7
|
* Bun has no `pnpm -r publish`, so this loop is the recursive-publish stand-in.
|
|
8
8
|
* It leans on native bun for everything bun already does:
|
|
9
9
|
*
|
|
10
10
|
* - **version validation** fails when any member differs from the reviewed
|
|
11
11
|
* `VERSION`; publication never repairs committed release metadata;
|
|
12
|
-
* - **`workspace:` / `catalog:` rewriting** is NOT done here - `bun
|
|
12
|
+
* - **`workspace:` / `catalog:` rewriting** is NOT done here - `bun pm pack`
|
|
13
13
|
* strips both protocols in the PACKED tarball, resolving `workspace:*` to the
|
|
14
14
|
* sibling's version and `catalog:` to the root catalog entry. (Verified: a
|
|
15
15
|
* packed manifest shows `"@scope/x": "<version>"` and the real catalog range,
|
|
16
16
|
* while the on-disk manifest keeps the protocols.) Setting each member's
|
|
17
17
|
* version first is the only prerequisite, so a sibling resolves the release
|
|
18
|
-
* version. When Bun's workspace lock already carries the version, its
|
|
19
|
-
* refresh is skipped;
|
|
20
18
|
* - **`publishConfig` substitution** (compiled `lib/` entry points) is done
|
|
21
19
|
* inside the temporary archive, NOT in the checkout: unlike pnpm/npm, `bun
|
|
22
20
|
* publish`/`bun pm pack` do NOT fold `publishConfig`'s `main`/`types`/`bin`/
|
|
@@ -29,27 +27,26 @@
|
|
|
29
27
|
* - **compiled output** is emitted once, before packing, by one root-level
|
|
30
28
|
* filtered `bun run` that fans out to every publishable member in parallel.
|
|
31
29
|
* Every package is then packed once with lifecycle scripts disabled. The
|
|
32
|
-
* exact validated archive is passed to `
|
|
30
|
+
* exact validated archive is passed to `npm publish`, so upload never
|
|
33
31
|
* repacks or repeats a member's `prepack`. Packages retain `prepack` for
|
|
34
32
|
* standalone publishes.
|
|
35
33
|
* - **release recovery** compares each packed archive's integrity and
|
|
36
34
|
* repository identity with registry metadata. A matching immutable version
|
|
37
35
|
* is skipped, while any mismatch fails the retry.
|
|
38
36
|
*
|
|
39
|
-
* `--dry-run` forwards to `
|
|
40
|
-
* but uploads nothing
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* repo-relative) skips a member owned by another publication flow.
|
|
37
|
+
* `--dry-run` forwards to `npm publish`: it packs + validates
|
|
38
|
+
* but uploads nothing. `--registry` targets a non-default registry (a local
|
|
39
|
+
* verdaccio); `--exclude <dir>` (repeatable, repo-relative) skips a member owned
|
|
40
|
+
* by another publication flow.
|
|
44
41
|
*
|
|
45
|
-
* The checkout remains byte-for-byte unchanged. A stale manifest
|
|
46
|
-
*
|
|
42
|
+
* The checkout remains byte-for-byte unchanged. A stale manifest is a release
|
|
43
|
+
* error rather than something publication repairs. Local lockfiles are ignored
|
|
44
|
+
* because configured registries can leak into them.
|
|
47
45
|
*/
|
|
48
46
|
import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
49
47
|
import { tmpdir } from "node:os";
|
|
50
48
|
import { delimiter, dirname, join, resolve } from "node:path";
|
|
51
49
|
import { asyncUtils, log } from "@dbx-tools/shared-core";
|
|
52
|
-
import ts from "typescript";
|
|
53
50
|
import { parse } from "yaml";
|
|
54
51
|
import {
|
|
55
52
|
npmReleaseMatches,
|
|
@@ -58,7 +55,6 @@ import {
|
|
|
58
55
|
readNpmArchiveIdentity,
|
|
59
56
|
} from "./publish-npm.ts";
|
|
60
57
|
import { runTaskCommand, runTaskCommandAsync } from "../src/_task-command.ts";
|
|
61
|
-
import { toPosix } from "../src/packages.ts";
|
|
62
58
|
|
|
63
59
|
const logger = log.logger("projen:publish");
|
|
64
60
|
|
|
@@ -85,28 +81,6 @@ function manifestsMatchVersion(members: readonly string[], version: string): boo
|
|
|
85
81
|
});
|
|
86
82
|
}
|
|
87
83
|
|
|
88
|
-
/** Whether Bun's workspace lock records each live workspace manifest version. */
|
|
89
|
-
function lockfileMatchesManifestVersions(root: string, members: readonly string[]): boolean {
|
|
90
|
-
const lockfile = join(root, "bun.lock");
|
|
91
|
-
if (!existsSync(lockfile)) return false;
|
|
92
|
-
try {
|
|
93
|
-
const parsed = ts.parseConfigFileTextToJson(lockfile, readFileSync(lockfile, "utf8"));
|
|
94
|
-
if (parsed.error) return false;
|
|
95
|
-
const lock = parsed.config as {
|
|
96
|
-
workspaces?: Record<string, { version?: string }>;
|
|
97
|
-
};
|
|
98
|
-
return members.every((dir) => {
|
|
99
|
-
const manifest = JSON.parse(readFileSync(join(dir, "package.json"), "utf8")) as {
|
|
100
|
-
version?: string;
|
|
101
|
-
};
|
|
102
|
-
const relative = toPosix(dir.slice(resolve(root).length + 1));
|
|
103
|
-
return lock.workspaces?.[relative]?.version === manifest.version;
|
|
104
|
-
});
|
|
105
|
-
} catch {
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
84
|
/** Entry-point fields projen writes as `.ts` source in-repo and rewrites to `lib/` for publish. */
|
|
111
85
|
const PUBLISH_CONFIG_ENTRY_FIELDS = ["main", "types", "bin", "exports"] as const;
|
|
112
86
|
|
|
@@ -183,11 +157,6 @@ if (!manifestsMatchVersion(members, version)) {
|
|
|
183
157
|
}
|
|
184
158
|
logger.info(`validated ${members.length} selected member manifests`);
|
|
185
159
|
|
|
186
|
-
if (!lockfileMatchesManifestVersions(root, allMembers)) {
|
|
187
|
-
throw new Error("bun.lock does not match workspace versions; run bun install and commit it");
|
|
188
|
-
}
|
|
189
|
-
logger.info("validated workspace lock versions");
|
|
190
|
-
|
|
191
160
|
const publishArgs = [
|
|
192
161
|
"--ignore-scripts",
|
|
193
162
|
...(registry ? ["--registry", registry] : []),
|
|
@@ -206,17 +175,12 @@ for (const dir of members) {
|
|
|
206
175
|
name?: string;
|
|
207
176
|
version?: string;
|
|
208
177
|
private?: boolean;
|
|
209
|
-
dbxToolsConfig?: { uniffi?: boolean };
|
|
210
178
|
publishConfig?: { access?: unknown };
|
|
211
179
|
};
|
|
212
180
|
if (pkg.private) {
|
|
213
181
|
logger.info(`skip private ${pkg.name ?? dirname(dir)}`);
|
|
214
182
|
continue;
|
|
215
183
|
}
|
|
216
|
-
if (pkg.dbxToolsConfig?.uniffi === true) {
|
|
217
|
-
logger.info(`skip UniFFI ${pkg.name ?? dirname(dir)}`);
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
184
|
if (!pkg.version) throw new Error(`Missing package version for ${pkg.name ?? dirname(dir)}`);
|
|
221
185
|
const access = pkg.publishConfig?.access;
|
|
222
186
|
if (access !== undefined && access !== "public" && access !== "restricted") {
|
|
@@ -303,7 +267,7 @@ await asyncUtils.mapConcurrent(
|
|
|
303
267
|
logger.info(`${dryRun ? "dry-run publishing" : "publishing"} ${name} @ ${packageVersion}`);
|
|
304
268
|
await runTaskCommandAsync(
|
|
305
269
|
dir,
|
|
306
|
-
"
|
|
270
|
+
"npm",
|
|
307
271
|
["publish", ...(access ? ["--access", access] : []), ...publishArgs, archive],
|
|
308
272
|
{ env: { ...process.env, PATH: path } },
|
|
309
273
|
);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env -S bun
|
|
2
|
+
import { dirname, isAbsolute, resolve, sep } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { parseArgs } from "node:util";
|
|
5
|
+
import { log } from "@dbx-tools/shared-core";
|
|
6
|
+
import { runTaskCommand } from "../src/_task-command.ts";
|
|
7
|
+
import { pythonNodeBindingProjects, repoRoot } from "../src/packages.ts";
|
|
8
|
+
import {
|
|
9
|
+
pythonNodeBindingWatchInputs,
|
|
10
|
+
resolvePythonNodeBindings,
|
|
11
|
+
type ResolvedPythonNodeBindings,
|
|
12
|
+
} from "../src/python-node-bindings.ts";
|
|
13
|
+
import { watchLoop } from "../src/watch.ts";
|
|
14
|
+
|
|
15
|
+
const logger = log.logger("projen:python-node-bindings");
|
|
16
|
+
const task = resolve(dirname(fileURLToPath(import.meta.url)), "python-node-bindings.ts");
|
|
17
|
+
const { values } = parseArgs({ options: { project: { type: "string" } } });
|
|
18
|
+
const projects = values.project ? [values.project] : pythonNodeBindingProjects(repoRoot);
|
|
19
|
+
const configs = projects.map((project) => resolvePythonNodeBindings(repoRoot, project));
|
|
20
|
+
|
|
21
|
+
if (configs.length > 0) {
|
|
22
|
+
watchLoop(
|
|
23
|
+
"python-node-bindings",
|
|
24
|
+
[...new Set(configs.flatMap(pythonNodeBindingWatchInputs))],
|
|
25
|
+
(changed) => {
|
|
26
|
+
for (const config of configs.filter((candidate) => affected(candidate, changed))) {
|
|
27
|
+
logger.start(`generating ${config.project} Node bindings`);
|
|
28
|
+
runTaskCommand(repoRoot, "bun", [task, "--project", config.project]);
|
|
29
|
+
logger.success(`generated ${config.project} Node bindings`);
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
check: (changed) => configs.some((config) => affected(config, changed)),
|
|
34
|
+
},
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function affected(config: ResolvedPythonNodeBindings, changed: readonly string[]): boolean {
|
|
39
|
+
const inputs = pythonNodeBindingWatchInputs(config);
|
|
40
|
+
return changed.some((path) => inputs.some((input) => contains(input, path)));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function contains(input: string, candidate: string): boolean {
|
|
44
|
+
const root = isAbsolute(input) ? input : resolve(repoRoot, input);
|
|
45
|
+
const path = isAbsolute(candidate) ? candidate : resolve(repoRoot, candidate);
|
|
46
|
+
return path === root || path.startsWith(root + sep);
|
|
47
|
+
}
|