@dbx-tools/projen 0.9.46 → 0.9.50
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 +44 -5
- package/index.ts +2 -2
- package/package.json +5 -5
- package/src/project-py.ts +39 -3
- package/src/python-node-bindings.ts +32 -2
- package/src/python-node-bundle.ts +5 -0
- package/tasks/compile-workspace.ts +141 -2
- package/tasks/{lib/local-publish.ts → local-publish.ts} +8 -3
- package/tasks/publish-npm.ts +464 -2
- package/tasks/publish-python.ts +223 -2
- package/tasks/publish.ts +262 -2
- package/tasks/python-node-bindings.ts +1322 -2
- package/tasks/{lib/python-registry.ts → python-registry.ts} +4 -2
- package/tasks/python-sync.ts +248 -0
- package/tasks/release-version.ts +64 -2
- package/tasks/release.ts +272 -2
- package/tasks/lib/compile-workspace.ts +0 -141
- package/tasks/lib/publish-npm.ts +0 -466
- package/tasks/lib/publish-python.ts +0 -213
- package/tasks/lib/publish.ts +0 -264
- package/tasks/lib/python-node-bindings.ts +0 -1343
- package/tasks/lib/release-version.ts +0 -66
- package/tasks/lib/release.ts +0 -278
- /package/tasks/{lib/python-release.ts → python-release.ts} +0 -0
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env -S bun
|
|
2
|
-
/**
|
|
3
|
-
* Build selected uv workspace packages from isolated publication copies and
|
|
4
|
-
* optionally publish the resulting distributions.
|
|
5
|
-
*/
|
|
6
|
-
import {
|
|
7
|
-
chmodSync,
|
|
8
|
-
cpSync,
|
|
9
|
-
existsSync,
|
|
10
|
-
mkdirSync,
|
|
11
|
-
mkdtempSync,
|
|
12
|
-
readFileSync,
|
|
13
|
-
readdirSync,
|
|
14
|
-
rmSync,
|
|
15
|
-
writeFileSync,
|
|
16
|
-
} from "node:fs";
|
|
17
|
-
import { tmpdir } from "node:os";
|
|
18
|
-
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
19
|
-
import * as projectUtils from "@dbx-tools/core/project-utils";
|
|
20
|
-
import { Command } from "commander";
|
|
21
|
-
import { parse, stringify } from "smol-toml";
|
|
22
|
-
import { preparePythonProjectForPublication, pythonProjectInfo } from "./python-release.ts";
|
|
23
|
-
import { runTaskCommand } from "../../src/_task-command.ts";
|
|
24
|
-
|
|
25
|
-
interface PythonProjectFile {
|
|
26
|
-
readonly directory: string;
|
|
27
|
-
readonly name: string;
|
|
28
|
-
readonly version: string;
|
|
29
|
-
readonly path: string;
|
|
30
|
-
readonly private: boolean;
|
|
31
|
-
readonly source: string;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/** Python wheel and source archives accepted by Twine and package indexes. */
|
|
35
|
-
export function pythonDistributionPaths(directory: string): string[] {
|
|
36
|
-
return readdirSync(directory)
|
|
37
|
-
.filter((file) => file.endsWith(".whl") || file.endsWith(".tar.gz"))
|
|
38
|
-
.sort()
|
|
39
|
-
.map((file) => join(directory, file));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function pythonProjects(root: string): PythonProjectFile[] {
|
|
43
|
-
const packageFiles = readdirSync(root, { withFileTypes: true })
|
|
44
|
-
.filter((entry) => entry.isDirectory())
|
|
45
|
-
.map((entry) => resolve(root, entry.name, "pyproject.toml"))
|
|
46
|
-
.filter(existsSync)
|
|
47
|
-
.sort();
|
|
48
|
-
const allProjects: PythonProjectFile[] = packageFiles.map((path) => {
|
|
49
|
-
const source = readFileSync(path, "utf8");
|
|
50
|
-
const info = pythonProjectInfo(source, { parse });
|
|
51
|
-
return {
|
|
52
|
-
directory: basename(resolve(path, "..")),
|
|
53
|
-
name: info.name,
|
|
54
|
-
version: info.version,
|
|
55
|
-
path,
|
|
56
|
-
private: info.private,
|
|
57
|
-
source,
|
|
58
|
-
};
|
|
59
|
-
});
|
|
60
|
-
return allProjects;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function publishPythonProjects(options: {
|
|
64
|
-
readonly dryRun?: boolean;
|
|
65
|
-
readonly indexUrl: string;
|
|
66
|
-
readonly publishUrl: string;
|
|
67
|
-
readonly root: string;
|
|
68
|
-
readonly version: string;
|
|
69
|
-
}): void {
|
|
70
|
-
const root = resolve(options.root);
|
|
71
|
-
const workspace = projectUtils.root(root) ?? dirname(root);
|
|
72
|
-
const output = mkdtempSync(join(workspace, ".projen-python-publish-"));
|
|
73
|
-
try {
|
|
74
|
-
buildPythonProjects({ ...options, output });
|
|
75
|
-
runTaskCommand(
|
|
76
|
-
process.cwd(),
|
|
77
|
-
"uvx",
|
|
78
|
-
[
|
|
79
|
-
"--from",
|
|
80
|
-
"devpi-client",
|
|
81
|
-
"devpi",
|
|
82
|
-
"upload",
|
|
83
|
-
"--index",
|
|
84
|
-
options.publishUrl,
|
|
85
|
-
"--from-dir",
|
|
86
|
-
...(options.dryRun ? ["--dry-run"] : []),
|
|
87
|
-
output,
|
|
88
|
-
],
|
|
89
|
-
{ env: { ...process.env, UV_DEFAULT_INDEX: options.indexUrl } },
|
|
90
|
-
);
|
|
91
|
-
} finally {
|
|
92
|
-
rmSync(output, { recursive: true, force: true });
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/** Build the exact Python release distributions without publishing them. */
|
|
97
|
-
export function buildPythonProjects(options: {
|
|
98
|
-
readonly allowEmpty?: boolean;
|
|
99
|
-
readonly output: string;
|
|
100
|
-
readonly packages?: readonly string[];
|
|
101
|
-
/** Place each package's artifacts in its directory beneath output. */
|
|
102
|
-
readonly packageDirectories?: boolean;
|
|
103
|
-
readonly root: string;
|
|
104
|
-
readonly version: string;
|
|
105
|
-
}): void {
|
|
106
|
-
const root = resolve(options.root);
|
|
107
|
-
const output = resolve(options.output);
|
|
108
|
-
const workspace = projectUtils.root(root) ?? dirname(root);
|
|
109
|
-
const outputPath = relative(workspace, output);
|
|
110
|
-
if (!outputPath || outputPath.startsWith("..") || isAbsolute(outputPath)) {
|
|
111
|
-
throw new Error("Python release output must be a non-root directory inside the workspace");
|
|
112
|
-
}
|
|
113
|
-
rmSync(output, { recursive: true, force: true });
|
|
114
|
-
const allProjects = pythonProjects(root);
|
|
115
|
-
const requested = new Set(options.packages ?? []);
|
|
116
|
-
const missing = [...requested].filter(
|
|
117
|
-
(directory) => !allProjects.some((project) => project.directory === directory),
|
|
118
|
-
);
|
|
119
|
-
if (missing.length > 0) throw new Error(`Unknown Python packages: ${missing.join(", ")}`);
|
|
120
|
-
const projects = allProjects.filter(
|
|
121
|
-
(project) => !project.private && (requested.size === 0 || requested.has(project.directory)),
|
|
122
|
-
);
|
|
123
|
-
if (projects.length === 0 && options.allowEmpty) {
|
|
124
|
-
mkdirSync(output, { recursive: true });
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
if (projects.length === 0) throw new Error(`No Python packages found under ${root}`);
|
|
128
|
-
|
|
129
|
-
const temporaryRoot = mkdtempSync(join(tmpdir(), "projen-python-release-"));
|
|
130
|
-
try {
|
|
131
|
-
mkdirSync(output, { recursive: true });
|
|
132
|
-
for (const project of projects) {
|
|
133
|
-
const packageRoot = join(temporaryRoot, project.directory);
|
|
134
|
-
cpSync(dirname(project.path), packageRoot, { recursive: true });
|
|
135
|
-
const manifestPath = join(packageRoot, "pyproject.toml");
|
|
136
|
-
chmodSync(manifestPath, 0o644);
|
|
137
|
-
writeFileSync(
|
|
138
|
-
manifestPath,
|
|
139
|
-
preparePythonProjectForPublication(project.source, {
|
|
140
|
-
packages: allProjects,
|
|
141
|
-
toml: { parse, stringify },
|
|
142
|
-
version: options.version,
|
|
143
|
-
}),
|
|
144
|
-
);
|
|
145
|
-
const packageOutput = options.packageDirectories ? join(output, project.directory) : output;
|
|
146
|
-
runTaskCommand(root, "uv", ["build", "--out-dir", packageOutput, packageRoot]);
|
|
147
|
-
if (pythonDistributionPaths(packageOutput).length === 0) {
|
|
148
|
-
throw new Error(`No Python distributions found for ${project.name}`);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
const distributions = options.packageDirectories
|
|
152
|
-
? projects.flatMap((project) => pythonDistributionPaths(join(output, project.directory)))
|
|
153
|
-
: pythonDistributionPaths(output);
|
|
154
|
-
if (distributions.length === 0) {
|
|
155
|
-
throw new Error(`No Python distributions found in ${output}`);
|
|
156
|
-
}
|
|
157
|
-
runTaskCommand(process.cwd(), "uvx", ["twine", "check", ...distributions]);
|
|
158
|
-
} finally {
|
|
159
|
-
rmSync(temporaryRoot, { recursive: true, force: true });
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export async function main(): Promise<void> {
|
|
164
|
-
const program = new Command();
|
|
165
|
-
program
|
|
166
|
-
.argument("<version>", "Python package version")
|
|
167
|
-
.option("--index-url <url>", "devpi Simple API URL")
|
|
168
|
-
.option("--publish-url <url>", "devpi writable index URL")
|
|
169
|
-
.option("--root <path>", "Python workspace package root", "packages/py")
|
|
170
|
-
.option("--package <directory...>", "Build only selected package directories")
|
|
171
|
-
.option("--output <path>", "Build distributions into a directory without publishing")
|
|
172
|
-
.option(
|
|
173
|
-
"--package-directories",
|
|
174
|
-
"Keep each package's distributions in its own output directory",
|
|
175
|
-
)
|
|
176
|
-
.option("--dry-run", "build and inspect distributions without uploading")
|
|
177
|
-
.action(
|
|
178
|
-
(
|
|
179
|
-
version: string,
|
|
180
|
-
options: {
|
|
181
|
-
dryRun?: boolean;
|
|
182
|
-
indexUrl?: string;
|
|
183
|
-
package?: string[];
|
|
184
|
-
packageDirectories?: boolean;
|
|
185
|
-
publishUrl?: string;
|
|
186
|
-
root: string;
|
|
187
|
-
output?: string;
|
|
188
|
-
},
|
|
189
|
-
) => {
|
|
190
|
-
if (options.output) {
|
|
191
|
-
buildPythonProjects({
|
|
192
|
-
output: options.output,
|
|
193
|
-
packages: options.package,
|
|
194
|
-
packageDirectories: options.packageDirectories,
|
|
195
|
-
root: options.root,
|
|
196
|
-
version,
|
|
197
|
-
});
|
|
198
|
-
} else {
|
|
199
|
-
if (!options.indexUrl || !options.publishUrl) {
|
|
200
|
-
throw new Error("--index-url and --publish-url are required when publishing");
|
|
201
|
-
}
|
|
202
|
-
publishPythonProjects({
|
|
203
|
-
dryRun: options.dryRun,
|
|
204
|
-
indexUrl: options.indexUrl,
|
|
205
|
-
publishUrl: options.publishUrl,
|
|
206
|
-
root: options.root,
|
|
207
|
-
version,
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
},
|
|
211
|
-
);
|
|
212
|
-
await program.parseAsync();
|
|
213
|
-
}
|
package/tasks/lib/publish.ts
DELETED
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env -S bun
|
|
2
|
-
/**
|
|
3
|
-
* `bun tasks/publish.ts <version> [--registry <url>] [--exclude <dir>] [--dry-run] [--skip-compile]`
|
|
4
|
-
* - verify every workspace member carries the release version, then pack and
|
|
5
|
-
* publish each package owned by the standard Node release.
|
|
6
|
-
*
|
|
7
|
-
* Bun has no `pnpm -r publish`, so this loop is the recursive-publish stand-in.
|
|
8
|
-
* It leans on native bun for everything bun already does:
|
|
9
|
-
*
|
|
10
|
-
* - **version validation** fails when any member differs from the reviewed
|
|
11
|
-
* `VERSION`; publication never repairs committed release metadata;
|
|
12
|
-
* - **`workspace:` / `catalog:` rewriting** is NOT done here - `bun pm pack`
|
|
13
|
-
* strips both protocols in the PACKED tarball, resolving `workspace:*` to the
|
|
14
|
-
* sibling's version and `catalog:` to the root catalog entry. (Verified: a
|
|
15
|
-
* packed manifest shows `"@scope/x": "<version>"` and the real catalog range,
|
|
16
|
-
* while the on-disk manifest keeps the protocols.) Setting each member's
|
|
17
|
-
* version first is the only prerequisite, so a sibling resolves the release
|
|
18
|
-
* - **`publishConfig` substitution** (compiled `lib/` entry points) is done
|
|
19
|
-
* inside the temporary archive, NOT in the checkout: unlike pnpm/npm, `bun
|
|
20
|
-
* publish`/`bun pm pack` do NOT fold `publishConfig`'s `main`/`types`/`bin`/
|
|
21
|
-
* `exports` into the packed manifest (verified: the packed manifest keeps the
|
|
22
|
-
* raw `.ts` source paths and an inert `publishConfig`). Left unsubstituted, a
|
|
23
|
-
* published CLI's `bin` points at `./bin/x.ts`, and because the bin runs via
|
|
24
|
-
* its `#!/usr/bin/env node` shebang, node chokes on the `.ts`
|
|
25
|
-
* (ERR_UNKNOWN_FILE_EXTENSION). We merge `publishConfig` onto the top-level
|
|
26
|
-
* manifest after packing so the tarball advertises the compiled `lib/` tree;
|
|
27
|
-
* - **compiled output** is emitted once, before packing, by one root-level
|
|
28
|
-
* filtered `bun run` that fans out to every publishable member in parallel.
|
|
29
|
-
* Every package is then packed once with lifecycle scripts disabled. The
|
|
30
|
-
* exact validated archive is passed to `npm publish`, so upload never
|
|
31
|
-
* repacks or repeats a member's `prepack`. Packages retain `prepack` for
|
|
32
|
-
* standalone publishes.
|
|
33
|
-
* - **release recovery** compares each packed archive's integrity and
|
|
34
|
-
* repository identity with registry metadata. A matching immutable version
|
|
35
|
-
* is skipped, while any mismatch fails the retry.
|
|
36
|
-
*
|
|
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.
|
|
41
|
-
*
|
|
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.
|
|
45
|
-
*/
|
|
46
|
-
import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
47
|
-
import { tmpdir } from "node:os";
|
|
48
|
-
import { delimiter, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
49
|
-
import { asyncUtils, log } from "@dbx-tools/shared-core";
|
|
50
|
-
import {
|
|
51
|
-
applyPublishConfig,
|
|
52
|
-
npmReleaseMatches,
|
|
53
|
-
packNpmPackage,
|
|
54
|
-
publishedNpmRelease,
|
|
55
|
-
readNpmArchiveIdentity,
|
|
56
|
-
} from "./publish-npm.ts";
|
|
57
|
-
import { runTaskCommand, runTaskCommandAsync } from "../../src/_task-command.ts";
|
|
58
|
-
import { recordedPackages } from "../../src/packages.ts";
|
|
59
|
-
|
|
60
|
-
const logger = log.logger("projen:publish");
|
|
61
|
-
|
|
62
|
-
export { applyPublishConfig };
|
|
63
|
-
|
|
64
|
-
/** Whether selected workspace members carry the reviewed workspace version. */
|
|
65
|
-
function manifestsMatchVersion(members: readonly string[], version: string): boolean {
|
|
66
|
-
return members.every((dir) => {
|
|
67
|
-
const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8")) as {
|
|
68
|
-
version?: string;
|
|
69
|
-
};
|
|
70
|
-
return pkg.version === version;
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/** Compiled files referenced by a publishConfig entry-point tree. */
|
|
75
|
-
function compiledPublishTargets(value: unknown): string[] {
|
|
76
|
-
if (typeof value === "string") return value.startsWith("./lib/") ? [value] : [];
|
|
77
|
-
if (Array.isArray(value)) return value.flatMap(compiledPublishTargets);
|
|
78
|
-
if (!value || typeof value !== "object") return [];
|
|
79
|
-
return Object.values(value).flatMap(compiledPublishTargets);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* PATH with the workspace-root `node_modules/.bin` prepended. The root-level
|
|
84
|
-
* compile reaches each package's projen/dax task, which resolves `tsc` off PATH;
|
|
85
|
-
* under the hoisted linker `tsc` lives only in the root `.bin`.
|
|
86
|
-
*/
|
|
87
|
-
function enrichedPath(root: string): string {
|
|
88
|
-
const binDir = join(root, "node_modules", ".bin");
|
|
89
|
-
const current = process.env.PATH ?? "";
|
|
90
|
-
return current.split(delimiter).includes(binDir) ? current : `${binDir}${delimiter}${current}`;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function outputDirectory(root: string, value: string | undefined): string | undefined {
|
|
94
|
-
if (!value) return undefined;
|
|
95
|
-
const output = resolve(root, value);
|
|
96
|
-
const path = relative(root, output);
|
|
97
|
-
if (!path || path.startsWith("..") || isAbsolute(path)) {
|
|
98
|
-
throw new Error("--output must be a non-root directory inside the workspace");
|
|
99
|
-
}
|
|
100
|
-
return output;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export async function main(argv: string[] = process.argv.slice(2)): Promise<void> {
|
|
104
|
-
const version = argv[0] && !argv[0].startsWith("--") ? argv[0] : undefined;
|
|
105
|
-
const rest = version ? argv.slice(1) : argv;
|
|
106
|
-
const root = process.cwd();
|
|
107
|
-
if (!version) {
|
|
108
|
-
logger.error(
|
|
109
|
-
"usage: bun tasks/publish.ts <version> [--registry <url>] [--output <dir>] [--exclude <dir>] [--dry-run] [--skip-compile]",
|
|
110
|
-
);
|
|
111
|
-
process.exit(1);
|
|
112
|
-
}
|
|
113
|
-
const registryIdx = rest.indexOf("--registry");
|
|
114
|
-
const registry = registryIdx >= 0 ? rest[registryIdx + 1] : undefined;
|
|
115
|
-
const outputIdx = rest.indexOf("--output");
|
|
116
|
-
const output = outputDirectory(root, outputIdx >= 0 ? rest[outputIdx + 1] : undefined);
|
|
117
|
-
const dryRun = rest.includes("--dry-run");
|
|
118
|
-
const skipCompile = rest.includes("--skip-compile");
|
|
119
|
-
const concurrencyIdx = rest.indexOf("--concurrency");
|
|
120
|
-
const parsedConcurrency = Number(concurrencyIdx >= 0 ? rest[concurrencyIdx + 1] : 4);
|
|
121
|
-
if (!Number.isInteger(parsedConcurrency) || parsedConcurrency < 1) {
|
|
122
|
-
throw new Error(`--concurrency must be a positive integer, got ${String(parsedConcurrency)}`);
|
|
123
|
-
}
|
|
124
|
-
const concurrency = parsedConcurrency;
|
|
125
|
-
const excluded = new Set(
|
|
126
|
-
rest.reduce<string[]>((acc, arg, i) => (arg === "--exclude" ? [...acc, rest[i + 1]] : acc), []),
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
const path = enrichedPath(root);
|
|
130
|
-
const allMembers = recordedPackages(root)
|
|
131
|
-
.map((pkg) => pkg.dir)
|
|
132
|
-
.filter((dir) => existsSync(join(dir, "package.json")))
|
|
133
|
-
.filter((dir) => !excluded.has(resolve(root, dir).replace(`${resolve(root)}/`, "")));
|
|
134
|
-
const members = allMembers;
|
|
135
|
-
|
|
136
|
-
if (!manifestsMatchVersion(members, version)) {
|
|
137
|
-
throw new Error(`workspace manifests do not match release ${version}; run projen`);
|
|
138
|
-
}
|
|
139
|
-
logger.info(`validated ${members.length} selected member manifests`);
|
|
140
|
-
|
|
141
|
-
const publishArgs = [
|
|
142
|
-
"--ignore-scripts",
|
|
143
|
-
...(registry ? ["--registry", registry] : []),
|
|
144
|
-
...(dryRun ? ["--dry-run"] : []),
|
|
145
|
-
];
|
|
146
|
-
const publishable: Array<{
|
|
147
|
-
dir: string;
|
|
148
|
-
name: string;
|
|
149
|
-
version: string;
|
|
150
|
-
compile: boolean;
|
|
151
|
-
compiledTargets: string[];
|
|
152
|
-
access?: "public" | "restricted";
|
|
153
|
-
}> = [];
|
|
154
|
-
for (const dir of members) {
|
|
155
|
-
const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8")) as {
|
|
156
|
-
name?: string;
|
|
157
|
-
version?: string;
|
|
158
|
-
private?: boolean;
|
|
159
|
-
publishConfig?: { access?: unknown };
|
|
160
|
-
};
|
|
161
|
-
if (pkg.private) {
|
|
162
|
-
logger.info(`skip private ${pkg.name ?? dirname(dir)}`);
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
if (!pkg.version) throw new Error(`Missing package version for ${pkg.name ?? dirname(dir)}`);
|
|
166
|
-
const access = pkg.publishConfig?.access;
|
|
167
|
-
if (access !== undefined && access !== "public" && access !== "restricted") {
|
|
168
|
-
throw new Error(
|
|
169
|
-
`${pkg.name ?? dirname(dir)} has invalid publishConfig.access ${String(access)}`,
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
const compiledTargets = [...new Set(compiledPublishTargets(pkg.publishConfig))];
|
|
173
|
-
publishable.push({
|
|
174
|
-
dir,
|
|
175
|
-
name: pkg.name ?? dirname(dir),
|
|
176
|
-
version: pkg.version,
|
|
177
|
-
compile: compiledTargets.length > 0,
|
|
178
|
-
compiledTargets,
|
|
179
|
-
...(access ? { access } : {}),
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const compiled = publishable.filter((pkg) => pkg.compile);
|
|
184
|
-
if (compiled.length > 0) {
|
|
185
|
-
if (skipCompile) {
|
|
186
|
-
const missing = compiled.flatMap((pkg) =>
|
|
187
|
-
pkg.compiledTargets
|
|
188
|
-
.filter((target) => !existsSync(resolve(pkg.dir, target)))
|
|
189
|
-
.map((target) => `${pkg.name}:${target}`),
|
|
190
|
-
);
|
|
191
|
-
if (missing.length > 0) {
|
|
192
|
-
throw new Error(
|
|
193
|
-
`--skip-compile requires validated compiled output; missing ${missing.join(", ")}`,
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
logger.info(`reusing validated compiled output for ${compiled.length} publishable packages`);
|
|
197
|
-
} else {
|
|
198
|
-
logger.info(`compiling ${compiled.length} publishable packages from the workspace root`);
|
|
199
|
-
runTaskCommand(
|
|
200
|
-
root,
|
|
201
|
-
"bun",
|
|
202
|
-
["run", ...compiled.flatMap((pkg) => ["--filter", pkg.name]), "compile"],
|
|
203
|
-
{ env: { ...process.env, PATH: path } },
|
|
204
|
-
);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
logger.info(
|
|
209
|
-
`${output ? "packing" : dryRun ? "dry-run packing" : "publishing"} ${publishable.length} packages with concurrency ${concurrency}`,
|
|
210
|
-
);
|
|
211
|
-
if (output) {
|
|
212
|
-
rmSync(output, { recursive: true, force: true });
|
|
213
|
-
mkdirSync(output, { recursive: true });
|
|
214
|
-
}
|
|
215
|
-
await asyncUtils.mapConcurrent(
|
|
216
|
-
publishable,
|
|
217
|
-
async ({ dir, name, version: packageVersion, access }) => {
|
|
218
|
-
const packed = mkdtempSync(join(tmpdir(), "projen-npm-release-"));
|
|
219
|
-
try {
|
|
220
|
-
const archive = packNpmPackage(dir, packed, path, applyPublishConfig);
|
|
221
|
-
const local = readNpmArchiveIdentity(archive);
|
|
222
|
-
if (local.name !== name || local.version !== packageVersion) {
|
|
223
|
-
throw new Error(
|
|
224
|
-
`Packed npm identity ${local.name}@${local.version} does not match ${name}@${packageVersion}`,
|
|
225
|
-
);
|
|
226
|
-
}
|
|
227
|
-
if (local.access !== access) {
|
|
228
|
-
throw new Error(
|
|
229
|
-
`Packed npm access ${String(local.access)} does not match ${String(access)} for ${name}`,
|
|
230
|
-
);
|
|
231
|
-
}
|
|
232
|
-
if (output) {
|
|
233
|
-
const destination = join(output, archive.split(/[\\/]/).at(-1)!);
|
|
234
|
-
if (existsSync(destination)) {
|
|
235
|
-
throw new Error(`Duplicate npm archive name: ${destination}`);
|
|
236
|
-
}
|
|
237
|
-
copyFileSync(archive, destination);
|
|
238
|
-
logger.info(`packed ${name} @ ${packageVersion}`);
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
if (!dryRun) {
|
|
242
|
-
const published = await publishedNpmRelease(local.name, local.version, registry);
|
|
243
|
-
if (npmReleaseMatches(local, published)) {
|
|
244
|
-
logger.info(`skip published ${name} @ ${packageVersion}`);
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
logger.info(`${dryRun ? "dry-run publishing" : "publishing"} ${name} @ ${packageVersion}`);
|
|
249
|
-
await runTaskCommandAsync(
|
|
250
|
-
dir,
|
|
251
|
-
"npm",
|
|
252
|
-
["publish", ...(access ? ["--access", access] : []), ...publishArgs, archive],
|
|
253
|
-
{ env: { ...process.env, PATH: path } },
|
|
254
|
-
);
|
|
255
|
-
} finally {
|
|
256
|
-
rmSync(packed, { recursive: true, force: true });
|
|
257
|
-
}
|
|
258
|
-
},
|
|
259
|
-
{ concurrency, errorMode: "settle" },
|
|
260
|
-
);
|
|
261
|
-
logger.success(
|
|
262
|
-
`${output || dryRun ? "packed" : "published"} ${publishable.length} packages${output ? ` to ${output}` : ""}`,
|
|
263
|
-
);
|
|
264
|
-
}
|