├── .eslintrc.json ├── .gitattributes ├── .github ├── pull_request_template.md └── workflows │ ├── auto-approve.yml │ ├── auto-queue.yml │ ├── build.yml │ ├── pull-request-lint.yml │ ├── release.yml │ ├── upgrade-cdklabs-projen-project-types-main.yml │ ├── upgrade-dev-deps-main.yml │ └── upgrade-main.yml ├── .gitignore ├── .idea └── jsLinters │ └── eslint.xml ├── .npmignore ├── .prettierignore ├── .prettierrc.json ├── .projen ├── deps.json ├── files.json └── tasks.json ├── .projenrc.ts ├── API.md ├── Dockerfile ├── LICENSE ├── README.md ├── package.json ├── rosetta └── default.ts-fixture ├── src ├── bundling.ts ├── function.ts ├── index.ts ├── package-manager.ts ├── types.ts └── util.ts ├── test ├── bundling.test.ts ├── docker.test.ts ├── function.test.binary1 │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── function.test.ts ├── integ.function-bins.ts ├── integ.function-bins.ts.snapshot │ ├── cdk.out │ ├── integ-lambda-rust-function-bins.assets.json │ ├── integ-lambda-rust-function-bins.template.json │ ├── integ.json │ ├── lambdarustfunctionDefaultTestDeployAssert1211A947.assets.json │ ├── lambdarustfunctionDefaultTestDeployAssert1211A947.template.json │ ├── manifest.json │ └── tree.json ├── integ.function-workspace.ts ├── integ.function-workspace.ts.snapshot │ ├── cdk.out │ ├── integ-lambda-rust-function-workspace.assets.json │ ├── integ-lambda-rust-function-workspace.template.json │ ├── integ.json │ ├── lambdarustfunctionDefaultTestDeployAssert1211A947.assets.json │ ├── lambdarustfunctionDefaultTestDeployAssert1211A947.template.json │ ├── manifest.json │ └── tree.json ├── integ.function.ts ├── integ.function.ts.snapshot │ ├── cdk.out │ ├── integ-lambda-rust-function.assets.json │ ├── integ-lambda-rust-function.template.json │ ├── integ.json │ ├── lambdarustfunctionDefaultTestDeployAssert1211A947.assets.json │ ├── lambdarustfunctionDefaultTestDeployAssert1211A947.template.json │ ├── manifest.json │ └── tree.json ├── package-manager.test.ts ├── rust-bins │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── bin │ │ ├── my_lambda1 │ │ └── main.rs │ │ └── my_lambda2 │ │ └── main.rs ├── rust-standalone │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── rust-workspaces │ ├── Cargo.lock │ ├── Cargo.toml │ ├── my_lambda1 │ │ ├── Cargo.toml │ │ └── src │ │ │ └── main.rs │ └── my_lambda2 │ │ ├── Cargo.toml │ │ └── src │ │ └── main.rs └── util.test.ts ├── tsconfig.dev.json └── yarn.lock /.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 | "curly": [ 48 | "error", 49 | "multi-line", 50 | "consistent" 51 | ], 52 | "@typescript-eslint/no-require-imports": "error", 53 | "import/no-extraneous-dependencies": [ 54 | "error", 55 | { 56 | "devDependencies": [ 57 | "**/test/**", 58 | "**/build-tools/**", 59 | ".projenrc.ts", 60 | "projenrc/**/*.ts" 61 | ], 62 | "optionalDependencies": false, 63 | "peerDependencies": true 64 | } 65 | ], 66 | "import/no-unresolved": [ 67 | "error" 68 | ], 69 | "import/order": [ 70 | "warn", 71 | { 72 | "groups": [ 73 | "builtin", 74 | "external" 75 | ], 76 | "alphabetize": { 77 | "order": "asc", 78 | "caseInsensitive": true 79 | } 80 | } 81 | ], 82 | "import/no-duplicates": [ 83 | "error" 84 | ], 85 | "no-shadow": [ 86 | "off" 87 | ], 88 | "@typescript-eslint/no-shadow": "error", 89 | "@typescript-eslint/no-floating-promises": "error", 90 | "no-return-await": [ 91 | "off" 92 | ], 93 | "@typescript-eslint/return-await": "error", 94 | "dot-notation": [ 95 | "error" 96 | ], 97 | "no-bitwise": [ 98 | "error" 99 | ], 100 | "@typescript-eslint/member-ordering": [ 101 | "error", 102 | { 103 | "default": [ 104 | "public-static-field", 105 | "public-static-method", 106 | "protected-static-field", 107 | "protected-static-method", 108 | "private-static-field", 109 | "private-static-method", 110 | "field", 111 | "constructor", 112 | "method" 113 | ] 114 | } 115 | ] 116 | }, 117 | "overrides": [ 118 | { 119 | "files": [ 120 | ".projenrc.ts" 121 | ], 122 | "rules": { 123 | "@typescript-eslint/no-require-imports": "off", 124 | "import/no-extraneous-dependencies": "off" 125 | } 126 | } 127 | ] 128 | } 129 | -------------------------------------------------------------------------------- /.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/auto-approve.yml linguist-generated 9 | /.github/workflows/auto-queue.yml linguist-generated 10 | /.github/workflows/build.yml linguist-generated 11 | /.github/workflows/pull-request-lint.yml linguist-generated 12 | /.github/workflows/release.yml linguist-generated 13 | /.github/workflows/upgrade-cdklabs-projen-project-types-main.yml linguist-generated 14 | /.github/workflows/upgrade-dev-deps-main.yml linguist-generated 15 | /.github/workflows/upgrade-main.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 -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Fixes # -------------------------------------------------------------------------------- /.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 | - labeled 8 | - opened 9 | - synchronize 10 | - reopened 11 | - ready_for_review 12 | jobs: 13 | approve: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | pull-requests: write 17 | if: contains(github.event.pull_request.labels.*.name, 'auto-approve') && (github.event.pull_request.user.login == 'cdklabs-automation' || github.event.pull_request.user.login == 'dependabot[bot]') 18 | steps: 19 | - uses: hmarr/auto-approve-action@v2.2.1 20 | with: 21 | github-token: ${{ secrets.GITHUB_TOKEN }} 22 | -------------------------------------------------------------------------------- /.github/workflows/auto-queue.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | name: auto-queue 4 | on: 5 | pull_request_target: 6 | types: 7 | - opened 8 | - reopened 9 | - ready_for_review 10 | jobs: 11 | enableAutoQueue: 12 | name: "Set AutoQueue on PR #${{ github.event.number }}" 13 | runs-on: ubuntu-latest 14 | permissions: 15 | pull-requests: write 16 | contents: write 17 | steps: 18 | - uses: peter-evans/enable-pull-request-automerge@v3 19 | with: 20 | token: ${{ secrets.PROJEN_GITHUB_TOKEN }} 21 | pull-request-number: ${{ github.event.number }} 22 | merge-method: squash 23 | -------------------------------------------------------------------------------- /.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 | merge_group: {} 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: write 13 | outputs: 14 | self_mutation_happened: ${{ steps.self_mutation.outputs.self_mutation_happened }} 15 | env: 16 | CI: "true" 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | with: 21 | ref: ${{ github.event.pull_request.head.ref }} 22 | repository: ${{ github.event.pull_request.head.repo.full_name }} 23 | - name: Setup Node.js 24 | uses: actions/setup-node@v4 25 | with: 26 | node-version: lts/* 27 | - name: Install dependencies 28 | run: yarn install --check-files 29 | - name: build 30 | run: npx projen build 31 | - name: Find mutations 32 | id: self_mutation 33 | run: |- 34 | git add . 35 | git diff --staged --patch --exit-code > repo.patch || echo "self_mutation_happened=true" >> $GITHUB_OUTPUT 36 | working-directory: ./ 37 | - name: Upload patch 38 | if: steps.self_mutation.outputs.self_mutation_happened 39 | uses: actions/upload-artifact@v4.4.0 40 | with: 41 | name: repo.patch 42 | path: repo.patch 43 | overwrite: true 44 | - name: Fail build on mutation 45 | if: steps.self_mutation.outputs.self_mutation_happened 46 | run: |- 47 | echo "::error::Files were changed during build (see build log). If this was triggered from a fork, you will need to update your branch." 48 | cat repo.patch 49 | exit 1 50 | - name: Backup artifact permissions 51 | run: cd dist && getfacl -R . > permissions-backup.acl 52 | continue-on-error: true 53 | - name: Upload artifact 54 | uses: actions/upload-artifact@v4.4.0 55 | with: 56 | name: build-artifact 57 | path: dist 58 | overwrite: true 59 | self-mutation: 60 | needs: build 61 | runs-on: ubuntu-latest 62 | permissions: 63 | contents: write 64 | if: always() && needs.build.outputs.self_mutation_happened && !(github.event.pull_request.head.repo.full_name != github.repository) 65 | steps: 66 | - name: Checkout 67 | uses: actions/checkout@v4 68 | with: 69 | token: ${{ secrets.PROJEN_GITHUB_TOKEN }} 70 | ref: ${{ github.event.pull_request.head.ref }} 71 | repository: ${{ github.event.pull_request.head.repo.full_name }} 72 | - name: Download patch 73 | uses: actions/download-artifact@v4 74 | with: 75 | name: repo.patch 76 | path: ${{ runner.temp }} 77 | - name: Apply patch 78 | run: '[ -s ${{ runner.temp }}/repo.patch ] && git apply ${{ runner.temp }}/repo.patch || echo "Empty patch. Skipping."' 79 | - name: Set git identity 80 | run: |- 81 | git config user.name "github-actions" 82 | git config user.email "github-actions@github.com" 83 | - name: Push changes 84 | env: 85 | PULL_REQUEST_REF: ${{ github.event.pull_request.head.ref }} 86 | run: |- 87 | git add . 88 | git commit -s -m "chore: self mutation" 89 | git push origin HEAD:$PULL_REQUEST_REF 90 | package-js: 91 | needs: build 92 | runs-on: ubuntu-latest 93 | permissions: 94 | contents: read 95 | if: ${{ !needs.build.outputs.self_mutation_happened }} 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 | ref: ${{ github.event.pull_request.head.ref }} 112 | repository: ${{ github.event.pull_request.head.repo.full_name }} 113 | path: .repo 114 | - name: Install Dependencies 115 | run: cd .repo && yarn install --check-files --frozen-lockfile 116 | - name: Extract build artifact 117 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo 118 | - name: Move build artifact out of the way 119 | run: mv dist dist.old 120 | - name: Create js artifact 121 | run: cd .repo && npx projen package:js 122 | - name: Collect js artifact 123 | run: mv .repo/dist dist 124 | package-java: 125 | needs: build 126 | runs-on: ubuntu-latest 127 | permissions: 128 | contents: read 129 | if: ${{ !needs.build.outputs.self_mutation_happened }} 130 | steps: 131 | - uses: actions/setup-java@v4 132 | with: 133 | distribution: corretto 134 | java-version: "11" 135 | - uses: actions/setup-node@v4 136 | with: 137 | node-version: lts/* 138 | - name: Download build artifacts 139 | uses: actions/download-artifact@v4 140 | with: 141 | name: build-artifact 142 | path: dist 143 | - name: Restore build artifact permissions 144 | run: cd dist && setfacl --restore=permissions-backup.acl 145 | continue-on-error: true 146 | - name: Checkout 147 | uses: actions/checkout@v4 148 | with: 149 | ref: ${{ github.event.pull_request.head.ref }} 150 | repository: ${{ github.event.pull_request.head.repo.full_name }} 151 | path: .repo 152 | - name: Install Dependencies 153 | run: cd .repo && yarn install --check-files --frozen-lockfile 154 | - name: Extract build artifact 155 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo 156 | - name: Move build artifact out of the way 157 | run: mv dist dist.old 158 | - name: Create java artifact 159 | run: cd .repo && npx projen package:java 160 | - name: Collect java artifact 161 | run: mv .repo/dist dist 162 | package-python: 163 | needs: build 164 | runs-on: ubuntu-latest 165 | permissions: 166 | contents: read 167 | if: ${{ !needs.build.outputs.self_mutation_happened }} 168 | steps: 169 | - uses: actions/setup-node@v4 170 | with: 171 | node-version: lts/* 172 | - uses: actions/setup-python@v5 173 | with: 174 | python-version: 3.x 175 | - name: Download build artifacts 176 | uses: actions/download-artifact@v4 177 | with: 178 | name: build-artifact 179 | path: dist 180 | - name: Restore build artifact permissions 181 | run: cd dist && setfacl --restore=permissions-backup.acl 182 | continue-on-error: true 183 | - name: Checkout 184 | uses: actions/checkout@v4 185 | with: 186 | ref: ${{ github.event.pull_request.head.ref }} 187 | repository: ${{ github.event.pull_request.head.repo.full_name }} 188 | path: .repo 189 | - name: Install Dependencies 190 | run: cd .repo && yarn install --check-files --frozen-lockfile 191 | - name: Extract build artifact 192 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo 193 | - name: Move build artifact out of the way 194 | run: mv dist dist.old 195 | - name: Create python artifact 196 | run: cd .repo && npx projen package:python 197 | - name: Collect python artifact 198 | run: mv .repo/dist dist 199 | package-dotnet: 200 | needs: build 201 | runs-on: ubuntu-latest 202 | permissions: 203 | contents: read 204 | if: ${{ !needs.build.outputs.self_mutation_happened }} 205 | steps: 206 | - uses: actions/setup-node@v4 207 | with: 208 | node-version: lts/* 209 | - uses: actions/setup-dotnet@v4 210 | with: 211 | dotnet-version: 6.x 212 | - name: Download build artifacts 213 | uses: actions/download-artifact@v4 214 | with: 215 | name: build-artifact 216 | path: dist 217 | - name: Restore build artifact permissions 218 | run: cd dist && setfacl --restore=permissions-backup.acl 219 | continue-on-error: true 220 | - name: Checkout 221 | uses: actions/checkout@v4 222 | with: 223 | ref: ${{ github.event.pull_request.head.ref }} 224 | repository: ${{ github.event.pull_request.head.repo.full_name }} 225 | path: .repo 226 | - name: Install Dependencies 227 | run: cd .repo && yarn install --check-files --frozen-lockfile 228 | - name: Extract build artifact 229 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo 230 | - name: Move build artifact out of the way 231 | run: mv dist dist.old 232 | - name: Create dotnet artifact 233 | run: cd .repo && npx projen package:dotnet 234 | - name: Collect dotnet artifact 235 | run: mv .repo/dist dist 236 | -------------------------------------------------------------------------------- /.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 | merge_group: {} 14 | jobs: 15 | validate: 16 | name: Validate PR title 17 | runs-on: ubuntu-latest 18 | permissions: 19 | pull-requests: write 20 | if: (github.event_name == 'pull_request' || github.event_name == 'pull_request_target') 21 | steps: 22 | - uses: amannn/action-semantic-pull-request@v5.4.0 23 | env: 24 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 25 | with: 26 | types: |- 27 | feat 28 | fix 29 | chore 30 | requireScope: false 31 | -------------------------------------------------------------------------------- /.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: Set git identity 28 | run: |- 29 | git config user.name "github-actions" 30 | git config user.email "github-actions@github.com" 31 | - name: Setup Node.js 32 | uses: actions/setup-node@v4 33 | with: 34 | node-version: lts/* 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 | - release_maven 67 | - release_pypi 68 | - release_nuget 69 | runs-on: ubuntu-latest 70 | permissions: 71 | contents: write 72 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha 73 | steps: 74 | - uses: actions/setup-node@v4 75 | with: 76 | node-version: lts/* 77 | - name: Download build artifacts 78 | uses: actions/download-artifact@v4 79 | with: 80 | name: build-artifact 81 | path: dist 82 | - name: Restore build artifact permissions 83 | run: cd dist && setfacl --restore=permissions-backup.acl 84 | continue-on-error: true 85 | - name: Release 86 | env: 87 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 88 | run: errout=$(mktemp); gh release create $(cat dist/releasetag.txt) -R $GITHUB_REPOSITORY -F dist/changelog.md -t $(cat dist/releasetag.txt) --target $GITHUB_SHA 2> $errout && true; exitcode=$?; if [ $exitcode -ne 0 ] && ! grep -q "Release.tag_name already exists" $errout; then cat $errout; exit $exitcode; fi 89 | release_npm: 90 | name: Publish to npm 91 | needs: release 92 | runs-on: ubuntu-latest 93 | permissions: 94 | id-token: write 95 | contents: read 96 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha 97 | steps: 98 | - uses: actions/setup-node@v4 99 | with: 100 | node-version: lts/* 101 | - name: Download build artifacts 102 | uses: actions/download-artifact@v4 103 | with: 104 | name: build-artifact 105 | path: dist 106 | - name: Restore build artifact permissions 107 | run: cd dist && setfacl --restore=permissions-backup.acl 108 | continue-on-error: true 109 | - name: Checkout 110 | uses: actions/checkout@v4 111 | with: 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 | - name: Release 124 | env: 125 | NPM_DIST_TAG: latest 126 | NPM_REGISTRY: registry.npmjs.org 127 | NPM_CONFIG_PROVENANCE: "true" 128 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 129 | run: npx -p publib@latest publib-npm 130 | release_maven: 131 | name: Publish to Maven Central 132 | needs: release 133 | runs-on: ubuntu-latest 134 | permissions: 135 | contents: read 136 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha 137 | steps: 138 | - uses: actions/setup-java@v4 139 | with: 140 | distribution: corretto 141 | java-version: "11" 142 | - uses: actions/setup-node@v4 143 | with: 144 | node-version: lts/* 145 | - name: Download build artifacts 146 | uses: actions/download-artifact@v4 147 | with: 148 | name: build-artifact 149 | path: dist 150 | - name: Restore build artifact permissions 151 | run: cd dist && setfacl --restore=permissions-backup.acl 152 | continue-on-error: true 153 | - name: Checkout 154 | uses: actions/checkout@v4 155 | with: 156 | path: .repo 157 | - name: Install Dependencies 158 | run: cd .repo && yarn install --check-files --frozen-lockfile 159 | - name: Extract build artifact 160 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo 161 | - name: Move build artifact out of the way 162 | run: mv dist dist.old 163 | - name: Create java artifact 164 | run: cd .repo && npx projen package:java 165 | - name: Collect java artifact 166 | run: mv .repo/dist dist 167 | - name: Release 168 | env: 169 | MAVEN_SERVER_ID: central-ossrh 170 | MAVEN_GPG_PRIVATE_KEY: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} 171 | MAVEN_GPG_PRIVATE_KEY_PASSPHRASE: ${{ secrets.MAVEN_GPG_PRIVATE_KEY_PASSPHRASE }} 172 | MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} 173 | MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} 174 | MAVEN_STAGING_PROFILE_ID: ${{ secrets.MAVEN_STAGING_PROFILE_ID }} 175 | run: npx -p publib@latest publib-maven 176 | release_pypi: 177 | name: Publish to PyPI 178 | needs: release 179 | runs-on: ubuntu-latest 180 | permissions: 181 | contents: read 182 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha 183 | steps: 184 | - uses: actions/setup-node@v4 185 | with: 186 | node-version: lts/* 187 | - uses: actions/setup-python@v5 188 | with: 189 | python-version: 3.x 190 | - name: Download build artifacts 191 | uses: actions/download-artifact@v4 192 | with: 193 | name: build-artifact 194 | path: dist 195 | - name: Restore build artifact permissions 196 | run: cd dist && setfacl --restore=permissions-backup.acl 197 | continue-on-error: true 198 | - name: Checkout 199 | uses: actions/checkout@v4 200 | with: 201 | path: .repo 202 | - name: Install Dependencies 203 | run: cd .repo && yarn install --check-files --frozen-lockfile 204 | - name: Extract build artifact 205 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo 206 | - name: Move build artifact out of the way 207 | run: mv dist dist.old 208 | - name: Create python artifact 209 | run: cd .repo && npx projen package:python 210 | - name: Collect python artifact 211 | run: mv .repo/dist dist 212 | - name: Release 213 | env: 214 | TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }} 215 | TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} 216 | run: npx -p publib@latest publib-pypi 217 | release_nuget: 218 | name: Publish to NuGet Gallery 219 | needs: release 220 | runs-on: ubuntu-latest 221 | permissions: 222 | contents: read 223 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha 224 | steps: 225 | - uses: actions/setup-node@v4 226 | with: 227 | node-version: lts/* 228 | - uses: actions/setup-dotnet@v4 229 | with: 230 | dotnet-version: 6.x 231 | - name: Download build artifacts 232 | uses: actions/download-artifact@v4 233 | with: 234 | name: build-artifact 235 | path: dist 236 | - name: Restore build artifact permissions 237 | run: cd dist && setfacl --restore=permissions-backup.acl 238 | continue-on-error: true 239 | - name: Checkout 240 | uses: actions/checkout@v4 241 | with: 242 | path: .repo 243 | - name: Install Dependencies 244 | run: cd .repo && yarn install --check-files --frozen-lockfile 245 | - name: Extract build artifact 246 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo 247 | - name: Move build artifact out of the way 248 | run: mv dist dist.old 249 | - name: Create dotnet artifact 250 | run: cd .repo && npx projen package:dotnet 251 | - name: Collect dotnet artifact 252 | run: mv .repo/dist dist 253 | - name: Release 254 | env: 255 | NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} 256 | run: npx -p publib@latest publib-nuget 257 | -------------------------------------------------------------------------------- /.github/workflows/upgrade-cdklabs-projen-project-types-main.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | name: upgrade-cdklabs-projen-project-types-main 4 | on: 5 | workflow_dispatch: {} 6 | jobs: 7 | upgrade: 8 | name: Upgrade 9 | runs-on: ubuntu-latest 10 | permissions: 11 | contents: read 12 | outputs: 13 | patch_created: ${{ steps.create_patch.outputs.patch_created }} 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | with: 18 | ref: main 19 | - name: Setup Node.js 20 | uses: actions/setup-node@v4 21 | - name: Install dependencies 22 | run: yarn install --check-files --frozen-lockfile 23 | - name: Upgrade dependencies 24 | run: npx projen upgrade-cdklabs-projen-project-types 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 cdklabs-projen-project-types 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-cdklabs-projen-project-types-main" workflow* 76 | branch: github-actions/upgrade-cdklabs-projen-project-types-main 77 | title: "chore(deps): upgrade cdklabs-projen-project-types" 78 | labels: auto-approve 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-cdklabs-projen-project-types-main" workflow* 87 | author: github-actions 88 | committer: github-actions 89 | signoff: true 90 | -------------------------------------------------------------------------------- /.github/workflows/upgrade-dev-deps-main.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | 3 | name: upgrade-dev-deps-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@v4 19 | with: 20 | ref: main 21 | - name: Setup Node.js 22 | uses: actions/setup-node@v4 23 | with: 24 | node-version: lts/* 25 | - name: Install dependencies 26 | run: yarn install --check-files --frozen-lockfile 27 | - name: Upgrade dependencies 28 | run: npx projen upgrade-dev-deps 29 | - name: Find mutations 30 | id: create_patch 31 | run: |- 32 | git add . 33 | git diff --staged --patch --exit-code > repo.patch || echo "patch_created=true" >> $GITHUB_OUTPUT 34 | working-directory: ./ 35 | - name: Upload patch 36 | if: steps.create_patch.outputs.patch_created 37 | uses: actions/upload-artifact@v4.4.0 38 | with: 39 | name: repo.patch 40 | path: repo.patch 41 | overwrite: true 42 | pr: 43 | name: Create Pull Request 44 | needs: upgrade 45 | runs-on: ubuntu-latest 46 | permissions: 47 | contents: read 48 | if: ${{ needs.upgrade.outputs.patch_created }} 49 | steps: 50 | - name: Checkout 51 | uses: actions/checkout@v4 52 | with: 53 | ref: main 54 | - name: Download patch 55 | uses: actions/download-artifact@v4 56 | with: 57 | name: repo.patch 58 | path: ${{ runner.temp }} 59 | - name: Apply patch 60 | run: '[ -s ${{ runner.temp }}/repo.patch ] && git apply ${{ runner.temp }}/repo.patch || echo "Empty patch. Skipping."' 61 | - name: Set git identity 62 | run: |- 63 | git config user.name "github-actions" 64 | git config user.email "github-actions@github.com" 65 | - name: Create Pull Request 66 | id: create-pr 67 | uses: peter-evans/create-pull-request@v6 68 | with: 69 | token: ${{ secrets.PROJEN_GITHUB_TOKEN }} 70 | commit-message: |- 71 | chore(deps): upgrade dev dependencies 72 | 73 | Upgrades project dependencies. See details in [workflow run]. 74 | 75 | [Workflow Run]: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} 76 | 77 | ------ 78 | 79 | *Automatically created by projen via the "upgrade-dev-deps-main" workflow* 80 | branch: github-actions/upgrade-dev-deps-main 81 | title: "chore(deps): upgrade dev dependencies" 82 | labels: auto-approve 83 | body: |- 84 | Upgrades project dependencies. See details in [workflow run]. 85 | 86 | [Workflow Run]: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} 87 | 88 | ------ 89 | 90 | *Automatically created by projen via the "upgrade-dev-deps-main" workflow* 91 | author: github-actions 92 | committer: github-actions 93 | signoff: true 94 | -------------------------------------------------------------------------------- /.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@v4 19 | with: 20 | ref: main 21 | - name: Setup Node.js 22 | uses: actions/setup-node@v4 23 | with: 24 | node-version: lts/* 25 | - name: Install dependencies 26 | run: yarn install --check-files --frozen-lockfile 27 | - name: Upgrade dependencies 28 | run: npx projen upgrade 29 | - name: Find mutations 30 | id: create_patch 31 | run: |- 32 | git add . 33 | git diff --staged --patch --exit-code > repo.patch || echo "patch_created=true" >> $GITHUB_OUTPUT 34 | working-directory: ./ 35 | - name: Upload patch 36 | if: steps.create_patch.outputs.patch_created 37 | uses: actions/upload-artifact@v4.4.0 38 | with: 39 | name: repo.patch 40 | path: repo.patch 41 | overwrite: true 42 | pr: 43 | name: Create Pull Request 44 | needs: upgrade 45 | runs-on: ubuntu-latest 46 | permissions: 47 | contents: read 48 | if: ${{ needs.upgrade.outputs.patch_created }} 49 | steps: 50 | - name: Checkout 51 | uses: actions/checkout@v4 52 | with: 53 | ref: main 54 | - name: Download patch 55 | uses: actions/download-artifact@v4 56 | with: 57 | name: repo.patch 58 | path: ${{ runner.temp }} 59 | - name: Apply patch 60 | run: '[ -s ${{ runner.temp }}/repo.patch ] && git apply ${{ runner.temp }}/repo.patch || echo "Empty patch. Skipping."' 61 | - name: Set git identity 62 | run: |- 63 | git config user.name "github-actions" 64 | git config user.email "github-actions@github.com" 65 | - name: Create Pull Request 66 | id: create-pr 67 | uses: peter-evans/create-pull-request@v6 68 | with: 69 | token: ${{ secrets.PROJEN_GITHUB_TOKEN }} 70 | commit-message: |- 71 | fix(deps): upgrade dependencies 72 | 73 | Upgrades project dependencies. See details in [workflow run]. 74 | 75 | [Workflow Run]: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} 76 | 77 | ------ 78 | 79 | *Automatically created by projen via the "upgrade-main" workflow* 80 | branch: github-actions/upgrade-main 81 | title: "fix(deps): upgrade dependencies" 82 | labels: auto-approve 83 | body: |- 84 | Upgrades project dependencies. See details in [workflow run]. 85 | 86 | [Workflow Run]: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} 87 | 88 | ------ 89 | 90 | *Automatically created by projen via the "upgrade-main" workflow* 91 | author: github-actions 92 | committer: github-actions 93 | signoff: true 94 | -------------------------------------------------------------------------------- /.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 | !/.github/workflows/auto-approve.yml 8 | !/package.json 9 | !/LICENSE 10 | !/.npmignore 11 | logs 12 | *.log 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | lerna-debug.log* 17 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 18 | pids 19 | *.pid 20 | *.seed 21 | *.pid.lock 22 | lib-cov 23 | coverage 24 | *.lcov 25 | .nyc_output 26 | build/Release 27 | node_modules/ 28 | jspm_packages/ 29 | *.tsbuildinfo 30 | .eslintcache 31 | *.tgz 32 | .yarn-integrity 33 | .cache 34 | *.d.ts 35 | *.generated.ts 36 | *.js 37 | **/integ.*.snapshot/asset.*/ 38 | **/target 39 | /test-reports/ 40 | junit.xml 41 | /coverage/ 42 | !/.github/workflows/build.yml 43 | /dist/changelog.md 44 | /dist/version.txt 45 | !/.github/workflows/release.yml 46 | !/.github/pull_request_template.md 47 | !/.prettierignore 48 | !/.prettierrc.json 49 | !/test/ 50 | !/tsconfig.dev.json 51 | !/src/ 52 | /lib 53 | /dist/ 54 | !/.eslintrc.json 55 | .jsii 56 | tsconfig.json 57 | !/API.md 58 | .jsii.tabl.json 59 | !/rosetta/default.ts-fixture 60 | !/.github/workflows/auto-queue.yml 61 | !/.github/workflows/upgrade-cdklabs-projen-project-types-main.yml 62 | !/.github/workflows/upgrade-main.yml 63 | !/.github/workflows/upgrade-dev-deps-main.yml 64 | !/.projenrc.ts 65 | -------------------------------------------------------------------------------- /.idea/jsLinters/eslint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen". 2 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "overrides": [] 4 | } 5 | -------------------------------------------------------------------------------- /.projen/deps.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | { 4 | "name": "@aws-cdk/integ-tests-alpha", 5 | "type": "build" 6 | }, 7 | { 8 | "name": "@types/jest", 9 | "type": "build" 10 | }, 11 | { 12 | "name": "@types/node", 13 | "version": "^18", 14 | "type": "build" 15 | }, 16 | { 17 | "name": "@typescript-eslint/eslint-plugin", 18 | "version": "^8", 19 | "type": "build" 20 | }, 21 | { 22 | "name": "@typescript-eslint/parser", 23 | "version": "^8", 24 | "type": "build" 25 | }, 26 | { 27 | "name": "cdklabs-projen-project-types", 28 | "type": "build" 29 | }, 30 | { 31 | "name": "commit-and-tag-version", 32 | "version": "^12", 33 | "type": "build" 34 | }, 35 | { 36 | "name": "eslint-config-prettier", 37 | "type": "build" 38 | }, 39 | { 40 | "name": "eslint-import-resolver-typescript", 41 | "type": "build" 42 | }, 43 | { 44 | "name": "eslint-plugin-import", 45 | "type": "build" 46 | }, 47 | { 48 | "name": "eslint-plugin-prettier", 49 | "type": "build" 50 | }, 51 | { 52 | "name": "eslint", 53 | "version": "^9", 54 | "type": "build" 55 | }, 56 | { 57 | "name": "jest", 58 | "type": "build" 59 | }, 60 | { 61 | "name": "jest-junit", 62 | "version": "^16", 63 | "type": "build" 64 | }, 65 | { 66 | "name": "jsii-diff", 67 | "type": "build" 68 | }, 69 | { 70 | "name": "jsii-docgen", 71 | "version": "^10.5.0", 72 | "type": "build" 73 | }, 74 | { 75 | "name": "jsii-pacmak", 76 | "type": "build" 77 | }, 78 | { 79 | "name": "jsii-rosetta", 80 | "type": "build" 81 | }, 82 | { 83 | "name": "jsii", 84 | "version": "~5.3.0", 85 | "type": "build" 86 | }, 87 | { 88 | "name": "prettier", 89 | "type": "build" 90 | }, 91 | { 92 | "name": "projen", 93 | "type": "build" 94 | }, 95 | { 96 | "name": "ts-jest", 97 | "type": "build" 98 | }, 99 | { 100 | "name": "ts-node", 101 | "type": "build" 102 | }, 103 | { 104 | "name": "typescript", 105 | "type": "build" 106 | }, 107 | { 108 | "name": "toml", 109 | "type": "bundled" 110 | }, 111 | { 112 | "name": "@aws-cdk/integ-runner", 113 | "version": "latest", 114 | "type": "devenv" 115 | }, 116 | { 117 | "name": "@aws-cdk/integ-tests-alpha", 118 | "version": "latest", 119 | "type": "devenv" 120 | }, 121 | { 122 | "name": "aws-cdk-lib", 123 | "version": "^2.121.0", 124 | "type": "peer" 125 | }, 126 | { 127 | "name": "constructs", 128 | "version": "^10.0.5", 129 | "type": "peer" 130 | }, 131 | { 132 | "name": "@aws-cdk/integ-tests-alpha", 133 | "version": "2.121.0-alpha.0", 134 | "type": "test" 135 | } 136 | ], 137 | "//": "~~ Generated by projen. To modify, edit .projenrc.ts and run \"npx projen\"." 138 | } 139 | -------------------------------------------------------------------------------- /.projen/files.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | ".eslintrc.json", 4 | ".gitattributes", 5 | ".github/pull_request_template.md", 6 | ".github/workflows/auto-approve.yml", 7 | ".github/workflows/auto-queue.yml", 8 | ".github/workflows/build.yml", 9 | ".github/workflows/pull-request-lint.yml", 10 | ".github/workflows/release.yml", 11 | ".github/workflows/upgrade-cdklabs-projen-project-types-main.yml", 12 | ".github/workflows/upgrade-dev-deps-main.yml", 13 | ".github/workflows/upgrade-main.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 | -------------------------------------------------------------------------------- /.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 | "RELEASABLE_COMMITS": "git log --no-merges --oneline $LATEST_TAG..HEAD -E --grep \"^(feat|fix){1}(\\([^()[:space:]]+\\))?(!)?:[[:blank:]]+.+\"" 38 | }, 39 | "steps": [ 40 | { 41 | "builtin": "release/bump-version" 42 | } 43 | ], 44 | "condition": "git log --oneline -1 | grep -qv \"chore(release):\"" 45 | }, 46 | "clobber": { 47 | "name": "clobber", 48 | "description": "hard resets to HEAD of origin and cleans the local repo", 49 | "env": { 50 | "BRANCH": "$(git branch --show-current)" 51 | }, 52 | "steps": [ 53 | { 54 | "exec": "git checkout -b scratch", 55 | "name": "save current HEAD in \"scratch\" branch" 56 | }, 57 | { 58 | "exec": "git checkout $BRANCH" 59 | }, 60 | { 61 | "exec": "git fetch origin", 62 | "name": "fetch latest changes from origin" 63 | }, 64 | { 65 | "exec": "git reset --hard origin/$BRANCH", 66 | "name": "hard reset to origin commit" 67 | }, 68 | { 69 | "exec": "git clean -fdx", 70 | "name": "clean all untracked files" 71 | }, 72 | { 73 | "say": "ready to rock! (unpushed commits are under the \"scratch\" branch)" 74 | } 75 | ], 76 | "condition": "git diff --exit-code > /dev/null" 77 | }, 78 | "compat": { 79 | "name": "compat", 80 | "description": "Perform API compatibility check against latest version", 81 | "steps": [ 82 | { 83 | "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)" 84 | } 85 | ] 86 | }, 87 | "compile": { 88 | "name": "compile", 89 | "description": "Only compile", 90 | "steps": [ 91 | { 92 | "exec": "jsii --silence-warnings=reserved-word" 93 | } 94 | ] 95 | }, 96 | "default": { 97 | "name": "default", 98 | "description": "Synthesize project files", 99 | "steps": [ 100 | { 101 | "exec": "ts-node --project tsconfig.dev.json .projenrc.ts" 102 | } 103 | ] 104 | }, 105 | "docgen": { 106 | "name": "docgen", 107 | "description": "Generate API.md from .jsii manifest", 108 | "steps": [ 109 | { 110 | "exec": "jsii-docgen -o API.md" 111 | } 112 | ] 113 | }, 114 | "eject": { 115 | "name": "eject", 116 | "description": "Remove projen from the project", 117 | "env": { 118 | "PROJEN_EJECTING": "true" 119 | }, 120 | "steps": [ 121 | { 122 | "spawn": "default" 123 | } 124 | ] 125 | }, 126 | "eslint": { 127 | "name": "eslint", 128 | "description": "Runs eslint against the codebase", 129 | "env": { 130 | "ESLINT_USE_FLAT_CONFIG": "false" 131 | }, 132 | "steps": [ 133 | { 134 | "exec": "eslint --ext .ts,.tsx --fix --no-error-on-unmatched-pattern $@ src test build-tools projenrc .projenrc.ts", 135 | "receiveArgs": true 136 | } 137 | ] 138 | }, 139 | "install": { 140 | "name": "install", 141 | "description": "Install project dependencies and update lockfile (non-frozen)", 142 | "steps": [ 143 | { 144 | "exec": "yarn install --check-files" 145 | } 146 | ] 147 | }, 148 | "install:ci": { 149 | "name": "install:ci", 150 | "description": "Install project dependencies using frozen lockfile", 151 | "steps": [ 152 | { 153 | "exec": "yarn install --check-files --frozen-lockfile" 154 | } 155 | ] 156 | }, 157 | "integ": { 158 | "name": "integ", 159 | "description": "Run integration snapshot tests", 160 | "steps": [ 161 | { 162 | "exec": "yarn integ-runner --language typescript", 163 | "receiveArgs": true 164 | } 165 | ] 166 | }, 167 | "integ:update": { 168 | "name": "integ:update", 169 | "description": "Run and update integration snapshot tests", 170 | "steps": [ 171 | { 172 | "exec": "yarn integ-runner --language typescript --update-on-failed", 173 | "receiveArgs": true 174 | } 175 | ] 176 | }, 177 | "package": { 178 | "name": "package", 179 | "description": "Creates the distribution package", 180 | "steps": [ 181 | { 182 | "spawn": "package:js", 183 | "condition": "node -e \"if (!process.env.CI) process.exit(1)\"" 184 | }, 185 | { 186 | "spawn": "package-all", 187 | "condition": "node -e \"if (process.env.CI) process.exit(1)\"" 188 | } 189 | ] 190 | }, 191 | "package-all": { 192 | "name": "package-all", 193 | "description": "Packages artifacts for all target languages", 194 | "steps": [ 195 | { 196 | "spawn": "package:js" 197 | }, 198 | { 199 | "spawn": "package:java" 200 | }, 201 | { 202 | "spawn": "package:python" 203 | }, 204 | { 205 | "spawn": "package:dotnet" 206 | } 207 | ] 208 | }, 209 | "package:dotnet": { 210 | "name": "package:dotnet", 211 | "description": "Create dotnet language bindings", 212 | "steps": [ 213 | { 214 | "exec": "jsii-pacmak -v --target dotnet" 215 | } 216 | ] 217 | }, 218 | "package:java": { 219 | "name": "package:java", 220 | "description": "Create java language bindings", 221 | "steps": [ 222 | { 223 | "exec": "jsii-pacmak -v --target java" 224 | } 225 | ] 226 | }, 227 | "package:js": { 228 | "name": "package:js", 229 | "description": "Create js language bindings", 230 | "steps": [ 231 | { 232 | "exec": "jsii-pacmak -v --target js" 233 | } 234 | ] 235 | }, 236 | "package:python": { 237 | "name": "package:python", 238 | "description": "Create python language bindings", 239 | "steps": [ 240 | { 241 | "exec": "jsii-pacmak -v --target python" 242 | } 243 | ] 244 | }, 245 | "post-compile": { 246 | "name": "post-compile", 247 | "description": "Runs after successful compilation", 248 | "steps": [ 249 | { 250 | "spawn": "docgen" 251 | }, 252 | { 253 | "spawn": "rosetta:extract" 254 | } 255 | ] 256 | }, 257 | "post-upgrade": { 258 | "name": "post-upgrade", 259 | "description": "Runs after upgrading dependencies" 260 | }, 261 | "pre-compile": { 262 | "name": "pre-compile", 263 | "description": "Prepare the project for compilation" 264 | }, 265 | "release": { 266 | "name": "release", 267 | "description": "Prepare a release from \"main\" branch", 268 | "env": { 269 | "RELEASE": "true" 270 | }, 271 | "steps": [ 272 | { 273 | "exec": "rm -fr dist" 274 | }, 275 | { 276 | "spawn": "bump" 277 | }, 278 | { 279 | "spawn": "build" 280 | }, 281 | { 282 | "spawn": "unbump" 283 | }, 284 | { 285 | "exec": "git diff --ignore-space-at-eol --exit-code" 286 | } 287 | ] 288 | }, 289 | "rosetta:extract": { 290 | "name": "rosetta:extract", 291 | "description": "Test rosetta extract", 292 | "steps": [ 293 | { 294 | "exec": "yarn --silent jsii-rosetta extract --strict" 295 | } 296 | ] 297 | }, 298 | "test": { 299 | "name": "test", 300 | "description": "Run tests", 301 | "steps": [ 302 | { 303 | "exec": "jest --passWithNoTests --updateSnapshot", 304 | "receiveArgs": true 305 | }, 306 | { 307 | "spawn": "eslint" 308 | }, 309 | { 310 | "spawn": "integ" 311 | } 312 | ] 313 | }, 314 | "test:watch": { 315 | "name": "test:watch", 316 | "description": "Run jest in watch mode", 317 | "steps": [ 318 | { 319 | "exec": "jest --watch" 320 | } 321 | ] 322 | }, 323 | "unbump": { 324 | "name": "unbump", 325 | "description": "Restores version to 0.0.0", 326 | "env": { 327 | "OUTFILE": "package.json", 328 | "CHANGELOG": "dist/changelog.md", 329 | "BUMPFILE": "dist/version.txt", 330 | "RELEASETAG": "dist/releasetag.txt", 331 | "RELEASE_TAG_PREFIX": "", 332 | "BUMP_PACKAGE": "commit-and-tag-version@^12", 333 | "RELEASABLE_COMMITS": "git log --no-merges --oneline $LATEST_TAG..HEAD -E --grep \"^(feat|fix){1}(\\([^()[:space:]]+\\))?(!)?:[[:blank:]]+.+\"" 334 | }, 335 | "steps": [ 336 | { 337 | "builtin": "release/reset-version" 338 | } 339 | ] 340 | }, 341 | "upgrade": { 342 | "name": "upgrade", 343 | "description": "upgrade dependencies", 344 | "env": { 345 | "CI": "0" 346 | }, 347 | "steps": [ 348 | { 349 | "exec": "npx npm-check-updates@16 --upgrade --target=minor --peer --no-deprecated --dep=prod --filter=toml" 350 | }, 351 | { 352 | "exec": "yarn install --check-files" 353 | }, 354 | { 355 | "exec": "yarn upgrade toml" 356 | }, 357 | { 358 | "exec": "npx projen" 359 | }, 360 | { 361 | "spawn": "post-upgrade" 362 | } 363 | ] 364 | }, 365 | "upgrade-cdklabs-projen-project-types": { 366 | "name": "upgrade-cdklabs-projen-project-types", 367 | "description": "upgrade cdklabs-projen-project-types", 368 | "env": { 369 | "CI": "0" 370 | }, 371 | "steps": [ 372 | { 373 | "exec": "npx npm-check-updates@16 --upgrade --target=latest --peer --no-deprecated --dep=dev,peer,prod,optional --filter=cdklabs-projen-project-types,projen" 374 | }, 375 | { 376 | "exec": "yarn install --check-files" 377 | }, 378 | { 379 | "exec": "yarn upgrade cdklabs-projen-project-types projen" 380 | }, 381 | { 382 | "exec": "npx projen" 383 | }, 384 | { 385 | "spawn": "post-upgrade" 386 | } 387 | ] 388 | }, 389 | "upgrade-dev-deps": { 390 | "name": "upgrade-dev-deps", 391 | "description": "upgrade dev dependencies", 392 | "env": { 393 | "CI": "0" 394 | }, 395 | "steps": [ 396 | { 397 | "exec": "npx npm-check-updates@16 --upgrade --target=minor --peer --no-deprecated --dep=dev --filter=@aws-cdk/integ-tests-alpha,@types/jest,eslint-config-prettier,eslint-import-resolver-typescript,eslint-plugin-import,eslint-plugin-prettier,jest,jsii-diff,jsii-pacmak,jsii-rosetta,prettier,ts-jest,ts-node,typescript" 398 | }, 399 | { 400 | "exec": "yarn install --check-files" 401 | }, 402 | { 403 | "exec": "yarn upgrade @aws-cdk/integ-tests-alpha @types/jest @types/node @typescript-eslint/eslint-plugin @typescript-eslint/parser commit-and-tag-version 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 ts-jest ts-node typescript @aws-cdk/integ-runner" 404 | }, 405 | { 406 | "exec": "npx projen" 407 | }, 408 | { 409 | "spawn": "post-upgrade" 410 | } 411 | ] 412 | }, 413 | "watch": { 414 | "name": "watch", 415 | "description": "Watch & compile in the background", 416 | "steps": [ 417 | { 418 | "exec": "jsii -w --silence-warnings=reserved-word" 419 | } 420 | ] 421 | } 422 | }, 423 | "env": { 424 | "PATH": "$(npx -c \"node --print process.env.PATH\")" 425 | }, 426 | "//": "~~ Generated by projen. To modify, edit .projenrc.ts and run \"npx projen\"." 427 | } 428 | -------------------------------------------------------------------------------- /.projenrc.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CdklabsConstructLibrary, 3 | JsiiLanguage, 4 | } from 'cdklabs-projen-project-types'; 5 | import { DependencyType } from 'projen'; 6 | import { UpgradeDependenciesSchedule } from 'projen/lib/javascript'; 7 | 8 | const project = new CdklabsConstructLibrary({ 9 | author: 'AWS', 10 | authorAddress: 'aws-cdk-dev@amazon.com', 11 | cdkVersion: '2.121.0', 12 | jsiiVersion: '~5.3.0', 13 | defaultReleaseBranch: 'main', 14 | devDeps: ['cdklabs-projen-project-types', '@aws-cdk/integ-tests-alpha'], 15 | bundledDeps: ['toml'], 16 | name: '@cdklabs/aws-lambda-rust', 17 | projenrcTs: true, 18 | private: false, 19 | enablePRAutoMerge: true, 20 | stability: 'experimental', 21 | setNodeEngineVersion: false, 22 | repositoryUrl: 'https://github.com/cdklabs/aws-lambda-rust.git', 23 | depsUpgradeOptions: { 24 | workflowOptions: { 25 | schedule: UpgradeDependenciesSchedule.WEEKLY, 26 | }, 27 | }, 28 | prettier: true, 29 | prettierOptions: { 30 | settings: { 31 | singleQuote: true, 32 | }, 33 | }, 34 | jsiiTargetLanguages: [ 35 | JsiiLanguage.JAVA, 36 | JsiiLanguage.PYTHON, 37 | JsiiLanguage.DOTNET, 38 | ], 39 | publishToMaven: { 40 | javaPackage: 'io.github.cdklabs.awslambdarust', 41 | mavenGroupId: 'io.github.cdklabs', 42 | mavenArtifactId: 'aws-lambda-rust', 43 | mavenServerId: 'central-ossrh', 44 | }, 45 | publishToPypi: { 46 | distName: 'cdklabs.aws-lambda-rust', 47 | module: 'cdklabs.aws_lambda_rust', 48 | }, 49 | publishToNuget: { 50 | dotNetNamespace: 'Cdklabs.AwsLambdaRust', 51 | packageId: 'Cdklabs.AwsLambdaRust', 52 | }, 53 | autoApproveOptions: { 54 | allowedUsernames: ['aws-cdk-automation', 'dependabot[bot]', 'mergify[bot]'], 55 | secret: 'GITHUB_TOKEN', 56 | }, 57 | autoApproveUpgrades: true, 58 | gitignore: [ 59 | '*.d.ts', 60 | '*.generated.ts', 61 | '*.js', 62 | '**/integ.*.snapshot/asset.*/', 63 | '**/target', 64 | ], 65 | }); 66 | project.deps.addDependency( 67 | '@aws-cdk/integ-tests-alpha@2.121.0-alpha.0', 68 | DependencyType.TEST, 69 | ); 70 | 71 | project.synth(); 72 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # The correct AWS SAM build image based on the runtime of the function will be 2 | # passed as build arg. The default allows to do `docker build .` when testing. 3 | ARG IMAGE=public.ecr.aws/sam/build-provided.al2023 4 | FROM $IMAGE 5 | 6 | ENV RUSTUP_HOME=/usr/local/rustup \ 7 | CARGO_HOME=/usr/local/cargo \ 8 | PATH=/usr/local/cargo/bin:$PATH \ 9 | RUST_VERSION=1.85.1 10 | 11 | # Install Rust 12 | RUN set -eux; \ 13 | arch="$(arch)"; \ 14 | case "${arch##*-}" in \ 15 | x86_64) rustArch='x86_64-unknown-linux-gnu'; rustupSha256='0b2f6c8f85a3d02fde2efc0ced4657869d73fccfce59defb4e8d29233116e6db' ;; \ 16 | aarch64) rustArch='aarch64-unknown-linux-gnu'; rustupSha256='673e336c81c65e6b16dcdede33f4cc9ed0f08bde1dbe7a935f113605292dc800' ;; \ 17 | *) echo >&2 "unsupported architecture: ${arch}"; exit 1 ;; \ 18 | esac; \ 19 | url="https://static.rust-lang.org/rustup/archive/1.26.0/${rustArch}/rustup-init"; \ 20 | curl "$url" -o "rustup-init"; \ 21 | echo "${rustupSha256} *rustup-init" | sha256sum -c -; \ 22 | chmod +x rustup-init; \ 23 | ./rustup-init -y --no-modify-path --profile minimal --default-toolchain $RUST_VERSION --default-host ${rustArch}; \ 24 | rm rustup-init; \ 25 | chmod -R a+w $RUSTUP_HOME $CARGO_HOME; 26 | 27 | # Install Rust tools and targets 28 | RUN set -eux; \ 29 | rustup toolchain install stable; \ 30 | rustup target install x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu; 31 | 32 | RUN --mount=type=cache,target=/usr/local/cargo/registry 33 | RUN mkdir /.cache; \ 34 | chmod a+w /.cache; 35 | 36 | # Install Zig 37 | RUN set -eux; \ 38 | pip3 install ziglang; 39 | 40 | # Install Cargo-Zigbuild 41 | RUN cargo install cargo-zigbuild 42 | 43 | # Install Cross 44 | RUN cargo install cross --git https://github.com/cross-rs/cross 45 | 46 | RUN chmod -R a+w $RUSTUP_HOME $CARGO_HOME 47 | 48 | CMD [ "cargo-zigbuild", "--version" ] 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Amazon Lambda Rust Library 2 | 3 | 4 | 5 | --- 6 | 7 | ![cdk-constructs: Experimental](https://img.shields.io/badge/cdk--constructs-experimental-important.svg?style=for-the-badge) 8 | 9 | > The APIs of higher level constructs in this module are experimental and under active development. 10 | > They are subject to non-backward compatible changes or removal in any future version. These are 11 | > not subject to the [Semantic Versioning](https://semver.org/) model and breaking changes will be 12 | > announced in the release notes. This means that while you may use them, you may need to update 13 | > your source code when upgrading to a newer version of this package. 14 | 15 | --- 16 | 17 | 18 | 19 | This library provides constructs for Rust Lambda functions. 20 | 21 | The `rust.RustFunction` construct creates a Lambda function with automatic building and bundling of Rust code. 22 | 23 | To use this module you will either need to have `cargo-lambda` installed or Docker installed. 24 | 25 | See [Local Bundling/Docker Bundling](#local-bundling) for more information. 26 | 27 | ## Rust Function 28 | 29 | ```plaintext 30 | . 31 | ├── my-construct.ts 32 | ├── myconstruct.my_function 33 | │ ├── Cargo.toml 34 | │ └── src 35 | │ └── main.rs 36 | ``` 37 | 38 | By default, the construct will use the name of the defining file and the construct's id to look up the entry file by following this pattern `defining-file.id/Cargo.toml`. In `my-construct.ts` above we have: 39 | 40 | ```typescript 41 | const myFunction = new rust.RustFunction(this, "my_function"); 42 | ``` 43 | 44 | Alternatively, `entry` and `binaryName` properties can be specified to override this default behavior. If no `binaryName` argument is passed in, it will default to the package name as defined in the main `Cargo.toml`. 45 | 46 | ```typescript 47 | new rust.RustFunction(this, "my_function1", { 48 | entry: '/path/to/directory/with/Cargo.toml', 49 | binaryName: 'my_function' 50 | }); 51 | 52 | // You can also specify entry path directory which will contain your Cargo.toml file 53 | new rust.RustFunction(this, "my_function2", { 54 | entry: '/path/to/directory', 55 | binaryName: 'my_function' 56 | }); 57 | ``` 58 | 59 | For more complex project structure combining multiple Rust Lambda function, the construct offer the ability to use Binaries or [Workspaces](https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html) defined in your Cargo project. 60 | 61 | Given a sample project structure: 62 | 63 | ```plaintext 64 | . 65 | ├── Cargo.toml 66 | ├── my_lambda_1 67 | │ ├── Cargo.toml 68 | │ └── src 69 | │ └── main.rs 70 | └── my_lambda_2 71 | ├── Cargo.toml 72 | └── src 73 | └── main.rs 74 | ``` 75 | 76 | And with a given `Cargo.toml`: 77 | 78 | ```toml 79 | [workspace] 80 | members = [ 81 | "my_lambda_1", 82 | "my_lambda_2" 83 | ] 84 | ``` 85 | 86 | Rust functions can be declared using `binaryName` property: 87 | 88 | ```typescript 89 | new rust.RustFunction(this, 'FirstFunction', { 90 | binaryName: 'my_lambda_1', 91 | }); 92 | 93 | new rust.RustFunction(this, 'SecondFunction', { 94 | binaryName: 'my_lambda_2', 95 | }); 96 | ``` 97 | 98 | ## How It Works 99 | 100 | When bundling the code, the `rust.RustFunction` runs will build and bundle your binary by passing `--release` and `--target` flags to the Cargo build command, so it compiles for a Lambda environment - which defaults to the **aarch64-unknown-linux-gnu** target. 101 | 102 | ### Use `cross` for Deployment 103 | 104 | If you instead prefer to use [Docker](https://www.docker.com/get-started) and [Cross](https://github.com/rust-embedded/cross) for deployment, as outlined 105 | in the [official AWS docs](https://docs.aws.amazon.com/sdk-for-rust/latest/dg/lambda.html), you can define the `packageManager` property: 106 | 107 | ```typescript 108 | new rust.RustFunction(this, 'FirstFunction', { 109 | binaryName: 'my_lambda_1', 110 | bundling: { 111 | packageManagerType: rust.PackageManagerType.CROSS 112 | } 113 | }); 114 | ``` 115 | 116 | ## Customizing the underlying Lambda function 117 | 118 | All properties of `lambda.Function` can be used to customize the underlying `lambda.Function`. 119 | 120 | See also the [AWS Lambda construct library](https://github.com/aws/aws-cdk/tree/main/packages/aws-cdk-lib/aws-lambda). 121 | 122 | ## Local Bundling 123 | 124 | By default, Cargo Lambda uses the [Zig toolchain](https://crates.io/crates/cargo-zigbuild) to cross compile your code. 125 | This is the most convenient cross compilation mechanism because it comes built in, and it works for the majority of use cases. 126 | Any pure Rust Lambda function should compile correctly with this toolchain. 127 | 128 | By default, Construct will compile the code for Linux X86-64 architectures, but you can compile for ARM-64 architectures by providing the right property if needed: 129 | 130 | ```typescript 131 | import { Architecture } from 'aws-cdk-lib/aws-lambda'; 132 | 133 | new rust.RustFunction(this, 'my_function', { 134 | architecture: Architecture.ARM_64 135 | }); 136 | ``` 137 | 138 | If bundling is made locally for **ARM-64** or **X86-64**, make sure to install the dedicated target: 139 | 140 | ```bash 141 | rustup target add aarch64-unknown-linux-gnu 142 | rustup target add x86_64-unknown-linux-gnu 143 | ``` 144 | 145 | ## Customizing Docker bundling 146 | 147 | To force bundling in a Docker container even if `Zig toolchain` is available in your environment, set `bundling.forceDockerBundling` to true. 148 | 149 | Use `bundling.environment` to define environments variables when `cargo` runs: 150 | 151 | ```typescript 152 | new rust.RustFunction(this, 'my_function', { 153 | bundling: { 154 | environment: { 155 | HELLO: 'world' 156 | }, 157 | }, 158 | }); 159 | ``` 160 | 161 | Use the `bundling.buildArgs` to pass build arguments to `cargo`: 162 | 163 | ```typescript 164 | new rust.RustFunction(this, 'my_function', { 165 | bundling: { 166 | extraBuildArgs: ['--all-features'], 167 | }, 168 | }); 169 | ``` 170 | 171 | Use the `bundling.dockerImage` to use a custom Docker bundling image: 172 | 173 | ```typescript 174 | new rust.RustFunction(this, 'my_function', { 175 | bundling: { 176 | dockerImage: DockerImage.fromBuild('/path/to/Dockerfile'), 177 | }, 178 | }); 179 | ``` 180 | 181 | You can set additional Docker options to configure the build environment: 182 | 183 | ```typescript 184 | new rust.RustFunction(this, 'my_function', { 185 | bundling: { 186 | network: 'host', 187 | securityOpt: 'no-new-privileges', 188 | user: 'user:group', 189 | volumesFrom: ['777f7dc92da7'], 190 | volumes: [{ hostPath: '/host-path', containerPath: '/container-path' }], 191 | }, 192 | }); 193 | ``` 194 | 195 | ## Command hooks 196 | 197 | It is possible to run additional commands by specifying the commandHooks property. 198 | 199 | The following hooks are available: 200 | 201 | - **beforeBundling**: runs before all bundling commands 202 | - **afterBundling**: runs after all bundling commands 203 | 204 | They all receive the directory containing the lock file (inputDir) and the directory where the bundled asset will be output (outputDir). 205 | They must return an array of commands to run. Commands are chained with &&. 206 | 207 | The commands will run in the environment in which bundling occurs: inside the container for Docker bundling or on the host OS for local bundling. 208 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@cdklabs/aws-lambda-rust", 3 | "repository": { 4 | "type": "git", 5 | "url": "https://github.com/cdklabs/aws-lambda-rust.git" 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 | "integ": "npx projen integ", 18 | "integ:update": "npx projen integ:update", 19 | "package": "npx projen package", 20 | "package-all": "npx projen package-all", 21 | "package:dotnet": "npx projen package:dotnet", 22 | "package:java": "npx projen package:java", 23 | "package:js": "npx projen package:js", 24 | "package:python": "npx projen package:python", 25 | "post-compile": "npx projen post-compile", 26 | "post-upgrade": "npx projen post-upgrade", 27 | "pre-compile": "npx projen pre-compile", 28 | "release": "npx projen release", 29 | "rosetta:extract": "npx projen rosetta:extract", 30 | "test": "npx projen test", 31 | "test:watch": "npx projen test:watch", 32 | "unbump": "npx projen unbump", 33 | "upgrade": "npx projen upgrade", 34 | "upgrade-cdklabs-projen-project-types": "npx projen upgrade-cdklabs-projen-project-types", 35 | "upgrade-dev-deps": "npx projen upgrade-dev-deps", 36 | "watch": "npx projen watch", 37 | "projen": "npx projen" 38 | }, 39 | "author": { 40 | "name": "Amazon Web Services", 41 | "email": "aws-cdk-dev@amazon.com", 42 | "organization": true 43 | }, 44 | "devDependencies": { 45 | "@aws-cdk/integ-runner": "latest", 46 | "@aws-cdk/integ-tests-alpha": "2.121.0-alpha.0", 47 | "@types/jest": "^29", 48 | "@types/node": "^18", 49 | "@typescript-eslint/eslint-plugin": "^8", 50 | "@typescript-eslint/parser": "^8", 51 | "aws-cdk-lib": "2.121.0", 52 | "cdklabs-projen-project-types": "^0.3.1", 53 | "commit-and-tag-version": "^12", 54 | "constructs": "10.0.5", 55 | "eslint": "^9", 56 | "eslint-config-prettier": "^9.1.0", 57 | "eslint-import-resolver-typescript": "^3.10.1", 58 | "eslint-plugin-import": "^2.31.0", 59 | "eslint-plugin-prettier": "^5.4.1", 60 | "jest": "^29", 61 | "jest-junit": "^16", 62 | "jsii": "~5.3.0", 63 | "jsii-diff": "^1.112.0", 64 | "jsii-docgen": "^10.5.0", 65 | "jsii-pacmak": "^1.112.0", 66 | "jsii-rosetta": "^5.8.9", 67 | "prettier": "^3.5.3", 68 | "projen": "^0.92.9", 69 | "ts-jest": "^29", 70 | "ts-node": "^10.9.2", 71 | "typescript": "~5.8.3" 72 | }, 73 | "peerDependencies": { 74 | "aws-cdk-lib": "^2.121.0", 75 | "constructs": "^10.0.5" 76 | }, 77 | "dependencies": { 78 | "toml": "^3.0.0" 79 | }, 80 | "bundledDependencies": [ 81 | "toml" 82 | ], 83 | "keywords": [ 84 | "cdk" 85 | ], 86 | "main": "lib/index.js", 87 | "license": "Apache-2.0", 88 | "publishConfig": { 89 | "access": "public" 90 | }, 91 | "version": "0.0.0", 92 | "jest": { 93 | "coverageProvider": "v8", 94 | "testMatch": [ 95 | "/@(src|test)/**/*(*.)@(spec|test).ts?(x)", 96 | "/@(src|test)/**/__tests__/**/*.ts?(x)", 97 | "/@(projenrc)/**/*(*.)@(spec|test).ts?(x)", 98 | "/@(projenrc)/**/__tests__/**/*.ts?(x)" 99 | ], 100 | "clearMocks": true, 101 | "collectCoverage": true, 102 | "coverageReporters": [ 103 | "json", 104 | "lcov", 105 | "clover", 106 | "cobertura", 107 | "text" 108 | ], 109 | "coverageDirectory": "coverage", 110 | "coveragePathIgnorePatterns": [ 111 | "/node_modules/" 112 | ], 113 | "testPathIgnorePatterns": [ 114 | "/node_modules/" 115 | ], 116 | "watchPathIgnorePatterns": [ 117 | "/node_modules/" 118 | ], 119 | "reporters": [ 120 | "default", 121 | [ 122 | "jest-junit", 123 | { 124 | "outputDirectory": "test-reports" 125 | } 126 | ] 127 | ], 128 | "transform": { 129 | "^.+\\.[t]sx?$": [ 130 | "ts-jest", 131 | { 132 | "tsconfig": "tsconfig.dev.json" 133 | } 134 | ] 135 | } 136 | }, 137 | "types": "lib/index.d.ts", 138 | "stability": "experimental", 139 | "jsii": { 140 | "outdir": "dist", 141 | "targets": { 142 | "java": { 143 | "package": "io.github.cdklabs.awslambdarust", 144 | "maven": { 145 | "groupId": "io.github.cdklabs", 146 | "artifactId": "aws-lambda-rust" 147 | } 148 | }, 149 | "python": { 150 | "distName": "cdklabs.aws-lambda-rust", 151 | "module": "cdklabs.aws_lambda_rust" 152 | }, 153 | "dotnet": { 154 | "namespace": "Cdklabs.AwsLambdaRust", 155 | "packageId": "Cdklabs.AwsLambdaRust" 156 | } 157 | }, 158 | "tsc": { 159 | "outDir": "lib", 160 | "rootDir": "src" 161 | } 162 | }, 163 | "//": "~~ Generated by projen. To modify, edit .projenrc.ts and run \"npx projen\"." 164 | } 165 | -------------------------------------------------------------------------------- /rosetta/default.ts-fixture: -------------------------------------------------------------------------------- 1 | // Fixture with packages imported, but nothing else 2 | import { Construct } from 'constructs'; 3 | import { Stack, DockerImage } from 'aws-cdk-lib'; 4 | import * as lambda from 'aws-cdk-lib/aws-lambda'; 5 | import * as rust from '@cdklabs/aws-lambda-rust'; 6 | 7 | class Fixture extends Stack { 8 | constructor(scope: Construct, id: string) { 9 | super(scope, id); 10 | 11 | /// here 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/bundling.ts: -------------------------------------------------------------------------------- 1 | import * as os from 'os'; 2 | import * as path from 'path'; 3 | import { Architecture, AssetCode, Code, Runtime } from 'aws-cdk-lib/aws-lambda'; 4 | import { 5 | AssetHashType, 6 | AssetStaging, 7 | BundlingFileAccess, 8 | BundlingOptions as CdkBundlingOptions, 9 | DockerImage, 10 | DockerVolume, 11 | ILocalBundling, 12 | } from 'aws-cdk-lib/core'; 13 | import { PackageManager } from './package-manager'; 14 | import { BundlingOptions, LogLevel, PackageManagerType } from './types'; 15 | import { 16 | checkInstalledTarget, 17 | exec, 18 | getBinaryName, 19 | hasMultipleBinaries, 20 | isWorkspace, 21 | } from './util'; 22 | 23 | /** 24 | * Bundling properties 25 | */ 26 | export interface BundlingProps extends BundlingOptions { 27 | /** 28 | * Path to folder containing the Cargo.toml file. 29 | */ 30 | readonly entry: string; 31 | 32 | /** 33 | * The runtime of the lambda function 34 | */ 35 | readonly runtime: Runtime; 36 | 37 | /** 38 | * The system architecture of the lambda function 39 | * 40 | * @default - Architecture.X86_64 41 | */ 42 | readonly architecture: Architecture; 43 | 44 | /** 45 | * Build only the specified binary 46 | * 47 | * @default Build all binaries 48 | */ 49 | readonly binaryName?: string; 50 | 51 | /** 52 | * Which option to use to copy the source files to the docker container and output files back 53 | * @default - BundlingFileAccess.BIND_MOUNT 54 | */ 55 | readonly bundlingFileAccess?: BundlingFileAccess; 56 | } 57 | 58 | /** 59 | * Bundling with esbuild 60 | */ 61 | export class Bundling implements CdkBundlingOptions { 62 | public static X86_64__TARGET: string = 'x86_64-unknown-linux-gnu'; 63 | public static ARM_TARGET: string = 'aarch64-unknown-linux-gnu'; 64 | 65 | /** 66 | * Cargo bundled Lambda asset code 67 | */ 68 | public static bundle(options: BundlingProps): AssetCode { 69 | return Code.fromAsset(path.dirname(options.entry), { 70 | assetHash: options.assetHash, 71 | assetHashType: options.assetHash 72 | ? AssetHashType.CUSTOM 73 | : AssetHashType.OUTPUT, 74 | bundling: new Bundling(options), 75 | }); 76 | } 77 | 78 | public readonly image: DockerImage; 79 | public readonly entrypoint?: string[]; 80 | public readonly command: string[]; 81 | public readonly volumes?: DockerVolume[]; 82 | public readonly volumesFrom?: string[]; 83 | public readonly environment?: { [key: string]: string }; 84 | public readonly workingDirectory?: string; 85 | public readonly user?: string; 86 | public readonly securityOpt?: string; 87 | public readonly network?: string; 88 | public readonly local?: ILocalBundling; 89 | public readonly bundlingFileAccess?: BundlingFileAccess; 90 | 91 | private readonly packageManager: PackageManager; 92 | private readonly target: string; 93 | 94 | constructor(private readonly props: BundlingProps) { 95 | const packageManagerType = 96 | props.packageManagerType ?? PackageManagerType.CARGO_ZIGBUILD; 97 | this.packageManager = PackageManager.fromType(packageManagerType); 98 | this.target = 99 | this.props.target && isValidTarget(this.props.target) 100 | ? this.props.target 101 | : toTarget(this.props.architecture); 102 | 103 | const buildArgs = ['--release', '--color', 'always']; 104 | if (props.extraBuildArgs) { 105 | buildArgs.push(...props.extraBuildArgs); 106 | } 107 | // Docker bundling 108 | const shouldBuildImage = 109 | props.forceDockerBundling || 110 | !this.packageManager.runLocally || 111 | !checkInstalledTarget(toTarget(this.props.architecture)); 112 | this.image = shouldBuildImage 113 | ? (props.dockerImage ?? 114 | DockerImage.fromBuild(path.join(__dirname, '../'), { 115 | buildArgs: { 116 | ...(props.buildArgs ?? {}), 117 | // If runtime isn't passed use regional default, lowest common denominator is node18 118 | IMAGE: props.runtime.bundlingImage.image, 119 | }, 120 | platform: props.architecture.dockerPlatform, 121 | })) 122 | : DockerImage.fromRegistry('dummy'); // Do not build if we don't need to 123 | 124 | const bundlingCommand = this.createBundlingCommand({ 125 | inputDir: AssetStaging.BUNDLING_INPUT_DIR, 126 | outputDir: AssetStaging.BUNDLING_OUTPUT_DIR, 127 | buildArgs: buildArgs, 128 | buildRunner: this.packageManager.runBuildCommand(), 129 | osPlatform: 'linux', // linux docker image 130 | }); 131 | this.command = props.command ?? ['bash', '-c', bundlingCommand]; 132 | this.environment = props.environment; 133 | this.workingDirectory = props.workingDirectory; 134 | this.entrypoint = props.entrypoint; 135 | this.volumes = props.volumes; 136 | this.volumesFrom = props.volumesFrom; 137 | this.user = props.user; 138 | this.securityOpt = props.securityOpt; 139 | this.network = props.network; 140 | this.bundlingFileAccess = props.bundlingFileAccess; 141 | 142 | // Local bundling 143 | if (!props.forceDockerBundling) { 144 | // only if Docker is not forced 145 | this.local = this.getLocalBundlingProvider(); 146 | } 147 | } 148 | 149 | public createBundlingCommand(options: BundlingCommandOptions): string { 150 | const pathJoin = osPathJoin(options.osPlatform); 151 | const osPlatform = os.platform(); 152 | let relativeManifestPath = options.inputDir 153 | ? pathJoin( 154 | options.inputDir, 155 | path.relative(path.dirname(this.props.entry), this.props.entry), 156 | ) 157 | : this.props.entry; 158 | let binaryName; 159 | 160 | const buildCommand: string[] = [ 161 | options.buildRunner, 162 | `--manifest-path=${relativeManifestPath}`, 163 | `--target ${this.target}`, 164 | ...options.buildArgs, 165 | ]; 166 | 167 | if (this.props.binaryName) { 168 | binaryName = this.props.binaryName; 169 | } else if ( 170 | isWorkspace(this.props.entry) || 171 | hasMultipleBinaries(this.props.entry) 172 | ) { 173 | throw new Error( 174 | 'Your Cargo project is a workspace or contains multiple binaries, use the property `binaryName` to specify the binary to use.', 175 | ); 176 | } else { 177 | binaryName = getBinaryName(this.props.entry); 178 | } 179 | 180 | if (!binaryName) { 181 | throw new Error( 182 | 'Your Cargo project is missing the package name or a [[bin]] section, use the property `binaryName` to specify the binary to use', 183 | ); 184 | } 185 | buildCommand.push('--bin', binaryName); 186 | 187 | // Features 188 | if (this.props.features) { 189 | buildCommand.push('--features', this.props.features.join(',')); 190 | } 191 | 192 | // Log level 193 | if (this.props.logLevel && this.props.logLevel == LogLevel.SILENT) { 194 | buildCommand.push('--silent'); 195 | } else if (this.props.logLevel && this.props.logLevel == LogLevel.VERBOSE) { 196 | buildCommand.push('--verbose'); 197 | } 198 | 199 | // Move target file to destination 200 | const sourceBootstrap = pathJoin( 201 | path.dirname(relativeManifestPath), 202 | 'target', 203 | this.target, 204 | 'release', 205 | binaryName, 206 | ); 207 | const targetBootstrap = pathJoin(options.outputDir, 'bootstrap'); 208 | const moveCommand: string = 209 | osPlatform === 'win32' 210 | ? [ 211 | 'powershell', 212 | '-command', 213 | 'Move-Item', 214 | '-Path', 215 | sourceBootstrap, 216 | '-Destination', 217 | targetBootstrap, 218 | ] 219 | .filter((c) => !!c) 220 | .join(' ') 221 | : ['mv', sourceBootstrap, targetBootstrap].filter((c) => !!c).join(' '); 222 | 223 | return chain([ 224 | ...(this.props.commandHooks?.beforeBundling( 225 | options.inputDir, 226 | options.outputDir, 227 | ) ?? []), 228 | buildCommand.filter((c) => !!c).join(' '), 229 | moveCommand, 230 | ...(this.props.commandHooks?.afterBundling( 231 | options.inputDir, 232 | options.outputDir, 233 | ) ?? []), 234 | ]); 235 | } 236 | 237 | private getLocalBundlingProvider(): ILocalBundling { 238 | const osPlatform = os.platform(); 239 | const projectRoot = path.dirname(this.props.entry); 240 | const createLocalCommand = (outputDir: string, buildArgs: string[]) => 241 | this.createBundlingCommand({ 242 | inputDir: '', 243 | outputDir, 244 | buildRunner: this.packageManager.runBuildCommand(), 245 | osPlatform, 246 | buildArgs, 247 | }); 248 | const environment = this.props.environment ?? {}; 249 | const cwd = projectRoot; 250 | 251 | return { 252 | tryBundle(outputDir: string) { 253 | if ( 254 | !PackageManager.fromType(PackageManagerType.CARGO_ZIGBUILD) 255 | .runLocally && 256 | !PackageManager.fromType(PackageManagerType.CROSS).runLocally 257 | ) { 258 | process.stderr.write( 259 | 'No package manager cannot run locally. Switching to Docker bundling.\n', 260 | ); 261 | return false; 262 | } 263 | 264 | const buildArgs = ['--release', '--color', 'always']; 265 | const localCommand = createLocalCommand(outputDir, buildArgs); 266 | 267 | exec( 268 | osPlatform === 'win32' ? 'cmd' : 'bash', 269 | [osPlatform === 'win32' ? '/c' : '-c', localCommand], 270 | { 271 | env: { ...process.env, ...environment }, 272 | stdio: [ 273 | // show output 274 | 'ignore', // ignore stdio 275 | process.stderr, // redirect stdout to stderr 276 | 'inherit', // inherit stderr 277 | ], 278 | cwd, 279 | windowsVerbatimArguments: osPlatform === 'win32', 280 | }, 281 | ); 282 | 283 | return true; 284 | }, 285 | }; 286 | } 287 | } 288 | 289 | /** 290 | * Command options for bundling a Lambda asset 291 | */ 292 | interface BundlingCommandOptions { 293 | readonly inputDir: string; 294 | readonly outputDir: string; 295 | readonly buildRunner: string; 296 | readonly buildArgs: string[]; 297 | readonly osPlatform: NodeJS.Platform; 298 | } 299 | 300 | function chain(commands: string[]): string { 301 | return commands.filter((c) => !!c).join(' && '); 302 | } 303 | 304 | /** 305 | * Validates a target against a list of allowed targets. 306 | * @param target the target to validate. 307 | */ 308 | function isValidTarget(target: string): boolean { 309 | return ( 310 | target.startsWith(Bundling.X86_64__TARGET) || 311 | target.startsWith(Bundling.ARM_TARGET) 312 | ); 313 | } 314 | 315 | /** 316 | * Converts an Architecture to a bundling target 317 | */ 318 | function toTarget(architecture: Architecture): string { 319 | switch (architecture) { 320 | case Architecture.ARM_64: 321 | return Bundling.ARM_TARGET; 322 | default: 323 | return Bundling.X86_64__TARGET; 324 | } 325 | } 326 | 327 | /** 328 | * Platform specific path join 329 | */ 330 | function osPathJoin(platform: NodeJS.Platform) { 331 | return function (...paths: string[]): string { 332 | const joined = path.join(...paths); 333 | // If we are on win32 but need posix style paths 334 | if (os.platform() === 'win32' && platform !== 'win32') { 335 | return joined.replace(/\\/g, '/'); 336 | } 337 | return joined; 338 | }; 339 | } 340 | -------------------------------------------------------------------------------- /src/function.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import { 4 | Architecture, 5 | FunctionOptions, 6 | Runtime, 7 | RuntimeFamily, 8 | Function, 9 | } from 'aws-cdk-lib/aws-lambda'; 10 | import { Construct } from 'constructs'; 11 | import { Bundling } from './bundling'; 12 | import { BundlingOptions } from './types'; 13 | import { callsites } from './util'; 14 | 15 | /** 16 | * Properties for a RustFunction 17 | */ 18 | export interface RustFunctionProps extends FunctionOptions { 19 | /** 20 | * Path to the entry Cargo.toml file. 21 | * 22 | * @default - Derived from the name of the defining file and the construct's id. 23 | * If the `RustsFunction` is defined in `stack.ts` with `my-binary` as id 24 | * (`new RustFunction(this, 'my-binary')`), the construct will look at `stack.my-binary/Cargo.toml` 25 | */ 26 | readonly entry?: string; 27 | 28 | /** 29 | * The name of the binary to build, in case that it's different that the package's name. 30 | * 31 | * @default Build all binaries 32 | */ 33 | readonly binaryName?: string; 34 | 35 | /** 36 | * The runtime environment. Only OS-only runtimes are supported. 37 | * 38 | * @default `Runtime.PROVIDED_AL2023` 39 | */ 40 | readonly runtime?: Runtime; 41 | 42 | /** 43 | * Bundling options 44 | * 45 | * @default - use default bundling options: all 46 | * binaries and packages are bundled. 47 | */ 48 | readonly bundling?: BundlingOptions; 49 | 50 | /** 51 | * The path to the directory containing project Manifest file 52 | * 53 | * @default - the directory containing the `depsLockFilePath` 54 | */ 55 | readonly projectRoot?: string; 56 | } 57 | 58 | /** 59 | * A Rust Lambda function 60 | */ 61 | export class RustFunction extends Function { 62 | constructor(scope: Construct, id: string, props: RustFunctionProps = {}) { 63 | if (props.runtime && props.runtime.family !== RuntimeFamily.OTHER) { 64 | throw new Error('Only `Custom` runtimes are supported.'); 65 | } 66 | 67 | // Entry and defaults 68 | const entry = path.resolve(findEntry(id, props.entry)); 69 | // Handler is not important for Rust function 70 | const handler = 'rust.handler'; 71 | const architecture = props.architecture ?? Architecture.X86_64; 72 | const runtime = props.runtime ?? Runtime.PROVIDED_AL2023; 73 | 74 | super(scope, id, { 75 | ...props, 76 | runtime, 77 | code: Bundling.bundle({ 78 | ...(props.bundling ?? {}), 79 | entry, 80 | runtime, 81 | architecture, 82 | binaryName: props.binaryName, 83 | }), 84 | handler: handler, 85 | }); 86 | } 87 | } 88 | 89 | /** 90 | * Searches for an entry file. Preference order is the following: 91 | * 1. Given entry file 92 | * 2. A Cargo.toml file named as the defining file with id as u (defining-file.id/Cargo.toml) 93 | */ 94 | function findEntry(id: string, entry?: string): string { 95 | if (entry) { 96 | if (!/\Cargo.toml$/.test(entry)) { 97 | // Only Cargo.toml files are supported. 98 | // If the user gave a path to a directory, we'll look for a Cargo.toml file in it. 99 | // If the user gave a path to a file, we'll use it as-is. 100 | entry = path.join(entry, 'Cargo.toml'); 101 | } 102 | 103 | if (!fs.existsSync(entry)) { 104 | throw new Error(`Cannot find manifest file at ${entry}`); 105 | } 106 | 107 | return entry; 108 | } 109 | 110 | const definingFile = findDefiningFile(); 111 | const extname = path.extname(definingFile); 112 | 113 | const manifestFile = definingFile.replace( 114 | new RegExp(`${extname}$`), 115 | `.${id}/Cargo.toml`, 116 | ); 117 | if (fs.existsSync(manifestFile)) { 118 | return manifestFile; 119 | } 120 | 121 | throw new Error('Cannot find manifest file - Cargo.toml'); 122 | } 123 | 124 | /** 125 | * Finds the name of the file where the `RustFunction` is defined 126 | */ 127 | function findDefiningFile(): string { 128 | let definingIndex; 129 | const sites = callsites(); 130 | for (const [index, site] of sites.entries()) { 131 | if (site.getFunctionName() === 'RustFunction') { 132 | // The next site is the site where the RustFunction was created 133 | definingIndex = index + 1; 134 | break; 135 | } 136 | } 137 | 138 | if (!definingIndex || !sites[definingIndex]) { 139 | throw new Error('Cannot find defining file.'); 140 | } 141 | 142 | return sites[definingIndex].getFileName(); 143 | } 144 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './types'; 2 | export * from './function'; 3 | -------------------------------------------------------------------------------- /src/package-manager.ts: -------------------------------------------------------------------------------- 1 | import * as os from 'os'; 2 | import { PackageManagerType } from './types'; 3 | import { canRunLocally } from './util'; 4 | 5 | interface PackageManagerProps { 6 | readonly runCommand: string; 7 | readonly buildCommand: string[]; 8 | readonly crossCompile: boolean; 9 | readonly runLocally: boolean; 10 | readonly type: PackageManagerType; 11 | } 12 | 13 | /** 14 | * A Rust package manager 15 | */ 16 | export class PackageManager { 17 | /** 18 | * Create a package manager from a package manager type. Method will also check if it can be runned locally 19 | * @param type Package manager used 20 | * @returns the right PackageManager for that type 21 | */ 22 | public static fromType(type: PackageManagerType): PackageManager { 23 | let runCommand = 'cargo-zigbuild'; 24 | let buildCommand = ['zigbuild']; 25 | let crossCompile = true; 26 | 27 | if (type === PackageManagerType.CROSS) { 28 | runCommand = 'cross'; 29 | buildCommand = ['build']; 30 | } 31 | return new PackageManager({ 32 | runCommand: runCommand, 33 | buildCommand: buildCommand, 34 | runLocally: canRunLocally(runCommand), 35 | crossCompile: crossCompile, 36 | type, 37 | }); 38 | } 39 | 40 | public readonly runCommand: string; 41 | public readonly buildCommand: string[]; 42 | public readonly runLocally: boolean; 43 | public readonly crossCompile: boolean; 44 | public readonly type: PackageManagerType; 45 | 46 | constructor(props: PackageManagerProps) { 47 | this.runCommand = props.runCommand; 48 | this.buildCommand = props.buildCommand; 49 | this.crossCompile = props.crossCompile; 50 | this.runLocally = props.runLocally; 51 | this.type = props.type; 52 | } 53 | 54 | public runBuildCommand(): string { 55 | return [ 56 | os.platform() === 'win32' ? `${this.runCommand}.cmd` : this.runCommand, 57 | ...this.buildCommand, 58 | ].join(' '); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AssetHashType, 3 | BundlingFileAccess, 4 | DockerImage, 5 | DockerRunOptions, 6 | } from 'aws-cdk-lib/core'; 7 | 8 | /** 9 | * Bundling options 10 | */ 11 | export interface BundlingOptions extends DockerRunOptions { 12 | /** 13 | * Cross compilation target environment for the generated binary. 14 | */ 15 | readonly target?: string; 16 | 17 | /** 18 | * A list of features to activate when compiling Rust code. 19 | */ 20 | readonly features?: string[]; 21 | 22 | /** 23 | * The type of package manager to use 24 | * 25 | * @default - PackageManagerType.CARGO_ZIGBUILD 26 | */ 27 | readonly packageManagerType?: PackageManagerType; 28 | 29 | /** 30 | * Force bundling in a Docker container even if local bundling is 31 | * possible. 32 | * 33 | * @default - false 34 | */ 35 | readonly forceDockerBundling?: boolean; 36 | 37 | /** 38 | * A custom bundling Docker image. 39 | * 40 | * @default - use the Docker image provided by @aws-cdk/aws-lambda-go-alpha 41 | */ 42 | readonly dockerImage?: DockerImage; 43 | 44 | /** 45 | * Additional arguments that are passed in at build time to package manager. 46 | * 47 | * ## Examples 48 | * 49 | * - `--all-features` 50 | * - `--no-default-features` 51 | */ 52 | readonly extraBuildArgs?: string[]; 53 | 54 | /** 55 | * Build arguments to pass when building the bundling image. 56 | * 57 | * @default - no build arguments are passed 58 | */ 59 | readonly buildArgs?: { [key: string]: string }; 60 | 61 | /** 62 | * Determines how the asset hash is calculated. Assets will 63 | * get rebuilt and uploaded only if their hash has changed. 64 | * 65 | * If the asset hash is set to `OUTPUT` (default), the hash is calculated 66 | * after bundling. This means that any change in the output will cause 67 | * the asset to be invalidated and uploaded. Bear in mind that the 68 | * rust binary that is output can be different depending on the target 69 | * that it was compiled for. 70 | * 71 | * If the asset hash is set to `SOURCE`, then only changes to the source 72 | * directory will cause the asset to rebuild. If your go project has multiple 73 | * Lambda functions this means that an update to any one function could cause 74 | * all the functions to be rebuilt and uploaded. 75 | * 76 | * @default - AssetHashType.OUTPUT. If `assetHash` is also specified, 77 | * the default is `CUSTOM`. 78 | * 79 | * @default AssetHashType.CUSTOM 80 | */ 81 | readonly assetHashType?: AssetHashType; 82 | 83 | /** 84 | * Specify a custom hash for this asset. If `assetHashType` is set it must 85 | * be set to `AssetHashType.CUSTOM`. For consistency, this custom hash will 86 | * be SHA256 hashed and encoded as hex. The resulting hash will be the asset 87 | * hash. 88 | * 89 | * NOTE: the hash is used in order to identify a specific revision of the asset, and 90 | * used for optimizing and caching deployment activities related to this asset such as 91 | * packaging, uploading to Amazon S3, etc. If you chose to customize the hash, you will 92 | * need to make sure it is updated every time the asset changes, or otherwise it is 93 | * possible that some deployments will not be invalidated. 94 | * 95 | * @default - based on `assetHashType` 96 | */ 97 | readonly assetHash?: string; 98 | 99 | /** 100 | * Command hooks 101 | * 102 | * @default - do not run additional commands 103 | */ 104 | readonly commandHooks?: ICommandHooks; 105 | 106 | /** 107 | * Which option to use to copy the source files to the docker container and output files back 108 | * @default - BundlingFileAccess.BIND_MOUNT 109 | */ 110 | readonly bundlingFileAccess?: BundlingFileAccess; 111 | 112 | /** 113 | * Log level for cargo. 114 | * 115 | * @default LogLevel.WARNING 116 | */ 117 | readonly logLevel?: LogLevel; 118 | } 119 | 120 | /** 121 | * Command hooks 122 | * 123 | * These commands will run in the environment in which bundling occurs: inside 124 | * the container for Docker bundling or on the host OS for local bundling. 125 | * 126 | * Commands are chained with `&&`. 127 | * 128 | * The following example (specified in TypeScript) copies a file from the input 129 | * directory to the output directory to include it in the bundled asset: 130 | * 131 | * ```text 132 | * afterBundling(inputDir: string, outputDir: string): string[]{ 133 | * return [`cp ${inputDir}/my-binary.node ${outputDir}`]; 134 | * } 135 | * ``` 136 | */ 137 | export interface ICommandHooks { 138 | /** 139 | * Returns commands to run before bundling. 140 | * 141 | * Commands are chained with `&&`. 142 | */ 143 | beforeBundling(inputDir: string, outputDir: string): string[]; 144 | 145 | /** 146 | * Returns commands to run after bundling. 147 | * 148 | * Commands are chained with `&&`. 149 | */ 150 | afterBundling(inputDir: string, outputDir: string): string[]; 151 | } 152 | 153 | export enum PackageManagerType { 154 | CARGO_ZIGBUILD, 155 | CROSS, 156 | } 157 | 158 | /** 159 | * Log levels for cargo commands. 160 | */ 161 | export enum LogLevel { 162 | /** Show everything */ 163 | VERBOSE = 'verbose', 164 | /** Show warnings, errors, and an output file summary */ 165 | INFO = 'info', 166 | /** Show nothing */ 167 | SILENT = 'silent', 168 | } 169 | -------------------------------------------------------------------------------- /src/util.ts: -------------------------------------------------------------------------------- 1 | import { spawnSync, SpawnSyncOptions } from 'child_process'; 2 | import { readFileSync } from 'fs'; 3 | import * as toml from 'toml'; 4 | 5 | export interface CallSite { 6 | getThis(): any; 7 | getTypeName(): string; 8 | getFunctionName(): string; 9 | getMethodName(): string; 10 | getFileName(): string; 11 | getLineNumber(): number; 12 | getColumnNumber(): number; 13 | getFunction(): Function; 14 | getEvalOrigin(): string; 15 | isNative(): boolean; 16 | isToplevel(): boolean; 17 | isEval(): boolean; 18 | isConstructor(): boolean; 19 | } 20 | 21 | export interface Package { 22 | name: string; 23 | } 24 | 25 | /** 26 | * Base layout of a Rust manifest file 27 | */ 28 | export interface TomlProps { 29 | readonly package?: Package; 30 | readonly workspace?: { 31 | members: string[]; 32 | }; 33 | readonly bin?: Package[]; 34 | } 35 | 36 | /** 37 | * Get Manifest binary name 38 | * @param entry the Manifest file path 39 | */ 40 | export function getBinaryName(entry: string) { 41 | try { 42 | const contents = readFileSync(entry, 'utf8'); 43 | let data: TomlProps = toml.parse(contents); 44 | if (data.bin && data.bin.length == 1) { 45 | return data.bin[0].name; 46 | } 47 | return data.package?.name; 48 | } catch (err) { 49 | throw new Error( 50 | `Unable to parse Manifest file \`${entry}\`\n` + `${err}\n`, 51 | ); 52 | } 53 | } 54 | 55 | export function hasMultipleBinaries(entry: string) { 56 | try { 57 | const contents = readFileSync(entry, 'utf8'); 58 | let data: TomlProps = toml.parse(contents); 59 | return data.bin && data.bin.length > 1; 60 | } catch (err) { 61 | throw new Error( 62 | `Unable to parse Manifest file \`${entry}\`\n` + `${err}\n`, 63 | ); 64 | } 65 | } 66 | 67 | /** 68 | * Check if the given Manifest contains multiple workspaces 69 | * @param entry the Manifest file path 70 | */ 71 | export function isWorkspace(entry: string) { 72 | try { 73 | const contents = readFileSync(entry, 'utf8'); 74 | let data: TomlProps = toml.parse(contents); 75 | return ( 76 | data.workspace && 77 | data.workspace.members && 78 | data.workspace.members.length > 0 79 | ); 80 | } catch (err) { 81 | throw new Error( 82 | `Unable to parse Manifest file \`${entry}\`\n` + `${err}\n`, 83 | ); 84 | } 85 | } 86 | 87 | /** 88 | * Spawn sync with error handling 89 | */ 90 | export function exec(cmd: string, args: string[], options?: SpawnSyncOptions) { 91 | const proc = spawnSync(cmd, args, options); 92 | 93 | if (proc.error) { 94 | throw proc.error; 95 | } 96 | 97 | if (proc.status !== 0) { 98 | if (proc.stdout || proc.stderr) { 99 | throw new Error( 100 | `[Status ${proc.status}] stdout: ${proc.stdout 101 | ?.toString() 102 | .trim()}\n\n\nstderr: ${proc.stderr?.toString().trim()}`, 103 | ); 104 | } 105 | throw new Error(`${cmd} exited with status ${proc.status}`); 106 | } 107 | 108 | return proc; 109 | } 110 | 111 | /** 112 | * Check if an architecture target is installed. 113 | * @param target The target to test 114 | */ 115 | export function checkInstalledTarget(target: string) { 116 | const proc = spawnSync('rustup', ['target', 'list', '--installed']); 117 | if ( 118 | proc && 119 | proc.status === 0 && 120 | !proc.error && 121 | proc.stdout.toString().includes(target) 122 | ) { 123 | return true; 124 | } 125 | return false; 126 | } 127 | 128 | /** 129 | * Check if a command can be run locally. 130 | * @param cmd The command to execute 131 | */ 132 | export function canRunLocally(cmd: string) { 133 | try { 134 | const proc = spawnSync(cmd, ['--version']); 135 | if (proc.status === 0 && !proc.error) { 136 | return true; 137 | } 138 | return false; 139 | } catch (err) { 140 | return false; 141 | } 142 | } 143 | 144 | /** 145 | * Get callsites from the V8 stack trace API 146 | * 147 | * https://github.com/sindresorhus/callsites 148 | */ 149 | export function callsites(): CallSite[] { 150 | const _prepareStackTrace = Error.prepareStackTrace; 151 | Error.prepareStackTrace = (_, stack) => stack; 152 | const stack = new Error().stack?.slice(1); 153 | Error.prepareStackTrace = _prepareStackTrace; 154 | return stack as unknown as CallSite[]; 155 | } 156 | -------------------------------------------------------------------------------- /test/docker.test.ts: -------------------------------------------------------------------------------- 1 | import { spawnSync } from 'child_process'; 2 | import * as path from 'path'; 3 | 4 | const docker = process.env.CDK_DOCKER ?? 'docker'; 5 | 6 | beforeAll(() => { 7 | const process = spawnSync( 8 | docker, 9 | ['build', '-t', 'cargo-builder', path.join(__dirname, '../')], 10 | { stdio: 'inherit' }, 11 | ); 12 | expect(process.error).toBeUndefined(); 13 | expect(process.status).toBe(0); 14 | }); 15 | 16 | test('cargo is available', () => { 17 | const proc = spawnSync(docker, [ 18 | 'run', 19 | 'cargo-builder', 20 | 'cargo', 21 | '--version', 22 | ]); 23 | expect(proc.status).toEqual(0); 24 | }); 25 | 26 | test('cargo-zigbuild is available', () => { 27 | const proc = spawnSync(docker, [ 28 | 'run', 29 | 'cargo-builder', 30 | 'cargo-zigbuild', 31 | '--version', 32 | ]); 33 | expect(proc.status).toEqual(0); 34 | }); 35 | 36 | test('cross is available', () => { 37 | const proc = spawnSync(docker, [ 38 | 'run', 39 | 'cargo-builder', 40 | 'cross', 41 | '--version', 42 | ]); 43 | expect(proc.status).toEqual(0); 44 | }); 45 | 46 | test('can package manager install with non root user', () => { 47 | const proc = spawnSync(docker, [ 48 | 'run', 49 | '-u', 50 | '1000:1000', 51 | 'cargo-builder', 52 | 'bash', 53 | '-c', 54 | [ 55 | 'mkdir /tmp/test', 56 | 'cd /tmp/test', 57 | 'cargo new sample', 58 | 'cd sample', 59 | 'cargo add aws-config', 60 | ].join(' && '), 61 | ]); 62 | expect(proc.status).toEqual(0); 63 | }); 64 | 65 | test('cache folders have the right permissions', () => { 66 | const proc = spawnSync(docker, [ 67 | 'run', 68 | 'cargo-builder', 69 | 'bash', 70 | '-c', 71 | "stat -c '%a' /usr/local/cargo/bin", 72 | ]); 73 | expect(proc.stdout.toString()).toMatch('777'); 74 | }); 75 | -------------------------------------------------------------------------------- /test/function.test.binary1/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sample-rust" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "binary1" 8 | 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | 11 | [dependencies] 12 | -------------------------------------------------------------------------------- /test/function.test.binary1/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /test/function.test.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import { Stack } from 'aws-cdk-lib'; 3 | import { Match, Template } from 'aws-cdk-lib/assertions'; 4 | import { CodeConfig, Runtime } from 'aws-cdk-lib/aws-lambda'; 5 | import { RustFunction } from '../lib'; 6 | import { Bundling } from '../lib/bundling'; 7 | import * as util from '../lib/util'; 8 | 9 | jest.mock('../lib/bundling', () => { 10 | return { 11 | Bundling: { 12 | bundle: jest.fn().mockReturnValue({ 13 | bind: (): CodeConfig => { 14 | return { 15 | s3Location: { 16 | bucketName: 'my-bucket', 17 | objectKey: 'my-key', 18 | }, 19 | }; 20 | }, 21 | bindToResource: () => { 22 | return; 23 | }, 24 | }), 25 | }, 26 | }; 27 | }); 28 | 29 | let stack: Stack; 30 | beforeEach(() => { 31 | stack = new Stack(); 32 | jest.clearAllMocks(); 33 | jest.spyOn(util, 'isWorkspace').mockReturnValue(false); 34 | jest.spyOn(util, 'hasMultipleBinaries').mockReturnValue(false); 35 | jest.spyOn(util, 'getBinaryName').mockReturnValue('testBin'); 36 | }); 37 | 38 | test('RustFunction with defaults', () => { 39 | // WHEN 40 | new RustFunction(stack, 'binary1'); 41 | 42 | expect(Bundling.bundle).toHaveBeenCalledWith( 43 | expect.objectContaining({ 44 | entry: expect.stringContaining('function.test.binary1/Cargo.toml'), 45 | }), 46 | ); 47 | 48 | Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { 49 | Handler: 'rust.handler', 50 | Runtime: Match.stringLikeRegexp('provided'), 51 | }); 52 | }); 53 | 54 | test('RustFunction with container env vars', () => { 55 | // WHEN 56 | new RustFunction(stack, 'binary1', { 57 | bundling: { 58 | environment: { 59 | KEY: 'VALUE', 60 | }, 61 | }, 62 | }); 63 | 64 | expect(Bundling.bundle).toHaveBeenCalledWith( 65 | expect.objectContaining({ 66 | environment: { 67 | KEY: 'VALUE', 68 | }, 69 | }), 70 | ); 71 | }); 72 | 73 | test('RustFunction with specific binary', () => { 74 | // WHEN 75 | new RustFunction(stack, 'binary1', { 76 | entry: path.join(__dirname, 'rust-workspaces/Cargo.toml'), 77 | binaryName: 'my_lambda1', 78 | }); 79 | 80 | expect(Bundling.bundle).toHaveBeenCalledWith( 81 | expect.objectContaining({ 82 | binaryName: 'my_lambda1', 83 | }), 84 | ); 85 | }); 86 | 87 | test('Find manifest when entry is a directory', () => { 88 | new RustFunction(stack, 'Fn', { 89 | entry: path.join(__dirname, 'rust-standalone'), 90 | }); 91 | 92 | expect(Bundling.bundle).toHaveBeenCalledWith( 93 | expect.objectContaining({ 94 | entry: expect.stringContaining('Cargo.toml'), 95 | }), 96 | ); 97 | }); 98 | 99 | test('throws when entry does not exist', () => { 100 | expect( 101 | () => 102 | new RustFunction(stack, 'Fn', { 103 | entry: 'folder/Cargo.toml', 104 | }), 105 | ).toThrow(/Cannot find manifest file at folder/); 106 | }); 107 | 108 | test('throws when entry cannot be automatically found', () => { 109 | expect(() => new RustFunction(stack, 'Fn')).toThrow( 110 | /Cannot find manifest file - Cargo.toml/, 111 | ); 112 | }); 113 | 114 | test('throws with the wrong runtime family', () => { 115 | expect( 116 | () => 117 | new RustFunction(stack, 'binary1', { 118 | runtime: Runtime.PYTHON_3_8, 119 | }), 120 | ).toThrow(/Only `Custom` runtimes are supported/); 121 | }); 122 | 123 | test('defaults to AL2023', () => { 124 | // WHEN 125 | new RustFunction(stack, 'binary1'); 126 | 127 | Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { 128 | Runtime: Runtime.PROVIDED_AL2023.toString(), 129 | }); 130 | }); 131 | -------------------------------------------------------------------------------- /test/integ.function-bins.ts: -------------------------------------------------------------------------------- 1 | import { ExpectedResult, IntegTest } from '@aws-cdk/integ-tests-alpha'; 2 | import { App, CfnOutput, Stack, StackProps } from 'aws-cdk-lib'; 3 | import { Construct } from 'constructs'; 4 | import { RustFunction } from '../lib'; 5 | 6 | /* 7 | * Stack verification steps: 8 | * * aws lambda invoke --function-name --invocation-type Event --payload '"OK"' response.json 9 | */ 10 | class TestStack extends Stack { 11 | public readonly functionName: string; 12 | constructor(scope: Construct, id: string, props?: StackProps) { 13 | super(scope, id, props); 14 | 15 | const fn = new RustFunction(this, 'binary1', { 16 | entry: 'rust-bins/Cargo.toml', 17 | binaryName: 'my_lambda1', 18 | bundling: { 19 | forceDockerBundling: true, 20 | }, 21 | }); 22 | this.functionName = fn.functionName; 23 | 24 | new CfnOutput(this, 'FunctionArn', { 25 | value: fn.functionArn, 26 | }); 27 | } 28 | } 29 | 30 | const app = new App(); 31 | const testCase = new TestStack(app, 'integ-lambda-rust-function-bins'); 32 | const integ = new IntegTest(app, 'lambda-rust-function', { 33 | testCases: [testCase], 34 | stackUpdateWorkflow: false, 35 | }); 36 | 37 | const invoke = integ.assertions.invokeFunction({ 38 | functionName: testCase.functionName, 39 | }); 40 | 41 | invoke.expect( 42 | ExpectedResult.objectLike({ 43 | Payload: 44 | '{"statusCode":200,"headers":{},"multiValueHeaders":{},"body":"OK1","isBase64Encoded":false}', 45 | }), 46 | ); 47 | app.synth(); 48 | -------------------------------------------------------------------------------- /test/integ.function-bins.ts.snapshot/cdk.out: -------------------------------------------------------------------------------- 1 | {"version":"35.0.0"} -------------------------------------------------------------------------------- /test/integ.function-bins.ts.snapshot/integ-lambda-rust-function-bins.assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "35.0.0", 3 | "files": { 4 | "ddf2fdaa0d92e75225bacebdf6b6184aa3ba3cfaffb1403e73e8f2e7062b613f": { 5 | "source": { 6 | "path": "asset.ddf2fdaa0d92e75225bacebdf6b6184aa3ba3cfaffb1403e73e8f2e7062b613f", 7 | "packaging": "zip" 8 | }, 9 | "destinations": { 10 | "current_account-current_region": { 11 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 12 | "objectKey": "ddf2fdaa0d92e75225bacebdf6b6184aa3ba3cfaffb1403e73e8f2e7062b613f.zip", 13 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 14 | } 15 | } 16 | }, 17 | "4a9a354335c19acde268d9c76e3241ff6c676ca74f65202a7ec6a275f0857b01": { 18 | "source": { 19 | "path": "integ-lambda-rust-function-bins.template.json", 20 | "packaging": "file" 21 | }, 22 | "destinations": { 23 | "current_account-current_region": { 24 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 25 | "objectKey": "4a9a354335c19acde268d9c76e3241ff6c676ca74f65202a7ec6a275f0857b01.json", 26 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 27 | } 28 | } 29 | } 30 | }, 31 | "dockerImages": {} 32 | } -------------------------------------------------------------------------------- /test/integ.function-bins.ts.snapshot/integ-lambda-rust-function-bins.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Resources": { 3 | "binary1ServiceRole6833FBF9": { 4 | "Type": "AWS::IAM::Role", 5 | "Properties": { 6 | "AssumeRolePolicyDocument": { 7 | "Statement": [ 8 | { 9 | "Action": "sts:AssumeRole", 10 | "Effect": "Allow", 11 | "Principal": { 12 | "Service": "lambda.amazonaws.com" 13 | } 14 | } 15 | ], 16 | "Version": "2012-10-17" 17 | }, 18 | "ManagedPolicyArns": [ 19 | { 20 | "Fn::Join": [ 21 | "", 22 | [ 23 | "arn:", 24 | { 25 | "Ref": "AWS::Partition" 26 | }, 27 | ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 28 | ] 29 | ] 30 | } 31 | ] 32 | } 33 | }, 34 | "binary13A08F2FB": { 35 | "Type": "AWS::Lambda::Function", 36 | "Properties": { 37 | "Code": { 38 | "S3Bucket": { 39 | "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" 40 | }, 41 | "S3Key": "ddf2fdaa0d92e75225bacebdf6b6184aa3ba3cfaffb1403e73e8f2e7062b613f.zip" 42 | }, 43 | "Handler": "rust.handler", 44 | "Role": { 45 | "Fn::GetAtt": [ 46 | "binary1ServiceRole6833FBF9", 47 | "Arn" 48 | ] 49 | }, 50 | "Runtime": "provided.al2023" 51 | }, 52 | "DependsOn": [ 53 | "binary1ServiceRole6833FBF9" 54 | ] 55 | } 56 | }, 57 | "Outputs": { 58 | "FunctionArn": { 59 | "Value": { 60 | "Fn::GetAtt": [ 61 | "binary13A08F2FB", 62 | "Arn" 63 | ] 64 | } 65 | }, 66 | "ExportsOutputRefbinary13A08F2FBE7683BB1": { 67 | "Value": { 68 | "Ref": "binary13A08F2FB" 69 | }, 70 | "Export": { 71 | "Name": "integ-lambda-rust-function-bins:ExportsOutputRefbinary13A08F2FBE7683BB1" 72 | } 73 | } 74 | }, 75 | "Parameters": { 76 | "BootstrapVersion": { 77 | "Type": "AWS::SSM::Parameter::Value", 78 | "Default": "/cdk-bootstrap/hnb659fds/version", 79 | "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" 80 | } 81 | }, 82 | "Rules": { 83 | "CheckBootstrapVersion": { 84 | "Assertions": [ 85 | { 86 | "Assert": { 87 | "Fn::Not": [ 88 | { 89 | "Fn::Contains": [ 90 | [ 91 | "1", 92 | "2", 93 | "3", 94 | "4", 95 | "5" 96 | ], 97 | { 98 | "Ref": "BootstrapVersion" 99 | } 100 | ] 101 | } 102 | ] 103 | }, 104 | "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." 105 | } 106 | ] 107 | } 108 | } 109 | } -------------------------------------------------------------------------------- /test/integ.function-bins.ts.snapshot/integ.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "35.0.0", 3 | "testCases": { 4 | "lambda-rust-function/DefaultTest": { 5 | "stacks": [ 6 | "integ-lambda-rust-function-bins" 7 | ], 8 | "stackUpdateWorkflow": true, 9 | "assertionStack": "lambda-rust-function/DefaultTest/DeployAssert", 10 | "assertionStackName": "lambdarustfunctionDefaultTestDeployAssert1211A947" 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /test/integ.function-bins.ts.snapshot/lambdarustfunctionDefaultTestDeployAssert1211A947.assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "35.0.0", 3 | "files": { 4 | "4012e9468badb2b8e1a18d0513e222f2ad6246cf6ddc9f167fc9add9a48bb84e": { 5 | "source": { 6 | "path": "asset.4012e9468badb2b8e1a18d0513e222f2ad6246cf6ddc9f167fc9add9a48bb84e.bundle", 7 | "packaging": "zip" 8 | }, 9 | "destinations": { 10 | "current_account-current_region": { 11 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 12 | "objectKey": "4012e9468badb2b8e1a18d0513e222f2ad6246cf6ddc9f167fc9add9a48bb84e.zip", 13 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 14 | } 15 | } 16 | }, 17 | "a7654c33c2d9332cb5bc99446af796b18abbb8af32ea80736423bff9802ce071": { 18 | "source": { 19 | "path": "lambdarustfunctionDefaultTestDeployAssert1211A947.template.json", 20 | "packaging": "file" 21 | }, 22 | "destinations": { 23 | "current_account-current_region": { 24 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 25 | "objectKey": "a7654c33c2d9332cb5bc99446af796b18abbb8af32ea80736423bff9802ce071.json", 26 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 27 | } 28 | } 29 | } 30 | }, 31 | "dockerImages": {} 32 | } -------------------------------------------------------------------------------- /test/integ.function-bins.ts.snapshot/lambdarustfunctionDefaultTestDeployAssert1211A947.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Resources": { 3 | "LambdaInvoke5f64dcf3ca116111b3990b2653985d2f": { 4 | "Type": "Custom::DeployAssert@SdkCallLambdainvoke", 5 | "Properties": { 6 | "ServiceToken": { 7 | "Fn::GetAtt": [ 8 | "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F", 9 | "Arn" 10 | ] 11 | }, 12 | "service": "Lambda", 13 | "api": "invoke", 14 | "expected": "{\"$ObjectLike\":{\"Payload\":\"{\\\"statusCode\\\":200,\\\"headers\\\":{},\\\"multiValueHeaders\\\":{},\\\"body\\\":\\\"OK1\\\",\\\"isBase64Encoded\\\":false}\"}}", 15 | "parameters": { 16 | "FunctionName": { 17 | "Fn::Join": [ 18 | "", 19 | [ 20 | "\"", 21 | { 22 | "Fn::ImportValue": "integ-lambda-rust-function-bins:ExportsOutputRefbinary13A08F2FBE7683BB1" 23 | }, 24 | "\"" 25 | ] 26 | ] 27 | } 28 | }, 29 | "flattenResponse": "false", 30 | "salt": "1704368361247" 31 | }, 32 | "UpdateReplacePolicy": "Delete", 33 | "DeletionPolicy": "Delete" 34 | }, 35 | "LambdaInvoke5f64dcf3ca116111b3990b2653985d2fInvokeB1FAC0C5": { 36 | "Type": "AWS::Lambda::Permission", 37 | "Properties": { 38 | "Action": "lambda:InvokeFunction", 39 | "FunctionName": { 40 | "Fn::ImportValue": "integ-lambda-rust-function-bins:ExportsOutputRefbinary13A08F2FBE7683BB1" 41 | }, 42 | "Principal": { 43 | "Fn::GetAtt": [ 44 | "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73", 45 | "Arn" 46 | ] 47 | } 48 | } 49 | }, 50 | "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73": { 51 | "Type": "AWS::IAM::Role", 52 | "Properties": { 53 | "AssumeRolePolicyDocument": { 54 | "Version": "2012-10-17", 55 | "Statement": [ 56 | { 57 | "Action": "sts:AssumeRole", 58 | "Effect": "Allow", 59 | "Principal": { 60 | "Service": "lambda.amazonaws.com" 61 | } 62 | } 63 | ] 64 | }, 65 | "ManagedPolicyArns": [ 66 | { 67 | "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 68 | } 69 | ], 70 | "Policies": [ 71 | { 72 | "PolicyName": "Inline", 73 | "PolicyDocument": { 74 | "Version": "2012-10-17", 75 | "Statement": [ 76 | { 77 | "Action": [ 78 | "lambda:Invoke" 79 | ], 80 | "Effect": "Allow", 81 | "Resource": [ 82 | "*" 83 | ] 84 | }, 85 | { 86 | "Action": [ 87 | "lambda:InvokeFunction" 88 | ], 89 | "Effect": "Allow", 90 | "Resource": [ 91 | { 92 | "Fn::Join": [ 93 | "", 94 | [ 95 | "arn:", 96 | { 97 | "Ref": "AWS::Partition" 98 | }, 99 | ":lambda:", 100 | { 101 | "Ref": "AWS::Region" 102 | }, 103 | ":", 104 | { 105 | "Ref": "AWS::AccountId" 106 | }, 107 | ":function:", 108 | { 109 | "Fn::ImportValue": "integ-lambda-rust-function-bins:ExportsOutputRefbinary13A08F2FBE7683BB1" 110 | } 111 | ] 112 | ] 113 | } 114 | ] 115 | } 116 | ] 117 | } 118 | } 119 | ] 120 | } 121 | }, 122 | "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F": { 123 | "Type": "AWS::Lambda::Function", 124 | "Properties": { 125 | "Runtime": "nodejs18.x", 126 | "Code": { 127 | "S3Bucket": { 128 | "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" 129 | }, 130 | "S3Key": "4012e9468badb2b8e1a18d0513e222f2ad6246cf6ddc9f167fc9add9a48bb84e.zip" 131 | }, 132 | "Timeout": 120, 133 | "Handler": "index.handler", 134 | "Role": { 135 | "Fn::GetAtt": [ 136 | "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73", 137 | "Arn" 138 | ] 139 | } 140 | } 141 | } 142 | }, 143 | "Outputs": { 144 | "AssertionResultsLambdaInvoke5f64dcf3ca116111b3990b2653985d2f": { 145 | "Value": { 146 | "Fn::GetAtt": [ 147 | "LambdaInvoke5f64dcf3ca116111b3990b2653985d2f", 148 | "assertion" 149 | ] 150 | } 151 | } 152 | }, 153 | "Parameters": { 154 | "BootstrapVersion": { 155 | "Type": "AWS::SSM::Parameter::Value", 156 | "Default": "/cdk-bootstrap/hnb659fds/version", 157 | "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" 158 | } 159 | }, 160 | "Rules": { 161 | "CheckBootstrapVersion": { 162 | "Assertions": [ 163 | { 164 | "Assert": { 165 | "Fn::Not": [ 166 | { 167 | "Fn::Contains": [ 168 | [ 169 | "1", 170 | "2", 171 | "3", 172 | "4", 173 | "5" 174 | ], 175 | { 176 | "Ref": "BootstrapVersion" 177 | } 178 | ] 179 | } 180 | ] 181 | }, 182 | "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." 183 | } 184 | ] 185 | } 186 | } 187 | } -------------------------------------------------------------------------------- /test/integ.function-bins.ts.snapshot/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "35.0.0", 3 | "artifacts": { 4 | "integ-lambda-rust-function-bins.assets": { 5 | "type": "cdk:asset-manifest", 6 | "properties": { 7 | "file": "integ-lambda-rust-function-bins.assets.json", 8 | "requiresBootstrapStackVersion": 6, 9 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 10 | } 11 | }, 12 | "integ-lambda-rust-function-bins": { 13 | "type": "aws:cloudformation:stack", 14 | "environment": "aws://unknown-account/unknown-region", 15 | "properties": { 16 | "templateFile": "integ-lambda-rust-function-bins.template.json", 17 | "terminationProtection": false, 18 | "validateOnSynth": false, 19 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", 20 | "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", 21 | "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/4a9a354335c19acde268d9c76e3241ff6c676ca74f65202a7ec6a275f0857b01.json", 22 | "requiresBootstrapStackVersion": 6, 23 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", 24 | "additionalDependencies": [ 25 | "integ-lambda-rust-function-bins.assets" 26 | ], 27 | "lookupRole": { 28 | "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", 29 | "requiresBootstrapStackVersion": 8, 30 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 31 | } 32 | }, 33 | "dependencies": [ 34 | "integ-lambda-rust-function-bins.assets" 35 | ], 36 | "metadata": { 37 | "/integ-lambda-rust-function-bins/binary1/ServiceRole/Resource": [ 38 | { 39 | "type": "aws:cdk:logicalId", 40 | "data": "binary1ServiceRole6833FBF9" 41 | } 42 | ], 43 | "/integ-lambda-rust-function-bins/binary1/Resource": [ 44 | { 45 | "type": "aws:cdk:logicalId", 46 | "data": "binary13A08F2FB" 47 | } 48 | ], 49 | "/integ-lambda-rust-function-bins/FunctionArn": [ 50 | { 51 | "type": "aws:cdk:logicalId", 52 | "data": "FunctionArn" 53 | } 54 | ], 55 | "/integ-lambda-rust-function-bins/Exports/Output{\"Ref\":\"binary13A08F2FB\"}": [ 56 | { 57 | "type": "aws:cdk:logicalId", 58 | "data": "ExportsOutputRefbinary13A08F2FBE7683BB1" 59 | } 60 | ], 61 | "/integ-lambda-rust-function-bins/BootstrapVersion": [ 62 | { 63 | "type": "aws:cdk:logicalId", 64 | "data": "BootstrapVersion" 65 | } 66 | ], 67 | "/integ-lambda-rust-function-bins/CheckBootstrapVersion": [ 68 | { 69 | "type": "aws:cdk:logicalId", 70 | "data": "CheckBootstrapVersion" 71 | } 72 | ] 73 | }, 74 | "displayName": "integ-lambda-rust-function-bins" 75 | }, 76 | "lambdarustfunctionDefaultTestDeployAssert1211A947.assets": { 77 | "type": "cdk:asset-manifest", 78 | "properties": { 79 | "file": "lambdarustfunctionDefaultTestDeployAssert1211A947.assets.json", 80 | "requiresBootstrapStackVersion": 6, 81 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 82 | } 83 | }, 84 | "lambdarustfunctionDefaultTestDeployAssert1211A947": { 85 | "type": "aws:cloudformation:stack", 86 | "environment": "aws://unknown-account/unknown-region", 87 | "properties": { 88 | "templateFile": "lambdarustfunctionDefaultTestDeployAssert1211A947.template.json", 89 | "terminationProtection": false, 90 | "validateOnSynth": false, 91 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", 92 | "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", 93 | "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/a7654c33c2d9332cb5bc99446af796b18abbb8af32ea80736423bff9802ce071.json", 94 | "requiresBootstrapStackVersion": 6, 95 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", 96 | "additionalDependencies": [ 97 | "lambdarustfunctionDefaultTestDeployAssert1211A947.assets" 98 | ], 99 | "lookupRole": { 100 | "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", 101 | "requiresBootstrapStackVersion": 8, 102 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 103 | } 104 | }, 105 | "dependencies": [ 106 | "integ-lambda-rust-function-bins", 107 | "lambdarustfunctionDefaultTestDeployAssert1211A947.assets" 108 | ], 109 | "metadata": { 110 | "/lambda-rust-function/DefaultTest/DeployAssert/LambdaInvoke5f64dcf3ca116111b3990b2653985d2f/Default/Default": [ 111 | { 112 | "type": "aws:cdk:logicalId", 113 | "data": "LambdaInvoke5f64dcf3ca116111b3990b2653985d2f" 114 | } 115 | ], 116 | "/lambda-rust-function/DefaultTest/DeployAssert/LambdaInvoke5f64dcf3ca116111b3990b2653985d2f/Invoke": [ 117 | { 118 | "type": "aws:cdk:logicalId", 119 | "data": "LambdaInvoke5f64dcf3ca116111b3990b2653985d2fInvokeB1FAC0C5" 120 | } 121 | ], 122 | "/lambda-rust-function/DefaultTest/DeployAssert/LambdaInvoke5f64dcf3ca116111b3990b2653985d2f/AssertionResults": [ 123 | { 124 | "type": "aws:cdk:logicalId", 125 | "data": "AssertionResultsLambdaInvoke5f64dcf3ca116111b3990b2653985d2f" 126 | } 127 | ], 128 | "/lambda-rust-function/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role": [ 129 | { 130 | "type": "aws:cdk:logicalId", 131 | "data": "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73" 132 | } 133 | ], 134 | "/lambda-rust-function/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler": [ 135 | { 136 | "type": "aws:cdk:logicalId", 137 | "data": "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F" 138 | } 139 | ], 140 | "/lambda-rust-function/DefaultTest/DeployAssert/BootstrapVersion": [ 141 | { 142 | "type": "aws:cdk:logicalId", 143 | "data": "BootstrapVersion" 144 | } 145 | ], 146 | "/lambda-rust-function/DefaultTest/DeployAssert/CheckBootstrapVersion": [ 147 | { 148 | "type": "aws:cdk:logicalId", 149 | "data": "CheckBootstrapVersion" 150 | } 151 | ] 152 | }, 153 | "displayName": "lambda-rust-function/DefaultTest/DeployAssert" 154 | }, 155 | "Tree": { 156 | "type": "cdk:tree", 157 | "properties": { 158 | "file": "tree.json" 159 | } 160 | } 161 | } 162 | } -------------------------------------------------------------------------------- /test/integ.function-workspace.ts: -------------------------------------------------------------------------------- 1 | import { ExpectedResult, IntegTest } from '@aws-cdk/integ-tests-alpha'; 2 | import { App, CfnOutput, Stack, StackProps } from 'aws-cdk-lib'; 3 | import { Construct } from 'constructs'; 4 | import { RustFunction } from '../lib'; 5 | 6 | /* 7 | * Stack verification steps: 8 | * * aws lambda invoke --function-name --invocation-type Event --payload '"OK"' response.json 9 | */ 10 | class TestStack extends Stack { 11 | public readonly functionName: string; 12 | constructor(scope: Construct, id: string, props?: StackProps) { 13 | super(scope, id, props); 14 | 15 | const fn = new RustFunction(this, 'binary1', { 16 | entry: 'rust-workspaces/Cargo.toml', 17 | binaryName: 'my_lambda2', 18 | bundling: { 19 | forceDockerBundling: true, 20 | }, 21 | }); 22 | this.functionName = fn.functionName; 23 | 24 | new CfnOutput(this, 'FunctionArn', { 25 | value: fn.functionArn, 26 | }); 27 | } 28 | } 29 | 30 | const app = new App(); 31 | const testCase = new TestStack(app, 'integ-lambda-rust-function-workspace'); 32 | const integ = new IntegTest(app, 'lambda-rust-function', { 33 | testCases: [testCase], 34 | stackUpdateWorkflow: false, 35 | }); 36 | 37 | const invoke = integ.assertions.invokeFunction({ 38 | functionName: testCase.functionName, 39 | }); 40 | 41 | invoke.expect( 42 | ExpectedResult.objectLike({ 43 | Payload: 44 | '{"statusCode":200,"headers":{},"multiValueHeaders":{},"body":"OK2","isBase64Encoded":false}', 45 | }), 46 | ); 47 | app.synth(); 48 | -------------------------------------------------------------------------------- /test/integ.function-workspace.ts.snapshot/cdk.out: -------------------------------------------------------------------------------- 1 | {"version":"35.0.0"} -------------------------------------------------------------------------------- /test/integ.function-workspace.ts.snapshot/integ-lambda-rust-function-workspace.assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "35.0.0", 3 | "files": { 4 | "4ab2ea74fc521d4c9315866a54100948ff0f9b9f9bdc76c97358ff847b0bebc5": { 5 | "source": { 6 | "path": "asset.4ab2ea74fc521d4c9315866a54100948ff0f9b9f9bdc76c97358ff847b0bebc5", 7 | "packaging": "zip" 8 | }, 9 | "destinations": { 10 | "current_account-current_region": { 11 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 12 | "objectKey": "4ab2ea74fc521d4c9315866a54100948ff0f9b9f9bdc76c97358ff847b0bebc5.zip", 13 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 14 | } 15 | } 16 | }, 17 | "4c1ed1f37d9eccba33170579df6b3a991285bbabd66a81ef46c4c0b3e59132ff": { 18 | "source": { 19 | "path": "integ-lambda-rust-function-workspace.template.json", 20 | "packaging": "file" 21 | }, 22 | "destinations": { 23 | "current_account-current_region": { 24 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 25 | "objectKey": "4c1ed1f37d9eccba33170579df6b3a991285bbabd66a81ef46c4c0b3e59132ff.json", 26 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 27 | } 28 | } 29 | } 30 | }, 31 | "dockerImages": {} 32 | } -------------------------------------------------------------------------------- /test/integ.function-workspace.ts.snapshot/integ-lambda-rust-function-workspace.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Resources": { 3 | "binary1ServiceRole6833FBF9": { 4 | "Type": "AWS::IAM::Role", 5 | "Properties": { 6 | "AssumeRolePolicyDocument": { 7 | "Statement": [ 8 | { 9 | "Action": "sts:AssumeRole", 10 | "Effect": "Allow", 11 | "Principal": { 12 | "Service": "lambda.amazonaws.com" 13 | } 14 | } 15 | ], 16 | "Version": "2012-10-17" 17 | }, 18 | "ManagedPolicyArns": [ 19 | { 20 | "Fn::Join": [ 21 | "", 22 | [ 23 | "arn:", 24 | { 25 | "Ref": "AWS::Partition" 26 | }, 27 | ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 28 | ] 29 | ] 30 | } 31 | ] 32 | } 33 | }, 34 | "binary13A08F2FB": { 35 | "Type": "AWS::Lambda::Function", 36 | "Properties": { 37 | "Code": { 38 | "S3Bucket": { 39 | "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" 40 | }, 41 | "S3Key": "4ab2ea74fc521d4c9315866a54100948ff0f9b9f9bdc76c97358ff847b0bebc5.zip" 42 | }, 43 | "Handler": "rust.handler", 44 | "Role": { 45 | "Fn::GetAtt": [ 46 | "binary1ServiceRole6833FBF9", 47 | "Arn" 48 | ] 49 | }, 50 | "Runtime": "provided.al2023" 51 | }, 52 | "DependsOn": [ 53 | "binary1ServiceRole6833FBF9" 54 | ] 55 | } 56 | }, 57 | "Outputs": { 58 | "FunctionArn": { 59 | "Value": { 60 | "Fn::GetAtt": [ 61 | "binary13A08F2FB", 62 | "Arn" 63 | ] 64 | } 65 | }, 66 | "ExportsOutputRefbinary13A08F2FBE7683BB1": { 67 | "Value": { 68 | "Ref": "binary13A08F2FB" 69 | }, 70 | "Export": { 71 | "Name": "integ-lambda-rust-function-workspace:ExportsOutputRefbinary13A08F2FBE7683BB1" 72 | } 73 | } 74 | }, 75 | "Parameters": { 76 | "BootstrapVersion": { 77 | "Type": "AWS::SSM::Parameter::Value", 78 | "Default": "/cdk-bootstrap/hnb659fds/version", 79 | "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" 80 | } 81 | }, 82 | "Rules": { 83 | "CheckBootstrapVersion": { 84 | "Assertions": [ 85 | { 86 | "Assert": { 87 | "Fn::Not": [ 88 | { 89 | "Fn::Contains": [ 90 | [ 91 | "1", 92 | "2", 93 | "3", 94 | "4", 95 | "5" 96 | ], 97 | { 98 | "Ref": "BootstrapVersion" 99 | } 100 | ] 101 | } 102 | ] 103 | }, 104 | "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." 105 | } 106 | ] 107 | } 108 | } 109 | } -------------------------------------------------------------------------------- /test/integ.function-workspace.ts.snapshot/integ.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "35.0.0", 3 | "testCases": { 4 | "lambda-rust-function/DefaultTest": { 5 | "stacks": [ 6 | "integ-lambda-rust-function-workspace" 7 | ], 8 | "stackUpdateWorkflow": true, 9 | "assertionStack": "lambda-rust-function/DefaultTest/DeployAssert", 10 | "assertionStackName": "lambdarustfunctionDefaultTestDeployAssert1211A947" 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /test/integ.function-workspace.ts.snapshot/lambdarustfunctionDefaultTestDeployAssert1211A947.assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "35.0.0", 3 | "files": { 4 | "4012e9468badb2b8e1a18d0513e222f2ad6246cf6ddc9f167fc9add9a48bb84e": { 5 | "source": { 6 | "path": "asset.4012e9468badb2b8e1a18d0513e222f2ad6246cf6ddc9f167fc9add9a48bb84e.bundle", 7 | "packaging": "zip" 8 | }, 9 | "destinations": { 10 | "current_account-current_region": { 11 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 12 | "objectKey": "4012e9468badb2b8e1a18d0513e222f2ad6246cf6ddc9f167fc9add9a48bb84e.zip", 13 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 14 | } 15 | } 16 | }, 17 | "556b98d36784294f90501bb37ac0c43f4c55a1da2a4f007f0d6c15f64d21f7b2": { 18 | "source": { 19 | "path": "lambdarustfunctionDefaultTestDeployAssert1211A947.template.json", 20 | "packaging": "file" 21 | }, 22 | "destinations": { 23 | "current_account-current_region": { 24 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 25 | "objectKey": "556b98d36784294f90501bb37ac0c43f4c55a1da2a4f007f0d6c15f64d21f7b2.json", 26 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 27 | } 28 | } 29 | } 30 | }, 31 | "dockerImages": {} 32 | } -------------------------------------------------------------------------------- /test/integ.function-workspace.ts.snapshot/lambdarustfunctionDefaultTestDeployAssert1211A947.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Resources": { 3 | "LambdaInvoke5f64dcf3ca116111b3990b2653985d2f": { 4 | "Type": "Custom::DeployAssert@SdkCallLambdainvoke", 5 | "Properties": { 6 | "ServiceToken": { 7 | "Fn::GetAtt": [ 8 | "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F", 9 | "Arn" 10 | ] 11 | }, 12 | "service": "Lambda", 13 | "api": "invoke", 14 | "expected": "{\"$ObjectLike\":{\"Payload\":\"{\\\"statusCode\\\":200,\\\"headers\\\":{},\\\"multiValueHeaders\\\":{},\\\"body\\\":\\\"OK2\\\",\\\"isBase64Encoded\\\":false}\"}}", 15 | "parameters": { 16 | "FunctionName": { 17 | "Fn::Join": [ 18 | "", 19 | [ 20 | "\"", 21 | { 22 | "Fn::ImportValue": "integ-lambda-rust-function-workspace:ExportsOutputRefbinary13A08F2FBE7683BB1" 23 | }, 24 | "\"" 25 | ] 26 | ] 27 | } 28 | }, 29 | "flattenResponse": "false", 30 | "salt": "1704368190642" 31 | }, 32 | "UpdateReplacePolicy": "Delete", 33 | "DeletionPolicy": "Delete" 34 | }, 35 | "LambdaInvoke5f64dcf3ca116111b3990b2653985d2fInvokeB1FAC0C5": { 36 | "Type": "AWS::Lambda::Permission", 37 | "Properties": { 38 | "Action": "lambda:InvokeFunction", 39 | "FunctionName": { 40 | "Fn::ImportValue": "integ-lambda-rust-function-workspace:ExportsOutputRefbinary13A08F2FBE7683BB1" 41 | }, 42 | "Principal": { 43 | "Fn::GetAtt": [ 44 | "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73", 45 | "Arn" 46 | ] 47 | } 48 | } 49 | }, 50 | "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73": { 51 | "Type": "AWS::IAM::Role", 52 | "Properties": { 53 | "AssumeRolePolicyDocument": { 54 | "Version": "2012-10-17", 55 | "Statement": [ 56 | { 57 | "Action": "sts:AssumeRole", 58 | "Effect": "Allow", 59 | "Principal": { 60 | "Service": "lambda.amazonaws.com" 61 | } 62 | } 63 | ] 64 | }, 65 | "ManagedPolicyArns": [ 66 | { 67 | "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 68 | } 69 | ], 70 | "Policies": [ 71 | { 72 | "PolicyName": "Inline", 73 | "PolicyDocument": { 74 | "Version": "2012-10-17", 75 | "Statement": [ 76 | { 77 | "Action": [ 78 | "lambda:Invoke" 79 | ], 80 | "Effect": "Allow", 81 | "Resource": [ 82 | "*" 83 | ] 84 | }, 85 | { 86 | "Action": [ 87 | "lambda:InvokeFunction" 88 | ], 89 | "Effect": "Allow", 90 | "Resource": [ 91 | { 92 | "Fn::Join": [ 93 | "", 94 | [ 95 | "arn:", 96 | { 97 | "Ref": "AWS::Partition" 98 | }, 99 | ":lambda:", 100 | { 101 | "Ref": "AWS::Region" 102 | }, 103 | ":", 104 | { 105 | "Ref": "AWS::AccountId" 106 | }, 107 | ":function:", 108 | { 109 | "Fn::ImportValue": "integ-lambda-rust-function-workspace:ExportsOutputRefbinary13A08F2FBE7683BB1" 110 | } 111 | ] 112 | ] 113 | } 114 | ] 115 | } 116 | ] 117 | } 118 | } 119 | ] 120 | } 121 | }, 122 | "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F": { 123 | "Type": "AWS::Lambda::Function", 124 | "Properties": { 125 | "Runtime": "nodejs18.x", 126 | "Code": { 127 | "S3Bucket": { 128 | "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" 129 | }, 130 | "S3Key": "4012e9468badb2b8e1a18d0513e222f2ad6246cf6ddc9f167fc9add9a48bb84e.zip" 131 | }, 132 | "Timeout": 120, 133 | "Handler": "index.handler", 134 | "Role": { 135 | "Fn::GetAtt": [ 136 | "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73", 137 | "Arn" 138 | ] 139 | } 140 | } 141 | } 142 | }, 143 | "Outputs": { 144 | "AssertionResultsLambdaInvoke5f64dcf3ca116111b3990b2653985d2f": { 145 | "Value": { 146 | "Fn::GetAtt": [ 147 | "LambdaInvoke5f64dcf3ca116111b3990b2653985d2f", 148 | "assertion" 149 | ] 150 | } 151 | } 152 | }, 153 | "Parameters": { 154 | "BootstrapVersion": { 155 | "Type": "AWS::SSM::Parameter::Value", 156 | "Default": "/cdk-bootstrap/hnb659fds/version", 157 | "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" 158 | } 159 | }, 160 | "Rules": { 161 | "CheckBootstrapVersion": { 162 | "Assertions": [ 163 | { 164 | "Assert": { 165 | "Fn::Not": [ 166 | { 167 | "Fn::Contains": [ 168 | [ 169 | "1", 170 | "2", 171 | "3", 172 | "4", 173 | "5" 174 | ], 175 | { 176 | "Ref": "BootstrapVersion" 177 | } 178 | ] 179 | } 180 | ] 181 | }, 182 | "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." 183 | } 184 | ] 185 | } 186 | } 187 | } -------------------------------------------------------------------------------- /test/integ.function-workspace.ts.snapshot/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "35.0.0", 3 | "artifacts": { 4 | "integ-lambda-rust-function-workspace.assets": { 5 | "type": "cdk:asset-manifest", 6 | "properties": { 7 | "file": "integ-lambda-rust-function-workspace.assets.json", 8 | "requiresBootstrapStackVersion": 6, 9 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 10 | } 11 | }, 12 | "integ-lambda-rust-function-workspace": { 13 | "type": "aws:cloudformation:stack", 14 | "environment": "aws://unknown-account/unknown-region", 15 | "properties": { 16 | "templateFile": "integ-lambda-rust-function-workspace.template.json", 17 | "terminationProtection": false, 18 | "validateOnSynth": false, 19 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", 20 | "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", 21 | "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/4c1ed1f37d9eccba33170579df6b3a991285bbabd66a81ef46c4c0b3e59132ff.json", 22 | "requiresBootstrapStackVersion": 6, 23 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", 24 | "additionalDependencies": [ 25 | "integ-lambda-rust-function-workspace.assets" 26 | ], 27 | "lookupRole": { 28 | "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", 29 | "requiresBootstrapStackVersion": 8, 30 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 31 | } 32 | }, 33 | "dependencies": [ 34 | "integ-lambda-rust-function-workspace.assets" 35 | ], 36 | "metadata": { 37 | "/integ-lambda-rust-function-workspace/binary1/ServiceRole/Resource": [ 38 | { 39 | "type": "aws:cdk:logicalId", 40 | "data": "binary1ServiceRole6833FBF9" 41 | } 42 | ], 43 | "/integ-lambda-rust-function-workspace/binary1/Resource": [ 44 | { 45 | "type": "aws:cdk:logicalId", 46 | "data": "binary13A08F2FB" 47 | } 48 | ], 49 | "/integ-lambda-rust-function-workspace/FunctionArn": [ 50 | { 51 | "type": "aws:cdk:logicalId", 52 | "data": "FunctionArn" 53 | } 54 | ], 55 | "/integ-lambda-rust-function-workspace/Exports/Output{\"Ref\":\"binary13A08F2FB\"}": [ 56 | { 57 | "type": "aws:cdk:logicalId", 58 | "data": "ExportsOutputRefbinary13A08F2FBE7683BB1" 59 | } 60 | ], 61 | "/integ-lambda-rust-function-workspace/BootstrapVersion": [ 62 | { 63 | "type": "aws:cdk:logicalId", 64 | "data": "BootstrapVersion" 65 | } 66 | ], 67 | "/integ-lambda-rust-function-workspace/CheckBootstrapVersion": [ 68 | { 69 | "type": "aws:cdk:logicalId", 70 | "data": "CheckBootstrapVersion" 71 | } 72 | ] 73 | }, 74 | "displayName": "integ-lambda-rust-function-workspace" 75 | }, 76 | "lambdarustfunctionDefaultTestDeployAssert1211A947.assets": { 77 | "type": "cdk:asset-manifest", 78 | "properties": { 79 | "file": "lambdarustfunctionDefaultTestDeployAssert1211A947.assets.json", 80 | "requiresBootstrapStackVersion": 6, 81 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 82 | } 83 | }, 84 | "lambdarustfunctionDefaultTestDeployAssert1211A947": { 85 | "type": "aws:cloudformation:stack", 86 | "environment": "aws://unknown-account/unknown-region", 87 | "properties": { 88 | "templateFile": "lambdarustfunctionDefaultTestDeployAssert1211A947.template.json", 89 | "terminationProtection": false, 90 | "validateOnSynth": false, 91 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", 92 | "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", 93 | "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/556b98d36784294f90501bb37ac0c43f4c55a1da2a4f007f0d6c15f64d21f7b2.json", 94 | "requiresBootstrapStackVersion": 6, 95 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", 96 | "additionalDependencies": [ 97 | "lambdarustfunctionDefaultTestDeployAssert1211A947.assets" 98 | ], 99 | "lookupRole": { 100 | "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", 101 | "requiresBootstrapStackVersion": 8, 102 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 103 | } 104 | }, 105 | "dependencies": [ 106 | "integ-lambda-rust-function-workspace", 107 | "lambdarustfunctionDefaultTestDeployAssert1211A947.assets" 108 | ], 109 | "metadata": { 110 | "/lambda-rust-function/DefaultTest/DeployAssert/LambdaInvoke5f64dcf3ca116111b3990b2653985d2f/Default/Default": [ 111 | { 112 | "type": "aws:cdk:logicalId", 113 | "data": "LambdaInvoke5f64dcf3ca116111b3990b2653985d2f" 114 | } 115 | ], 116 | "/lambda-rust-function/DefaultTest/DeployAssert/LambdaInvoke5f64dcf3ca116111b3990b2653985d2f/Invoke": [ 117 | { 118 | "type": "aws:cdk:logicalId", 119 | "data": "LambdaInvoke5f64dcf3ca116111b3990b2653985d2fInvokeB1FAC0C5" 120 | } 121 | ], 122 | "/lambda-rust-function/DefaultTest/DeployAssert/LambdaInvoke5f64dcf3ca116111b3990b2653985d2f/AssertionResults": [ 123 | { 124 | "type": "aws:cdk:logicalId", 125 | "data": "AssertionResultsLambdaInvoke5f64dcf3ca116111b3990b2653985d2f" 126 | } 127 | ], 128 | "/lambda-rust-function/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role": [ 129 | { 130 | "type": "aws:cdk:logicalId", 131 | "data": "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73" 132 | } 133 | ], 134 | "/lambda-rust-function/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler": [ 135 | { 136 | "type": "aws:cdk:logicalId", 137 | "data": "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F" 138 | } 139 | ], 140 | "/lambda-rust-function/DefaultTest/DeployAssert/BootstrapVersion": [ 141 | { 142 | "type": "aws:cdk:logicalId", 143 | "data": "BootstrapVersion" 144 | } 145 | ], 146 | "/lambda-rust-function/DefaultTest/DeployAssert/CheckBootstrapVersion": [ 147 | { 148 | "type": "aws:cdk:logicalId", 149 | "data": "CheckBootstrapVersion" 150 | } 151 | ] 152 | }, 153 | "displayName": "lambda-rust-function/DefaultTest/DeployAssert" 154 | }, 155 | "Tree": { 156 | "type": "cdk:tree", 157 | "properties": { 158 | "file": "tree.json" 159 | } 160 | } 161 | } 162 | } -------------------------------------------------------------------------------- /test/integ.function.ts: -------------------------------------------------------------------------------- 1 | import { ExpectedResult, IntegTest } from '@aws-cdk/integ-tests-alpha'; 2 | import { App, CfnOutput, Stack, StackProps } from 'aws-cdk-lib'; 3 | import { Construct } from 'constructs'; 4 | import { RustFunction } from '../lib'; 5 | 6 | /* 7 | * Stack verification steps: 8 | * * aws lambda invoke --function-name --invocation-type Event --payload '"OK"' response.json 9 | */ 10 | class TestStack extends Stack { 11 | public readonly functionName: string; 12 | constructor(scope: Construct, id: string, props?: StackProps) { 13 | super(scope, id, props); 14 | 15 | const fn = new RustFunction(this, 'binary1', { 16 | entry: 'rust-standalone/Cargo.toml', 17 | bundling: { 18 | forceDockerBundling: true, 19 | }, 20 | }); 21 | this.functionName = fn.functionName; 22 | 23 | new CfnOutput(this, 'FunctionArn', { 24 | value: fn.functionArn, 25 | }); 26 | } 27 | } 28 | 29 | const app = new App(); 30 | const testCase = new TestStack(app, 'integ-lambda-rust-function'); 31 | const integ = new IntegTest(app, 'lambda-rust-function', { 32 | testCases: [testCase], 33 | stackUpdateWorkflow: false, 34 | }); 35 | 36 | const invoke = integ.assertions.invokeFunction({ 37 | functionName: testCase.functionName, 38 | }); 39 | 40 | invoke.expect( 41 | ExpectedResult.objectLike({ 42 | Payload: 43 | '{"statusCode":200,"headers":{},"multiValueHeaders":{},"body":"OK","isBase64Encoded":false}', 44 | }), 45 | ); 46 | app.synth(); 47 | -------------------------------------------------------------------------------- /test/integ.function.ts.snapshot/cdk.out: -------------------------------------------------------------------------------- 1 | {"version":"35.0.0"} -------------------------------------------------------------------------------- /test/integ.function.ts.snapshot/integ-lambda-rust-function.assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "35.0.0", 3 | "files": { 4 | "2c9536aa54ef4116ba5579355660ffe8e11a8a7bb876ca0db342eaf98c86ce37": { 5 | "source": { 6 | "path": "asset.2c9536aa54ef4116ba5579355660ffe8e11a8a7bb876ca0db342eaf98c86ce37", 7 | "packaging": "zip" 8 | }, 9 | "destinations": { 10 | "current_account-current_region": { 11 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 12 | "objectKey": "2c9536aa54ef4116ba5579355660ffe8e11a8a7bb876ca0db342eaf98c86ce37.zip", 13 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 14 | } 15 | } 16 | }, 17 | "e84178c7003a608a3eda47970229795ab45634fff430f7283ca35828db0261a3": { 18 | "source": { 19 | "path": "integ-lambda-rust-function.template.json", 20 | "packaging": "file" 21 | }, 22 | "destinations": { 23 | "current_account-current_region": { 24 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 25 | "objectKey": "e84178c7003a608a3eda47970229795ab45634fff430f7283ca35828db0261a3.json", 26 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 27 | } 28 | } 29 | } 30 | }, 31 | "dockerImages": {} 32 | } -------------------------------------------------------------------------------- /test/integ.function.ts.snapshot/integ-lambda-rust-function.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Resources": { 3 | "binary1ServiceRole6833FBF9": { 4 | "Type": "AWS::IAM::Role", 5 | "Properties": { 6 | "AssumeRolePolicyDocument": { 7 | "Statement": [ 8 | { 9 | "Action": "sts:AssumeRole", 10 | "Effect": "Allow", 11 | "Principal": { 12 | "Service": "lambda.amazonaws.com" 13 | } 14 | } 15 | ], 16 | "Version": "2012-10-17" 17 | }, 18 | "ManagedPolicyArns": [ 19 | { 20 | "Fn::Join": [ 21 | "", 22 | [ 23 | "arn:", 24 | { 25 | "Ref": "AWS::Partition" 26 | }, 27 | ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 28 | ] 29 | ] 30 | } 31 | ] 32 | } 33 | }, 34 | "binary13A08F2FB": { 35 | "Type": "AWS::Lambda::Function", 36 | "Properties": { 37 | "Code": { 38 | "S3Bucket": { 39 | "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" 40 | }, 41 | "S3Key": "2c9536aa54ef4116ba5579355660ffe8e11a8a7bb876ca0db342eaf98c86ce37.zip" 42 | }, 43 | "Handler": "rust.handler", 44 | "Role": { 45 | "Fn::GetAtt": [ 46 | "binary1ServiceRole6833FBF9", 47 | "Arn" 48 | ] 49 | }, 50 | "Runtime": "provided.al2023" 51 | }, 52 | "DependsOn": [ 53 | "binary1ServiceRole6833FBF9" 54 | ] 55 | } 56 | }, 57 | "Outputs": { 58 | "FunctionArn": { 59 | "Value": { 60 | "Fn::GetAtt": [ 61 | "binary13A08F2FB", 62 | "Arn" 63 | ] 64 | } 65 | }, 66 | "ExportsOutputRefbinary13A08F2FBE7683BB1": { 67 | "Value": { 68 | "Ref": "binary13A08F2FB" 69 | }, 70 | "Export": { 71 | "Name": "integ-lambda-rust-function:ExportsOutputRefbinary13A08F2FBE7683BB1" 72 | } 73 | } 74 | }, 75 | "Parameters": { 76 | "BootstrapVersion": { 77 | "Type": "AWS::SSM::Parameter::Value", 78 | "Default": "/cdk-bootstrap/hnb659fds/version", 79 | "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" 80 | } 81 | }, 82 | "Rules": { 83 | "CheckBootstrapVersion": { 84 | "Assertions": [ 85 | { 86 | "Assert": { 87 | "Fn::Not": [ 88 | { 89 | "Fn::Contains": [ 90 | [ 91 | "1", 92 | "2", 93 | "3", 94 | "4", 95 | "5" 96 | ], 97 | { 98 | "Ref": "BootstrapVersion" 99 | } 100 | ] 101 | } 102 | ] 103 | }, 104 | "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." 105 | } 106 | ] 107 | } 108 | } 109 | } -------------------------------------------------------------------------------- /test/integ.function.ts.snapshot/integ.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "35.0.0", 3 | "testCases": { 4 | "lambda-rust-function/DefaultTest": { 5 | "stacks": [ 6 | "integ-lambda-rust-function" 7 | ], 8 | "stackUpdateWorkflow": false, 9 | "assertionStack": "lambda-rust-function/DefaultTest/DeployAssert", 10 | "assertionStackName": "lambdarustfunctionDefaultTestDeployAssert1211A947" 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /test/integ.function.ts.snapshot/lambdarustfunctionDefaultTestDeployAssert1211A947.assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "35.0.0", 3 | "files": { 4 | "4012e9468badb2b8e1a18d0513e222f2ad6246cf6ddc9f167fc9add9a48bb84e": { 5 | "source": { 6 | "path": "asset.4012e9468badb2b8e1a18d0513e222f2ad6246cf6ddc9f167fc9add9a48bb84e.bundle", 7 | "packaging": "zip" 8 | }, 9 | "destinations": { 10 | "current_account-current_region": { 11 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 12 | "objectKey": "4012e9468badb2b8e1a18d0513e222f2ad6246cf6ddc9f167fc9add9a48bb84e.zip", 13 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 14 | } 15 | } 16 | }, 17 | "b8bd2a54f71520b1d464f86ce4cc612566306127a10dc128f7861015fa75c63f": { 18 | "source": { 19 | "path": "lambdarustfunctionDefaultTestDeployAssert1211A947.template.json", 20 | "packaging": "file" 21 | }, 22 | "destinations": { 23 | "current_account-current_region": { 24 | "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", 25 | "objectKey": "b8bd2a54f71520b1d464f86ce4cc612566306127a10dc128f7861015fa75c63f.json", 26 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" 27 | } 28 | } 29 | } 30 | }, 31 | "dockerImages": {} 32 | } -------------------------------------------------------------------------------- /test/integ.function.ts.snapshot/lambdarustfunctionDefaultTestDeployAssert1211A947.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Resources": { 3 | "LambdaInvoke5f64dcf3ca116111b3990b2653985d2f": { 4 | "Type": "Custom::DeployAssert@SdkCallLambdainvoke", 5 | "Properties": { 6 | "ServiceToken": { 7 | "Fn::GetAtt": [ 8 | "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F", 9 | "Arn" 10 | ] 11 | }, 12 | "service": "Lambda", 13 | "api": "invoke", 14 | "expected": "{\"$ObjectLike\":{\"Payload\":\"{\\\"statusCode\\\":200,\\\"headers\\\":{},\\\"multiValueHeaders\\\":{},\\\"body\\\":\\\"OK\\\",\\\"isBase64Encoded\\\":false}\"}}", 15 | "parameters": { 16 | "FunctionName": { 17 | "Fn::Join": [ 18 | "", 19 | [ 20 | "\"", 21 | { 22 | "Fn::ImportValue": "integ-lambda-rust-function:ExportsOutputRefbinary13A08F2FBE7683BB1" 23 | }, 24 | "\"" 25 | ] 26 | ] 27 | } 28 | }, 29 | "flattenResponse": "false", 30 | "salt": "1704368019952" 31 | }, 32 | "UpdateReplacePolicy": "Delete", 33 | "DeletionPolicy": "Delete" 34 | }, 35 | "LambdaInvoke5f64dcf3ca116111b3990b2653985d2fInvokeB1FAC0C5": { 36 | "Type": "AWS::Lambda::Permission", 37 | "Properties": { 38 | "Action": "lambda:InvokeFunction", 39 | "FunctionName": { 40 | "Fn::ImportValue": "integ-lambda-rust-function:ExportsOutputRefbinary13A08F2FBE7683BB1" 41 | }, 42 | "Principal": { 43 | "Fn::GetAtt": [ 44 | "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73", 45 | "Arn" 46 | ] 47 | } 48 | } 49 | }, 50 | "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73": { 51 | "Type": "AWS::IAM::Role", 52 | "Properties": { 53 | "AssumeRolePolicyDocument": { 54 | "Version": "2012-10-17", 55 | "Statement": [ 56 | { 57 | "Action": "sts:AssumeRole", 58 | "Effect": "Allow", 59 | "Principal": { 60 | "Service": "lambda.amazonaws.com" 61 | } 62 | } 63 | ] 64 | }, 65 | "ManagedPolicyArns": [ 66 | { 67 | "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 68 | } 69 | ], 70 | "Policies": [ 71 | { 72 | "PolicyName": "Inline", 73 | "PolicyDocument": { 74 | "Version": "2012-10-17", 75 | "Statement": [ 76 | { 77 | "Action": [ 78 | "lambda:Invoke" 79 | ], 80 | "Effect": "Allow", 81 | "Resource": [ 82 | "*" 83 | ] 84 | }, 85 | { 86 | "Action": [ 87 | "lambda:InvokeFunction" 88 | ], 89 | "Effect": "Allow", 90 | "Resource": [ 91 | { 92 | "Fn::Join": [ 93 | "", 94 | [ 95 | "arn:", 96 | { 97 | "Ref": "AWS::Partition" 98 | }, 99 | ":lambda:", 100 | { 101 | "Ref": "AWS::Region" 102 | }, 103 | ":", 104 | { 105 | "Ref": "AWS::AccountId" 106 | }, 107 | ":function:", 108 | { 109 | "Fn::ImportValue": "integ-lambda-rust-function:ExportsOutputRefbinary13A08F2FBE7683BB1" 110 | } 111 | ] 112 | ] 113 | } 114 | ] 115 | } 116 | ] 117 | } 118 | } 119 | ] 120 | } 121 | }, 122 | "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F": { 123 | "Type": "AWS::Lambda::Function", 124 | "Properties": { 125 | "Runtime": "nodejs18.x", 126 | "Code": { 127 | "S3Bucket": { 128 | "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" 129 | }, 130 | "S3Key": "4012e9468badb2b8e1a18d0513e222f2ad6246cf6ddc9f167fc9add9a48bb84e.zip" 131 | }, 132 | "Timeout": 120, 133 | "Handler": "index.handler", 134 | "Role": { 135 | "Fn::GetAtt": [ 136 | "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73", 137 | "Arn" 138 | ] 139 | } 140 | } 141 | } 142 | }, 143 | "Outputs": { 144 | "AssertionResultsLambdaInvoke5f64dcf3ca116111b3990b2653985d2f": { 145 | "Value": { 146 | "Fn::GetAtt": [ 147 | "LambdaInvoke5f64dcf3ca116111b3990b2653985d2f", 148 | "assertion" 149 | ] 150 | } 151 | } 152 | }, 153 | "Parameters": { 154 | "BootstrapVersion": { 155 | "Type": "AWS::SSM::Parameter::Value", 156 | "Default": "/cdk-bootstrap/hnb659fds/version", 157 | "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" 158 | } 159 | }, 160 | "Rules": { 161 | "CheckBootstrapVersion": { 162 | "Assertions": [ 163 | { 164 | "Assert": { 165 | "Fn::Not": [ 166 | { 167 | "Fn::Contains": [ 168 | [ 169 | "1", 170 | "2", 171 | "3", 172 | "4", 173 | "5" 174 | ], 175 | { 176 | "Ref": "BootstrapVersion" 177 | } 178 | ] 179 | } 180 | ] 181 | }, 182 | "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." 183 | } 184 | ] 185 | } 186 | } 187 | } -------------------------------------------------------------------------------- /test/integ.function.ts.snapshot/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "35.0.0", 3 | "artifacts": { 4 | "integ-lambda-rust-function.assets": { 5 | "type": "cdk:asset-manifest", 6 | "properties": { 7 | "file": "integ-lambda-rust-function.assets.json", 8 | "requiresBootstrapStackVersion": 6, 9 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 10 | } 11 | }, 12 | "integ-lambda-rust-function": { 13 | "type": "aws:cloudformation:stack", 14 | "environment": "aws://unknown-account/unknown-region", 15 | "properties": { 16 | "templateFile": "integ-lambda-rust-function.template.json", 17 | "terminationProtection": false, 18 | "validateOnSynth": false, 19 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", 20 | "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", 21 | "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/e84178c7003a608a3eda47970229795ab45634fff430f7283ca35828db0261a3.json", 22 | "requiresBootstrapStackVersion": 6, 23 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", 24 | "additionalDependencies": [ 25 | "integ-lambda-rust-function.assets" 26 | ], 27 | "lookupRole": { 28 | "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", 29 | "requiresBootstrapStackVersion": 8, 30 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 31 | } 32 | }, 33 | "dependencies": [ 34 | "integ-lambda-rust-function.assets" 35 | ], 36 | "metadata": { 37 | "/integ-lambda-rust-function/binary1/ServiceRole/Resource": [ 38 | { 39 | "type": "aws:cdk:logicalId", 40 | "data": "binary1ServiceRole6833FBF9" 41 | } 42 | ], 43 | "/integ-lambda-rust-function/binary1/Resource": [ 44 | { 45 | "type": "aws:cdk:logicalId", 46 | "data": "binary13A08F2FB" 47 | } 48 | ], 49 | "/integ-lambda-rust-function/FunctionArn": [ 50 | { 51 | "type": "aws:cdk:logicalId", 52 | "data": "FunctionArn" 53 | } 54 | ], 55 | "/integ-lambda-rust-function/Exports/Output{\"Ref\":\"binary13A08F2FB\"}": [ 56 | { 57 | "type": "aws:cdk:logicalId", 58 | "data": "ExportsOutputRefbinary13A08F2FBE7683BB1" 59 | } 60 | ], 61 | "/integ-lambda-rust-function/BootstrapVersion": [ 62 | { 63 | "type": "aws:cdk:logicalId", 64 | "data": "BootstrapVersion" 65 | } 66 | ], 67 | "/integ-lambda-rust-function/CheckBootstrapVersion": [ 68 | { 69 | "type": "aws:cdk:logicalId", 70 | "data": "CheckBootstrapVersion" 71 | } 72 | ] 73 | }, 74 | "displayName": "integ-lambda-rust-function" 75 | }, 76 | "lambdarustfunctionDefaultTestDeployAssert1211A947.assets": { 77 | "type": "cdk:asset-manifest", 78 | "properties": { 79 | "file": "lambdarustfunctionDefaultTestDeployAssert1211A947.assets.json", 80 | "requiresBootstrapStackVersion": 6, 81 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 82 | } 83 | }, 84 | "lambdarustfunctionDefaultTestDeployAssert1211A947": { 85 | "type": "aws:cloudformation:stack", 86 | "environment": "aws://unknown-account/unknown-region", 87 | "properties": { 88 | "templateFile": "lambdarustfunctionDefaultTestDeployAssert1211A947.template.json", 89 | "terminationProtection": false, 90 | "validateOnSynth": false, 91 | "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", 92 | "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", 93 | "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/b8bd2a54f71520b1d464f86ce4cc612566306127a10dc128f7861015fa75c63f.json", 94 | "requiresBootstrapStackVersion": 6, 95 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", 96 | "additionalDependencies": [ 97 | "lambdarustfunctionDefaultTestDeployAssert1211A947.assets" 98 | ], 99 | "lookupRole": { 100 | "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", 101 | "requiresBootstrapStackVersion": 8, 102 | "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" 103 | } 104 | }, 105 | "dependencies": [ 106 | "integ-lambda-rust-function", 107 | "lambdarustfunctionDefaultTestDeployAssert1211A947.assets" 108 | ], 109 | "metadata": { 110 | "/lambda-rust-function/DefaultTest/DeployAssert/LambdaInvoke5f64dcf3ca116111b3990b2653985d2f/Default/Default": [ 111 | { 112 | "type": "aws:cdk:logicalId", 113 | "data": "LambdaInvoke5f64dcf3ca116111b3990b2653985d2f" 114 | } 115 | ], 116 | "/lambda-rust-function/DefaultTest/DeployAssert/LambdaInvoke5f64dcf3ca116111b3990b2653985d2f/Invoke": [ 117 | { 118 | "type": "aws:cdk:logicalId", 119 | "data": "LambdaInvoke5f64dcf3ca116111b3990b2653985d2fInvokeB1FAC0C5" 120 | } 121 | ], 122 | "/lambda-rust-function/DefaultTest/DeployAssert/LambdaInvoke5f64dcf3ca116111b3990b2653985d2f/AssertionResults": [ 123 | { 124 | "type": "aws:cdk:logicalId", 125 | "data": "AssertionResultsLambdaInvoke5f64dcf3ca116111b3990b2653985d2f" 126 | } 127 | ], 128 | "/lambda-rust-function/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role": [ 129 | { 130 | "type": "aws:cdk:logicalId", 131 | "data": "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73" 132 | } 133 | ], 134 | "/lambda-rust-function/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler": [ 135 | { 136 | "type": "aws:cdk:logicalId", 137 | "data": "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F" 138 | } 139 | ], 140 | "/lambda-rust-function/DefaultTest/DeployAssert/BootstrapVersion": [ 141 | { 142 | "type": "aws:cdk:logicalId", 143 | "data": "BootstrapVersion" 144 | } 145 | ], 146 | "/lambda-rust-function/DefaultTest/DeployAssert/CheckBootstrapVersion": [ 147 | { 148 | "type": "aws:cdk:logicalId", 149 | "data": "CheckBootstrapVersion" 150 | } 151 | ] 152 | }, 153 | "displayName": "lambda-rust-function/DefaultTest/DeployAssert" 154 | }, 155 | "Tree": { 156 | "type": "cdk:tree", 157 | "properties": { 158 | "file": "tree.json" 159 | } 160 | } 161 | } 162 | } -------------------------------------------------------------------------------- /test/integ.function.ts.snapshot/tree.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "tree-0.1", 3 | "tree": { 4 | "id": "App", 5 | "path": "", 6 | "children": { 7 | "integ-lambda-rust-function": { 8 | "id": "integ-lambda-rust-function", 9 | "path": "integ-lambda-rust-function", 10 | "children": { 11 | "binary1": { 12 | "id": "binary1", 13 | "path": "integ-lambda-rust-function/binary1", 14 | "children": { 15 | "ServiceRole": { 16 | "id": "ServiceRole", 17 | "path": "integ-lambda-rust-function/binary1/ServiceRole", 18 | "children": { 19 | "ImportServiceRole": { 20 | "id": "ImportServiceRole", 21 | "path": "integ-lambda-rust-function/binary1/ServiceRole/ImportServiceRole", 22 | "constructInfo": { 23 | "fqn": "aws-cdk-lib.Resource", 24 | "version": "2.114.0" 25 | } 26 | }, 27 | "Resource": { 28 | "id": "Resource", 29 | "path": "integ-lambda-rust-function/binary1/ServiceRole/Resource", 30 | "attributes": { 31 | "aws:cdk:cloudformation:type": "AWS::IAM::Role", 32 | "aws:cdk:cloudformation:props": { 33 | "assumeRolePolicyDocument": { 34 | "Statement": [ 35 | { 36 | "Action": "sts:AssumeRole", 37 | "Effect": "Allow", 38 | "Principal": { 39 | "Service": "lambda.amazonaws.com" 40 | } 41 | } 42 | ], 43 | "Version": "2012-10-17" 44 | }, 45 | "managedPolicyArns": [ 46 | { 47 | "Fn::Join": [ 48 | "", 49 | [ 50 | "arn:", 51 | { 52 | "Ref": "AWS::Partition" 53 | }, 54 | ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 55 | ] 56 | ] 57 | } 58 | ] 59 | } 60 | }, 61 | "constructInfo": { 62 | "fqn": "aws-cdk-lib.aws_iam.CfnRole", 63 | "version": "2.114.0" 64 | } 65 | } 66 | }, 67 | "constructInfo": { 68 | "fqn": "aws-cdk-lib.aws_iam.Role", 69 | "version": "2.114.0" 70 | } 71 | }, 72 | "Code": { 73 | "id": "Code", 74 | "path": "integ-lambda-rust-function/binary1/Code", 75 | "children": { 76 | "Stage": { 77 | "id": "Stage", 78 | "path": "integ-lambda-rust-function/binary1/Code/Stage", 79 | "constructInfo": { 80 | "fqn": "aws-cdk-lib.AssetStaging", 81 | "version": "2.114.0" 82 | } 83 | }, 84 | "AssetBucket": { 85 | "id": "AssetBucket", 86 | "path": "integ-lambda-rust-function/binary1/Code/AssetBucket", 87 | "constructInfo": { 88 | "fqn": "aws-cdk-lib.aws_s3.BucketBase", 89 | "version": "2.114.0" 90 | } 91 | } 92 | }, 93 | "constructInfo": { 94 | "fqn": "aws-cdk-lib.aws_s3_assets.Asset", 95 | "version": "2.114.0" 96 | } 97 | }, 98 | "Resource": { 99 | "id": "Resource", 100 | "path": "integ-lambda-rust-function/binary1/Resource", 101 | "attributes": { 102 | "aws:cdk:cloudformation:type": "AWS::Lambda::Function", 103 | "aws:cdk:cloudformation:props": { 104 | "code": { 105 | "s3Bucket": { 106 | "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" 107 | }, 108 | "s3Key": "2c9536aa54ef4116ba5579355660ffe8e11a8a7bb876ca0db342eaf98c86ce37.zip" 109 | }, 110 | "handler": "rust.handler", 111 | "role": { 112 | "Fn::GetAtt": [ 113 | "binary1ServiceRole6833FBF9", 114 | "Arn" 115 | ] 116 | }, 117 | "runtime": "provided.al2023" 118 | } 119 | }, 120 | "constructInfo": { 121 | "fqn": "aws-cdk-lib.aws_lambda.CfnFunction", 122 | "version": "2.114.0" 123 | } 124 | } 125 | }, 126 | "constructInfo": { 127 | "fqn": "@aws-cdk/aws-lambda-rust.RustFunction", 128 | "version": "0.0.0" 129 | } 130 | }, 131 | "FunctionArn": { 132 | "id": "FunctionArn", 133 | "path": "integ-lambda-rust-function/FunctionArn", 134 | "constructInfo": { 135 | "fqn": "aws-cdk-lib.CfnOutput", 136 | "version": "2.114.0" 137 | } 138 | }, 139 | "Exports": { 140 | "id": "Exports", 141 | "path": "integ-lambda-rust-function/Exports", 142 | "children": { 143 | "Output{\"Ref\":\"binary13A08F2FB\"}": { 144 | "id": "Output{\"Ref\":\"binary13A08F2FB\"}", 145 | "path": "integ-lambda-rust-function/Exports/Output{\"Ref\":\"binary13A08F2FB\"}", 146 | "constructInfo": { 147 | "fqn": "aws-cdk-lib.CfnOutput", 148 | "version": "2.114.0" 149 | } 150 | } 151 | }, 152 | "constructInfo": { 153 | "fqn": "constructs.Construct", 154 | "version": "10.0.5" 155 | } 156 | }, 157 | "BootstrapVersion": { 158 | "id": "BootstrapVersion", 159 | "path": "integ-lambda-rust-function/BootstrapVersion", 160 | "constructInfo": { 161 | "fqn": "aws-cdk-lib.CfnParameter", 162 | "version": "2.114.0" 163 | } 164 | }, 165 | "CheckBootstrapVersion": { 166 | "id": "CheckBootstrapVersion", 167 | "path": "integ-lambda-rust-function/CheckBootstrapVersion", 168 | "constructInfo": { 169 | "fqn": "aws-cdk-lib.CfnRule", 170 | "version": "2.114.0" 171 | } 172 | } 173 | }, 174 | "constructInfo": { 175 | "fqn": "aws-cdk-lib.Stack", 176 | "version": "2.114.0" 177 | } 178 | }, 179 | "lambda-rust-function": { 180 | "id": "lambda-rust-function", 181 | "path": "lambda-rust-function", 182 | "children": { 183 | "DefaultTest": { 184 | "id": "DefaultTest", 185 | "path": "lambda-rust-function/DefaultTest", 186 | "children": { 187 | "Default": { 188 | "id": "Default", 189 | "path": "lambda-rust-function/DefaultTest/Default", 190 | "constructInfo": { 191 | "fqn": "constructs.Construct", 192 | "version": "10.0.5" 193 | } 194 | }, 195 | "DeployAssert": { 196 | "id": "DeployAssert", 197 | "path": "lambda-rust-function/DefaultTest/DeployAssert", 198 | "children": { 199 | "LambdaInvoke5f64dcf3ca116111b3990b2653985d2f": { 200 | "id": "LambdaInvoke5f64dcf3ca116111b3990b2653985d2f", 201 | "path": "lambda-rust-function/DefaultTest/DeployAssert/LambdaInvoke5f64dcf3ca116111b3990b2653985d2f", 202 | "children": { 203 | "SdkProvider": { 204 | "id": "SdkProvider", 205 | "path": "lambda-rust-function/DefaultTest/DeployAssert/LambdaInvoke5f64dcf3ca116111b3990b2653985d2f/SdkProvider", 206 | "children": { 207 | "AssertionsProvider": { 208 | "id": "AssertionsProvider", 209 | "path": "lambda-rust-function/DefaultTest/DeployAssert/LambdaInvoke5f64dcf3ca116111b3990b2653985d2f/SdkProvider/AssertionsProvider", 210 | "constructInfo": { 211 | "fqn": "constructs.Construct", 212 | "version": "10.0.5" 213 | } 214 | } 215 | }, 216 | "constructInfo": { 217 | "fqn": "@aws-cdk/integ-tests-alpha.AssertionsProvider", 218 | "version": "2.114.0-alpha.0" 219 | } 220 | }, 221 | "Default": { 222 | "id": "Default", 223 | "path": "lambda-rust-function/DefaultTest/DeployAssert/LambdaInvoke5f64dcf3ca116111b3990b2653985d2f/Default", 224 | "children": { 225 | "Default": { 226 | "id": "Default", 227 | "path": "lambda-rust-function/DefaultTest/DeployAssert/LambdaInvoke5f64dcf3ca116111b3990b2653985d2f/Default/Default", 228 | "constructInfo": { 229 | "fqn": "aws-cdk-lib.CfnResource", 230 | "version": "2.114.0" 231 | } 232 | } 233 | }, 234 | "constructInfo": { 235 | "fqn": "aws-cdk-lib.CustomResource", 236 | "version": "2.114.0" 237 | } 238 | }, 239 | "Invoke": { 240 | "id": "Invoke", 241 | "path": "lambda-rust-function/DefaultTest/DeployAssert/LambdaInvoke5f64dcf3ca116111b3990b2653985d2f/Invoke", 242 | "constructInfo": { 243 | "fqn": "aws-cdk-lib.CfnResource", 244 | "version": "2.114.0" 245 | } 246 | }, 247 | "AssertionResults": { 248 | "id": "AssertionResults", 249 | "path": "lambda-rust-function/DefaultTest/DeployAssert/LambdaInvoke5f64dcf3ca116111b3990b2653985d2f/AssertionResults", 250 | "constructInfo": { 251 | "fqn": "aws-cdk-lib.CfnOutput", 252 | "version": "2.114.0" 253 | } 254 | } 255 | }, 256 | "constructInfo": { 257 | "fqn": "@aws-cdk/integ-tests-alpha.LambdaInvokeFunction", 258 | "version": "2.114.0-alpha.0" 259 | } 260 | }, 261 | "SingletonFunction1488541a7b23466481b69b4408076b81": { 262 | "id": "SingletonFunction1488541a7b23466481b69b4408076b81", 263 | "path": "lambda-rust-function/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81", 264 | "children": { 265 | "Staging": { 266 | "id": "Staging", 267 | "path": "lambda-rust-function/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Staging", 268 | "constructInfo": { 269 | "fqn": "aws-cdk-lib.AssetStaging", 270 | "version": "2.114.0" 271 | } 272 | }, 273 | "Role": { 274 | "id": "Role", 275 | "path": "lambda-rust-function/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role", 276 | "constructInfo": { 277 | "fqn": "aws-cdk-lib.CfnResource", 278 | "version": "2.114.0" 279 | } 280 | }, 281 | "Handler": { 282 | "id": "Handler", 283 | "path": "lambda-rust-function/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler", 284 | "constructInfo": { 285 | "fqn": "aws-cdk-lib.CfnResource", 286 | "version": "2.114.0" 287 | } 288 | } 289 | }, 290 | "constructInfo": { 291 | "fqn": "constructs.Construct", 292 | "version": "10.0.5" 293 | } 294 | }, 295 | "BootstrapVersion": { 296 | "id": "BootstrapVersion", 297 | "path": "lambda-rust-function/DefaultTest/DeployAssert/BootstrapVersion", 298 | "constructInfo": { 299 | "fqn": "aws-cdk-lib.CfnParameter", 300 | "version": "2.114.0" 301 | } 302 | }, 303 | "CheckBootstrapVersion": { 304 | "id": "CheckBootstrapVersion", 305 | "path": "lambda-rust-function/DefaultTest/DeployAssert/CheckBootstrapVersion", 306 | "constructInfo": { 307 | "fqn": "aws-cdk-lib.CfnRule", 308 | "version": "2.114.0" 309 | } 310 | } 311 | }, 312 | "constructInfo": { 313 | "fqn": "aws-cdk-lib.Stack", 314 | "version": "2.114.0" 315 | } 316 | } 317 | }, 318 | "constructInfo": { 319 | "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", 320 | "version": "2.114.0-alpha.0" 321 | } 322 | } 323 | }, 324 | "constructInfo": { 325 | "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", 326 | "version": "2.114.0-alpha.0" 327 | } 328 | }, 329 | "Tree": { 330 | "id": "Tree", 331 | "path": "Tree", 332 | "constructInfo": { 333 | "fqn": "constructs.Construct", 334 | "version": "10.0.5" 335 | } 336 | } 337 | }, 338 | "constructInfo": { 339 | "fqn": "aws-cdk-lib.App", 340 | "version": "2.114.0" 341 | } 342 | } 343 | } -------------------------------------------------------------------------------- /test/package-manager.test.ts: -------------------------------------------------------------------------------- 1 | import * as child_process from 'child_process'; 2 | import { PackageManagerType } from '../lib'; 3 | import { PackageManager } from '../lib/package-manager'; 4 | 5 | jest.mock('child_process'); 6 | 7 | beforeEach(() => { 8 | jest.clearAllMocks(); 9 | }); 10 | 11 | test('Cargo-Zigbuild package manager', () => { 12 | const spawnSyncMock = jest.spyOn(child_process, 'spawnSync').mockReturnValue({ 13 | status: 0, 14 | stderr: Buffer.from('stderr'), 15 | stdout: Buffer.from('global-version'), 16 | pid: 123, 17 | output: ['stdout', 'stderr'], 18 | signal: null, 19 | }); 20 | 21 | expect(PackageManager.fromType(PackageManagerType.CARGO_ZIGBUILD)).toEqual({ 22 | runCommand: 'cargo-zigbuild', 23 | buildCommand: ['zigbuild'], 24 | runLocally: true, 25 | crossCompile: true, 26 | type: PackageManagerType.CARGO_ZIGBUILD, 27 | }); 28 | 29 | spawnSyncMock.mockRestore(); 30 | }); 31 | 32 | test('Cargo-Zigbuild package manager build command', () => { 33 | const spawnSyncMock = jest.spyOn(child_process, 'spawnSync').mockReturnValue({ 34 | status: 0, 35 | stderr: Buffer.from('stderr'), 36 | stdout: Buffer.from('global-version'), 37 | pid: 123, 38 | output: ['stdout', 'stderr'], 39 | signal: null, 40 | }); 41 | 42 | const manager = PackageManager.fromType(PackageManagerType.CARGO_ZIGBUILD); 43 | 44 | expect(manager.runBuildCommand()).toEqual('cargo-zigbuild zigbuild'); 45 | 46 | spawnSyncMock.mockRestore(); 47 | }); 48 | 49 | test('Cross package manager', () => { 50 | const spawnSyncMock = jest.spyOn(child_process, 'spawnSync').mockReturnValue({ 51 | status: 0, 52 | stderr: Buffer.from('stderr'), 53 | stdout: Buffer.from('global-version'), 54 | pid: 123, 55 | output: ['stdout', 'stderr'], 56 | signal: null, 57 | }); 58 | 59 | expect(PackageManager.fromType(PackageManagerType.CROSS)).toEqual({ 60 | runCommand: 'cross', 61 | buildCommand: ['build'], 62 | runLocally: true, 63 | crossCompile: true, 64 | type: PackageManagerType.CROSS, 65 | }); 66 | 67 | spawnSyncMock.mockRestore(); 68 | }); 69 | 70 | test('Cross package manager build command', () => { 71 | const spawnSyncMock = jest.spyOn(child_process, 'spawnSync').mockReturnValue({ 72 | status: 0, 73 | stderr: Buffer.from('stderr'), 74 | stdout: Buffer.from('global-version'), 75 | pid: 123, 76 | output: ['stdout', 'stderr'], 77 | signal: null, 78 | }); 79 | 80 | const manager = PackageManager.fromType(PackageManagerType.CROSS); 81 | expect(manager.runBuildCommand()).toEqual('cross build'); 82 | 83 | spawnSyncMock.mockRestore(); 84 | }); 85 | -------------------------------------------------------------------------------- /test/rust-bins/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-bins" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "my_lambda1" 8 | path = "src/bin/my_lambda1/main.rs" 9 | 10 | [[bin]] 11 | name = "my_lambda2" 12 | path = "src/bin/my_lambda2/main.rs" 13 | 14 | 15 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 16 | 17 | [dependencies] 18 | lambda_http = "0.6.1" 19 | lambda_runtime = "0.6.1" 20 | tokio = { version = "1", features = ["macros"] } 21 | tracing = { version = "0.1", features = ["log"] } 22 | tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] } 23 | 24 | [features] 25 | 26 | # We could choose to enable any project dependencies marked as `optional = true` 27 | # here, but in this case it simply serves as a config flag that we use in code. 28 | # 29 | # See https://stackoverflow.com/a/27634313/10237506 for more info. 30 | my-dev-feature = [] 31 | my-prod-feature = [] 32 | -------------------------------------------------------------------------------- /test/rust-bins/src/bin/my_lambda1/main.rs: -------------------------------------------------------------------------------- 1 | // src/main.rs 2 | 3 | use lambda_http::{run, service_fn, Body, Error, Request, RequestExt, Response}; 4 | use lambda_http::http::StatusCode; 5 | 6 | async fn function_handler(event: Request) -> Result, Error> { 7 | let resp = Response::builder() 8 | .status(StatusCode::OK) 9 | .body("OK1".into()) 10 | .map_err(Box::new)?; 11 | 12 | Ok(resp) 13 | } 14 | 15 | #[tokio::main] 16 | async fn main() -> Result<(), Error> { 17 | tracing_subscriber::fmt() 18 | .with_max_level(tracing::Level::INFO) 19 | .with_target(false) 20 | .without_time() 21 | .init(); 22 | run(service_fn(function_handler)).await 23 | } 24 | -------------------------------------------------------------------------------- /test/rust-bins/src/bin/my_lambda2/main.rs: -------------------------------------------------------------------------------- 1 | // src/main.rs 2 | 3 | use lambda_http::{run, service_fn, Body, Error, Request, RequestExt, Response}; 4 | use lambda_http::http::StatusCode; 5 | 6 | async fn function_handler(event: Request) -> Result, Error> { 7 | let resp = Response::builder() 8 | .status(StatusCode::OK) 9 | .body("OK2".into()) 10 | .map_err(Box::new)?; 11 | 12 | Ok(resp) 13 | } 14 | 15 | #[tokio::main] 16 | async fn main() -> Result<(), Error> { 17 | tracing_subscriber::fmt() 18 | .with_max_level(tracing::Level::INFO) 19 | .with_target(false) 20 | .without_time() 21 | .init(); 22 | run(service_fn(function_handler)).await 23 | } 24 | -------------------------------------------------------------------------------- /test/rust-standalone/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sample-rust" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | lambda_http = "0.6.1" 10 | lambda_runtime = "0.6.1" 11 | tokio = { version = "1", features = ["macros"] } 12 | tracing = { version = "0.1", features = ["log"] } 13 | tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] } 14 | -------------------------------------------------------------------------------- /test/rust-standalone/src/main.rs: -------------------------------------------------------------------------------- 1 | // src/main.rs 2 | 3 | use lambda_http::{run, service_fn, Body, Error, Request, RequestExt, Response}; 4 | use lambda_http::http::StatusCode; 5 | 6 | async fn function_handler(event: Request) -> Result, Error> { 7 | let resp = Response::builder() 8 | .status(StatusCode::OK) 9 | .body("OK".into()) 10 | .map_err(Box::new)?; 11 | 12 | Ok(resp) 13 | } 14 | 15 | #[tokio::main] 16 | async fn main() -> Result<(), Error> { 17 | tracing_subscriber::fmt() 18 | .with_max_level(tracing::Level::INFO) 19 | .with_target(false) 20 | .without_time() 21 | .init(); 22 | run(service_fn(function_handler)).await 23 | } 24 | -------------------------------------------------------------------------------- /test/rust-workspaces/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "my_lambda1", 4 | "my_lambda2", 5 | ] 6 | -------------------------------------------------------------------------------- /test/rust-workspaces/my_lambda1/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "my_lambda1" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | lambda_http = "0.6.1" 10 | lambda_runtime = "0.6.1" 11 | tokio = { version = "1", features = ["macros"] } 12 | tracing = { version = "0.1", features = ["log"] } 13 | tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] } 14 | 15 | 16 | [features] 17 | 18 | # We could choose to enable any project dependencies marked as `optional = true` 19 | # here, but in this case it simply serves as a config flag that we use in code. 20 | # 21 | # See https://stackoverflow.com/a/27634313/10237506 for more info. 22 | my-dev-feature = [] 23 | my-prod-feature = [] 24 | -------------------------------------------------------------------------------- /test/rust-workspaces/my_lambda1/src/main.rs: -------------------------------------------------------------------------------- 1 | // src/main.rs 2 | 3 | use lambda_http::{run, service_fn, Body, Error, Request, RequestExt, Response}; 4 | 5 | async fn function_handler(event: Request) -> Result, Error> { 6 | let resp = Response::builder() 7 | .status(200) 8 | .body("OK1".into()) 9 | .map_err(Box::new)?; 10 | Ok(resp) 11 | } 12 | 13 | #[tokio::main] 14 | async fn main() -> Result<(), Error> { 15 | tracing_subscriber::fmt() 16 | .with_max_level(tracing::Level::INFO) 17 | .with_target(false) 18 | .without_time() 19 | .init(); 20 | run(service_fn(function_handler)).await 21 | } 22 | -------------------------------------------------------------------------------- /test/rust-workspaces/my_lambda2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "my_lambda2" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | lambda_http = "0.6.1" 10 | lambda_runtime = "0.6.1" 11 | tokio = { version = "1", features = ["macros"] } 12 | tracing = { version = "0.1", features = ["log"] } 13 | tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] } 14 | -------------------------------------------------------------------------------- /test/rust-workspaces/my_lambda2/src/main.rs: -------------------------------------------------------------------------------- 1 | // src/main.rs 2 | 3 | use lambda_http::{run, service_fn, Body, Error, Request, RequestExt, Response}; 4 | 5 | async fn function_handler(event: Request) -> Result, Error> { 6 | let resp = Response::builder() 7 | .status(200) 8 | .body("OK2".into()) 9 | .map_err(Box::new)?; 10 | Ok(resp) 11 | } 12 | 13 | #[tokio::main] 14 | async fn main() -> Result<(), Error> { 15 | tracing_subscriber::fmt() 16 | .with_max_level(tracing::Level::INFO) 17 | .with_target(false) 18 | .without_time() 19 | .init(); 20 | run(service_fn(function_handler)).await 21 | } 22 | -------------------------------------------------------------------------------- /test/util.test.ts: -------------------------------------------------------------------------------- 1 | import * as child_process from 'child_process'; 2 | import { join } from 'path'; 3 | import { Bundling } from '../lib/bundling'; 4 | import { 5 | callsites, 6 | checkInstalledTarget, 7 | exec, 8 | getBinaryName, 9 | hasMultipleBinaries, 10 | canRunLocally, 11 | isWorkspace, 12 | } from '../lib/util'; 13 | 14 | jest.mock('child_process'); 15 | 16 | beforeEach(() => { 17 | jest.clearAllMocks(); 18 | }); 19 | 20 | describe('exec', () => { 21 | test('normal execution', () => { 22 | const spawnSyncMock = jest 23 | .spyOn(child_process, 'spawnSync') 24 | .mockReturnValue({ 25 | status: 0, 26 | stderr: Buffer.from('stderr'), 27 | stdout: Buffer.from('stdout'), 28 | pid: 123, 29 | output: ['stdout', 'stderr'], 30 | signal: null, 31 | }); 32 | 33 | const proc = exec('cmd', ['arg1', 'arg2'], { env: { KEY: 'value' } }); 34 | 35 | expect(spawnSyncMock).toHaveBeenCalledWith('cmd', ['arg1', 'arg2'], { 36 | env: { KEY: 'value' }, 37 | }); 38 | expect(proc.stdout.toString()).toBe('stdout'); 39 | 40 | spawnSyncMock.mockRestore(); 41 | }); 42 | 43 | test('non zero status', () => { 44 | const spawnSyncMock = jest 45 | .spyOn(child_process, 'spawnSync') 46 | .mockReturnValue({ 47 | status: 999, 48 | stderr: Buffer.from('error occured'), 49 | stdout: Buffer.from('stdout'), 50 | pid: 123, 51 | output: ['stdout', 'stderr'], 52 | signal: null, 53 | }); 54 | 55 | expect(() => exec('cmd', ['arg1', 'arg2'])).toThrow('error occured'); 56 | 57 | spawnSyncMock.mockRestore(); 58 | }); 59 | 60 | test('with error', () => { 61 | const spawnSyncMock = jest 62 | .spyOn(child_process, 'spawnSync') 63 | .mockReturnValue({ 64 | error: new Error('bad error'), 65 | status: 0, 66 | stderr: Buffer.from('stderr'), 67 | stdout: Buffer.from('stdout'), 68 | pid: 123, 69 | output: ['stdout', 'stderr'], 70 | signal: null, 71 | }); 72 | 73 | expect(() => exec('cmd', ['arg1', 'arg2'])).toThrow(new Error('bad error')); 74 | 75 | spawnSyncMock.mockRestore(); 76 | }); 77 | }); 78 | 79 | describe('canRunLocally', () => { 80 | test('Can run cmd locally', () => { 81 | const spawnSyncMock = jest 82 | .spyOn(child_process, 'spawnSync') 83 | .mockReturnValue({ 84 | status: 0, 85 | stderr: Buffer.from('stderr'), 86 | stdout: Buffer.from('global-version'), 87 | pid: 123, 88 | output: ['stdout', 'stderr'], 89 | signal: null, 90 | }); 91 | 92 | expect(canRunLocally('test')).toEqual(true); 93 | 94 | spawnSyncMock.mockRestore(); 95 | }); 96 | 97 | test('Cannot run cmd locally', () => { 98 | const spawnSyncMock = jest 99 | .spyOn(child_process, 'spawnSync') 100 | .mockReturnValue({ 101 | error: new Error('bad error'), 102 | status: 0, 103 | stderr: Buffer.from('stderr'), 104 | stdout: Buffer.from('global-version'), 105 | pid: 123, 106 | output: ['stdout', 'stderr'], 107 | signal: null, 108 | }); 109 | 110 | expect(canRunLocally('test')).toEqual(false); 111 | 112 | spawnSyncMock.mockRestore(); 113 | }); 114 | }); 115 | 116 | describe('canRunLocally', () => { 117 | test('Can run cmd locally', () => { 118 | const spawnSyncMock = jest 119 | .spyOn(child_process, 'spawnSync') 120 | .mockReturnValue({ 121 | status: 0, 122 | stderr: Buffer.from('stderr'), 123 | stdout: Buffer.from('global-version'), 124 | pid: 123, 125 | output: ['stdout', 'stderr'], 126 | signal: null, 127 | }); 128 | 129 | expect(canRunLocally('test')).toEqual(true); 130 | 131 | spawnSyncMock.mockRestore(); 132 | }); 133 | 134 | test('Cannot run cmd locally', () => { 135 | const spawnSyncMock = jest 136 | .spyOn(child_process, 'spawnSync') 137 | .mockReturnValue({ 138 | error: new Error('bad error'), 139 | status: 0, 140 | stderr: Buffer.from('stderr'), 141 | stdout: Buffer.from('global-version'), 142 | pid: 123, 143 | output: ['stdout', 'stderr'], 144 | signal: null, 145 | }); 146 | 147 | expect(canRunLocally('test')).toEqual(false); 148 | 149 | spawnSyncMock.mockRestore(); 150 | }); 151 | }); 152 | 153 | describe('checkInstalledTarget', () => { 154 | test('Target installed', () => { 155 | const spawnSyncMock = jest 156 | .spyOn(child_process, 'spawnSync') 157 | .mockReturnValue({ 158 | status: 0, 159 | stderr: Buffer.from('stderr'), 160 | stdout: Buffer.from( 161 | '' + 'aarch64-unknown-linux-gnu\n' + 'x86_64-unknown-linux-gnu', 162 | ), 163 | pid: 123, 164 | output: ['stdout', 'stderr'], 165 | signal: null, 166 | }); 167 | 168 | expect(checkInstalledTarget(Bundling.ARM_TARGET)).toEqual(true); 169 | 170 | spawnSyncMock.mockRestore(); 171 | }); 172 | 173 | test('Target not installed', () => { 174 | const spawnSyncMock = jest 175 | .spyOn(child_process, 'spawnSync') 176 | .mockReturnValue({ 177 | status: 0, 178 | stderr: Buffer.from('stderr'), 179 | stdout: Buffer.from( 180 | '' + 'aarch64-apple-darwin\n' + 'x86_64-unknown-linux-gnu', 181 | ), 182 | pid: 123, 183 | output: ['stdout', 'stderr'], 184 | signal: null, 185 | }); 186 | 187 | expect(checkInstalledTarget(Bundling.ARM_TARGET)).toEqual(false); 188 | 189 | spawnSyncMock.mockRestore(); 190 | }); 191 | }); 192 | 193 | describe('callsites', () => { 194 | expect(callsites()[0].getFileName()).toMatch(/\/test\/util.test.ts$/); 195 | }); 196 | 197 | describe('getBinaryName', () => { 198 | test('From package name', () => { 199 | const manifestPath = join(__dirname, 'rust-standalone', 'Cargo.toml'); 200 | const binaryName = getBinaryName(manifestPath); 201 | expect(binaryName).toEqual('sample-rust'); 202 | }); 203 | 204 | test('From bin name', () => { 205 | const manifestPath = join(__dirname, 'function.test.binary1', 'Cargo.toml'); 206 | const binaryName = getBinaryName(manifestPath); 207 | expect(binaryName).toEqual('binary1'); 208 | }); 209 | 210 | test('From multiple bins project', () => { 211 | const manifestPath = join(__dirname, 'rust-bins', 'Cargo.toml'); 212 | const binaryName = getBinaryName(manifestPath); 213 | expect(binaryName).toEqual('rust-bins'); 214 | }); 215 | 216 | test('From workspace project', () => { 217 | const manifestPath = join(__dirname, 'rust-workspaces', 'Cargo.toml'); 218 | const binaryName = getBinaryName(manifestPath); 219 | expect(binaryName).toBeUndefined(); 220 | }); 221 | 222 | test('From non existing project', () => { 223 | expect(() => getBinaryName('test-unknown-path')).toThrow( 224 | 'Unable to parse Manifest file `test-unknown-path`', 225 | ); 226 | }); 227 | }); 228 | 229 | describe('hasMultipleBinaries', () => { 230 | test('From standalone project', () => { 231 | const manifestPath = join(__dirname, 'rust-standalone', 'Cargo.toml'); 232 | expect(hasMultipleBinaries(manifestPath)).toBeFalsy(); 233 | }); 234 | 235 | test('From multiple bins project', () => { 236 | const manifestPath = join(__dirname, 'rust-bins', 'Cargo.toml'); 237 | expect(hasMultipleBinaries(manifestPath)).toBeTruthy(); 238 | }); 239 | 240 | test('From workspace project', () => { 241 | const manifestPath = join(__dirname, 'rust-workspaces', 'Cargo.toml'); 242 | expect(hasMultipleBinaries(manifestPath)).toBeFalsy(); 243 | }); 244 | 245 | test('From non existing project', () => { 246 | expect(() => getBinaryName('test-unknown-path')).toThrow( 247 | 'Unable to parse Manifest file `test-unknown-path`', 248 | ); 249 | }); 250 | }); 251 | 252 | describe('isWorkspace', () => { 253 | test('From standalone project', () => { 254 | const manifestPath = join(__dirname, 'rust-standalone', 'Cargo.toml'); 255 | expect(isWorkspace(manifestPath)).toBeFalsy(); 256 | }); 257 | 258 | test('From multiple bins project', () => { 259 | const manifestPath = join(__dirname, 'rust-bins', 'Cargo.toml'); 260 | expect(isWorkspace(manifestPath)).toBeFalsy(); 261 | }); 262 | 263 | test('From workspace project', () => { 264 | const manifestPath = join(__dirname, 'rust-workspaces', 'Cargo.toml'); 265 | expect(isWorkspace(manifestPath)).toBeTruthy(); 266 | }); 267 | 268 | test('From non existing project', () => { 269 | expect(() => getBinaryName('test-unknown-path')).toThrow( 270 | 'Unable to parse Manifest file `test-unknown-path`', 271 | ); 272 | }); 273 | }); 274 | -------------------------------------------------------------------------------- /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 | "es2020" 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": "ES2020" 27 | }, 28 | "include": [ 29 | "src/**/*.ts", 30 | "test/**/*.ts", 31 | ".projenrc.ts", 32 | "projenrc/**/*.ts" 33 | ], 34 | "exclude": [ 35 | "node_modules" 36 | ] 37 | } 38 | --------------------------------------------------------------------------------