@xpertss/projen-types 0.0.27 → 0.0.28

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.
Files changed (42) hide show
  1. package/.jsii +105 -85
  2. package/API.md +120 -15
  3. package/README.md +9 -8
  4. package/lib/actions/action-build-workflow.js +1 -1
  5. package/lib/actions/action-dogfood-workflow.js +1 -1
  6. package/lib/actions/action-sonar-workflow.js +1 -1
  7. package/lib/actions/github-action-project.js +1 -1
  8. package/lib/cdk/app-runtime-scaffold.js +1 -1
  9. package/lib/cdk/cdk-app-project.js +1 -1
  10. package/lib/cdk/cdk-infra-project.js +1 -1
  11. package/lib/cdk/cdk-typescript-base.js +1 -1
  12. package/lib/cdk/components/ecr-ecs-constructs.js +1 -1
  13. package/lib/cdk/components/edge-networking-constructs.js +1 -1
  14. package/lib/cdk/database-component.js +1 -1
  15. package/lib/common/code-options.d.ts +14 -0
  16. package/lib/common/code-options.js +3 -0
  17. package/lib/common/internal-actions.d.ts +1 -1
  18. package/lib/common/internal-actions.js +2 -2
  19. package/lib/common/projen-drift-check-workflow.js +1 -1
  20. package/lib/common/sonar-workflow.js +1 -1
  21. package/lib/common/workflow-change-notice-workflow.js +1 -1
  22. package/lib/index.d.ts +1 -0
  23. package/lib/index.js +2 -1
  24. package/lib/java/components/cdk-deploy-hook.js +1 -1
  25. package/lib/java/components/code-index-workflow.d.ts +1 -1
  26. package/lib/java/components/code-index-workflow.js +18 -5
  27. package/lib/java/components/docker-publish.js +1 -1
  28. package/lib/java/components/flyway-migration.js +1 -1
  29. package/lib/java/components/github-packages-publish.js +1 -1
  30. package/lib/java/components/maven-central-publish.js +1 -1
  31. package/lib/java/components/maven-upgrade-report.js +1 -1
  32. package/lib/java/java-app-project.js +1 -1
  33. package/lib/java/java-library-project.js +2 -6
  34. package/lib/java/java-maven-base.d.ts +2 -2
  35. package/lib/java/java-maven-base.js +8 -4
  36. package/lib/java/java-service-project.js +1 -1
  37. package/lib/java/java-spring-boot-project.js +1 -1
  38. package/lib/java/maven/maven-module.js +1 -1
  39. package/lib/java/maven/maven-pom.js +1 -1
  40. package/lib/java/options.d.ts +2 -3
  41. package/lib/java/options.js +1 -1
  42. package/package.json +1 -1
package/.jsii CHANGED
@@ -106,7 +106,7 @@
106
106
  },
107
107
  "name": "@xpertss/projen-types",
108
108
  "readme": {
109
- "markdown": "# @xpertss/projen-types\n\nProjen project types for CDK/TypeScript and Java/Maven projects.\n\nInstead of hand-maintaining `pom.xml`, `cdk.json`, and GitHub workflows, you declare a project type in a `.projenrc.ts` file and let [projen](https://github.com/projen/projen) generate (and keep up to date) the whole scaffold: build files, source skeletons, CI workflows, and deploy pipelines.\n\n## Project types at a glance\n\n| Type | What it is | Publishes to | Generated workflows |\n| --- | --- | --- | --- |\n| `CdkInfraProject` | Pure-infrastructure CDK stacks (CloudFront, Route53, SQS, API Gateway, Cognito, ECR/ECS for externally-built images) | - | `build` (PR checks), `deploy` (manual dispatch) |\n| `CdkAppProject` | Full TypeScript service behind API Gateway: infra + app source + database | - | `build`, `deploy`, `app-build` (PR checks) |\n| `JavaMavenProject` | Plain Maven project, single- or multi-module, no framework | - | `build`, `upgrade` (nightly report) |\n| `JavaLibraryProject` | Reusable Java library | Maven Central | `build`, `upgrade` (nightly report), `publish-maven-central`, `codeindex` |\n| `JavaAppProject` | GUI/TUI/CLI Java application | GitHub Packages | `build`, `upgrade` (nightly report), `publish-ghpackages` |\n| `JavaSpringBootProject` | Spring Boot on Maven, single- or multi-module, **no Docker** | - | `build`, `upgrade` (nightly report) |\n| `JavaServiceProject` | Spring Boot service deployed as a container | Docker Hub | `build`, `upgrade` (nightly report), `publish-docker`, `deploy-cdk` |\n| `GitHubActionProject` | Reusable GitHub Action or Workflow | GitHub Releases | `build`, `test-dogfood`, `sonar`, `release` |\n\nThe Java types are layered, so pick the lowest layer that does what you need:\n\n```text\nJavaMavenProject java_maven Maven only; single- or multi-module; any supported Java line\n├── JavaLibraryProject java_library + Maven Central publish, source/javadoc jars, code index\n├── JavaAppProject java_app + GitHub Packages publish\n└── JavaSpringBootProject java_spring_boot + Spring Boot BOM and executable-jar repackaging; no Docker/Flyway/CDK\n └── JavaServiceProject java_service + Docker publish, Flyway, CDK deploy hook (single-module)\n```\n\nEvery Java type targets a configurable Java line (`javaVersion`: `1.8`, `17`, `21` or `25`; see [Java version support](#java-version-support)), and every one except `JavaServiceProject` can be a multi-module Maven reactor (see [JavaMavenProject](#javamavenproject)).\n\nOne foundation class is also exported for advanced use: `CdkTypescriptProject` (shared CDK + TypeScript base for the CDK types).\n\nAll project types:\n\n- run a **drift check** in PR builds - a job that re-runs projen and fails if generated files were hand-edited. Edit `.projenrc.ts`, then run `npx projen`; never edit generated files directly.\n- use the GitHub secret `PROJEN_GITHUB_TOKEN` (a fine-grained PAT) for projen's automation. Override with `gheTokenSecret`.\n- make all publishing/deploying **manual** (workflow_dispatch) rather than on every merge.\n- offer an opt-in SonarCloud scan (`sonar.yml`) on any type via `sonarHostUrl` (see [SonarCloud (opt-in)](#sonarcloud-opt-in)).\n\n## Getting started\n\nHow you start depends on what is already in the directory:\n\n| Starting point | Do this |\n| --- | --- |\n| Empty directory | `projen new --from` (below) |\n| Existing repo, no `.projenrc.ts` | `projen new --from ... --no-git` (below) |\n| A `.projenrc.ts` but no `.projen/` directory - copied from another repo, or one of the [examples](#examples) pasted into a fresh repo | [Starting from an existing `.projenrc.ts`](#starting-from-an-existing-projenrcts) |\n\nScaffold the repo with projen's own bootstrap, pointed at this package:\n\n```bash\nmkdir my-project && cd my-project\ngit init\nnpx projen new --from @xpertss/projen-types cdk_infra --name my-project\n```\n\nThat writes a starter `.projenrc.ts`, synthesizes the whole scaffold and\ninstalls dependencies. The type names `projen new` accepts are `cdk_infra`,\n`cdk_app`, `java_maven`, `java_library`, `java_app`, `java_spring_boot`,\n`java_service` and `git_hub_action`; pass a bogus one to have it list them.\nRequired options become flags: `--name` for every type, plus\n`--group-id`/`--artifact-id` (Java). Any other plainly-typed option can be\npassed the same way - `--sonar-host-url` (opt-in Sonar, all types),\n`--java-version 1.8`, `--cdk-deploy-target-repo owner/repo`,\n`--docker-registry ghcr.io`, `--no-use-flyway`, and so on. Modules can't be\npassed on the command line; add them to `.projenrc.ts` afterwards (see\n[JavaMavenProject](#javamavenproject)).\n\n**Adding projen to a repo that already has content.** `projen new` runs\n`git init` and commits the scaffold by default. In a repo with existing work,\npass `--no-git` so it writes the files and leaves committing to you:\n\n```bash\ncd existing-repo\nnpx projen new --from @xpertss/projen-types java_spring_boot --name obeya --group-id org.xpertss.obeya --artifact-id obeya-parent --no-git\n```\n\n**Commit a lockfile.** The generated `build.yml` and drift check install the\ntoolchain with `npm ci`, which needs a committed `package-lock.json`, and\n`projen new` doesn't always leave one behind. Run `npm install` once and\ncommit `package-lock.json` along with the scaffold. Without it those\nworkflows fail with an `::error::` that says exactly this. For the Java\ntypes, `package.json` is generated, and it pins `projen` and\n`@xpertss/projen-types` to **exact** versions (no `^`), so every machine and\nCI run uses the same generator. A floating range would make the drift check\nreport the differences between generator versions as drift.\n\nCommit the result. From then on, every change to the scaffold goes through\n`.projenrc.ts` followed by `npx projen`:\n\n```bash\nnpx projen\n```\n\nEvery type scaffolds from that one command; no option is *required* that\n`projen new` cannot pass. The structured options - `environments` and\n`GitHubActionProject`'s `dogfood` - are ones projen's CLI cannot render\ninto a projenrc, so they are added afterwards by editing `.projenrc.ts` and\nre-running `npx projen`. Leaving `environments` out simply generates no\ndeploy workflow; leaving `dogfood` out (or a service's\n`cdkDeployTargetRepo`) still generates the workflow, with one step that\nfails and tells you what to add - a gate this package considers load-bearing\nis allowed to be missing loudly, never silently.\n\nThe `name` option must match the `name` field in the project's `package.json` (for the CDK types). For the Java types it becomes the root pom's `<name>` and the generated `package.json` name.\n\n> **Never write projen's generated-file marker text literally in `.projenrc.ts`.** projen deletes, as an orphaned generated file, any file containing the marker line (`~~ Generated by projen. To modify, ...`), and that includes `.projenrc.ts` itself. When you generate a file yourself (for example with `TextFile`), insert the marker through the file's `marker` property: ``file.addLine(`# ${file.marker}`)``.\n\n## Starting from an existing `.projenrc.ts`\n\nA directory that has a `.projenrc.ts` but no `.projen/` directory has never\nbeen synthesized. This happens when you copy the rc from another repo, or paste\none of the [examples](#examples) into a fresh repo. In that state `npx projen`\nfails with:\n\n```\n👾 Unable to find projen project. Use \"projen new\" to create a new project.\n```\n\n`npx projen` doesn't run `.projenrc.ts` itself. It runs the `default` task\nlisted in `.projen/tasks.json`, and that file is written by the first synth.\nDo the first synth one of these two ways. After that, `npx projen` works as\nusual.\n\n**Option 1 (recommended): run `projen new` over the existing rc.** `projen\nnew` leaves an existing `.projenrc.ts` untouched and writes the rest of the\nscaffold, `.projen/` included. Pass the type and its required flags as you\nwould for a new repo. Their values don't have to match your rc, because the\n`npx projen` that follows re-synthesizes everything from the rc:\n\n```bash\nnpx projen new --from @xpertss/projen-types git_hub_action --name pull-request --sonar-host-url https://sonarcloud.io --no-git\nnpx projen\n```\n\nThe first command's output reflects only the flags. For example, a\n`GitHubActionProject`'s `test-dogfood.yml` is still the failing placeholder.\nThe second command applies everything in the rc (`dogfood`, `environments`,\nmodules, and so on).\n\n**Option 2 (Java types and `GitHubActionProject`): run the rc directly,\nonce.** The CDK types run their rc differently, so for those use option 1.\nInstall what the rc imports, give\nts-node a placeholder `tsconfig.projen.json` (the synth overwrites it with the\ngenerated one), and run the same command the generated `default` task runs:\n\n```bash\nnpm i -D projen @xpertss/projen-types constructs\necho '{}' > tsconfig.projen.json\nnpx -y -p ts-node@10.9.2 -p typescript@6.0.3 ts-node --project tsconfig.projen.json .projenrc.ts\nnpx projen\n```\n\nThe `echo` line works in bash and PowerShell. Don't drop `--project`: without\na tsconfig, ts-node fails on Node 22+ with `Unknown file extension \".ts\"`.\n\nEither way, commit `.projen/` along with the rest of the scaffold. A fresh\nclone then has its `tasks.json`, and `npx projen` works there straight away.\n\n## Updating an existing project when this package changes\n\nYour generated scaffold reflects the *installed* version of `@xpertss/projen-types` - `npx projen` reads your project type from the package in `node_modules`, not from this repository. So when this package ships a change (a bug fix, a new generated file, or altered workflow behavior), an existing project picks it up by bumping the dependency and re-synthesizing. A stale `node_modules` silently regenerates with the old behavior, so the bump is the load-bearing step.\n\nUsing the [`auto-commit` action](#githubactionproject) as a running example, say a new release adds the `# Purpose:` workflow header and SonarCloud wording. In the `auto-commit` repo:\n\n```bash\nnpm view @xpertss/projen-types version # what's the newest release?\nnpm i -D @xpertss/projen-types@latest # bump the installed project type\nnpx projen # re-synthesize every generated file\ngit diff # review before committing\n```\n\nThe diff here is the workflows gaining a `# Purpose:` comment and `sonar.yml` reflecting the SonarCloud wording. Commit the result with a normal `chore:` or `fix:` message - you never hand-edit the generated files themselves.\n\n## Examples\n\nEach example shows the `projen new` command first, then a fuller\n`.projenrc.ts`. Run the command first, then replace the generated\n`.projenrc.ts` with the example (or merge it in) and run `npx projen`. If you\npasted the example into a fresh repo first instead, see\n[Starting from an existing `.projenrc.ts`](#starting-from-an-existing-projenrcts).\n\n### CdkInfraProject\n\nPure infrastructure stacks with optional ECR/ECS and edge-networking constructs.\n\nScaffold it with the `cdk_infra` type:\n\n```bash\nnpx projen new --from @xpertss/projen-types cdk_infra --name media-edge-infra\n```\n\n```typescript\n// .projenrc.ts\nimport { CdkInfraProject } from '@xpertss/projen-types';\n\nconst project = new CdkInfraProject({\n name: 'media-edge-infra',\n environments: [\n 'dev',\n { name: 'stage', accountId: '111111111111', region: 'eu-central-1' },\n { name: 'prod', accountId: '222222222222', region: 'eu-central-1', requiresApproval: true },\n ],\n edgeResources: ['cloudfront', 'route53', 'sqs'],\n ecrEcs: { enabled: true, externalImageSource: true },\n});\n\nproject.synth();\n```\n\nYou get:\n\n- `cdk.json`, `cdk synth` / `cdk diff` / `cdk deploy` tasks, and the standard `AwsCdkTypeScriptApp` layout (CDK 2.189.1 by default).\n- `.github/workflows/build.yml` - PR build that hard-fails on projen drift.\n- `.github/workflows/deploy.yml` - manual dispatch with one `deploy-<env>` job per environment (runs `cdk deploy --all` with `--context environment=<env>`); `requiresApproval` environments get a GitHub Environment approval gate.\n- `LICENSE` (MIT by default), `.editorconfig`, and the projen drift check.\n- `src/constructs/edge-networking.ts` - helper constructs only for the requested `edgeResources` (`cloudfront`, `route53`, `apigateway`, `cognito`, `sqs`).\n- `src/constructs/ecr-ecs.ts` when `ecrEcs.enabled` - ECR repo + Fargate service; `externalImageSource: true` (default) means the service pulls an image built outside this repo.\n\n### CdkAppProject\n\n`CdkInfraProject` plus application source, a database construct, and an app-level build workflow.\n\nScaffold it with the `cdk_app` type:\n\n```bash\nnpx projen new --from @xpertss/projen-types cdk_app --name video-api\n```\n\n```typescript\n// .projenrc.ts\nimport { CdkAppProject } from '@xpertss/projen-types';\n\nconst project = new CdkAppProject({\n name: 'video-api',\n environments: ['dev', 'prod'],\n edgeResources: ['apigateway'],\n database: { engine: 'postgres', migrationTool: 'flyway' },\n appEntryPoint: 'src/app.ts',\n});\n\nproject.synth();\n```\n\nEverything from `CdkInfraProject`, plus:\n\n- `src/app.ts` - application entrypoint stub (`export function handler()`).\n- `src/handlers/example.ts` - API Gateway proxy handler stub, with `@types/aws-lambda` added as a dev dependency.\n- `src/constructs/database.ts` - a `Database` construct stub for the chosen engine (`postgres`/`mysql` -> RDS, `dynamodb` -> DynamoDB). `migrationTool` (no default, intentionally) is added as a dev dependency and referenced in the stub - wiring it up is left to you.\n- `.github/workflows/app-build.yml` - runs the project's test task on every PR, then checks for projen drift.\n\n### JavaMavenProject\n\nA plain Maven build with no framework and no publish target. It's the base of every other Java type, and it's usable on its own. It's single-module until you add a module, then it's a multi-module reactor.\n\nScaffold it with the `java_maven` type:\n\n```bash\nnpx projen new --from @xpertss/projen-types java_maven --name legacy-tools --group-id org.xpertss --artifact-id legacy-tools --java-version 1.8\n```\n\n**Single module:**\n\n```typescript\n// .projenrc.ts\nimport { JavaMavenProject } from '@xpertss/projen-types';\n\nconst project = new JavaMavenProject({\n name: 'legacy-tools',\n groupId: 'org.xpertss',\n artifactId: 'legacy-tools',\n javaVersion: '1.8',\n});\n\nproject.addDependency('commons-io/commons-io@2.20.0'); // exact version\nproject.addTestDependency('org.assertj/assertj-core@3.27.3');\n\nproject.synth();\n```\n\n**Multi-module:** each `addModule()` call adds a module directory with its own generated `pom.xml`. The root pom becomes the `pom`-packaged reactor parent. Everything stays one projen project, with one `.projen/`, one `.gitignore` and one `tasks.json`.\n\n```typescript\n// .projenrc.ts\nimport { JavaMavenProject } from '@xpertss/projen-types';\n\nconst project = new JavaMavenProject({\n name: 'toolkit',\n groupId: 'org.xpertss.toolkit',\n artifactId: 'toolkit-parent',\n version: '0.1.0-SNAPSHOT',\n javaVersion: '17',\n});\n\nconst model = project.addModule({ dir: 'toolkit-model', artifactId: 'toolkit-model', description: 'Domain model' });\nconst stub = project.addModule({ dir: 'tools/stub-model', artifactId: 'toolkit-stub-model' }); // nested dirs are fine\nconst cli = project.addModule({ dir: 'toolkit-cli', artifactId: 'toolkit-cli' });\n\nstub.addModuleDependency(model); // sibling module: versionless\ncli.addModuleDependency(model);\ncli.addDependency('info.picocli/picocli@4.7.7'); // pinned: the version moves to the parent\ncli.addDependency('org.yaml/snakeyaml'); // versionless: managed below\nproject.addManagedDependency('org.yaml/snakeyaml@2.4');\nproject.addBom('org.testcontainers/testcontainers-bom@1.21.3');\n\nproject.synth();\n```\n\nWhat the reactor looks like:\n\n- **Root `pom.xml`:**\n - `<packaging>pom</packaging>`, with `<modules>` in `addModule` order.\n - `<dependencyManagement>` holds the BOM imports first (`type=pom`, `scope=import`), then every module at `${project.version}`, then every pinned version.\n - `<pluginManagement>` holds the plugin versions, and versionless `<plugins>` are inherited by every module: compiler, surefire, failsafe, jar, and enforcer.\n - `junit-jupiter` is a test dependency every module inherits. Its version comes from `junit-bom`, or from the Spring Boot BOM in the Spring Boot types.\n- **Module `pom.xml`:** `<parent>` with the right `relativePath` (`tools/stub-model` → `../../pom.xml`), then `artifactId`, `name`, `description`, and versionless `<dependencies>`. Nothing else, unless the module adds plugins.\n- A version given to `module.addDependency`/`addTestDependency`/`addPlugin` is moved into the parent's `<dependencyManagement>`/`<pluginManagement>`, so every module that uses an artifact gets the same version. Pinning one artifact at two different versions fails the synth.\n- The synth fails with a clear message on:\n - two modules with the same `artifactId`\n - two modules with the same `dir`, or one `dir` nested inside another\n - a `dir` outside the repo\n - a module depending on itself\n - `packaging` set to anything but `pom` on a project with modules\n\nYou get:\n\n- the root `pom.xml` (and one per module), written by this package:\n - **exact versions only**: a range such as `^1`, `~1.2` or `[1,2)` fails the synth\n - the compiler level, enforcer rule and JUnit line all follow `javaVersion`\n- one Maven run per build. `npx projen build` synthesizes, then runs `mvn -B verify`: compile, unit tests (surefire), package, and `*IT` integration tests (failsafe). There is no `mvn deploy` and no `dist/`.\n- `.github/workflows/build.yml`: a PR build that installs the pinned Node toolchain (`npm ci`) and the project's JDK (`actions/setup-java`, Maven cache), then runs `npx projen build`. The Maven build itself never runs a Sonar step - SonarCloud scanning is a separate, opt-in `sonar.yml` (set `sonarHostUrl`). See [Adding CI jobs](#adding-ci-jobs-to-a-java-project) and [SonarCloud (opt-in)](#sonarcloud-opt-in).\n- `.github/workflows/upgrade.yml`: a nightly (03:00 UTC) **report** of available dependency and plugin updates in the job summary. It changes nothing, because every version comes from `.projenrc.ts`; apply an update there. Turn it off with `upgradeWorkflow: false`. The same report runs locally with `npx projen upgrade`.\n- the projen drift check, `LICENSE` (MIT by default), `.editorconfig`, and a generated `package.json` that pins the projen toolchain exactly.\n- no sample code, unless `sample: true` on a single-module project.\n\n### JavaLibraryProject\n\nA reusable Java library published to Maven Central.\n\nScaffold it with the `java_library` type:\n\n```bash\nnpx projen new --from @xpertss/projen-types java_library --name common-utils --group-id org.xpertss --artifact-id common-utils\n```\n\n```typescript\n// .projenrc.ts\nimport { JavaLibraryProject } from '@xpertss/projen-types';\n\nconst project = new JavaLibraryProject({\n name: 'common-utils',\n groupId: 'org.xpertss',\n artifactId: 'common-utils',\n version: '1.0.0',\n sonarHostUrl: 'https://sonarcloud.io', // opt-in: adds sonar.yml (see SonarCloud)\n mavenCentralOidc: true,\n});\n\nproject.synth();\n```\n\nYou get everything from [`JavaMavenProject`](#javamavenproject), so modules work here too, plus:\n\n- `maven-source-plugin` and `maven-javadoc-plugin`, attaching the `-sources`/`-javadoc` jars Maven Central requires. In a multi-module library every module attaches them.\n- `.github/workflows/publish-maven-central.yml` - manual dispatch running `mvn -B deploy -P release`. With `mavenCentralOidc: true` it uses Maven Central's OIDC trusted publishing (no GPG secrets needed); otherwise it expects the secrets `MAVEN_GPG_PRIVATE_KEY`, `MAVEN_GPG_PASSPHRASE`, `MAVEN_CENTRAL_USERNAME`, `MAVEN_CENTRAL_PASSWORD`.\n- `.github/workflows/codeindex.yml` - on push to `main`, generates a Java source index under `.cai/` covering every module (disable with `publishCodeIndex: false`).\n\n### JavaSpringBootProject\n\nSpring Boot on Maven, single- or multi-module, **without** Docker, Flyway or a deploy hook. It's [`JavaMavenProject`](#javamavenproject) plus Spring Boot dependency management. Use it for a Spring Boot repo that ships something other than this package's container image, such as a multi-module control plane, or for a Boot app you deploy your own way.\n\nScaffold it with the `java_spring_boot` type:\n\n```bash\nnpx projen new --from @xpertss/projen-types java_spring_boot --name obeya --group-id org.xpertss.obeya --artifact-id obeya-parent\n```\n\n```typescript\n// .projenrc.ts\nimport { JavaSpringBootProject } from '@xpertss/projen-types';\n\nconst project = new JavaSpringBootProject({\n name: 'obeya',\n groupId: 'org.xpertss.obeya',\n artifactId: 'obeya-parent',\n version: '0.1.0-SNAPSHOT',\n javaVersion: '21',\n copyrightOwner: 'Xpert Software',\n});\n\n// plain jar modules: shared libraries, clients, tools\nconst model = project.addModule({ dir: 'obeya-model', artifactId: 'obeya-model' });\nconst client = project.addModule({ dir: 'obeya-api-client', artifactId: 'obeya-api-client' });\nclient.addModuleDependency(model);\n\n// a Spring Boot application module, repackaged into an executable jar\nconst server = project.addSpringBootModule({ dir: 'obeya-server', artifactId: 'obeya-server' });\nserver.addModuleDependency(model);\nserver.addDependency('org.springframework.boot/spring-boot-starter-web'); // versionless: Boot's BOM manages it\nserver.addTestDependency('org.springframework.boot/spring-boot-starter-test');\n\nproject.addBom('org.testcontainers/testcontainers-bom@1.21.3');\n\nproject.synth();\n```\n\nYou get everything from [`JavaMavenProject`](#javamavenproject), plus:\n\n- `spring-boot-dependencies` imported as the **first** BOM, so starters and every library Boot manages (JUnit included) are added without a version. `springBootVersion` sets it; the default is the newest release of the line that supports `javaVersion` (see [Java version support](#java-version-support)).\n- `spring-boot-maven-plugin`, versioned in `<pluginManagement>`:\n - In a **single-module** project the root jar is repackaged into an executable jar.\n - In a **multi-module** project only modules added with `addSpringBootModule()` are repackaged; `addModule()` modules stay plain jars.\n - A repackaged module needs its `@SpringBootApplication` class before `mvn verify` passes, because `repackage` fails with `Unable to find main class` until one exists. Add a module as a plain `addModule()` while it's still empty.\n- failsafe configured to run integration tests against the compiled classes rather than the repackaged jar (as `spring-boot-starter-parent` does).\n- no starters, no Docker, no Flyway, no CDK. Add the starters you need with `addDependency`.\n\n### JavaServiceProject\n\nA Spring Boot service that publishes a Docker image and can trigger deploys in a companion CDK repo. It is [`JavaSpringBootProject`](#javaspringbootproject) plus Docker, Flyway and a deploy hook. It is **single-module**, because the Docker build, the migrations and the deploy hook all assume one deployable at the repo root; `addModule()` fails and points you to `JavaSpringBootProject`.\n\nScaffold it with the `java_service` type:\n\n```bash\nnpx projen new --from @xpertss/projen-types java_service --name stream-processor --group-id org.xpertss --artifact-id stream-processor\n```\n\n```typescript\n// .projenrc.ts\nimport { JavaServiceProject } from '@xpertss/projen-types';\n\nconst project = new JavaServiceProject({\n name: 'stream-processor',\n groupId: 'org.xpertss',\n artifactId: 'stream-processor',\n dockerRegistry: 'docker.io/xpertss',\n cdkDeployTargetRepo: 'xpertss/stream-infra',\n environments: ['dev', { name: 'prod', requiresApproval: true }],\n});\n\nproject.synth();\n```\n\nYou get everything from [`JavaSpringBootProject`](#javaspringbootproject) (and so from `JavaMavenProject`), plus:\n\n- `spring-boot-starter-web` added to the pom (versionless; Boot's BOM manages it).\n- `.github/workflows/publish-docker.yml` - manual dispatch: `mvn -B package && docker build -t <registry>/<name>:<sha>`, logged in with the `DOCKER_USERNAME` / `DOCKER_PASSWORD` secrets. `dockerRegistry` defaults to `docker.io`.\n- Flyway wiring when `useFlyway` (default `true`): `flyway-core` (versionless, so it matches what Boot's Flyway auto-configuration expects), `flyway-maven-plugin` pinned for the Java line, and `src/main/resources/db/migration/V1__init.sql`.\n- `.github/workflows/deploy-cdk.yml` (the `CdkDeployHook`, generated by default) - manual dispatch with an environment selector; each job sends a `workflow_dispatch` to `deploy.yml` in the companion `cdkDeployTargetRepo` (a `CdkInfraProject`/`CdkAppProject` repo). With no `cdkDeployTargetRepo` set, the workflow is still generated but each job's only step fails with instructions - it is dispatch-only, so that lands on whoever tries to deploy rather than on every PR. Turn the workflow off entirely with `cdkDeployHook: false`. `environments` defaults to `['prod']`.\n\n### JavaAppProject\n\nA GUI/TUI/CLI Java application published to GitHub Packages only - no Maven Central, no Docker, no CDK deploy hook.\n\nScaffold it with the `java_app` type:\n\n```bash\nnpx projen new --from @xpertss/projen-types java_app --name studio-cli --group-id org.xpertss --artifact-id studio-cli\n```\n\n```typescript\n// .projenrc.ts\nimport { JavaAppProject } from '@xpertss/projen-types';\n\nconst project = new JavaAppProject({\n name: 'studio-cli',\n groupId: 'org.xpertss',\n artifactId: 'studio-cli',\n ghPackagesRegistry: 'https://maven.pkg.github.com/xpertss/studio-cli',\n});\n\nproject.synth();\n```\n\nYou get everything from [`JavaMavenProject`](#javamavenproject) (modules included), plus `.github/workflows/publish-ghpackages.yml` - manual dispatch running `mvn -B deploy -DaltDeploymentRepository=github::<registry>`, authenticated with `GITHUB_TOKEN`. `ghPackagesRegistry` defaults to `https://maven.pkg.github.com/<repo>` derived from the repository URL.\n\n### GitHubActionProject\n\nA reusable GitHub Action or Workflow. This example scaffolds an action that stages a folder and, only if it changed, commits and pushes it.\n\nScaffold it with the `git_hub_action` type:\n\n```bash\nnpx projen new --from @xpertss/projen-types git_hub_action --name auto-commit --sonar-host-url https://sonarcloud.io\n```\n\n```typescript\n// .projenrc.ts\nimport { GitHubActionProject } from '@xpertss/projen-types';\n\nconst project = new GitHubActionProject({\n name: 'auto-commit',\n description: 'Stage a folder and, only if it changed, commit and push it',\n sonarHostUrl: 'https://sonarcloud.io', // optional - set it to add sonar.yml (see SonarCloud)\n sonarOrganization: 'xpertss', // default - your SonarCloud org key\n dogfood: {\n // Two scenario steps: the \"changed\" path and the \"no-op\" path are both\n // load-bearing behavior for this action (a double-commit or a\n // push-when-empty bug is the failure mode a hand-rolled inline-shell\n // alternative is most likely to introduce).\n scenario: [\n {\n name: 'Changed path',\n fixtureSteps: [\n 'echo \"$(date -u +%Y%m%dT%H%M%SZ)\" >> test/fixtures/dogfood-state.txt',\n ],\n inputs: {\n commit_message: 'test: dogfood',\n branches: 'test/dogfood',\n },\n assertions: [\n // step id defaults to a slug of `name` - here \"changed-path-0\"\n '[ \"${{ steps.changed-path-0.outputs.committed }}\" = \"true\" ]',\n ],\n },\n {\n // No fixture change this time - nothing new to commit.\n name: 'No-op path',\n id: 'no-op-check', // pin an explicit id instead of relying on the default slug\n inputs: {\n commit_message: 'test: dogfood',\n branches: 'test/dogfood',\n },\n assertions: [\n '[ \"${{ steps.no-op-check.outputs.committed }}\" = \"false\" ]',\n ],\n },\n ],\n cleanup: [\n 'git push origin --delete test/dogfood || true',\n ],\n },\n});\n\nproject.synth();\n```\n\nYou get:\n\n- `action.yml` and `auto-commit.sh` are hand-written - this type only lints their content via `build.yml`'s shellcheck/yamllint/actionlint checks.\n- `.github/workflows/build.yml` - lint gate: `apt`-installed shellcheck/yamllint plus a pinned, SHA-256-verified `actionlint` release binary. Runs on `pull_request` (and `workflow_dispatch`) only - the release path runs the same lint task inside its `release` job before tagging, so push-to-`main` runs would be a duplicate (alongside `sonar.yml` when Sonar is enabled).\n- `.github/workflows/test-dogfood.yml` - runs the `dogfood.scenario` steps above against this repo's own `action.yml` (via `uses: ./`), then the shared `cleanup`, on `workflow_dispatch`, every `pull_request`, and nightly. Omit `dogfood` and the workflow still exists, with one step that fails on every PR until you declare a scenario - AD-001 allows a dogfood to be missing loudly, never silently. A *partial* `dogfood` (a scenario with no cleanup) is a synth error.\n- `.github/workflows/sonar.yml` - SonarCloud scan via the Scanner CLI (the quality gate blocks the PR), scanning `action.yml`/`.github/workflows/**`/`**/*.sh` explicitly. Opt-in: generated only when `sonarHostUrl` is set (see [SonarCloud (opt-in)](#sonarcloud-opt-in)).\n- `.github/workflows/release.yml` - `feat:`/`fix:` commits on `main` bump the version, tag `vX.Y.Z`, and create a GitHub Release.\n- `.github/workflows/projen-drift-check.yml` and `workflow-change-notice.yml` - drift detection and a change notice, always included.\n- `package.json` (**private**, version source only), `.yamllint`, `LICENSE` (MIT by default), and a `README.md` template - all regenerated by `npx projen`.\n\nNeeds `PROJEN_GITHUB_TOKEN` (used for automated PR comments), and `SONAR_TOKEN` only when Sonar is enabled (see [SonarCloud (opt-in)](#sonarcloud-opt-in)). Onboard a brand-new action repo by running the `projen new` command above first (see [Getting started](#getting-started)), then replace the generated `.projenrc.ts` with the one above and run `npx projen`, then hand-write `action.yml`/`auto-commit.sh`/`test/fixtures/`. If you wrote the `.projenrc.ts` before running `projen new`, see [Starting from an existing `.projenrc.ts`](#starting-from-an-existing-projenrcts).\n\n## Java version support\n\n`javaVersion` picks the Java line a Java project targets. It defaults to `21`; the supported lines are `1.8` (alias `8`), `17`, `21` and `25`, and any other value fails the synth with that list. **Nothing in the generated build is hard-coded to one Java line.** Everything that depends on it comes from one table in this package, grouped by line:\n\n| | `1.8` | `17` | `21` | `25` |\n| --- | --- | --- | --- | --- |\n| Compiler level | `maven.compiler.source`/`target` = `1.8` (javac 8 has no `--release`) | `maven.compiler.release` = `17` | `release` = `21` | `release` = `25` |\n| Enforcer `requireJavaVersion` | `[1.8,)` | `[17,)` | `[21,)` | `[25,)` |\n| JUnit (`junit-bom`) | 5.14.4 (JUnit 6 needs Java 17) | 6.1.3 | 6.1.3 | 6.1.3 |\n| Default Spring Boot (`JavaSpringBootProject`) | 2.7.18 (Boot 3+ needs Java 17; a 3.x+ `springBootVersion` fails the synth) | 4.1.1 | 4.1.1 | 4.1.1 |\n| `flyway-maven-plugin` (`JavaServiceProject`) | 9.22.3 | 13.9.0 | 13.9.0 | 13.9.0 |\n| CI JDK (`actions/setup-java` `java-version`) | `8` | `17` | `21` | `25` |\n\nMaven plugins (the same on every line, because all of them run on JDK 8): `maven-compiler-plugin` 3.16.0, `maven-surefire-plugin`/`maven-failsafe-plugin` 3.6.0, `maven-jar-plugin` 3.5.1, `maven-enforcer-plugin` 3.6.3, `maven-source-plugin` 3.4.0 and `maven-javadoc-plugin` 3.12.0 (library only), `versions-maven-plugin` 2.22.0 (the `upgrade` report).\n\n**The enforcer.** `maven-enforcer-plugin` fails the build at the start when the JDK or Maven running it is older than the project needs. Without it you get confusing compiler errors later, or a jar built for the wrong runtime. Its Java rule always follows `javaVersion`. The Maven rule defaults to `[3.9,)`; change it with `minMavenVersion`, or drop the plugin entirely with `enforcer: false`. CI installs the matching JDK through `setup-java`, so the rule only fires on a developer machine with the wrong JDK. A Java 1.8 project builds on any newer JDK (the rule is a minimum), but the JDK 8 runtime API is guaranteed only when you build on JDK 8.\n\n**Overriding a version.** `pluginVersions` replaces any default in the table, keyed by `groupId/artifactId`, with an exact version:\n\n```typescript\nnew JavaMavenProject({\n name: 'svc',\n groupId: 'org.xpertss',\n artifactId: 'svc',\n pluginVersions: {\n 'org.apache.maven.plugins/maven-surefire-plugin': '3.5.6',\n 'org.junit/junit-bom': '5.14.4',\n },\n});\n```\n\n`project.pinnedVersion('org.apache.maven.plugins/maven-surefire-plugin')` returns the version in effect, for reuse in your own plugin config.\n\n**New Java lines** are one new row in this package's version table and a release. Until a line is listed, `javaVersion` rejects it rather than guessing.\n\n## Adding CI jobs to a Java project\n\n`build.yml` is exposed as `project.buildVerifyWorkflow`, so jobs are added through projen's `addJob` rather than `addOverride`. `project.ciSetupSteps` holds the steps every Maven job here needs: pinned Node, `npm ci`, and the project's JDK with a Maven cache. Every job in `build.yml` can then be made a required check on `main`.\n\n```typescript\nimport { github } from 'projen';\n\nproject.buildVerifyWorkflow.addJob('integration', {\n name: 'Container integration tests',\n runsOn: ['self-hosted', 'linux', 'fedora', 'podman'],\n permissions: { contents: github.workflows.JobPermission.READ },\n steps: [\n { name: 'Checkout', uses: 'actions/checkout@v7' },\n ...project.ciSetupSteps,\n { name: 'Integration tests', run: 'mvn -B verify -Pcontainer-its' },\n ],\n});\n```\n\n## Common options\n\nCDK project types (`CdkInfraProjectOptions` / `CdkAppProjectOptions`):\n\n| Option | Default | Description |\n| --- | --- | --- |\n| `name` | - (required) | Project name; must match `package.json` |\n| `cdkVersion` | `2.189.1` | AWS CDK version |\n| `gheTokenSecret` | `PROJEN_GITHUB_TOKEN` | GitHub secret holding projen's PAT |\n| `licensed` | `true` | Write a `LICENSE` |\n| `license` | `MIT` | SPDX identifier for the `LICENSE` |\n| `copyrightOwner` / `copyrightPeriod` | `Xpert Software` / current year | Named in the `LICENSE` |\n| `environments` | - (no `deploy` workflow) | Deploy targets for the `deploy` workflow; strings or `EnvironmentOptions` |\n| `ecrEcs` | - | `EcrEcsOptions` - `enabled`, `externalImageSource` (default `true`) |\n| `edgeResources` | - | Subset of `cloudfront`, `route53`, `apigateway`, `cognito`, `sqs` |\n| `database` | - (app only) | `DatabaseOptions` - `engine` (`postgres`/`mysql`/`dynamodb`, default `postgres`), `migrationTool` |\n| `appEntryPoint` | `src/app.ts` (app only) | Path of the generated application entrypoint |\n\nJava project types (`JavaMavenProjectOptions` and everything built on it - `JavaLibraryProjectOptions`, `JavaAppProjectOptions`, `JavaSpringBootProjectOptions`, `JavaServiceProjectOptions`):\n\n| Option | Default | Description |\n| --- | --- | --- |\n| `name` | - (required) | Project name |\n| `groupId` | - (required) | Maven group id |\n| `artifactId` | - (required) | Maven artifact id (of the reactor parent, in a multi-module project) |\n| `version` | `0.1.0` | Maven version; must be exact |\n| `description` / `url` | - | Written to the root pom |\n| `javaVersion` | `21` | Java line: `1.8`, `17`, `21`, `25` - see [Java version support](#java-version-support) |\n| `javaDistribution` | `temurin` | `actions/setup-java` distribution for CI |\n| `packaging` | `jar` | Root packaging while the project has no modules; a project with modules is always `pom` |\n| `sample` | `false` | Starter `Main` + test under the `groupId` package (single-module only) |\n| `enforcer` | `true` | `maven-enforcer-plugin` with Maven and Java version rules |\n| `minMavenVersion` | `3.9` | The enforcer's `requireMavenVersion` minimum |\n| `pluginVersions` | - | Exact-version overrides for this package's defaults, keyed by `groupId/artifactId` |\n| `licensed` | `true` | Write a `LICENSE` |\n| `license` | `MIT` | SPDX identifier for the `LICENSE` |\n| `copyrightOwner` / `copyrightPeriod` | `Xpert Software` / current year | Named in the `LICENSE` |\n| `editorconfig` | `true` | Write a projen-managed `.editorconfig` (also on the CDK and action types) |\n| `upgradeWorkflow` | `true` | Generate the nightly update report (`upgrade.yml`) |\n| Sonar options | - | `sonarHostUrl`, `sonarOrganization`, `sonarTokenSecret`, `sonarPullRequestGate`, `sonarProjectKey` - SonarCloud is opt-in; see [SonarCloud (opt-in)](#sonarcloud-opt-in) |\n| `gheTokenSecret` | `PROJEN_GITHUB_TOKEN` | GitHub secret holding projen's PAT |\n| `springBootVersion` | newest Boot for `javaVersion` (Spring Boot types only) | Exact Spring Boot version: the BOM and `spring-boot-maven-plugin` |\n| `cdkDeployTargetRepo` | - (service only; `deploy-cdk.yml` fails until set) | Companion CDK repo (`owner/repo`) whose `deploy.yml` the deploy hook dispatches |\n| `cdkDeployHook` | `true` (service only) | Whether to generate `deploy-cdk.yml` at all |\n| `dockerRegistry` | `docker.io` (service only) | Registry the Docker image is pushed to |\n| `useFlyway` | `true` (service only) | Flyway plugin/dependency + `V1__init.sql` |\n\nWith the default MIT license, the generated `LICENSE` looks like:\n\n```\nMIT License\n\nCopyright (c) 2024-2026 Xpert Software\n\nPermission is hereby granted, ...\n```\n\nThe header line (`MIT License`) is specific to the license type — an `Apache-2.0` project would instead show `Apache-2.0`.\n\nOverride the license type and copyright fields:\n\n```typescript\nnew JavaMavenProject({\n name: 'my-lib',\n groupId: 'org.xpertss',\n artifactId: 'my-lib',\n license: 'Apache-2.0', // any SPDX id projen ships a template for\n copyrightOwner: 'Xpert Software',\n copyrightPeriod: '2024-2026',\n});\n```\n\n`EnvironmentOptions` for deploy targets:\n\n```text\ninterface EnvironmentOptions {\n readonly name: string; // e.g. \"dev\", \"stage\", \"prod\"\n readonly accountId?: string; // AWS account id (CDK deploys)\n readonly region?: string; // AWS region (CDK deploys)\n readonly requiresApproval?: boolean; // GitHub Environment approval gate (default false)\n}\n```\n\nPlain strings (`'dev'`) are shorthand for `{ name: 'dev' }`.\n\n`GitHubActionProjectOptions`:\n\n| Option | Default | Description |\n| --- | --- | --- |\n| `name` | - (required) | Project name |\n| `description` | - | One-line description; used in the default README template and recorded in the private `package.json` |\n| Sonar options | - | `sonarHostUrl`, `sonarOrganization`, `sonarTokenSecret`, `sonarPullRequestGate`, `sonarProjectKey` - SonarCloud is opt-in; see [SonarCloud (opt-in)](#sonarcloud-opt-in) |\n| `dogfood` | - (a `test-dogfood.yml` that fails until you declare one) | `ActionDogfoodOptions` - the scenario that exercises the action end-to-end via `uses: ./` |\n| `license` | `MIT` | SPDX identifier for the generated `LICENSE` |\n| `copyrightOwner` / `copyrightPeriod` | `Xpert Software` / current year | Named in the `LICENSE` |\n| `gheTokenSecret` | `PROJEN_GITHUB_TOKEN` | GitHub secret holding projen's PAT (drift-check PR comments); a dogfood that passes a `token` input must reference this secret in the step's `inputs` |\n\n`ActionDogfoodOptions`/`ActionDogfoodStep` - the dogfood scenario (`test-dogfood.yml`):\n\n```text\ninterface ActionDogfoodOptions {\n readonly scenario: ActionDogfoodStep[]; // one or more, run in order - required\n readonly cleanup: string[]; // shared, run once at the end with `if: always()` - required\n}\n\ninterface ActionDogfoodStep {\n readonly name: string; // labels this step-group's generated workflow steps\n readonly id?: string; // step id for the `uses: ./` call; default: a slug of `name`\n readonly fixtureSteps?: string[]; // shell, before the invocation (default: none)\n readonly inputs?: Record<string, string>; // `with:` for this invocation (default: none)\n readonly assertions: string[]; // shell, after the invocation - required; the step runs under set -euo pipefail so any failing line fails the job\n}\n```\n\nMost actions need exactly one `scenario` step. Actions with a re-run/no-op/idempotency behavior to verify (e.g. `auto-commit`'s no-op-on-no-change path, `create-pull-request`'s reuse-the-PR path) declare two - the second typically omits `fixtureSteps` so its invocation sees no new state, and its assertion checks the opposite outcome of the first. Reference an invocation's own outputs from a later assertion via `${{ steps.<id>.outputs.<name> }}`, using either the default slug or an explicit `id`.\n\n## SonarCloud (opt-in)\n\nSonarCloud is a shared, **opt-in** capability on every project type (CDK, Java, and GitHub Action). Set `sonarHostUrl` on any type and it generates a `.github/workflows/sonar.yml` that runs a pinned, SHA-256-verified Sonar Scanner CLI and blocks the PR on the quality gate. Omit it and no `sonar.yml` is generated. The Maven build itself never runs a Sonar step.\n\nShared options (accepted by the CDK types, every Java type, and `GitHubActionProject`):\n\n| Option | Default | Description |\n| --- | --- | --- |\n| `sonarHostUrl` | - (required to enable) | URL of your SonarCloud instance (e.g. `https://sonarcloud.io`); must be reachable from github.com-hosted runners |\n| `sonarOrganization` | `xpertss` | SonarCloud org key (`sonar.organization`); required by the Scanner CLI, not derived from the token |\n| `sonarProjectKey` | `${sonarOrganization}_${name}` | `sonar.projectKey` - the org + project key SonarCloud uses (e.g. `xpertss_create-pull-request`) |\n| `sonarTokenSecret` | `SONAR_TOKEN` | GitHub secret holding the Sonar token |\n| `sonarPullRequestGate` | `true` | Whether `sonar.yml` also runs on `pull_request` as a pass/fail gate |\n\nInclusions differ by type: a `GitHubActionProject` scans `action.yml`/`.github/workflows/**`/`**/*.sh` explicitly (YAML and shell are not in Sonar's default-recognized set), while the CDK and Java types rely on Sonar's default inclusions for their languages.\n\n```typescript\nnew CdkInfraProject({\n name: 'my-infra',\n environments: ['dev', 'prod'],\n sonarHostUrl: 'https://sonarcloud.io', // adds sonar.yml\n});\n```\n\nRequires the `SONAR_TOKEN` secret (rename it with `sonarTokenSecret`).\n\n## Required GitHub secrets\n\n| Secret | Used by | Notes |\n| --- | --- | --- |\n| `PROJEN_GITHUB_TOKEN` | all types | PAT for projen's self-mutation/automation; override via `gheTokenSecret` |\n| `SONAR_TOKEN` | any type with `sonarHostUrl` set | SonarCloud scan in `sonar.yml`; override the secret name via `sonarTokenSecret` |\n| `DOCKER_USERNAME` / `DOCKER_PASSWORD` | `JavaServiceProject` | Docker image push |\n| `MAVEN_GPG_PRIVATE_KEY`, `MAVEN_GPG_PASSPHRASE`, `MAVEN_CENTRAL_USERNAME`, `MAVEN_CENTRAL_PASSWORD` | `JavaLibraryProject` without `mavenCentralOidc` | Not needed with OIDC trusted publishing |\n\n## Customizing `.gitignore`\n\nEvery project type already ignores JetBrains IDE state (`/.idea/*`) and the `/spec/` directory with all of its subdirectories (local plans and specs, never committed). The Java types also ignore Maven `target/` output and Eclipse files. To ignore more, call the inherited `addGitIgnore()` method after constructing the project in your `.projenrc.ts`:\n\n```typescript\n// .projenrc.ts\nimport { CdkInfraProject } from '@xpertss/projen-types';\n\nconst project = new CdkInfraProject({\n name: 'my-infra',\n});\n\nproject.addGitIgnore('*.log');\nproject.addGitIgnore('.env.*');\nproject.addGitIgnore('/build/');\n\nproject.synth();\n```\n\nThe same works for every type - just swap the class (e.g. `JavaServiceProject`, `GitHubActionProject`). Each call takes one standard gitignore glob pattern. There is no `gitignore` constructor option: the project types' option interfaces do not expose one, so patterns go through `addGitIgnore()` instead.\n\nRe-run `npx projen` after editing - `.gitignore` is a generated file, so hand-editing it is caught by the drift check.\n\n## Troubleshooting\n\n**`Unable to find projen project. Use \"projen new\" to create a new project.`**\nIf the directory already has a `.projenrc.ts`, this error means the rc has\nnever been synthesized (there's no `.projen/tasks.json`), not that you need a\nnew project. See [Starting from an existing `.projenrc.ts`](#starting-from-an-existing-projenrcts).\n\n## Going further\n\nThe project types are composed from smaller components you can also attach to your own projects:\n\n| Component | Applies to | Purpose |\n| --- | --- | --- |\n| `AppRuntimeScaffold` | `NodeProject` | App source skeleton (`app.ts` + handlers) |\n| `DatabaseComponent` | `NodeProject` | Database construct stub + migration tool wiring |\n| `EcrEcsConstructs` | `Project` | ECR + Fargate ECS construct helper |\n| `EdgeNetworkingConstructs` | `Project` | Per-resource edge networking construct helpers |\n| `MavenPom` | `Project` | A `pom.xml` with modules, BOM imports, dependency/plugin management, and exact versions only |\n| `MavenModule` | `JavaMavenProject` | One module of a reactor (created by `addModule()`) |\n| `MavenUpgradeReport` | `GitHubProject` | Nightly report-only `upgrade.yml` |\n| `MavenCentralPublish` | `JavaMavenProject` | Manual-dispatch Maven Central publish workflow |\n| `DockerPublish` | `JavaMavenProject` | Manual-dispatch Docker build+push workflow |\n| `GitHubPackagesPublish` | `JavaMavenProject` | Manual-dispatch GitHub Packages publish workflow |\n| `FlywayMigration` | `JavaSpringBootProject` | Flyway plugin/dependency + migrations directory |\n| `CdkDeployHook` | `JavaMavenProject` | Manual-dispatch workflow that triggers `deploy.yml` in a companion CDK repo |\n| `CodeIndexWorkflow` | `JavaMavenProject` | Code index generation on push to `main` |\n| `ActionBuildWorkflow` | `GitHubProject` | The `lint` task (shellcheck/yamllint/pinned actionlint) + `build.yml` |\n| `ActionDogfoodWorkflow` | `GitHubProject` | `test-dogfood.yml` from an `ActionDogfoodOptions` scenario |\n| `SonarWorkflow` | `GitHubProject` | `sonar.yml` (SonarCloud scan via the Scanner CLI); opt-in, shared by every type via `sonarHostUrl` |\n\nExample - adding a Docker publish to a `JavaMavenProject` (`JavaServiceProject` is the packaged version of this, with Spring Boot):\n\n```typescript\nimport { DockerPublish, JavaMavenProject } from '@xpertss/projen-types';\n\nconst project = new JavaMavenProject({\n name: 'my-service',\n groupId: 'org.xpertss',\n artifactId: 'my-service',\n});\n\nnew DockerPublish(project, { dockerRegistry: 'ghcr.io' });\n\nproject.synth();\n```\n\nThe full API reference, including every option and property, is in [API.md](./API.md).\n"
109
+ "markdown": "# @xpertss/projen-types\n\nProjen project types for CDK/TypeScript and Java/Maven projects.\n\nInstead of hand-maintaining `pom.xml`, `cdk.json`, and GitHub workflows, you declare a project type in a `.projenrc.ts` file and let [projen](https://github.com/projen/projen) generate (and keep up to date) the whole scaffold: build files, source skeletons, CI workflows, and deploy pipelines.\n\n## Project types at a glance\n\n| Type | What it is | Publishes to | Generated workflows |\n| --- | --- | --- | --- |\n| `CdkInfraProject` | Pure-infrastructure CDK stacks (CloudFront, Route53, SQS, API Gateway, Cognito, ECR/ECS for externally-built images) | - | `build` (PR checks), `deploy` (manual dispatch) |\n| `CdkAppProject` | Full TypeScript service behind API Gateway: infra + app source + database | - | `build`, `deploy`, `app-build` (PR checks) |\n| `JavaMavenProject` | Plain Maven project, single- or multi-module, no framework | - | `build`, `upgrade` (nightly report), `codeindex` |\n| `JavaLibraryProject` | Reusable Java library | Maven Central | `build`, `upgrade` (nightly report), `codeindex`, `publish-maven-central` |\n| `JavaAppProject` | GUI/TUI/CLI Java application | GitHub Packages | `build`, `upgrade` (nightly report), `codeindex`, `publish-ghpackages` |\n| `JavaSpringBootProject` | Spring Boot on Maven, single- or multi-module, **no Docker** | - | `build`, `upgrade` (nightly report), `codeindex` |\n| `JavaServiceProject` | Spring Boot service deployed as a container | Docker Hub | `build`, `upgrade` (nightly report), `codeindex`, `publish-docker`, `deploy-cdk` |\n| `GitHubActionProject` | Reusable GitHub Action or Workflow | GitHub Releases | `build`, `test-dogfood`, `sonar`, `release` |\n\nThe Java types are layered, so pick the lowest layer that does what you need:\n\n```text\nJavaMavenProject java_maven Maven only; single- or multi-module; any supported Java line\n├── JavaLibraryProject java_library + Maven Central publish, source/javadoc jars\n├── JavaAppProject java_app + GitHub Packages publish\n└── JavaSpringBootProject java_spring_boot + Spring Boot BOM and executable-jar repackaging; no Docker/Flyway/CDK\n └── JavaServiceProject java_service + Docker publish, Flyway, CDK deploy hook (single-module)\n```\n\nEvery Java type targets a configurable Java line (`javaVersion`: `1.8`, `17`, `21` or `25`; see [Java version support](#java-version-support)), and every one except `JavaServiceProject` can be a multi-module Maven reactor (see [JavaMavenProject](#javamavenproject)). Every Java type also generates the code index (`codeindex.yml`).\n\nOne foundation class is also exported for advanced use: `CdkTypescriptProject` (shared CDK + TypeScript base for the CDK types).\n\nAll project types:\n\n- run a **drift check** in PR builds - a job that re-runs projen and fails if generated files were hand-edited. Edit `.projenrc.ts`, then run `npx projen`; never edit generated files directly.\n- use the GitHub secret `PROJEN_GITHUB_TOKEN` (a fine-grained PAT) for projen's automation. Override with `gheTokenSecret`.\n- make all publishing/deploying **manual** (workflow_dispatch) rather than on every merge.\n- offer an opt-in SonarCloud scan (`sonar.yml`) on any type via `sonarHostUrl` (see [SonarCloud (opt-in)](#sonarcloud-opt-in)).\n\n## Getting started\n\nHow you start depends on what is already in the directory:\n\n| Starting point | Do this |\n| --- | --- |\n| Empty directory | `projen new --from` (below) |\n| Existing repo, no `.projenrc.ts` | `projen new --from ... --no-git` (below) |\n| A `.projenrc.ts` but no `.projen/` directory - copied from another repo, or one of the [examples](#examples) pasted into a fresh repo | [Starting from an existing `.projenrc.ts`](#starting-from-an-existing-projenrcts) |\n\nScaffold the repo with projen's own bootstrap, pointed at this package:\n\n```bash\nmkdir my-project && cd my-project\ngit init\nnpx projen new --from @xpertss/projen-types cdk_infra --name my-project\n```\n\nThat writes a starter `.projenrc.ts`, synthesizes the whole scaffold and\ninstalls dependencies. The type names `projen new` accepts are `cdk_infra`,\n`cdk_app`, `java_maven`, `java_library`, `java_app`, `java_spring_boot`,\n`java_service` and `git_hub_action`; pass a bogus one to have it list them.\nRequired options become flags: `--name` for every type, plus\n`--group-id`/`--artifact-id` (Java). Any other plainly-typed option can be\npassed the same way - `--sonar-host-url` (opt-in Sonar, all types),\n`--java-version 1.8`, `--cdk-deploy-target-repo owner/repo`,\n`--docker-registry ghcr.io`, `--no-use-flyway`, and so on. Modules can't be\npassed on the command line; add them to `.projenrc.ts` afterwards (see\n[JavaMavenProject](#javamavenproject)).\n\n**Adding projen to a repo that already has content.** `projen new` runs\n`git init` and commits the scaffold by default. In a repo with existing work,\npass `--no-git` so it writes the files and leaves committing to you:\n\n```bash\ncd existing-repo\nnpx projen new --from @xpertss/projen-types java_spring_boot --name obeya --group-id org.xpertss.obeya --artifact-id obeya-parent --no-git\n```\n\n**Commit a lockfile.** The generated `build.yml` and drift check install the\ntoolchain with `npm ci`, which needs a committed `package-lock.json`, and\n`projen new` doesn't always leave one behind. Run `npm install` once and\ncommit `package-lock.json` along with the scaffold. Without it those\nworkflows fail with an `::error::` that says exactly this. For the Java\ntypes, `package.json` is generated, and it pins `projen` and\n`@xpertss/projen-types` to **exact** versions (no `^`), so every machine and\nCI run uses the same generator. A floating range would make the drift check\nreport the differences between generator versions as drift.\n\nCommit the result. From then on, every change to the scaffold goes through\n`.projenrc.ts` followed by `npx projen`:\n\n```bash\nnpx projen\n```\n\nEvery type scaffolds from that one command; no option is *required* that\n`projen new` cannot pass. The structured options - `environments` and\n`GitHubActionProject`'s `dogfood` - are ones projen's CLI cannot render\ninto a projenrc, so they are added afterwards by editing `.projenrc.ts` and\nre-running `npx projen`. Leaving `environments` out simply generates no\ndeploy workflow; leaving `dogfood` out (or a service's\n`cdkDeployTargetRepo`) still generates the workflow, with one step that\nfails and tells you what to add - a gate this package considers load-bearing\nis allowed to be missing loudly, never silently.\n\nThe `name` option must match the `name` field in the project's `package.json` (for the CDK types). For the Java types it becomes the root pom's `<name>` and the generated `package.json` name.\n\n> **Never write projen's generated-file marker text literally in `.projenrc.ts`.** projen deletes, as an orphaned generated file, any file containing the marker line (`~~ Generated by projen. To modify, ...`), and that includes `.projenrc.ts` itself. When you generate a file yourself (for example with `TextFile`), insert the marker through the file's `marker` property: ``file.addLine(`# ${file.marker}`)``.\n\n## Starting from an existing `.projenrc.ts`\n\nA directory that has a `.projenrc.ts` but no `.projen/` directory has never\nbeen synthesized. This happens when you copy the rc from another repo, or paste\none of the [examples](#examples) into a fresh repo. In that state `npx projen`\nfails with:\n\n```\n👾 Unable to find projen project. Use \"projen new\" to create a new project.\n```\n\n`npx projen` doesn't run `.projenrc.ts` itself. It runs the `default` task\nlisted in `.projen/tasks.json`, and that file is written by the first synth.\nDo the first synth one of these two ways. After that, `npx projen` works as\nusual.\n\n**Option 1 (recommended): run `projen new` over the existing rc.** `projen\nnew` leaves an existing `.projenrc.ts` untouched and writes the rest of the\nscaffold, `.projen/` included. Pass the type and its required flags as you\nwould for a new repo. Their values don't have to match your rc, because the\n`npx projen` that follows re-synthesizes everything from the rc:\n\n```bash\nnpx projen new --from @xpertss/projen-types git_hub_action --name pull-request --sonar-host-url https://sonarcloud.io --no-git\nnpx projen\n```\n\nThe first command's output reflects only the flags. For example, a\n`GitHubActionProject`'s `test-dogfood.yml` is still the failing placeholder.\nThe second command applies everything in the rc (`dogfood`, `environments`,\nmodules, and so on).\n\n**Option 2 (Java types and `GitHubActionProject`): run the rc directly,\nonce.** The CDK types run their rc differently, so for those use option 1.\nInstall what the rc imports, give\nts-node a placeholder `tsconfig.projen.json` (the synth overwrites it with the\ngenerated one), and run the same command the generated `default` task runs:\n\n```bash\nnpm i -D projen @xpertss/projen-types constructs\necho '{}' > tsconfig.projen.json\nnpx -y -p ts-node@10.9.2 -p typescript@6.0.3 ts-node --project tsconfig.projen.json .projenrc.ts\nnpx projen\n```\n\nThe `echo` line works in bash and PowerShell. Don't drop `--project`: without\na tsconfig, ts-node fails on Node 22+ with `Unknown file extension \".ts\"`.\n\nEither way, commit `.projen/` along with the rest of the scaffold. A fresh\nclone then has its `tasks.json`, and `npx projen` works there straight away.\n\n## Updating an existing project when this package changes\n\nYour generated scaffold reflects the *installed* version of `@xpertss/projen-types` - `npx projen` reads your project type from the package in `node_modules`, not from this repository. So when this package ships a change (a bug fix, a new generated file, or altered workflow behavior), an existing project picks it up by bumping the dependency and re-synthesizing. A stale `node_modules` silently regenerates with the old behavior, so the bump is the load-bearing step.\n\nUsing the [`auto-commit` action](#githubactionproject) as a running example, say a new release adds the `# Purpose:` workflow header and SonarCloud wording. In the `auto-commit` repo:\n\n```bash\nnpm view @xpertss/projen-types version # what's the newest release?\nnpm i -D @xpertss/projen-types@latest # bump the installed project type\nnpx projen # re-synthesize every generated file\ngit diff # review before committing\n```\n\nThe diff here is the workflows gaining a `# Purpose:` comment and `sonar.yml` reflecting the SonarCloud wording. Commit the result with a normal `chore:` or `fix:` message - you never hand-edit the generated files themselves.\n\n## Examples\n\nEach example shows the `projen new` command first, then a fuller\n`.projenrc.ts`. Run the command first, then replace the generated\n`.projenrc.ts` with the example (or merge it in) and run `npx projen`. If you\npasted the example into a fresh repo first instead, see\n[Starting from an existing `.projenrc.ts`](#starting-from-an-existing-projenrcts).\n\n### CdkInfraProject\n\nPure infrastructure stacks with optional ECR/ECS and edge-networking constructs.\n\nScaffold it with the `cdk_infra` type:\n\n```bash\nnpx projen new --from @xpertss/projen-types cdk_infra --name media-edge-infra\n```\n\n```typescript\n// .projenrc.ts\nimport { CdkInfraProject } from '@xpertss/projen-types';\n\nconst project = new CdkInfraProject({\n name: 'media-edge-infra',\n environments: [\n 'dev',\n { name: 'stage', accountId: '111111111111', region: 'eu-central-1' },\n { name: 'prod', accountId: '222222222222', region: 'eu-central-1', requiresApproval: true },\n ],\n edgeResources: ['cloudfront', 'route53', 'sqs'],\n ecrEcs: { enabled: true, externalImageSource: true },\n});\n\nproject.synth();\n```\n\nYou get:\n\n- `cdk.json`, `cdk synth` / `cdk diff` / `cdk deploy` tasks, and the standard `AwsCdkTypeScriptApp` layout (CDK 2.189.1 by default).\n- `.github/workflows/build.yml` - PR build that hard-fails on projen drift.\n- `.github/workflows/deploy.yml` - manual dispatch with one `deploy-<env>` job per environment (runs `cdk deploy --all` with `--context environment=<env>`); `requiresApproval` environments get a GitHub Environment approval gate.\n- `LICENSE` (MIT by default), `.editorconfig`, and the projen drift check.\n- `src/constructs/edge-networking.ts` - helper constructs only for the requested `edgeResources` (`cloudfront`, `route53`, `apigateway`, `cognito`, `sqs`).\n- `src/constructs/ecr-ecs.ts` when `ecrEcs.enabled` - ECR repo + Fargate service; `externalImageSource: true` (default) means the service pulls an image built outside this repo.\n\n### CdkAppProject\n\n`CdkInfraProject` plus application source, a database construct, and an app-level build workflow.\n\nScaffold it with the `cdk_app` type:\n\n```bash\nnpx projen new --from @xpertss/projen-types cdk_app --name video-api\n```\n\n```typescript\n// .projenrc.ts\nimport { CdkAppProject } from '@xpertss/projen-types';\n\nconst project = new CdkAppProject({\n name: 'video-api',\n environments: ['dev', 'prod'],\n edgeResources: ['apigateway'],\n database: { engine: 'postgres', migrationTool: 'flyway' },\n appEntryPoint: 'src/app.ts',\n});\n\nproject.synth();\n```\n\nEverything from `CdkInfraProject`, plus:\n\n- `src/app.ts` - application entrypoint stub (`export function handler()`).\n- `src/handlers/example.ts` - API Gateway proxy handler stub, with `@types/aws-lambda` added as a dev dependency.\n- `src/constructs/database.ts` - a `Database` construct stub for the chosen engine (`postgres`/`mysql` -> RDS, `dynamodb` -> DynamoDB). `migrationTool` (no default, intentionally) is added as a dev dependency and referenced in the stub - wiring it up is left to you.\n- `.github/workflows/app-build.yml` - runs the project's test task on every PR, then checks for projen drift.\n\n### JavaMavenProject\n\nA plain Maven build with no framework and no publish target. It's the base of every other Java type, and it's usable on its own. It's single-module until you add a module, then it's a multi-module reactor.\n\nScaffold it with the `java_maven` type:\n\n```bash\nnpx projen new --from @xpertss/projen-types java_maven --name legacy-tools --group-id org.xpertss --artifact-id legacy-tools --java-version 1.8\n```\n\n**Single module:**\n\n```typescript\n// .projenrc.ts\nimport { JavaMavenProject } from '@xpertss/projen-types';\n\nconst project = new JavaMavenProject({\n name: 'legacy-tools',\n groupId: 'org.xpertss',\n artifactId: 'legacy-tools',\n javaVersion: '1.8',\n});\n\nproject.addDependency('commons-io/commons-io@2.20.0'); // exact version\nproject.addTestDependency('org.assertj/assertj-core@3.27.3');\n\nproject.synth();\n```\n\n**Multi-module:** each `addModule()` call adds a module directory with its own generated `pom.xml`. The root pom becomes the `pom`-packaged reactor parent. Everything stays one projen project, with one `.projen/`, one `.gitignore` and one `tasks.json`.\n\n```typescript\n// .projenrc.ts\nimport { JavaMavenProject } from '@xpertss/projen-types';\n\nconst project = new JavaMavenProject({\n name: 'toolkit',\n groupId: 'org.xpertss.toolkit',\n artifactId: 'toolkit-parent',\n version: '0.1.0-SNAPSHOT',\n javaVersion: '17',\n});\n\nconst model = project.addModule({ dir: 'toolkit-model', artifactId: 'toolkit-model', description: 'Domain model' });\nconst stub = project.addModule({ dir: 'tools/stub-model', artifactId: 'toolkit-stub-model' }); // nested dirs are fine\nconst cli = project.addModule({ dir: 'toolkit-cli', artifactId: 'toolkit-cli' });\n\nstub.addModuleDependency(model); // sibling module: versionless\ncli.addModuleDependency(model);\ncli.addDependency('info.picocli/picocli@4.7.7'); // pinned: the version moves to the parent\ncli.addDependency('org.yaml/snakeyaml'); // versionless: managed below\nproject.addManagedDependency('org.yaml/snakeyaml@2.4');\nproject.addBom('org.testcontainers/testcontainers-bom@1.21.3');\n\nproject.synth();\n```\n\nWhat the reactor looks like:\n\n- **Root `pom.xml`:**\n - `<packaging>pom</packaging>`, with `<modules>` in `addModule` order.\n - `<dependencyManagement>` holds the BOM imports first (`type=pom`, `scope=import`), then every module at `${project.version}`, then every pinned version.\n - `<pluginManagement>` holds the plugin versions, and versionless `<plugins>` are inherited by every module: compiler, surefire, failsafe, jar, and enforcer.\n - `junit-jupiter` is a test dependency every module inherits. Its version comes from `junit-bom`, or from the Spring Boot BOM in the Spring Boot types.\n- **Module `pom.xml`:** `<parent>` with the right `relativePath` (`tools/stub-model` → `../../pom.xml`), then `artifactId`, `name`, `description`, and versionless `<dependencies>`. Nothing else, unless the module adds plugins.\n- A version given to `module.addDependency`/`addTestDependency`/`addPlugin` is moved into the parent's `<dependencyManagement>`/`<pluginManagement>`, so every module that uses an artifact gets the same version. Pinning one artifact at two different versions fails the synth.\n- The synth fails with a clear message on:\n - two modules with the same `artifactId`\n - two modules with the same `dir`, or one `dir` nested inside another\n - a `dir` outside the repo\n - a module depending on itself\n - `packaging` set to anything but `pom` on a project with modules\n\nYou get:\n\n- the root `pom.xml` (and one per module), written by this package:\n - **exact versions only**: a range such as `^1`, `~1.2` or `[1,2)` fails the synth\n - the compiler level, enforcer rule and JUnit line all follow `javaVersion`\n- one Maven run per build. `npx projen build` synthesizes, then runs `mvn -B verify`: compile, unit tests (surefire), package, and `*IT` integration tests (failsafe). There is no `mvn deploy` and no `dist/`.\n- `.github/workflows/build.yml`: a PR build that installs the pinned Node toolchain (`npm ci`) and the project's JDK (`actions/setup-java`, Maven cache), then runs `npx projen build`. The Maven build itself never runs a Sonar step - SonarCloud scanning is a separate, opt-in `sonar.yml` (set `sonarHostUrl`). See [Adding CI jobs](#adding-ci-jobs-to-a-java-project) and [SonarCloud (opt-in)](#sonarcloud-opt-in).\n- `.github/workflows/upgrade.yml`: a nightly (03:00 UTC) **report** of available dependency and plugin updates in the job summary. It changes nothing, because every version comes from `.projenrc.ts`; apply an update there. Turn it off with `upgradeWorkflow: false`. The same report runs locally with `npx projen upgrade`.\n- `.github/workflows/codeindex.yml`: on push to `main`, regenerates `.xss/index.txt` (a sorted list of every `.java` file, across all modules) and commits it straight to `main` as `chore: update code index`. The commit is pushed with the workflow's `GITHUB_TOKEN`, so it starts no build, Sonar scan or release, but the workflow's bot must be allowed to push to `main`. A run that loses the push to a newer merge succeeds quietly, because the newer run regenerates the index. The same index builds locally with `npx projen codeindex`. Turn it off with `publishCodeIndex: false`.\n- the projen drift check, `LICENSE` (MIT by default), `.editorconfig`, and a generated `package.json` that pins the projen toolchain exactly.\n- no sample code, unless `sample: true` on a single-module project.\n\n### JavaLibraryProject\n\nA reusable Java library published to Maven Central.\n\nScaffold it with the `java_library` type:\n\n```bash\nnpx projen new --from @xpertss/projen-types java_library --name common-utils --group-id org.xpertss --artifact-id common-utils\n```\n\n```typescript\n// .projenrc.ts\nimport { JavaLibraryProject } from '@xpertss/projen-types';\n\nconst project = new JavaLibraryProject({\n name: 'common-utils',\n groupId: 'org.xpertss',\n artifactId: 'common-utils',\n version: '1.0.0',\n sonarHostUrl: 'https://sonarcloud.io', // opt-in: adds sonar.yml (see SonarCloud)\n mavenCentralOidc: true,\n});\n\nproject.synth();\n```\n\nYou get everything from [`JavaMavenProject`](#javamavenproject), so modules work here too, plus:\n\n- `maven-source-plugin` and `maven-javadoc-plugin`, attaching the `-sources`/`-javadoc` jars Maven Central requires. In a multi-module library every module attaches them.\n- `.github/workflows/publish-maven-central.yml` - manual dispatch running `mvn -B deploy -P release`. With `mavenCentralOidc: true` it uses Maven Central's OIDC trusted publishing (no GPG secrets needed); otherwise it expects the secrets `MAVEN_GPG_PRIVATE_KEY`, `MAVEN_GPG_PASSPHRASE`, `MAVEN_CENTRAL_USERNAME`, `MAVEN_CENTRAL_PASSWORD`.\n\n### JavaSpringBootProject\n\nSpring Boot on Maven, single- or multi-module, **without** Docker, Flyway or a deploy hook. It's [`JavaMavenProject`](#javamavenproject) plus Spring Boot dependency management. Use it for a Spring Boot repo that ships something other than this package's container image, such as a multi-module control plane, or for a Boot app you deploy your own way.\n\nScaffold it with the `java_spring_boot` type:\n\n```bash\nnpx projen new --from @xpertss/projen-types java_spring_boot --name obeya --group-id org.xpertss.obeya --artifact-id obeya-parent\n```\n\n```typescript\n// .projenrc.ts\nimport { JavaSpringBootProject } from '@xpertss/projen-types';\n\nconst project = new JavaSpringBootProject({\n name: 'obeya',\n groupId: 'org.xpertss.obeya',\n artifactId: 'obeya-parent',\n version: '0.1.0-SNAPSHOT',\n javaVersion: '21',\n copyrightOwner: 'Xpert Software',\n});\n\n// plain jar modules: shared libraries, clients, tools\nconst model = project.addModule({ dir: 'obeya-model', artifactId: 'obeya-model' });\nconst client = project.addModule({ dir: 'obeya-api-client', artifactId: 'obeya-api-client' });\nclient.addModuleDependency(model);\n\n// a Spring Boot application module, repackaged into an executable jar\nconst server = project.addSpringBootModule({ dir: 'obeya-server', artifactId: 'obeya-server' });\nserver.addModuleDependency(model);\nserver.addDependency('org.springframework.boot/spring-boot-starter-web'); // versionless: Boot's BOM manages it\nserver.addTestDependency('org.springframework.boot/spring-boot-starter-test');\n\nproject.addBom('org.testcontainers/testcontainers-bom@1.21.3');\n\nproject.synth();\n```\n\nYou get everything from [`JavaMavenProject`](#javamavenproject), plus:\n\n- `spring-boot-dependencies` imported as the **first** BOM, so starters and every library Boot manages (JUnit included) are added without a version. `springBootVersion` sets it; the default is the newest release of the line that supports `javaVersion` (see [Java version support](#java-version-support)).\n- `spring-boot-maven-plugin`, versioned in `<pluginManagement>`:\n - In a **single-module** project the root jar is repackaged into an executable jar.\n - In a **multi-module** project only modules added with `addSpringBootModule()` are repackaged; `addModule()` modules stay plain jars.\n - A repackaged module needs its `@SpringBootApplication` class before `mvn verify` passes, because `repackage` fails with `Unable to find main class` until one exists. Add a module as a plain `addModule()` while it's still empty.\n- failsafe configured to run integration tests against the compiled classes rather than the repackaged jar (as `spring-boot-starter-parent` does).\n- no starters, no Docker, no Flyway, no CDK. Add the starters you need with `addDependency`.\n\n### JavaServiceProject\n\nA Spring Boot service that publishes a Docker image and can trigger deploys in a companion CDK repo. It is [`JavaSpringBootProject`](#javaspringbootproject) plus Docker, Flyway and a deploy hook. It is **single-module**, because the Docker build, the migrations and the deploy hook all assume one deployable at the repo root; `addModule()` fails and points you to `JavaSpringBootProject`.\n\nScaffold it with the `java_service` type:\n\n```bash\nnpx projen new --from @xpertss/projen-types java_service --name stream-processor --group-id org.xpertss --artifact-id stream-processor\n```\n\n```typescript\n// .projenrc.ts\nimport { JavaServiceProject } from '@xpertss/projen-types';\n\nconst project = new JavaServiceProject({\n name: 'stream-processor',\n groupId: 'org.xpertss',\n artifactId: 'stream-processor',\n dockerRegistry: 'docker.io/xpertss',\n cdkDeployTargetRepo: 'xpertss/stream-infra',\n environments: ['dev', { name: 'prod', requiresApproval: true }],\n});\n\nproject.synth();\n```\n\nYou get everything from [`JavaSpringBootProject`](#javaspringbootproject) (and so from `JavaMavenProject`), plus:\n\n- `spring-boot-starter-web` added to the pom (versionless; Boot's BOM manages it).\n- `.github/workflows/publish-docker.yml` - manual dispatch: `mvn -B package && docker build -t <registry>/<name>:<sha>`, logged in with the `DOCKER_USERNAME` / `DOCKER_PASSWORD` secrets. `dockerRegistry` defaults to `docker.io`.\n- Flyway wiring when `useFlyway` (default `true`): `flyway-core` (versionless, so it matches what Boot's Flyway auto-configuration expects), `flyway-maven-plugin` pinned for the Java line, and `src/main/resources/db/migration/V1__init.sql`.\n- `.github/workflows/deploy-cdk.yml` (the `CdkDeployHook`, generated by default) - manual dispatch with an environment selector; each job sends a `workflow_dispatch` to `deploy.yml` in the companion `cdkDeployTargetRepo` (a `CdkInfraProject`/`CdkAppProject` repo). With no `cdkDeployTargetRepo` set, the workflow is still generated but each job's only step fails with instructions - it is dispatch-only, so that lands on whoever tries to deploy rather than on every PR. Turn the workflow off entirely with `cdkDeployHook: false`. `environments` defaults to `['prod']`.\n\n### JavaAppProject\n\nA GUI/TUI/CLI Java application published to GitHub Packages only - no Maven Central, no Docker, no CDK deploy hook.\n\nScaffold it with the `java_app` type:\n\n```bash\nnpx projen new --from @xpertss/projen-types java_app --name studio-cli --group-id org.xpertss --artifact-id studio-cli\n```\n\n```typescript\n// .projenrc.ts\nimport { JavaAppProject } from '@xpertss/projen-types';\n\nconst project = new JavaAppProject({\n name: 'studio-cli',\n groupId: 'org.xpertss',\n artifactId: 'studio-cli',\n ghPackagesRegistry: 'https://maven.pkg.github.com/xpertss/studio-cli',\n});\n\nproject.synth();\n```\n\nYou get everything from [`JavaMavenProject`](#javamavenproject) (modules included), plus `.github/workflows/publish-ghpackages.yml` - manual dispatch running `mvn -B deploy -DaltDeploymentRepository=github::<registry>`, authenticated with `GITHUB_TOKEN`. `ghPackagesRegistry` defaults to `https://maven.pkg.github.com/<repo>` derived from the repository URL.\n\n### GitHubActionProject\n\nA reusable GitHub Action or Workflow. This example scaffolds an action that stages a folder and, only if it changed, commits and pushes it.\n\nScaffold it with the `git_hub_action` type:\n\n```bash\nnpx projen new --from @xpertss/projen-types git_hub_action --name auto-commit --sonar-host-url https://sonarcloud.io\n```\n\n```typescript\n// .projenrc.ts\nimport { GitHubActionProject } from '@xpertss/projen-types';\n\nconst project = new GitHubActionProject({\n name: 'auto-commit',\n description: 'Stage a folder and, only if it changed, commit and push it',\n sonarHostUrl: 'https://sonarcloud.io', // optional - set it to add sonar.yml (see SonarCloud)\n sonarOrganization: 'xpertss', // default - your SonarCloud org key\n dogfood: {\n // Two scenario steps: the \"changed\" path and the \"no-op\" path are both\n // load-bearing behavior for this action (a double-commit or a\n // push-when-empty bug is the failure mode a hand-rolled inline-shell\n // alternative is most likely to introduce).\n scenario: [\n {\n name: 'Changed path',\n fixtureSteps: [\n 'echo \"$(date -u +%Y%m%dT%H%M%SZ)\" >> test/fixtures/dogfood-state.txt',\n ],\n inputs: {\n commit_message: 'test: dogfood',\n branches: 'test/dogfood',\n },\n assertions: [\n // step id defaults to a slug of `name` - here \"changed-path-0\"\n '[ \"${{ steps.changed-path-0.outputs.committed }}\" = \"true\" ]',\n ],\n },\n {\n // No fixture change this time - nothing new to commit.\n name: 'No-op path',\n id: 'no-op-check', // pin an explicit id instead of relying on the default slug\n inputs: {\n commit_message: 'test: dogfood',\n branches: 'test/dogfood',\n },\n assertions: [\n '[ \"${{ steps.no-op-check.outputs.committed }}\" = \"false\" ]',\n ],\n },\n ],\n cleanup: [\n 'git push origin --delete test/dogfood || true',\n ],\n },\n});\n\nproject.synth();\n```\n\nYou get:\n\n- `action.yml` and `auto-commit.sh` are hand-written - this type only lints their content via `build.yml`'s shellcheck/yamllint/actionlint checks.\n- `.github/workflows/build.yml` - lint gate: `apt`-installed shellcheck/yamllint plus a pinned, SHA-256-verified `actionlint` release binary. Runs on `pull_request` (and `workflow_dispatch`) only - the release path runs the same lint task inside its `release` job before tagging, so push-to-`main` runs would be a duplicate (alongside `sonar.yml` when Sonar is enabled).\n- `.github/workflows/test-dogfood.yml` - runs the `dogfood.scenario` steps above against this repo's own `action.yml` (via `uses: ./`), then the shared `cleanup`, on `workflow_dispatch`, every `pull_request`, and nightly. Omit `dogfood` and the workflow still exists, with one step that fails on every PR until you declare a scenario - AD-001 allows a dogfood to be missing loudly, never silently. A *partial* `dogfood` (a scenario with no cleanup) is a synth error.\n- `.github/workflows/sonar.yml` - SonarCloud scan via the Scanner CLI (the quality gate blocks the PR), scanning `action.yml`/`.github/workflows/**`/`**/*.sh` explicitly. Opt-in: generated only when `sonarHostUrl` is set (see [SonarCloud (opt-in)](#sonarcloud-opt-in)).\n- `.github/workflows/release.yml` - `feat:`/`fix:` commits on `main` bump the version, tag `vX.Y.Z`, and create a GitHub Release.\n- `.github/workflows/projen-drift-check.yml` and `workflow-change-notice.yml` - drift detection and a change notice, always included.\n- `package.json` (**private**, version source only), `.yamllint`, `LICENSE` (MIT by default), and a `README.md` template - all regenerated by `npx projen`.\n\nNeeds `PROJEN_GITHUB_TOKEN` (used for automated PR comments), and `SONAR_TOKEN` only when Sonar is enabled (see [SonarCloud (opt-in)](#sonarcloud-opt-in)). Onboard a brand-new action repo by running the `projen new` command above first (see [Getting started](#getting-started)), then replace the generated `.projenrc.ts` with the one above and run `npx projen`, then hand-write `action.yml`/`auto-commit.sh`/`test/fixtures/`. If you wrote the `.projenrc.ts` before running `projen new`, see [Starting from an existing `.projenrc.ts`](#starting-from-an-existing-projenrcts).\n\n## Java version support\n\n`javaVersion` picks the Java line a Java project targets. It defaults to `21`; the supported lines are `1.8` (alias `8`), `17`, `21` and `25`, and any other value fails the synth with that list. **Nothing in the generated build is hard-coded to one Java line.** Everything that depends on it comes from one table in this package, grouped by line:\n\n| | `1.8` | `17` | `21` | `25` |\n| --- | --- | --- | --- | --- |\n| Compiler level | `maven.compiler.source`/`target` = `1.8` (javac 8 has no `--release`) | `maven.compiler.release` = `17` | `release` = `21` | `release` = `25` |\n| Enforcer `requireJavaVersion` | `[1.8,)` | `[17,)` | `[21,)` | `[25,)` |\n| JUnit (`junit-bom`) | 5.14.4 (JUnit 6 needs Java 17) | 6.1.3 | 6.1.3 | 6.1.3 |\n| Default Spring Boot (`JavaSpringBootProject`) | 2.7.18 (Boot 3+ needs Java 17; a 3.x+ `springBootVersion` fails the synth) | 4.1.1 | 4.1.1 | 4.1.1 |\n| `flyway-maven-plugin` (`JavaServiceProject`) | 9.22.3 | 13.9.0 | 13.9.0 | 13.9.0 |\n| CI JDK (`actions/setup-java` `java-version`) | `8` | `17` | `21` | `25` |\n\nMaven plugins (the same on every line, because all of them run on JDK 8): `maven-compiler-plugin` 3.16.0, `maven-surefire-plugin`/`maven-failsafe-plugin` 3.6.0, `maven-jar-plugin` 3.5.1, `maven-enforcer-plugin` 3.6.3, `maven-source-plugin` 3.4.0 and `maven-javadoc-plugin` 3.12.0 (library only), `versions-maven-plugin` 2.22.0 (the `upgrade` report).\n\n**The enforcer.** `maven-enforcer-plugin` fails the build at the start when the JDK or Maven running it is older than the project needs. Without it you get confusing compiler errors later, or a jar built for the wrong runtime. Its Java rule always follows `javaVersion`. The Maven rule defaults to `[3.9,)`; change it with `minMavenVersion`, or drop the plugin entirely with `enforcer: false`. CI installs the matching JDK through `setup-java`, so the rule only fires on a developer machine with the wrong JDK. A Java 1.8 project builds on any newer JDK (the rule is a minimum), but the JDK 8 runtime API is guaranteed only when you build on JDK 8.\n\n**Overriding a version.** `pluginVersions` replaces any default in the table, keyed by `groupId/artifactId`, with an exact version:\n\n```typescript\nnew JavaMavenProject({\n name: 'svc',\n groupId: 'org.xpertss',\n artifactId: 'svc',\n pluginVersions: {\n 'org.apache.maven.plugins/maven-surefire-plugin': '3.5.6',\n 'org.junit/junit-bom': '5.14.4',\n },\n});\n```\n\n`project.pinnedVersion('org.apache.maven.plugins/maven-surefire-plugin')` returns the version in effect, for reuse in your own plugin config.\n\n**New Java lines** are one new row in this package's version table and a release. Until a line is listed, `javaVersion` rejects it rather than guessing.\n\n## Adding CI jobs to a Java project\n\n`build.yml` is exposed as `project.buildVerifyWorkflow`, so jobs are added through projen's `addJob` rather than `addOverride`. `project.ciSetupSteps` holds the steps every Maven job here needs: pinned Node, `npm ci`, and the project's JDK with a Maven cache. Every job in `build.yml` can then be made a required check on `main`.\n\n```typescript\nimport { github } from 'projen';\n\nproject.buildVerifyWorkflow.addJob('integration', {\n name: 'Container integration tests',\n runsOn: ['self-hosted', 'linux', 'fedora', 'podman'],\n permissions: { contents: github.workflows.JobPermission.READ },\n steps: [\n { name: 'Checkout', uses: 'actions/checkout@v7' },\n ...project.ciSetupSteps,\n { name: 'Integration tests', run: 'mvn -B verify -Pcontainer-its' },\n ],\n});\n```\n\n## Common options\n\nCDK project types (`CdkInfraProjectOptions` / `CdkAppProjectOptions`):\n\n| Option | Default | Description |\n| --- | --- | --- |\n| `name` | - (required) | Project name; must match `package.json` |\n| `cdkVersion` | `2.189.1` | AWS CDK version |\n| `gheTokenSecret` | `PROJEN_GITHUB_TOKEN` | GitHub secret holding projen's PAT |\n| `licensed` | `true` | Write a `LICENSE` |\n| `license` | `MIT` | SPDX identifier for the `LICENSE` |\n| `copyrightOwner` / `copyrightPeriod` | `Xpert Software` / current year | Named in the `LICENSE` |\n| `environments` | - (no `deploy` workflow) | Deploy targets for the `deploy` workflow; strings or `EnvironmentOptions` |\n| `ecrEcs` | - | `EcrEcsOptions` - `enabled`, `externalImageSource` (default `true`) |\n| `edgeResources` | - | Subset of `cloudfront`, `route53`, `apigateway`, `cognito`, `sqs` |\n| `database` | - (app only) | `DatabaseOptions` - `engine` (`postgres`/`mysql`/`dynamodb`, default `postgres`), `migrationTool` |\n| `appEntryPoint` | `src/app.ts` (app only) | Path of the generated application entrypoint |\n\nJava project types (`JavaMavenProjectOptions` and everything built on it - `JavaLibraryProjectOptions`, `JavaAppProjectOptions`, `JavaSpringBootProjectOptions`, `JavaServiceProjectOptions`):\n\n| Option | Default | Description |\n| --- | --- | --- |\n| `name` | - (required) | Project name |\n| `groupId` | - (required) | Maven group id |\n| `artifactId` | - (required) | Maven artifact id (of the reactor parent, in a multi-module project) |\n| `version` | `0.1.0` | Maven version; must be exact |\n| `description` / `url` | - | Written to the root pom |\n| `javaVersion` | `21` | Java line: `1.8`, `17`, `21`, `25` - see [Java version support](#java-version-support) |\n| `javaDistribution` | `temurin` | `actions/setup-java` distribution for CI |\n| `packaging` | `jar` | Root packaging while the project has no modules; a project with modules is always `pom` |\n| `sample` | `false` | Starter `Main` + test under the `groupId` package (single-module only) |\n| `enforcer` | `true` | `maven-enforcer-plugin` with Maven and Java version rules |\n| `minMavenVersion` | `3.9` | The enforcer's `requireMavenVersion` minimum |\n| `pluginVersions` | - | Exact-version overrides for this package's defaults, keyed by `groupId/artifactId` |\n| `licensed` | `true` | Write a `LICENSE` |\n| `license` | `MIT` | SPDX identifier for the `LICENSE` |\n| `copyrightOwner` / `copyrightPeriod` | `Xpert Software` / current year | Named in the `LICENSE` |\n| `editorconfig` | `true` | Write a projen-managed `.editorconfig` (also on the CDK and action types) |\n| `upgradeWorkflow` | `true` | Generate the nightly update report (`upgrade.yml`) |\n| `publishCodeIndex` | `true` | Generate the code index (`codeindex` task + `codeindex.yml`, committing `.xss/index.txt` to `main`); from `CommonCodeOptions` |\n| Sonar options | - | `sonarHostUrl`, `sonarOrganization`, `sonarTokenSecret`, `sonarPullRequestGate`, `sonarProjectKey` - SonarCloud is opt-in; see [SonarCloud (opt-in)](#sonarcloud-opt-in) |\n| `gheTokenSecret` | `PROJEN_GITHUB_TOKEN` | GitHub secret holding projen's PAT |\n| `springBootVersion` | newest Boot for `javaVersion` (Spring Boot types only) | Exact Spring Boot version: the BOM and `spring-boot-maven-plugin` |\n| `cdkDeployTargetRepo` | - (service only; `deploy-cdk.yml` fails until set) | Companion CDK repo (`owner/repo`) whose `deploy.yml` the deploy hook dispatches |\n| `cdkDeployHook` | `true` (service only) | Whether to generate `deploy-cdk.yml` at all |\n| `dockerRegistry` | `docker.io` (service only) | Registry the Docker image is pushed to |\n| `useFlyway` | `true` (service only) | Flyway plugin/dependency + `V1__init.sql` |\n\nWith the default MIT license, the generated `LICENSE` looks like:\n\n```\nMIT License\n\nCopyright (c) 2024-2026 Xpert Software\n\nPermission is hereby granted, ...\n```\n\nThe header line (`MIT License`) is specific to the license type — an `Apache-2.0` project would instead show `Apache-2.0`.\n\nOverride the license type and copyright fields:\n\n```typescript\nnew JavaMavenProject({\n name: 'my-lib',\n groupId: 'org.xpertss',\n artifactId: 'my-lib',\n license: 'Apache-2.0', // any SPDX id projen ships a template for\n copyrightOwner: 'Xpert Software',\n copyrightPeriod: '2024-2026',\n});\n```\n\n`EnvironmentOptions` for deploy targets:\n\n```text\ninterface EnvironmentOptions {\n readonly name: string; // e.g. \"dev\", \"stage\", \"prod\"\n readonly accountId?: string; // AWS account id (CDK deploys)\n readonly region?: string; // AWS region (CDK deploys)\n readonly requiresApproval?: boolean; // GitHub Environment approval gate (default false)\n}\n```\n\nPlain strings (`'dev'`) are shorthand for `{ name: 'dev' }`.\n\n`GitHubActionProjectOptions`:\n\n| Option | Default | Description |\n| --- | --- | --- |\n| `name` | - (required) | Project name |\n| `description` | - | One-line description; used in the default README template and recorded in the private `package.json` |\n| Sonar options | - | `sonarHostUrl`, `sonarOrganization`, `sonarTokenSecret`, `sonarPullRequestGate`, `sonarProjectKey` - SonarCloud is opt-in; see [SonarCloud (opt-in)](#sonarcloud-opt-in) |\n| `dogfood` | - (a `test-dogfood.yml` that fails until you declare one) | `ActionDogfoodOptions` - the scenario that exercises the action end-to-end via `uses: ./` |\n| `license` | `MIT` | SPDX identifier for the generated `LICENSE` |\n| `copyrightOwner` / `copyrightPeriod` | `Xpert Software` / current year | Named in the `LICENSE` |\n| `gheTokenSecret` | `PROJEN_GITHUB_TOKEN` | GitHub secret holding projen's PAT (drift-check PR comments); a dogfood that passes a `token` input must reference this secret in the step's `inputs` |\n\n`ActionDogfoodOptions`/`ActionDogfoodStep` - the dogfood scenario (`test-dogfood.yml`):\n\n```text\ninterface ActionDogfoodOptions {\n readonly scenario: ActionDogfoodStep[]; // one or more, run in order - required\n readonly cleanup: string[]; // shared, run once at the end with `if: always()` - required\n}\n\ninterface ActionDogfoodStep {\n readonly name: string; // labels this step-group's generated workflow steps\n readonly id?: string; // step id for the `uses: ./` call; default: a slug of `name`\n readonly fixtureSteps?: string[]; // shell, before the invocation (default: none)\n readonly inputs?: Record<string, string>; // `with:` for this invocation (default: none)\n readonly assertions: string[]; // shell, after the invocation - required; the step runs under set -euo pipefail so any failing line fails the job\n}\n```\n\nMost actions need exactly one `scenario` step. Actions with a re-run/no-op/idempotency behavior to verify (e.g. `auto-commit`'s no-op-on-no-change path, `create-pull-request`'s reuse-the-PR path) declare two - the second typically omits `fixtureSteps` so its invocation sees no new state, and its assertion checks the opposite outcome of the first. Reference an invocation's own outputs from a later assertion via `${{ steps.<id>.outputs.<name> }}`, using either the default slug or an explicit `id`.\n\n## SonarCloud (opt-in)\n\nSonarCloud is a shared, **opt-in** capability on every project type (CDK, Java, and GitHub Action). Set `sonarHostUrl` on any type and it generates a `.github/workflows/sonar.yml` that runs a pinned, SHA-256-verified Sonar Scanner CLI and blocks the PR on the quality gate. Omit it and no `sonar.yml` is generated. The Maven build itself never runs a Sonar step.\n\nShared options (accepted by the CDK types, every Java type, and `GitHubActionProject`):\n\n| Option | Default | Description |\n| --- | --- | --- |\n| `sonarHostUrl` | - (required to enable) | URL of your SonarCloud instance (e.g. `https://sonarcloud.io`); must be reachable from github.com-hosted runners |\n| `sonarOrganization` | `xpertss` | SonarCloud org key (`sonar.organization`); required by the Scanner CLI, not derived from the token |\n| `sonarProjectKey` | `${sonarOrganization}_${name}` | `sonar.projectKey` - the org + project key SonarCloud uses (e.g. `xpertss_create-pull-request`) |\n| `sonarTokenSecret` | `SONAR_TOKEN` | GitHub secret holding the Sonar token |\n| `sonarPullRequestGate` | `true` | Whether `sonar.yml` also runs on `pull_request` as a pass/fail gate |\n\nInclusions differ by type: a `GitHubActionProject` scans `action.yml`/`.github/workflows/**`/`**/*.sh` explicitly (YAML and shell are not in Sonar's default-recognized set), while the CDK and Java types rely on Sonar's default inclusions for their languages.\n\n```typescript\nnew CdkInfraProject({\n name: 'my-infra',\n environments: ['dev', 'prod'],\n sonarHostUrl: 'https://sonarcloud.io', // adds sonar.yml\n});\n```\n\nRequires the `SONAR_TOKEN` secret (rename it with `sonarTokenSecret`).\n\n## Required GitHub secrets\n\n| Secret | Used by | Notes |\n| --- | --- | --- |\n| `PROJEN_GITHUB_TOKEN` | all types | PAT for projen's self-mutation/automation; override via `gheTokenSecret` |\n| `SONAR_TOKEN` | any type with `sonarHostUrl` set | SonarCloud scan in `sonar.yml`; override the secret name via `sonarTokenSecret` |\n| `DOCKER_USERNAME` / `DOCKER_PASSWORD` | `JavaServiceProject` | Docker image push |\n| `MAVEN_GPG_PRIVATE_KEY`, `MAVEN_GPG_PASSPHRASE`, `MAVEN_CENTRAL_USERNAME`, `MAVEN_CENTRAL_PASSWORD` | `JavaLibraryProject` without `mavenCentralOidc` | Not needed with OIDC trusted publishing |\n\n## Customizing `.gitignore`\n\nEvery project type already ignores JetBrains IDE state (`/.idea/*`) and the `/spec/` directory with all of its subdirectories (local plans and specs, never committed). The Java types also ignore Maven `target/` output and Eclipse files. To ignore more, call the inherited `addGitIgnore()` method after constructing the project in your `.projenrc.ts`:\n\n```typescript\n// .projenrc.ts\nimport { CdkInfraProject } from '@xpertss/projen-types';\n\nconst project = new CdkInfraProject({\n name: 'my-infra',\n});\n\nproject.addGitIgnore('*.log');\nproject.addGitIgnore('.env.*');\nproject.addGitIgnore('/build/');\n\nproject.synth();\n```\n\nThe same works for every type - just swap the class (e.g. `JavaServiceProject`, `GitHubActionProject`). Each call takes one standard gitignore glob pattern. There is no `gitignore` constructor option: the project types' option interfaces do not expose one, so patterns go through `addGitIgnore()` instead.\n\nRe-run `npx projen` after editing - `.gitignore` is a generated file, so hand-editing it is caught by the drift check.\n\n## Troubleshooting\n\n**`Unable to find projen project. Use \"projen new\" to create a new project.`**\nIf the directory already has a `.projenrc.ts`, this error means the rc has\nnever been synthesized (there's no `.projen/tasks.json`), not that you need a\nnew project. See [Starting from an existing `.projenrc.ts`](#starting-from-an-existing-projenrcts).\n\n## Going further\n\nThe project types are composed from smaller components you can also attach to your own projects:\n\n| Component | Applies to | Purpose |\n| --- | --- | --- |\n| `AppRuntimeScaffold` | `NodeProject` | App source skeleton (`app.ts` + handlers) |\n| `DatabaseComponent` | `NodeProject` | Database construct stub + migration tool wiring |\n| `EcrEcsConstructs` | `Project` | ECR + Fargate ECS construct helper |\n| `EdgeNetworkingConstructs` | `Project` | Per-resource edge networking construct helpers |\n| `MavenPom` | `Project` | A `pom.xml` with modules, BOM imports, dependency/plugin management, and exact versions only |\n| `MavenModule` | `JavaMavenProject` | One module of a reactor (created by `addModule()`) |\n| `MavenUpgradeReport` | `GitHubProject` | Nightly report-only `upgrade.yml` |\n| `MavenCentralPublish` | `JavaMavenProject` | Manual-dispatch Maven Central publish workflow |\n| `DockerPublish` | `JavaMavenProject` | Manual-dispatch Docker build+push workflow |\n| `GitHubPackagesPublish` | `JavaMavenProject` | Manual-dispatch GitHub Packages publish workflow |\n| `FlywayMigration` | `JavaSpringBootProject` | Flyway plugin/dependency + migrations directory |\n| `CdkDeployHook` | `JavaMavenProject` | Manual-dispatch workflow that triggers `deploy.yml` in a companion CDK repo |\n| `CodeIndexWorkflow` | `JavaMavenProject` | Code index generation on push to `main` |\n| `ActionBuildWorkflow` | `GitHubProject` | The `lint` task (shellcheck/yamllint/pinned actionlint) + `build.yml` |\n| `ActionDogfoodWorkflow` | `GitHubProject` | `test-dogfood.yml` from an `ActionDogfoodOptions` scenario |\n| `SonarWorkflow` | `GitHubProject` | `sonar.yml` (SonarCloud scan via the Scanner CLI); opt-in, shared by every type via `sonarHostUrl` |\n\nExample - adding a Docker publish to a `JavaMavenProject` (`JavaServiceProject` is the packaged version of this, with Spring Boot):\n\n```typescript\nimport { DockerPublish, JavaMavenProject } from '@xpertss/projen-types';\n\nconst project = new JavaMavenProject({\n name: 'my-service',\n groupId: 'org.xpertss',\n artifactId: 'my-service',\n});\n\nnew DockerPublish(project, { dockerRegistry: 'ghcr.io' });\n\nproject.synth();\n```\n\nThe full API reference, including every option and property, is in [API.md](./API.md).\n"
110
110
  },
111
111
  "repository": {
112
112
  "type": "git",
@@ -656,7 +656,7 @@
656
656
  "kind": "interface",
657
657
  "locationInModule": {
658
658
  "filename": "src/java/options.ts",
659
- "line": 150
659
+ "line": 148
660
660
  },
661
661
  "name": "CdkDeployHookOptions",
662
662
  "properties": [
@@ -670,7 +670,7 @@
670
670
  "immutable": true,
671
671
  "locationInModule": {
672
672
  "filename": "src/java/options.ts",
673
- "line": 158
673
+ "line": 156
674
674
  },
675
675
  "name": "targetRepo",
676
676
  "optional": true,
@@ -911,7 +911,7 @@
911
911
  "base": "projen.Component",
912
912
  "docs": {
913
913
  "stability": "experimental",
914
- "summary": "Generates a code index and publishes it to `.cai/` on checkin."
914
+ "summary": "Generates a code index and publishes it to `.xss/` on checkin."
915
915
  },
916
916
  "fqn": "@xpertss/projen-types.CodeIndexWorkflow",
917
917
  "initializer": {
@@ -1098,6 +1098,42 @@
1098
1098
  ],
1099
1099
  "symbolId": "src/cdk/options:CommonCdkOptions"
1100
1100
  },
1101
+ "@xpertss/projen-types.CommonCodeOptions": {
1102
+ "assembly": "@xpertss/projen-types",
1103
+ "datatype": true,
1104
+ "docs": {
1105
+ "stability": "experimental",
1106
+ "summary": "Options shared by project types that own source code."
1107
+ },
1108
+ "fqn": "@xpertss/projen-types.CommonCodeOptions",
1109
+ "kind": "interface",
1110
+ "locationInModule": {
1111
+ "filename": "src/common/code-options.ts",
1112
+ "line": 2
1113
+ },
1114
+ "name": "CommonCodeOptions",
1115
+ "properties": [
1116
+ {
1117
+ "abstract": true,
1118
+ "docs": {
1119
+ "default": "true",
1120
+ "stability": "experimental",
1121
+ "summary": "Generate the `codeindex` task and the `codeindex.yml` workflow, which on push to `main` regenerates `.xss/index.txt` (a sorted list of the repo's source files) and commits it straight to `main` as `chore: update code index`. The commit is pushed with the workflow's `GITHUB_TOKEN`, so it starts no further workflows (no build, Sonar scan or release); the workflow's bot must be allowed to push to `main`."
1122
+ },
1123
+ "immutable": true,
1124
+ "locationInModule": {
1125
+ "filename": "src/common/code-options.ts",
1126
+ "line": 13
1127
+ },
1128
+ "name": "publishCodeIndex",
1129
+ "optional": true,
1130
+ "type": {
1131
+ "primitive": "boolean"
1132
+ }
1133
+ }
1134
+ ],
1135
+ "symbolId": "src/common/code-options:CommonCodeOptions"
1136
+ },
1101
1137
  "@xpertss/projen-types.CommonJavaOptions": {
1102
1138
  "assembly": "@xpertss/projen-types",
1103
1139
  "datatype": true,
@@ -1106,12 +1142,13 @@
1106
1142
  },
1107
1143
  "fqn": "@xpertss/projen-types.CommonJavaOptions",
1108
1144
  "interfaces": [
1109
- "@xpertss/projen-types.SonarScanOptions"
1145
+ "@xpertss/projen-types.SonarScanOptions",
1146
+ "@xpertss/projen-types.CommonCodeOptions"
1110
1147
  ],
1111
1148
  "kind": "interface",
1112
1149
  "locationInModule": {
1113
1150
  "filename": "src/java/options.ts",
1114
- "line": 4
1151
+ "line": 5
1115
1152
  },
1116
1153
  "name": "CommonJavaOptions",
1117
1154
  "properties": [
@@ -1123,7 +1160,7 @@
1123
1160
  "immutable": true,
1124
1161
  "locationInModule": {
1125
1162
  "filename": "src/java/options.ts",
1126
- "line": 7
1163
+ "line": 8
1127
1164
  },
1128
1165
  "name": "artifactId",
1129
1166
  "type": {
@@ -1138,7 +1175,7 @@
1138
1175
  "immutable": true,
1139
1176
  "locationInModule": {
1140
1177
  "filename": "src/java/options.ts",
1141
- "line": 6
1178
+ "line": 7
1142
1179
  },
1143
1180
  "name": "groupId",
1144
1181
  "type": {
@@ -1153,7 +1190,7 @@
1153
1190
  "immutable": true,
1154
1191
  "locationInModule": {
1155
1192
  "filename": "src/java/options.ts",
1156
- "line": 5
1193
+ "line": 6
1157
1194
  },
1158
1195
  "name": "name",
1159
1196
  "type": {
@@ -1170,7 +1207,7 @@
1170
1207
  "immutable": true,
1171
1208
  "locationInModule": {
1172
1209
  "filename": "src/java/options.ts",
1173
- "line": 101
1210
+ "line": 102
1174
1211
  },
1175
1212
  "name": "copyrightOwner",
1176
1213
  "optional": true,
@@ -1188,7 +1225,7 @@
1188
1225
  "immutable": true,
1189
1226
  "locationInModule": {
1190
1227
  "filename": "src/java/options.ts",
1191
- "line": 107
1228
+ "line": 108
1192
1229
  },
1193
1230
  "name": "copyrightPeriod",
1194
1231
  "optional": true,
@@ -1206,7 +1243,7 @@
1206
1243
  "immutable": true,
1207
1244
  "locationInModule": {
1208
1245
  "filename": "src/java/options.ts",
1209
- "line": 13
1246
+ "line": 14
1210
1247
  },
1211
1248
  "name": "description",
1212
1249
  "optional": true,
@@ -1224,7 +1261,7 @@
1224
1261
  "immutable": true,
1225
1262
  "locationInModule": {
1226
1263
  "filename": "src/java/options.ts",
1227
- "line": 113
1264
+ "line": 114
1228
1265
  },
1229
1266
  "name": "editorconfig",
1230
1267
  "optional": true,
@@ -1243,7 +1280,7 @@
1243
1280
  "immutable": true,
1244
1281
  "locationInModule": {
1245
1282
  "filename": "src/java/options.ts",
1246
- "line": 65
1283
+ "line": 66
1247
1284
  },
1248
1285
  "name": "enforcer",
1249
1286
  "optional": true,
@@ -1260,7 +1297,7 @@
1260
1297
  "immutable": true,
1261
1298
  "locationInModule": {
1262
1299
  "filename": "src/java/options.ts",
1263
- "line": 19
1300
+ "line": 20
1264
1301
  },
1265
1302
  "name": "gheTokenSecret",
1266
1303
  "optional": true,
@@ -1278,7 +1315,7 @@
1278
1315
  "immutable": true,
1279
1316
  "locationInModule": {
1280
1317
  "filename": "src/java/options.ts",
1281
- "line": 38
1318
+ "line": 39
1282
1319
  },
1283
1320
  "name": "javaDistribution",
1284
1321
  "optional": true,
@@ -1297,7 +1334,7 @@
1297
1334
  "immutable": true,
1298
1335
  "locationInModule": {
1299
1336
  "filename": "src/java/options.ts",
1300
- "line": 32
1337
+ "line": 33
1301
1338
  },
1302
1339
  "name": "javaVersion",
1303
1340
  "optional": true,
@@ -1315,7 +1352,7 @@
1315
1352
  "immutable": true,
1316
1353
  "locationInModule": {
1317
1354
  "filename": "src/java/options.ts",
1318
- "line": 95
1355
+ "line": 96
1319
1356
  },
1320
1357
  "name": "license",
1321
1358
  "optional": true,
@@ -1333,7 +1370,7 @@
1333
1370
  "immutable": true,
1334
1371
  "locationInModule": {
1335
1372
  "filename": "src/java/options.ts",
1336
- "line": 89
1373
+ "line": 90
1337
1374
  },
1338
1375
  "name": "licensed",
1339
1376
  "optional": true,
@@ -1352,7 +1389,7 @@
1352
1389
  "immutable": true,
1353
1390
  "locationInModule": {
1354
1391
  "filename": "src/java/options.ts",
1355
- "line": 73
1392
+ "line": 74
1356
1393
  },
1357
1394
  "name": "minMavenVersion",
1358
1395
  "optional": true,
@@ -1371,7 +1408,7 @@
1371
1408
  "immutable": true,
1372
1409
  "locationInModule": {
1373
1410
  "filename": "src/java/options.ts",
1374
- "line": 47
1411
+ "line": 48
1375
1412
  },
1376
1413
  "name": "packaging",
1377
1414
  "optional": true,
@@ -1389,7 +1426,7 @@
1389
1426
  "immutable": true,
1390
1427
  "locationInModule": {
1391
1428
  "filename": "src/java/options.ts",
1392
- "line": 83
1429
+ "line": 84
1393
1430
  },
1394
1431
  "name": "pluginVersions",
1395
1432
  "optional": true,
@@ -1413,7 +1450,7 @@
1413
1450
  "immutable": true,
1414
1451
  "locationInModule": {
1415
1452
  "filename": "src/java/options.ts",
1416
- "line": 55
1453
+ "line": 56
1417
1454
  },
1418
1455
  "name": "sample",
1419
1456
  "optional": true,
@@ -1431,7 +1468,7 @@
1431
1468
  "immutable": true,
1432
1469
  "locationInModule": {
1433
1470
  "filename": "src/java/options.ts",
1434
- "line": 122
1471
+ "line": 123
1435
1472
  },
1436
1473
  "name": "upgradeWorkflow",
1437
1474
  "optional": true,
@@ -1449,7 +1486,7 @@
1449
1486
  "immutable": true,
1450
1487
  "locationInModule": {
1451
1488
  "filename": "src/java/options.ts",
1452
- "line": 16
1489
+ "line": 17
1453
1490
  },
1454
1491
  "name": "url",
1455
1492
  "optional": true,
@@ -1466,7 +1503,7 @@
1466
1503
  "immutable": true,
1467
1504
  "locationInModule": {
1468
1505
  "filename": "src/java/options.ts",
1469
- "line": 10
1506
+ "line": 11
1470
1507
  },
1471
1508
  "name": "version",
1472
1509
  "optional": true,
@@ -2263,7 +2300,7 @@
2263
2300
  "kind": "interface",
2264
2301
  "locationInModule": {
2265
2302
  "filename": "src/java/options.ts",
2266
- "line": 199
2303
+ "line": 197
2267
2304
  },
2268
2305
  "name": "JavaAppProjectOptions",
2269
2306
  "properties": [
@@ -2276,7 +2313,7 @@
2276
2313
  "immutable": true,
2277
2314
  "locationInModule": {
2278
2315
  "filename": "src/java/options.ts",
2279
- "line": 201
2316
+ "line": 199
2280
2317
  },
2281
2318
  "name": "ghPackagesRegistry",
2282
2319
  "optional": true,
@@ -2302,7 +2339,7 @@
2302
2339
  },
2303
2340
  "locationInModule": {
2304
2341
  "filename": "src/java/java-library-project.ts",
2305
- "line": 13
2342
+ "line": 12
2306
2343
  },
2307
2344
  "parameters": [
2308
2345
  {
@@ -2316,7 +2353,7 @@
2316
2353
  "kind": "class",
2317
2354
  "locationInModule": {
2318
2355
  "filename": "src/java/java-library-project.ts",
2319
- "line": 12
2356
+ "line": 11
2320
2357
  },
2321
2358
  "name": "JavaLibraryProject",
2322
2359
  "symbolId": "src/java/java-library-project:JavaLibraryProject"
@@ -2334,7 +2371,7 @@
2334
2371
  "kind": "interface",
2335
2372
  "locationInModule": {
2336
2373
  "filename": "src/java/options.ts",
2337
- "line": 142
2374
+ "line": 143
2338
2375
  },
2339
2376
  "name": "JavaLibraryProjectOptions",
2340
2377
  "properties": [
@@ -2347,30 +2384,13 @@
2347
2384
  "immutable": true,
2348
2385
  "locationInModule": {
2349
2386
  "filename": "src/java/options.ts",
2350
- "line": 144
2387
+ "line": 145
2351
2388
  },
2352
2389
  "name": "mavenCentralOidc",
2353
2390
  "optional": true,
2354
2391
  "type": {
2355
2392
  "primitive": "boolean"
2356
2393
  }
2357
- },
2358
- {
2359
- "abstract": true,
2360
- "docs": {
2361
- "default": "true",
2362
- "stability": "experimental"
2363
- },
2364
- "immutable": true,
2365
- "locationInModule": {
2366
- "filename": "src/java/options.ts",
2367
- "line": 147
2368
- },
2369
- "name": "publishCodeIndex",
2370
- "optional": true,
2371
- "type": {
2372
- "primitive": "boolean"
2373
- }
2374
2394
  }
2375
2395
  ],
2376
2396
  "symbolId": "src/java/options:JavaLibraryProjectOptions"
@@ -2379,7 +2399,7 @@
2379
2399
  "assembly": "@xpertss/projen-types",
2380
2400
  "base": "projen.github.GitHubProject",
2381
2401
  "docs": {
2382
- "remarks": "It is the base of every Java type in this package and is\nusable on its own (`projen new ... java_maven`).\n\n- The pom is written by this package (`MavenPom`), not projen's\n `java.Pom`: exact versions only, BOM imports, `<modules>`,\n `<dependencyManagement>`, `<pluginManagement>`.\n- Everything Java-version-dependent (compiler level, enforcer rule, JUnit\n line, CI JDK) follows `javaVersion`.\n- With no `addModule()` calls the root pom is the artifact. After the\n first `addModule()` it is a `pom`-packaged reactor parent and the\n modules inherit its plugins and test dependencies.\n- `npx projen build` synthesizes, then runs Maven once: `mvn -B verify`\n (unit tests via surefire, `*IT` tests via failsafe).\n - CI: a PR build, the projen drift check, an optional SonarCloud scan\n (`sonar.yml`, when `sonarHostUrl` is set), and a nightly report-only\n update check.",
2402
+ "remarks": "It is the base of every Java type in this package and is\nusable on its own (`projen new ... java_maven`).\n\n- The pom is written by this package (`MavenPom`), not projen's\n `java.Pom`: exact versions only, BOM imports, `<modules>`,\n `<dependencyManagement>`, `<pluginManagement>`.\n- Everything Java-version-dependent (compiler level, enforcer rule, JUnit\n line, CI JDK) follows `javaVersion`.\n- With no `addModule()` calls the root pom is the artifact. After the\n first `addModule()` it is a `pom`-packaged reactor parent and the\n modules inherit its plugins and test dependencies.\n- `npx projen build` synthesizes, then runs Maven once: `mvn -B verify`\n (unit tests via surefire, `*IT` tests via failsafe).\n - CI: a PR build, the projen drift check, an optional SonarCloud scan\n (`sonar.yml`, when `sonarHostUrl` is set), a nightly report-only\n update check, and the code index (`codeindex.yml`, on push to `main`).",
2383
2403
  "stability": "experimental",
2384
2404
  "summary": "Baseline Maven project, single- or multi-module, with no framework assumptions."
2385
2405
  },
@@ -2390,7 +2410,7 @@
2390
2410
  },
2391
2411
  "locationInModule": {
2392
2412
  "filename": "src/java/java-maven-base.ts",
2393
- "line": 90
2413
+ "line": 91
2394
2414
  },
2395
2415
  "parameters": [
2396
2416
  {
@@ -2404,7 +2424,7 @@
2404
2424
  "kind": "class",
2405
2425
  "locationInModule": {
2406
2426
  "filename": "src/java/java-maven-base.ts",
2407
- "line": 58
2427
+ "line": 59
2408
2428
  },
2409
2429
  "methods": [
2410
2430
  {
@@ -2414,7 +2434,7 @@
2414
2434
  },
2415
2435
  "locationInModule": {
2416
2436
  "filename": "src/java/java-maven-base.ts",
2417
- "line": 280
2437
+ "line": 285
2418
2438
  },
2419
2439
  "name": "addBom",
2420
2440
  "parameters": [
@@ -2437,7 +2457,7 @@
2437
2457
  },
2438
2458
  "locationInModule": {
2439
2459
  "filename": "src/java/java-maven-base.ts",
2440
- "line": 302
2460
+ "line": 307
2441
2461
  },
2442
2462
  "name": "addDependency",
2443
2463
  "parameters": [
@@ -2459,7 +2479,7 @@
2459
2479
  },
2460
2480
  "locationInModule": {
2461
2481
  "filename": "src/java/java-maven-base.ts",
2462
- "line": 290
2482
+ "line": 295
2463
2483
  },
2464
2484
  "name": "addManagedDependency",
2465
2485
  "parameters": [
@@ -2482,7 +2502,7 @@
2482
2502
  },
2483
2503
  "locationInModule": {
2484
2504
  "filename": "src/java/java-maven-base.ts",
2485
- "line": 249
2505
+ "line": 254
2486
2506
  },
2487
2507
  "name": "addModule",
2488
2508
  "parameters": [
@@ -2506,7 +2526,7 @@
2506
2526
  },
2507
2527
  "locationInModule": {
2508
2528
  "filename": "src/java/java-maven-base.ts",
2509
- "line": 312
2529
+ "line": 317
2510
2530
  },
2511
2531
  "name": "addPlugin",
2512
2532
  "parameters": [
@@ -2532,7 +2552,7 @@
2532
2552
  },
2533
2553
  "locationInModule": {
2534
2554
  "filename": "src/java/java-maven-base.ts",
2535
- "line": 307
2555
+ "line": 312
2536
2556
  },
2537
2557
  "name": "addTestDependency",
2538
2558
  "parameters": [
@@ -2551,7 +2571,7 @@
2551
2571
  },
2552
2572
  "locationInModule": {
2553
2573
  "filename": "src/java/java-maven-base.ts",
2554
- "line": 340
2574
+ "line": 345
2555
2575
  },
2556
2576
  "name": "maxSpringBootMajor",
2557
2577
  "protected": true,
@@ -2569,7 +2589,7 @@
2569
2589
  },
2570
2590
  "locationInModule": {
2571
2591
  "filename": "src/java/java-maven-base.ts",
2572
- "line": 328
2592
+ "line": 333
2573
2593
  },
2574
2594
  "name": "pinnedVersion",
2575
2595
  "parameters": [
@@ -2593,7 +2613,7 @@
2593
2613
  },
2594
2614
  "locationInModule": {
2595
2615
  "filename": "src/java/java-maven-base.ts",
2596
- "line": 316
2616
+ "line": 321
2597
2617
  },
2598
2618
  "name": "preSynthesize",
2599
2619
  "overrides": "projen.Project"
@@ -2609,7 +2629,7 @@
2609
2629
  "immutable": true,
2610
2630
  "locationInModule": {
2611
2631
  "filename": "src/java/java-maven-base.ts",
2612
- "line": 73
2632
+ "line": 74
2613
2633
  },
2614
2634
  "name": "buildVerifyWorkflow",
2615
2635
  "type": {
@@ -2624,7 +2644,7 @@
2624
2644
  "immutable": true,
2625
2645
  "locationInModule": {
2626
2646
  "filename": "src/java/java-maven-base.ts",
2627
- "line": 79
2647
+ "line": 80
2628
2648
  },
2629
2649
  "name": "ciSetupSteps",
2630
2650
  "type": {
@@ -2644,7 +2664,7 @@
2644
2664
  "immutable": true,
2645
2665
  "locationInModule": {
2646
2666
  "filename": "src/java/java-maven-base.ts",
2647
- "line": 63
2667
+ "line": 64
2648
2668
  },
2649
2669
  "name": "javaVersion",
2650
2670
  "type": {
@@ -2659,7 +2679,7 @@
2659
2679
  "immutable": true,
2660
2680
  "locationInModule": {
2661
2681
  "filename": "src/java/java-maven-base.ts",
2662
- "line": 238
2682
+ "line": 243
2663
2683
  },
2664
2684
  "name": "modules",
2665
2685
  "type": {
@@ -2679,7 +2699,7 @@
2679
2699
  "immutable": true,
2680
2700
  "locationInModule": {
2681
2701
  "filename": "src/java/java-maven-base.ts",
2682
- "line": 60
2702
+ "line": 61
2683
2703
  },
2684
2704
  "name": "pom",
2685
2705
  "type": {
@@ -2694,7 +2714,7 @@
2694
2714
  "immutable": true,
2695
2715
  "locationInModule": {
2696
2716
  "filename": "src/java/java-maven-base.ts",
2697
- "line": 66
2717
+ "line": 67
2698
2718
  },
2699
2719
  "name": "upgradeTask",
2700
2720
  "type": {
@@ -2709,7 +2729,7 @@
2709
2729
  "immutable": true,
2710
2730
  "locationInModule": {
2711
2731
  "filename": "src/java/java-maven-base.ts",
2712
- "line": 82
2732
+ "line": 83
2713
2733
  },
2714
2734
  "name": "sonarWorkflow",
2715
2735
  "optional": true,
@@ -2734,7 +2754,7 @@
2734
2754
  "kind": "interface",
2735
2755
  "locationInModule": {
2736
2756
  "filename": "src/java/options.ts",
2737
- "line": 126
2757
+ "line": 127
2738
2758
  },
2739
2759
  "name": "JavaMavenProjectOptions",
2740
2760
  "symbolId": "src/java/options:JavaMavenProjectOptions"
@@ -2814,7 +2834,7 @@
2814
2834
  "kind": "interface",
2815
2835
  "locationInModule": {
2816
2836
  "filename": "src/java/options.ts",
2817
- "line": 161
2837
+ "line": 159
2818
2838
  },
2819
2839
  "name": "JavaServiceProjectOptions",
2820
2840
  "properties": [
@@ -2828,7 +2848,7 @@
2828
2848
  "immutable": true,
2829
2849
  "locationInModule": {
2830
2850
  "filename": "src/java/options.ts",
2831
- "line": 188
2851
+ "line": 186
2832
2852
  },
2833
2853
  "name": "cdkDeployHook",
2834
2854
  "optional": true,
@@ -2850,7 +2870,7 @@
2850
2870
  "immutable": true,
2851
2871
  "locationInModule": {
2852
2872
  "filename": "src/java/options.ts",
2853
- "line": 181
2873
+ "line": 179
2854
2874
  },
2855
2875
  "name": "cdkDeployTargetRepo",
2856
2876
  "optional": true,
@@ -2867,7 +2887,7 @@
2867
2887
  "immutable": true,
2868
2888
  "locationInModule": {
2869
2889
  "filename": "src/java/options.ts",
2870
- "line": 163
2890
+ "line": 161
2871
2891
  },
2872
2892
  "name": "dockerRegistry",
2873
2893
  "optional": true,
@@ -2885,7 +2905,7 @@
2885
2905
  "immutable": true,
2886
2906
  "locationInModule": {
2887
2907
  "filename": "src/java/options.ts",
2888
- "line": 196
2908
+ "line": 194
2889
2909
  },
2890
2910
  "name": "environments",
2891
2911
  "optional": true,
@@ -2916,7 +2936,7 @@
2916
2936
  "immutable": true,
2917
2937
  "locationInModule": {
2918
2938
  "filename": "src/java/options.ts",
2919
- "line": 166
2939
+ "line": 164
2920
2940
  },
2921
2941
  "name": "useFlyway",
2922
2942
  "optional": true,
@@ -3030,7 +3050,7 @@
3030
3050
  "kind": "interface",
3031
3051
  "locationInModule": {
3032
3052
  "filename": "src/java/options.ts",
3033
- "line": 129
3053
+ "line": 130
3034
3054
  },
3035
3055
  "name": "JavaSpringBootProjectOptions",
3036
3056
  "properties": [
@@ -3045,7 +3065,7 @@
3045
3065
  "immutable": true,
3046
3066
  "locationInModule": {
3047
3067
  "filename": "src/java/options.ts",
3048
- "line": 139
3068
+ "line": 140
3049
3069
  },
3050
3070
  "name": "springBootVersion",
3051
3071
  "optional": true,
@@ -3400,7 +3420,7 @@
3400
3420
  "kind": "interface",
3401
3421
  "locationInModule": {
3402
3422
  "filename": "src/java/options.ts",
3403
- "line": 205
3423
+ "line": 203
3404
3424
  },
3405
3425
  "name": "MavenModuleOptions",
3406
3426
  "properties": [
@@ -3412,7 +3432,7 @@
3412
3432
  "immutable": true,
3413
3433
  "locationInModule": {
3414
3434
  "filename": "src/java/options.ts",
3415
- "line": 212
3435
+ "line": 210
3416
3436
  },
3417
3437
  "name": "artifactId",
3418
3438
  "type": {
@@ -3428,7 +3448,7 @@
3428
3448
  "immutable": true,
3429
3449
  "locationInModule": {
3430
3450
  "filename": "src/java/options.ts",
3431
- "line": 210
3451
+ "line": 208
3432
3452
  },
3433
3453
  "name": "dir",
3434
3454
  "type": {
@@ -3444,7 +3464,7 @@
3444
3464
  "immutable": true,
3445
3465
  "locationInModule": {
3446
3466
  "filename": "src/java/options.ts",
3447
- "line": 218
3467
+ "line": 216
3448
3468
  },
3449
3469
  "name": "description",
3450
3470
  "optional": true,
@@ -3461,7 +3481,7 @@
3461
3481
  "immutable": true,
3462
3482
  "locationInModule": {
3463
3483
  "filename": "src/java/options.ts",
3464
- "line": 215
3484
+ "line": 213
3465
3485
  },
3466
3486
  "name": "name",
3467
3487
  "optional": true,
@@ -3478,7 +3498,7 @@
3478
3498
  "immutable": true,
3479
3499
  "locationInModule": {
3480
3500
  "filename": "src/java/options.ts",
3481
- "line": 221
3501
+ "line": 219
3482
3502
  },
3483
3503
  "name": "packaging",
3484
3504
  "optional": true,
@@ -4766,6 +4786,6 @@
4766
4786
  "symbolId": "src/common/workflow-change-notice-workflow:WorkflowChangeNoticeWorkflowOptions"
4767
4787
  }
4768
4788
  },
4769
- "version": "0.0.27",
4770
- "fingerprint": "+ueCEoQPIta22ZLy/JbGZgnF6VGCfVTNi6TpbSCpAwk="
4789
+ "version": "0.0.28",
4790
+ "fingerprint": "yE+3Ebk//Nub+ekqIij+15PeXA8HIRl3/+uoX0wNZGw="
4771
4791
  }