├── examples ├── hybrid-module │ ├── README.md │ ├── .github │ │ ├── pull_request_template.md │ │ └── workflows │ │ │ ├── pull-request-lint.yml │ │ │ ├── upgrade-main.yml │ │ │ ├── build.yml │ │ │ └── release.yml │ ├── terraform │ │ ├── main.tf │ │ ├── README.md │ │ └── provider.tf │ ├── .prettierrc.json │ ├── .prettierignore │ ├── src │ │ ├── __tests__ │ │ │ ├── __snapshots__ │ │ │ │ └── index-test.ts.snap │ │ │ └── index-test.ts │ │ ├── my-awesome-module.md │ │ ├── cdktf.json │ │ ├── index.ts │ │ └── tfModules.ts │ ├── construct-examples │ │ ├── cdktf.json │ │ ├── README.md │ │ ├── index.ts │ │ └── basic.ts │ ├── scripts │ │ ├── tf-module-test.sh │ │ └── copy-modules.sh │ ├── modules │ │ └── my-awesome-module │ │ │ ├── cdk.tf.json │ │ │ └── README.md │ ├── .npmignore │ ├── .projen │ │ ├── files.json │ │ ├── deps.json │ │ └── tasks.json │ ├── tsconfig.dev.json │ ├── .gitattributes │ ├── .projenrc.ts │ ├── .gitignore │ ├── .eslintrc.json │ ├── package.json │ └── API.md └── terraform-module │ ├── README.md │ ├── src │ ├── .gen │ │ ├── versions.json │ │ └── constraints.json │ ├── terraformModules.ts │ ├── index.ts │ ├── __tests__ │ │ └── index-test.ts │ └── cdktf.json │ ├── .github │ ├── pull_request_template.md │ └── workflows │ │ ├── pull-request-lint.yml │ │ ├── upgrade-main.yml │ │ ├── build.yml │ │ └── release.yml │ ├── .prettierrc.json │ ├── .prettierignore │ ├── .npmignore │ ├── .projen │ ├── files.json │ ├── deps.json │ └── tasks.json │ ├── .projenrc.ts │ ├── tsconfig.dev.json │ ├── .gitattributes │ ├── .gitignore │ ├── .eslintrc.json │ └── package.json ├── .prettierrc.json ├── .prettierignore ├── src ├── index.ts ├── defaults.ts ├── terraform-module.ts └── publishing.ts ├── .github ├── CODEOWNERS ├── dependabot.yml ├── workflows │ ├── pull-request-lint.yml │ ├── automerge.yml │ ├── auto-approve.yml │ ├── upgrade-projen.yml │ ├── upgrade-main.yml │ ├── upgrade-cdktf.yml │ ├── build.yml │ ├── release.yml │ └── upgrade-jsii-typescript.yml └── MAINTENANCE.md ├── .npmignore ├── projenrc ├── customized-license.ts ├── automerge.ts ├── auto-approve.ts ├── upgrade-projen.ts ├── upgrade-cdktf.ts └── upgrade-jsii-typescript.ts ├── scripts ├── update-projen.sh ├── update-jsii-typescript.sh ├── check-jsii-versions.js └── update-cdktf.sh ├── .copywrite.hcl ├── .projen ├── files.json └── deps.json ├── tsconfig.dev.json ├── .gitattributes ├── .gitignore ├── test ├── hybrid-module.test.ts ├── terraform-module.test.ts └── helper.ts ├── .eslintrc.json ├── package.json └── .projenrc.ts /examples/hybrid-module/README.md: -------------------------------------------------------------------------------- 1 | # replace this -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "overrides": [] 3 | } 4 | -------------------------------------------------------------------------------- /examples/terraform-module/README.md: -------------------------------------------------------------------------------- 1 | # replace this -------------------------------------------------------------------------------- /examples/terraform-module/src/.gen/versions.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /examples/hybrid-module/.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Fixes # -------------------------------------------------------------------------------- /examples/hybrid-module/terraform/main.tf: -------------------------------------------------------------------------------- 1 | # Configure you module here -------------------------------------------------------------------------------- /examples/terraform-module/.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Fixes # -------------------------------------------------------------------------------- /examples/hybrid-module/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "overrides": [] 3 | } 4 | -------------------------------------------------------------------------------- /examples/terraform-module/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "overrides": [] 3 | } 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | -------------------------------------------------------------------------------- /examples/hybrid-module/.prettierignore: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | -------------------------------------------------------------------------------- /examples/terraform-module/.prettierignore: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | -------------------------------------------------------------------------------- /examples/terraform-module/src/terraformModules.ts: -------------------------------------------------------------------------------- 1 | export * from "./.gen/modules/eks"; 2 | export * from "./.gen/modules/eks-managed-nodegroup"; 3 | -------------------------------------------------------------------------------- /examples/terraform-module/src/index.ts: -------------------------------------------------------------------------------- 1 | // Re-Export module bindings 2 | export * from "./terraformModules"; 3 | 4 | // Add your custom constructs here -------------------------------------------------------------------------------- /examples/terraform-module/src/.gen/constraints.json: -------------------------------------------------------------------------------- 1 | { 2 | "cdktf": "0.21.0", 3 | "providers": { 4 | "eks": "~> 18.0", 5 | "eks-managed-nodegroup": "~> 18.0" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/hybrid-module/src/__tests__/__snapshots__/index-test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`MyConstruct should synthesize 1`] = ` 4 | "{ 5 | }" 6 | `; 7 | -------------------------------------------------------------------------------- /examples/hybrid-module/construct-examples/cdktf.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "typescript", 3 | "app": "npx ts-node --project ../tsconfig.dev.json index.ts", 4 | "projectId": "my-project-id" 5 | } -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) HashiCorp, Inc. 3 | * SPDX-License-Identifier: MPL-2.0 4 | */ 5 | 6 | export * from "./hybrid-module"; 7 | export * from "./terraform-module"; 8 | export * from "./publishing"; 9 | -------------------------------------------------------------------------------- /examples/hybrid-module/src/my-awesome-module.md: -------------------------------------------------------------------------------- 1 | # My Awesome Module 2 | 3 | ## Usage 4 | 5 | ```hcl 6 | module "eks_managed_node_group" { 7 | source = "DanielMSchmidt/my-module//modules/my-awesome-module" 8 | 9 | } 10 | ``` -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # These owners will be the default owners for everything in 2 | # the repo. Unless a later match takes precedence, 3 | # they will be requested for review when someone opens a 4 | # pull request. 5 | * @cdktf/tf-cdk-team 6 | -------------------------------------------------------------------------------- /examples/hybrid-module/terraform/README.md: -------------------------------------------------------------------------------- 1 | # Please add here some pure HCL tests for your modules in order to test HCL Interoperability 2 | 3 | Examples: 4 | 5 | module "my_awesome_test" { 6 | source = "../modules/my-awesome-modules" 7 | ...variables... 8 | } -------------------------------------------------------------------------------- /examples/terraform-module/src/__tests__/index-test.ts: -------------------------------------------------------------------------------- 1 | // import { Testing } from "cdktf"; 2 | // import "cdktf/lib/testing/adapters/jest"; 3 | 4 | // To learn more about testing see cdk.tf/testing 5 | describe("MyModule", () => { 6 | it.todo("should be tested") 7 | }); -------------------------------------------------------------------------------- /examples/hybrid-module/construct-examples/README.md: -------------------------------------------------------------------------------- 1 | # Construct Examples 2 | 3 | Example use-cases for the Construct library. 4 | 5 | - [Basic Usage](./basic.ts) 6 | 7 | To ensure all examples are working, please make sure the [index.ts](./index.ts) file is importing all of them. -------------------------------------------------------------------------------- /examples/hybrid-module/scripts/tf-module-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This script is created by projen, do not edit it directly. 3 | set -e 4 | 5 | terraform -chdir=terraform init --upgrade 6 | terraform -chdir=terraform fmt 7 | terraform -chdir=terraform validate 8 | 9 | -------------------------------------------------------------------------------- /examples/hybrid-module/src/cdktf.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "typescript", 3 | "app": "npx ts-node tfModules.ts", 4 | "terraformProviders": [ 5 | "hashicorp/null@3.1.1" 6 | ], 7 | "terraformModules": [], 8 | "output": "modules", 9 | "projectId": "my-project-id" 10 | } -------------------------------------------------------------------------------- /examples/hybrid-module/modules/my-awesome-module/cdk.tf.json: -------------------------------------------------------------------------------- 1 | { 2 | "//": { 3 | "metadata": { 4 | "backend": "local", 5 | "stackName": "my-awesome-module", 6 | "version": "0.21.0" 7 | }, 8 | "outputs": { 9 | } 10 | }, 11 | "terraform": { 12 | } 13 | } -------------------------------------------------------------------------------- /examples/hybrid-module/src/index.ts: -------------------------------------------------------------------------------- 1 | import { Construct } from "constructs"; 2 | 3 | export interface MyConstructOptions { 4 | readonly propertyA: string; 5 | } 6 | 7 | export class MyConstruct extends Construct { 8 | constructor(scope: Construct, id: string, public config: MyConstructOptions) { 9 | super(scope, id); 10 | } 11 | } -------------------------------------------------------------------------------- /examples/hybrid-module/construct-examples/index.ts: -------------------------------------------------------------------------------- 1 | // This file will be synthesized to check if all examples are working 2 | 3 | import { App } from "cdktf"; 4 | // All examples need to be imported here 5 | import { BasicExample } from "./basic"; 6 | 7 | const app = new App(); 8 | 9 | // All examples need to be initialized here 10 | new BasicExample(app, "basic-example"); 11 | app.synth(); -------------------------------------------------------------------------------- /examples/hybrid-module/construct-examples/basic.ts: -------------------------------------------------------------------------------- 1 | import { TerraformStack } from "cdktf"; 2 | import { Construct } from "constructs"; 3 | 4 | import { MyConstruct } from "../src/"; 5 | 6 | export class BasicExample extends TerraformStack { 7 | constructor(scope: Construct, name: string) { 8 | super(scope, name); 9 | 10 | new MyConstruct(this, "my-construct", { 11 | propertyA: "valueA", 12 | }); 13 | } 14 | } -------------------------------------------------------------------------------- /examples/hybrid-module/terraform/provider.tf: -------------------------------------------------------------------------------- 1 | # This file is managed by projen. Do not edit, change the .projenrc file instead. 2 | 3 | terraform { 4 | # Limit provider version (some modules are not compatible with aws 4.x) 5 | required_providers { 6 | aws = { 7 | source = "hashicorp/aws" 8 | version = "~> 3.74" 9 | } 10 | } 11 | # Terraform binary version constraint 12 | required_version = ">= 1.2.0" 13 | } 14 | 15 | 16 | provider "aws" { 17 | region = "eu-central-1" 18 | } 19 | -------------------------------------------------------------------------------- /examples/hybrid-module/src/__tests__/index-test.ts: -------------------------------------------------------------------------------- 1 | import { Testing } from "cdktf"; 2 | import "cdktf/lib/testing/adapters/jest"; 3 | import { MyConstruct } from "../"; 4 | 5 | // To learn more about testing see cdk.tf/testing 6 | describe("MyConstruct", () => { 7 | it("should synthesize", () => { 8 | expect( 9 | Testing.synthScope((scope) => { 10 | new MyConstruct(scope, "my-construct", { 11 | propertyA: "valueA", 12 | }); 13 | }) 14 | ).toMatchSnapshot(); 15 | }); 16 | }); -------------------------------------------------------------------------------- /src/defaults.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) HashiCorp, Inc. 3 | * SPDX-License-Identifier: MPL-2.0 4 | */ 5 | 6 | import { ConstructLibraryOptions } from "projen/lib/cdk"; 7 | 8 | const defaultOptions = { 9 | prettier: true, 10 | projenrcTs: true, 11 | defaultReleaseBranch: "main", 12 | sampleCode: false, 13 | mergify: false, 14 | jsiiVersion: ">=5.1.0", 15 | }; 16 | // Ensure they match the parent option 17 | export const defaults: Pick< 18 | ConstructLibraryOptions, 19 | keyof typeof defaultOptions 20 | > = defaultOptions; 21 | -------------------------------------------------------------------------------- /examples/hybrid-module/modules/my-awesome-module/README.md: -------------------------------------------------------------------------------- 1 | # My Awesome Module 2 | 3 | ## Usage 4 | 5 | ```hcl 6 | module "eks_managed_node_group" { 7 | source = "DanielMSchmidt/my-module//modules/my-awesome-module" 8 | 9 | } 10 | ``` 11 | 12 | ## Requirements 13 | 14 | No requirements. 15 | 16 | ## Providers 17 | 18 | No providers. 19 | 20 | ## Modules 21 | 22 | No modules. 23 | 24 | ## Resources 25 | 26 | No resources. 27 | 28 | ## Inputs 29 | 30 | No inputs. 31 | 32 | ## Outputs 33 | 34 | No outputs. 35 | -------------------------------------------------------------------------------- /examples/hybrid-module/.npmignore: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | /.projen/ 3 | /test-reports/ 4 | junit.xml 5 | /coverage/ 6 | permissions-backup.acl 7 | /dist/changelog.md 8 | /dist/version.txt 9 | /.prettierignore 10 | /.prettierrc.json 11 | /test/ 12 | /tsconfig.dev.json 13 | /src/ 14 | !/lib/ 15 | !/lib/**/*.js 16 | !/lib/**/*.d.ts 17 | dist 18 | /tsconfig.json 19 | /.github/ 20 | /.vscode/ 21 | /.idea/ 22 | /.projenrc.js 23 | tsconfig.tsbuildinfo 24 | /.eslintrc.json 25 | !.jsii 26 | /.gitattributes 27 | /.projenrc.ts 28 | /projenrc 29 | -------------------------------------------------------------------------------- /examples/terraform-module/.npmignore: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | /.projen/ 3 | /test-reports/ 4 | junit.xml 5 | /coverage/ 6 | permissions-backup.acl 7 | /dist/changelog.md 8 | /dist/version.txt 9 | /.prettierignore 10 | /.prettierrc.json 11 | /test/ 12 | /tsconfig.dev.json 13 | /src/ 14 | !/lib/ 15 | !/lib/**/*.js 16 | !/lib/**/*.d.ts 17 | dist 18 | /tsconfig.json 19 | /.github/ 20 | /.vscode/ 21 | /.idea/ 22 | /.projenrc.js 23 | tsconfig.tsbuildinfo 24 | /.eslintrc.json 25 | !.jsii 26 | /.gitattributes 27 | /.projenrc.ts 28 | /projenrc 29 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | /.projen/ 3 | /test-reports/ 4 | junit.xml 5 | /coverage/ 6 | permissions-backup.acl 7 | /dist/changelog.md 8 | /dist/version.txt 9 | /.prettierignore 10 | /.prettierrc.json 11 | /test/ 12 | /tsconfig.dev.json 13 | /src/ 14 | !/lib/ 15 | !/lib/**/*.js 16 | !/lib/**/*.d.ts 17 | dist 18 | /tsconfig.json 19 | /.github/ 20 | /.vscode/ 21 | /.idea/ 22 | /.projenrc.js 23 | tsconfig.tsbuildinfo 24 | /.eslintrc.json 25 | !.jsii 26 | scripts 27 | examples 28 | projenrc 29 | .copywrite.hcl 30 | /.gitattributes 31 | /.projenrc.ts 32 | /projenrc 33 | -------------------------------------------------------------------------------- /examples/terraform-module/src/cdktf.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "typescript", 3 | "app": "npx ts-node index.ts", 4 | "terraformProviders": [], 5 | "terraformModules": [ 6 | { 7 | "name": "eks", 8 | "source": "terraform-aws-modules/eks/aws", 9 | "version": "~> 18.0" 10 | }, 11 | { 12 | "name": "eks-managed-nodegroup", 13 | "source": "terraform-aws-modules/eks/aws//modules/eks-managed-node-group", 14 | "version": "~> 18.0" 15 | } 16 | ], 17 | "projectId": "my-project-id", 18 | "//": "~~ Generated by projen. To modify, edit .projenrc.ts and run \"npx projen\"." 19 | } 20 | -------------------------------------------------------------------------------- /projenrc/customized-license.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) HashiCorp, Inc. 3 | * SPDX-License-Identifier: MPL-2.0 4 | */ 5 | 6 | import { IResolver, License } from "projen"; 7 | import { TypeScriptProject } from "projen/lib/typescript"; 8 | 9 | const SPDX = "MPL-2.0"; 10 | 11 | export class CustomizedLicense extends License { 12 | constructor(project: TypeScriptProject) { 13 | super(project, { spdx: SPDX }); 14 | 15 | project.addFields({ license: SPDX }); 16 | } 17 | 18 | synthesizeContent(resolver: IResolver) { 19 | return ( 20 | "Copyright (c) 2022 HashiCorp, Inc.\n\n" + 21 | super.synthesizeContent(resolver) 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /scripts/update-projen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) HashiCorp, Inc. 3 | # SPDX-License-Identifier: MPL-2.0 4 | 5 | set -ex 6 | 7 | PROJECT_ROOT=$(cd "$(dirname "${BASH_SOURCE:-$0}")/.." && pwd) 8 | NEW_VERSION=$1 9 | 10 | if [ -z "$NEW_VERSION" ]; then 11 | echo "Usage: $0 " 12 | exit 1 13 | fi 14 | 15 | echo "Updating Projen version to $NEW_VERSION" 16 | yarn 17 | sed -i "s/projenVersion = \".*\";/projenVersion = \"$NEW_VERSION\";/" "$PROJECT_ROOT/.projenrc.ts" 18 | CI=0 npx projen 19 | CI=0 npx projen build 20 | 21 | echo "Updating README" 22 | sed -i 's/`projen` >= .*/`projen` >= '"$CDKTF_VERSION"'/' "$PROJECT_ROOT/README.md" 23 | 24 | echo "Done" 25 | -------------------------------------------------------------------------------- /.copywrite.hcl: -------------------------------------------------------------------------------- 1 | schema_version = 1 2 | 3 | project { 4 | license = "MPL-2.0" 5 | copyright_year = 2022 6 | 7 | # (OPTIONAL) A list of globs that should not have copyright/license headers. 8 | # Supports doublestar glob patterns for more flexibility in defining which 9 | # files or folders should be ignored 10 | header_ignore = [ 11 | # "vendors/**", 12 | # "**autogen**", 13 | "**/node_modules/**", 14 | "**/.mergify.yml", 15 | "**/.github/workflows/*.yml", 16 | "**/coverage/**", 17 | "**/test-reports/**", 18 | "**/dist/**", 19 | "examples/**", # this probably should have the headers, but I'm tired of fighting Projen 20 | "test/helper.ts", 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /examples/terraform-module/.projen/files.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | ".eslintrc.json", 4 | ".gitattributes", 5 | ".github/pull_request_template.md", 6 | ".github/workflows/build.yml", 7 | ".github/workflows/pull-request-lint.yml", 8 | ".github/workflows/release.yml", 9 | ".github/workflows/upgrade-main.yml", 10 | ".gitignore", 11 | ".prettierignore", 12 | ".prettierrc.json", 13 | ".projen/deps.json", 14 | ".projen/files.json", 15 | ".projen/tasks.json", 16 | "LICENSE", 17 | "src/cdktf.json", 18 | "src/terraformModules.ts", 19 | "tsconfig.dev.json" 20 | ], 21 | "//": "~~ Generated by projen. To modify, edit .projenrc.ts and run \"npx projen\"." 22 | } 23 | -------------------------------------------------------------------------------- /examples/hybrid-module/.projen/files.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | ".eslintrc.json", 4 | ".gitattributes", 5 | ".github/pull_request_template.md", 6 | ".github/workflows/build.yml", 7 | ".github/workflows/pull-request-lint.yml", 8 | ".github/workflows/release.yml", 9 | ".github/workflows/upgrade-main.yml", 10 | ".gitignore", 11 | ".prettierignore", 12 | ".prettierrc.json", 13 | ".projen/deps.json", 14 | ".projen/files.json", 15 | ".projen/tasks.json", 16 | "LICENSE", 17 | "scripts/copy-modules.sh", 18 | "scripts/tf-module-test.sh", 19 | "tsconfig.dev.json" 20 | ], 21 | "//": "~~ Generated by projen. To modify, edit .projenrc.ts and run \"npx projen\"." 22 | } 23 | -------------------------------------------------------------------------------- /examples/hybrid-module/src/tfModules.ts: -------------------------------------------------------------------------------- 1 | import { App } from "cdktf"; 2 | import { Construct } from "constructs"; 3 | import { TFModuleStack } from "@cdktf/tf-module-stack"; 4 | import { MyConstruct } from "./index"; 5 | 6 | class MyAwesomeModule extends TFModuleStack { 7 | constructor(scope: Construct, id: string) { 8 | super(scope, id); 9 | 10 | new MyConstruct(this, "my-construct", { 11 | propertyA: "valueA", 12 | }); 13 | } 14 | } 15 | 16 | const app = new App(); 17 | // This is the name the module can be found under. 18 | // We expect a "my-awesome-module.md" file in this directory. 19 | // The README.md file will be generated from this file. 20 | new MyAwesomeModule(app, "my-awesome-module"); 21 | app.synth(); -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # NOTE: This file is not managed by Projen because if you enable Dependabot through Projen, 2 | # it will delete the upgrade-main job and expect you to only use Dependabot for updates. 3 | # That is not what we want either; we just want to use Dependabot for security updates. 4 | 5 | version: 2 6 | updates: 7 | - package-ecosystem: npm 8 | versioning-strategy: lockfile-only 9 | directory: / 10 | schedule: 11 | interval: daily 12 | ignore: 13 | - dependency-name: projen 14 | labels: 15 | - auto-approve 16 | - automerge 17 | - dependencies 18 | - security 19 | # Disable version updates for npm dependencies, only use Dependabot for security updates 20 | open-pull-requests-limit: 0 21 | -------------------------------------------------------------------------------- /examples/hybrid-module/.github/workflows/pull-request-lint.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | name: pull-request-lint 4 | on: 5 | pull_request_target: 6 | types: 7 | - labeled 8 | - opened 9 | - synchronize 10 | - reopened 11 | - ready_for_review 12 | - edited 13 | jobs: 14 | validate: 15 | name: Validate PR title 16 | runs-on: ubuntu-latest 17 | permissions: 18 | pull-requests: write 19 | steps: 20 | - uses: amannn/action-semantic-pull-request@v5.4.0 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | with: 24 | types: |- 25 | feat 26 | fix 27 | chore 28 | requireScope: false 29 | -------------------------------------------------------------------------------- /examples/terraform-module/.github/workflows/pull-request-lint.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | name: pull-request-lint 4 | on: 5 | pull_request_target: 6 | types: 7 | - labeled 8 | - opened 9 | - synchronize 10 | - reopened 11 | - ready_for_review 12 | - edited 13 | jobs: 14 | validate: 15 | name: Validate PR title 16 | runs-on: ubuntu-latest 17 | permissions: 18 | pull-requests: write 19 | steps: 20 | - uses: amannn/action-semantic-pull-request@v5.4.0 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | with: 24 | types: |- 25 | feat 26 | fix 27 | chore 28 | requireScope: false 29 | -------------------------------------------------------------------------------- /.github/workflows/pull-request-lint.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | name: pull-request-lint 4 | on: 5 | pull_request_target: 6 | types: 7 | - labeled 8 | - opened 9 | - synchronize 10 | - reopened 11 | - ready_for_review 12 | - edited 13 | jobs: 14 | validate: 15 | name: Validate PR title 16 | runs-on: ubuntu-latest 17 | permissions: 18 | pull-requests: write 19 | steps: 20 | - uses: amannn/action-semantic-pull-request@0723387faaf9b38adef4775cd42cfd5155ed6017 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | with: 24 | types: |- 25 | feat 26 | fix 27 | chore 28 | requireScope: false 29 | -------------------------------------------------------------------------------- /.projen/files.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | ".eslintrc.json", 4 | ".gitattributes", 5 | ".github/workflows/auto-approve.yml", 6 | ".github/workflows/automerge.yml", 7 | ".github/workflows/build.yml", 8 | ".github/workflows/pull-request-lint.yml", 9 | ".github/workflows/release.yml", 10 | ".github/workflows/upgrade-cdktf.yml", 11 | ".github/workflows/upgrade-jsii-typescript.yml", 12 | ".github/workflows/upgrade-main.yml", 13 | ".github/workflows/upgrade-projen.yml", 14 | ".gitignore", 15 | ".prettierignore", 16 | ".prettierrc.json", 17 | ".projen/deps.json", 18 | ".projen/files.json", 19 | ".projen/tasks.json", 20 | "LICENSE", 21 | "tsconfig.dev.json" 22 | ], 23 | "//": "~~ Generated by projen. To modify, edit .projenrc.ts and run \"npx projen\"." 24 | } 25 | -------------------------------------------------------------------------------- /examples/terraform-module/.projenrc.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) HashiCorp, Inc. 3 | * SPDX-License-Identifier: MPL-2.0 4 | */ 5 | 6 | import { TerraformModule } from "../../lib"; 7 | 8 | const project = new TerraformModule({ 9 | prettier: true, 10 | projenrcTs: true, 11 | defaultReleaseBranch: "main", 12 | name: "my-module", 13 | author: "Daniel Schmidt", 14 | authorAddress: "danielmschmidt92@gmail.com", 15 | repositoryUrl: "github.com/DanielMSchmidt/my-module", 16 | cdktfVersion: "0.21.0", 17 | 18 | terraformModules: [ 19 | { 20 | name: "eks", 21 | source: "terraform-aws-modules/eks/aws", 22 | version: "~> 18.0", 23 | }, 24 | { 25 | name: "eks-managed-nodegroup", 26 | source: "terraform-aws-modules/eks/aws//modules/eks-managed-node-group", 27 | version: "~> 18.0", 28 | }, 29 | ], 30 | 31 | projectId: "my-project-id", 32 | }); 33 | project.gitignore.addPatterns("lib/"); 34 | project.synth(); 35 | -------------------------------------------------------------------------------- /scripts/update-jsii-typescript.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) HashiCorp, Inc. 3 | # SPDX-License-Identifier: MPL-2.0 4 | 5 | set -ex 6 | 7 | PROJECT_ROOT=$(cd "$(dirname "${BASH_SOURCE:-$0}")/.." && pwd) 8 | NEW_VERSION=$1 9 | 10 | if [ -z "$NEW_VERSION" ]; then 11 | echo "Usage: $0 " 12 | exit 1 13 | fi 14 | 15 | echo "Updating JSII & TypeScript version to $NEW_VERSION" 16 | yarn 17 | sed -i "s/typescriptVersion = \".*\";/typescriptVersion = \"~$NEW_VERSION\";/" "$PROJECT_ROOT/.projenrc.ts" 18 | CI=0 npx projen 19 | sed -i "s/typescriptVersion: \".*\",/typescriptVersion: \"~$NEW_VERSION\",/" "$PROJECT_ROOT/src/hybrid-module.ts" 20 | sed -i "s/jsiiVersion: \".*\",/jsiiVersion: \"~$NEW_VERSION\",/" "$PROJECT_ROOT/src/hybrid-module.ts" 21 | sed -i "s/typescriptVersion: \".*\",/typescriptVersion: \"~$NEW_VERSION\",/" "$PROJECT_ROOT/src/terraform-module.ts" 22 | sed -i "s/jsiiVersion: \".*\",/jsiiVersion: \"~$NEW_VERSION\",/" "$PROJECT_ROOT/src/terraform-module.ts" 23 | CI=0 npx projen build 24 | 25 | echo "Done" 26 | -------------------------------------------------------------------------------- /tsconfig.dev.json: -------------------------------------------------------------------------------- 1 | // ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | { 3 | "compilerOptions": { 4 | "alwaysStrict": true, 5 | "declaration": true, 6 | "esModuleInterop": true, 7 | "experimentalDecorators": true, 8 | "inlineSourceMap": true, 9 | "inlineSources": true, 10 | "lib": [ 11 | "es2019" 12 | ], 13 | "module": "CommonJS", 14 | "noEmitOnError": false, 15 | "noFallthroughCasesInSwitch": true, 16 | "noImplicitAny": true, 17 | "noImplicitReturns": true, 18 | "noImplicitThis": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "resolveJsonModule": true, 22 | "strict": true, 23 | "strictNullChecks": true, 24 | "strictPropertyInitialization": true, 25 | "stripInternal": true, 26 | "target": "ES2019" 27 | }, 28 | "include": [ 29 | "src/**/*.ts", 30 | "test/**/*.ts", 31 | ".projenrc.ts", 32 | "projenrc/**/*.ts" 33 | ], 34 | "exclude": [ 35 | "node_modules" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /examples/hybrid-module/tsconfig.dev.json: -------------------------------------------------------------------------------- 1 | // ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | { 3 | "compilerOptions": { 4 | "alwaysStrict": true, 5 | "declaration": true, 6 | "esModuleInterop": true, 7 | "experimentalDecorators": true, 8 | "inlineSourceMap": true, 9 | "inlineSources": true, 10 | "lib": [ 11 | "es2019" 12 | ], 13 | "module": "CommonJS", 14 | "noEmitOnError": false, 15 | "noFallthroughCasesInSwitch": true, 16 | "noImplicitAny": true, 17 | "noImplicitReturns": true, 18 | "noImplicitThis": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "resolveJsonModule": true, 22 | "strict": true, 23 | "strictNullChecks": true, 24 | "strictPropertyInitialization": true, 25 | "stripInternal": true, 26 | "target": "ES2019" 27 | }, 28 | "include": [ 29 | "src/**/*.ts", 30 | "test/**/*.ts", 31 | ".projenrc.ts", 32 | "projenrc/**/*.ts" 33 | ], 34 | "exclude": [ 35 | "node_modules" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /examples/terraform-module/tsconfig.dev.json: -------------------------------------------------------------------------------- 1 | // ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | { 3 | "compilerOptions": { 4 | "alwaysStrict": true, 5 | "declaration": true, 6 | "esModuleInterop": true, 7 | "experimentalDecorators": true, 8 | "inlineSourceMap": true, 9 | "inlineSources": true, 10 | "lib": [ 11 | "es2019" 12 | ], 13 | "module": "CommonJS", 14 | "noEmitOnError": false, 15 | "noFallthroughCasesInSwitch": true, 16 | "noImplicitAny": true, 17 | "noImplicitReturns": true, 18 | "noImplicitThis": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "resolveJsonModule": true, 22 | "strict": true, 23 | "strictNullChecks": true, 24 | "strictPropertyInitialization": true, 25 | "stripInternal": true, 26 | "target": "ES2019" 27 | }, 28 | "include": [ 29 | "src/**/*.ts", 30 | "test/**/*.ts", 31 | ".projenrc.ts", 32 | "projenrc/**/*.ts" 33 | ], 34 | "exclude": [ 35 | "node_modules" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /examples/terraform-module/.gitattributes: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | * text=auto eol=lf 4 | *.snap linguist-generated 5 | /.eslintrc.json linguist-generated 6 | /.gitattributes linguist-generated 7 | /.github/pull_request_template.md linguist-generated 8 | /.github/workflows/build.yml linguist-generated 9 | /.github/workflows/pull-request-lint.yml linguist-generated 10 | /.github/workflows/release.yml linguist-generated 11 | /.github/workflows/upgrade-main.yml linguist-generated 12 | /.gitignore linguist-generated 13 | /.npmignore linguist-generated 14 | /.prettierignore linguist-generated 15 | /.prettierrc.json linguist-generated 16 | /.projen/** linguist-generated 17 | /.projen/deps.json linguist-generated 18 | /.projen/files.json linguist-generated 19 | /.projen/tasks.json linguist-generated 20 | /API.md linguist-generated 21 | /LICENSE linguist-generated 22 | /package.json linguist-generated 23 | /src/cdktf.json linguist-generated 24 | /src/terraformModules.ts linguist-generated 25 | /tsconfig.dev.json linguist-generated 26 | /yarn.lock linguist-generated -------------------------------------------------------------------------------- /.github/workflows/automerge.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | name: automerge 4 | on: 5 | pull_request_target: 6 | types: 7 | - opened 8 | - labeled 9 | - ready_for_review 10 | - reopened 11 | - synchronize 12 | concurrency: 13 | group: ${{ github.workflow }}-${{ github.ref }} 14 | jobs: 15 | automerge: 16 | runs-on: ubuntu-latest 17 | permissions: 18 | contents: read 19 | if: contains(github.event.pull_request.labels.*.name, 'automerge') && github.event.pull_request.draft == false 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 23 | - name: Turn on automerge for this PR by a trusted user or bot 24 | if: github.event.pull_request.user.login == 'team-tf-cdk' || contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.pull_request.author_association) || github.actor == 'dependabot[bot]' 25 | env: 26 | GH_TOKEN: ${{ secrets.PROJEN_GITHUB_TOKEN }} 27 | run: gh pr merge --auto --squash ${{ github.event.pull_request.number }} 28 | -------------------------------------------------------------------------------- /examples/hybrid-module/.gitattributes: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | * text=auto eol=lf 4 | *.snap linguist-generated 5 | /./scripts/tf-module-test.sh linguist-generated 6 | /.eslintrc.json linguist-generated 7 | /.gitattributes linguist-generated 8 | /.github/pull_request_template.md linguist-generated 9 | /.github/workflows/build.yml linguist-generated 10 | /.github/workflows/pull-request-lint.yml linguist-generated 11 | /.github/workflows/release.yml linguist-generated 12 | /.github/workflows/upgrade-main.yml linguist-generated 13 | /.gitignore linguist-generated 14 | /.npmignore linguist-generated 15 | /.prettierignore linguist-generated 16 | /.prettierrc.json linguist-generated 17 | /.projen/** linguist-generated 18 | /.projen/deps.json linguist-generated 19 | /.projen/files.json linguist-generated 20 | /.projen/tasks.json linguist-generated 21 | /API.md linguist-generated 22 | /LICENSE linguist-generated 23 | /package.json linguist-generated 24 | /scripts/copy-modules.sh linguist-generated 25 | /terraform/provider.tf linguist-generated 26 | /tsconfig.dev.json linguist-generated 27 | /yarn.lock linguist-generated -------------------------------------------------------------------------------- /examples/terraform-module/.gitignore: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | !/.gitattributes 3 | !/.projen/tasks.json 4 | !/.projen/deps.json 5 | !/.projen/files.json 6 | !/.github/workflows/pull-request-lint.yml 7 | !/package.json 8 | !/LICENSE 9 | !/.npmignore 10 | logs 11 | *.log 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | lerna-debug.log* 16 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 17 | pids 18 | *.pid 19 | *.seed 20 | *.pid.lock 21 | lib-cov 22 | coverage 23 | *.lcov 24 | .nyc_output 25 | build/Release 26 | node_modules/ 27 | jspm_packages/ 28 | *.tsbuildinfo 29 | .eslintcache 30 | *.tgz 31 | .yarn-integrity 32 | .cache 33 | /test-reports/ 34 | junit.xml 35 | /coverage/ 36 | !/.github/workflows/build.yml 37 | /dist/changelog.md 38 | /dist/version.txt 39 | !/.github/workflows/release.yml 40 | !/.github/workflows/upgrade-main.yml 41 | !/.github/pull_request_template.md 42 | !/.prettierignore 43 | !/.prettierrc.json 44 | !/test/ 45 | !/tsconfig.dev.json 46 | !/src/ 47 | /lib 48 | /dist/ 49 | !/.eslintrc.json 50 | .jsii 51 | tsconfig.json 52 | !/API.md 53 | !/src/terraformModules.ts 54 | !/src/cdktf.json 55 | lib/ 56 | !/.projenrc.ts 57 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | * text=auto eol=lf 4 | *.snap linguist-generated 5 | /.eslintrc.json linguist-generated 6 | /.gitattributes linguist-generated 7 | /.github/workflows/auto-approve.yml linguist-generated 8 | /.github/workflows/automerge.yml linguist-generated 9 | /.github/workflows/build.yml linguist-generated 10 | /.github/workflows/pull-request-lint.yml linguist-generated 11 | /.github/workflows/release.yml linguist-generated 12 | /.github/workflows/upgrade-cdktf.yml linguist-generated 13 | /.github/workflows/upgrade-jsii-typescript.yml linguist-generated 14 | /.github/workflows/upgrade-main.yml linguist-generated 15 | /.github/workflows/upgrade-projen.yml linguist-generated 16 | /.gitignore linguist-generated 17 | /.npmignore linguist-generated 18 | /.prettierignore linguist-generated 19 | /.prettierrc.json linguist-generated 20 | /.projen/** linguist-generated 21 | /.projen/deps.json linguist-generated 22 | /.projen/files.json linguist-generated 23 | /.projen/tasks.json linguist-generated 24 | /API.md linguist-generated 25 | /LICENSE linguist-generated 26 | /package.json linguist-generated 27 | /tsconfig.dev.json linguist-generated 28 | /yarn.lock linguist-generated -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | !/.gitattributes 3 | !/.projen/tasks.json 4 | !/.projen/deps.json 5 | !/.projen/files.json 6 | !/.github/workflows/pull-request-lint.yml 7 | !/package.json 8 | !/.npmignore 9 | logs 10 | *.log 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | lerna-debug.log* 15 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 16 | pids 17 | *.pid 18 | *.seed 19 | *.pid.lock 20 | lib-cov 21 | coverage 22 | *.lcov 23 | .nyc_output 24 | build/Release 25 | node_modules/ 26 | jspm_packages/ 27 | *.tsbuildinfo 28 | .eslintcache 29 | *.tgz 30 | .yarn-integrity 31 | .cache 32 | .idea/ 33 | /test-reports/ 34 | junit.xml 35 | /coverage/ 36 | !/.github/workflows/build.yml 37 | /dist/changelog.md 38 | /dist/version.txt 39 | !/.github/workflows/release.yml 40 | !/.github/workflows/upgrade-main.yml 41 | !/.prettierignore 42 | !/.prettierrc.json 43 | !/test/ 44 | !/tsconfig.dev.json 45 | !/src/ 46 | /lib 47 | /dist/ 48 | !/.eslintrc.json 49 | .jsii 50 | tsconfig.json 51 | !/API.md 52 | !/LICENSE 53 | !/.github/workflows/auto-approve.yml 54 | !/.github/workflows/automerge.yml 55 | !/.github/workflows/upgrade-cdktf.yml 56 | !/.github/workflows/upgrade-jsii-typescript.yml 57 | !/.github/workflows/upgrade-projen.yml 58 | !/.projenrc.ts 59 | -------------------------------------------------------------------------------- /examples/hybrid-module/.projenrc.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) HashiCorp, Inc. 3 | * SPDX-License-Identifier: MPL-2.0 4 | */ 5 | 6 | import { HybridModule } from "../../lib"; 7 | 8 | const project = new HybridModule({ 9 | prettier: true, 10 | projenrcTs: true, 11 | defaultReleaseBranch: "main", 12 | name: "my-module", 13 | author: "Daniel Schmidt", 14 | authorAddress: "danielmschmidt92@gmail.com", 15 | repositoryUrl: "github.com/DanielMSchmidt/my-module", 16 | cdktfVersion: "0.21.0", 17 | outdir: ".", 18 | terraformExamples: { 19 | enabled: true, 20 | folder: "terraform", 21 | providerConfig: ` 22 | terraform { 23 | # Limit provider version (some modules are not compatible with aws 4.x) 24 | required_providers { 25 | aws = { 26 | source = "hashicorp/aws" 27 | version = "~> 3.74" 28 | } 29 | } 30 | # Terraform binary version constraint 31 | required_version = ">= 1.2.0" 32 | } 33 | 34 | 35 | provider "aws" { 36 | region = "eu-central-1" 37 | } 38 | `, 39 | }, 40 | constructExamples: { 41 | enabled: true, 42 | folder: "construct-examples", 43 | }, 44 | projectId: "my-project-id", 45 | }); 46 | project.gitignore.addPatterns("lib/"); 47 | project.synth(); 48 | -------------------------------------------------------------------------------- /examples/hybrid-module/.gitignore: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | !/.gitattributes 3 | !/.projen/tasks.json 4 | !/.projen/deps.json 5 | !/.projen/files.json 6 | !/.github/workflows/pull-request-lint.yml 7 | !/package.json 8 | !/LICENSE 9 | !/.npmignore 10 | logs 11 | *.log 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | lerna-debug.log* 16 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 17 | pids 18 | *.pid 19 | *.seed 20 | *.pid.lock 21 | lib-cov 22 | coverage 23 | *.lcov 24 | .nyc_output 25 | build/Release 26 | node_modules/ 27 | jspm_packages/ 28 | *.tsbuildinfo 29 | .eslintcache 30 | *.tgz 31 | .yarn-integrity 32 | .cache 33 | /test-reports/ 34 | junit.xml 35 | /coverage/ 36 | !/.github/workflows/build.yml 37 | /dist/changelog.md 38 | /dist/version.txt 39 | !/.github/workflows/release.yml 40 | !/.github/workflows/upgrade-main.yml 41 | !/.github/pull_request_template.md 42 | !/.prettierignore 43 | !/.prettierrc.json 44 | !/test/ 45 | !/tsconfig.dev.json 46 | !/src/ 47 | /lib 48 | /dist/ 49 | !/.eslintrc.json 50 | .jsii 51 | tsconfig.json 52 | !/API.md 53 | !/terraform/provider.tf 54 | terraform/.terraform 55 | terraform/.terraform.lock.hcl 56 | !/scripts/tf-module-test.sh 57 | construct-examples/cdktf.out 58 | src/.gen 59 | src/cdktf.out 60 | src/modules 61 | !/scripts/copy-modules.sh 62 | lib/ 63 | !/.projenrc.ts 64 | -------------------------------------------------------------------------------- /test/hybrid-module.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) HashiCorp, Inc. 3 | * SPDX-License-Identifier: MPL-2.0 4 | */ 5 | 6 | import { expectSnapshot } from "./helper"; 7 | import { defaults } from "../src/defaults"; 8 | import { HybridModule } from "../src/index"; 9 | 10 | describe("HybridModule", () => { 11 | it("snapshot", () => { 12 | const project = new HybridModule({ 13 | ...defaults, 14 | name: "my-module", 15 | author: "Daniel Schmidt", 16 | authorAddress: "danielmschmidt92@gmail.com", 17 | repositoryUrl: "github.com/DanielMSchmidt/my-module", 18 | terraformExamples: { 19 | enabled: true, 20 | folder: "terraform", 21 | providerConfig: ` 22 | terraform { 23 | # Limit provider version (some modules are not compatible with aws 4.x) 24 | required_providers { 25 | aws = { 26 | source = "hashicorp/aws" 27 | version = "~> 3.74" 28 | } 29 | } 30 | # Terraform binary version constraint 31 | required_version = ">= 1.1.0" 32 | } 33 | 34 | 35 | provider "aws" { 36 | region = "eu-central-1" 37 | } 38 | `, 39 | }, 40 | constructExamples: { 41 | enabled: true, 42 | folder: "construct-examples", 43 | }, 44 | projectId: "test-project", 45 | }); 46 | 47 | expectSnapshot(project); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /.github/workflows/auto-approve.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | name: auto-approve 4 | on: 5 | pull_request_target: 6 | types: 7 | - opened 8 | - labeled 9 | - ready_for_review 10 | - reopened 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.ref }} 13 | jobs: 14 | approve: 15 | runs-on: ubuntu-latest 16 | permissions: 17 | contents: read 18 | pull-requests: write 19 | if: contains(github.event.pull_request.labels.*.name, 'auto-approve') && github.event.pull_request.draft == false 20 | steps: 21 | - name: Checkout PR 22 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 23 | with: 24 | ref: ${{ github.event.pull_request.head.ref }} 25 | repository: ${{ github.event.pull_request.head.repo.full_name }} 26 | - name: Auto-approve PRs by other users as team-tf-cdk 27 | if: github.event.pull_request.user.login != 'team-tf-cdk' && (contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.pull_request.author_association) || github.actor == 'dependabot[bot]') 28 | env: 29 | GH_TOKEN: ${{ secrets.PROJEN_GITHUB_TOKEN }} 30 | run: gh pr review ${{ github.event.pull_request.number }} --approve 31 | - name: Auto-approve PRs by team-tf-cdk as github-actions[bot] 32 | if: github.event.pull_request.user.login == 'team-tf-cdk' 33 | env: 34 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | run: gh pr review ${{ github.event.pull_request.number }} --approve 36 | -------------------------------------------------------------------------------- /examples/hybrid-module/scripts/copy-modules.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This script is created by projen, do not edit it directly. 3 | set -e 4 | 5 | SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" 6 | 7 | # check if the module folder has been generated as expected 8 | MODULES_FOLDER=$( cd -- "$SCRIPTPATH/../src/modules/stacks" >/dev/null 2>&1 ; pwd -P ) 9 | SRC_FOLDER=$( cd -- "$SCRIPTPATH/../src/" >/dev/null 2>&1 ; pwd -P ) 10 | TARGET_FOLDER="$SCRIPTPATH/../modules" 11 | 12 | if [ ! -d "$MODULES_FOLDER" ]; then 13 | echo "Expected module folder to exist at $MODULES_FOLDER" 14 | exit 1 15 | fi 16 | 17 | cd "$MODULES_FOLDER" 18 | for d in */ ; do 19 | [ -L "${d%/}" ] && continue 20 | 21 | dirname="${d%/}" 22 | 23 | 24 | # Check if everything is in place 25 | if [ ! -f "$MODULES_FOLDER/$dirname/cdk.tf.json" ]; then 26 | echo "Expected code for $dirname at cdk.tf.json to exist at $MODULES_FOLDER/$dirname" 27 | exit 1 28 | fi 29 | 30 | if [ ! -f "$SRC_FOLDER/$dirname.md" ]; then 31 | echo "Expected Documentation for $dirname at $dirname.md to exist at $SRC_FOLDER/$dirname.md" 32 | exit 1 33 | fi 34 | 35 | # Copy module and readme together 36 | echo "Copying Code and README for module $dirname" 37 | mkdir -p "$TARGET_FOLDER" 38 | mkdir -p "$TARGET_FOLDER/$dirname" 39 | cp "$MODULES_FOLDER/$dirname/cdk.tf.json" "$TARGET_FOLDER/$dirname/cdk.tf.json" 40 | cp "$SRC_FOLDER/$dirname.md" "$TARGET_FOLDER/$dirname/README.md" 41 | 42 | # Add README hcl docs 43 | if which terraform-docs >/dev/null; then 44 | terraform-docs markdown table --output-file "$TARGET_FOLDER/$dirname/README.md" "$TARGET_FOLDER/$dirname" 45 | else 46 | docker run --rm --volume "$SCRIPTPATH/../modules:/terraform-docs" -u $(id -u) quay.io/terraform-docs/terraform-docs:0.16.0 markdown table --output-file "/terraform-docs/$dirname/README.md" /terraform-docs/$dirname 47 | fi 48 | done -------------------------------------------------------------------------------- /projenrc/automerge.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) HashiCorp, Inc. 3 | * SPDX-License-Identifier: MPL-2.0 4 | */ 5 | 6 | import { javascript } from "projen"; 7 | import { JobPermission } from "projen/lib/github/workflows-model"; 8 | 9 | /** 10 | * Enables GitHub's built-in automerge for PRs with the "automerge" label 11 | */ 12 | export class Automerge { 13 | constructor(project: javascript.NodeProject) { 14 | const workflow = project.github?.addWorkflow("automerge"); 15 | 16 | if (!workflow) throw new Error("no workflow defined"); 17 | 18 | workflow.on({ 19 | pullRequestTarget: { 20 | types: [ 21 | "opened", 22 | "labeled", 23 | "ready_for_review", 24 | "reopened", 25 | "synchronize", 26 | ], 27 | }, 28 | }); 29 | 30 | (workflow.concurrency as any) = { 31 | group: "${{ github.workflow }}-${{ github.ref }}", 32 | }; 33 | 34 | const maintainerStatuses = `fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]')`; 35 | workflow.addJobs({ 36 | automerge: { 37 | runsOn: ["ubuntu-latest"], 38 | if: "contains(github.event.pull_request.labels.*.name, 'automerge') && github.event.pull_request.draft == false", 39 | steps: [ 40 | { 41 | name: "Checkout", 42 | uses: "actions/checkout@v3", 43 | }, 44 | { 45 | name: "Turn on automerge for this PR by a trusted user or bot", 46 | if: `github.event.pull_request.user.login == 'team-tf-cdk' || contains(${maintainerStatuses}, github.event.pull_request.author_association) || github.actor == 'dependabot[bot]'`, 47 | run: "gh pr merge --auto --squash ${{ github.event.pull_request.number }}", 48 | env: { 49 | GH_TOKEN: "${{ secrets.PROJEN_GITHUB_TOKEN }}", 50 | }, 51 | }, 52 | ], 53 | permissions: { 54 | contents: JobPermission.READ, 55 | }, 56 | }, 57 | }); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /scripts/check-jsii-versions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) HashiCorp, Inc. 3 | * SPDX-License-Identifier: MPL-2.0 4 | */ 5 | const today = new Date(); 6 | const oneMonthFromToday = new Date(); 7 | oneMonthFromToday.setDate(today.getDate() + 30); 8 | // console.debug("oneMonthFromToday", oneMonthFromToday.toDateString()); 9 | 10 | /** Return the earliest supported version whose EOS date is at least a month away */ 11 | async function getEarliestSupportedVersion() { 12 | // https://github.com/aws/jsii-compiler/blob/main/releases.json 13 | const response = await fetch("https://raw.githubusercontent.com/aws/jsii-compiler/main/releases.json"); 14 | const data = await response.json(); 15 | const activelySupportedVersions = Object.entries(data.maintenance).filter(([version, supportEndDate]) => { 16 | return new Date(supportEndDate) > oneMonthFromToday; 17 | }).sort((a, b) => { 18 | // Very naive sorting function: treat "5.4" like (int) 54, "5.5" like (int) 55, etc. and compare accordingly 19 | return parseInt(a[0].replace(".", ""), 10) > parseInt(b[0].replace(".", ""), 10); 20 | }); 21 | 22 | console.debug("Actively supported versions with an EOS date at least 1 month away") 23 | console.debug(Object.fromEntries(activelySupportedVersions)); 24 | 25 | return activelySupportedVersions[0][0]; 26 | } 27 | 28 | async function getDesiredVersion() { 29 | const earliestSupportedVersion = await getEarliestSupportedVersion(); 30 | console.debug("earliestSupportedVersion", earliestSupportedVersion); 31 | 32 | return earliestSupportedVersion; 33 | } 34 | 35 | module.exports = async ({github, context, core}) => { 36 | const version = await getDesiredVersion(); 37 | 38 | core.exportVariable('NEW_JSII_VERSION', version + ".0"); // e.g. "5.4.0" 39 | core.exportVariable('NEW_JSII_VERSION_SHORT', version); // e.g. "5.4" 40 | core.exportVariable('NEW_JSII_VERSION_MAJOR', version.split(".")[0]); // e.g. "5" 41 | core.exportVariable('NEW_JSII_VERSION_MINOR', version.split(".")[1]); // e.g. "4" 42 | } 43 | -------------------------------------------------------------------------------- /scripts/update-cdktf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) HashiCorp, Inc. 3 | # SPDX-License-Identifier: MPL-2.0 4 | 5 | set -ex 6 | 7 | PROJECT_ROOT=$(cd "$(dirname "${BASH_SOURCE:-$0}")/.." && pwd) 8 | CDKTF_VERSION=$1 9 | CONSTRUCTS_VERSION=$2 10 | 11 | if [ -z "$CDKTF_VERSION" ]; then 12 | echo "Usage: $0 " 13 | exit 1 14 | fi 15 | if [ -z "$CONSTRUCTS_VERSION" ]; then 16 | echo "Usage: $0 " 17 | exit 1 18 | fi 19 | 20 | echo "Updating self to constructs version $CONSTRUCTS_VERSION" 21 | yarn 22 | sed -i "s/constructsVersion = \".*\";/constructsVersion = \"$CONSTRUCTS_VERSION\";/" "$PROJECT_ROOT/.projenrc.ts" 23 | 24 | echo "Updating /src to cdktf version $CDKTF_VERSION" 25 | find ./src -type f -name "*.ts" -print0 | xargs -0 sed -i "s/const cdktfVersion = options.cdktfVersion || \".*\"/const cdktfVersion = options.cdktfVersion || \"$CDKTF_VERSION\"/" 26 | # @TODO This is a hack and will stop working when CDKTF goes 1.0 27 | find ./src -type f -name "*.ts" -print0 | xargs -0 sed -i "s/@default \"0\..*\"/@default \"$CDKTF_VERSION\"/" 28 | 29 | echo "Updating /src to constructs version $CONSTRUCTS_VERSION" 30 | find ./src -type f -name "*.ts" -print0 | xargs -0 sed -i "s/const constructVersion = options.constructVersion || \".*\"/const constructVersion = options.constructVersion || \"$CONSTRUCTS_VERSION\"/" 31 | # @TODO This is also a hack ... but it works 32 | find ./src -type f -name "*.ts" -print0 | xargs -0 sed -i "s/@default \"^.*\"/@default \"^$CONSTRUCTS_VERSION\"/" 33 | 34 | echo "Updating README" 35 | sed -i 's/`cdktf` >= .*/`cdktf` >= '"$CDKTF_VERSION"'/' "$PROJECT_ROOT/README.md" 36 | sed -i 's/`constructs` >= .*/`constructs` >= '"$CONSTRUCTS_VERSION"'/' "$PROJECT_ROOT/README.md" 37 | 38 | CI=0 npx projen 39 | CI=0 npx projen build 40 | 41 | echo "Updating examples" 42 | # Loop through all examples and update the cdktf version 43 | for example in $(find "$PROJECT_ROOT/examples" -mindepth 1 -maxdepth 1 -type d); do 44 | echo "Updating example $example" 45 | cd "$example" 46 | yarn 47 | sed -i "s/cdktfVersion: \".*\",/cdktfVersion: \"$CDKTF_VERSION\",/" ./.projenrc.ts 48 | CI=0 npx projen 49 | done 50 | 51 | echo "Done" 52 | -------------------------------------------------------------------------------- /.github/workflows/upgrade-projen.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | name: upgrade-projen 4 | on: 5 | schedule: 6 | - cron: 13 2 10 */2 * 7 | workflow_dispatch: {} 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.ref }} 10 | jobs: 11 | upgrade: 12 | name: Upgrade Projen 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: read 16 | env: 17 | CI: "true" 18 | CHECKPOINT_DISABLE: "1" 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 22 | - name: Setup Terraform 23 | uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd 24 | with: 25 | terraform_wrapper: false 26 | - name: Setup Node.js 27 | uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e 28 | with: {} 29 | - name: Install 30 | run: yarn install 31 | - name: Get latest Projen version 32 | id: latest_version 33 | run: |- 34 | NEW_VERSION=$(yarn info projen --json | jq -r '.data.version') 35 | NEW_VERSION_SHORT=$(cut -d "." -f 1,2 <<< "$NEW_VERSION") 36 | echo "value=$NEW_VERSION" >> $GITHUB_OUTPUT 37 | echo "short=$NEW_VERSION_SHORT" >> $GITHUB_OUTPUT 38 | - name: Run upgrade script 39 | run: scripts/update-projen.sh ${{ steps.latest_version.outputs.value }} 40 | - name: Create Pull Request 41 | uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e 42 | with: 43 | commit-message: "chore(deps)!: increase minimum required version of Projen to ${{ steps.latest_version.outputs.short }}" 44 | branch: auto/upgrade-projen-${{ steps.latest_version.outputs.short }} 45 | base: main 46 | title: "chore(deps)!: increase minimum required version of Projen to ${{ steps.latest_version.outputs.short }}" 47 | body: This PR increases the version of Projen to `>= ${{ steps.latest_version.outputs.value }}`, which is currently the latest version available. This script runs once per month. 48 | labels: auto-approve,automerge,automated 49 | token: ${{ secrets.PROJEN_GITHUB_TOKEN }} 50 | author: team-tf-cdk 51 | committer: team-tf-cdk 52 | signoff: true 53 | delete-branch: true 54 | -------------------------------------------------------------------------------- /projenrc/auto-approve.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) HashiCorp, Inc. 3 | * SPDX-License-Identifier: MPL-2.0 4 | */ 5 | 6 | import { javascript } from "projen"; 7 | import { JobPermission } from "projen/lib/github/workflows-model"; 8 | 9 | /** 10 | * Approves PRs with the "auto-approve" label 11 | */ 12 | export class AutoApprove { 13 | constructor(project: javascript.NodeProject) { 14 | const workflow = project.github?.addWorkflow("auto-approve"); 15 | 16 | if (!workflow) throw new Error("no workflow defined"); 17 | 18 | workflow.on({ 19 | pullRequestTarget: { 20 | types: ["opened", "labeled", "ready_for_review", "reopened"], 21 | }, 22 | }); 23 | 24 | (workflow.concurrency as any) = { 25 | group: "${{ github.workflow }}-${{ github.ref }}", 26 | }; 27 | 28 | const maintainerStatuses = `fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]')`; 29 | workflow.addJobs({ 30 | approve: { 31 | runsOn: ["ubuntu-latest"], 32 | if: "contains(github.event.pull_request.labels.*.name, 'auto-approve') && github.event.pull_request.draft == false", 33 | steps: [ 34 | { 35 | name: "Checkout PR", 36 | uses: "actions/checkout@v3", 37 | with: { 38 | ref: "${{ github.event.pull_request.head.ref }}", 39 | repository: 40 | "${{ github.event.pull_request.head.repo.full_name }}", 41 | }, 42 | }, 43 | { 44 | name: "Auto-approve PRs by other users as team-tf-cdk", 45 | if: `github.event.pull_request.user.login != 'team-tf-cdk' && (contains(${maintainerStatuses}, github.event.pull_request.author_association) || github.actor == 'dependabot[bot]')`, 46 | run: "gh pr review ${{ github.event.pull_request.number }} --approve", 47 | env: { 48 | GH_TOKEN: "${{ secrets.PROJEN_GITHUB_TOKEN }}", 49 | }, 50 | }, 51 | { 52 | name: "Auto-approve PRs by team-tf-cdk as github-actions[bot]", 53 | if: "github.event.pull_request.user.login == 'team-tf-cdk'", 54 | run: "gh pr review ${{ github.event.pull_request.number }} --approve", 55 | env: { 56 | GH_TOKEN: "${{ secrets.GITHUB_TOKEN }}", 57 | }, 58 | }, 59 | ], 60 | permissions: { 61 | contents: JobPermission.READ, 62 | pullRequests: JobPermission.WRITE, 63 | }, 64 | }, 65 | }); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /examples/terraform-module/.projen/deps.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | { 4 | "name": "@types/jest", 5 | "type": "build" 6 | }, 7 | { 8 | "name": "@types/node", 9 | "version": "ts5.8", 10 | "type": "build" 11 | }, 12 | { 13 | "name": "@typescript-eslint/eslint-plugin", 14 | "version": "^7", 15 | "type": "build" 16 | }, 17 | { 18 | "name": "@typescript-eslint/parser", 19 | "version": "^7", 20 | "type": "build" 21 | }, 22 | { 23 | "name": "cdktf-cli", 24 | "version": "~0.21.0", 25 | "type": "build" 26 | }, 27 | { 28 | "name": "cdktf", 29 | "version": "~0.21.0", 30 | "type": "build" 31 | }, 32 | { 33 | "name": "commit-and-tag-version", 34 | "version": "^12", 35 | "type": "build" 36 | }, 37 | { 38 | "name": "constructs", 39 | "version": "10.4.2", 40 | "type": "build" 41 | }, 42 | { 43 | "name": "eslint-config-prettier", 44 | "type": "build" 45 | }, 46 | { 47 | "name": "eslint-import-resolver-typescript", 48 | "type": "build" 49 | }, 50 | { 51 | "name": "eslint-plugin-import", 52 | "type": "build" 53 | }, 54 | { 55 | "name": "eslint-plugin-prettier", 56 | "type": "build" 57 | }, 58 | { 59 | "name": "eslint", 60 | "version": "^8", 61 | "type": "build" 62 | }, 63 | { 64 | "name": "jest", 65 | "type": "build" 66 | }, 67 | { 68 | "name": "jest-junit", 69 | "version": "^15", 70 | "type": "build" 71 | }, 72 | { 73 | "name": "jsii-diff", 74 | "type": "build" 75 | }, 76 | { 77 | "name": "jsii-docgen", 78 | "version": "^10.0.0", 79 | "type": "build" 80 | }, 81 | { 82 | "name": "jsii-pacmak", 83 | "type": "build" 84 | }, 85 | { 86 | "name": "jsii-rosetta", 87 | "version": "~5.8.0", 88 | "type": "build" 89 | }, 90 | { 91 | "name": "jsii", 92 | "version": "~5.8.0", 93 | "type": "build" 94 | }, 95 | { 96 | "name": "prettier", 97 | "type": "build" 98 | }, 99 | { 100 | "name": "projen", 101 | "type": "build" 102 | }, 103 | { 104 | "name": "ts-jest", 105 | "type": "build" 106 | }, 107 | { 108 | "name": "ts-node", 109 | "version": ">=10.9.1", 110 | "type": "build" 111 | }, 112 | { 113 | "name": "typescript", 114 | "version": "~5.8.0", 115 | "type": "build" 116 | }, 117 | { 118 | "name": "cdktf", 119 | "version": ">=0.21.0", 120 | "type": "peer" 121 | }, 122 | { 123 | "name": "constructs", 124 | "version": ">=10.4.2", 125 | "type": "peer" 126 | } 127 | ], 128 | "//": "~~ Generated by projen. To modify, edit .projenrc.ts and run \"npx projen\"." 129 | } 130 | -------------------------------------------------------------------------------- /examples/hybrid-module/.projen/deps.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | { 4 | "name": "@types/jest", 5 | "type": "build" 6 | }, 7 | { 8 | "name": "@types/node", 9 | "version": "ts5.8", 10 | "type": "build" 11 | }, 12 | { 13 | "name": "@typescript-eslint/eslint-plugin", 14 | "version": "^7", 15 | "type": "build" 16 | }, 17 | { 18 | "name": "@typescript-eslint/parser", 19 | "version": "^7", 20 | "type": "build" 21 | }, 22 | { 23 | "name": "cdktf-cli", 24 | "version": "~0.21.0", 25 | "type": "build" 26 | }, 27 | { 28 | "name": "cdktf", 29 | "version": "~0.21.0", 30 | "type": "build" 31 | }, 32 | { 33 | "name": "commit-and-tag-version", 34 | "version": "^12", 35 | "type": "build" 36 | }, 37 | { 38 | "name": "constructs", 39 | "version": "10.4.2", 40 | "type": "build" 41 | }, 42 | { 43 | "name": "eslint-config-prettier", 44 | "type": "build" 45 | }, 46 | { 47 | "name": "eslint-import-resolver-typescript", 48 | "type": "build" 49 | }, 50 | { 51 | "name": "eslint-plugin-import", 52 | "type": "build" 53 | }, 54 | { 55 | "name": "eslint-plugin-prettier", 56 | "type": "build" 57 | }, 58 | { 59 | "name": "eslint", 60 | "version": "^8", 61 | "type": "build" 62 | }, 63 | { 64 | "name": "jest", 65 | "type": "build" 66 | }, 67 | { 68 | "name": "jest-junit", 69 | "version": "^15", 70 | "type": "build" 71 | }, 72 | { 73 | "name": "jsii-diff", 74 | "type": "build" 75 | }, 76 | { 77 | "name": "jsii-docgen", 78 | "version": "^10.0.0", 79 | "type": "build" 80 | }, 81 | { 82 | "name": "jsii-pacmak", 83 | "type": "build" 84 | }, 85 | { 86 | "name": "jsii-rosetta", 87 | "version": "~5.8.0", 88 | "type": "build" 89 | }, 90 | { 91 | "name": "jsii", 92 | "version": "~5.8.0", 93 | "type": "build" 94 | }, 95 | { 96 | "name": "prettier", 97 | "type": "build" 98 | }, 99 | { 100 | "name": "projen", 101 | "type": "build" 102 | }, 103 | { 104 | "name": "ts-jest", 105 | "type": "build" 106 | }, 107 | { 108 | "name": "ts-node", 109 | "version": ">=10.9.1", 110 | "type": "build" 111 | }, 112 | { 113 | "name": "typescript", 114 | "version": "~5.8.0", 115 | "type": "build" 116 | }, 117 | { 118 | "name": "cdktf", 119 | "version": ">=0.21.0", 120 | "type": "peer" 121 | }, 122 | { 123 | "name": "constructs", 124 | "version": ">=10.4.2", 125 | "type": "peer" 126 | }, 127 | { 128 | "name": "@cdktf/tf-module-stack", 129 | "version": ">=7.0.0", 130 | "type": "runtime" 131 | } 132 | ], 133 | "//": "~~ Generated by projen. To modify, edit .projenrc.ts and run \"npx projen\"." 134 | } 135 | -------------------------------------------------------------------------------- /examples/hybrid-module/.github/workflows/upgrade-main.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | name: upgrade-main 4 | on: 5 | workflow_dispatch: {} 6 | schedule: 7 | - cron: 0 0 * * * 8 | jobs: 9 | upgrade: 10 | name: Upgrade 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: read 14 | outputs: 15 | patch_created: ${{ steps.create_patch.outputs.patch_created }} 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4 19 | with: 20 | ref: main 21 | - name: Install dependencies 22 | run: yarn install --check-files --frozen-lockfile 23 | - name: Upgrade dependencies 24 | run: npx projen upgrade 25 | - name: Find mutations 26 | id: create_patch 27 | run: |- 28 | git add . 29 | git diff --staged --patch --exit-code > repo.patch || echo "patch_created=true" >> $GITHUB_OUTPUT 30 | working-directory: ./ 31 | - name: Upload patch 32 | if: steps.create_patch.outputs.patch_created 33 | uses: actions/upload-artifact@v4.4.0 34 | with: 35 | name: repo.patch 36 | path: repo.patch 37 | overwrite: true 38 | pr: 39 | name: Create Pull Request 40 | needs: upgrade 41 | runs-on: ubuntu-latest 42 | permissions: 43 | contents: read 44 | if: ${{ needs.upgrade.outputs.patch_created }} 45 | steps: 46 | - name: Checkout 47 | uses: actions/checkout@v4 48 | with: 49 | ref: main 50 | - name: Download patch 51 | uses: actions/download-artifact@v4 52 | with: 53 | name: repo.patch 54 | path: ${{ runner.temp }} 55 | - name: Apply patch 56 | run: '[ -s ${{ runner.temp }}/repo.patch ] && git apply ${{ runner.temp }}/repo.patch || echo "Empty patch. Skipping."' 57 | - name: Set git identity 58 | run: |- 59 | git config user.name "github-actions" 60 | git config user.email "github-actions@github.com" 61 | - name: Create Pull Request 62 | id: create-pr 63 | uses: peter-evans/create-pull-request@v6 64 | with: 65 | token: ${{ secrets.PROJEN_GITHUB_TOKEN }} 66 | commit-message: |- 67 | chore(deps): upgrade dependencies 68 | 69 | Upgrades project dependencies. See details in [workflow run]. 70 | 71 | [Workflow Run]: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} 72 | 73 | ------ 74 | 75 | *Automatically created by projen via the "upgrade-main" workflow* 76 | branch: github-actions/upgrade-main 77 | title: "chore(deps): upgrade dependencies" 78 | body: |- 79 | Upgrades project dependencies. See details in [workflow run]. 80 | 81 | [Workflow Run]: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} 82 | 83 | ------ 84 | 85 | *Automatically created by projen via the "upgrade-main" workflow* 86 | author: github-actions 87 | committer: github-actions 88 | signoff: true 89 | -------------------------------------------------------------------------------- /examples/terraform-module/.github/workflows/upgrade-main.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | name: upgrade-main 4 | on: 5 | workflow_dispatch: {} 6 | schedule: 7 | - cron: 0 0 * * * 8 | jobs: 9 | upgrade: 10 | name: Upgrade 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: read 14 | outputs: 15 | patch_created: ${{ steps.create_patch.outputs.patch_created }} 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4 19 | with: 20 | ref: main 21 | - name: Install dependencies 22 | run: yarn install --check-files --frozen-lockfile 23 | - name: Upgrade dependencies 24 | run: npx projen upgrade 25 | - name: Find mutations 26 | id: create_patch 27 | run: |- 28 | git add . 29 | git diff --staged --patch --exit-code > repo.patch || echo "patch_created=true" >> $GITHUB_OUTPUT 30 | working-directory: ./ 31 | - name: Upload patch 32 | if: steps.create_patch.outputs.patch_created 33 | uses: actions/upload-artifact@v4.4.0 34 | with: 35 | name: repo.patch 36 | path: repo.patch 37 | overwrite: true 38 | pr: 39 | name: Create Pull Request 40 | needs: upgrade 41 | runs-on: ubuntu-latest 42 | permissions: 43 | contents: read 44 | if: ${{ needs.upgrade.outputs.patch_created }} 45 | steps: 46 | - name: Checkout 47 | uses: actions/checkout@v4 48 | with: 49 | ref: main 50 | - name: Download patch 51 | uses: actions/download-artifact@v4 52 | with: 53 | name: repo.patch 54 | path: ${{ runner.temp }} 55 | - name: Apply patch 56 | run: '[ -s ${{ runner.temp }}/repo.patch ] && git apply ${{ runner.temp }}/repo.patch || echo "Empty patch. Skipping."' 57 | - name: Set git identity 58 | run: |- 59 | git config user.name "github-actions" 60 | git config user.email "github-actions@github.com" 61 | - name: Create Pull Request 62 | id: create-pr 63 | uses: peter-evans/create-pull-request@v6 64 | with: 65 | token: ${{ secrets.PROJEN_GITHUB_TOKEN }} 66 | commit-message: |- 67 | chore(deps): upgrade dependencies 68 | 69 | Upgrades project dependencies. See details in [workflow run]. 70 | 71 | [Workflow Run]: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} 72 | 73 | ------ 74 | 75 | *Automatically created by projen via the "upgrade-main" workflow* 76 | branch: github-actions/upgrade-main 77 | title: "chore(deps): upgrade dependencies" 78 | body: |- 79 | Upgrades project dependencies. See details in [workflow run]. 80 | 81 | [Workflow Run]: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} 82 | 83 | ------ 84 | 85 | *Automatically created by projen via the "upgrade-main" workflow* 86 | author: github-actions 87 | committer: github-actions 88 | signoff: true 89 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | // ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | { 3 | "env": { 4 | "jest": true, 5 | "node": true 6 | }, 7 | "root": true, 8 | "plugins": [ 9 | "@typescript-eslint", 10 | "import" 11 | ], 12 | "parser": "@typescript-eslint/parser", 13 | "parserOptions": { 14 | "ecmaVersion": 2018, 15 | "sourceType": "module", 16 | "project": "./tsconfig.dev.json" 17 | }, 18 | "extends": [ 19 | "plugin:import/typescript", 20 | "plugin:prettier/recommended" 21 | ], 22 | "settings": { 23 | "import/parsers": { 24 | "@typescript-eslint/parser": [ 25 | ".ts", 26 | ".tsx" 27 | ] 28 | }, 29 | "import/resolver": { 30 | "node": {}, 31 | "typescript": { 32 | "project": "./tsconfig.dev.json", 33 | "alwaysTryTypes": true 34 | } 35 | } 36 | }, 37 | "ignorePatterns": [ 38 | "*.js", 39 | "*.d.ts", 40 | "node_modules/", 41 | "*.generated.ts", 42 | "coverage", 43 | "!.projenrc.ts", 44 | "!projenrc/**/*.ts" 45 | ], 46 | "rules": { 47 | "@typescript-eslint/no-require-imports": [ 48 | "error" 49 | ], 50 | "import/no-extraneous-dependencies": [ 51 | "error", 52 | { 53 | "devDependencies": [ 54 | "**/test/**", 55 | "**/build-tools/**", 56 | ".projenrc.ts", 57 | "projenrc/**/*.ts" 58 | ], 59 | "optionalDependencies": false, 60 | "peerDependencies": true 61 | } 62 | ], 63 | "import/no-unresolved": [ 64 | "error" 65 | ], 66 | "import/order": [ 67 | "warn", 68 | { 69 | "groups": [ 70 | "builtin", 71 | "external" 72 | ], 73 | "alphabetize": { 74 | "order": "asc", 75 | "caseInsensitive": true 76 | } 77 | } 78 | ], 79 | "import/no-duplicates": [ 80 | "error" 81 | ], 82 | "no-shadow": [ 83 | "off" 84 | ], 85 | "@typescript-eslint/no-shadow": [ 86 | "error" 87 | ], 88 | "key-spacing": [ 89 | "error" 90 | ], 91 | "no-multiple-empty-lines": [ 92 | "error" 93 | ], 94 | "@typescript-eslint/no-floating-promises": [ 95 | "error" 96 | ], 97 | "no-return-await": [ 98 | "off" 99 | ], 100 | "@typescript-eslint/return-await": [ 101 | "error" 102 | ], 103 | "no-trailing-spaces": [ 104 | "error" 105 | ], 106 | "dot-notation": [ 107 | "error" 108 | ], 109 | "no-bitwise": [ 110 | "error" 111 | ], 112 | "@typescript-eslint/member-ordering": [ 113 | "error", 114 | { 115 | "default": [ 116 | "public-static-field", 117 | "public-static-method", 118 | "protected-static-field", 119 | "protected-static-method", 120 | "private-static-field", 121 | "private-static-method", 122 | "field", 123 | "constructor", 124 | "method" 125 | ] 126 | } 127 | ] 128 | }, 129 | "overrides": [ 130 | { 131 | "files": [ 132 | ".projenrc.ts" 133 | ], 134 | "rules": { 135 | "@typescript-eslint/no-require-imports": "off", 136 | "import/no-extraneous-dependencies": "off" 137 | } 138 | } 139 | ] 140 | } 141 | -------------------------------------------------------------------------------- /examples/hybrid-module/.eslintrc.json: -------------------------------------------------------------------------------- 1 | // ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | { 3 | "env": { 4 | "jest": true, 5 | "node": true 6 | }, 7 | "root": true, 8 | "plugins": [ 9 | "@typescript-eslint", 10 | "import" 11 | ], 12 | "parser": "@typescript-eslint/parser", 13 | "parserOptions": { 14 | "ecmaVersion": 2018, 15 | "sourceType": "module", 16 | "project": "./tsconfig.dev.json" 17 | }, 18 | "extends": [ 19 | "plugin:import/typescript", 20 | "plugin:prettier/recommended" 21 | ], 22 | "settings": { 23 | "import/parsers": { 24 | "@typescript-eslint/parser": [ 25 | ".ts", 26 | ".tsx" 27 | ] 28 | }, 29 | "import/resolver": { 30 | "node": {}, 31 | "typescript": { 32 | "project": "./tsconfig.dev.json", 33 | "alwaysTryTypes": true 34 | } 35 | } 36 | }, 37 | "ignorePatterns": [ 38 | "*.js", 39 | "*.d.ts", 40 | "node_modules/", 41 | "*.generated.ts", 42 | "coverage", 43 | "!.projenrc.ts", 44 | "!projenrc/**/*.ts" 45 | ], 46 | "rules": { 47 | "@typescript-eslint/no-require-imports": [ 48 | "error" 49 | ], 50 | "import/no-extraneous-dependencies": [ 51 | "error", 52 | { 53 | "devDependencies": [ 54 | "**/test/**", 55 | "**/build-tools/**", 56 | ".projenrc.ts", 57 | "projenrc/**/*.ts" 58 | ], 59 | "optionalDependencies": false, 60 | "peerDependencies": true 61 | } 62 | ], 63 | "import/no-unresolved": [ 64 | "error" 65 | ], 66 | "import/order": [ 67 | "warn", 68 | { 69 | "groups": [ 70 | "builtin", 71 | "external" 72 | ], 73 | "alphabetize": { 74 | "order": "asc", 75 | "caseInsensitive": true 76 | } 77 | } 78 | ], 79 | "import/no-duplicates": [ 80 | "error" 81 | ], 82 | "no-shadow": [ 83 | "off" 84 | ], 85 | "@typescript-eslint/no-shadow": [ 86 | "error" 87 | ], 88 | "key-spacing": [ 89 | "error" 90 | ], 91 | "no-multiple-empty-lines": [ 92 | "error" 93 | ], 94 | "@typescript-eslint/no-floating-promises": [ 95 | "error" 96 | ], 97 | "no-return-await": [ 98 | "off" 99 | ], 100 | "@typescript-eslint/return-await": [ 101 | "error" 102 | ], 103 | "no-trailing-spaces": [ 104 | "error" 105 | ], 106 | "dot-notation": [ 107 | "error" 108 | ], 109 | "no-bitwise": [ 110 | "error" 111 | ], 112 | "@typescript-eslint/member-ordering": [ 113 | "error", 114 | { 115 | "default": [ 116 | "public-static-field", 117 | "public-static-method", 118 | "protected-static-field", 119 | "protected-static-method", 120 | "private-static-field", 121 | "private-static-method", 122 | "field", 123 | "constructor", 124 | "method" 125 | ] 126 | } 127 | ] 128 | }, 129 | "overrides": [ 130 | { 131 | "files": [ 132 | ".projenrc.ts" 133 | ], 134 | "rules": { 135 | "@typescript-eslint/no-require-imports": "off", 136 | "import/no-extraneous-dependencies": "off" 137 | } 138 | } 139 | ] 140 | } 141 | -------------------------------------------------------------------------------- /examples/terraform-module/.eslintrc.json: -------------------------------------------------------------------------------- 1 | // ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | { 3 | "env": { 4 | "jest": true, 5 | "node": true 6 | }, 7 | "root": true, 8 | "plugins": [ 9 | "@typescript-eslint", 10 | "import" 11 | ], 12 | "parser": "@typescript-eslint/parser", 13 | "parserOptions": { 14 | "ecmaVersion": 2018, 15 | "sourceType": "module", 16 | "project": "./tsconfig.dev.json" 17 | }, 18 | "extends": [ 19 | "plugin:import/typescript", 20 | "plugin:prettier/recommended" 21 | ], 22 | "settings": { 23 | "import/parsers": { 24 | "@typescript-eslint/parser": [ 25 | ".ts", 26 | ".tsx" 27 | ] 28 | }, 29 | "import/resolver": { 30 | "node": {}, 31 | "typescript": { 32 | "project": "./tsconfig.dev.json", 33 | "alwaysTryTypes": true 34 | } 35 | } 36 | }, 37 | "ignorePatterns": [ 38 | "*.js", 39 | "*.d.ts", 40 | "node_modules/", 41 | "*.generated.ts", 42 | "coverage", 43 | "!.projenrc.ts", 44 | "!projenrc/**/*.ts" 45 | ], 46 | "rules": { 47 | "@typescript-eslint/no-require-imports": [ 48 | "error" 49 | ], 50 | "import/no-extraneous-dependencies": [ 51 | "error", 52 | { 53 | "devDependencies": [ 54 | "**/test/**", 55 | "**/build-tools/**", 56 | ".projenrc.ts", 57 | "projenrc/**/*.ts" 58 | ], 59 | "optionalDependencies": false, 60 | "peerDependencies": true 61 | } 62 | ], 63 | "import/no-unresolved": [ 64 | "error" 65 | ], 66 | "import/order": [ 67 | "warn", 68 | { 69 | "groups": [ 70 | "builtin", 71 | "external" 72 | ], 73 | "alphabetize": { 74 | "order": "asc", 75 | "caseInsensitive": true 76 | } 77 | } 78 | ], 79 | "import/no-duplicates": [ 80 | "error" 81 | ], 82 | "no-shadow": [ 83 | "off" 84 | ], 85 | "@typescript-eslint/no-shadow": [ 86 | "error" 87 | ], 88 | "key-spacing": [ 89 | "error" 90 | ], 91 | "no-multiple-empty-lines": [ 92 | "error" 93 | ], 94 | "@typescript-eslint/no-floating-promises": [ 95 | "error" 96 | ], 97 | "no-return-await": [ 98 | "off" 99 | ], 100 | "@typescript-eslint/return-await": [ 101 | "error" 102 | ], 103 | "no-trailing-spaces": [ 104 | "error" 105 | ], 106 | "dot-notation": [ 107 | "error" 108 | ], 109 | "no-bitwise": [ 110 | "error" 111 | ], 112 | "@typescript-eslint/member-ordering": [ 113 | "error", 114 | { 115 | "default": [ 116 | "public-static-field", 117 | "public-static-method", 118 | "protected-static-field", 119 | "protected-static-method", 120 | "private-static-field", 121 | "private-static-method", 122 | "field", 123 | "constructor", 124 | "method" 125 | ] 126 | } 127 | ] 128 | }, 129 | "overrides": [ 130 | { 131 | "files": [ 132 | ".projenrc.ts" 133 | ], 134 | "rules": { 135 | "@typescript-eslint/no-require-imports": "off", 136 | "import/no-extraneous-dependencies": "off" 137 | } 138 | } 139 | ] 140 | } 141 | -------------------------------------------------------------------------------- /.github/workflows/upgrade-main.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | name: upgrade-main 4 | on: 5 | workflow_dispatch: {} 6 | schedule: 7 | - cron: 0 0 * * 1 8 | jobs: 9 | upgrade: 10 | name: Upgrade 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: read 14 | outputs: 15 | patch_created: ${{ steps.create_patch.outputs.patch_created }} 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 19 | with: 20 | ref: main 21 | - name: Install dependencies 22 | run: yarn install --check-files --frozen-lockfile 23 | - name: Upgrade dependencies 24 | run: npx projen upgrade 25 | - name: Find mutations 26 | id: create_patch 27 | run: |- 28 | git add . 29 | git diff --staged --patch --exit-code > repo.patch || echo "patch_created=true" >> $GITHUB_OUTPUT 30 | working-directory: ./ 31 | - name: Upload patch 32 | if: steps.create_patch.outputs.patch_created 33 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 34 | with: 35 | name: repo.patch 36 | path: repo.patch 37 | overwrite: true 38 | pr: 39 | name: Create Pull Request 40 | needs: upgrade 41 | runs-on: ubuntu-latest 42 | permissions: 43 | contents: read 44 | if: ${{ needs.upgrade.outputs.patch_created }} 45 | steps: 46 | - name: Checkout 47 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 48 | with: 49 | ref: main 50 | - name: Download patch 51 | uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e 52 | with: 53 | name: repo.patch 54 | path: ${{ runner.temp }} 55 | - name: Apply patch 56 | run: '[ -s ${{ runner.temp }}/repo.patch ] && git apply ${{ runner.temp }}/repo.patch || echo "Empty patch. Skipping."' 57 | - name: Set git identity 58 | run: |- 59 | git config user.name "team-tf-cdk" 60 | git config user.email "github-team-tf-cdk@hashicorp.com" 61 | - name: Create Pull Request 62 | id: create-pr 63 | uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e 64 | with: 65 | token: ${{ secrets.PROJEN_GITHUB_TOKEN }} 66 | commit-message: |- 67 | chore(deps): upgrade dependencies 68 | 69 | Upgrades project dependencies. See details in [workflow run]. 70 | 71 | [Workflow Run]: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} 72 | 73 | ------ 74 | 75 | *Automatically created by projen via the "upgrade-main" workflow* 76 | branch: github-actions/upgrade-main 77 | title: "chore(deps): upgrade dependencies" 78 | labels: auto-approve,automerge,dependencies 79 | body: |- 80 | Upgrades project dependencies. See details in [workflow run]. 81 | 82 | [Workflow Run]: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} 83 | 84 | ------ 85 | 86 | *Automatically created by projen via the "upgrade-main" workflow* 87 | author: team-tf-cdk 88 | committer: team-tf-cdk 89 | signoff: true 90 | -------------------------------------------------------------------------------- /.projen/deps.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | { 4 | "name": "@types/change-case", 5 | "type": "build" 6 | }, 7 | { 8 | "name": "@types/fs-extra", 9 | "type": "build" 10 | }, 11 | { 12 | "name": "@types/glob", 13 | "type": "build" 14 | }, 15 | { 16 | "name": "@types/jest", 17 | "type": "build" 18 | }, 19 | { 20 | "name": "@types/node", 21 | "version": "ts5.8", 22 | "type": "build" 23 | }, 24 | { 25 | "name": "@typescript-eslint/eslint-plugin", 26 | "version": "^7", 27 | "type": "build" 28 | }, 29 | { 30 | "name": "@typescript-eslint/parser", 31 | "version": "^7", 32 | "type": "build" 33 | }, 34 | { 35 | "name": "comment-json", 36 | "type": "build" 37 | }, 38 | { 39 | "name": "commit-and-tag-version", 40 | "version": "^12", 41 | "type": "build" 42 | }, 43 | { 44 | "name": "constructs", 45 | "version": "10.4.2", 46 | "type": "build" 47 | }, 48 | { 49 | "name": "eslint-config-prettier", 50 | "type": "build" 51 | }, 52 | { 53 | "name": "eslint-import-resolver-typescript", 54 | "type": "build" 55 | }, 56 | { 57 | "name": "eslint-plugin-import", 58 | "type": "build" 59 | }, 60 | { 61 | "name": "eslint-plugin-prettier", 62 | "type": "build" 63 | }, 64 | { 65 | "name": "eslint", 66 | "version": "^8", 67 | "type": "build" 68 | }, 69 | { 70 | "name": "fs-extra", 71 | "type": "build" 72 | }, 73 | { 74 | "name": "glob", 75 | "type": "build" 76 | }, 77 | { 78 | "name": "jest", 79 | "type": "build" 80 | }, 81 | { 82 | "name": "jest-junit", 83 | "version": "^15", 84 | "type": "build" 85 | }, 86 | { 87 | "name": "jsii-diff", 88 | "type": "build" 89 | }, 90 | { 91 | "name": "jsii-docgen", 92 | "version": "^10.5.0", 93 | "type": "build" 94 | }, 95 | { 96 | "name": "jsii-pacmak", 97 | "type": "build" 98 | }, 99 | { 100 | "name": "jsii-rosetta", 101 | "version": "~5.8.0", 102 | "type": "build" 103 | }, 104 | { 105 | "name": "jsii", 106 | "version": "~5.8.0", 107 | "type": "build" 108 | }, 109 | { 110 | "name": "prettier", 111 | "type": "build" 112 | }, 113 | { 114 | "name": "projen", 115 | "version": "0.88.0", 116 | "type": "build" 117 | }, 118 | { 119 | "name": "ts-jest", 120 | "type": "build" 121 | }, 122 | { 123 | "name": "ts-node", 124 | "version": "10.9.1", 125 | "type": "build" 126 | }, 127 | { 128 | "name": "typescript", 129 | "version": "~5.8.0", 130 | "type": "build" 131 | }, 132 | { 133 | "name": "change-case", 134 | "type": "bundled" 135 | }, 136 | { 137 | "name": "@types/minimatch", 138 | "version": "5.1.2", 139 | "type": "override" 140 | }, 141 | { 142 | "name": "constructs", 143 | "version": "10.4.2", 144 | "type": "override" 145 | }, 146 | { 147 | "name": "constructs", 148 | "version": ">= 10.4.2", 149 | "type": "peer" 150 | }, 151 | { 152 | "name": "projen", 153 | "version": ">= 0.88.0", 154 | "type": "peer" 155 | } 156 | ], 157 | "//": "~~ Generated by projen. To modify, edit .projenrc.ts and run \"npx projen\"." 158 | } 159 | -------------------------------------------------------------------------------- /examples/terraform-module/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-module", 3 | "repository": { 4 | "type": "git", 5 | "url": "github.com/DanielMSchmidt/my-module" 6 | }, 7 | "scripts": { 8 | "build": "npx projen build", 9 | "bump": "npx projen bump", 10 | "clobber": "npx projen clobber", 11 | "compat": "npx projen compat", 12 | "compile": "npx projen compile", 13 | "default": "npx projen default", 14 | "docgen": "npx projen docgen", 15 | "eject": "npx projen eject", 16 | "eslint": "npx projen eslint", 17 | "package": "npx projen package", 18 | "package-all": "npx projen package-all", 19 | "package:js": "npx projen package:js", 20 | "post-compile": "npx projen post-compile", 21 | "post-upgrade": "npx projen post-upgrade", 22 | "pre-compile": "npx projen pre-compile", 23 | "release": "npx projen release", 24 | "test": "npx projen test", 25 | "test:watch": "npx projen test:watch", 26 | "unbump": "npx projen unbump", 27 | "upgrade": "npx projen upgrade", 28 | "watch": "npx projen watch", 29 | "projen": "npx projen" 30 | }, 31 | "author": { 32 | "name": "Daniel Schmidt", 33 | "email": "danielmschmidt92@gmail.com", 34 | "organization": false 35 | }, 36 | "devDependencies": { 37 | "@types/jest": "^29", 38 | "@types/node": "ts5.8", 39 | "@typescript-eslint/eslint-plugin": "^7", 40 | "@typescript-eslint/parser": "^7", 41 | "cdktf": "0.21.0", 42 | "cdktf-cli": "~0.21.0", 43 | "commit-and-tag-version": "^12", 44 | "constructs": "10.4.2", 45 | "eslint": "^8", 46 | "eslint-config-prettier": "^8.10.2", 47 | "eslint-import-resolver-typescript": "^2.7.1", 48 | "eslint-plugin-import": "^2.32.0", 49 | "eslint-plugin-prettier": "^4.2.5", 50 | "jest": "^29", 51 | "jest-junit": "^15", 52 | "jsii": "~5.8.0", 53 | "jsii-diff": "^1.114.1", 54 | "jsii-docgen": "^10.0.0", 55 | "jsii-pacmak": "^1.114.1", 56 | "jsii-rosetta": "~5.8.0", 57 | "prettier": "^2.8.8", 58 | "projen": "^0.96.3", 59 | "ts-jest": "^29", 60 | "ts-node": ">=10.9.1", 61 | "typescript": "~5.8.0" 62 | }, 63 | "peerDependencies": { 64 | "cdktf": ">=0.21.0", 65 | "constructs": ">=10.4.2" 66 | }, 67 | "keywords": [ 68 | "cdk", 69 | "cdktf", 70 | "cdktf-hybrid" 71 | ], 72 | "main": "lib/index.js", 73 | "license": "Apache-2.0", 74 | "publishConfig": { 75 | "access": "public" 76 | }, 77 | "version": "0.0.0", 78 | "jest": { 79 | "coverageProvider": "v8", 80 | "testMatch": [ 81 | "/@(src|test)/**/*(*.)@(spec|test).ts?(x)", 82 | "/@(src|test)/**/__tests__/**/*.ts?(x)", 83 | "/@(projenrc)/**/*(*.)@(spec|test).ts?(x)", 84 | "/@(projenrc)/**/__tests__/**/*.ts?(x)" 85 | ], 86 | "clearMocks": true, 87 | "collectCoverage": true, 88 | "coverageReporters": [ 89 | "json", 90 | "lcov", 91 | "clover", 92 | "cobertura", 93 | "text" 94 | ], 95 | "coverageDirectory": "coverage", 96 | "coveragePathIgnorePatterns": [ 97 | "/node_modules/" 98 | ], 99 | "testPathIgnorePatterns": [ 100 | "/node_modules/" 101 | ], 102 | "watchPathIgnorePatterns": [ 103 | "/node_modules/" 104 | ], 105 | "reporters": [ 106 | "default", 107 | [ 108 | "jest-junit", 109 | { 110 | "outputDirectory": "test-reports" 111 | } 112 | ] 113 | ], 114 | "transform": { 115 | "^.+\\.[t]sx?$": [ 116 | "ts-jest", 117 | { 118 | "tsconfig": "tsconfig.dev.json" 119 | } 120 | ] 121 | } 122 | }, 123 | "types": "lib/index.d.ts", 124 | "stability": "stable", 125 | "jsii": { 126 | "outdir": "dist", 127 | "targets": {}, 128 | "tsc": { 129 | "outDir": "lib", 130 | "rootDir": "src" 131 | } 132 | }, 133 | "//": "~~ Generated by projen. To modify, edit .projenrc.ts and run \"npx projen\"." 134 | } 135 | -------------------------------------------------------------------------------- /projenrc/upgrade-projen.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) HashiCorp, Inc. 3 | * SPDX-License-Identifier: MPL-2.0 4 | */ 5 | 6 | import { javascript } from "projen"; 7 | import { JobPermission } from "projen/lib/github/workflows-model"; 8 | 9 | /** 10 | * Runs monthly to upgrade Projen. Since Projen is a peer dependency for this 11 | * project, it's not updated through the usual upgrade-main workflow. 12 | */ 13 | export class UpgradeProjen { 14 | constructor(project: javascript.NodeProject) { 15 | const workflow = project.github?.addWorkflow("upgrade-projen"); 16 | if (!workflow) throw new Error("no workflow defined"); 17 | 18 | workflow.on({ 19 | schedule: [{ cron: "13 2 10 */2 *" }], // Runs every other month 20 | workflowDispatch: {}, // allow manual triggering 21 | }); 22 | 23 | (workflow.concurrency as any) = { 24 | group: "${{ github.workflow }}-${{ github.ref }}", 25 | }; 26 | 27 | workflow.addJobs({ 28 | upgrade: { 29 | name: "Upgrade Projen", 30 | runsOn: ["ubuntu-latest"], 31 | steps: [ 32 | { 33 | name: "Checkout", 34 | uses: "actions/checkout", 35 | }, 36 | { 37 | name: "Setup Terraform", 38 | uses: "hashicorp/setup-terraform", 39 | with: { 40 | terraform_wrapper: false, 41 | }, 42 | }, 43 | { 44 | name: "Setup Node.js", 45 | uses: "actions/setup-node", 46 | with: { 47 | "node-version": project.minNodeVersion, 48 | }, 49 | }, 50 | { 51 | name: "Install", 52 | run: "yarn install", 53 | }, 54 | { 55 | name: "Get latest Projen version", 56 | id: "latest_version", 57 | run: [ 58 | `NEW_VERSION=$(yarn info projen --json | jq -r '.data.version')`, 59 | `NEW_VERSION_SHORT=$(cut -d "." -f 1,2 <<< "$NEW_VERSION")`, 60 | `echo "value=$NEW_VERSION" >> $GITHUB_OUTPUT`, 61 | `echo "short=$NEW_VERSION_SHORT" >> $GITHUB_OUTPUT`, 62 | ].join("\n"), 63 | // IMPORTANT: the above behavior changed in Yarn 2+; `yarn info` instead gives the version of the installed package 64 | // If/when we upgrade we'll likely want to switch to `yarn npm info`: https://yarnpkg.com/cli/npm/info 65 | }, 66 | { 67 | name: "Run upgrade script", 68 | run: "scripts/update-projen.sh ${{ steps.latest_version.outputs.value }}", 69 | }, 70 | { 71 | name: "Create Pull Request", 72 | uses: "peter-evans/create-pull-request", 73 | with: { 74 | "commit-message": 75 | "chore(deps)!: increase minimum required version of Projen to ${{ steps.latest_version.outputs.short }}", 76 | branch: 77 | "auto/upgrade-projen-${{ steps.latest_version.outputs.short }}", 78 | base: "main", 79 | title: 80 | "chore(deps)!: increase minimum required version of Projen to ${{ steps.latest_version.outputs.short }}", 81 | body: [ 82 | "This PR increases the version of Projen to `>= ${{ steps.latest_version.outputs.value }}`, ", 83 | "which is currently the latest version available. This script runs once per month.", 84 | ].join(" "), 85 | labels: "auto-approve,automerge,automated", 86 | token: "${{ secrets.PROJEN_GITHUB_TOKEN }}", 87 | author: "team-tf-cdk ", 88 | committer: "team-tf-cdk ", 89 | signoff: true, 90 | "delete-branch": true, 91 | }, 92 | }, 93 | ], 94 | env: { 95 | CI: "true", 96 | CHECKPOINT_DISABLE: "1", 97 | }, 98 | permissions: { 99 | contents: JobPermission.READ, 100 | }, 101 | }, 102 | }); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /.github/workflows/upgrade-cdktf.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | name: upgrade-cdktf 4 | on: 5 | schedule: 6 | - cron: 49 */6 * * * 7 | workflow_dispatch: {} 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.ref }} 10 | jobs: 11 | upgrade: 12 | name: Upgrade CDKTF 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: read 16 | env: 17 | CI: "true" 18 | CHECKPOINT_DISABLE: "1" 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 22 | - name: Setup Terraform 23 | uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd 24 | with: 25 | terraform_wrapper: false 26 | - name: Setup Node.js 27 | uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e 28 | with: {} 29 | - name: Install 30 | run: yarn install 31 | - name: Install examples 32 | run: ls -d examples/* | xargs -I {} bash -c "cd '{}' && yarn" 33 | - name: Get current CDKTF version 34 | id: current_version 35 | run: |- 36 | OLD_VERSION=$(cd examples/hybrid-module && npm list cdktf --depth=0 --json | jq -r '.dependencies.cdktf.version') 37 | OLD_VERSION_SHORT=$(cut -d "." -f 2 <<< "$OLD_VERSION") 38 | echo "value=$OLD_VERSION" >> $GITHUB_OUTPUT 39 | echo "short=$OLD_VERSION_SHORT" >> $GITHUB_OUTPUT 40 | - name: Get latest CDKTF version 41 | id: latest_version 42 | run: |- 43 | CDKTF_VERSION=$(yarn info cdktf --json | jq -r '.data.version') 44 | CDKTF_VERSION_SHORT=$(cut -d "." -f 2 <<< "$CDKTF_VERSION") 45 | CONSTRUCTS_VERSION=$(yarn info cdktf --json | jq -r '.data.peerDependencies.constructs') 46 | CONSTRUCTS_VERSION_EXACT=$(cut -d "^" -f 2 <<< "$CONSTRUCTS_VERSION") 47 | echo "value=$CDKTF_VERSION" >> $GITHUB_OUTPUT 48 | echo "short=$CDKTF_VERSION_SHORT" >> $GITHUB_OUTPUT 49 | echo "constructs=$CONSTRUCTS_VERSION_EXACT" >> $GITHUB_OUTPUT 50 | - name: Run upgrade script 51 | if: steps.current_version.outputs.short != steps.latest_version.outputs.short 52 | run: scripts/update-cdktf.sh ${{ steps.latest_version.outputs.value }} ${{ steps.latest_version.outputs.constructs }} 53 | - name: Check if there are any changes 54 | id: get_changes 55 | run: echo "changed=$(git status --porcelain | wc -l)" >> $GITHUB_OUTPUT 56 | - name: Create draft pull request 57 | if: steps.get_changes.outputs.changed != 0 58 | uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e 59 | with: 60 | commit-message: "chore!: upgrade to cdktf ${{ steps.latest_version.outputs.value }}" 61 | branch: auto/upgrade-cdktf-${{ steps.latest_version.outputs.short }} 62 | base: main 63 | title: "chore!: upgrade to cdktf ${{ steps.latest_version.outputs.value }}" 64 | body: |- 65 | This PR initiates the upgrade of CDKTF from version `${{ steps.current_version.outputs.value }}` to version `${{ steps.latest_version.outputs.value }}`. 66 | Unfortunately, not everything can be automated, and the following steps need to be completed manually: 67 | 68 | - [ ] Update `@cdktf/tf-module-stack` to a version compatible with `cdktf@${{ steps.latest_version.outputs.value }}` [here](https://github.com/cdktf/projen-cdktf-hybrid-construct/blob/62c33dca3eaa249b519edd82fa0889ffcd12c6eb/src/hybrid-module.ts#L194). Look up the version [here](https://www.npmjs.com/package/@cdktf/tf-module-stack?activeTab=versions). 69 | - [ ] Run `npx projen build` 70 | 71 | Please checkout this PR, complete the above steps, push the changes to this branch, and then mark this PR as ready for review to complete the upgrade. Thanks! 72 | labels: automerge,auto-approve,dependencies 73 | token: ${{ secrets.PROJEN_GITHUB_TOKEN }} 74 | author: team-tf-cdk 75 | committer: team-tf-cdk 76 | signoff: true 77 | delete-branch: true 78 | draft: true 79 | -------------------------------------------------------------------------------- /examples/hybrid-module/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-module", 3 | "repository": { 4 | "type": "git", 5 | "url": "github.com/DanielMSchmidt/my-module" 6 | }, 7 | "scripts": { 8 | "build": "npx projen build", 9 | "bump": "npx projen bump", 10 | "clobber": "npx projen clobber", 11 | "compat": "npx projen compat", 12 | "compile": "npx projen compile", 13 | "default": "npx projen default", 14 | "docgen": "npx projen docgen", 15 | "eject": "npx projen eject", 16 | "eslint": "npx projen eslint", 17 | "package": "npx projen package", 18 | "package-all": "npx projen package-all", 19 | "package:js": "npx projen package:js", 20 | "post-compile": "npx projen post-compile", 21 | "post-upgrade": "npx projen post-upgrade", 22 | "pre-compile": "npx projen pre-compile", 23 | "release": "npx projen release", 24 | "test": "npx projen test", 25 | "test:watch": "npx projen test:watch", 26 | "unbump": "npx projen unbump", 27 | "upgrade": "npx projen upgrade", 28 | "watch": "npx projen watch", 29 | "projen": "npx projen", 30 | "terraform:test": "./scripts/tf-module-test.sh" 31 | }, 32 | "author": { 33 | "name": "Daniel Schmidt", 34 | "email": "danielmschmidt92@gmail.com", 35 | "organization": false 36 | }, 37 | "devDependencies": { 38 | "@types/jest": "^29", 39 | "@types/node": "ts5.8", 40 | "@typescript-eslint/eslint-plugin": "^7", 41 | "@typescript-eslint/parser": "^7", 42 | "cdktf": "0.21.0", 43 | "cdktf-cli": "~0.21.0", 44 | "commit-and-tag-version": "^12", 45 | "constructs": "10.4.2", 46 | "eslint": "^8", 47 | "eslint-config-prettier": "^8.10.2", 48 | "eslint-import-resolver-typescript": "^2.7.1", 49 | "eslint-plugin-import": "^2.32.0", 50 | "eslint-plugin-prettier": "^4.2.5", 51 | "jest": "^29", 52 | "jest-junit": "^15", 53 | "jsii": "~5.8.0", 54 | "jsii-diff": "^1.114.1", 55 | "jsii-docgen": "^10.0.0", 56 | "jsii-pacmak": "^1.114.1", 57 | "jsii-rosetta": "~5.8.0", 58 | "prettier": "^2.8.8", 59 | "projen": "^0.96.3", 60 | "ts-jest": "^29", 61 | "ts-node": ">=10.9.1", 62 | "typescript": "~5.8.0" 63 | }, 64 | "peerDependencies": { 65 | "cdktf": ">=0.21.0", 66 | "constructs": ">=10.4.2" 67 | }, 68 | "dependencies": { 69 | "@cdktf/tf-module-stack": ">=7.0.0" 70 | }, 71 | "keywords": [ 72 | "cdk", 73 | "cdktf", 74 | "cdktf-hybrid", 75 | "terraform" 76 | ], 77 | "main": "lib/index.js", 78 | "license": "Apache-2.0", 79 | "publishConfig": { 80 | "access": "public" 81 | }, 82 | "version": "0.0.0", 83 | "jest": { 84 | "coverageProvider": "v8", 85 | "testMatch": [ 86 | "/@(src|test)/**/*(*.)@(spec|test).ts?(x)", 87 | "/@(src|test)/**/__tests__/**/*.ts?(x)", 88 | "/@(projenrc)/**/*(*.)@(spec|test).ts?(x)", 89 | "/@(projenrc)/**/__tests__/**/*.ts?(x)" 90 | ], 91 | "clearMocks": true, 92 | "collectCoverage": true, 93 | "coverageReporters": [ 94 | "json", 95 | "lcov", 96 | "clover", 97 | "cobertura", 98 | "text" 99 | ], 100 | "coverageDirectory": "coverage", 101 | "coveragePathIgnorePatterns": [ 102 | "/node_modules/", 103 | "/terraform", 104 | "dist" 105 | ], 106 | "testPathIgnorePatterns": [ 107 | "/node_modules/", 108 | "/terraform", 109 | "dist" 110 | ], 111 | "watchPathIgnorePatterns": [ 112 | "/node_modules/" 113 | ], 114 | "reporters": [ 115 | "default", 116 | [ 117 | "jest-junit", 118 | { 119 | "outputDirectory": "test-reports" 120 | } 121 | ] 122 | ], 123 | "transform": { 124 | "^.+\\.[t]sx?$": [ 125 | "ts-jest", 126 | { 127 | "tsconfig": "tsconfig.dev.json" 128 | } 129 | ] 130 | } 131 | }, 132 | "types": "lib/index.d.ts", 133 | "stability": "stable", 134 | "jsii": { 135 | "outdir": "dist", 136 | "targets": {}, 137 | "tsc": { 138 | "outDir": "lib", 139 | "rootDir": "src" 140 | } 141 | }, 142 | "//": "~~ Generated by projen. To modify, edit .projenrc.ts and run \"npx projen\"." 143 | } 144 | -------------------------------------------------------------------------------- /test/terraform-module.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) HashiCorp, Inc. 3 | * SPDX-License-Identifier: MPL-2.0 4 | */ 5 | 6 | import { expectSnapshot, expectSnapshotOnly } from "./helper"; 7 | import { defaults } from "../src/defaults"; 8 | import { 9 | TerraformModule, 10 | publishToRegistries, 11 | publishToGithubPackages, 12 | } from "../src/index"; 13 | import { publishToArtifactory } from "../src/publishing"; 14 | 15 | describe("TerraformModule", () => { 16 | it("snapshot", () => { 17 | const project = new TerraformModule({ 18 | ...defaults, 19 | name: "my-module", 20 | author: "Daniel Schmidt", 21 | authorAddress: "danielmschmidt92@gmail.com", 22 | repositoryUrl: "github.com/DanielMSchmidt/my-module", 23 | terraformModules: [ 24 | { 25 | name: "eks", 26 | source: "terraform-aws-modules/eks/aws", 27 | version: "~> 18.0", 28 | }, 29 | { 30 | name: "eks-managed-nodegroup", 31 | source: 32 | "terraform-aws-modules/eks/aws//modules/eks-managed-node-group", 33 | version: "~> 18.0", 34 | }, 35 | ], 36 | projectId: "test-project", 37 | }); 38 | 39 | expectSnapshot(project); 40 | }); 41 | 42 | it("can be released", () => { 43 | const project = new TerraformModule({ 44 | ...defaults, 45 | name: "my-module", 46 | author: "Daniel Schmidt", 47 | authorAddress: "danielmschmidt92@gmail.com", 48 | repositoryUrl: "github.com/DanielMSchmidt/my-module", 49 | terraformModules: [ 50 | { 51 | name: "eks", 52 | source: "terraform-aws-modules/eks/aws", 53 | version: "~> 18.0", 54 | }, 55 | { 56 | name: "eks-managed-nodegroup", 57 | source: 58 | "terraform-aws-modules/eks/aws//modules/eks-managed-node-group", 59 | version: "~> 18.0", 60 | }, 61 | ], 62 | projectId: "test-project", 63 | ...publishToRegistries({ 64 | name: "my-module", 65 | namespace: "dschmidt", 66 | registries: ["npm", "nuget", "maven", "pypi"], 67 | }), 68 | }); 69 | expectSnapshotOnly(project, [".github/workflows/release.yml"]); 70 | }); 71 | 72 | it("can be released to github packages", () => { 73 | const project = new TerraformModule({ 74 | ...defaults, 75 | author: "Daniel Schmidt", 76 | authorAddress: "danielmschmidt92@gmail.com", 77 | terraformModules: [ 78 | { 79 | name: "eks", 80 | source: "terraform-aws-modules/eks/aws", 81 | version: "~> 18.0", 82 | }, 83 | { 84 | name: "eks-managed-nodegroup", 85 | source: 86 | "terraform-aws-modules/eks/aws//modules/eks-managed-node-group", 87 | version: "~> 18.0", 88 | }, 89 | ], 90 | projectId: "test-project", 91 | ...publishToGithubPackages({ 92 | repositoryName: "my-module", 93 | repositoryOwner: "dschmidt", 94 | registries: ["npm", "maven"], 95 | }), 96 | }); 97 | expectSnapshotOnly(project, [".github/workflows/release.yml"]); 98 | }); 99 | 100 | it("can be released to artifactory", () => { 101 | const project = new TerraformModule({ 102 | ...defaults, 103 | name: "my-module", 104 | author: "Daniel Schmidt", 105 | authorAddress: "danielmschmidt92@gmail.com", 106 | repositoryUrl: "github.com/DanielMSchmidt/my-module", 107 | terraformModules: [ 108 | { 109 | name: "eks", 110 | source: "terraform-aws-modules/eks/aws", 111 | version: "~> 18.0", 112 | }, 113 | { 114 | name: "eks-managed-nodegroup", 115 | source: 116 | "terraform-aws-modules/eks/aws//modules/eks-managed-node-group", 117 | version: "~> 18.0", 118 | }, 119 | ], 120 | projectId: "test-project", 121 | ...publishToArtifactory({ 122 | name: "my-module", 123 | namespace: "dschmidt", 124 | registries: ["npm", "pypi"], 125 | artifactoryApiUrl: "http://my-company.com/artifactory/api", 126 | }), 127 | }); 128 | expectSnapshotOnly(project, [".github/workflows/release.yml"]); 129 | }); 130 | }); 131 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "projen-cdktf-hybrid-construct", 3 | "description": "Projen template for CDKTF Constructs that should also be used as Terraform Modules.", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/cdktf/projen-cdktf-hybrid-construct.git" 7 | }, 8 | "scripts": { 9 | "build": "npx projen build", 10 | "buildExample": "npx projen buildExample", 11 | "buildExample:hybrid": "npx projen buildExample:hybrid", 12 | "buildExample:terraform": "npx projen buildExample:terraform", 13 | "bump": "npx projen bump", 14 | "clobber": "npx projen clobber", 15 | "compat": "npx projen compat", 16 | "compile": "npx projen compile", 17 | "default": "npx projen default", 18 | "docgen": "npx projen docgen", 19 | "eject": "npx projen eject", 20 | "eslint": "npx projen eslint", 21 | "package": "npx projen package", 22 | "package-all": "npx projen package-all", 23 | "package:js": "npx projen package:js", 24 | "post-compile": "npx projen post-compile", 25 | "post-upgrade": "npx projen post-upgrade", 26 | "pre-compile": "npx projen pre-compile", 27 | "release": "npx projen release", 28 | "test": "npx projen test", 29 | "test:watch": "npx projen test:watch", 30 | "unbump": "npx projen unbump", 31 | "upgrade": "npx projen upgrade", 32 | "upgrade:hybrid": "npx projen upgrade:hybrid", 33 | "upgrade:terraform": "npx projen upgrade:terraform", 34 | "watch": "npx projen watch", 35 | "projen": "npx projen" 36 | }, 37 | "author": { 38 | "name": "HashiCorp", 39 | "url": "https://hashicorp.com", 40 | "organization": true 41 | }, 42 | "devDependencies": { 43 | "@types/change-case": "^2.3.1", 44 | "@types/fs-extra": "^9.0.13", 45 | "@types/glob": "^7.2.0", 46 | "@types/jest": "^29", 47 | "@types/node": "ts5.8", 48 | "@typescript-eslint/eslint-plugin": "^7", 49 | "@typescript-eslint/parser": "^7", 50 | "comment-json": "^4.3.0", 51 | "commit-and-tag-version": "^12", 52 | "constructs": "10.4.2", 53 | "eslint": "^8", 54 | "eslint-config-prettier": "^8.10.2", 55 | "eslint-import-resolver-typescript": "^2.7.1", 56 | "eslint-plugin-import": "^2.32.0", 57 | "eslint-plugin-prettier": "^4.2.5", 58 | "fs-extra": "^10.1.0", 59 | "glob": "^7.2.3", 60 | "jest": "^29", 61 | "jest-junit": "^15", 62 | "jsii": "~5.8.0", 63 | "jsii-diff": "^1.114.1", 64 | "jsii-docgen": "^10.5.0", 65 | "jsii-pacmak": "^1.114.1", 66 | "jsii-rosetta": "~5.8.0", 67 | "prettier": "^2.8.8", 68 | "projen": "0.88.0", 69 | "ts-jest": "^29", 70 | "ts-node": "10.9.1", 71 | "typescript": "~5.8.0" 72 | }, 73 | "peerDependencies": { 74 | "constructs": ">= 10.4.2", 75 | "projen": ">= 0.88.0" 76 | }, 77 | "dependencies": { 78 | "change-case": "^4.1.2" 79 | }, 80 | "bundledDependencies": [ 81 | "change-case" 82 | ], 83 | "resolutions": { 84 | "@types/minimatch": "5.1.2", 85 | "constructs": "10.4.2" 86 | }, 87 | "main": "lib/index.js", 88 | "license": "MPL-2.0", 89 | "publishConfig": { 90 | "access": "public" 91 | }, 92 | "version": "0.0.0", 93 | "jest": { 94 | "coverageProvider": "v8", 95 | "testMatch": [ 96 | "/@(src|test)/**/*(*.)@(spec|test).ts?(x)", 97 | "/@(src|test)/**/__tests__/**/*.ts?(x)", 98 | "/@(projenrc)/**/*(*.)@(spec|test).ts?(x)", 99 | "/@(projenrc)/**/__tests__/**/*.ts?(x)" 100 | ], 101 | "clearMocks": true, 102 | "collectCoverage": true, 103 | "coverageReporters": [ 104 | "json", 105 | "lcov", 106 | "clover", 107 | "cobertura", 108 | "text" 109 | ], 110 | "coverageDirectory": "coverage", 111 | "coveragePathIgnorePatterns": [ 112 | "/node_modules/" 113 | ], 114 | "testPathIgnorePatterns": [ 115 | "/node_modules/" 116 | ], 117 | "watchPathIgnorePatterns": [ 118 | "/node_modules/" 119 | ], 120 | "reporters": [ 121 | "default", 122 | [ 123 | "jest-junit", 124 | { 125 | "outputDirectory": "test-reports" 126 | } 127 | ] 128 | ], 129 | "transform": { 130 | "^.+\\.[t]sx?$": [ 131 | "ts-jest", 132 | { 133 | "tsconfig": "tsconfig.dev.json" 134 | } 135 | ] 136 | } 137 | }, 138 | "types": "lib/index.d.ts", 139 | "stability": "stable", 140 | "jsii": { 141 | "outdir": "dist", 142 | "targets": {}, 143 | "tsc": { 144 | "outDir": "lib", 145 | "rootDir": "src" 146 | } 147 | }, 148 | "//": "~~ Generated by projen. To modify, edit .projenrc.ts and run \"npx projen\"." 149 | } 150 | -------------------------------------------------------------------------------- /examples/hybrid-module/.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | name: build 4 | on: 5 | pull_request: {} 6 | workflow_dispatch: {} 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | contents: write 12 | outputs: 13 | self_mutation_happened: ${{ steps.self_mutation.outputs.self_mutation_happened }} 14 | env: 15 | CI: "true" 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4 19 | with: 20 | ref: ${{ github.event.pull_request.head.ref }} 21 | repository: ${{ github.event.pull_request.head.repo.full_name }} 22 | - name: Install dependencies 23 | run: yarn install --check-files 24 | - name: Setup Terraform 25 | uses: hashicorp/setup-terraform 26 | with: 27 | terraform_wrapper: false 28 | - name: build 29 | run: npx projen build 30 | - name: Find mutations 31 | id: self_mutation 32 | run: |- 33 | git add . 34 | git diff --staged --patch --exit-code > repo.patch || echo "self_mutation_happened=true" >> $GITHUB_OUTPUT 35 | working-directory: ./ 36 | - name: Upload patch 37 | if: steps.self_mutation.outputs.self_mutation_happened 38 | uses: actions/upload-artifact@v4.4.0 39 | with: 40 | name: repo.patch 41 | path: repo.patch 42 | overwrite: true 43 | - name: Fail build on mutation 44 | if: steps.self_mutation.outputs.self_mutation_happened 45 | run: |- 46 | echo "::error::Files were changed during build (see build log). If this was triggered from a fork, you will need to update your branch." 47 | cat repo.patch 48 | exit 1 49 | - name: Backup artifact permissions 50 | run: cd dist && getfacl -R . > permissions-backup.acl 51 | continue-on-error: true 52 | - name: Upload artifact 53 | uses: actions/upload-artifact@v4.4.0 54 | with: 55 | name: build-artifact 56 | path: dist 57 | overwrite: true 58 | self-mutation: 59 | needs: build 60 | runs-on: ubuntu-latest 61 | permissions: 62 | contents: write 63 | if: always() && needs.build.outputs.self_mutation_happened && !(github.event.pull_request.head.repo.full_name != github.repository) 64 | steps: 65 | - name: Checkout 66 | uses: actions/checkout@v4 67 | with: 68 | token: ${{ secrets.PROJEN_GITHUB_TOKEN }} 69 | ref: ${{ github.event.pull_request.head.ref }} 70 | repository: ${{ github.event.pull_request.head.repo.full_name }} 71 | - name: Download patch 72 | uses: actions/download-artifact@v4 73 | with: 74 | name: repo.patch 75 | path: ${{ runner.temp }} 76 | - name: Apply patch 77 | run: '[ -s ${{ runner.temp }}/repo.patch ] && git apply ${{ runner.temp }}/repo.patch || echo "Empty patch. Skipping."' 78 | - name: Set git identity 79 | run: |- 80 | git config user.name "github-actions" 81 | git config user.email "github-actions@github.com" 82 | - name: Push changes 83 | env: 84 | PULL_REQUEST_REF: ${{ github.event.pull_request.head.ref }} 85 | run: |- 86 | git add . 87 | git commit -s -m "chore: self mutation" 88 | git push origin HEAD:$PULL_REQUEST_REF 89 | package-js: 90 | needs: build 91 | runs-on: ubuntu-latest 92 | permissions: 93 | contents: read 94 | if: ${{ !needs.build.outputs.self_mutation_happened }} 95 | steps: 96 | - uses: actions/setup-node@v4 97 | with: 98 | node-version: lts/* 99 | - name: Download build artifacts 100 | uses: actions/download-artifact@v4 101 | with: 102 | name: build-artifact 103 | path: dist 104 | - name: Restore build artifact permissions 105 | run: cd dist && setfacl --restore=permissions-backup.acl 106 | continue-on-error: true 107 | - name: Checkout 108 | uses: actions/checkout@v4 109 | with: 110 | ref: ${{ github.event.pull_request.head.ref }} 111 | repository: ${{ github.event.pull_request.head.repo.full_name }} 112 | path: .repo 113 | - name: Install Dependencies 114 | run: cd .repo && yarn install --check-files --frozen-lockfile 115 | - name: Extract build artifact 116 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo 117 | - name: Move build artifact out of the way 118 | run: mv dist dist.old 119 | - name: Create js artifact 120 | run: cd .repo && npx projen package:js 121 | - name: Collect js artifact 122 | run: mv .repo/dist dist 123 | -------------------------------------------------------------------------------- /examples/terraform-module/.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | name: build 4 | on: 5 | pull_request: {} 6 | workflow_dispatch: {} 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | contents: write 12 | outputs: 13 | self_mutation_happened: ${{ steps.self_mutation.outputs.self_mutation_happened }} 14 | env: 15 | CI: "true" 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4 19 | with: 20 | ref: ${{ github.event.pull_request.head.ref }} 21 | repository: ${{ github.event.pull_request.head.repo.full_name }} 22 | - name: Install dependencies 23 | run: yarn install --check-files 24 | - name: Setup Terraform 25 | uses: hashicorp/setup-terraform 26 | with: 27 | terraform_wrapper: false 28 | - name: build 29 | run: npx projen build 30 | - name: Find mutations 31 | id: self_mutation 32 | run: |- 33 | git add . 34 | git diff --staged --patch --exit-code > repo.patch || echo "self_mutation_happened=true" >> $GITHUB_OUTPUT 35 | working-directory: ./ 36 | - name: Upload patch 37 | if: steps.self_mutation.outputs.self_mutation_happened 38 | uses: actions/upload-artifact@v4.4.0 39 | with: 40 | name: repo.patch 41 | path: repo.patch 42 | overwrite: true 43 | - name: Fail build on mutation 44 | if: steps.self_mutation.outputs.self_mutation_happened 45 | run: |- 46 | echo "::error::Files were changed during build (see build log). If this was triggered from a fork, you will need to update your branch." 47 | cat repo.patch 48 | exit 1 49 | - name: Backup artifact permissions 50 | run: cd dist && getfacl -R . > permissions-backup.acl 51 | continue-on-error: true 52 | - name: Upload artifact 53 | uses: actions/upload-artifact@v4.4.0 54 | with: 55 | name: build-artifact 56 | path: dist 57 | overwrite: true 58 | self-mutation: 59 | needs: build 60 | runs-on: ubuntu-latest 61 | permissions: 62 | contents: write 63 | if: always() && needs.build.outputs.self_mutation_happened && !(github.event.pull_request.head.repo.full_name != github.repository) 64 | steps: 65 | - name: Checkout 66 | uses: actions/checkout@v4 67 | with: 68 | token: ${{ secrets.PROJEN_GITHUB_TOKEN }} 69 | ref: ${{ github.event.pull_request.head.ref }} 70 | repository: ${{ github.event.pull_request.head.repo.full_name }} 71 | - name: Download patch 72 | uses: actions/download-artifact@v4 73 | with: 74 | name: repo.patch 75 | path: ${{ runner.temp }} 76 | - name: Apply patch 77 | run: '[ -s ${{ runner.temp }}/repo.patch ] && git apply ${{ runner.temp }}/repo.patch || echo "Empty patch. Skipping."' 78 | - name: Set git identity 79 | run: |- 80 | git config user.name "github-actions" 81 | git config user.email "github-actions@github.com" 82 | - name: Push changes 83 | env: 84 | PULL_REQUEST_REF: ${{ github.event.pull_request.head.ref }} 85 | run: |- 86 | git add . 87 | git commit -s -m "chore: self mutation" 88 | git push origin HEAD:$PULL_REQUEST_REF 89 | package-js: 90 | needs: build 91 | runs-on: ubuntu-latest 92 | permissions: 93 | contents: read 94 | if: ${{ !needs.build.outputs.self_mutation_happened }} 95 | steps: 96 | - uses: actions/setup-node@v4 97 | with: 98 | node-version: lts/* 99 | - name: Download build artifacts 100 | uses: actions/download-artifact@v4 101 | with: 102 | name: build-artifact 103 | path: dist 104 | - name: Restore build artifact permissions 105 | run: cd dist && setfacl --restore=permissions-backup.acl 106 | continue-on-error: true 107 | - name: Checkout 108 | uses: actions/checkout@v4 109 | with: 110 | ref: ${{ github.event.pull_request.head.ref }} 111 | repository: ${{ github.event.pull_request.head.repo.full_name }} 112 | path: .repo 113 | - name: Install Dependencies 114 | run: cd .repo && yarn install --check-files --frozen-lockfile 115 | - name: Extract build artifact 116 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo 117 | - name: Move build artifact out of the way 118 | run: mv dist dist.old 119 | - name: Create js artifact 120 | run: cd .repo && npx projen package:js 121 | - name: Collect js artifact 122 | run: mv .repo/dist dist 123 | -------------------------------------------------------------------------------- /src/terraform-module.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) HashiCorp, Inc. 3 | * SPDX-License-Identifier: MPL-2.0 4 | */ 5 | 6 | import { JsonFile, SampleDir, TextFile } from "projen"; 7 | import { ConstructLibrary, ConstructLibraryOptions } from "projen/lib/cdk"; 8 | import { JobStep } from "projen/lib/github/workflows-model"; 9 | 10 | import { defaults } from "./defaults"; 11 | 12 | export interface TerraformVersionConstraint { 13 | // name of the module 14 | readonly name: string; 15 | // path / url / registry identifier for the module 16 | readonly source: string; 17 | // version constraint (https://www.terraform.io/docs/language/providers/requirements.html#version-constraints) 18 | readonly version: string; 19 | } 20 | 21 | export interface TerraformModuleOptions extends ConstructLibraryOptions { 22 | /** 23 | * Minimum target version of this library. 24 | * @default "0.21.0" 25 | * @featured 26 | */ 27 | readonly cdktfVersion?: string; 28 | 29 | /** 30 | * Construct version to use 31 | * @default "^10.4.2" 32 | */ 33 | readonly constructVersion?: string; 34 | 35 | /** 36 | * List of provider bindings to generate in addition to the modules. 37 | */ 38 | readonly terraformProviders?: TerraformVersionConstraint[]; 39 | 40 | /** 41 | * List of modules to generate bindings for. 42 | * @featured 43 | */ 44 | readonly terraformModules: TerraformVersionConstraint[]; 45 | 46 | // Defaulted to a uuid string as cdktf would 47 | readonly projectId?: string; 48 | } 49 | 50 | /** 51 | * Terraform Modules republished as CDKTF Constructs 52 | * 53 | * @pjid terraform-module 54 | */ 55 | export class TerraformModule extends ConstructLibrary { 56 | constructor(options: TerraformModuleOptions) { 57 | super({ 58 | ...defaults, 59 | ...options, 60 | eslintOptions: Object.assign({}, options.eslintOptions, { 61 | lintProjenRc: false, 62 | // Necessary due to this bug: https://github.com/projen/projen/issues/3950#issuecomment-2483442005 63 | // If this bug gets fixed, the below line should be able to be removed 64 | fileExtensions: [], 65 | }), 66 | postBuildSteps: [], 67 | jsiiVersion: "~5.8.0", 68 | typescriptVersion: "~5.8.0", // should always be the same major/minor as JSII 69 | }); 70 | const constructVersion = options.constructVersion || "10.4.2"; 71 | const cdktfVersion = options.cdktfVersion || "0.21.0"; 72 | 73 | const constructSrcCode = ` 74 | // Re-Export module bindings 75 | export * from "./terraformModules"; 76 | 77 | // Add your custom constructs here 78 | `; 79 | 80 | const constructTestCode = ` 81 | // import { Testing } from "cdktf"; 82 | // import "cdktf/lib/testing/adapters/jest"; 83 | 84 | // To learn more about testing see cdk.tf/testing 85 | describe("MyModule", () => { 86 | it.todo("should be tested") 87 | }); 88 | `; 89 | 90 | this.addPeerDeps( 91 | `constructs@>=${constructVersion}`, 92 | `cdktf@>=${cdktfVersion}` 93 | ); 94 | this.addDevDeps( 95 | `cdktf@~${cdktfVersion}`, 96 | `cdktf-cli@~${cdktfVersion}`, 97 | `constructs@${constructVersion}`, 98 | "ts-node@>=10.9.1", 99 | "jsii-docgen@^10.0.0" 100 | ); 101 | this.addKeywords("cdktf", "cdktf-hybrid"); 102 | 103 | new SampleDir(this, this.srcdir, { 104 | files: { 105 | "index.ts": constructSrcCode.trim(), 106 | "__tests__/index-test.ts": constructTestCode.trim(), 107 | }, 108 | }); 109 | new TextFile(this, `${this.srcdir}/terraformModules.ts`, { 110 | committed: true, 111 | marker: true, 112 | lines: [ 113 | ...(options.terraformModules || []).map( 114 | (tfModule) => `export * from "./.gen/modules/${tfModule.name}";` 115 | ), 116 | "", 117 | ], 118 | }); 119 | 120 | new JsonFile(this, `${this.srcdir}/cdktf.json`, { 121 | committed: true, 122 | obj: { 123 | language: "typescript", 124 | app: "npx ts-node index.ts", 125 | terraformProviders: options.terraformProviders || [], 126 | terraformModules: options.terraformModules, 127 | projectId: 128 | options.projectId || 129 | "terraform-module-99a770a7-3ec5-40f7-8cd3-dbc72f75a00a", 130 | }, 131 | }); 132 | 133 | this.preCompileTask.exec(`npx cdktf get`, { cwd: this.srcdir }); 134 | 135 | const setupTerraformStep = { 136 | name: "Setup Terraform", 137 | uses: "hashicorp/setup-terraform", 138 | with: { 139 | terraform_wrapper: false, 140 | }, 141 | }; 142 | const buildSteps = (this.buildWorkflow as any).preBuildSteps as JobStep[]; 143 | const releaseSteps = (this.release as any).defaultBranch.workflow.jobs 144 | .release.steps; 145 | buildSteps.push(setupTerraformStep); 146 | releaseSteps.splice(1, 0, setupTerraformStep); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /examples/hybrid-module/.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | name: release 4 | on: 5 | push: 6 | branches: 7 | - main 8 | workflow_dispatch: {} 9 | concurrency: 10 | group: ${{ github.workflow }} 11 | cancel-in-progress: false 12 | jobs: 13 | release: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: write 17 | outputs: 18 | latest_commit: ${{ steps.git_remote.outputs.latest_commit }} 19 | tag_exists: ${{ steps.check_tag_exists.outputs.exists }} 20 | env: 21 | CI: "true" 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v4 25 | with: 26 | fetch-depth: 0 27 | - name: Setup Terraform 28 | uses: hashicorp/setup-terraform 29 | with: 30 | terraform_wrapper: false 31 | - name: Set git identity 32 | run: |- 33 | git config user.name "github-actions" 34 | git config user.email "github-actions@github.com" 35 | - name: Install dependencies 36 | run: yarn install --check-files --frozen-lockfile 37 | - name: release 38 | run: npx projen release 39 | - name: Check if version has already been tagged 40 | id: check_tag_exists 41 | run: |- 42 | TAG=$(cat dist/releasetag.txt) 43 | ([ ! -z "$TAG" ] && git ls-remote -q --exit-code --tags origin $TAG && (echo "exists=true" >> $GITHUB_OUTPUT)) || (echo "exists=false" >> $GITHUB_OUTPUT) 44 | cat $GITHUB_OUTPUT 45 | - name: Check for new commits 46 | id: git_remote 47 | run: |- 48 | echo "latest_commit=$(git ls-remote origin -h ${{ github.ref }} | cut -f1)" >> $GITHUB_OUTPUT 49 | cat $GITHUB_OUTPUT 50 | - name: Backup artifact permissions 51 | if: ${{ steps.git_remote.outputs.latest_commit == github.sha }} 52 | run: cd dist && getfacl -R . > permissions-backup.acl 53 | continue-on-error: true 54 | - name: Upload artifact 55 | if: ${{ steps.git_remote.outputs.latest_commit == github.sha }} 56 | uses: actions/upload-artifact@v4.4.0 57 | with: 58 | name: build-artifact 59 | path: dist 60 | overwrite: true 61 | release_github: 62 | name: Publish to GitHub Releases 63 | needs: 64 | - release 65 | - release_npm 66 | runs-on: ubuntu-latest 67 | permissions: 68 | contents: write 69 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha 70 | steps: 71 | - uses: actions/setup-node@v4 72 | with: 73 | node-version: lts/* 74 | - name: Download build artifacts 75 | uses: actions/download-artifact@v4 76 | with: 77 | name: build-artifact 78 | path: dist 79 | - name: Restore build artifact permissions 80 | run: cd dist && setfacl --restore=permissions-backup.acl 81 | continue-on-error: true 82 | - name: Release 83 | env: 84 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 85 | GITHUB_REPOSITORY: ${{ github.repository }} 86 | GITHUB_REF: ${{ github.sha }} 87 | run: errout=$(mktemp); gh release create $(cat dist/releasetag.txt) -R $GITHUB_REPOSITORY -F dist/changelog.md -t $(cat dist/releasetag.txt) --target $GITHUB_REF 2> $errout && true; exitcode=$?; if [ $exitcode -ne 0 ] && ! grep -q "Release.tag_name already exists" $errout; then cat $errout; exit $exitcode; fi 88 | release_npm: 89 | name: Publish to npm 90 | needs: release 91 | runs-on: ubuntu-latest 92 | permissions: 93 | id-token: write 94 | contents: read 95 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha 96 | steps: 97 | - uses: actions/setup-node@v4 98 | with: 99 | node-version: lts/* 100 | - name: Download build artifacts 101 | uses: actions/download-artifact@v4 102 | with: 103 | name: build-artifact 104 | path: dist 105 | - name: Restore build artifact permissions 106 | run: cd dist && setfacl --restore=permissions-backup.acl 107 | continue-on-error: true 108 | - name: Checkout 109 | uses: actions/checkout@v4 110 | with: 111 | path: .repo 112 | - name: Install Dependencies 113 | run: cd .repo && yarn install --check-files --frozen-lockfile 114 | - name: Extract build artifact 115 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo 116 | - name: Move build artifact out of the way 117 | run: mv dist dist.old 118 | - name: Create js artifact 119 | run: cd .repo && npx projen package:js 120 | - name: Collect js artifact 121 | run: mv .repo/dist dist 122 | - name: Release 123 | env: 124 | NPM_DIST_TAG: latest 125 | NPM_REGISTRY: registry.npmjs.org 126 | NPM_CONFIG_PROVENANCE: "true" 127 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 128 | run: npx -p publib@latest publib-npm 129 | -------------------------------------------------------------------------------- /examples/terraform-module/.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | name: release 4 | on: 5 | push: 6 | branches: 7 | - main 8 | workflow_dispatch: {} 9 | concurrency: 10 | group: ${{ github.workflow }} 11 | cancel-in-progress: false 12 | jobs: 13 | release: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: write 17 | outputs: 18 | latest_commit: ${{ steps.git_remote.outputs.latest_commit }} 19 | tag_exists: ${{ steps.check_tag_exists.outputs.exists }} 20 | env: 21 | CI: "true" 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v4 25 | with: 26 | fetch-depth: 0 27 | - name: Setup Terraform 28 | uses: hashicorp/setup-terraform 29 | with: 30 | terraform_wrapper: false 31 | - name: Set git identity 32 | run: |- 33 | git config user.name "github-actions" 34 | git config user.email "github-actions@github.com" 35 | - name: Install dependencies 36 | run: yarn install --check-files --frozen-lockfile 37 | - name: release 38 | run: npx projen release 39 | - name: Check if version has already been tagged 40 | id: check_tag_exists 41 | run: |- 42 | TAG=$(cat dist/releasetag.txt) 43 | ([ ! -z "$TAG" ] && git ls-remote -q --exit-code --tags origin $TAG && (echo "exists=true" >> $GITHUB_OUTPUT)) || (echo "exists=false" >> $GITHUB_OUTPUT) 44 | cat $GITHUB_OUTPUT 45 | - name: Check for new commits 46 | id: git_remote 47 | run: |- 48 | echo "latest_commit=$(git ls-remote origin -h ${{ github.ref }} | cut -f1)" >> $GITHUB_OUTPUT 49 | cat $GITHUB_OUTPUT 50 | - name: Backup artifact permissions 51 | if: ${{ steps.git_remote.outputs.latest_commit == github.sha }} 52 | run: cd dist && getfacl -R . > permissions-backup.acl 53 | continue-on-error: true 54 | - name: Upload artifact 55 | if: ${{ steps.git_remote.outputs.latest_commit == github.sha }} 56 | uses: actions/upload-artifact@v4.4.0 57 | with: 58 | name: build-artifact 59 | path: dist 60 | overwrite: true 61 | release_github: 62 | name: Publish to GitHub Releases 63 | needs: 64 | - release 65 | - release_npm 66 | runs-on: ubuntu-latest 67 | permissions: 68 | contents: write 69 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha 70 | steps: 71 | - uses: actions/setup-node@v4 72 | with: 73 | node-version: lts/* 74 | - name: Download build artifacts 75 | uses: actions/download-artifact@v4 76 | with: 77 | name: build-artifact 78 | path: dist 79 | - name: Restore build artifact permissions 80 | run: cd dist && setfacl --restore=permissions-backup.acl 81 | continue-on-error: true 82 | - name: Release 83 | env: 84 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 85 | GITHUB_REPOSITORY: ${{ github.repository }} 86 | GITHUB_REF: ${{ github.sha }} 87 | run: errout=$(mktemp); gh release create $(cat dist/releasetag.txt) -R $GITHUB_REPOSITORY -F dist/changelog.md -t $(cat dist/releasetag.txt) --target $GITHUB_REF 2> $errout && true; exitcode=$?; if [ $exitcode -ne 0 ] && ! grep -q "Release.tag_name already exists" $errout; then cat $errout; exit $exitcode; fi 88 | release_npm: 89 | name: Publish to npm 90 | needs: release 91 | runs-on: ubuntu-latest 92 | permissions: 93 | id-token: write 94 | contents: read 95 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha 96 | steps: 97 | - uses: actions/setup-node@v4 98 | with: 99 | node-version: lts/* 100 | - name: Download build artifacts 101 | uses: actions/download-artifact@v4 102 | with: 103 | name: build-artifact 104 | path: dist 105 | - name: Restore build artifact permissions 106 | run: cd dist && setfacl --restore=permissions-backup.acl 107 | continue-on-error: true 108 | - name: Checkout 109 | uses: actions/checkout@v4 110 | with: 111 | path: .repo 112 | - name: Install Dependencies 113 | run: cd .repo && yarn install --check-files --frozen-lockfile 114 | - name: Extract build artifact 115 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo 116 | - name: Move build artifact out of the way 117 | run: mv dist dist.old 118 | - name: Create js artifact 119 | run: cd .repo && npx projen package:js 120 | - name: Collect js artifact 121 | run: mv .repo/dist dist 122 | - name: Release 123 | env: 124 | NPM_DIST_TAG: latest 125 | NPM_REGISTRY: registry.npmjs.org 126 | NPM_CONFIG_PROVENANCE: "true" 127 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 128 | run: npx -p publib@latest publib-npm 129 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | name: build 4 | on: 5 | pull_request: {} 6 | workflow_dispatch: {} 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | contents: write 12 | outputs: 13 | self_mutation_happened: ${{ steps.self_mutation.outputs.self_mutation_happened }} 14 | env: 15 | CI: "true" 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 19 | with: 20 | ref: ${{ github.event.pull_request.head.ref }} 21 | repository: ${{ github.event.pull_request.head.repo.full_name }} 22 | - name: Install dependencies 23 | run: yarn install --check-files 24 | - name: Setup Terraform 25 | uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd 26 | with: 27 | terraform_wrapper: false 28 | - name: build 29 | run: npx projen build 30 | - name: Setup Copywrite tool 31 | uses: hashicorp/setup-copywrite@32638da2d4e81d56a0764aa1547882fc4d209636 32 | - name: Add headers using Copywrite tool 33 | run: copywrite headers 34 | - name: Find mutations 35 | id: self_mutation 36 | run: |- 37 | git add . 38 | git diff --staged --patch --exit-code > repo.patch || echo "self_mutation_happened=true" >> $GITHUB_OUTPUT 39 | working-directory: ./ 40 | - name: Upload patch 41 | if: steps.self_mutation.outputs.self_mutation_happened 42 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 43 | with: 44 | name: repo.patch 45 | path: repo.patch 46 | overwrite: true 47 | - name: Fail build on mutation 48 | if: steps.self_mutation.outputs.self_mutation_happened 49 | run: |- 50 | echo "::error::Files were changed during build (see build log). If this was triggered from a fork, you will need to update your branch." 51 | cat repo.patch 52 | exit 1 53 | - name: Backup artifact permissions 54 | run: cd dist && getfacl -R . > permissions-backup.acl 55 | continue-on-error: true 56 | - name: Upload artifact 57 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 58 | with: 59 | name: build-artifact 60 | path: dist 61 | overwrite: true 62 | self-mutation: 63 | needs: build 64 | runs-on: ubuntu-latest 65 | permissions: 66 | contents: write 67 | if: always() && needs.build.outputs.self_mutation_happened && !(github.event.pull_request.head.repo.full_name != github.repository) 68 | steps: 69 | - name: Checkout 70 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 71 | with: 72 | token: ${{ secrets.PROJEN_GITHUB_TOKEN }} 73 | ref: ${{ github.event.pull_request.head.ref }} 74 | repository: ${{ github.event.pull_request.head.repo.full_name }} 75 | - name: Download patch 76 | uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e 77 | with: 78 | name: repo.patch 79 | path: ${{ runner.temp }} 80 | - name: Apply patch 81 | run: '[ -s ${{ runner.temp }}/repo.patch ] && git apply ${{ runner.temp }}/repo.patch || echo "Empty patch. Skipping."' 82 | - name: Set git identity 83 | run: |- 84 | git config user.name "team-tf-cdk" 85 | git config user.email "github-team-tf-cdk@hashicorp.com" 86 | - name: Push changes 87 | env: 88 | PULL_REQUEST_REF: ${{ github.event.pull_request.head.ref }} 89 | run: |- 90 | git add . 91 | git commit -s -m "chore: self mutation" 92 | git push origin HEAD:$PULL_REQUEST_REF 93 | package-js: 94 | needs: build 95 | runs-on: ubuntu-latest 96 | permissions: 97 | contents: read 98 | if: ${{ !needs.build.outputs.self_mutation_happened }} 99 | steps: 100 | - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e 101 | with: 102 | node-version: lts/* 103 | - name: Download build artifacts 104 | uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e 105 | with: 106 | name: build-artifact 107 | path: dist 108 | - name: Restore build artifact permissions 109 | run: cd dist && setfacl --restore=permissions-backup.acl 110 | continue-on-error: true 111 | - name: Checkout 112 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 113 | with: 114 | ref: ${{ github.event.pull_request.head.ref }} 115 | repository: ${{ github.event.pull_request.head.repo.full_name }} 116 | path: .repo 117 | - name: Install Dependencies 118 | run: cd .repo && yarn install --check-files --frozen-lockfile 119 | - name: Extract build artifact 120 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo 121 | - name: Move build artifact out of the way 122 | run: mv dist dist.old 123 | - name: Create js artifact 124 | run: cd .repo && npx projen package:js 125 | - name: Collect js artifact 126 | run: mv .repo/dist dist 127 | -------------------------------------------------------------------------------- /src/publishing.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) HashiCorp, Inc. 3 | * SPDX-License-Identifier: MPL-2.0 4 | */ 5 | 6 | import { pascalCase } from "change-case"; 7 | import { ConstructLibraryOptions } from "projen/lib/cdk"; 8 | 9 | export type Registry = "npm" | "maven" | "nuget" | "pypi"; 10 | 11 | export type PublishOptions = { 12 | name: string; 13 | namespace: string; 14 | registries: Registry[]; 15 | }; 16 | 17 | type AtomicObject = 18 | | Function 19 | | Promise 20 | | Date 21 | | RegExp 22 | | Boolean 23 | | Number 24 | | String; 25 | 26 | type Mutable = T extends AtomicObject 27 | ? T 28 | : T extends ReadonlyMap // Map extends ReadonlyMap 29 | ? Map, Mutable> 30 | : T extends ReadonlySet // Set extends ReadonlySet 31 | ? Set> 32 | : T extends object 33 | ? { -readonly [K in keyof T]: Mutable } 34 | : T; 35 | 36 | type PublishingConfig = Mutable< 37 | Pick< 38 | ConstructLibraryOptions, 39 | | "publishToPypi" 40 | | "publishToMaven" 41 | | "publishToNuget" 42 | | "releaseToNpm" 43 | | "npmRegistryUrl" 44 | > 45 | >; 46 | 47 | export function publishToRegistries(options: PublishOptions): PublishingConfig { 48 | const { name, namespace, registries } = options; 49 | const sanitizedNamespace = namespace.replace(/-/gi, "_"); 50 | const sanitizedName = name.replace(/-/gi, "_"); 51 | const config: PublishingConfig = { 52 | releaseToNpm: registries.includes("npm"), 53 | }; 54 | 55 | if (registries.includes("pypi")) { 56 | config.publishToPypi = { 57 | distName: `${sanitizedNamespace}-${sanitizedName}`, 58 | module: `${sanitizedNamespace}_${sanitizedName}`, 59 | }; 60 | } 61 | 62 | if (registries.includes("maven")) { 63 | config.publishToMaven = { 64 | javaPackage: `com.${sanitizedNamespace}.${sanitizedName}`, 65 | mavenGroupId: `com.${sanitizedNamespace}`, 66 | mavenArtifactId: sanitizedName, 67 | }; 68 | } 69 | 70 | if (registries.includes("nuget")) { 71 | const nugetName = `${pascalCase(namespace)}.${pascalCase(name)}`; 72 | config.publishToNuget = { 73 | dotNetNamespace: nugetName, 74 | packageId: nugetName, 75 | }; 76 | } 77 | return config; 78 | } 79 | 80 | export type GithubRegistry = "npm" | "maven" | "nuget"; 81 | type GitHubPublishOptions = { 82 | /** 83 | * The GitHub repository of this project. 84 | */ 85 | repositoryName: string; 86 | 87 | /** 88 | * The GitHub owner of this project. 89 | */ 90 | repositoryOwner: string; 91 | 92 | /** 93 | * Registries to publish to 94 | */ 95 | registries: GithubRegistry[]; 96 | }; 97 | 98 | export type GitHubPublishConfig = { 99 | name: string; 100 | repositoryUrl: string; 101 | } & PublishingConfig; 102 | 103 | export function publishToGithubPackages( 104 | options: GitHubPublishOptions 105 | ): GitHubPublishConfig { 106 | const { registries, repositoryName, repositoryOwner } = options; 107 | const config = publishToRegistries({ 108 | name: repositoryName, 109 | namespace: repositoryOwner, 110 | registries, 111 | }); 112 | 113 | if (registries.includes("npm")) { 114 | config.npmRegistryUrl = "https://npm.pkg.github.com"; 115 | } 116 | 117 | if (registries.includes("maven")) { 118 | config.publishToMaven!.mavenRepositoryUrl = `https://maven.pkg.github.com/${repositoryOwner}/${repositoryName}`; 119 | } 120 | 121 | if (registries.includes("nuget")) { 122 | config.publishToNuget!.nugetServer = `https://nuget.pkg.github.com/${repositoryOwner}`; 123 | } 124 | 125 | return { 126 | ...config, 127 | repositoryUrl: `https://github.com/${repositoryOwner}/${repositoryName}.git`, 128 | name: `@${repositoryOwner}/${repositoryName}`, 129 | }; 130 | } 131 | 132 | export type ArtifactoryPublishOptions = PublishOptions & { 133 | // The artifactory url to publish to, e.g. "https://artifactory.example.com/artifactory/api" 134 | artifactoryApiUrl: string; 135 | 136 | // Name of the artifactory repository to publish to, defaults to the namespace 137 | artifactoryRepository?: string; 138 | }; 139 | 140 | export function publishToArtifactory( 141 | options: ArtifactoryPublishOptions 142 | ): PublishingConfig { 143 | const { 144 | registries, 145 | artifactoryApiUrl, 146 | artifactoryRepository = options.namespace, 147 | } = options; 148 | const config = publishToRegistries(options); 149 | 150 | if (registries.includes("npm")) { 151 | config.npmRegistryUrl = `${artifactoryApiUrl}/npm/${artifactoryRepository}/`; 152 | } 153 | 154 | if (registries.includes("pypi")) { 155 | config.publishToPypi!.twineRegistryUrl = `${artifactoryApiUrl}/pypi/${artifactoryRepository}/`; 156 | } 157 | 158 | if (registries.includes("maven")) { 159 | throw new Error( 160 | "Artifactory does support maven packages, but this library does not yet support it" 161 | ); 162 | // With no chance to test it the docs dont tell me enough to implement it 163 | // https://www.jfrog.com/confluence/display/JFROG/Maven+Repository#MavenRepository-ResolvingArtifactsThroughArtifactory 164 | // config.publishToMaven!.mavenRepositoryUrl = `${artifactoryApiUrl}/`; 165 | } 166 | 167 | if (registries.includes("nuget")) { 168 | config.publishToNuget!.nugetServer = `${artifactoryApiUrl}/nuget/${artifactoryRepository}/`; 169 | } 170 | 171 | return config; 172 | } 173 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | name: release 4 | on: 5 | push: 6 | branches: 7 | - main 8 | paths-ignore: 9 | - examples/** 10 | - .github/ISSUE_TEMPLATE/** 11 | - .github/CODEOWNERS 12 | - .github/dependabot.yml 13 | - .github/**/*.md 14 | workflow_dispatch: {} 15 | concurrency: 16 | group: ${{ github.workflow }} 17 | cancel-in-progress: false 18 | jobs: 19 | release: 20 | runs-on: ubuntu-latest 21 | permissions: 22 | contents: write 23 | outputs: 24 | latest_commit: ${{ steps.git_remote.outputs.latest_commit }} 25 | tag_exists: ${{ steps.check_tag_exists.outputs.exists }} 26 | env: 27 | CI: "true" 28 | steps: 29 | - name: Checkout 30 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 31 | with: 32 | fetch-depth: 0 33 | - name: Setup Terraform 34 | uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd 35 | with: 36 | terraform_wrapper: false 37 | - name: Set git identity 38 | run: |- 39 | git config user.name "github-actions" 40 | git config user.email "github-actions@github.com" 41 | - name: Install dependencies 42 | run: yarn install --check-files --frozen-lockfile 43 | - name: release 44 | run: npx projen release 45 | - name: Check if version has already been tagged 46 | id: check_tag_exists 47 | run: |- 48 | TAG=$(cat dist/releasetag.txt) 49 | ([ ! -z "$TAG" ] && git ls-remote -q --exit-code --tags origin $TAG && (echo "exists=true" >> $GITHUB_OUTPUT)) || (echo "exists=false" >> $GITHUB_OUTPUT) 50 | cat $GITHUB_OUTPUT 51 | - name: Check for new commits 52 | id: git_remote 53 | run: |- 54 | echo "latest_commit=$(git ls-remote origin -h ${{ github.ref }} | cut -f1)" >> $GITHUB_OUTPUT 55 | cat $GITHUB_OUTPUT 56 | - name: Backup artifact permissions 57 | if: ${{ steps.git_remote.outputs.latest_commit == github.sha }} 58 | run: cd dist && getfacl -R . > permissions-backup.acl 59 | continue-on-error: true 60 | - name: Upload artifact 61 | if: ${{ steps.git_remote.outputs.latest_commit == github.sha }} 62 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 63 | with: 64 | name: build-artifact 65 | path: dist 66 | overwrite: true 67 | release_github: 68 | name: Publish to GitHub Releases 69 | needs: 70 | - release 71 | - release_npm 72 | runs-on: ubuntu-latest 73 | permissions: 74 | contents: write 75 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha 76 | steps: 77 | - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e 78 | with: 79 | node-version: lts/* 80 | - name: Download build artifacts 81 | uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e 82 | with: 83 | name: build-artifact 84 | path: dist 85 | - name: Restore build artifact permissions 86 | run: cd dist && setfacl --restore=permissions-backup.acl 87 | continue-on-error: true 88 | - name: Release 89 | env: 90 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 91 | GITHUB_REPOSITORY: ${{ github.repository }} 92 | GITHUB_REF: ${{ github.sha }} 93 | run: errout=$(mktemp); gh release create $(cat dist/releasetag.txt) -R $GITHUB_REPOSITORY -F dist/changelog.md -t $(cat dist/releasetag.txt) --target $GITHUB_REF 2> $errout && true; exitcode=$?; if [ $exitcode -ne 0 ] && ! grep -q "Release.tag_name already exists" $errout; then cat $errout; exit $exitcode; fi 94 | release_npm: 95 | name: Publish to npm 96 | needs: release 97 | runs-on: ubuntu-latest 98 | permissions: 99 | id-token: write 100 | contents: read 101 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha 102 | steps: 103 | - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e 104 | with: 105 | node-version: lts/* 106 | - name: Download build artifacts 107 | uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e 108 | with: 109 | name: build-artifact 110 | path: dist 111 | - name: Restore build artifact permissions 112 | run: cd dist && setfacl --restore=permissions-backup.acl 113 | continue-on-error: true 114 | - name: Checkout 115 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 116 | with: 117 | path: .repo 118 | - name: Install Dependencies 119 | run: cd .repo && yarn install --check-files --frozen-lockfile 120 | - name: Extract build artifact 121 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo 122 | - name: Move build artifact out of the way 123 | run: mv dist dist.old 124 | - name: Create js artifact 125 | run: cd .repo && npx projen package:js 126 | - name: Collect js artifact 127 | run: mv .repo/dist dist 128 | - name: Release 129 | env: 130 | NPM_DIST_TAG: latest 131 | NPM_REGISTRY: registry.npmjs.org 132 | NPM_CONFIG_PROVENANCE: "true" 133 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 134 | run: npx -p publib@latest publib-npm 135 | -------------------------------------------------------------------------------- /examples/hybrid-module/API.md: -------------------------------------------------------------------------------- 1 | # API Reference 2 | 3 | ## Constructs 4 | 5 | ### MyConstruct 6 | 7 | #### Initializers 8 | 9 | ```typescript 10 | import { MyConstruct } from 'my-module' 11 | 12 | new MyConstruct(scope: Construct, id: string, config: MyConstructOptions) 13 | ``` 14 | 15 | | **Name** | **Type** | **Description** | 16 | | --- | --- | --- | 17 | | scope | constructs.Construct | *No description.* | 18 | | id | string | *No description.* | 19 | | config | MyConstructOptions | *No description.* | 20 | 21 | --- 22 | 23 | ##### `scope`Required 24 | 25 | - *Type:* constructs.Construct 26 | 27 | --- 28 | 29 | ##### `id`Required 30 | 31 | - *Type:* string 32 | 33 | --- 34 | 35 | ##### `config`Required 36 | 37 | - *Type:* MyConstructOptions 38 | 39 | --- 40 | 41 | #### Methods 42 | 43 | | **Name** | **Description** | 44 | | --- | --- | 45 | | toString | Returns a string representation of this construct. | 46 | 47 | --- 48 | 49 | ##### `toString` 50 | 51 | ```typescript 52 | public toString(): string 53 | ``` 54 | 55 | Returns a string representation of this construct. 56 | 57 | #### Static Functions 58 | 59 | | **Name** | **Description** | 60 | | --- | --- | 61 | | isConstruct | Checks if `x` is a construct. | 62 | 63 | --- 64 | 65 | ##### `isConstruct` 66 | 67 | ```typescript 68 | import { MyConstruct } from 'my-module' 69 | 70 | MyConstruct.isConstruct(x: any) 71 | ``` 72 | 73 | Checks if `x` is a construct. 74 | 75 | Use this method instead of `instanceof` to properly detect `Construct` 76 | instances, even when the construct library is symlinked. 77 | 78 | Explanation: in JavaScript, multiple copies of the `constructs` library on 79 | disk are seen as independent, completely different libraries. As a 80 | consequence, the class `Construct` in each copy of the `constructs` library 81 | is seen as a different class, and an instance of one class will not test as 82 | `instanceof` the other class. `npm install` will not create installations 83 | like this, but users may manually symlink construct libraries together or 84 | use a monorepo tool: in those cases, multiple copies of the `constructs` 85 | library can be accidentally installed, and `instanceof` will behave 86 | unpredictably. It is safest to avoid using `instanceof`, and using 87 | this type-testing method instead. 88 | 89 | ###### `x`Required 90 | 91 | - *Type:* any 92 | 93 | Any object. 94 | 95 | --- 96 | 97 | #### Properties 98 | 99 | | **Name** | **Type** | **Description** | 100 | | --- | --- | --- | 101 | | node | constructs.Node | The tree node. | 102 | | config | MyConstructOptions | *No description.* | 103 | 104 | --- 105 | 106 | ##### `node`Required 107 | 108 | ```typescript 109 | public readonly node: Node; 110 | ``` 111 | 112 | - *Type:* constructs.Node 113 | 114 | The tree node. 115 | 116 | --- 117 | 118 | ##### `config`Required 119 | 120 | ```typescript 121 | public readonly config: MyConstructOptions; 122 | ``` 123 | 124 | - *Type:* MyConstructOptions 125 | 126 | --- 127 | 128 | 129 | ## Structs 130 | 131 | ### MyConstructOptions 132 | 133 | #### Initializer 134 | 135 | ```typescript 136 | import { MyConstructOptions } from 'my-module' 137 | 138 | const myConstructOptions: MyConstructOptions = { ... } 139 | ``` 140 | 141 | #### Properties 142 | 143 | | **Name** | **Type** | **Description** | 144 | | --- | --- | --- | 145 | | propertyA | string | *No description.* | 146 | 147 | --- 148 | 149 | ##### `propertyA`Required 150 | 151 | ```typescript 152 | public readonly propertyA: string; 153 | ``` 154 | 155 | - *Type:* string 156 | 157 | --- 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /.github/workflows/upgrade-jsii-typescript.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | name: upgrade-jsii-typescript 4 | on: 5 | schedule: 6 | - cron: 21 12 * * * 7 | workflow_dispatch: 8 | inputs: 9 | version: 10 | description: New JSII/TypeScript version (e.g. "5.8.0"), without carets or tildes 11 | required: false 12 | type: string 13 | concurrency: 14 | group: ${{ github.workflow }}-${{ github.ref }} 15 | jobs: 16 | version: 17 | name: Determine version to upgrade to 18 | runs-on: ubuntu-latest 19 | permissions: 20 | contents: read 21 | outputs: 22 | current: ${{ steps.current_version.outputs.value }} 23 | latest: ${{ steps.latest_version.outputs.value }} 24 | short: ${{ steps.latest_version.outputs.short }} 25 | should_upgrade: ${{ steps.latest_version.outputs.is_newer }} 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 29 | - name: Setup Node.js 30 | uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e 31 | with: {} 32 | - name: Install 33 | run: yarn install 34 | - name: Get current JSII version 35 | id: current_version 36 | run: |- 37 | CURRENT_VERSION=$(npm list jsii --depth=0 --json | jq -r '.dependencies.jsii.version') 38 | CURRENT_VERSION_SHORT=$(cut -d "." -f 1,2 <<< "$CURRENT_VERSION") 39 | CURRENT_VERSION_MAJOR=$(cut -d "." -f 1 <<< "$CURRENT_VERSION") 40 | CURRENT_VERSION_MINOR=$(cut -d "." -f 2 <<< "$CURRENT_VERSION") 41 | echo "CURRENT_JSII_VERSION=$CURRENT_VERSION" >> $GITHUB_ENV 42 | echo "CURRENT_JSII_VERSION_SHORT=$CURRENT_VERSION_SHORT" >> $GITHUB_ENV 43 | echo "CURRENT_JSII_VERSION_MAJOR=$CURRENT_VERSION_MAJOR" >> $GITHUB_ENV 44 | echo "CURRENT_JSII_VERSION_MINOR=$CURRENT_VERSION_MINOR" >> $GITHUB_ENV 45 | echo "value=$CURRENT_VERSION" >> $GITHUB_OUTPUT 46 | - name: Get the earliest supported JSII version whose EOS date is at least a month away 47 | if: ${{ ! inputs.version }} 48 | uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea 49 | with: 50 | script: |- 51 | const script = require('./scripts/check-jsii-versions.js') 52 | await script({github, context, core}) 53 | - name: Save the manually-input version to environment variables for comparison 54 | if: ${{ inputs.version }} 55 | env: 56 | NEW_VERSION: ${{ inputs.version }} 57 | run: |- 58 | NEW_VERSION_SHORT=$(cut -d "." -f 1,2 <<< "$NEW_VERSION") 59 | NEW_VERSION_MAJOR=$(cut -d "." -f 1 <<< "$NEW_VERSION") 60 | NEW_VERSION_MINOR=$(cut -d "." -f 2 <<< "$NEW_VERSION") 61 | echo "NEW_JSII_VERSION=$NEW_VERSION" >> $GITHUB_ENV 62 | echo "NEW_JSII_VERSION_SHORT=$NEW_VERSION_SHORT" >> $GITHUB_ENV 63 | echo "NEW_JSII_VERSION_MAJOR=$NEW_VERSION_MAJOR" >> $GITHUB_ENV 64 | echo "NEW_JSII_VERSION_MINOR=$NEW_VERSION_MINOR" >> $GITHUB_ENV 65 | - name: Output env variables for use in the next job 66 | id: latest_version 67 | run: |- 68 | echo "value=$NEW_JSII_VERSION" >> $GITHUB_OUTPUT 69 | echo "short=$NEW_JSII_VERSION_SHORT" >> $GITHUB_OUTPUT 70 | [[ "$NEW_JSII_VERSION_MAJOR" > "$CURRENT_JSII_VERSION_MAJOR" || ("$NEW_JSII_VERSION_MAJOR" == "$CURRENT_JSII_VERSION_MAJOR" && "$NEW_JSII_VERSION_MINOR" > "$CURRENT_JSII_VERSION_MINOR") ]] && IS_NEWER=true 71 | echo "is_newer=$IS_NEWER" >> $GITHUB_OUTPUT 72 | upgrade: 73 | name: Upgrade JSII & TypeScript 74 | needs: version 75 | runs-on: ubuntu-latest 76 | permissions: 77 | contents: read 78 | env: 79 | CI: "true" 80 | CHECKPOINT_DISABLE: "1" 81 | if: always() && needs.version.outputs.should_upgrade 82 | steps: 83 | - name: Checkout 84 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 85 | - name: Setup Terraform 86 | uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd 87 | with: 88 | terraform_wrapper: false 89 | - name: Setup Node.js 90 | uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e 91 | with: {} 92 | - name: Install 93 | run: yarn install 94 | - name: Run upgrade script 95 | run: scripts/update-jsii-typescript.sh ${{ needs.version.outputs.latest }} 96 | - name: Create Pull Request 97 | uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e 98 | with: 99 | base: main 100 | branch: auto/upgrade-jsii-ts-${{ needs.version.outputs.short }} 101 | commit-message: "chore(deps): upgrade jsii & typescript to v${{ needs.version.outputs.short }}" 102 | title: "chore(deps): upgrade jsii & typescript to v${{ needs.version.outputs.short }}" 103 | body: "This PR increases the version of JSII and TypeScript to `~${{ needs.version.outputs.latest }}` because the previous version is close to EOL or no longer supported. Support timeline: https://github.com/aws/jsii-compiler/blob/main/README.md#gear-maintenance--support" 104 | labels: auto-approve,automerge,automated 105 | token: ${{ secrets.PROJEN_GITHUB_TOKEN }} 106 | author: team-tf-cdk 107 | committer: team-tf-cdk 108 | signoff: true 109 | delete-branch: true 110 | -------------------------------------------------------------------------------- /test/helper.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @source https://github.com/projen/projen/blob/15c05dc8841033714b1bff95672dce6bca76b57d/src/util/synth.ts 3 | */ 4 | 5 | import * as fs from "fs"; 6 | import * as os from "os"; 7 | import * as path from "path"; 8 | import * as JSONC from "comment-json"; 9 | import { CommentArray } from "comment-json"; 10 | import { glob } from "glob"; 11 | import { JsonFile, Project } from "projen"; 12 | 13 | /** 14 | * Options for the Snapshot synthesis 15 | */ 16 | export interface SnapshotOptions { 17 | /** 18 | * Parse .json files as a JS object for improved inspection. 19 | * This will fail if the contents are invalid JSON. 20 | * 21 | * @default true parse .json files into an object 22 | */ 23 | readonly parseJson?: boolean; 24 | } 25 | 26 | export interface SynthOutput { 27 | [filePath: string]: any; 28 | } 29 | 30 | /** 31 | * Creates a snapshot of the files generated by a project. Ignores any non-text 32 | * files so that the snapshots are human readable. 33 | */ 34 | export function synthSnapshot( 35 | project: Project, 36 | options: SnapshotOptions = {} 37 | ): SynthOutput { 38 | // defensive: verify that "outdir" is actually in a temporary directory 39 | if ( 40 | !path.resolve(project.outdir).startsWith(os.tmpdir()) && 41 | !project.outdir.includes("project-temp-dir") 42 | ) { 43 | throw new Error( 44 | "Trying to capture a snapshot of a project outside of tmpdir, which implies this test might corrupt an existing project" 45 | ); 46 | } 47 | 48 | const synthed = Symbol.for("synthed"); 49 | if (synthed in project) { 50 | throw new Error("duplicate synth()"); 51 | } 52 | 53 | (project as any)[synthed] = true; 54 | 55 | const ENV_PROJEN_DISABLE_POST = process.env.PROJEN_DISABLE_POST; 56 | try { 57 | process.env.PROJEN_DISABLE_POST = "true"; 58 | project.synth(); 59 | const ignoreExts = ["png", "ico"]; 60 | return directorySnapshot(project.outdir, { 61 | ...options, 62 | excludeGlobs: ignoreExts.map((ext) => `**/*.${ext}`), 63 | supportJsonComments: project.files.some( 64 | // At least one json file in project supports comments 65 | (file) => file instanceof JsonFile && file.supportsComments 66 | ), 67 | }); 68 | } finally { 69 | fs.rmSync(project.outdir, { force: true, recursive: true }); 70 | 71 | // values assigned to process.env.XYZ are automatically converted to strings 72 | if (ENV_PROJEN_DISABLE_POST === undefined) { 73 | delete process.env.PROJEN_DISABLE_POST; 74 | } else { 75 | process.env.PROJEN_DISABLE_POST = ENV_PROJEN_DISABLE_POST; 76 | } 77 | } 78 | } 79 | 80 | export interface DirectorySnapshotOptions extends SnapshotOptions { 81 | /** 82 | * Globs of files to exclude. 83 | * @default [] include all files 84 | */ 85 | readonly excludeGlobs?: string[]; 86 | 87 | /** 88 | * Only snapshot the names of files and not their contents. 89 | * The value for a path will be `true` if it exists. 90 | * 91 | * @default false include file content 92 | */ 93 | readonly onlyFileNames?: boolean; 94 | 95 | /** 96 | * Parses files with different parser, supporting comments 97 | * inside .json files. 98 | * @default false 99 | */ 100 | readonly supportJsonComments?: boolean; 101 | } 102 | 103 | function isJsonLikeFile(filePath: string): boolean { 104 | const file = filePath.toLowerCase(); 105 | return ( 106 | file.endsWith(".json") || file.endsWith(".json5") || file.endsWith(".jsonc") 107 | ); 108 | } 109 | 110 | export function directorySnapshot( 111 | root: string, 112 | options: DirectorySnapshotOptions = {} 113 | ) { 114 | const output: SynthOutput = {}; 115 | 116 | const files = glob.sync("**", { 117 | ignore: [".git/**", ...(options.excludeGlobs ?? [])], 118 | cwd: root, 119 | nodir: true, 120 | dot: true, 121 | }); // returns relative file paths with POSIX separators 122 | 123 | const parseJson = options.parseJson ?? true; 124 | 125 | for (const file of files) { 126 | const filePath = path.join(root, file); 127 | 128 | let content; 129 | if (!options.onlyFileNames) { 130 | content = fs.readFileSync(filePath, "utf-8"); 131 | if (parseJson && isJsonLikeFile(filePath)) { 132 | content = cleanCommentArrays( 133 | JSONC.parse(content, undefined, !options.supportJsonComments) 134 | ); 135 | } 136 | } else { 137 | content = true; 138 | } 139 | 140 | output[file] = content; 141 | } 142 | 143 | return output; 144 | } 145 | 146 | /** 147 | * Converts type "CommentArray" back to regular JS "Array" 148 | * if there are no comments stored in it. 149 | * Prevents strict checks from failing. 150 | */ 151 | function cleanCommentArrays(obj: any): typeof obj { 152 | if (Array.isArray(obj) || isCommentArrayWithoutComments(obj)) { 153 | return Array.from(obj).map(cleanCommentArrays); 154 | } 155 | 156 | if (obj instanceof Object) { 157 | for (const p of Object.keys(obj)) { 158 | if (isCommentArrayWithoutComments(obj[p])) { 159 | obj[p] = Array.from(obj[p]).map(cleanCommentArrays); 160 | } else if (obj[p] instanceof Object) { 161 | obj[p] = cleanCommentArrays(obj[p]); 162 | } 163 | } 164 | } 165 | 166 | return obj; 167 | } 168 | 169 | /** 170 | * Checks if a "CommentArray" has no comments stored in it. 171 | */ 172 | function isCommentArrayWithoutComments(obj: any): boolean { 173 | return ( 174 | obj instanceof CommentArray && 175 | Object.getOwnPropertySymbols(obj).length === 0 176 | ); 177 | } 178 | 179 | export function expectSnapshot( 180 | project: Project, 181 | ignoredFiles = [".projen", "example"] 182 | ) { 183 | const out = synthSnapshot(project); 184 | 185 | Object.entries(out).forEach(([filePath, content]) => { 186 | if (ignoredFiles.some((file) => filePath.indexOf(file) !== -1)) { 187 | return; 188 | } 189 | expect(content).toMatchSnapshot(filePath); 190 | }); 191 | } 192 | 193 | export function expectSnapshotOnly(project: Project, onlyFiles: string[]) { 194 | const out = synthSnapshot(project); 195 | 196 | onlyFiles.forEach((file) => { 197 | expect(out[file]).toMatchSnapshot(file); 198 | }); 199 | } 200 | -------------------------------------------------------------------------------- /projenrc/upgrade-cdktf.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) HashiCorp, Inc. 3 | * SPDX-License-Identifier: MPL-2.0 4 | */ 5 | 6 | import { javascript } from "projen"; 7 | import { JobPermission } from "projen/lib/github/workflows-model"; 8 | 9 | /** 10 | * Checks for new versions of CDKTF and creates a PR with an upgrade change if there are changes. 11 | */ 12 | export class UpgradeCDKTF { 13 | constructor(project: javascript.NodeProject) { 14 | const workflow = project.github?.addWorkflow("upgrade-cdktf"); 15 | 16 | if (!workflow) throw new Error("no workflow defined"); 17 | 18 | workflow.on({ 19 | schedule: [{ cron: "49 */6 * * *" }], // Runs four times a day 20 | workflowDispatch: {}, // allow manual triggering 21 | }); 22 | 23 | (workflow.concurrency as any) = { 24 | group: "${{ github.workflow }}-${{ github.ref }}", 25 | }; 26 | 27 | workflow.addJobs({ 28 | upgrade: { 29 | name: "Upgrade CDKTF", 30 | runsOn: ["ubuntu-latest"], 31 | steps: [ 32 | { 33 | name: "Checkout", 34 | uses: "actions/checkout", 35 | }, 36 | { 37 | name: "Setup Terraform", 38 | uses: "hashicorp/setup-terraform", 39 | with: { 40 | terraform_wrapper: false, 41 | }, 42 | }, 43 | { 44 | name: "Setup Node.js", 45 | uses: "actions/setup-node", 46 | with: { 47 | "node-version": project.minNodeVersion, 48 | }, 49 | }, 50 | { 51 | name: "Install", 52 | run: "yarn install", 53 | }, 54 | { 55 | name: "Install examples", 56 | run: `ls -d examples/* | xargs -I {} bash -c "cd '{}' && yarn"`, 57 | }, 58 | { 59 | name: "Get current CDKTF version", 60 | id: "current_version", 61 | run: [ 62 | `OLD_VERSION=$(cd examples/hybrid-module && npm list cdktf --depth=0 --json | jq -r '.dependencies.cdktf.version')`, 63 | `OLD_VERSION_SHORT=$(cut -d "." -f 2 <<< "$OLD_VERSION")`, 64 | `echo "value=$OLD_VERSION" >> $GITHUB_OUTPUT`, 65 | `echo "short=$OLD_VERSION_SHORT" >> $GITHUB_OUTPUT`, 66 | ].join("\n"), 67 | // NOTE: No, there is no good way to do this in Yarn, until (if) we upgrade to Yarn 2+ (see below) 68 | }, 69 | { 70 | name: "Get latest CDKTF version", 71 | id: "latest_version", 72 | run: [ 73 | `CDKTF_VERSION=$(yarn info cdktf --json | jq -r '.data.version')`, 74 | `CDKTF_VERSION_SHORT=$(cut -d "." -f 2 <<< "$CDKTF_VERSION")`, 75 | `CONSTRUCTS_VERSION=$(yarn info cdktf --json | jq -r '.data.peerDependencies.constructs')`, 76 | `CONSTRUCTS_VERSION_EXACT=$(cut -d "^" -f 2 <<< "$CONSTRUCTS_VERSION")`, // strip the caret off the beginning 77 | `echo "value=$CDKTF_VERSION" >> $GITHUB_OUTPUT`, 78 | `echo "short=$CDKTF_VERSION_SHORT" >> $GITHUB_OUTPUT`, 79 | `echo "constructs=$CONSTRUCTS_VERSION_EXACT" >> $GITHUB_OUTPUT`, 80 | ].join("\n"), 81 | // IMPORTANT: the above behavior changed in Yarn 2+; `yarn info` instead gives the version of the installed package 82 | // If/when we upgrade we'll likely want to switch to `yarn npm info`: https://yarnpkg.com/cli/npm/info 83 | }, 84 | { 85 | name: "Run upgrade script", 86 | if: "steps.current_version.outputs.short != steps.latest_version.outputs.short", 87 | run: "scripts/update-cdktf.sh ${{ steps.latest_version.outputs.value }} ${{ steps.latest_version.outputs.constructs }}", 88 | }, 89 | { 90 | name: "Check if there are any changes", 91 | id: "get_changes", 92 | run: `echo "changed=$(git status --porcelain | wc -l)" >> $GITHUB_OUTPUT`, 93 | }, 94 | { 95 | name: "Create draft pull request", 96 | if: "steps.get_changes.outputs.changed != 0", 97 | uses: "peter-evans/create-pull-request@v3", 98 | with: { 99 | "commit-message": 100 | "chore!: upgrade to cdktf ${{ steps.latest_version.outputs.value }}", 101 | branch: 102 | "auto/upgrade-cdktf-${{ steps.latest_version.outputs.short }}", 103 | base: "main", 104 | title: 105 | "chore!: upgrade to cdktf ${{ steps.latest_version.outputs.value }}", 106 | body: [ 107 | "This PR initiates the upgrade of CDKTF from version `${{ steps.current_version.outputs.value }}` to version `${{ steps.latest_version.outputs.value }}`.", 108 | "Unfortunately, not everything can be automated, and the following steps need to be completed manually:", 109 | " ", 110 | "- [ ] Update `@cdktf/tf-module-stack` to a version compatible with `cdktf@${{ steps.latest_version.outputs.value }}` [here](https://github.com/cdktf/projen-cdktf-hybrid-construct/blob/62c33dca3eaa249b519edd82fa0889ffcd12c6eb/src/hybrid-module.ts#L194). Look up the version [here](https://www.npmjs.com/package/@cdktf/tf-module-stack?activeTab=versions).", 111 | "- [ ] Run `npx projen build`", 112 | " ", 113 | "Please checkout this PR, complete the above steps, push the changes to this branch, and then mark this PR as ready for review to complete the upgrade. Thanks!", 114 | ].join("\n"), 115 | labels: "automerge,auto-approve,dependencies", 116 | token: "${{ secrets.PROJEN_GITHUB_TOKEN }}", 117 | author: "team-tf-cdk ", 118 | committer: "team-tf-cdk ", 119 | signoff: true, 120 | "delete-branch": true, 121 | draft: true, 122 | }, 123 | }, 124 | ], 125 | env: { 126 | CI: "true", 127 | CHECKPOINT_DISABLE: "1", 128 | }, 129 | permissions: { 130 | contents: JobPermission.READ, 131 | }, 132 | }, 133 | }); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /.projenrc.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) HashiCorp, Inc. 3 | * SPDX-License-Identifier: MPL-2.0 4 | */ 5 | 6 | import { JsiiProject } from "projen/lib/cdk"; 7 | import { JobStep } from "projen/lib/github/workflows-model"; 8 | import { NpmAccess, UpgradeDependenciesSchedule } from "projen/lib/javascript"; 9 | import { AutoApprove } from "./projenrc/auto-approve"; 10 | import { Automerge } from "./projenrc/automerge"; 11 | import { CustomizedLicense } from "./projenrc/customized-license"; 12 | import { UpgradeCDKTF } from "./projenrc/upgrade-cdktf"; 13 | import { UpgradeJSIIAndTypeScript } from "./projenrc/upgrade-jsii-typescript"; 14 | import { UpgradeProjen } from "./projenrc/upgrade-projen"; 15 | 16 | const name = "projen-cdktf-hybrid-construct"; 17 | const constructsVersion = "10.4.2"; 18 | /** JSII and TS should always use the same major/minor version range */ 19 | const typescriptVersion = "~5.8.0"; 20 | const projenVersion = "0.88.0"; 21 | 22 | const githubActionPinnedVersions = { 23 | "actions/checkout": "11bd71901bbe5b1630ceea73d27597364c9af683", // v4.2.2 24 | "actions/download-artifact": "95815c38cf2ff2164869cbab79da8d1f422bc89e", // v4.2.1 25 | "actions/github-script": "60a0d83039c74a4aee543508d2ffcb1c3799cdea", // v7.0.1 26 | "actions/setup-node": "cdca7365b2dadb8aad0a33bc7601856ffabcc48e", // v4.3.0 27 | "actions/upload-artifact": "ea165f8d65b6e75b540449e92b4886f43607fa02", // v4.6.2 28 | "amannn/action-semantic-pull-request": 29 | "0723387faaf9b38adef4775cd42cfd5155ed6017", // v5.5.3 30 | "hashicorp/setup-copywrite": "32638da2d4e81d56a0764aa1547882fc4d209636", // v1.1.3 31 | "hashicorp/setup-terraform": "b9cd54a3c349d3f38e8881555d616ced269862dd", // v3.1.2 32 | "peter-evans/create-pull-request": "271a8d0340265f705b14b6d32b9829c1cb33d45e", // v7.0.8 33 | }; 34 | 35 | const project = new JsiiProject({ 36 | defaultReleaseBranch: "main", 37 | name, 38 | repositoryUrl: `https://github.com/cdktf/${name}.git`, 39 | author: "HashiCorp", 40 | authorAddress: "https://hashicorp.com", 41 | authorOrganization: true, 42 | packageName: name, 43 | prettier: true, 44 | projenrcTs: true, 45 | description: 46 | "Projen template for CDKTF Constructs that should also be used as Terraform Modules.", 47 | licensed: false, 48 | release: true, 49 | releaseToNpm: true, 50 | npmAccess: NpmAccess.PUBLIC, 51 | mergify: false, 52 | depsUpgradeOptions: { 53 | workflowOptions: { 54 | labels: ["auto-approve", "automerge", "dependencies"], 55 | schedule: UpgradeDependenciesSchedule.WEEKLY, 56 | }, 57 | }, 58 | gitignore: [".idea/"], 59 | workflowGitIdentity: { 60 | name: "team-tf-cdk", 61 | email: "github-team-tf-cdk@hashicorp.com", 62 | }, 63 | typescriptVersion, 64 | jsiiVersion: typescriptVersion, 65 | pullRequestTemplate: false, 66 | // Necessary due to this bug: https://github.com/projen/projen/issues/3950#issuecomment-2483442005 67 | // If this bug gets fixed, the below `eslintOptions` section should be able to be removed 68 | eslintOptions: { 69 | fileExtensions: [], 70 | dirs: ["src", "test", "projenrc", ".projenrc.ts"], 71 | }, 72 | }); 73 | project.tsconfig?.exclude?.push("src/exampleCode/**"); 74 | project.tsconfig?.exclude?.push("example/**"); 75 | project.tsconfig?.exclude?.push("examples/**"); 76 | 77 | project.addPeerDeps( 78 | `projen@>= ${projenVersion}`, 79 | `constructs@>= ${constructsVersion}` 80 | ); 81 | project.addBundledDeps("change-case"); 82 | project.addDevDeps( 83 | "fs-extra", 84 | "glob", 85 | `projen@${projenVersion}`, 86 | `constructs@${constructsVersion}`, 87 | "@types/fs-extra", 88 | "@types/glob", 89 | "@types/change-case", 90 | "ts-node@10.9.1", 91 | "comment-json" 92 | ); 93 | // This gets rid of the following error during the build » package » package-all » package:js step: 94 | // Error: Conflicting versions of constructs in type system: previously loaded 10.3.0, trying to load 10.4.2 95 | // It is not clear why the above error occurs; none of our other Projen projects have this problem 96 | project.package.addPackageResolutions(`constructs@${constructsVersion}`); 97 | // This gets rid of the following error when running `npx projen compile`: 98 | // error TS2688: Cannot find type definition file for 'minimatch'. 99 | // The file is in the program because: 100 | // Entry point for implicit type library 'minimatch' 101 | project.package.addPackageResolutions(`@types/minimatch@5.1.2`); 102 | 103 | new CustomizedLicense(project); 104 | new AutoApprove(project); 105 | new Automerge(project); 106 | new UpgradeCDKTF(project); 107 | new UpgradeJSIIAndTypeScript(project, typescriptVersion); 108 | new UpgradeProjen(project); 109 | 110 | project.addTask("buildExample", { 111 | exec: "yarn buildExample:hybrid && yarn buildExample:terraform", 112 | }); 113 | project.addTask("buildExample:hybrid", { 114 | exec: "git clean -dfx . && rm -rf lib modules src terraform construct-examples && yarn && yarn projen && yarn && yarn build", 115 | cwd: "./examples/hybrid-module", 116 | }); 117 | project.addTask("buildExample:terraform", { 118 | exec: "git clean -dfx . && rm -rf lib modules src terraform construct-examples && yarn && yarn projen && yarn && yarn build", 119 | cwd: "./examples/terraform-module", 120 | }); 121 | 122 | project.addTask("upgrade:hybrid", { 123 | exec: "yarn projen upgrade", 124 | cwd: "./examples/hybrid-module", 125 | }); 126 | project.addTask("upgrade:terraform", { 127 | exec: "yarn projen upgrade", 128 | cwd: "./examples/terraform-module", 129 | }); 130 | project.removeTask("post-upgrade"); 131 | const upgradeTask = project.addTask("post-upgrade", { 132 | description: "Runs after upgrading dependencies", 133 | exec: "yarn compile", 134 | }); 135 | upgradeTask.exec("yarn projen upgrade:hybrid"); 136 | upgradeTask.exec("yarn projen upgrade:terraform"); 137 | 138 | project.testTask.exec("yarn buildExample"); 139 | 140 | project.addPackageIgnore("scripts"); 141 | project.addPackageIgnore("examples"); 142 | project.addPackageIgnore("projenrc"); 143 | project.addPackageIgnore("/.projenrc.ts"); 144 | 145 | project.addPackageIgnore(".copywrite.hcl"); 146 | // Run copywrite tool to add copyright headers to all files 147 | project.buildWorkflow?.addPostBuildSteps( 148 | { 149 | name: "Setup Copywrite tool", 150 | uses: "hashicorp/setup-copywrite", 151 | }, 152 | { name: "Add headers using Copywrite tool", run: "copywrite headers" } 153 | ); 154 | 155 | const setupTerraformStep = { 156 | name: "Setup Terraform", 157 | uses: "hashicorp/setup-terraform", 158 | with: { 159 | terraform_wrapper: false, 160 | }, 161 | }; 162 | const buildSteps = (project.buildWorkflow as any).preBuildSteps as JobStep[]; 163 | const releaseSteps = (project.release as any).defaultBranch.workflow.jobs 164 | .release.steps; 165 | buildSteps.push(setupTerraformStep); 166 | releaseSteps.splice(1, 0, setupTerraformStep); 167 | 168 | // Use pinned versions of github actions 169 | Object.entries(githubActionPinnedVersions).forEach(([action, sha]) => { 170 | project.github?.actions.set(action, `${action}@${sha}`); 171 | }); 172 | 173 | const releaseWorkflow = project.tryFindObjectFile( 174 | ".github/workflows/release.yml" 175 | ); 176 | releaseWorkflow?.addOverride("on.push", { 177 | branches: ["main"], 178 | "paths-ignore": [ 179 | // don't do a release if the change was only to these files/directories 180 | "examples/**", 181 | ".github/ISSUE_TEMPLATE/**", 182 | ".github/CODEOWNERS", 183 | ".github/dependabot.yml", 184 | ".github/**/*.md", 185 | ], 186 | }); 187 | 188 | project.synth(); 189 | -------------------------------------------------------------------------------- /projenrc/upgrade-jsii-typescript.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) HashiCorp, Inc. 3 | * SPDX-License-Identifier: MPL-2.0 4 | */ 5 | 6 | import { javascript } from "projen"; 7 | import { JobPermission } from "projen/lib/github/workflows-model"; 8 | 9 | /** 10 | * Helper script for upgrading JSII and TypeScript in the right way. 11 | * Auto-updates to the next version a month before the previous JSII version goes EOS 12 | * Can also be triggered manually with a hard-coded version of JSII/TypeScript as input 13 | * https://github.com/aws/jsii-compiler/blob/main/README.md#gear-maintenance--support 14 | */ 15 | export class UpgradeJSIIAndTypeScript { 16 | constructor(project: javascript.NodeProject, typescriptVersion: string) { 17 | const workflow = project.github?.addWorkflow("upgrade-jsii-typescript"); 18 | if (!workflow) throw new Error("no workflow defined"); 19 | 20 | const plainVersion = typescriptVersion.replace("~", ""); 21 | workflow.on({ 22 | schedule: [{ cron: "21 12 * * *" }], // Runs once a day 23 | workflowDispatch: { 24 | inputs: { 25 | version: { 26 | description: `New JSII/TypeScript version (e.g. "${plainVersion}"), without carets or tildes`, 27 | required: false, 28 | type: "string", 29 | }, 30 | }, 31 | }, 32 | }); 33 | 34 | (workflow.concurrency as any) = { 35 | group: "${{ github.workflow }}-${{ github.ref }}", 36 | }; 37 | 38 | workflow.addJobs({ 39 | version: { 40 | name: "Determine version to upgrade to", 41 | runsOn: ["ubuntu-latest"], 42 | steps: [ 43 | { 44 | name: "Checkout", 45 | uses: "actions/checkout", 46 | }, 47 | { 48 | name: "Setup Node.js", 49 | uses: "actions/setup-node", 50 | with: { 51 | "node-version": project.minNodeVersion, 52 | }, 53 | }, 54 | { 55 | name: "Install", 56 | run: "yarn install", 57 | }, 58 | { 59 | name: "Get current JSII version", 60 | id: "current_version", 61 | run: [ 62 | `CURRENT_VERSION=$(npm list jsii --depth=0 --json | jq -r '.dependencies.jsii.version')`, 63 | `CURRENT_VERSION_SHORT=$(cut -d "." -f 1,2 <<< "$CURRENT_VERSION")`, 64 | `CURRENT_VERSION_MAJOR=$(cut -d "." -f 1 <<< "$CURRENT_VERSION")`, 65 | `CURRENT_VERSION_MINOR=$(cut -d "." -f 2 <<< "$CURRENT_VERSION")`, 66 | `echo "CURRENT_JSII_VERSION=$CURRENT_VERSION" >> $GITHUB_ENV`, 67 | `echo "CURRENT_JSII_VERSION_SHORT=$CURRENT_VERSION_SHORT" >> $GITHUB_ENV`, 68 | `echo "CURRENT_JSII_VERSION_MAJOR=$CURRENT_VERSION_MAJOR" >> $GITHUB_ENV`, 69 | `echo "CURRENT_JSII_VERSION_MINOR=$CURRENT_VERSION_MINOR" >> $GITHUB_ENV`, 70 | `echo "value=$CURRENT_VERSION" >> $GITHUB_OUTPUT`, 71 | ].join("\n"), 72 | }, 73 | { 74 | name: "Get the earliest supported JSII version whose EOS date is at least a month away", 75 | if: "${{ ! inputs.version }}", 76 | uses: "actions/github-script", 77 | with: { 78 | script: [ 79 | `const script = require('./scripts/check-jsii-versions.js')`, 80 | `await script({github, context, core})`, 81 | ].join("\n"), 82 | }, 83 | }, 84 | { 85 | // In an ideal world this is where we'd validate that the manually-input version actually exists 86 | // In practice, I couldn't figure out how to do this properly and it wasn't worth the effort 87 | // name: "Check if the manually-input version actually exists (has been published to NPM)", 88 | name: "Save the manually-input version to environment variables for comparison", 89 | if: "${{ inputs.version }}", 90 | env: { 91 | NEW_VERSION: "${{ inputs.version }}", 92 | }, 93 | run: [ 94 | // My command line skillz aren't good enough to figure out how to make the below work (error if the version doesn't exist) 95 | // `yarn info jsii versions --json | jq -e 'select(.data | index("$NEW_VERSION"))`, 96 | `NEW_VERSION_SHORT=$(cut -d "." -f 1,2 <<< "$NEW_VERSION")`, 97 | `NEW_VERSION_MAJOR=$(cut -d "." -f 1 <<< "$NEW_VERSION")`, 98 | `NEW_VERSION_MINOR=$(cut -d "." -f 2 <<< "$NEW_VERSION")`, 99 | `echo "NEW_JSII_VERSION=$NEW_VERSION" >> $GITHUB_ENV`, 100 | `echo "NEW_JSII_VERSION_SHORT=$NEW_VERSION_SHORT" >> $GITHUB_ENV`, 101 | `echo "NEW_JSII_VERSION_MAJOR=$NEW_VERSION_MAJOR" >> $GITHUB_ENV`, 102 | `echo "NEW_JSII_VERSION_MINOR=$NEW_VERSION_MINOR" >> $GITHUB_ENV`, 103 | ].join("\n"), 104 | }, 105 | { 106 | name: "Output env variables for use in the next job", 107 | id: "latest_version", 108 | run: [ 109 | `echo "value=$NEW_JSII_VERSION" >> $GITHUB_OUTPUT`, 110 | `echo "short=$NEW_JSII_VERSION_SHORT" >> $GITHUB_OUTPUT`, 111 | `[[ "$NEW_JSII_VERSION_MAJOR" > "$CURRENT_JSII_VERSION_MAJOR" || ("$NEW_JSII_VERSION_MAJOR" == "$CURRENT_JSII_VERSION_MAJOR" && "$NEW_JSII_VERSION_MINOR" > "$CURRENT_JSII_VERSION_MINOR") ]] && IS_NEWER=true`, 112 | `echo "is_newer=$IS_NEWER" >> $GITHUB_OUTPUT`, 113 | ].join("\n"), 114 | }, 115 | ], 116 | outputs: { 117 | current: { 118 | stepId: "current_version", 119 | outputName: "value", 120 | }, 121 | latest: { 122 | stepId: "latest_version", 123 | outputName: "value", 124 | }, 125 | short: { 126 | stepId: "latest_version", 127 | outputName: "short", 128 | }, 129 | should_upgrade: { 130 | stepId: "latest_version", 131 | outputName: "is_newer", 132 | }, 133 | }, 134 | permissions: { 135 | contents: JobPermission.READ, 136 | }, 137 | }, 138 | upgrade: { 139 | name: "Upgrade JSII & TypeScript", 140 | runsOn: ["ubuntu-latest"], 141 | needs: ["version"], 142 | if: "always() && needs.version.outputs.should_upgrade", 143 | steps: [ 144 | { 145 | name: "Checkout", 146 | uses: "actions/checkout", 147 | }, 148 | { 149 | name: "Setup Terraform", 150 | uses: "hashicorp/setup-terraform", 151 | with: { 152 | terraform_wrapper: false, 153 | }, 154 | }, 155 | { 156 | name: "Setup Node.js", 157 | uses: "actions/setup-node", 158 | with: { 159 | "node-version": project.minNodeVersion, 160 | }, 161 | }, 162 | { 163 | name: "Install", 164 | run: "yarn install", 165 | }, 166 | { 167 | name: "Run upgrade script", 168 | run: "scripts/update-jsii-typescript.sh ${{ needs.version.outputs.latest }}", 169 | }, 170 | { 171 | name: "Create Pull Request", 172 | uses: "peter-evans/create-pull-request", 173 | with: { 174 | base: "main", 175 | branch: "auto/upgrade-jsii-ts-${{ needs.version.outputs.short }}", 176 | "commit-message": 177 | "chore(deps): upgrade jsii & typescript to v${{ needs.version.outputs.short }}", 178 | title: 179 | "chore(deps): upgrade jsii & typescript to v${{ needs.version.outputs.short }}", 180 | body: [ 181 | "This PR increases the version of JSII and TypeScript to `~${{ needs.version.outputs.latest }}` ", 182 | "because the previous version is close to EOL or no longer supported. Support timeline: ", 183 | "https://github.com/aws/jsii-compiler/blob/main/README.md#gear-maintenance--support", 184 | ].join(" "), 185 | labels: "auto-approve,automerge,automated", 186 | token: "${{ secrets.PROJEN_GITHUB_TOKEN }}", 187 | author: "team-tf-cdk ", 188 | committer: "team-tf-cdk ", 189 | signoff: true, 190 | "delete-branch": true, 191 | }, 192 | }, 193 | ], 194 | env: { 195 | CI: "true", 196 | CHECKPOINT_DISABLE: "1", 197 | }, 198 | permissions: { 199 | contents: JobPermission.READ, 200 | }, 201 | }, 202 | }); 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /examples/terraform-module/.projen/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "build": { 4 | "name": "build", 5 | "description": "Full release build", 6 | "steps": [ 7 | { 8 | "spawn": "default" 9 | }, 10 | { 11 | "spawn": "pre-compile" 12 | }, 13 | { 14 | "spawn": "compile" 15 | }, 16 | { 17 | "spawn": "post-compile" 18 | }, 19 | { 20 | "spawn": "test" 21 | }, 22 | { 23 | "spawn": "package" 24 | } 25 | ] 26 | }, 27 | "bump": { 28 | "name": "bump", 29 | "description": "Bumps version based on latest git tag and generates a changelog entry", 30 | "env": { 31 | "OUTFILE": "package.json", 32 | "CHANGELOG": "dist/changelog.md", 33 | "BUMPFILE": "dist/version.txt", 34 | "RELEASETAG": "dist/releasetag.txt", 35 | "RELEASE_TAG_PREFIX": "", 36 | "BUMP_PACKAGE": "commit-and-tag-version@^12" 37 | }, 38 | "steps": [ 39 | { 40 | "builtin": "release/bump-version" 41 | } 42 | ], 43 | "condition": "git log --oneline -1 | grep -qv \"chore(release):\"" 44 | }, 45 | "clobber": { 46 | "name": "clobber", 47 | "description": "hard resets to HEAD of origin and cleans the local repo", 48 | "env": { 49 | "BRANCH": "$(git branch --show-current)" 50 | }, 51 | "steps": [ 52 | { 53 | "exec": "git checkout -b scratch", 54 | "name": "save current HEAD in \"scratch\" branch" 55 | }, 56 | { 57 | "exec": "git checkout $BRANCH" 58 | }, 59 | { 60 | "exec": "git fetch origin", 61 | "name": "fetch latest changes from origin" 62 | }, 63 | { 64 | "exec": "git reset --hard origin/$BRANCH", 65 | "name": "hard reset to origin commit" 66 | }, 67 | { 68 | "exec": "git clean -fdx", 69 | "name": "clean all untracked files" 70 | }, 71 | { 72 | "say": "ready to rock! (unpushed commits are under the \"scratch\" branch)" 73 | } 74 | ], 75 | "condition": "git diff --exit-code > /dev/null" 76 | }, 77 | "compat": { 78 | "name": "compat", 79 | "description": "Perform API compatibility check against latest version", 80 | "steps": [ 81 | { 82 | "exec": "jsii-diff npm:$(node -p \"require('./package.json').name\") -k --ignore-file .compatignore || (echo \"\nUNEXPECTED BREAKING CHANGES: add keys such as 'removed:constructs.Node.of' to .compatignore to skip.\n\" && exit 1)" 83 | } 84 | ] 85 | }, 86 | "compile": { 87 | "name": "compile", 88 | "description": "Only compile", 89 | "steps": [ 90 | { 91 | "exec": "jsii --silence-warnings=reserved-word" 92 | } 93 | ] 94 | }, 95 | "default": { 96 | "name": "default", 97 | "description": "Synthesize project files", 98 | "steps": [ 99 | { 100 | "exec": "ts-node --project tsconfig.dev.json .projenrc.ts" 101 | } 102 | ] 103 | }, 104 | "docgen": { 105 | "name": "docgen", 106 | "description": "Generate API.md from .jsii manifest", 107 | "steps": [ 108 | { 109 | "exec": "jsii-docgen -o API.md" 110 | } 111 | ] 112 | }, 113 | "eject": { 114 | "name": "eject", 115 | "description": "Remove projen from the project", 116 | "env": { 117 | "PROJEN_EJECTING": "true" 118 | }, 119 | "steps": [ 120 | { 121 | "spawn": "default" 122 | } 123 | ] 124 | }, 125 | "eslint": { 126 | "name": "eslint", 127 | "description": "Runs eslint against the codebase", 128 | "steps": [ 129 | { 130 | "exec": "eslint --fix --no-error-on-unmatched-pattern $@ src test build-tools projenrc .projenrc.ts", 131 | "receiveArgs": true 132 | } 133 | ] 134 | }, 135 | "install": { 136 | "name": "install", 137 | "description": "Install project dependencies and update lockfile (non-frozen)", 138 | "steps": [ 139 | { 140 | "exec": "yarn install --check-files" 141 | } 142 | ] 143 | }, 144 | "install:ci": { 145 | "name": "install:ci", 146 | "description": "Install project dependencies using frozen lockfile", 147 | "steps": [ 148 | { 149 | "exec": "yarn install --check-files --frozen-lockfile" 150 | } 151 | ] 152 | }, 153 | "package": { 154 | "name": "package", 155 | "description": "Creates the distribution package", 156 | "steps": [ 157 | { 158 | "spawn": "package:js", 159 | "condition": "node -e \"if (!process.env.CI) process.exit(1)\"" 160 | }, 161 | { 162 | "spawn": "package-all", 163 | "condition": "node -e \"if (process.env.CI) process.exit(1)\"" 164 | } 165 | ] 166 | }, 167 | "package-all": { 168 | "name": "package-all", 169 | "description": "Packages artifacts for all target languages", 170 | "steps": [ 171 | { 172 | "spawn": "package:js" 173 | } 174 | ] 175 | }, 176 | "package:js": { 177 | "name": "package:js", 178 | "description": "Create js language bindings", 179 | "steps": [ 180 | { 181 | "exec": "jsii-pacmak -v --target js" 182 | } 183 | ] 184 | }, 185 | "post-compile": { 186 | "name": "post-compile", 187 | "description": "Runs after successful compilation", 188 | "steps": [ 189 | { 190 | "spawn": "docgen" 191 | } 192 | ] 193 | }, 194 | "post-upgrade": { 195 | "name": "post-upgrade", 196 | "description": "Runs after upgrading dependencies" 197 | }, 198 | "pre-compile": { 199 | "name": "pre-compile", 200 | "description": "Prepare the project for compilation", 201 | "steps": [ 202 | { 203 | "exec": "npx cdktf get", 204 | "cwd": "src" 205 | } 206 | ] 207 | }, 208 | "release": { 209 | "name": "release", 210 | "description": "Prepare a release from \"main\" branch", 211 | "env": { 212 | "RELEASE": "true" 213 | }, 214 | "steps": [ 215 | { 216 | "exec": "rm -fr dist" 217 | }, 218 | { 219 | "spawn": "bump" 220 | }, 221 | { 222 | "spawn": "build" 223 | }, 224 | { 225 | "spawn": "unbump" 226 | }, 227 | { 228 | "exec": "git diff --ignore-space-at-eol --exit-code" 229 | } 230 | ] 231 | }, 232 | "test": { 233 | "name": "test", 234 | "description": "Run tests", 235 | "steps": [ 236 | { 237 | "exec": "jest --passWithNoTests --updateSnapshot", 238 | "receiveArgs": true 239 | }, 240 | { 241 | "spawn": "eslint" 242 | } 243 | ] 244 | }, 245 | "test:watch": { 246 | "name": "test:watch", 247 | "description": "Run jest in watch mode", 248 | "steps": [ 249 | { 250 | "exec": "jest --watch" 251 | } 252 | ] 253 | }, 254 | "unbump": { 255 | "name": "unbump", 256 | "description": "Restores version to 0.0.0", 257 | "env": { 258 | "OUTFILE": "package.json", 259 | "CHANGELOG": "dist/changelog.md", 260 | "BUMPFILE": "dist/version.txt", 261 | "RELEASETAG": "dist/releasetag.txt", 262 | "RELEASE_TAG_PREFIX": "", 263 | "BUMP_PACKAGE": "commit-and-tag-version@^12" 264 | }, 265 | "steps": [ 266 | { 267 | "builtin": "release/reset-version" 268 | } 269 | ] 270 | }, 271 | "upgrade": { 272 | "name": "upgrade", 273 | "description": "upgrade dependencies", 274 | "env": { 275 | "CI": "0" 276 | }, 277 | "steps": [ 278 | { 279 | "exec": "npx npm-check-updates@16 --upgrade --target=minor --peer --dep=dev,peer,prod,optional --filter=@types/jest,eslint-config-prettier,eslint-import-resolver-typescript,eslint-plugin-import,eslint-plugin-prettier,jest,jsii-diff,jsii-pacmak,prettier,projen,ts-jest" 280 | }, 281 | { 282 | "exec": "yarn install --check-files" 283 | }, 284 | { 285 | "exec": "yarn upgrade @types/jest @types/node @typescript-eslint/eslint-plugin @typescript-eslint/parser cdktf-cli cdktf commit-and-tag-version constructs eslint-config-prettier eslint-import-resolver-typescript eslint-plugin-import eslint-plugin-prettier eslint jest jest-junit jsii-diff jsii-docgen jsii-pacmak jsii-rosetta jsii prettier projen ts-jest ts-node typescript" 286 | }, 287 | { 288 | "exec": "npx projen" 289 | }, 290 | { 291 | "spawn": "post-upgrade" 292 | } 293 | ] 294 | }, 295 | "watch": { 296 | "name": "watch", 297 | "description": "Watch & compile in the background", 298 | "steps": [ 299 | { 300 | "exec": "jsii -w --silence-warnings=reserved-word" 301 | } 302 | ] 303 | } 304 | }, 305 | "env": { 306 | "PATH": "$(npx -c \"node --print process.env.PATH\")" 307 | }, 308 | "//": "~~ Generated by projen. To modify, edit .projenrc.ts and run \"npx projen\"." 309 | } 310 | -------------------------------------------------------------------------------- /.github/MAINTENANCE.md: -------------------------------------------------------------------------------- 1 | # Maintenance Guide 2 | 3 | This document is intended for maintainers of this repository and outlines how to perform various maintenance-related activities, including descriptions of what processes are currently automated and which are not (yet). 4 | 5 | This repository contains extensive GitHub Actions [workflows](https://github.com/cdktf/projen-cdktf-hybrid-construct/tree/main/.github/workflows) that automate as much of the project's lifecycle as possible. The project is built using [Projen](https://projen.io/) and as such **these workflows should not be edited directly**. Their sources can be found in the [`.projenrc/`](https://github.com/cdktf/projen-cdktf-hybrid-construct/tree/main/projenrc) directory, and new workflows are added to the project in [`.projenrc.ts`](https://github.com/cdktf/projen-cdktf-hybrid-construct/blob/main/.projenrc.ts). 6 | 7 | This project is considered experimental, and does not offer any support or maintenance guarantees. 8 | 9 | 10 | ## Prerequisites & Gotchas 11 | 12 | This projects works a little bit differently than our other construct libraries and there are a couple of quirks worth calling attention to: 13 | 14 | - This library generates two custom Projen templates, the source of which lives in [`src/hybrid-module.ts`](https://github.com/cdktf/projen-cdktf-hybrid-construct/blob/main/src/hybrid-module.ts) and [`src/terraform-module.ts`](https://github.com/cdktf/projen-cdktf-hybrid-construct/blob/main/src/terraform-module.ts). Depending on what type of maintenance you're doing, you may also need to make changes to these Projen configurations _in addition to_ making changes to [`.projenrc.ts`](https://github.com/cdktf/projen-cdktf-hybrid-construct/blob/main/.projenrc.ts) like you're probably used to by now. 15 | - Because of the above, you typically need to run `npx projen build` in addition to `npx projen` when making changes. 16 | - All builds require an active Docker container to be running. If you're doing local development, you'll want to have Docker Desktop open in the background (if that's not something you normally have set up), otherwise the builds will fail. 17 | 18 | 19 | ## Security & Dependency Management 20 | 21 | Dependency upgrades (for security purposes as well as a best practice) can be divided into three categories: fully-automated, semi-automated, and not automated. 22 | 23 | ### Fully Automated 24 | 25 | The following Actions exist to automate various dependency upgrades: 26 | 27 | - [upgrade-jsii-typescript](https://github.com/cdktf/projen-cdktf-hybrid-construct/actions/workflows/upgrade-jsii-typescript.yml): This is a custom workflow (source [here](https://github.com/cdktf/projen-cdktf-hybrid-construct/blob/main/projenrc/upgrade-jsii-typescript.ts)) that checks the [JSII support timeline](https://github.com/aws/jsii-compiler/blob/main/README.md#gear-maintenance--support) daily via [this](https://github.com/cdktf/projen-cdktf-hybrid-construct/blob/main/scripts/check-jsii-versions.js) script to see if the current version is less than 30 days away from EOS, and if so, creates a PR upgrading to the next supported version. The code for the upgrade itself lives in [this](https://github.com/cdktf/projen-cdktf-hybrid-construct/blob/main/scripts/update-jsii-typescript.sh) script. This process is 100% automated; as long as the build succeeds and any tests pass, the PR will be automatically merged without any human intervention. 28 | - This workflow can also be manually triggered, optionally taking a hard-coded JSII/TypeScript version as input in case we ever want to upgrade to a newer version without waiting until the old one is less than 30 days away from EOS. 29 | - [upgrade-main](https://github.com/cdktf/projen-cdktf-hybrid-construct/actions/workflows/upgrade-main.yml): This is a Projen built-in/default workflow that handles automated dependency updates. It currently runs on a weekly basis, which can be configured [here](https://github.com/cdktf/projen-cdktf-hybrid-construct/blob/4ea42f59370a23fbdc5d3acd6f3a08a7f6dfd254/.projenrc.ts#L54). This process is 100% automated; as long as the build succeeds and any tests pass, the PR that is generated will be automatically merged without any human intervention. 30 | - [upgrade-projen](https://github.com/cdktf/projen-cdktf-hybrid-construct/actions/workflows/upgrade-projen.yml): This is a custom workflow (source [here](https://github.com/cdktf/projen-cdktf-hybrid-construct/blob/main/projenrc/upgrade-projen.ts)) that updates Projen itself. Because Projen is a peer dependency for this project, the `upgrade-main` workflow above will _never_ increment its version. This custom workflow currently runs every other month because we needed to find the delicate balance between not falling too far behind on Projen updates (because otherwise upgrading Projen can become a painful process), while respecting that doing so is technically a breaking change for any consumers of this library (because they may be forced to upgrade their version of Projen used), so we don't want to do it too often. The code for the upgrade itself lives in [this](https://github.com/cdktf/projen-cdktf-hybrid-construct/blob/main/scripts/update-projen.sh) script. 31 | 32 | Dependabot is also [configured](https://github.com/cdktf/projen-cdktf-hybrid-construct/blob/main/.github/dependabot.yml) to check for new security updates daily and, if found, make changes to the lockfile only. This is because Dependabot can sometimes address security issues in dependencies more quickly than Projen due to its atomic nature. While you could tweak the Dependabot settings, note that Projen and Dependabot do not generally play nicely together; in particular, Dependabot cannot make changes to `package.json` because Projen would just override these changes (hence the reason why Dependabot is currently limited to lockfile-only). If you wanted to fully automate dependency management using Dependabot, you would want to disable Projen's [automatic updates](https://projen.io/docs/api/typescript#projen.typescript.TypeScriptProjectOptions.property.depsUpgrade). 33 | 34 | ### Semi-Automated 35 | 36 | The following Actions either need to be manually triggered or require significant manual effort as part of the upgrade process: 37 | 38 | - [upgrade-cdktf](https://github.com/cdktf/projen-cdktf-hybrid-construct/actions/workflows/upgrade-cdktf.yml): This is a custom workflow (source [here](https://github.com/cdktf/projen-cdktf-hybrid-construct/blob/main/projenrc/upgrade-cdktf.ts)) that runs four times a day and checks whether there is a new minor version of CDKTF itself (e.g. `0.19`, `0.20`, `0.21`, etc.), using the latest version published to npm as the source of truth. If a new version is found, it runs [this](https://github.com/cdktf/projen-cdktf-hybrid-construct/blob/main/scripts/update-cdktf.sh) script to update the CDKTF version in all the right places, and then it creates a draft PR. The reason for the draft status is because a few steps related to the upgrade cannot be automated and must be done manually by an engineer; these are outlined step-by-step in the PR body. Once the steps are completed, the PR can be marked as ready for review & approved in order to complete the upgrade. 39 | 40 | ### Not Automated 41 | 42 | - **GitHub Actions version pinning**: Because this project leverages Projen, HashiCorp Security's [tsccr-helper](https://github.com/hashicorp/security-tsccr?tab=readme-ov-file#tsccr-helper-cli) CLI and other tooling cannot be used to manage/upgrade GitHub Actions versions. Instead, we have consolidated all of the versions into a single [object](https://github.com/cdktf/projen-cdktf-hybrid-construct/blob/4ea42f59370a23fbdc5d3acd6f3a08a7f6dfd254/.projenrc.ts#L21-L32) in code that must be manually updated. Historically, one of the maintainers has followed these manual steps on a roughly monthly basis: 43 | 1. Look up the latest supported versions [here](https://github.com/hashicorp/security-tsccr/tree/main/components/github_actions) 44 | 2. Update the [object](https://github.com/cdktf/projen-cdktf-hybrid-construct/blob/4ea42f59370a23fbdc5d3acd6f3a08a7f6dfd254/.projenrc.ts#L21-L32) 45 | 3. Run `npx projen` 46 | 4. Create a new PR with the title `chore(deps): update pinned versions of GitHub Actions` 47 | 48 | Also worth noting: Unlike many of our other Projen-based projects, this one does not have a script that automatically upgrades Node.js because this library does not enforce a `minNodeVersion`. If we did at some point want to start enforcing a `minNodeVersion`, we should copy over the `upgrade-node` script that our other Projen projects use. 49 | 50 | 51 | ## Releasing 52 | 53 | Releases are fully automated by Projen and require no manual intervention whatsoever. In general, this repository is configured to publish a new release for each pull request that gets merged. The only way to force it to create one release that combines multiple merged PRs is to ensure that all of these PRs get merged into `main` at exactly the same time. The new version number is automatically calculated by Projen using [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) and [Semantic Versioning](https://semver.org/). 54 | 55 | If you wanted to change the logic that governs when releases are triggered (such as running them on a schedule, or only for certain types of commits), see Projen's [documentation](https://projen.io/docs/publishing/releases-and-versioning) on this subject. 56 | 57 | ### Package Managers 58 | 59 | This library is currently published to these package managers: 60 | 61 | - **npm**: The package is called [projen-cdktf-hybrid-construct](https://www.npmjs.com/package/projen-cdktf-hybrid-construct), and publishing is done using an access token associated with the shared [cdktf-team](https://www.npmjs.com/~cdktf-team) account. Credentials to access this account can be found in the CDK for Terraform Team 1Password Vault. 62 | -------------------------------------------------------------------------------- /examples/hybrid-module/.projen/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "build": { 4 | "name": "build", 5 | "description": "Full release build", 6 | "steps": [ 7 | { 8 | "spawn": "default" 9 | }, 10 | { 11 | "spawn": "pre-compile" 12 | }, 13 | { 14 | "spawn": "compile" 15 | }, 16 | { 17 | "spawn": "post-compile" 18 | }, 19 | { 20 | "spawn": "test" 21 | }, 22 | { 23 | "spawn": "package" 24 | } 25 | ] 26 | }, 27 | "bump": { 28 | "name": "bump", 29 | "description": "Bumps version based on latest git tag and generates a changelog entry", 30 | "env": { 31 | "OUTFILE": "package.json", 32 | "CHANGELOG": "dist/changelog.md", 33 | "BUMPFILE": "dist/version.txt", 34 | "RELEASETAG": "dist/releasetag.txt", 35 | "RELEASE_TAG_PREFIX": "", 36 | "BUMP_PACKAGE": "commit-and-tag-version@^12" 37 | }, 38 | "steps": [ 39 | { 40 | "builtin": "release/bump-version" 41 | } 42 | ], 43 | "condition": "git log --oneline -1 | grep -qv \"chore(release):\"" 44 | }, 45 | "clobber": { 46 | "name": "clobber", 47 | "description": "hard resets to HEAD of origin and cleans the local repo", 48 | "env": { 49 | "BRANCH": "$(git branch --show-current)" 50 | }, 51 | "steps": [ 52 | { 53 | "exec": "git checkout -b scratch", 54 | "name": "save current HEAD in \"scratch\" branch" 55 | }, 56 | { 57 | "exec": "git checkout $BRANCH" 58 | }, 59 | { 60 | "exec": "git fetch origin", 61 | "name": "fetch latest changes from origin" 62 | }, 63 | { 64 | "exec": "git reset --hard origin/$BRANCH", 65 | "name": "hard reset to origin commit" 66 | }, 67 | { 68 | "exec": "git clean -fdx", 69 | "name": "clean all untracked files" 70 | }, 71 | { 72 | "say": "ready to rock! (unpushed commits are under the \"scratch\" branch)" 73 | } 74 | ], 75 | "condition": "git diff --exit-code > /dev/null" 76 | }, 77 | "compat": { 78 | "name": "compat", 79 | "description": "Perform API compatibility check against latest version", 80 | "steps": [ 81 | { 82 | "exec": "jsii-diff npm:$(node -p \"require('./package.json').name\") -k --ignore-file .compatignore || (echo \"\nUNEXPECTED BREAKING CHANGES: add keys such as 'removed:constructs.Node.of' to .compatignore to skip.\n\" && exit 1)" 83 | } 84 | ] 85 | }, 86 | "compile": { 87 | "name": "compile", 88 | "description": "Only compile", 89 | "steps": [ 90 | { 91 | "exec": "npx cdktf get", 92 | "cwd": "src" 93 | }, 94 | { 95 | "exec": "jsii --silence-warnings=reserved-word" 96 | }, 97 | { 98 | "exec": "cdktf synth", 99 | "cwd": "src", 100 | "name": "Synthesize module HCL" 101 | }, 102 | { 103 | "exec": "./scripts/copy-modules.sh", 104 | "name": "Copy HCL Modules" 105 | } 106 | ] 107 | }, 108 | "default": { 109 | "name": "default", 110 | "description": "Synthesize project files", 111 | "steps": [ 112 | { 113 | "exec": "ts-node --project tsconfig.dev.json .projenrc.ts" 114 | } 115 | ] 116 | }, 117 | "docgen": { 118 | "name": "docgen", 119 | "description": "Generate API.md from .jsii manifest", 120 | "steps": [ 121 | { 122 | "exec": "jsii-docgen -o API.md" 123 | } 124 | ] 125 | }, 126 | "eject": { 127 | "name": "eject", 128 | "description": "Remove projen from the project", 129 | "env": { 130 | "PROJEN_EJECTING": "true" 131 | }, 132 | "steps": [ 133 | { 134 | "spawn": "default" 135 | } 136 | ] 137 | }, 138 | "eslint": { 139 | "name": "eslint", 140 | "description": "Runs eslint against the codebase", 141 | "steps": [ 142 | { 143 | "exec": "eslint --fix --no-error-on-unmatched-pattern $@ src test build-tools projenrc .projenrc.ts", 144 | "receiveArgs": true 145 | } 146 | ] 147 | }, 148 | "install": { 149 | "name": "install", 150 | "description": "Install project dependencies and update lockfile (non-frozen)", 151 | "steps": [ 152 | { 153 | "exec": "yarn install --check-files" 154 | } 155 | ] 156 | }, 157 | "install:ci": { 158 | "name": "install:ci", 159 | "description": "Install project dependencies using frozen lockfile", 160 | "steps": [ 161 | { 162 | "exec": "yarn install --check-files --frozen-lockfile" 163 | } 164 | ] 165 | }, 166 | "package": { 167 | "name": "package", 168 | "description": "Creates the distribution package", 169 | "steps": [ 170 | { 171 | "spawn": "package:js", 172 | "condition": "node -e \"if (!process.env.CI) process.exit(1)\"" 173 | }, 174 | { 175 | "spawn": "package-all", 176 | "condition": "node -e \"if (process.env.CI) process.exit(1)\"" 177 | } 178 | ] 179 | }, 180 | "package-all": { 181 | "name": "package-all", 182 | "description": "Packages artifacts for all target languages", 183 | "steps": [ 184 | { 185 | "spawn": "package:js" 186 | } 187 | ] 188 | }, 189 | "package:js": { 190 | "name": "package:js", 191 | "description": "Create js language bindings", 192 | "steps": [ 193 | { 194 | "exec": "jsii-pacmak -v --target js" 195 | } 196 | ] 197 | }, 198 | "post-compile": { 199 | "name": "post-compile", 200 | "description": "Runs after successful compilation", 201 | "steps": [ 202 | { 203 | "spawn": "docgen" 204 | } 205 | ] 206 | }, 207 | "post-upgrade": { 208 | "name": "post-upgrade", 209 | "description": "Runs after upgrading dependencies" 210 | }, 211 | "pre-compile": { 212 | "name": "pre-compile", 213 | "description": "Prepare the project for compilation" 214 | }, 215 | "release": { 216 | "name": "release", 217 | "description": "Prepare a release from \"main\" branch", 218 | "env": { 219 | "RELEASE": "true" 220 | }, 221 | "steps": [ 222 | { 223 | "exec": "rm -fr dist" 224 | }, 225 | { 226 | "spawn": "bump" 227 | }, 228 | { 229 | "spawn": "build" 230 | }, 231 | { 232 | "spawn": "unbump" 233 | }, 234 | { 235 | "exec": "git diff --ignore-space-at-eol --exit-code" 236 | } 237 | ] 238 | }, 239 | "test": { 240 | "name": "test", 241 | "description": "Run tests", 242 | "steps": [ 243 | { 244 | "exec": "jest --passWithNoTests --updateSnapshot", 245 | "receiveArgs": true 246 | }, 247 | { 248 | "spawn": "eslint" 249 | }, 250 | { 251 | "exec": "./scripts/tf-module-test.sh" 252 | }, 253 | { 254 | "exec": "cdktf synth", 255 | "cwd": "construct-examples" 256 | } 257 | ] 258 | }, 259 | "test:watch": { 260 | "name": "test:watch", 261 | "description": "Run jest in watch mode", 262 | "steps": [ 263 | { 264 | "exec": "jest --watch" 265 | } 266 | ] 267 | }, 268 | "unbump": { 269 | "name": "unbump", 270 | "description": "Restores version to 0.0.0", 271 | "env": { 272 | "OUTFILE": "package.json", 273 | "CHANGELOG": "dist/changelog.md", 274 | "BUMPFILE": "dist/version.txt", 275 | "RELEASETAG": "dist/releasetag.txt", 276 | "RELEASE_TAG_PREFIX": "", 277 | "BUMP_PACKAGE": "commit-and-tag-version@^12" 278 | }, 279 | "steps": [ 280 | { 281 | "builtin": "release/reset-version" 282 | } 283 | ] 284 | }, 285 | "upgrade": { 286 | "name": "upgrade", 287 | "description": "upgrade dependencies", 288 | "env": { 289 | "CI": "0" 290 | }, 291 | "steps": [ 292 | { 293 | "exec": "npx npm-check-updates@16 --upgrade --target=minor --peer --dep=dev,peer,prod,optional --filter=@types/jest,eslint-config-prettier,eslint-import-resolver-typescript,eslint-plugin-import,eslint-plugin-prettier,jest,jsii-diff,jsii-pacmak,prettier,projen,ts-jest" 294 | }, 295 | { 296 | "exec": "yarn install --check-files" 297 | }, 298 | { 299 | "exec": "yarn upgrade @types/jest @types/node @typescript-eslint/eslint-plugin @typescript-eslint/parser cdktf-cli cdktf commit-and-tag-version constructs eslint-config-prettier eslint-import-resolver-typescript eslint-plugin-import eslint-plugin-prettier eslint jest jest-junit jsii-diff jsii-docgen jsii-pacmak jsii-rosetta jsii prettier projen ts-jest ts-node typescript @cdktf/tf-module-stack" 300 | }, 301 | { 302 | "exec": "npx projen" 303 | }, 304 | { 305 | "spawn": "post-upgrade" 306 | } 307 | ] 308 | }, 309 | "watch": { 310 | "name": "watch", 311 | "description": "Watch & compile in the background", 312 | "steps": [ 313 | { 314 | "exec": "jsii -w --silence-warnings=reserved-word" 315 | } 316 | ] 317 | } 318 | }, 319 | "env": { 320 | "PATH": "$(npx -c \"node --print process.env.PATH\")" 321 | }, 322 | "//": "~~ Generated by projen. To modify, edit .projenrc.ts and run \"npx projen\"." 323 | } 324 | --------------------------------------------------------------------------------