@dbx-tools/projen 0.9.47 → 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 +37 -2
- 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
package/README.md
CHANGED
|
@@ -184,17 +184,56 @@ polyfills. Python-host-backed adapters for process execution, files, crypto,
|
|
|
184
184
|
OS values, and process state are included automatically, so packages do not
|
|
185
185
|
configure a shim directory.
|
|
186
186
|
|
|
187
|
-
The workspace creates `<name>:python-runtime
|
|
188
|
-
`<name>:python-runtime:
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
187
|
+
The workspace creates `<name>:python-runtime`, `<name>:python-runtime:check`,
|
|
188
|
+
and `<name>:python-runtime:watch`. The focused watcher regenerates a Python
|
|
189
|
+
project when its bound Node package, a transitive workspace dependency, its
|
|
190
|
+
binding configuration, a function override, or a shared shim changes.
|
|
191
|
+
`bun run sync --watch` supervises this watcher alongside Projen and barrel
|
|
192
|
+
generation. Full synthesis regenerates bindings and removes stale
|
|
193
|
+
`_generated/node` trees by reconciling them with the current `pyproject.toml`
|
|
194
|
+
files, including the resynthesis triggered by a `.projenrc.ts` change.
|
|
192
195
|
Built-in shims can make ordinary Node imports work under PythonMonkey.
|
|
193
196
|
`functionOverrides` replaces named exports only in the generated runtime, so the
|
|
194
197
|
Node package does not gain Python callbacks or alternate source files. A
|
|
195
198
|
replacement may use a different export name through `handlerExport`; every
|
|
196
199
|
other export continues to come from the original module.
|
|
197
200
|
|
|
201
|
+
### Synchronize Pinned Python Sources
|
|
202
|
+
|
|
203
|
+
Use a package's `sync` field when published Python code must include a reviewed
|
|
204
|
+
subset of a Git repository instead of exposing a direct URL dependency:
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
{
|
|
208
|
+
directory: "service",
|
|
209
|
+
description: "Python service with synchronized upstream code",
|
|
210
|
+
sync: [
|
|
211
|
+
{
|
|
212
|
+
name: "upstream_driver",
|
|
213
|
+
source:
|
|
214
|
+
"driver @ git+https://github.com/example/project.git@0123456789abcdef0123456789abcdef01234567#subdirectory=src/driver",
|
|
215
|
+
include: ["driver.py", "operations/**/*.py"],
|
|
216
|
+
exclude: ["**/test_*.py"],
|
|
217
|
+
replace: {
|
|
218
|
+
"from driver.": "from example.service._generated.sync.upstream_driver.",
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
],
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Each source is written beneath
|
|
226
|
+
`<python-module>/_generated/sync/<name-or-owner-repository-hash>`. The generated
|
|
227
|
+
package task parses pip-style Git sources, filters files with the configured
|
|
228
|
+
globs, verifies every replacement matched, records the options and resolved
|
|
229
|
+
commit in `.sync.json`, and recursively marks the result read-only.
|
|
230
|
+
|
|
231
|
+
Synchronization uses a check-lock-check sequence and an atomic directory
|
|
232
|
+
replacement. A current pinned commit performs no network request. Branches and
|
|
233
|
+
tags use `git ls-remote` to detect upstream movement. Run the generated
|
|
234
|
+
`<package>:python-sync:check` task in validation, or pass `--force` to refresh a
|
|
235
|
+
current source intentionally.
|
|
236
|
+
|
|
198
237
|
## Customize Packages With Mixins
|
|
199
238
|
|
|
200
239
|
```ts
|
package/index.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Hand edits are overwritten on the next watch; this file is read-only.
|
|
4
4
|
|
|
5
5
|
export const PACKAGE_IDENTIFIER = "@dbx-tools/projen";
|
|
6
|
-
export const PACKAGE_VERSION = "0.9.
|
|
6
|
+
export const PACKAGE_VERSION = "0.9.50";
|
|
7
7
|
export * as barrels from "./src/barrels.ts";
|
|
8
8
|
export * as bunApp from "./src/bun-app.ts";
|
|
9
9
|
export * as bunWorkflow from "./src/bun-workflow.ts";
|
|
@@ -50,7 +50,7 @@ export type { DBXToolsProjectLanguage, DBXToolsProjectOptions, DBXToolsProject,
|
|
|
50
50
|
export { PackageIdentifier, LICENSE, DBXToolsNodeProject, ROOT_INSTALL_ONLY_MIXIN, DBXToolsTypeScriptProject } from "./src/project-js.ts";
|
|
51
51
|
export type { DBXToolsJavaScriptProject, DBXToolsReleaseMode, DiscoveredTypeScriptPackageOptions, DBXToolsJavaScriptProjectOptions, DBXToolsTypeScriptProjectOptions } from "./src/project-js.ts";
|
|
52
52
|
export { DBXToolsPythonProject, DBXToolsPythonWorkspace } from "./src/project-py.ts";
|
|
53
|
-
export type { PythonRepositoryOptions, PythonPackageOptions, DBXToolsPythonProjectOptions, PythonReleaseOptions, DBXToolsPythonWorkspaceOptions } from "./src/project-py.ts";
|
|
53
|
+
export type { PythonRepositoryOptions, PythonPackageOptions, PythonSourceSyncOptions, DBXToolsPythonProjectOptions, PythonReleaseOptions, DBXToolsPythonWorkspaceOptions } from "./src/project-py.ts";
|
|
54
54
|
export { PROJEN_VERSION } from "./src/projen-version.ts";
|
|
55
55
|
export { COMPILED_DIR, COMPILED_COMPILER_OPTIONS } from "./src/publish.ts";
|
|
56
56
|
export { PYTHON_NODE_SHIM_ROOT } from "./src/python-node-bindings.ts";
|
package/package.json
CHANGED
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@clack/prompts": "^1.7.0",
|
|
33
|
-
"@dbx-tools/core": "^0.9.
|
|
34
|
-
"@dbx-tools/path": "^0.9.
|
|
35
|
-
"@dbx-tools/shared-core": "^0.9.
|
|
33
|
+
"@dbx-tools/core": "^0.9.50",
|
|
34
|
+
"@dbx-tools/path": "^0.9.50",
|
|
35
|
+
"@dbx-tools/shared-core": "^0.9.50",
|
|
36
36
|
"commander": "^15.0.0",
|
|
37
37
|
"concurrently": "^10.0.3",
|
|
38
38
|
"constructs": "^10.6.0",
|
|
@@ -54,12 +54,12 @@
|
|
|
54
54
|
},
|
|
55
55
|
"main": "index.ts",
|
|
56
56
|
"license": "Apache-2.0",
|
|
57
|
-
"version": "0.9.
|
|
57
|
+
"version": "0.9.50",
|
|
58
58
|
"types": "index.ts",
|
|
59
59
|
"type": "module",
|
|
60
60
|
"exports": {
|
|
61
61
|
".": "./index.ts",
|
|
62
|
-
"./release-packaging": "./tasks/
|
|
62
|
+
"./release-packaging": "./tasks/publish-npm.ts",
|
|
63
63
|
"./package.json": "./package.json"
|
|
64
64
|
},
|
|
65
65
|
"files": [
|
package/src/project-py.ts
CHANGED
|
@@ -3,7 +3,7 @@ import * as projectUtils from "@dbx-tools/core/project-utils";
|
|
|
3
3
|
import { stringUtils, type OneOrMany } from "@dbx-tools/shared-core";
|
|
4
4
|
import { Component, License, TextFile, type Project, github, javascript, python } from "projen";
|
|
5
5
|
import { JobPermission, type Job, type JobStep } from "projen/lib/github/workflows-model";
|
|
6
|
-
import { LICENSE, projectRepositoryUrl } from "./project-js.ts";
|
|
6
|
+
import { LICENSE, projectRepositoryUrl, taskCommand } from "./project-js.ts";
|
|
7
7
|
import { isDBXToolsJavaScriptProject } from "./project-predicate.ts";
|
|
8
8
|
import type { DBXToolsProject, DBXToolsProjectOptions } from "./project.ts";
|
|
9
9
|
import { PythonNodeBundle, type PythonNodeBindingsOptions } from "./python-node-bundle.ts";
|
|
@@ -33,6 +33,8 @@ export interface PythonPackageOptions extends DBXToolsProjectOptions {
|
|
|
33
33
|
readonly moduleRoot?: string;
|
|
34
34
|
readonly description: string;
|
|
35
35
|
readonly dependencies?: readonly string[];
|
|
36
|
+
/** Development dependencies emitted only in this package's dependency group. */
|
|
37
|
+
readonly devDependencies?: readonly string[];
|
|
36
38
|
/** Workspace package directories rendered as standalone Git dependencies. */
|
|
37
39
|
readonly internalDependencies?: readonly string[];
|
|
38
40
|
readonly scripts?: Readonly<Record<string, string>>;
|
|
@@ -40,6 +42,17 @@ export interface PythonPackageOptions extends DBXToolsProjectOptions {
|
|
|
40
42
|
readonly releaseEnvironment?: string;
|
|
41
43
|
/** One or more build-time Node packages embedded through PythonMonkey. */
|
|
42
44
|
readonly nodeBindings?: OneOrMany<PythonNodeBindingsOptions>;
|
|
45
|
+
/** Pinned Git source subsets synchronized into this package's generated tree. */
|
|
46
|
+
readonly sync?: readonly PythonSourceSyncOptions[];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** One pip-style Git source synchronized into a generated Python package tree. */
|
|
50
|
+
export interface PythonSourceSyncOptions {
|
|
51
|
+
readonly name?: string;
|
|
52
|
+
readonly source: string;
|
|
53
|
+
readonly include?: readonly string[];
|
|
54
|
+
readonly exclude?: readonly string[];
|
|
55
|
+
readonly replace?: Readonly<Record<string, string>>;
|
|
43
56
|
}
|
|
44
57
|
|
|
45
58
|
interface ResolvedPythonPackageOptions extends PythonPackageOptions {
|
|
@@ -245,7 +258,11 @@ export class DBXToolsPythonProject extends python.PythonProject implements DBXTo
|
|
|
245
258
|
this.tasks.removeTask("publish");
|
|
246
259
|
this.tasks.removeTask("publish:test");
|
|
247
260
|
this.uv.file.addDeletionOverride("project.authors");
|
|
248
|
-
|
|
261
|
+
if (pkg.devDependencies?.length) {
|
|
262
|
+
this.uv.file.addOverride("dependency-groups.dev", [...pkg.devDependencies]);
|
|
263
|
+
} else {
|
|
264
|
+
this.uv.file.addDeletionOverride("dependency-groups");
|
|
265
|
+
}
|
|
249
266
|
if (nodeBindings.length > 0) {
|
|
250
267
|
const rendered = nodeBindings.map(renderPythonNodeBindings);
|
|
251
268
|
this.uv.file.addOverride(
|
|
@@ -253,6 +270,9 @@ export class DBXToolsPythonProject extends python.PythonProject implements DBXTo
|
|
|
253
270
|
rendered.length === 1 ? rendered[0] : rendered,
|
|
254
271
|
);
|
|
255
272
|
}
|
|
273
|
+
if (pkg.sync?.length) {
|
|
274
|
+
this.uv.file.addOverride("tool.dbx_tools.sync", [...pkg.sync]);
|
|
275
|
+
}
|
|
256
276
|
this.uv.file.readonly = true;
|
|
257
277
|
|
|
258
278
|
for (const path of [".gitattributes", ".gitignore"]) {
|
|
@@ -345,6 +365,21 @@ export class DBXToolsPythonWorkspace extends Component {
|
|
|
345
365
|
projectDirectory: pythonPackagePath(this.repository, pkg.packageOptions.directory),
|
|
346
366
|
});
|
|
347
367
|
}
|
|
368
|
+
for (const pkg of this.packages) {
|
|
369
|
+
if (!pkg.packageOptions.sync?.length) continue;
|
|
370
|
+
const directory = pythonPackagePath(this.repository, pkg.packageOptions.directory);
|
|
371
|
+
const command = taskCommand("python-sync.ts", "--project", directory);
|
|
372
|
+
const sync = project.addTask(`${pkg.packageOptions.directory}:python-sync`, {
|
|
373
|
+
description: `Synchronize generated Git sources for ${pkg.packageOptions.name}`,
|
|
374
|
+
execArgs: command,
|
|
375
|
+
});
|
|
376
|
+
const check = project.addTask(`${pkg.packageOptions.directory}:python-sync:check`, {
|
|
377
|
+
description: `Verify generated Git sources for ${pkg.packageOptions.name}`,
|
|
378
|
+
execArgs: [...command, "--check"],
|
|
379
|
+
});
|
|
380
|
+
project.preCompileTask.spawn(sync);
|
|
381
|
+
project.testTask.spawn(check);
|
|
382
|
+
}
|
|
348
383
|
for (const pkg of this.packages) {
|
|
349
384
|
const pyproject = `/${pythonPackagePath(this.repository, pkg.packageOptions.directory)}/pyproject.toml`;
|
|
350
385
|
project.gitignore.include(pyproject);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Shared pyproject-backed configuration for PythonMonkey Node bindings. */
|
|
2
2
|
import { readFileSync, rmSync } from "node:fs";
|
|
3
|
-
import { dirname, join, relative, resolve } from "node:path";
|
|
3
|
+
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { find } from "@dbx-tools/path";
|
|
6
6
|
import { object, stringUtils } from "@dbx-tools/shared-core";
|
|
@@ -58,7 +58,8 @@ export function generatePythonNodeBindings(projectRoot: string = resolveRepoRoot
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
/** Repository-relative Python projects that configure generated Node bindings. */
|
|
62
|
+
export function pythonNodeBindingProjects(projectRoot: string): string[] {
|
|
62
63
|
return [
|
|
63
64
|
...find
|
|
64
65
|
.findFiles("**/pyproject.toml", { cwd: projectRoot })
|
|
@@ -169,6 +170,35 @@ export function pythonNodeBindingWatchInputs(config: ResolvedPythonNodeBindings)
|
|
|
169
170
|
];
|
|
170
171
|
}
|
|
171
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Python projects whose generated runtimes depend on at least one changed path.
|
|
175
|
+
*
|
|
176
|
+
* The project list is computed once per watch batch and reused by the lock preflight
|
|
177
|
+
* and generator callback. This keeps unrelated edits on the no-lock fast path while
|
|
178
|
+
* ensuring each affected Python project is regenerated only once.
|
|
179
|
+
*/
|
|
180
|
+
export function affectedPythonNodeBindingProjects(
|
|
181
|
+
root: string,
|
|
182
|
+
configs: readonly ResolvedPythonNodeBindings[],
|
|
183
|
+
changed: readonly string[],
|
|
184
|
+
): string[] {
|
|
185
|
+
const affected = new Set<string>();
|
|
186
|
+
for (const config of configs) {
|
|
187
|
+
const inputs = pythonNodeBindingWatchInputs(config);
|
|
188
|
+
if (changed.some((candidate) => inputs.some((input) => containsPath(root, input, candidate)))) {
|
|
189
|
+
affected.add(config.project);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return [...affected].sort();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** Whether `candidate` is an input path or one of its descendants. */
|
|
196
|
+
function containsPath(root: string, input: string, candidate: string): boolean {
|
|
197
|
+
const inputPath = resolve(root, input);
|
|
198
|
+
const candidatePath = resolve(root, candidate);
|
|
199
|
+
return candidatePath === inputPath || candidatePath.startsWith(`${inputPath}${sep}`);
|
|
200
|
+
}
|
|
201
|
+
|
|
172
202
|
function validateBindings(configs: readonly ResolvedPythonNodeBindings[]): void {
|
|
173
203
|
const names = configs.map(({ bindingName }) => bindingName);
|
|
174
204
|
if (new Set(names).size !== names.length) {
|
|
@@ -45,6 +45,7 @@ export interface PythonNodeBundleOptions {
|
|
|
45
45
|
export class PythonNodeBundle extends Component {
|
|
46
46
|
readonly buildTask: Task;
|
|
47
47
|
readonly checkTask: Task;
|
|
48
|
+
readonly watchTask: Task;
|
|
48
49
|
|
|
49
50
|
constructor(project: Project, options: PythonNodeBundleOptions) {
|
|
50
51
|
super(project);
|
|
@@ -57,6 +58,10 @@ export class PythonNodeBundle extends Component {
|
|
|
57
58
|
description: `Verify generated Node bindings for Python package ${options.name}`,
|
|
58
59
|
execArgs: [...command, "--check"],
|
|
59
60
|
});
|
|
61
|
+
this.watchTask = project.addTask(`${options.name}:python-runtime:watch`, {
|
|
62
|
+
description: `Watch and regenerate Node bindings for Python package ${options.name}`,
|
|
63
|
+
execArgs: taskCommand("python-node-bindings-watch.ts", "--project", options.projectDirectory),
|
|
64
|
+
});
|
|
60
65
|
if (options.test ?? true) project.testTask.spawn(this.checkTask);
|
|
61
66
|
}
|
|
62
67
|
}
|
|
@@ -1,6 +1,145 @@
|
|
|
1
1
|
#!/usr/bin/env -S bun
|
|
2
|
-
|
|
2
|
+
/** Batch package type-checks and run package-owned compile lifecycles. */
|
|
3
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
+
import { availableParallelism } from "node:os";
|
|
5
|
+
import { join, resolve } from "node:path";
|
|
6
|
+
import * as exec from "@dbx-tools/core/exec";
|
|
7
|
+
import { log } from "@dbx-tools/shared-core";
|
|
3
8
|
|
|
4
|
-
|
|
9
|
+
const logger = log.logger("projen:compile");
|
|
10
|
+
|
|
11
|
+
interface PackageManifest {
|
|
12
|
+
readonly name?: string;
|
|
13
|
+
readonly scripts?: Record<string, string>;
|
|
14
|
+
readonly workspaces?: string[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface TaskManifest {
|
|
18
|
+
readonly tasks?: {
|
|
19
|
+
readonly "pre-compile"?: {
|
|
20
|
+
readonly steps?: Array<{ readonly exec?: string; readonly execArgs?: string[] }>;
|
|
21
|
+
};
|
|
22
|
+
readonly compile?: {
|
|
23
|
+
readonly steps?: Array<{ readonly exec?: string; readonly execArgs?: string[] }>;
|
|
24
|
+
};
|
|
25
|
+
readonly "post-compile"?: {
|
|
26
|
+
readonly steps?: Array<{ readonly exec?: string; readonly execArgs?: string[] }>;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface CompilePlan {
|
|
32
|
+
readonly typescriptConfigs: string[];
|
|
33
|
+
readonly customPackages: Array<{ readonly name: string; readonly directory: string }>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function readJson<T>(path: string): T {
|
|
37
|
+
return JSON.parse(readFileSync(path, "utf8")) as T;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function plainTypeScriptBuild(steps: unknown): boolean {
|
|
41
|
+
if (!Array.isArray(steps) || steps.length !== 1) return false;
|
|
42
|
+
const step = steps[0] as { execArgs?: string[] };
|
|
43
|
+
if (!step || typeof step !== "object") return false;
|
|
44
|
+
return (
|
|
45
|
+
Object.keys(step).length === 1 &&
|
|
46
|
+
step.execArgs?.length === 2 &&
|
|
47
|
+
step.execArgs[0] === "tsc" &&
|
|
48
|
+
step.execArgs[1] === "--build"
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function hasSteps(steps: unknown): boolean {
|
|
53
|
+
return Array.isArray(steps) && steps.length > 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Read the root's concrete workspace paths on every run, including newly added members. */
|
|
57
|
+
export function compilePlan(root: string): CompilePlan {
|
|
58
|
+
const workspace = readJson<PackageManifest>(join(root, "package.json"));
|
|
59
|
+
const typescriptConfigs: string[] = [];
|
|
60
|
+
const customPackages: CompilePlan["customPackages"] = [];
|
|
61
|
+
for (const member of workspace.workspaces ?? []) {
|
|
62
|
+
const directory = resolve(root, member);
|
|
63
|
+
const manifest = readJson<PackageManifest>(join(directory, "package.json"));
|
|
64
|
+
const script = manifest.scripts?.compile;
|
|
65
|
+
if (!script) continue;
|
|
66
|
+
const taskPath = join(directory, ".projen", "tasks.json");
|
|
67
|
+
const tasks = existsSync(taskPath) ? readJson<TaskManifest>(taskPath).tasks : undefined;
|
|
68
|
+
const plainCompile = plainTypeScriptBuild(tasks?.compile?.steps);
|
|
69
|
+
if (
|
|
70
|
+
script === "projen compile" &&
|
|
71
|
+
plainCompile &&
|
|
72
|
+
!hasSteps(tasks?.["pre-compile"]?.steps) &&
|
|
73
|
+
!hasSteps(tasks?.["post-compile"]?.steps)
|
|
74
|
+
) {
|
|
75
|
+
typescriptConfigs.push(join(directory, "tsconfig.json"));
|
|
76
|
+
} else {
|
|
77
|
+
customPackages.push({ name: manifest.name ?? member, directory });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return { typescriptConfigs, customPackages };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const emoji = /[\p{Extended_Pictographic}\uFE0F\u200D]/gu;
|
|
84
|
+
|
|
85
|
+
export async function main(): Promise<void> {
|
|
86
|
+
const root = process.cwd();
|
|
87
|
+
const plan = compilePlan(root);
|
|
88
|
+
const jobs: Array<{ name: string; result: Promise<exec.ExecResult> }> = [];
|
|
89
|
+
if (plan.typescriptConfigs.length > 0) {
|
|
90
|
+
const batchCount = Math.min(4, availableParallelism(), plan.typescriptConfigs.length);
|
|
91
|
+
const batches = Array.from({ length: batchCount }, () => [] as string[]);
|
|
92
|
+
plan.typescriptConfigs.forEach((config, index) => batches[index % batchCount]!.push(config));
|
|
93
|
+
logger.info(
|
|
94
|
+
`type-checking ${plan.typescriptConfigs.length} workspaces in ${batchCount} batches`,
|
|
95
|
+
);
|
|
96
|
+
for (const [index, batch] of batches.entries()) {
|
|
97
|
+
jobs.push({
|
|
98
|
+
name: `TypeScript batch ${index + 1}`,
|
|
99
|
+
result: exec.spawn("bunx", ["tsc", "--build", ...batch], {
|
|
100
|
+
cwd: root,
|
|
101
|
+
stdin: "ignore",
|
|
102
|
+
stdout: "inherit",
|
|
103
|
+
stderr: "inherit",
|
|
104
|
+
}),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
for (const pkg of plan.customPackages) {
|
|
109
|
+
jobs.push({
|
|
110
|
+
name: pkg.name,
|
|
111
|
+
result: compileCustomPackage(pkg),
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
const results = await Promise.allSettled(jobs.map((job) => job.result));
|
|
115
|
+
let failed = false;
|
|
116
|
+
for (const [index, result] of results.entries()) {
|
|
117
|
+
if (result.status === "rejected") {
|
|
118
|
+
logger.error(`${jobs[index]!.name} compile failed:`, result.reason);
|
|
119
|
+
failed = true;
|
|
120
|
+
} else if (result.value.exitCode !== 0) {
|
|
121
|
+
logger.error(`${jobs[index]!.name} compile exited with ${result.value.exitCode}`);
|
|
122
|
+
failed = true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (failed) process.exitCode = 1;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function compileCustomPackage(pkg: {
|
|
129
|
+
readonly name: string;
|
|
130
|
+
readonly directory: string;
|
|
131
|
+
}): Promise<exec.ExecResult> {
|
|
132
|
+
let result: exec.ExecResult | undefined;
|
|
133
|
+
for (const task of ["pre-compile", "compile", "post-compile"]) {
|
|
134
|
+
result = await exec.spawn("bun", ["run", task], {
|
|
135
|
+
cwd: pkg.directory,
|
|
136
|
+
stdin: "ignore",
|
|
137
|
+
stdout: (line) => logger.info(`${pkg.name}: ${line.replace(emoji, "")}`),
|
|
138
|
+
stderr: (line) => logger.error(`${pkg.name}: ${line.replace(emoji, "")}`),
|
|
139
|
+
});
|
|
140
|
+
if (result.exitCode !== 0) return result;
|
|
141
|
+
}
|
|
142
|
+
return result!;
|
|
143
|
+
}
|
|
5
144
|
|
|
6
145
|
if (import.meta.main) await main();
|
|
@@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
|
|
|
4
4
|
import * as projectUtils from "@dbx-tools/core/project-utils";
|
|
5
5
|
import { log, net } from "@dbx-tools/shared-core";
|
|
6
6
|
import { activePythonIndexes, resolveLocalPypi } from "./python-registry.ts";
|
|
7
|
-
import { runTaskCommandAsync } from "
|
|
7
|
+
import { runTaskCommandAsync } from "../src/_task-command.ts";
|
|
8
8
|
|
|
9
9
|
const logger = log.logger("projen:local-publish");
|
|
10
10
|
|
|
@@ -33,6 +33,11 @@ export interface LocalPublishResult {
|
|
|
33
33
|
readonly python: boolean;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/** Resolve a sibling release task after task implementations are flattened. */
|
|
37
|
+
export function localPublishTaskPath(task: "publish.ts" | "publish-python.ts"): string {
|
|
38
|
+
return fileURLToPath(new URL(task, import.meta.url));
|
|
39
|
+
}
|
|
40
|
+
|
|
36
41
|
/** Build and publish the current workspace directly to local registries. */
|
|
37
42
|
export async function publishLocalRelease(
|
|
38
43
|
options: LocalPublishOptions,
|
|
@@ -54,7 +59,7 @@ export async function publishLocalRelease(
|
|
|
54
59
|
}
|
|
55
60
|
|
|
56
61
|
if (localRegistry) {
|
|
57
|
-
const publishNpmScript =
|
|
62
|
+
const publishNpmScript = localPublishTaskPath("publish.ts");
|
|
58
63
|
logger.info(`building and publishing npm workspace to ${localRegistry}`);
|
|
59
64
|
publishes.push(
|
|
60
65
|
runTaskCommandAsync(root, process.execPath, [
|
|
@@ -67,7 +72,7 @@ export async function publishLocalRelease(
|
|
|
67
72
|
}
|
|
68
73
|
|
|
69
74
|
if (localPypi) {
|
|
70
|
-
const publishPythonScript =
|
|
75
|
+
const publishPythonScript = localPublishTaskPath("publish-python.ts");
|
|
71
76
|
logger.info(`building and publishing Python workspace to ${localPypi.publishUrl}`);
|
|
72
77
|
publishes.push(
|
|
73
78
|
runTaskCommandAsync(root, process.execPath, [
|