@dbx-tools/projen 0.6.222 → 0.6.224
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 +41 -7
- package/index.ts +4 -2
- package/package.json +5 -5
- package/src/barrels.ts +10 -4
- package/src/bun-workflow.ts +36 -17
- package/src/clean.ts +3 -3
- package/src/codegen.ts +16 -8
- package/src/packages.ts +14 -13
- package/src/project-js.ts +316 -147
- package/src/project-py.ts +8 -25
- package/src/project-rs.ts +98 -71
- package/src/release-github.ts +37 -0
- package/src/release.ts +25 -36
- package/src/tsconfig.ts +17 -17
- package/tasks/local-publish.ts +3 -3
- package/tasks/publish-npm.ts +75 -4
- package/tasks/publish-python.ts +13 -29
- package/tasks/publish-uniffi-local.ts +12 -9
- package/tasks/publish.ts +2 -2
- package/tasks/release-pr.ts +27 -10
- package/tasks/uniffi-python.d.ts +56 -0
- package/tasks/uniffi-python.js +160 -0
- package/tasks/uniffi-release.mjs +25 -49
- package/tasks/uniffi.ts +11 -45
package/src/project-js.ts
CHANGED
|
@@ -13,8 +13,10 @@ import { ignore, match } from "@dbx-tools/path";
|
|
|
13
13
|
import { object, string, 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
|
+
import { BuildWorkflow } from "projen/lib/build";
|
|
17
|
+
import { AutoMerge } from "projen/lib/github";
|
|
16
18
|
import { JobPermission, type JobStep } from "projen/lib/github/workflows-model";
|
|
17
|
-
import {
|
|
19
|
+
import type { ReleaseProjectOptions } from "projen/lib/release";
|
|
18
20
|
import { mixin } from "..";
|
|
19
21
|
import { generateBarrels } from "./barrels.ts";
|
|
20
22
|
import {
|
|
@@ -33,7 +35,6 @@ import {
|
|
|
33
35
|
type DiscoveredPackage,
|
|
34
36
|
projectName,
|
|
35
37
|
readPackageManifest,
|
|
36
|
-
repoRoot,
|
|
37
38
|
scanPackages,
|
|
38
39
|
toPosix,
|
|
39
40
|
} from "./packages.ts";
|
|
@@ -67,6 +68,8 @@ export interface DBXToolsJavaScriptProject extends DBXToolsProject, javascript.N
|
|
|
67
68
|
rootTsconfig?: DBXToolsRootTsconfig;
|
|
68
69
|
/** Root `.vscode/*` - only a tree ROOT has one. */
|
|
69
70
|
vsCode?: DBXToolsVsCode;
|
|
71
|
+
/** Repository outputs excluded from generated workflow dependency-cache hashing. */
|
|
72
|
+
readonly workflowCacheIgnorePaths: readonly string[];
|
|
70
73
|
}
|
|
71
74
|
|
|
72
75
|
/** Parsed npm package identifier: optional scope plus the unscoped package name. */
|
|
@@ -146,10 +149,11 @@ function configureRootPackage(project: javascript.NodeProject): void {
|
|
|
146
149
|
* a normalized git remote), in npm's `git+https://.../repo.git` form.
|
|
147
150
|
*/
|
|
148
151
|
function applyRepository(project: javascript.NodeProject, override?: string): void {
|
|
149
|
-
const
|
|
152
|
+
const workspaceRoot = resolve(project.root.outdir);
|
|
153
|
+
const url =
|
|
154
|
+
override && override.length ? override : coreProject.repositoryUrl(workspaceRoot, "npm");
|
|
150
155
|
if (!url) return;
|
|
151
|
-
const
|
|
152
|
-
const directory = toPosix(relative(resolve(root.outdir), resolve(project.outdir)));
|
|
156
|
+
const directory = toPosix(relative(workspaceRoot, resolve(project.outdir)));
|
|
153
157
|
project.package.addField("repository", {
|
|
154
158
|
type: "git",
|
|
155
159
|
url,
|
|
@@ -389,9 +393,7 @@ export const DBX_TOOLS_LICENSE = "Apache-2.0";
|
|
|
389
393
|
* inherits the root's config rather than emitting its own. `name`/`defaultReleaseBranch`
|
|
390
394
|
* are resolved/applied by the caller.
|
|
391
395
|
*/
|
|
392
|
-
function defaultProjectOptions(
|
|
393
|
-
options: DBXToolsJavaScriptProjectOptions,
|
|
394
|
-
): DBXToolsJavaScriptProjectOptions {
|
|
396
|
+
function defaultProjectOptions(options: DBXToolsJavaScriptProjectOptions) {
|
|
395
397
|
const isRoot = options.parent === undefined;
|
|
396
398
|
return {
|
|
397
399
|
// Bun owns install/run/build/test locally and in CI. projen renders
|
|
@@ -423,9 +425,7 @@ function defaultProjectOptions(
|
|
|
423
425
|
// to Verdaccio still work without a CI OIDC provider. See
|
|
424
426
|
// {@link DBXToolsRelease}.
|
|
425
427
|
...(isRoot ? {} : { npmAccess: javascript.NpmAccess.PUBLIC }),
|
|
426
|
-
buildWorkflow: false,
|
|
427
428
|
workflowPackageCache: false,
|
|
428
|
-
release: false,
|
|
429
429
|
// The root build validates the whole workspace and must not also pack every
|
|
430
430
|
// member into unused `dist/js` tarballs. Child projects keep projen's package
|
|
431
431
|
// task so `bun run build` in ONE package remains a complete compile/test/pack
|
|
@@ -460,6 +460,8 @@ function defaultProjectOptions(
|
|
|
460
460
|
}
|
|
461
461
|
: {}),
|
|
462
462
|
...options,
|
|
463
|
+
buildWorkflow: false,
|
|
464
|
+
release: false,
|
|
463
465
|
githubOptions: {
|
|
464
466
|
...options.githubOptions,
|
|
465
467
|
pullRequestLint: false,
|
|
@@ -493,9 +495,7 @@ function copiedGitIgnoreOptions(
|
|
|
493
495
|
* same parent-based root/child logic applies; this just layers on typescript +
|
|
494
496
|
* bun types and disables sample code. No `tsx`: bun runs `.ts` directly.
|
|
495
497
|
*/
|
|
496
|
-
function defaultTypeScriptProjectOptions(
|
|
497
|
-
options: DBXToolsTypeScriptProjectOptions,
|
|
498
|
-
): DBXToolsTypeScriptProjectOptions {
|
|
498
|
+
function defaultTypeScriptProjectOptions(options: DBXToolsTypeScriptProjectOptions) {
|
|
499
499
|
const base = defaultProjectOptions(options);
|
|
500
500
|
return {
|
|
501
501
|
...base,
|
|
@@ -513,84 +513,152 @@ function defaultTypeScriptProjectOptions(
|
|
|
513
513
|
// Pinned to match the subproject defaults so bun resolves one TypeScript and
|
|
514
514
|
// one runtime-compatible set of Bun globals across the workspace.
|
|
515
515
|
const DEV_DEPS_ROOT: string[] = ["typescript@^5.9.3", `@types/bun@${BUN_VERSION}`];
|
|
516
|
+
const NATIVE_RELEASE_OPTIONS = [
|
|
517
|
+
"jsiiReleaseVersion",
|
|
518
|
+
"majorVersion",
|
|
519
|
+
"minMajorVersion",
|
|
520
|
+
"npmDistTag",
|
|
521
|
+
"prerelease",
|
|
522
|
+
"publishDryRun",
|
|
523
|
+
"publishTasks",
|
|
524
|
+
"releasableCommits",
|
|
525
|
+
"release",
|
|
526
|
+
"releaseBranches",
|
|
527
|
+
"releaseEnvironment",
|
|
528
|
+
"releaseEveryCommit",
|
|
529
|
+
"releaseFailureIssue",
|
|
530
|
+
"releaseFailureIssueLabel",
|
|
531
|
+
"releaseToNpm",
|
|
532
|
+
"releaseTrigger",
|
|
533
|
+
"releaseWorkflowName",
|
|
534
|
+
"releaseWorkflowSetupSteps",
|
|
535
|
+
"versionrcOptions",
|
|
536
|
+
] as const;
|
|
537
|
+
|
|
538
|
+
function validateReleaseOptions(options: DBXToolsJavaScriptProjectOptions): void {
|
|
539
|
+
for (const key of NATIVE_RELEASE_OPTIONS) {
|
|
540
|
+
if ((options as Record<string, unknown>)[key] !== undefined) {
|
|
541
|
+
throw new Error(
|
|
542
|
+
`${key} is a native Projen release option; use dbx-tools release options instead`,
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
if (
|
|
547
|
+
options.releaseMode === "disabled" &&
|
|
548
|
+
(options.releaseDocs !== undefined || options.nodeRelease !== undefined)
|
|
549
|
+
) {
|
|
550
|
+
throw new Error("releaseMode disabled cannot be combined with releaseDocs or nodeRelease");
|
|
551
|
+
}
|
|
552
|
+
}
|
|
516
553
|
|
|
517
554
|
/** Options for {@link DBXToolsNodeProject} (the monorepo root). */
|
|
518
|
-
export
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
/**
|
|
525
|
-
|
|
526
|
-
* the (resolved) project name; a leading `@` is optional.
|
|
527
|
-
*/
|
|
528
|
-
readonly scope?: string;
|
|
529
|
-
/**
|
|
530
|
-
* Roots scanned for packages (each `src`-bearing folder under a root is one).
|
|
531
|
-
* Only a ROOT scans. Defaults to {@link DEFAULT_PACKAGE_ROOTS}.
|
|
532
|
-
*/
|
|
533
|
-
readonly packageRoots?: readonly string[];
|
|
534
|
-
/**
|
|
535
|
-
* Descriptions for published packages, keyed by repository-relative package
|
|
536
|
-
* directory. When configured, synthesis rejects every public JavaScript
|
|
537
|
-
* package whose final manifest has no non-empty description.
|
|
538
|
-
*/
|
|
539
|
-
readonly packageDescriptions?: Readonly<Record<string, string>>;
|
|
540
|
-
/**
|
|
541
|
-
* Leading path segment(s) dropped from a discovered package's relative path
|
|
542
|
-
* before its npm name is derived, so a tier folder doesn't become a name
|
|
543
|
-
* prefix. E.g. with the default `"node"`, `packages/node/path` names as
|
|
544
|
-
* `@<scope>/path` instead of `@<scope>/node-path` (its `node` TAG still
|
|
545
|
-
* derives from the path). One or many segment names; a segment is only
|
|
546
|
-
* stripped when it is the FIRST segment of the relative path. Pass `[]` to
|
|
547
|
-
* disable. Defaults to `"node"`.
|
|
548
|
-
*/
|
|
549
|
-
readonly omitRelativePrefix?: OneOrMany<string>;
|
|
550
|
-
/**
|
|
551
|
-
* Maps a path token / relPath / glob to tag(s), unioned into a package's
|
|
552
|
-
* path-derived tags. Defaults to an identity map over the known tag names; a
|
|
553
|
-
* `""`/`"."` key tags the root.
|
|
554
|
-
*/
|
|
555
|
-
readonly packageTagPaths?: Record<string, string[]>;
|
|
556
|
-
/**
|
|
557
|
-
* Which built-in {@link PACKAGE_TAG_MIXINS} to apply and seed
|
|
558
|
-
* `packageTagPaths` identity entries for. Omitted = all; `false` = none;
|
|
559
|
-
* a list = only those tags.
|
|
560
|
-
*/
|
|
561
|
-
readonly defaultTagMixins?: false | PackageTag[];
|
|
562
|
-
/**
|
|
563
|
-
* Extra repo-root paths that trigger a full re-synth during `sync --watch`
|
|
564
|
-
* (alongside `.projenrc.ts`). Repo-relative, e.g. `".example.projenrc.ts"`.
|
|
565
|
-
*/
|
|
566
|
-
readonly syncResynthPaths?: readonly string[];
|
|
567
|
-
/** GitHub Pages documentation included in the unified release workflow. */
|
|
568
|
-
readonly releaseDocs?: ReleaseDocsOptions;
|
|
569
|
-
/** Set to `false` to omit normal npm workspace publication. */
|
|
570
|
-
readonly nodeRelease?: boolean;
|
|
571
|
-
/**
|
|
572
|
-
* Extra workspace member paths (repo-relative, POSIX) to list in the workspace
|
|
573
|
-
* config ALONGSIDE the discovered `packageRoots` members - for a package that
|
|
574
|
-
* is synthesized by its OWN `.projenrc.ts` (so it isn't a root subproject) but
|
|
575
|
-
* should still resolve as a workspace sibling. The `@dbx-tools/projen` engine in
|
|
576
|
-
* `projen/` is the case: it synthesizes itself (avoiding a dogfooding cycle) yet
|
|
577
|
-
* is a member of the single bun workspace, so the root links it from source.
|
|
578
|
-
*/
|
|
579
|
-
readonly extraWorkspaceMembers?: readonly string[];
|
|
580
|
-
/**
|
|
581
|
-
* Install workspace dependencies once from the custom root instead of once
|
|
582
|
-
* per child project during post-synthesis. Defaults to `true`; set `false` to
|
|
583
|
-
* preserve projen's native per-project install tasks.
|
|
584
|
-
*/
|
|
585
|
-
readonly rootInstallOnly?: boolean;
|
|
555
|
+
export type DBXToolsReleaseMode = "dbx-tools" | "disabled";
|
|
556
|
+
|
|
557
|
+
/** Optional semantic pull request title policy for the generated build workflow. */
|
|
558
|
+
export interface PullRequestTitlePolicyOptions {
|
|
559
|
+
/** Allowed semantic title types. */
|
|
560
|
+
readonly types: readonly string[];
|
|
561
|
+
/** Require a semantic title scope. Defaults to `false`. */
|
|
562
|
+
readonly requireScope?: boolean;
|
|
586
563
|
}
|
|
587
564
|
|
|
565
|
+
type SupportedWorkflowOptions = Pick<
|
|
566
|
+
ReleaseProjectOptions,
|
|
567
|
+
"postBuildSteps" | "workflowContainerImage" | "workflowRunsOn" | "workflowRunsOnGroup"
|
|
568
|
+
>;
|
|
569
|
+
|
|
570
|
+
export type DBXToolsJavaScriptProjectOptions = CommonProjectOptions &
|
|
571
|
+
Partial<
|
|
572
|
+
Omit<javascript.NodeProjectOptions, keyof ReleaseProjectOptions | "release" | "releaseToNpm">
|
|
573
|
+
> &
|
|
574
|
+
Partial<SupportedWorkflowOptions> &
|
|
575
|
+
DBXToolsConfigOptions &
|
|
576
|
+
DBXToolsPNPMWorkspaceOptions & {
|
|
577
|
+
/**
|
|
578
|
+
* The npm scope for generated package names (`@<scope>/<seg-...>`). Defaults to
|
|
579
|
+
* the (resolved) project name; a leading `@` is optional.
|
|
580
|
+
*/
|
|
581
|
+
readonly scope?: string;
|
|
582
|
+
/**
|
|
583
|
+
* Roots scanned for packages (each `src`-bearing folder under a root is one).
|
|
584
|
+
* Only a ROOT scans. Defaults to {@link DEFAULT_PACKAGE_ROOTS}.
|
|
585
|
+
*/
|
|
586
|
+
readonly packageRoots?: readonly string[];
|
|
587
|
+
/**
|
|
588
|
+
* Descriptions for published packages, keyed by repository-relative package
|
|
589
|
+
* directory. When configured, synthesis rejects every public JavaScript
|
|
590
|
+
* package whose final manifest has no non-empty description.
|
|
591
|
+
*/
|
|
592
|
+
readonly packageDescriptions?: Readonly<Record<string, string>>;
|
|
593
|
+
/**
|
|
594
|
+
* Leading path segment(s) dropped from a discovered package's relative path
|
|
595
|
+
* before its npm name is derived, so a tier folder doesn't become a name
|
|
596
|
+
* prefix. E.g. with the default `"node"`, `packages/node/path` names as
|
|
597
|
+
* `@<scope>/path` instead of `@<scope>/node-path` (its `node` TAG still
|
|
598
|
+
* derives from the path). One or many segment names; a segment is only
|
|
599
|
+
* stripped when it is the FIRST segment of the relative path. Pass `[]` to
|
|
600
|
+
* disable. Defaults to `"node"`.
|
|
601
|
+
*/
|
|
602
|
+
readonly omitRelativePrefix?: OneOrMany<string>;
|
|
603
|
+
/**
|
|
604
|
+
* Maps a path token / relPath / glob to tag(s), unioned into a package's
|
|
605
|
+
* path-derived tags. Defaults to an identity map over the known tag names; a
|
|
606
|
+
* `""`/`"."` key tags the root.
|
|
607
|
+
*/
|
|
608
|
+
readonly packageTagPaths?: Record<string, string[]>;
|
|
609
|
+
/**
|
|
610
|
+
* Which built-in {@link PACKAGE_TAG_MIXINS} to apply and seed
|
|
611
|
+
* `packageTagPaths` identity entries for. Omitted = all; `false` = none;
|
|
612
|
+
* a list = only those tags.
|
|
613
|
+
*/
|
|
614
|
+
readonly defaultTagMixins?: false | PackageTag[];
|
|
615
|
+
/**
|
|
616
|
+
* Extra repo-root paths that trigger a full re-synth during `sync --watch`
|
|
617
|
+
* (alongside `.projenrc.ts`). Repo-relative, e.g. `".example.projenrc.ts"`.
|
|
618
|
+
*/
|
|
619
|
+
readonly syncResynthPaths?: readonly string[];
|
|
620
|
+
/** GitHub Pages documentation included in the unified release workflow. */
|
|
621
|
+
readonly releaseDocs?: ReleaseDocsOptions;
|
|
622
|
+
/** Python package root passed to local release preparation when configured. */
|
|
623
|
+
readonly releasePythonRoot?: string;
|
|
624
|
+
/** Set to `false` to omit normal npm workspace publication. */
|
|
625
|
+
readonly nodeRelease?: boolean;
|
|
626
|
+
/** Unified dbx-tools release workflow, or no release surface. Defaults to `dbx-tools`. */
|
|
627
|
+
readonly releaseMode?: DBXToolsReleaseMode;
|
|
628
|
+
/** Prefix for generated release tags. Defaults to `v`. */
|
|
629
|
+
readonly releaseTagPrefix?: string;
|
|
630
|
+
/** Semantic PR title validation policy. Omitted or `false` disables title validation. */
|
|
631
|
+
readonly pullRequestTitlePolicy?: false | PullRequestTitlePolicyOptions;
|
|
632
|
+
/** Repository output paths excluded from generated workflow dependency-cache hashing. */
|
|
633
|
+
readonly workflowCacheIgnorePaths?: readonly string[];
|
|
634
|
+
/**
|
|
635
|
+
* Extra workspace member paths (repo-relative, POSIX) to list in the workspace
|
|
636
|
+
* config ALONGSIDE the discovered `packageRoots` members - for a package that
|
|
637
|
+
* is synthesized by its OWN `.projenrc.ts` (so it isn't a root subproject) but
|
|
638
|
+
* should still resolve as a workspace sibling. The `@dbx-tools/projen` engine in
|
|
639
|
+
* `projen/` is the case: it synthesizes itself (avoiding a dogfooding cycle) yet
|
|
640
|
+
* is a member of the single bun workspace, so the root links it from source.
|
|
641
|
+
*/
|
|
642
|
+
readonly extraWorkspaceMembers?: readonly string[];
|
|
643
|
+
/**
|
|
644
|
+
* Install workspace dependencies once from the custom root instead of once
|
|
645
|
+
* per child project during post-synthesis. Defaults to `true`; set `false` to
|
|
646
|
+
* preserve projen's native per-project install tasks.
|
|
647
|
+
*/
|
|
648
|
+
readonly rootInstallOnly?: boolean;
|
|
649
|
+
};
|
|
650
|
+
|
|
588
651
|
/** Options for {@link DBXToolsTypeScriptProject} (a package, or a compiling root). */
|
|
589
|
-
export
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
652
|
+
export type DBXToolsTypeScriptProjectOptions = Partial<
|
|
653
|
+
Omit<
|
|
654
|
+
typescript.TypeScriptProjectOptions,
|
|
655
|
+
keyof ReleaseProjectOptions | "release" | "releaseToNpm"
|
|
656
|
+
>
|
|
657
|
+
> &
|
|
658
|
+
DBXToolsJavaScriptProjectOptions & {
|
|
659
|
+
/** Emit the projen-owned bun app scaffolding (`bunfig.toml`/`dev.ts`/`build.ts`). */
|
|
660
|
+
readonly bunApp?: boolean;
|
|
661
|
+
};
|
|
594
662
|
|
|
595
663
|
/**
|
|
596
664
|
* A monorepo root. Scans `packageRoots` and appends a
|
|
@@ -609,14 +677,12 @@ export class DBXToolsNodeProject
|
|
|
609
677
|
vsCode?: DBXToolsVsCode;
|
|
610
678
|
readonly extraWorkspaceMembers: readonly string[];
|
|
611
679
|
readonly releaseBranch: string;
|
|
680
|
+
readonly workflowCacheIgnorePaths: readonly string[];
|
|
612
681
|
private readonly rootInstallOnly: boolean;
|
|
613
682
|
|
|
614
683
|
constructor(options: DBXToolsJavaScriptProjectOptions = {}) {
|
|
684
|
+
validateReleaseOptions(options);
|
|
615
685
|
const { name, scope } = resolveIdentity(options);
|
|
616
|
-
const releaseDefaults =
|
|
617
|
-
options.release && options.releaseTrigger === undefined
|
|
618
|
-
? { releaseTrigger: ReleaseTrigger.tagged({ tags: ["v*"] }) }
|
|
619
|
-
: {};
|
|
620
686
|
// Holds the workspace state (members/catalog/allowBuilds/overrides). Under
|
|
621
687
|
// bun, projen's base constructor does NOT create the `PnpmWorkspaceYaml`
|
|
622
688
|
// component (its `configurePnpm` call site is gated to pnpm), so this state's
|
|
@@ -626,7 +692,6 @@ export class DBXToolsNodeProject
|
|
|
626
692
|
const pnpmWorkspace = new PnpmWorkspaceState(options);
|
|
627
693
|
super({
|
|
628
694
|
...defaultProjectOptions(options),
|
|
629
|
-
...releaseDefaults,
|
|
630
695
|
pnpmOptions: {
|
|
631
696
|
...options.pnpmOptions,
|
|
632
697
|
workspaceYamlOptions: pnpmWorkspace.options,
|
|
@@ -645,8 +710,10 @@ export class DBXToolsNodeProject
|
|
|
645
710
|
this.scope = scope;
|
|
646
711
|
this.extraWorkspaceMembers = options.extraWorkspaceMembers ?? [];
|
|
647
712
|
this.releaseBranch = options.defaultReleaseBranch ?? "main";
|
|
713
|
+
this.workflowCacheIgnorePaths = options.workflowCacheIgnorePaths ?? [];
|
|
648
714
|
this.rootInstallOnly = options.rootInstallOnly !== false;
|
|
649
715
|
this.dbxToolsConfig = new DBXToolsConfig(this, options);
|
|
716
|
+
configureBuildWorkflow(this, options);
|
|
650
717
|
initProject(this, options);
|
|
651
718
|
}
|
|
652
719
|
|
|
@@ -654,7 +721,9 @@ export class DBXToolsNodeProject
|
|
|
654
721
|
const steps = super.renderWorkflowSetup(options);
|
|
655
722
|
if (this.parent) return steps;
|
|
656
723
|
return steps.flatMap((step) => {
|
|
657
|
-
if (step.uses === "oven-sh/setup-bun@v2")
|
|
724
|
+
if (step.uses === "oven-sh/setup-bun@v2") {
|
|
725
|
+
return [...bunCacheRestoreSteps(this, { ignorePaths: this.workflowCacheIgnorePaths })];
|
|
726
|
+
}
|
|
658
727
|
if (step.run === "bun install") {
|
|
659
728
|
return [
|
|
660
729
|
step,
|
|
@@ -669,16 +738,9 @@ export class DBXToolsNodeProject
|
|
|
669
738
|
}
|
|
670
739
|
|
|
671
740
|
public override preSynthesize(): void {
|
|
672
|
-
|
|
741
|
+
prepareRootSynthesis(this, this.rootInstallOnly);
|
|
673
742
|
super.preSynthesize();
|
|
674
|
-
|
|
675
|
-
for (const member of this.extraWorkspaceMembers) {
|
|
676
|
-
syncWorkspaceManifestVersion(join(this.outdir, member, "package.json"), version);
|
|
677
|
-
}
|
|
678
|
-
// Members come from the attached subprojects, which the root's scan appends
|
|
679
|
-
// after construction - so the list is filled here, not in the constructor.
|
|
680
|
-
// `extraWorkspaceMembers` adds self-synthesizing siblings (e.g. `projen/`).
|
|
681
|
-
this.pnpmWorkspace?.resolveMembers(this, this.extraWorkspaceMembers);
|
|
743
|
+
resolveRootWorkspace(this, this.extraWorkspaceMembers);
|
|
682
744
|
preSynthesizeProject(this);
|
|
683
745
|
}
|
|
684
746
|
}
|
|
@@ -726,10 +788,16 @@ export class DBXToolsTypeScriptProject
|
|
|
726
788
|
pnpmWorkspace?: PnpmWorkspaceState;
|
|
727
789
|
rootTsconfig?: DBXToolsRootTsconfig;
|
|
728
790
|
vsCode?: DBXToolsVsCode;
|
|
791
|
+
readonly extraWorkspaceMembers: readonly string[];
|
|
792
|
+
readonly releaseBranch: string;
|
|
793
|
+
readonly workflowCacheIgnorePaths: readonly string[];
|
|
794
|
+
private readonly rootInstallOnly: boolean;
|
|
729
795
|
|
|
730
796
|
constructor(options: DBXToolsTypeScriptProjectOptions) {
|
|
797
|
+
validateReleaseOptions(options);
|
|
731
798
|
const { name, scope } = resolveIdentity(options);
|
|
732
799
|
const parent = options?.parent;
|
|
800
|
+
const pnpmWorkspace = parent ? undefined : new PnpmWorkspaceState(options);
|
|
733
801
|
const packageManager =
|
|
734
802
|
options.packageManager ??
|
|
735
803
|
inheritedPackageManager(parent instanceof javascript.NodeProject ? parent : undefined);
|
|
@@ -738,6 +806,14 @@ export class DBXToolsTypeScriptProject
|
|
|
738
806
|
...defaultTypeScriptProjectOptions(options),
|
|
739
807
|
name: options.name ?? name,
|
|
740
808
|
packageManager,
|
|
809
|
+
...(pnpmWorkspace
|
|
810
|
+
? {
|
|
811
|
+
pnpmOptions: {
|
|
812
|
+
...options.pnpmOptions,
|
|
813
|
+
workspaceYamlOptions: pnpmWorkspace.options,
|
|
814
|
+
},
|
|
815
|
+
}
|
|
816
|
+
: {}),
|
|
741
817
|
tsconfig: {
|
|
742
818
|
...options.tsconfig,
|
|
743
819
|
include: options.tsconfig?.include,
|
|
@@ -750,7 +826,13 @@ export class DBXToolsTypeScriptProject
|
|
|
750
826
|
},
|
|
751
827
|
},
|
|
752
828
|
});
|
|
829
|
+
this.pnpmWorkspace = pnpmWorkspace;
|
|
830
|
+
pnpmWorkspace?.attachWorkspaceFile(this);
|
|
753
831
|
this.scope = scope;
|
|
832
|
+
this.extraWorkspaceMembers = options.extraWorkspaceMembers ?? [];
|
|
833
|
+
this.releaseBranch = options.defaultReleaseBranch ?? "main";
|
|
834
|
+
this.workflowCacheIgnorePaths = options.workflowCacheIgnorePaths ?? [];
|
|
835
|
+
this.rootInstallOnly = options.rootInstallOnly !== false;
|
|
754
836
|
// Pairs with `jsx` in SHARED_COMPILER_OPTIONS: projen's default `include` is
|
|
755
837
|
// `src/**/*.ts` only, which silently omits a `.tsx` file from the program
|
|
756
838
|
// instead of failing, so authoring a React component would otherwise need
|
|
@@ -772,27 +854,106 @@ export class DBXToolsTypeScriptProject
|
|
|
772
854
|
this.package.addField("version", readWorkspaceVersion(this.root.outdir));
|
|
773
855
|
addPackageFiles(this, "index.ts", "src");
|
|
774
856
|
// `bun test` intercepts `node:test` (the suites keep using node:test) and
|
|
775
|
-
// runs it with bun's own fast runner.
|
|
776
|
-
//
|
|
777
|
-
|
|
778
|
-
// exists, else succeed.
|
|
779
|
-
this.testTask.exec("bun test test", {
|
|
780
|
-
condition: 'find test -name "*.test.ts" 2>/dev/null | grep -q .',
|
|
781
|
-
});
|
|
857
|
+
// runs it with bun's own fast runner. The native no-tests option is portable
|
|
858
|
+
// across Windows and Unix and lets packages without tests remain a no-op.
|
|
859
|
+
this.testTask.exec("bun test test --pass-with-no-tests");
|
|
782
860
|
if (options.bunApp ?? false) {
|
|
783
861
|
new BunfigFile(this);
|
|
784
862
|
new BunDevServerFile(this);
|
|
785
863
|
new BunBuildFile(this);
|
|
786
864
|
}
|
|
865
|
+
configureBuildWorkflow(this, options);
|
|
787
866
|
initProject(this, options);
|
|
788
867
|
}
|
|
789
868
|
|
|
790
869
|
public override preSynthesize(): void {
|
|
870
|
+
prepareRootSynthesis(this, this.rootInstallOnly);
|
|
791
871
|
super.preSynthesize();
|
|
872
|
+
resolveRootWorkspace(this, this.extraWorkspaceMembers);
|
|
792
873
|
preSynthesizeProject(this);
|
|
793
874
|
}
|
|
794
875
|
}
|
|
795
876
|
|
|
877
|
+
function configureBuildWorkflow(
|
|
878
|
+
project: DBXToolsNodeProject | DBXToolsTypeScriptProject,
|
|
879
|
+
options: DBXToolsJavaScriptProjectOptions,
|
|
880
|
+
): void {
|
|
881
|
+
if (project.parent || !options.buildWorkflow || !project.github) return;
|
|
882
|
+
const validation = project.addTask("pr:validate", {
|
|
883
|
+
description: "Validate generated files and TypeScript packages",
|
|
884
|
+
});
|
|
885
|
+
validation.exec("bunx projen default");
|
|
886
|
+
validation.exec("bun run compile");
|
|
887
|
+
const configured = options.buildWorkflowOptions ?? {};
|
|
888
|
+
const compatibility = nodeWorkflowCompatibility(project);
|
|
889
|
+
const configuredRunner = configured.runsOn !== undefined || configured.runsOnGroup !== undefined;
|
|
890
|
+
const workflow = new BuildWorkflow(project, {
|
|
891
|
+
buildTask: validation,
|
|
892
|
+
artifactsDirectory: project.artifactsDirectory,
|
|
893
|
+
containerImage: options.workflowContainerImage,
|
|
894
|
+
gitIdentity: options.workflowGitIdentity,
|
|
895
|
+
permissions: { idToken: compatibility.determineIdTokenPermissions(options) },
|
|
896
|
+
...configured,
|
|
897
|
+
preBuildSteps: [
|
|
898
|
+
...project.renderWorkflowSetup({
|
|
899
|
+
installStepConfiguration: {
|
|
900
|
+
workingDirectory: compatibility.determineInstallWorkingDirectory(),
|
|
901
|
+
},
|
|
902
|
+
mutable: configured.mutableInstall ?? configured.mutableBuild ?? true,
|
|
903
|
+
}),
|
|
904
|
+
...(configured.preBuildSteps ?? []),
|
|
905
|
+
],
|
|
906
|
+
postBuildSteps: [...(options.postBuildSteps ?? [])],
|
|
907
|
+
runsOn: configuredRunner ? configured.runsOn : options.workflowRunsOn,
|
|
908
|
+
runsOnGroup: configuredRunner ? configured.runsOnGroup : options.workflowRunsOnGroup,
|
|
909
|
+
});
|
|
910
|
+
workflow.addPostBuildSteps(...compatibility.renderUploadCoverageJobStep(options));
|
|
911
|
+
compatibility.buildWorkflow = workflow;
|
|
912
|
+
|
|
913
|
+
if ((options.autoMerge ?? true) && project.github.mergify) {
|
|
914
|
+
const autoMerge = new AutoMerge(project.github, options.autoMergeOptions);
|
|
915
|
+
autoMerge.addConditionsLater({
|
|
916
|
+
render: () => workflow.buildJobIds.map((id) => `status-success=${id}`),
|
|
917
|
+
});
|
|
918
|
+
compatibility.autoMerge = autoMerge;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
interface NodeWorkflowCompatibility {
|
|
923
|
+
autoMerge?: AutoMerge;
|
|
924
|
+
buildWorkflow?: BuildWorkflow;
|
|
925
|
+
determineIdTokenPermissions(options: DBXToolsJavaScriptProjectOptions): JobPermission | undefined;
|
|
926
|
+
determineInstallWorkingDirectory(): string | undefined;
|
|
927
|
+
renderUploadCoverageJobStep(options: DBXToolsJavaScriptProjectOptions): JobStep[];
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
/**
|
|
931
|
+
* Access the narrow NodeProject workflow hooks that have no public equivalent.
|
|
932
|
+
* BuildWorkflow itself is created exclusively through its public constructor.
|
|
933
|
+
*/
|
|
934
|
+
function nodeWorkflowCompatibility(project: javascript.NodeProject): NodeWorkflowCompatibility {
|
|
935
|
+
return project as unknown as NodeWorkflowCompatibility;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
function prepareRootSynthesis(
|
|
939
|
+
project: DBXToolsNodeProject | DBXToolsTypeScriptProject,
|
|
940
|
+
rootInstallOnly: boolean,
|
|
941
|
+
): void {
|
|
942
|
+
if (!project.parent && rootInstallOnly) project.with(ROOT_INSTALL_ONLY_MIXIN);
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
function resolveRootWorkspace(
|
|
946
|
+
project: DBXToolsNodeProject | DBXToolsTypeScriptProject,
|
|
947
|
+
extraWorkspaceMembers: readonly string[],
|
|
948
|
+
): void {
|
|
949
|
+
if (project.parent) return;
|
|
950
|
+
const version = readWorkspaceVersion(project.outdir);
|
|
951
|
+
for (const member of extraWorkspaceMembers) {
|
|
952
|
+
syncWorkspaceManifestVersion(join(project.outdir, member, "package.json"), version);
|
|
953
|
+
}
|
|
954
|
+
project.pnpmWorkspace?.resolveMembers(project, extraWorkspaceMembers);
|
|
955
|
+
}
|
|
956
|
+
|
|
796
957
|
/**
|
|
797
958
|
* Regenerates the repo's generated source after synth: first the codegen
|
|
798
959
|
* modules (ts-to-zod schemas from each `codegen`-declaring package's upstream
|
|
@@ -808,8 +969,10 @@ export class DBXToolsTypeScriptProject
|
|
|
808
969
|
*/
|
|
809
970
|
class GeneratedSource extends Component {
|
|
810
971
|
public override postSynthesize(): void {
|
|
811
|
-
|
|
812
|
-
|
|
972
|
+
const projectRoot = this.project.outdir;
|
|
973
|
+
const includeRoot = this.project instanceof DBXToolsTypeScriptProject;
|
|
974
|
+
generateCodegen(projectRoot, { includeRoot });
|
|
975
|
+
generateBarrels({ projectRoot, includeRoot });
|
|
813
976
|
}
|
|
814
977
|
}
|
|
815
978
|
|
|
@@ -837,7 +1000,10 @@ class WorkspaceValidationTasks extends Component {
|
|
|
837
1000
|
public override preSynthesize(): void {
|
|
838
1001
|
if (this.configured) return;
|
|
839
1002
|
this.configured = true;
|
|
840
|
-
const project = this.project as
|
|
1003
|
+
const project = this.project as DBXToolsNodeProject | DBXToolsTypeScriptProject;
|
|
1004
|
+
if (project.subprojects.length === 0 && project.extraWorkspaceMembers.length === 0) {
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
841
1007
|
for (const task of [project.compileTask, project.testTask]) {
|
|
842
1008
|
task.exec(`bun run --filter '*' ${task.name}`);
|
|
843
1009
|
}
|
|
@@ -852,29 +1018,17 @@ class WorkspaceValidationTasks extends Component {
|
|
|
852
1018
|
* off) do not gain new files.
|
|
853
1019
|
*/
|
|
854
1020
|
class WorkflowDefaults extends Component {
|
|
1021
|
+
constructor(
|
|
1022
|
+
project: javascript.NodeProject,
|
|
1023
|
+
private readonly pullRequestTitlePolicy: false | PullRequestTitlePolicyOptions | undefined,
|
|
1024
|
+
) {
|
|
1025
|
+
super(project);
|
|
1026
|
+
}
|
|
1027
|
+
|
|
855
1028
|
public override preSynthesize(): void {
|
|
856
1029
|
const project = this.project as javascript.NodeProject;
|
|
857
1030
|
const workflow = project.buildWorkflow?.workflow;
|
|
858
|
-
if (workflow) {
|
|
859
|
-
const job = workflow.getJob("build");
|
|
860
|
-
if ("steps" in job) {
|
|
861
|
-
const mutableJob = job as unknown as { steps: JobStep[] | (() => JobStep[]) };
|
|
862
|
-
const configuredSteps = mutableJob.steps;
|
|
863
|
-
const rewrite = (steps: JobStep[]): JobStep[] =>
|
|
864
|
-
steps.map((step) =>
|
|
865
|
-
step.name === "build"
|
|
866
|
-
? {
|
|
867
|
-
...step,
|
|
868
|
-
name: "Validate generated files and types",
|
|
869
|
-
run: "bunx projen default\nbun run compile",
|
|
870
|
-
}
|
|
871
|
-
: step,
|
|
872
|
-
);
|
|
873
|
-
mutableJob.steps =
|
|
874
|
-
typeof configuredSteps === "function"
|
|
875
|
-
? () => rewrite(configuredSteps())
|
|
876
|
-
: rewrite(configuredSteps);
|
|
877
|
-
}
|
|
1031
|
+
if (workflow && this.pullRequestTitlePolicy) {
|
|
878
1032
|
workflow.addJob("pr-title", {
|
|
879
1033
|
name: "Validate PR title",
|
|
880
1034
|
runsOn: ["ubuntu-latest"],
|
|
@@ -886,8 +1040,8 @@ class WorkflowDefaults extends Component {
|
|
|
886
1040
|
uses: "amannn/action-semantic-pull-request@v6",
|
|
887
1041
|
env: { GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" },
|
|
888
1042
|
with: {
|
|
889
|
-
types:
|
|
890
|
-
requireScope: false,
|
|
1043
|
+
types: this.pullRequestTitlePolicy.types.join("\n"),
|
|
1044
|
+
requireScope: this.pullRequestTitlePolicy.requireScope ?? false,
|
|
891
1045
|
},
|
|
892
1046
|
},
|
|
893
1047
|
],
|
|
@@ -995,11 +1149,19 @@ function packageNameFor(scope: string, relPath: string, omitPrefixes: string[]):
|
|
|
995
1149
|
* either way it is parsed through {@link PackageIdentifier} so a scoped value
|
|
996
1150
|
* (`@dbx-tools` or a full `@dbx-tools/root` name) yields the bare scope `dbx-tools`.
|
|
997
1151
|
*/
|
|
998
|
-
function resolveIdentity(options: {
|
|
1152
|
+
function resolveIdentity(options: {
|
|
1153
|
+
name?: string;
|
|
1154
|
+
scope?: string;
|
|
1155
|
+
outdir?: string;
|
|
1156
|
+
parent?: Project;
|
|
1157
|
+
}): {
|
|
999
1158
|
name: string;
|
|
1000
1159
|
scope: string;
|
|
1001
1160
|
} {
|
|
1002
|
-
const
|
|
1161
|
+
const projectRoot = options.parent
|
|
1162
|
+
? resolve(options.parent.outdir, options.outdir ?? ".")
|
|
1163
|
+
: resolve(options.outdir ?? process.cwd());
|
|
1164
|
+
const name = options.name && options.name.length ? options.name : projectName(projectRoot);
|
|
1003
1165
|
const rawScope = options.scope && options.scope.length ? options.scope : name;
|
|
1004
1166
|
const identifier = PackageIdentifier.parse(rawScope);
|
|
1005
1167
|
return { name, scope: identifier?.scope ?? identifier?.name ?? rawScope };
|
|
@@ -1227,9 +1389,7 @@ function initProject(
|
|
|
1227
1389
|
project.annotateGenerated(`/${root}/**/index.ts`);
|
|
1228
1390
|
project.annotateGenerated(`/${root}/openapi/**`);
|
|
1229
1391
|
}
|
|
1230
|
-
const extraWorkspaceMembers
|
|
1231
|
-
project instanceof DBXToolsNodeProject ? project.extraWorkspaceMembers : [];
|
|
1232
|
-
for (const member of extraWorkspaceMembers) {
|
|
1392
|
+
for (const member of project.extraWorkspaceMembers) {
|
|
1233
1393
|
project.annotateGenerated(`/${member}/index.ts`);
|
|
1234
1394
|
}
|
|
1235
1395
|
|
|
@@ -1241,13 +1401,17 @@ function initProject(
|
|
|
1241
1401
|
// repairs the worktree it is meant to validate; `eslint:fix` is the explicit
|
|
1242
1402
|
// local mutation path.
|
|
1243
1403
|
const eslint = new javascript.Eslint(project, {
|
|
1244
|
-
dirs: [...roots,
|
|
1404
|
+
dirs: [...roots, ...project.extraWorkspaceMembers],
|
|
1245
1405
|
fileExtensions: [".ts", ".tsx"],
|
|
1246
1406
|
projectService: true,
|
|
1247
1407
|
prettier: Boolean(project.prettier),
|
|
1248
|
-
tsconfigPath:
|
|
1408
|
+
tsconfigPath:
|
|
1409
|
+
project instanceof typescript.TypeScriptProject
|
|
1410
|
+
? (project.tsconfig?.fileName ?? "./tsconfig.json")
|
|
1411
|
+
: "./tsconfig.json",
|
|
1249
1412
|
commandOptions: { fix: false },
|
|
1250
1413
|
});
|
|
1414
|
+
eslint.allowDefaultProjectFiles(".projenrc.ts");
|
|
1251
1415
|
project.addTask("eslint:fix", {
|
|
1252
1416
|
description: "Fix ESLint issues across the codebase",
|
|
1253
1417
|
exec: "bun run eslint -- --fix",
|
|
@@ -1260,7 +1424,9 @@ function initProject(
|
|
|
1260
1424
|
eslint.addIgnorePattern(`${root}/**/index.ts`);
|
|
1261
1425
|
}
|
|
1262
1426
|
eslint.addIgnorePattern("**/src/generated/**");
|
|
1263
|
-
|
|
1427
|
+
for (const member of project.extraWorkspaceMembers) {
|
|
1428
|
+
eslint.addIgnorePattern(`${member}/index.ts`);
|
|
1429
|
+
}
|
|
1264
1430
|
// The generated bun app scripts + unmanaged overrides live at the package root,
|
|
1265
1431
|
// outside any `src/**` tsconfig include, so the type-aware parser cannot resolve
|
|
1266
1432
|
// them to a project. ESLint still cannot parse them.
|
|
@@ -1362,17 +1528,20 @@ function initProject(
|
|
|
1362
1528
|
}
|
|
1363
1529
|
|
|
1364
1530
|
new WorkspaceValidationTasks(project);
|
|
1365
|
-
new WorkflowDefaults(project);
|
|
1531
|
+
new WorkflowDefaults(project, options.pullRequestTitlePolicy);
|
|
1366
1532
|
new PrettierIgnoreGenerated(project);
|
|
1367
1533
|
|
|
1368
1534
|
new GeneratedSource(project);
|
|
1369
1535
|
// Version mutation, reviewed release preparation, and public publication are
|
|
1370
1536
|
// separate surfaces owned by one release component.
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1537
|
+
if (options.releaseMode !== "disabled") {
|
|
1538
|
+
new DBXToolsRelease(project, {
|
|
1539
|
+
tagPrefix: options.releaseTagPrefix,
|
|
1540
|
+
nodeRelease: options.nodeRelease,
|
|
1541
|
+
docs: options.releaseDocs,
|
|
1542
|
+
pythonRoot: options.releasePythonRoot,
|
|
1543
|
+
});
|
|
1544
|
+
}
|
|
1376
1545
|
}
|
|
1377
1546
|
|
|
1378
1547
|
/**
|