@dbx-tools/projen 0.9.5 → 0.9.7
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 +69 -17
- package/index.ts +7 -5
- package/package.json +9 -6
- package/src/_rust-project.ts +356 -0
- package/src/_rust-release-workflow.ts +954 -0
- package/src/_rust-release.ts +605 -0
- package/src/_task-command.ts +89 -0
- package/src/barrels.ts +12 -11
- package/src/bun-app.ts +96 -85
- package/src/bun-workflow.ts +60 -52
- package/src/engine-root.ts +5 -5
- package/src/openapi.ts +16 -9
- package/src/packages.ts +9 -9
- package/src/pnpm-workspace.ts +1 -1
- package/src/project-js.ts +19 -25
- package/src/project-py.ts +28 -23
- package/src/project-rs.ts +100 -1098
- package/src/projen-version.ts +9 -0
- package/src/publish.ts +3 -4
- package/src/release-catalog.ts +1 -1
- package/src/release.ts +212 -177
- package/src/scaffold.ts +1 -1
- package/src/uniffi.ts +7 -0
- package/src/watch.ts +2 -2
- package/src/workspace-version.ts +20 -15
- package/tasks/barrels.ts +4 -4
- package/tasks/build-rust-release.ts +68 -0
- package/tasks/bump.ts +3 -2
- package/tasks/clean.ts +3 -3
- package/tasks/local-publish.ts +21 -3
- package/tasks/openapi.ts +2 -2
- package/tasks/publish-npm.ts +12 -6
- package/tasks/publish-python.ts +2 -2
- package/tasks/publish-uniffi-local.ts +11 -15
- package/tasks/publish.ts +88 -88
- package/tasks/python-registry.ts +1 -1
- package/tasks/release-bootstrap.ts +2 -2
- package/tasks/release-plan.ts +1 -1
- package/tasks/release-please.ts +1 -1
- package/tasks/release-pr.ts +60 -115
- package/tasks/release-request.ts +8 -29
- package/tasks/release-summary-units.ts +2 -9
- package/tasks/release-summary.ts +35 -24
- package/tasks/release-version.ts +44 -0
- package/tasks/rust-release.d.ts +41 -0
- package/tasks/rust-release.mjs +1717 -0
- package/tasks/sync.ts +3 -3
- package/tasks/uniffi-python.js +1 -1
- package/tasks/uniffi.ts +7 -1
- package/tasks/version-check.ts +6 -5
package/src/project-js.ts
CHANGED
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { existsSync, readdirSync } from "node:fs";
|
|
10
10
|
import { dirname, join, relative, resolve } from "node:path";
|
|
11
|
-
import
|
|
11
|
+
import * as projectUtils from "@dbx-tools/core/project-utils";
|
|
12
12
|
import { ignore, match } from "@dbx-tools/path";
|
|
13
|
-
import { object,
|
|
13
|
+
import { object, stringUtils, type OneOrMany } from "@dbx-tools/shared-core";
|
|
14
14
|
import { type IConstruct } from "constructs";
|
|
15
15
|
import { Component, IgnoreFile, Project, type TaskOptions, javascript, typescript } from "projen";
|
|
16
16
|
import { BuildWorkflow } from "projen/lib/build";
|
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
} from "./packages.ts";
|
|
41
41
|
import { PnpmWorkspaceState, type DBXToolsPNPMWorkspaceOptions } from "./pnpm-workspace.ts";
|
|
42
42
|
import type { DBXToolsProject, DBXToolsProjectOptions as CommonProjectOptions } from "./project.ts";
|
|
43
|
+
import { PROJEN_VERSION } from "./projen-version.ts";
|
|
43
44
|
import { applyCompiledPublish } from "./publish.ts";
|
|
44
45
|
import {
|
|
45
46
|
DBXToolsReleaseCatalog,
|
|
@@ -120,17 +121,17 @@ export class PackageIdentifier {
|
|
|
120
121
|
* `@<first>/<rest joined by ->`.
|
|
121
122
|
*
|
|
122
123
|
* The leading segment is the npm `@scope`, kebab-cased with
|
|
123
|
-
* {@link
|
|
124
|
+
* {@link stringUtils.toSlug} so a multi-word scope survives intact
|
|
124
125
|
* (`dbx-tools` -> `dbx-tools`, not `dbx`/`tools`). Every later path
|
|
125
|
-
* segment is tokenized with {@link
|
|
126
|
+
* segment is tokenized with {@link stringUtils.tokenize}, so nested folders
|
|
126
127
|
* split into their own dash-joined name parts.
|
|
127
128
|
*/
|
|
128
129
|
static of(...names: OneOrMany<string>): PackageIdentifier {
|
|
129
130
|
const segments = names.flatMap((part) => part.split("/")).filter(Boolean);
|
|
130
|
-
const scope = segments.length ?
|
|
131
|
+
const scope = segments.length ? stringUtils.toSlug(segments[0]!) : "";
|
|
131
132
|
const nameParts = [
|
|
132
133
|
scope,
|
|
133
|
-
...segments.slice(1).flatMap((segment) => [...
|
|
134
|
+
...segments.slice(1).flatMap((segment) => [...stringUtils.tokenize(segment)]),
|
|
134
135
|
].filter(Boolean);
|
|
135
136
|
if (!nameParts.length) throw new Error(`Invalid name: ${names.join(", ")}`);
|
|
136
137
|
if (nameParts.length === 1) return new PackageIdentifier(undefined, nameParts[0]!);
|
|
@@ -154,13 +155,13 @@ function configureRootPackage(project: javascript.NodeProject): void {
|
|
|
154
155
|
* published source (without it, publish fails with E422). A child also carries the
|
|
155
156
|
* monorepo `directory` subpath (its path relative to the root); the root omits it.
|
|
156
157
|
* No-op when no git remote is detected and no `repository` override was supplied.
|
|
157
|
-
* The URL is auto-detected + cached by {@link
|
|
158
|
+
* The URL is auto-detected + cached by {@link projectUtils.repositoryUrl} (gh, then
|
|
158
159
|
* a normalized git remote), in npm's `git+https://.../repo.git` form.
|
|
159
160
|
*/
|
|
160
161
|
function applyRepository(project: javascript.NodeProject, override?: string): void {
|
|
161
162
|
const workspaceRoot = resolve(project.root.outdir);
|
|
162
163
|
const url =
|
|
163
|
-
override && override.length ? override :
|
|
164
|
+
override && override.length ? override : projectUtils.repositoryUrl(workspaceRoot, "npm");
|
|
164
165
|
if (!url) return;
|
|
165
166
|
const directory = toPosix(relative(workspaceRoot, resolve(project.outdir)));
|
|
166
167
|
project.package.addField("repository", {
|
|
@@ -278,7 +279,7 @@ export function projectRepositoryUrl(project: javascript.NodeProject): string |
|
|
|
278
279
|
typeof repository === "string"
|
|
279
280
|
? repository
|
|
280
281
|
: object.isRecord(repository)
|
|
281
|
-
? (
|
|
282
|
+
? (stringUtils.trimToNull(repository.url) ?? undefined)
|
|
282
283
|
: undefined;
|
|
283
284
|
return configured?.replace(/^git\+/, "").replace(/\.git$/, "");
|
|
284
285
|
}
|
|
@@ -382,14 +383,7 @@ const PRETTIER_SETTINGS: javascript.PrettierSettings = {
|
|
|
382
383
|
endOfLine: javascript.EndOfLine.LF,
|
|
383
384
|
};
|
|
384
385
|
|
|
385
|
-
|
|
386
|
-
* The `projen` version every generated manifest pins.
|
|
387
|
-
*
|
|
388
|
-
* Kept as one constant so the root's devDependency and this engine's own
|
|
389
|
-
* dependency can never drift apart - a synth run loads the engine from one copy
|
|
390
|
-
* of projen and the tasks execute against another otherwise.
|
|
391
|
-
*/
|
|
392
|
-
export const PROJEN_VERSION = "^0.101.16";
|
|
386
|
+
export { PROJEN_VERSION } from "./projen-version.ts";
|
|
393
387
|
|
|
394
388
|
/** SPDX license shared by generated JavaScript, Python, and Rust packages. */
|
|
395
389
|
export const DBX_TOOLS_LICENSE = "Apache-2.0";
|
|
@@ -412,7 +406,7 @@ function defaultProjectOptions(options: DBXToolsJavaScriptProjectOptions) {
|
|
|
412
406
|
// phase installs with pnpm - so a deployed app keeps its catalog + build
|
|
413
407
|
// allowances even though the local/CI manager is bun.
|
|
414
408
|
packageManager: javascript.NodePackageManager.BUN,
|
|
415
|
-
// Pinned rather than left to projen's "latest":
|
|
409
|
+
// Pinned rather than left to projen's "latest": this is the co-tested release
|
|
416
410
|
// whose `NodePackage` renders bun's `trustedDependencies` natively. Under bun,
|
|
417
411
|
// projen does NOT create the `pnpm-workspace.yaml` component itself (that call
|
|
418
412
|
// site is gated to pnpm), so the engine constructs it directly ({@link
|
|
@@ -750,7 +744,7 @@ export class DBXToolsNodeProject
|
|
|
750
744
|
const steps = super.renderWorkflowSetup(options);
|
|
751
745
|
if (this.parent) return steps;
|
|
752
746
|
return steps.flatMap((step) => {
|
|
753
|
-
if (step.uses
|
|
747
|
+
if (step.uses?.startsWith("oven-sh/setup-bun@")) {
|
|
754
748
|
return [...bunCacheRestoreSteps(this, { ignorePaths: this.workflowCacheIgnorePaths })];
|
|
755
749
|
}
|
|
756
750
|
if (step.run === "bun install") {
|
|
@@ -963,7 +957,7 @@ function configureBuildWorkflow(
|
|
|
963
957
|
"!**/package.json",
|
|
964
958
|
"!**/pyproject.toml",
|
|
965
959
|
"!**/index.ts",
|
|
966
|
-
"!packages/js/node/rust-binary/src/
|
|
960
|
+
"!packages/js/node/rust-binary/src/_release-binaries.ts",
|
|
967
961
|
"!packages/js/node/appkit-graphiti/src/_python-runtime.ts",
|
|
968
962
|
],
|
|
969
963
|
},
|
|
@@ -1216,7 +1210,7 @@ const DEFAULT_OMIT_RELATIVE_PREFIX = ["node"];
|
|
|
1216
1210
|
function resolveOmitRelativePrefix(option: OneOrMany<string> | undefined): string[] {
|
|
1217
1211
|
const raw = option === undefined ? DEFAULT_OMIT_RELATIVE_PREFIX : option;
|
|
1218
1212
|
const list = Array.isArray(raw) ? raw : [raw];
|
|
1219
|
-
return list.map((segment) =>
|
|
1213
|
+
return list.map((segment) => stringUtils.toSlug(segment)).filter(Boolean);
|
|
1220
1214
|
}
|
|
1221
1215
|
|
|
1222
1216
|
/**
|
|
@@ -1226,7 +1220,7 @@ function resolveOmitRelativePrefix(option: OneOrMany<string> | undefined): strin
|
|
|
1226
1220
|
*/
|
|
1227
1221
|
function packageNameFor(scope: string, relPath: string, omitPrefixes: string[]): string {
|
|
1228
1222
|
const segments = relPath.split("/").filter(Boolean);
|
|
1229
|
-
if (segments.length > 1 && omitPrefixes.includes(
|
|
1223
|
+
if (segments.length > 1 && omitPrefixes.includes(stringUtils.toSlug(segments[0]!))) {
|
|
1230
1224
|
segments.shift();
|
|
1231
1225
|
}
|
|
1232
1226
|
return PackageIdentifier.of(scope, segments.join("/")).packageName;
|
|
@@ -1267,14 +1261,14 @@ function engineSelfDependency(project: javascript.NodeProject): string | undefin
|
|
|
1267
1261
|
const enginePkgJson = join(resolvePkgRoot(), "package.json");
|
|
1268
1262
|
if (!toPosix(enginePkgJson).includes("/node_modules/")) return undefined;
|
|
1269
1263
|
const engine = readPackageManifest(dirname(enginePkgJson));
|
|
1270
|
-
const name =
|
|
1264
|
+
const name = stringUtils.trimToNull(engine?.name);
|
|
1271
1265
|
if (!name) return undefined;
|
|
1272
|
-
const version =
|
|
1266
|
+
const version = stringUtils.trimToNull(engine?.version);
|
|
1273
1267
|
|
|
1274
1268
|
// No existing consumer manifest (or no entry) falls through to a computed pin.
|
|
1275
1269
|
const consumer = readPackageManifest(resolve(project.outdir));
|
|
1276
1270
|
const dependencyOf = (field: unknown): string | undefined =>
|
|
1277
|
-
object.isRecord(field) ? (
|
|
1271
|
+
object.isRecord(field) ? (stringUtils.trimToNull(field[name]) ?? undefined) : undefined;
|
|
1278
1272
|
const existing = dependencyOf(consumer?.devDependencies) ?? dependencyOf(consumer?.dependencies);
|
|
1279
1273
|
if (existing) return `${name}@${existing}`;
|
|
1280
1274
|
return `${name}@^${version}`;
|
package/src/project-py.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Reusable uv workspace generation for Python packages hosted in a projen tree. */
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
2
|
+
import * as projectUtils from "@dbx-tools/core/project-utils";
|
|
3
|
+
import { stringUtils } from "@dbx-tools/shared-core";
|
|
4
4
|
import { Component, License, TextFile, type Project, javascript, python, vscode } from "projen";
|
|
5
5
|
import { JobPermission } from "projen/lib/github/workflows-model";
|
|
6
6
|
import { BUN_VERSION, bunCacheRestoreSteps, bunCacheSaveStep } from "./bun-workflow.ts";
|
|
@@ -237,12 +237,12 @@ export class DBXToolsPythonWorkspace extends Component {
|
|
|
237
237
|
constructor(project: javascript.NodeProject, options: DBXToolsPythonWorkspaceOptions) {
|
|
238
238
|
super(project);
|
|
239
239
|
const scope = isDBXToolsJavaScriptProject()(project)
|
|
240
|
-
?
|
|
241
|
-
:
|
|
240
|
+
? stringUtils.toSlug(project.scope)
|
|
241
|
+
: stringUtils.toSlug(project.name).replace(/-root$/, "");
|
|
242
242
|
const repositoryUrl =
|
|
243
243
|
options.repository?.url ??
|
|
244
244
|
projectRepositoryUrl(project) ??
|
|
245
|
-
|
|
245
|
+
projectUtils.repositoryUrl(project.outdir);
|
|
246
246
|
if (!repositoryUrl) {
|
|
247
247
|
throw new Error("Python workspace repository URL was not configured or detected");
|
|
248
248
|
}
|
|
@@ -253,7 +253,7 @@ export class DBXToolsPythonWorkspace extends Component {
|
|
|
253
253
|
};
|
|
254
254
|
const packageIdentities: ResolvedPythonPackageOptions[] = options.packages.map((pkg) => ({
|
|
255
255
|
...pkg,
|
|
256
|
-
name: pkg.name ?? `${scope}-${
|
|
256
|
+
name: pkg.name ?? `${scope}-${stringUtils.toSlug(pkg.directory)}`,
|
|
257
257
|
module: pkg.module ?? pythonModuleName(scope, pkg.directory),
|
|
258
258
|
}));
|
|
259
259
|
const packagesByDirectory = new Map(packageIdentities.map((pkg) => [pkg.directory, pkg]));
|
|
@@ -766,23 +766,28 @@ export class DBXToolsPythonWorkspace extends Component {
|
|
|
766
766
|
];
|
|
767
767
|
const helper = ".projen/pypi-trusted-publisher-instructions.mjs";
|
|
768
768
|
new TextFile(project.root, helper, {
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
769
|
+
// prettier-ignore
|
|
770
|
+
lines: stringUtils.dedent(
|
|
771
|
+
// ============================================================================
|
|
772
|
+
/*js*/`
|
|
773
|
+
#!/usr/bin/env node
|
|
774
|
+
import { parseArgs } from "node:util";
|
|
775
|
+
|
|
776
|
+
const { values } = parseArgs({
|
|
777
|
+
options: { secretFile: { type: "string" } },
|
|
778
|
+
});
|
|
779
|
+
const beforeAuthentication = ${JSON.stringify(linesBeforeAuthentication)};
|
|
780
|
+
const afterAuthentication = ${JSON.stringify(linesAfterAuthentication)};
|
|
781
|
+
const authentication = values.secretFile
|
|
782
|
+
? [
|
|
783
|
+
\`- If browser authentication is required, read credentials from \${values.secretFile} directly into the browser without printing, logging, or exposing secret values.\`,
|
|
784
|
+
\`- If \${values.secretFile} is absent, invalid, or insufficient for authentication, pause and ask the user to complete authentication.\`,
|
|
785
|
+
]
|
|
786
|
+
: ["- If browser authentication is required, pause and ask the user to complete authentication."];
|
|
787
|
+
process.stdout.write(\`\${[...beforeAuthentication, ...authentication, ...afterAuthentication].join("\\n").trimEnd()}\\n\`);
|
|
788
|
+
`
|
|
789
|
+
// ============================================================================
|
|
790
|
+
).split("\n"),
|
|
786
791
|
});
|
|
787
792
|
project.root.addTask("pypiTrustedPublisherInstructions", {
|
|
788
793
|
description: "Print system-browser instructions for PyPI trusted publishers",
|