├── .eslintrc.json
├── .gitattributes
├── .github
├── pull_request_template.md
└── workflows
│ ├── auto-approve.yml
│ ├── build.yml
│ ├── pull-request-lint.yml
│ ├── release.yml
│ └── upgrade-main.yml
├── .gitignore
├── .mergify.yml
├── .npmignore
├── .projen
├── deps.json
├── files.json
└── tasks.json
├── .projenrc.ts
├── API.md
├── API.md.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── NOTICE
├── README.md
├── package.json
├── src
├── codebuild-handler
│ └── index.ts
├── destination.ts
├── docker-image-deployment.ts
├── index.ts
├── login.ts
└── source.ts
├── test
├── assets
│ ├── test1
│ │ └── Dockerfile
│ └── test2
│ │ └── Dockerfile
├── docker-image-deploy.test.ts
└── integ
│ └── app.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 | "@stylistic"
12 | ],
13 | "parser": "@typescript-eslint/parser",
14 | "parserOptions": {
15 | "ecmaVersion": 2018,
16 | "sourceType": "module",
17 | "project": "./tsconfig.dev.json"
18 | },
19 | "extends": [
20 | "plugin:import/typescript"
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 | "@stylistic/indent": [
48 | "error",
49 | 2
50 | ],
51 | "@stylistic/quotes": [
52 | "error",
53 | "single",
54 | {
55 | "avoidEscape": true
56 | }
57 | ],
58 | "@stylistic/comma-dangle": [
59 | "error",
60 | "always-multiline"
61 | ],
62 | "@stylistic/comma-spacing": [
63 | "error",
64 | {
65 | "before": false,
66 | "after": true
67 | }
68 | ],
69 | "@stylistic/no-multi-spaces": [
70 | "error",
71 | {
72 | "ignoreEOLComments": false
73 | }
74 | ],
75 | "@stylistic/array-bracket-spacing": [
76 | "error",
77 | "never"
78 | ],
79 | "@stylistic/array-bracket-newline": [
80 | "error",
81 | "consistent"
82 | ],
83 | "@stylistic/object-curly-spacing": [
84 | "error",
85 | "always"
86 | ],
87 | "@stylistic/object-curly-newline": [
88 | "error",
89 | {
90 | "multiline": true,
91 | "consistent": true
92 | }
93 | ],
94 | "@stylistic/object-property-newline": [
95 | "error",
96 | {
97 | "allowAllPropertiesOnSameLine": true
98 | }
99 | ],
100 | "@stylistic/keyword-spacing": [
101 | "error"
102 | ],
103 | "@stylistic/brace-style": [
104 | "error",
105 | "1tbs",
106 | {
107 | "allowSingleLine": true
108 | }
109 | ],
110 | "@stylistic/space-before-blocks": [
111 | "error"
112 | ],
113 | "@stylistic/member-delimiter-style": [
114 | "error"
115 | ],
116 | "@stylistic/semi": [
117 | "error",
118 | "always"
119 | ],
120 | "@stylistic/max-len": [
121 | "error",
122 | {
123 | "code": 150,
124 | "ignoreUrls": true,
125 | "ignoreStrings": true,
126 | "ignoreTemplateLiterals": true,
127 | "ignoreComments": true,
128 | "ignoreRegExpLiterals": true
129 | }
130 | ],
131 | "@stylistic/quote-props": [
132 | "error",
133 | "consistent-as-needed"
134 | ],
135 | "@stylistic/key-spacing": [
136 | "error"
137 | ],
138 | "@stylistic/no-multiple-empty-lines": [
139 | "error"
140 | ],
141 | "@stylistic/no-trailing-spaces": [
142 | "error"
143 | ],
144 | "curly": [
145 | "error",
146 | "multi-line",
147 | "consistent"
148 | ],
149 | "@typescript-eslint/no-require-imports": "error",
150 | "import/no-extraneous-dependencies": [
151 | "error",
152 | {
153 | "devDependencies": [
154 | "**/test/**",
155 | "**/build-tools/**",
156 | ".projenrc.ts",
157 | "projenrc/**/*.ts"
158 | ],
159 | "optionalDependencies": false,
160 | "peerDependencies": true
161 | }
162 | ],
163 | "import/no-unresolved": [
164 | "error"
165 | ],
166 | "import/order": [
167 | "warn",
168 | {
169 | "groups": [
170 | "builtin",
171 | "external"
172 | ],
173 | "alphabetize": {
174 | "order": "asc",
175 | "caseInsensitive": true
176 | }
177 | }
178 | ],
179 | "import/no-duplicates": [
180 | "error"
181 | ],
182 | "no-shadow": [
183 | "off"
184 | ],
185 | "@typescript-eslint/no-shadow": "error",
186 | "@typescript-eslint/no-floating-promises": "error",
187 | "no-return-await": [
188 | "off"
189 | ],
190 | "@typescript-eslint/return-await": "error",
191 | "dot-notation": [
192 | "error"
193 | ],
194 | "no-bitwise": [
195 | "error"
196 | ],
197 | "@typescript-eslint/member-ordering": [
198 | "error",
199 | {
200 | "default": [
201 | "public-static-field",
202 | "public-static-method",
203 | "protected-static-field",
204 | "protected-static-method",
205 | "private-static-field",
206 | "private-static-method",
207 | "field",
208 | "constructor",
209 | "method"
210 | ]
211 | }
212 | ]
213 | },
214 | "overrides": [
215 | {
216 | "files": [
217 | ".projenrc.ts"
218 | ],
219 | "rules": {
220 | "@typescript-eslint/no-require-imports": "off",
221 | "import/no-extraneous-dependencies": "off"
222 | }
223 | }
224 | ]
225 | }
226 |
--------------------------------------------------------------------------------
/.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/build.yml linguist-generated
10 | /.github/workflows/pull-request-lint.yml linguist-generated
11 | /.github/workflows/release.yml linguist-generated
12 | /.github/workflows/upgrade-main.yml linguist-generated
13 | /.gitignore linguist-generated
14 | /.mergify.yml linguist-generated
15 | /.npmignore linguist-generated
16 | /.projen/** linguist-generated
17 | /.projen/deps.json linguist-generated
18 | /.projen/files.json linguist-generated
19 | /.projen/tasks.json linguist-generated
20 | /API.md linguist-generated
21 | /LICENSE linguist-generated
22 | /package.json linguist-generated
23 | /tsconfig.dev.json linguist-generated
24 | /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')
18 | steps:
19 | - uses: hmarr/auto-approve-action@f0939ea97e9205ef24d872e76833fa908a770363
20 | with:
21 | github-token: ${{ secrets.GITHUB_TOKEN }}
22 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen".
2 |
3 | name: build
4 | on:
5 | pull_request: {}
6 | workflow_dispatch: {}
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 | permissions:
11 | contents: write
12 | outputs:
13 | self_mutation_happened: ${{ steps.self_mutation.outputs.self_mutation_happened }}
14 | env:
15 | CI: "true"
16 | steps:
17 | - name: Checkout
18 | uses: actions/checkout@v5
19 | with:
20 | ref: ${{ github.event.pull_request.head.ref }}
21 | repository: ${{ github.event.pull_request.head.repo.full_name }}
22 | - name: Setup Node.js
23 | uses: actions/setup-node@v5
24 | with:
25 | node-version: lts/*
26 | - name: Install dependencies
27 | run: yarn install --check-files
28 | - name: build
29 | run: npx projen build
30 | - name: Find mutations
31 | id: self_mutation
32 | run: |-
33 | git add .
34 | git diff --staged --patch --exit-code > repo.patch || echo "self_mutation_happened=true" >> $GITHUB_OUTPUT
35 | shell: bash
36 | working-directory: ./
37 | - name: Upload patch
38 | if: steps.self_mutation.outputs.self_mutation_happened
39 | uses: actions/upload-artifact@v4.6.2
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.6.2
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@v5
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@v5
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[bot]"
82 | git config user.email "41898282+github-actions[bot]@users.noreply.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@v5
98 | with:
99 | node-version: lts/*
100 | - name: Download build artifacts
101 | uses: actions/download-artifact@v5
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@v5
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@v5
132 | with:
133 | distribution: corretto
134 | java-version: "11"
135 | - uses: actions/setup-node@v5
136 | with:
137 | node-version: lts/*
138 | - name: Download build artifacts
139 | uses: actions/download-artifact@v5
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@v5
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@v5
170 | with:
171 | node-version: lts/*
172 | - uses: actions/setup-python@v6
173 | with:
174 | python-version: 3.x
175 | - name: Download build artifacts
176 | uses: actions/download-artifact@v5
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@v5
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@v5
207 | with:
208 | node-version: lts/*
209 | - uses: actions/setup-dotnet@v5
210 | with:
211 | dotnet-version: 6.x
212 | - name: Download build artifacts
213 | uses: actions/download-artifact@v5
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@v5
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@v6
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@v5
25 | with:
26 | fetch-depth: 0
27 | - name: Set git identity
28 | run: |-
29 | git config user.name "github-actions[bot]"
30 | git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
31 | - name: Setup Node.js
32 | uses: actions/setup-node@v5
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 | shell: bash
51 | - name: Backup artifact permissions
52 | if: ${{ steps.git_remote.outputs.latest_commit == github.sha }}
53 | run: cd dist && getfacl -R . > permissions-backup.acl
54 | continue-on-error: true
55 | - name: Upload artifact
56 | if: ${{ steps.git_remote.outputs.latest_commit == github.sha }}
57 | uses: actions/upload-artifact@v4.6.2
58 | with:
59 | name: build-artifact
60 | path: dist
61 | overwrite: true
62 | release_github:
63 | name: Publish to GitHub Releases
64 | needs:
65 | - release
66 | - release_npm
67 | - release_maven
68 | - release_pypi
69 | - release_nuget
70 | runs-on: ubuntu-latest
71 | permissions:
72 | contents: write
73 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha
74 | steps:
75 | - uses: actions/setup-node@v5
76 | with:
77 | node-version: lts/*
78 | - name: Download build artifacts
79 | uses: actions/download-artifact@v5
80 | with:
81 | name: build-artifact
82 | path: dist
83 | - name: Restore build artifact permissions
84 | run: cd dist && setfacl --restore=permissions-backup.acl
85 | continue-on-error: true
86 | - name: Release
87 | env:
88 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
89 | 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
90 | release_npm:
91 | name: Publish to npm
92 | needs: release
93 | runs-on: ubuntu-latest
94 | permissions:
95 | id-token: write
96 | contents: read
97 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha
98 | steps:
99 | - uses: actions/setup-node@v5
100 | with:
101 | node-version: lts/*
102 | - name: Download build artifacts
103 | uses: actions/download-artifact@v5
104 | with:
105 | name: build-artifact
106 | path: dist
107 | - name: Restore build artifact permissions
108 | run: cd dist && setfacl --restore=permissions-backup.acl
109 | continue-on-error: true
110 | - name: Checkout
111 | uses: actions/checkout@v5
112 | with:
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 | - name: Release
125 | env:
126 | NPM_DIST_TAG: latest
127 | NPM_REGISTRY: registry.npmjs.org
128 | NPM_CONFIG_PROVENANCE: "true"
129 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
130 | run: npx -p publib@latest publib-npm
131 | release_maven:
132 | name: Publish to Maven Central
133 | needs: release
134 | runs-on: ubuntu-latest
135 | permissions:
136 | contents: read
137 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha
138 | steps:
139 | - uses: actions/setup-java@v5
140 | with:
141 | distribution: corretto
142 | java-version: "11"
143 | - uses: actions/setup-node@v5
144 | with:
145 | node-version: lts/*
146 | - name: Download build artifacts
147 | uses: actions/download-artifact@v5
148 | with:
149 | name: build-artifact
150 | path: dist
151 | - name: Restore build artifact permissions
152 | run: cd dist && setfacl --restore=permissions-backup.acl
153 | continue-on-error: true
154 | - name: Checkout
155 | uses: actions/checkout@v5
156 | with:
157 | path: .repo
158 | - name: Install Dependencies
159 | run: cd .repo && yarn install --check-files --frozen-lockfile
160 | - name: Extract build artifact
161 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo
162 | - name: Move build artifact out of the way
163 | run: mv dist dist.old
164 | - name: Create java artifact
165 | run: cd .repo && npx projen package:java
166 | - name: Collect java artifact
167 | run: mv .repo/dist dist
168 | - name: Release
169 | env:
170 | MAVEN_SERVER_ID: central-ossrh
171 | MAVEN_GPG_PRIVATE_KEY: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }}
172 | MAVEN_GPG_PRIVATE_KEY_PASSPHRASE: ${{ secrets.MAVEN_GPG_PRIVATE_KEY_PASSPHRASE }}
173 | MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
174 | MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
175 | MAVEN_STAGING_PROFILE_ID: ${{ secrets.MAVEN_STAGING_PROFILE_ID }}
176 | run: npx -p publib@latest publib-maven
177 | release_pypi:
178 | name: Publish to PyPI
179 | needs: release
180 | runs-on: ubuntu-latest
181 | permissions:
182 | contents: read
183 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha
184 | steps:
185 | - uses: actions/setup-node@v5
186 | with:
187 | node-version: lts/*
188 | - uses: actions/setup-python@v6
189 | with:
190 | python-version: 3.x
191 | - name: Download build artifacts
192 | uses: actions/download-artifact@v5
193 | with:
194 | name: build-artifact
195 | path: dist
196 | - name: Restore build artifact permissions
197 | run: cd dist && setfacl --restore=permissions-backup.acl
198 | continue-on-error: true
199 | - name: Checkout
200 | uses: actions/checkout@v5
201 | with:
202 | path: .repo
203 | - name: Install Dependencies
204 | run: cd .repo && yarn install --check-files --frozen-lockfile
205 | - name: Extract build artifact
206 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo
207 | - name: Move build artifact out of the way
208 | run: mv dist dist.old
209 | - name: Create python artifact
210 | run: cd .repo && npx projen package:python
211 | - name: Collect python artifact
212 | run: mv .repo/dist dist
213 | - name: Release
214 | env:
215 | TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }}
216 | TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}
217 | run: npx -p publib@latest publib-pypi
218 | release_nuget:
219 | name: Publish to NuGet Gallery
220 | needs: release
221 | runs-on: ubuntu-latest
222 | permissions:
223 | contents: read
224 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha
225 | steps:
226 | - uses: actions/setup-node@v5
227 | with:
228 | node-version: lts/*
229 | - uses: actions/setup-dotnet@v5
230 | with:
231 | dotnet-version: 6.x
232 | - name: Download build artifacts
233 | uses: actions/download-artifact@v5
234 | with:
235 | name: build-artifact
236 | path: dist
237 | - name: Restore build artifact permissions
238 | run: cd dist && setfacl --restore=permissions-backup.acl
239 | continue-on-error: true
240 | - name: Checkout
241 | uses: actions/checkout@v5
242 | with:
243 | path: .repo
244 | - name: Install Dependencies
245 | run: cd .repo && yarn install --check-files --frozen-lockfile
246 | - name: Extract build artifact
247 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo
248 | - name: Move build artifact out of the way
249 | run: mv dist dist.old
250 | - name: Create dotnet artifact
251 | run: cd .repo && npx projen package:dotnet
252 | - name: Collect dotnet artifact
253 | run: mv .repo/dist dist
254 | - name: Release
255 | env:
256 | NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
257 | run: npx -p publib@latest publib-nuget
258 |
--------------------------------------------------------------------------------
/.github/workflows/upgrade-main.yml:
--------------------------------------------------------------------------------
1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen".
2 |
3 | name: upgrade-main
4 | on:
5 | workflow_dispatch: {}
6 | schedule:
7 | - cron: 0 0 * * *
8 | jobs:
9 | upgrade:
10 | name: Upgrade
11 | runs-on: ubuntu-latest
12 | permissions:
13 | contents: read
14 | outputs:
15 | patch_created: ${{ steps.create_patch.outputs.patch_created }}
16 | steps:
17 | - name: Checkout
18 | uses: actions/checkout@v5
19 | with:
20 | ref: main
21 | - name: Setup Node.js
22 | uses: actions/setup-node@v5
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 | shell: bash
35 | working-directory: ./
36 | - name: Upload patch
37 | if: steps.create_patch.outputs.patch_created
38 | uses: actions/upload-artifact@v4.6.2
39 | with:
40 | name: repo.patch
41 | path: repo.patch
42 | overwrite: true
43 | pr:
44 | name: Create Pull Request
45 | needs: upgrade
46 | runs-on: ubuntu-latest
47 | permissions:
48 | contents: read
49 | if: ${{ needs.upgrade.outputs.patch_created }}
50 | steps:
51 | - name: Checkout
52 | uses: actions/checkout@v5
53 | with:
54 | ref: main
55 | - name: Download patch
56 | uses: actions/download-artifact@v5
57 | with:
58 | name: repo.patch
59 | path: ${{ runner.temp }}
60 | - name: Apply patch
61 | run: '[ -s ${{ runner.temp }}/repo.patch ] && git apply ${{ runner.temp }}/repo.patch || echo "Empty patch. Skipping."'
62 | - name: Set git identity
63 | run: |-
64 | git config user.name "github-actions[bot]"
65 | git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
66 | - name: Create Pull Request
67 | id: create-pr
68 | uses: peter-evans/create-pull-request@v7
69 | with:
70 | token: ${{ secrets.PROJEN_GITHUB_TOKEN }}
71 | commit-message: |-
72 | chore(deps): upgrade dependencies
73 |
74 | Upgrades project dependencies. See details in [workflow run].
75 |
76 | [Workflow Run]: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
77 |
78 | ------
79 |
80 | *Automatically created by projen via the "upgrade-main" workflow*
81 | branch: github-actions/upgrade-main
82 | title: "chore(deps): upgrade dependencies"
83 | labels: auto-approve
84 | body: |-
85 | Upgrades project dependencies. See details in [workflow run].
86 |
87 | [Workflow Run]: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
88 |
89 | ------
90 |
91 | *Automatically created by projen via the "upgrade-main" workflow*
92 | author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
93 | committer: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
94 | signoff: true
95 |
--------------------------------------------------------------------------------
/.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 | /cdk.out
35 | /test-reports/
36 | junit.xml
37 | /coverage/
38 | !/.github/workflows/build.yml
39 | /dist/changelog.md
40 | /dist/version.txt
41 | !/.github/workflows/release.yml
42 | !/.mergify.yml
43 | !/.github/workflows/upgrade-main.yml
44 | !/.github/pull_request_template.md
45 | !/test/
46 | !/tsconfig.dev.json
47 | !/src/
48 | /lib
49 | /dist/
50 | !/.eslintrc.json
51 | .jsii
52 | tsconfig.json
53 | !/API.md
54 | !/.projenrc.ts
55 |
--------------------------------------------------------------------------------
/.mergify.yml:
--------------------------------------------------------------------------------
1 | # ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen".
2 |
3 | queue_rules:
4 | - name: default
5 | update_method: merge
6 | queue_conditions:
7 | - "#approved-reviews-by>=1"
8 | - -label~=(do-not-merge)
9 | - status-success=build
10 | - status-success=package-js
11 | - status-success=package-java
12 | - status-success=package-python
13 | - status-success=package-dotnet
14 | merge_method: squash
15 | commit_message_template: |-
16 | {{ title }} (#{{ number }})
17 |
18 | {{ body }}
19 | pull_request_rules:
20 | - name: Automatic merge on approval and successful build
21 | actions:
22 | delete_head_branch: {}
23 | queue:
24 | name: default
25 | conditions:
26 | - "#approved-reviews-by>=1"
27 | - -label~=(do-not-merge)
28 | - status-success=build
29 | - status-success=package-js
30 | - status-success=package-java
31 | - status-success=package-python
32 | - status-success=package-dotnet
33 | merge_queue:
34 | max_parallel_checks: 1
35 |
--------------------------------------------------------------------------------
/.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 | /.mergify.yml
10 | /test/
11 | /tsconfig.dev.json
12 | /src/
13 | !/lib/
14 | !/lib/**/*.js
15 | !/lib/**/*.d.ts
16 | dist
17 | /tsconfig.json
18 | /.github/
19 | /.vscode/
20 | /.idea/
21 | /.projenrc.js
22 | tsconfig.tsbuildinfo
23 | /.eslintrc.json
24 | !.jsii
25 | /.gitattributes
26 | /.projenrc.ts
27 | /projenrc
28 |
--------------------------------------------------------------------------------
/.projen/deps.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": [
3 | {
4 | "name": "@stylistic/eslint-plugin",
5 | "version": "^2",
6 | "type": "build"
7 | },
8 | {
9 | "name": "@types/jest",
10 | "type": "build"
11 | },
12 | {
13 | "name": "@types/node",
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": "commit-and-tag-version",
28 | "version": "^12",
29 | "type": "build"
30 | },
31 | {
32 | "name": "esbuild",
33 | "type": "build"
34 | },
35 | {
36 | "name": "eslint-import-resolver-typescript",
37 | "type": "build"
38 | },
39 | {
40 | "name": "eslint-plugin-import",
41 | "type": "build"
42 | },
43 | {
44 | "name": "eslint",
45 | "version": "^9",
46 | "type": "build"
47 | },
48 | {
49 | "name": "jest",
50 | "type": "build"
51 | },
52 | {
53 | "name": "jest-junit",
54 | "version": "^16",
55 | "type": "build"
56 | },
57 | {
58 | "name": "jsii-diff",
59 | "type": "build"
60 | },
61 | {
62 | "name": "jsii-docgen",
63 | "version": "^10.5.0",
64 | "type": "build"
65 | },
66 | {
67 | "name": "jsii-pacmak",
68 | "type": "build"
69 | },
70 | {
71 | "name": "jsii-rosetta",
72 | "version": "~5.8.0",
73 | "type": "build"
74 | },
75 | {
76 | "name": "jsii",
77 | "version": "~5.8.0",
78 | "type": "build"
79 | },
80 | {
81 | "name": "projen",
82 | "type": "build"
83 | },
84 | {
85 | "name": "ts-jest",
86 | "type": "build"
87 | },
88 | {
89 | "name": "ts-node",
90 | "type": "build"
91 | },
92 | {
93 | "name": "typescript",
94 | "type": "build"
95 | },
96 | {
97 | "name": "@types/aws-lambda",
98 | "type": "bundled"
99 | },
100 | {
101 | "name": "aws-sdk",
102 | "type": "bundled"
103 | },
104 | {
105 | "name": "aws-cdk-lib",
106 | "version": "^2.24.0",
107 | "type": "peer"
108 | },
109 | {
110 | "name": "constructs",
111 | "version": "^10.0.5",
112 | "type": "peer"
113 | }
114 | ],
115 | "//": "~~ Generated by projen. To modify, edit .projenrc.ts and run \"npx projen\"."
116 | }
117 |
--------------------------------------------------------------------------------
/.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/build.yml",
8 | ".github/workflows/pull-request-lint.yml",
9 | ".github/workflows/release.yml",
10 | ".github/workflows/upgrade-main.yml",
11 | ".gitignore",
12 | ".mergify.yml",
13 | ".projen/deps.json",
14 | ".projen/files.json",
15 | ".projen/tasks.json",
16 | "LICENSE",
17 | "tsconfig.dev.json"
18 | ],
19 | "//": "~~ Generated by projen. To modify, edit .projenrc.ts and run \"npx projen\"."
20 | }
21 |
--------------------------------------------------------------------------------
/.projen/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "tasks": {
3 | "build": {
4 | "name": "build",
5 | "description": "Full release build",
6 | "steps": [
7 | {
8 | "spawn": "default"
9 | },
10 | {
11 | "spawn": "pre-compile"
12 | },
13 | {
14 | "spawn": "compile"
15 | },
16 | {
17 | "spawn": "post-compile"
18 | },
19 | {
20 | "spawn": "test"
21 | },
22 | {
23 | "spawn": "package"
24 | }
25 | ]
26 | },
27 | "bump": {
28 | "name": "bump",
29 | "description": "Bumps version based on latest git tag and generates a changelog entry",
30 | "env": {
31 | "OUTFILE": "package.json",
32 | "CHANGELOG": "dist/changelog.md",
33 | "BUMPFILE": "dist/version.txt",
34 | "RELEASETAG": "dist/releasetag.txt",
35 | "RELEASE_TAG_PREFIX": "",
36 | "BUMP_PACKAGE": "commit-and-tag-version@^12"
37 | },
38 | "steps": [
39 | {
40 | "builtin": "release/bump-version"
41 | }
42 | ],
43 | "condition": "git log --oneline -1 | grep -qv \"chore(release):\""
44 | },
45 | "clobber": {
46 | "name": "clobber",
47 | "description": "hard resets to HEAD of origin and cleans the local repo",
48 | "env": {
49 | "BRANCH": "$(git branch --show-current)"
50 | },
51 | "steps": [
52 | {
53 | "exec": "git checkout -b scratch",
54 | "name": "save current HEAD in \"scratch\" branch"
55 | },
56 | {
57 | "exec": "git checkout $BRANCH"
58 | },
59 | {
60 | "exec": "git fetch origin",
61 | "name": "fetch latest changes from origin"
62 | },
63 | {
64 | "exec": "git reset --hard origin/$BRANCH",
65 | "name": "hard reset to origin commit"
66 | },
67 | {
68 | "exec": "git clean -fdx",
69 | "name": "clean all untracked files"
70 | },
71 | {
72 | "say": "ready to rock! (unpushed commits are under the \"scratch\" branch)"
73 | }
74 | ],
75 | "condition": "git diff --exit-code > /dev/null"
76 | },
77 | "compat": {
78 | "name": "compat",
79 | "description": "Perform API compatibility check against latest version",
80 | "steps": [
81 | {
82 | "exec": "jsii-diff npm:$(node -p \"require('./package.json').name\") -k --ignore-file .compatignore || (echo \"\nUNEXPECTED BREAKING CHANGES: add keys such as 'removed:constructs.Node.of' to .compatignore to skip.\n\" && exit 1)"
83 | }
84 | ]
85 | },
86 | "compile": {
87 | "name": "compile",
88 | "description": "Only compile",
89 | "steps": [
90 | {
91 | "exec": "jsii --silence-warnings=reserved-word"
92 | }
93 | ]
94 | },
95 | "default": {
96 | "name": "default",
97 | "description": "Synthesize project files",
98 | "steps": [
99 | {
100 | "exec": "ts-node --project tsconfig.dev.json .projenrc.ts"
101 | }
102 | ]
103 | },
104 | "docgen": {
105 | "name": "docgen",
106 | "description": "Generate API.md from .jsii manifest",
107 | "steps": [
108 | {
109 | "exec": "jsii-docgen -o API.md"
110 | }
111 | ]
112 | },
113 | "eject": {
114 | "name": "eject",
115 | "description": "Remove projen from the project",
116 | "env": {
117 | "PROJEN_EJECTING": "true"
118 | },
119 | "steps": [
120 | {
121 | "spawn": "default"
122 | }
123 | ]
124 | },
125 | "eslint": {
126 | "name": "eslint",
127 | "description": "Runs eslint against the codebase",
128 | "env": {
129 | "ESLINT_USE_FLAT_CONFIG": "false",
130 | "NODE_NO_WARNINGS": "1"
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 | "package": {
158 | "name": "package",
159 | "description": "Creates the distribution package",
160 | "steps": [
161 | {
162 | "spawn": "package:js",
163 | "condition": "node -e \"if (!process.env.CI) process.exit(1)\""
164 | },
165 | {
166 | "spawn": "package-all",
167 | "condition": "node -e \"if (process.env.CI) process.exit(1)\""
168 | }
169 | ]
170 | },
171 | "package-all": {
172 | "name": "package-all",
173 | "description": "Packages artifacts for all target languages",
174 | "steps": [
175 | {
176 | "spawn": "package:js"
177 | },
178 | {
179 | "spawn": "package:java"
180 | },
181 | {
182 | "spawn": "package:python"
183 | },
184 | {
185 | "spawn": "package:dotnet"
186 | }
187 | ]
188 | },
189 | "package:dotnet": {
190 | "name": "package:dotnet",
191 | "description": "Create dotnet language bindings",
192 | "steps": [
193 | {
194 | "exec": "jsii-pacmak -v --target dotnet"
195 | }
196 | ]
197 | },
198 | "package:java": {
199 | "name": "package:java",
200 | "description": "Create java language bindings",
201 | "steps": [
202 | {
203 | "exec": "jsii-pacmak -v --target java"
204 | }
205 | ]
206 | },
207 | "package:js": {
208 | "name": "package:js",
209 | "description": "Create js language bindings",
210 | "steps": [
211 | {
212 | "exec": "jsii-pacmak -v --target js"
213 | }
214 | ]
215 | },
216 | "package:python": {
217 | "name": "package:python",
218 | "description": "Create python language bindings",
219 | "steps": [
220 | {
221 | "exec": "jsii-pacmak -v --target python"
222 | }
223 | ]
224 | },
225 | "post-compile": {
226 | "name": "post-compile",
227 | "description": "Runs after successful compilation",
228 | "steps": [
229 | {
230 | "spawn": "docgen"
231 | }
232 | ]
233 | },
234 | "post-upgrade": {
235 | "name": "post-upgrade",
236 | "description": "Runs after upgrading dependencies"
237 | },
238 | "pre-compile": {
239 | "name": "pre-compile",
240 | "description": "Prepare the project for compilation"
241 | },
242 | "release": {
243 | "name": "release",
244 | "description": "Prepare a release from \"main\" branch",
245 | "env": {
246 | "RELEASE": "true"
247 | },
248 | "steps": [
249 | {
250 | "exec": "rm -fr dist"
251 | },
252 | {
253 | "spawn": "bump"
254 | },
255 | {
256 | "spawn": "build"
257 | },
258 | {
259 | "spawn": "unbump"
260 | },
261 | {
262 | "exec": "git diff --ignore-space-at-eol --exit-code"
263 | }
264 | ]
265 | },
266 | "test": {
267 | "name": "test",
268 | "description": "Run tests",
269 | "steps": [
270 | {
271 | "exec": "jest --passWithNoTests --updateSnapshot",
272 | "receiveArgs": true
273 | },
274 | {
275 | "spawn": "eslint"
276 | }
277 | ]
278 | },
279 | "test:watch": {
280 | "name": "test:watch",
281 | "description": "Run jest in watch mode",
282 | "steps": [
283 | {
284 | "exec": "jest --watch"
285 | }
286 | ]
287 | },
288 | "unbump": {
289 | "name": "unbump",
290 | "description": "Restores version to 0.0.0",
291 | "env": {
292 | "OUTFILE": "package.json",
293 | "CHANGELOG": "dist/changelog.md",
294 | "BUMPFILE": "dist/version.txt",
295 | "RELEASETAG": "dist/releasetag.txt",
296 | "RELEASE_TAG_PREFIX": "",
297 | "BUMP_PACKAGE": "commit-and-tag-version@^12"
298 | },
299 | "steps": [
300 | {
301 | "builtin": "release/reset-version"
302 | }
303 | ]
304 | },
305 | "upgrade": {
306 | "name": "upgrade",
307 | "description": "upgrade dependencies",
308 | "env": {
309 | "CI": "0"
310 | },
311 | "steps": [
312 | {
313 | "exec": "npx npm-check-updates@16 --upgrade --target=minor --peer --no-deprecated --dep=dev,peer,prod,optional --filter=@types/jest,@types/node,esbuild,eslint-import-resolver-typescript,eslint-plugin-import,jest,jsii-diff,jsii-pacmak,projen,ts-jest,ts-node,typescript,@types/aws-lambda,aws-sdk"
314 | },
315 | {
316 | "exec": "yarn install --check-files"
317 | },
318 | {
319 | "exec": "yarn upgrade @stylistic/eslint-plugin @types/jest @types/node @typescript-eslint/eslint-plugin @typescript-eslint/parser commit-and-tag-version esbuild eslint-import-resolver-typescript eslint-plugin-import eslint jest jest-junit jsii-diff jsii-docgen jsii-pacmak jsii-rosetta jsii projen ts-jest ts-node typescript @types/aws-lambda aws-sdk aws-cdk-lib constructs"
320 | },
321 | {
322 | "exec": "npx projen"
323 | },
324 | {
325 | "spawn": "post-upgrade"
326 | }
327 | ]
328 | },
329 | "watch": {
330 | "name": "watch",
331 | "description": "Watch & compile in the background",
332 | "steps": [
333 | {
334 | "exec": "jsii -w --silence-warnings=reserved-word"
335 | }
336 | ]
337 | }
338 | },
339 | "env": {
340 | "PATH": "$(npx -c \"node --print process.env.PATH\")"
341 | },
342 | "//": "~~ Generated by projen. To modify, edit .projenrc.ts and run \"npx projen\"."
343 | }
344 |
--------------------------------------------------------------------------------
/.projenrc.ts:
--------------------------------------------------------------------------------
1 | import { awscdk } from 'projen';
2 | const project = new awscdk.AwsCdkConstructLibrary({
3 | author: 'Parker Scanlon',
4 | authorAddress: 'https://aws.amazon.com/',
5 | cdkVersion: '2.24.0', // needed for node16
6 | defaultReleaseBranch: 'main',
7 | name: 'cdk-docker-image-deployment',
8 | projenrcTs: true,
9 | repositoryUrl: 'https://github.com/cdklabs/cdk-docker-image-deployment.git',
10 | homepage: 'https://github.com/cdklabs/cdk-docker-image-deployment#readme',
11 | autoApproveUpgrades: true,
12 | autoApproveOptions: {
13 | allowedUsernames: ['cdklabs-automation'],
14 | secret: 'GITHUB_TOKEN',
15 | },
16 | gitignore: ['/cdk.out'],
17 | description: 'This module allows you to copy docker image assets to a repository you control. This can be necessary if you want to build a Docker image in one CDK app and consume it in a different app or outside the CDK.',
18 | bundledDeps: ['@types/aws-lambda', 'aws-sdk'],
19 | devDeps: ['esbuild'],
20 | publishToPypi: {
21 | distName: 'cdk-docker-image-deployment',
22 | module: 'cdk_docker_image_deployment',
23 | },
24 | publishToMaven: {
25 | javaPackage: 'io.github.cdklabs.cdk.docker.image.deployment',
26 | mavenGroupId: 'io.github.cdklabs',
27 | mavenArtifactId: 'cdk-docker-image-deployment',
28 | mavenServerId: 'central-ossrh',
29 | },
30 | publishToNuget: {
31 | dotNetNamespace: 'Cdklabs.CdkDockerImageDeployment',
32 | packageId: 'Cdklabs.CdkDockerImageDeployment',
33 | },
34 | });
35 |
36 | project.synth();
37 |
--------------------------------------------------------------------------------
/API.md:
--------------------------------------------------------------------------------
1 | # API Reference
2 |
3 | ## Constructs
4 |
5 | ### DockerImageDeployment
6 |
7 | `DockerImageDeployment` pushes an image from a local or external source to a specified external destination.
8 |
9 | #### Initializers
10 |
11 | ```typescript
12 | import { DockerImageDeployment } from 'cdk-docker-image-deployment'
13 |
14 | new DockerImageDeployment(scope: Construct, id: string, props: DockerImageDeploymentProps)
15 | ```
16 |
17 | | **Name** | **Type** | **Description** |
18 | | --- | --- | --- |
19 | | scope | constructs.Construct | *No description.* |
20 | | id | string | *No description.* |
21 | | props | DockerImageDeploymentProps | *No description.* |
22 |
23 | ---
24 |
25 | ##### `scope`Required
26 |
27 | - *Type:* constructs.Construct
28 |
29 | ---
30 |
31 | ##### `id`Required
32 |
33 | - *Type:* string
34 |
35 | ---
36 |
37 | ##### `props`Required
38 |
39 | - *Type:* DockerImageDeploymentProps
40 |
41 | ---
42 |
43 | #### Methods
44 |
45 | | **Name** | **Description** |
46 | | --- | --- |
47 | | toString | Returns a string representation of this construct. |
48 |
49 | ---
50 |
51 | ##### `toString`
52 |
53 | ```typescript
54 | public toString(): string
55 | ```
56 |
57 | Returns a string representation of this construct.
58 |
59 | #### Static Functions
60 |
61 | | **Name** | **Description** |
62 | | --- | --- |
63 | | isConstruct | Checks if `x` is a construct. |
64 |
65 | ---
66 |
67 | ##### ~~`isConstruct`~~
68 |
69 | ```typescript
70 | import { DockerImageDeployment } from 'cdk-docker-image-deployment'
71 |
72 | DockerImageDeployment.isConstruct(x: any)
73 | ```
74 |
75 | Checks if `x` is a construct.
76 |
77 | ###### `x`Required
78 |
79 | - *Type:* any
80 |
81 | Any object.
82 |
83 | ---
84 |
85 | #### Properties
86 |
87 | | **Name** | **Type** | **Description** |
88 | | --- | --- | --- |
89 | | node | constructs.Node | The tree node. |
90 |
91 | ---
92 |
93 | ##### `node`Required
94 |
95 | ```typescript
96 | public readonly node: Node;
97 | ```
98 |
99 | - *Type:* constructs.Node
100 |
101 | The tree node.
102 |
103 | ---
104 |
105 |
106 | ## Structs
107 |
108 | ### DestinationConfig
109 |
110 | Destination information.
111 |
112 | #### Initializer
113 |
114 | ```typescript
115 | import { DestinationConfig } from 'cdk-docker-image-deployment'
116 |
117 | const destinationConfig: DestinationConfig = { ... }
118 | ```
119 |
120 | #### Properties
121 |
122 | | **Name** | **Type** | **Description** |
123 | | --- | --- | --- |
124 | | destinationUri | string | The URI of the destination repository to deploy to. |
125 | | loginConfig | LoginConfig | The login command and region. |
126 | | destinationTag | string | The tag of the deployed image. |
127 |
128 | ---
129 |
130 | ##### `destinationUri`Required
131 |
132 | ```typescript
133 | public readonly destinationUri: string;
134 | ```
135 |
136 | - *Type:* string
137 |
138 | The URI of the destination repository to deploy to.
139 |
140 | ---
141 |
142 | ##### `loginConfig`Required
143 |
144 | ```typescript
145 | public readonly loginConfig: LoginConfig;
146 | ```
147 |
148 | - *Type:* LoginConfig
149 |
150 | The login command and region.
151 |
152 | ---
153 |
154 | ##### `destinationTag`Optional
155 |
156 | ```typescript
157 | public readonly destinationTag: string;
158 | ```
159 |
160 | - *Type:* string
161 | - *Default:* the tag of the source
162 |
163 | The tag of the deployed image.
164 |
165 | ---
166 |
167 | ### DockerImageDeploymentProps
168 |
169 | #### Initializer
170 |
171 | ```typescript
172 | import { DockerImageDeploymentProps } from 'cdk-docker-image-deployment'
173 |
174 | const dockerImageDeploymentProps: DockerImageDeploymentProps = { ... }
175 | ```
176 |
177 | #### Properties
178 |
179 | | **Name** | **Type** | **Description** |
180 | | --- | --- | --- |
181 | | destination | Destination | Destination repository to deploy the image to. |
182 | | source | Source | Source of the image to deploy. |
183 |
184 | ---
185 |
186 | ##### `destination`Required
187 |
188 | ```typescript
189 | public readonly destination: Destination;
190 | ```
191 |
192 | - *Type:* Destination
193 |
194 | Destination repository to deploy the image to.
195 |
196 | ---
197 |
198 | ##### `source`Required
199 |
200 | ```typescript
201 | public readonly source: Source;
202 | ```
203 |
204 | - *Type:* Source
205 |
206 | Source of the image to deploy.
207 |
208 | ---
209 |
210 | ### EcrSourceOptions
211 |
212 | Properties needed for Source.ecr.
213 |
214 | #### Initializer
215 |
216 | ```typescript
217 | import { EcrSourceOptions } from 'cdk-docker-image-deployment'
218 |
219 | const ecrSourceOptions: EcrSourceOptions = { ... }
220 | ```
221 |
222 | #### Properties
223 |
224 | | **Name** | **Type** | **Description** |
225 | | --- | --- | --- |
226 | | tag | string | Tag of deployed image. |
227 |
228 | ---
229 |
230 | ##### `tag`Optional
231 |
232 | ```typescript
233 | public readonly tag: string;
234 | ```
235 |
236 | - *Type:* string
237 | - *Default:* tag of source
238 |
239 | Tag of deployed image.
240 |
241 | ---
242 |
243 | ### LoginConfig
244 |
245 | Login commands for specified registry.
246 |
247 | #### Initializer
248 |
249 | ```typescript
250 | import { LoginConfig } from 'cdk-docker-image-deployment'
251 |
252 | const loginConfig: LoginConfig = { ... }
253 | ```
254 |
255 | #### Properties
256 |
257 | | **Name** | **Type** | **Description** |
258 | | --- | --- | --- |
259 | | loginCommand | string | Command to run in codebuild to login. |
260 | | region | string | Region of ECR repository. |
261 |
262 | ---
263 |
264 | ##### `loginCommand`Required
265 |
266 | ```typescript
267 | public readonly loginCommand: string;
268 | ```
269 |
270 | - *Type:* string
271 |
272 | Command to run in codebuild to login.
273 |
274 | Formatted `docker login ...`.
275 |
276 | ---
277 |
278 | ##### `region`Optional
279 |
280 | ```typescript
281 | public readonly region: string;
282 | ```
283 |
284 | - *Type:* string
285 | - *Default:* undefined if not an ECR repository
286 |
287 | Region of ECR repository.
288 |
289 | ---
290 |
291 | ### SourceConfig
292 |
293 | Source information.
294 |
295 | #### Initializer
296 |
297 | ```typescript
298 | import { SourceConfig } from 'cdk-docker-image-deployment'
299 |
300 | const sourceConfig: SourceConfig = { ... }
301 | ```
302 |
303 | #### Properties
304 |
305 | | **Name** | **Type** | **Description** |
306 | | --- | --- | --- |
307 | | imageTag | string | The source tag. |
308 | | imageUri | string | The source image URI. |
309 | | loginConfig | LoginConfig | The login command and region. |
310 |
311 | ---
312 |
313 | ##### `imageTag`Required
314 |
315 | ```typescript
316 | public readonly imageTag: string;
317 | ```
318 |
319 | - *Type:* string
320 |
321 | The source tag.
322 |
323 | ---
324 |
325 | ##### `imageUri`Required
326 |
327 | ```typescript
328 | public readonly imageUri: string;
329 | ```
330 |
331 | - *Type:* string
332 |
333 | The source image URI.
334 |
335 | ---
336 |
337 | ##### `loginConfig`Required
338 |
339 | ```typescript
340 | public readonly loginConfig: LoginConfig;
341 | ```
342 |
343 | - *Type:* LoginConfig
344 |
345 | The login command and region.
346 |
347 | ---
348 |
349 | ### SourceContext
350 |
351 | Bind context for Source.
352 |
353 | #### Initializer
354 |
355 | ```typescript
356 | import { SourceContext } from 'cdk-docker-image-deployment'
357 |
358 | const sourceContext: SourceContext = { ... }
359 | ```
360 |
361 | #### Properties
362 |
363 | | **Name** | **Type** | **Description** |
364 | | --- | --- | --- |
365 | | handlerRole | aws-cdk-lib.aws_iam.IRole | The role for the handler. |
366 |
367 | ---
368 |
369 | ##### `handlerRole`Required
370 |
371 | ```typescript
372 | public readonly handlerRole: IRole;
373 | ```
374 |
375 | - *Type:* aws-cdk-lib.aws_iam.IRole
376 |
377 | The role for the handler.
378 |
379 | ---
380 |
381 | ## Classes
382 |
383 | ### Destination
384 |
385 | Specifies docker image deployment destination.
386 |
387 | Usage:
388 |
389 | ```ts
390 | declare const repo: ecr.IRepository;
391 | const destinationEcr = dockerDeploy.Destination.ecr(repository, {
392 | tag: 'tag',
393 | });
394 | ```
395 |
396 | #### Initializers
397 |
398 | ```typescript
399 | import { Destination } from 'cdk-docker-image-deployment'
400 |
401 | new Destination()
402 | ```
403 |
404 | | **Name** | **Type** | **Description** |
405 | | --- | --- | --- |
406 |
407 | ---
408 |
409 | #### Methods
410 |
411 | | **Name** | **Description** |
412 | | --- | --- |
413 | | bind | Bind grants the CodeBuild role permissions to pull and push to a repository if necessary. |
414 |
415 | ---
416 |
417 | ##### `bind`
418 |
419 | ```typescript
420 | public bind(role: IGrantable): DestinationConfig
421 | ```
422 |
423 | Bind grants the CodeBuild role permissions to pull and push to a repository if necessary.
424 |
425 | Bind should be invoked by the caller to get the DestinationConfig.
426 |
427 | ###### `role`Required
428 |
429 | - *Type:* aws-cdk-lib.aws_iam.IGrantable
430 |
431 | ---
432 |
433 | #### Static Functions
434 |
435 | | **Name** | **Description** |
436 | | --- | --- |
437 | | ecr | Uses an ECR repository in the same account as the stack as the destination for the image. |
438 |
439 | ---
440 |
441 | ##### `ecr`
442 |
443 | ```typescript
444 | import { Destination } from 'cdk-docker-image-deployment'
445 |
446 | Destination.ecr(repository: IRepository, options?: EcrSourceOptions)
447 | ```
448 |
449 | Uses an ECR repository in the same account as the stack as the destination for the image.
450 |
451 | ###### `repository`Required
452 |
453 | - *Type:* aws-cdk-lib.aws_ecr.IRepository
454 |
455 | ---
456 |
457 | ###### `options`Optional
458 |
459 | - *Type:* EcrSourceOptions
460 |
461 | ---
462 |
463 |
464 |
465 | ### Source
466 |
467 | Specifies docker image deployment source.
468 |
469 | Usage:
470 |
471 | ```ts
472 | import * as path from 'path';
473 | const path = path.join(__dirname, 'path/to/directory');
474 | const sourceDirectory = Source.directory(path);
475 | ```
476 | or with additional `assetOptions`
477 | ```ts
478 | import * as path from 'path';
479 | const path = path.join(__dirname, 'path/to/directory');
480 | const sourceDirectory = Source.directory(path, {
481 | file: 'Dockerfile.api',
482 | buildArgs: {
483 | HTTP_PROXY: 'http://10.20.30.2:1234'
484 | }
485 | })
486 | ```
487 |
488 | #### Initializers
489 |
490 | ```typescript
491 | import { Source } from 'cdk-docker-image-deployment'
492 |
493 | new Source()
494 | ```
495 |
496 | | **Name** | **Type** | **Description** |
497 | | --- | --- | --- |
498 |
499 | ---
500 |
501 | #### Methods
502 |
503 | | **Name** | **Description** |
504 | | --- | --- |
505 | | bind | Bind grants the CodeBuild role permissions to pull from a repository if necessary. |
506 |
507 | ---
508 |
509 | ##### `bind`
510 |
511 | ```typescript
512 | public bind(scope: Construct, context: SourceContext): SourceConfig
513 | ```
514 |
515 | Bind grants the CodeBuild role permissions to pull from a repository if necessary.
516 |
517 | Bind should be invoked by the caller to get the SourceConfig.
518 |
519 | ###### `scope`Required
520 |
521 | - *Type:* constructs.Construct
522 |
523 | ---
524 |
525 | ###### `context`Required
526 |
527 | - *Type:* SourceContext
528 |
529 | ---
530 |
531 | #### Static Functions
532 |
533 | | **Name** | **Description** |
534 | | --- | --- |
535 | | directory | Uses a local image built from a Dockerfile in a local directory as the source. |
536 |
537 | ---
538 |
539 | ##### `directory`
540 |
541 | ```typescript
542 | import { Source } from 'cdk-docker-image-deployment'
543 |
544 | Source.directory(path: string, assetOptions?: DockerImageAssetOptions)
545 | ```
546 |
547 | Uses a local image built from a Dockerfile in a local directory as the source.
548 |
549 | ###### `path`Required
550 |
551 | - *Type:* string
552 |
553 | path to the directory containing your Dockerfile (not a path to a file).
554 |
555 | ---
556 |
557 | ###### `assetOptions`Optional
558 |
559 | - *Type:* aws-cdk-lib.aws_ecr_assets.DockerImageAssetOptions
560 |
561 | specify any additional [DockerImageAssetOptions](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ecr_assets.DockerImageAssetOptions.html) (except `path`).
562 |
563 | ---
564 |
565 |
566 |
567 |
568 |
--------------------------------------------------------------------------------
/API.md.md:
--------------------------------------------------------------------------------
1 | # API Reference
2 |
3 | ## Constructs
4 |
5 | ### DockerImageDeployment
6 |
7 | `DockerImageDeployment` pushes an image from a local or external source to a specified external destination.
8 |
9 | #### Initializers
10 |
11 | ```typescript
12 | import { DockerImageDeployment } from 'cdk-docker-image-deployment'
13 |
14 | new DockerImageDeployment(scope: Construct, id: string, props: DockerImageDeploymentProps)
15 | ```
16 |
17 | | **Name** | **Type** | **Description** |
18 | | --- | --- | --- |
19 | | scope | constructs.Construct | *No description.* |
20 | | id | string | *No description.* |
21 | | props | DockerImageDeploymentProps | *No description.* |
22 |
23 | ---
24 |
25 | ##### `scope`Required
26 |
27 | - *Type:* constructs.Construct
28 |
29 | ---
30 |
31 | ##### `id`Required
32 |
33 | - *Type:* string
34 |
35 | ---
36 |
37 | ##### `props`Required
38 |
39 | - *Type:* DockerImageDeploymentProps
40 |
41 | ---
42 |
43 | #### Methods
44 |
45 | | **Name** | **Description** |
46 | | --- | --- |
47 | | toString | Returns a string representation of this construct. |
48 |
49 | ---
50 |
51 | ##### `toString`
52 |
53 | ```typescript
54 | public toString(): string
55 | ```
56 |
57 | Returns a string representation of this construct.
58 |
59 | #### Static Functions
60 |
61 | | **Name** | **Description** |
62 | | --- | --- |
63 | | isConstruct | Checks if `x` is a construct. |
64 |
65 | ---
66 |
67 | ##### ~~`isConstruct`~~
68 |
69 | ```typescript
70 | import { DockerImageDeployment } from 'cdk-docker-image-deployment'
71 |
72 | DockerImageDeployment.isConstruct(x: any)
73 | ```
74 |
75 | Checks if `x` is a construct.
76 |
77 | ###### `x`Required
78 |
79 | - *Type:* any
80 |
81 | Any object.
82 |
83 | ---
84 |
85 | #### Properties
86 |
87 | | **Name** | **Type** | **Description** |
88 | | --- | --- | --- |
89 | | node | constructs.Node | The tree node. |
90 |
91 | ---
92 |
93 | ##### `node`Required
94 |
95 | ```typescript
96 | public readonly node: Node;
97 | ```
98 |
99 | - *Type:* constructs.Node
100 |
101 | The tree node.
102 |
103 | ---
104 |
105 |
106 | ## Structs
107 |
108 | ### DestinationConfig
109 |
110 | Destination information.
111 |
112 | #### Initializer
113 |
114 | ```typescript
115 | import { DestinationConfig } from 'cdk-docker-image-deployment'
116 |
117 | const destinationConfig: DestinationConfig = { ... }
118 | ```
119 |
120 | #### Properties
121 |
122 | | **Name** | **Type** | **Description** |
123 | | --- | --- | --- |
124 | | destinationUri | string | The URI of the destination repository to deploy to. |
125 | | loginConfig | LoginConfig | The login command and region. |
126 | | destinationTag | string | The tag of the deployed image. |
127 |
128 | ---
129 |
130 | ##### `destinationUri`Required
131 |
132 | ```typescript
133 | public readonly destinationUri: string;
134 | ```
135 |
136 | - *Type:* string
137 |
138 | The URI of the destination repository to deploy to.
139 |
140 | ---
141 |
142 | ##### `loginConfig`Required
143 |
144 | ```typescript
145 | public readonly loginConfig: LoginConfig;
146 | ```
147 |
148 | - *Type:* LoginConfig
149 |
150 | The login command and region.
151 |
152 | ---
153 |
154 | ##### `destinationTag`Optional
155 |
156 | ```typescript
157 | public readonly destinationTag: string;
158 | ```
159 |
160 | - *Type:* string
161 | - *Default:* the tag of the source
162 |
163 | The tag of the deployed image.
164 |
165 | ---
166 |
167 | ### DockerImageDeploymentProps
168 |
169 | #### Initializer
170 |
171 | ```typescript
172 | import { DockerImageDeploymentProps } from 'cdk-docker-image-deployment'
173 |
174 | const dockerImageDeploymentProps: DockerImageDeploymentProps = { ... }
175 | ```
176 |
177 | #### Properties
178 |
179 | | **Name** | **Type** | **Description** |
180 | | --- | --- | --- |
181 | | destination | Destination | Destination repository to deploy the image to. |
182 | | source | Source | Source of the image to deploy. |
183 |
184 | ---
185 |
186 | ##### `destination`Required
187 |
188 | ```typescript
189 | public readonly destination: Destination;
190 | ```
191 |
192 | - *Type:* Destination
193 |
194 | Destination repository to deploy the image to.
195 |
196 | ---
197 |
198 | ##### `source`Required
199 |
200 | ```typescript
201 | public readonly source: Source;
202 | ```
203 |
204 | - *Type:* Source
205 |
206 | Source of the image to deploy.
207 |
208 | ---
209 |
210 | ### EcrSourceOptions
211 |
212 | Properties needed for Source.ecr.
213 |
214 | #### Initializer
215 |
216 | ```typescript
217 | import { EcrSourceOptions } from 'cdk-docker-image-deployment'
218 |
219 | const ecrSourceOptions: EcrSourceOptions = { ... }
220 | ```
221 |
222 | #### Properties
223 |
224 | | **Name** | **Type** | **Description** |
225 | | --- | --- | --- |
226 | | tag | string | Tag of deployed image. |
227 |
228 | ---
229 |
230 | ##### `tag`Optional
231 |
232 | ```typescript
233 | public readonly tag: string;
234 | ```
235 |
236 | - *Type:* string
237 | - *Default:* tag of source
238 |
239 | Tag of deployed image.
240 |
241 | ---
242 |
243 | ### LoginConfig
244 |
245 | Login commands for specified registry.
246 |
247 | #### Initializer
248 |
249 | ```typescript
250 | import { LoginConfig } from 'cdk-docker-image-deployment'
251 |
252 | const loginConfig: LoginConfig = { ... }
253 | ```
254 |
255 | #### Properties
256 |
257 | | **Name** | **Type** | **Description** |
258 | | --- | --- | --- |
259 | | loginCommand | string | Command to run in codebuild to login. |
260 | | region | string | Region of ECR repository. |
261 |
262 | ---
263 |
264 | ##### `loginCommand`Required
265 |
266 | ```typescript
267 | public readonly loginCommand: string;
268 | ```
269 |
270 | - *Type:* string
271 |
272 | Command to run in codebuild to login.
273 |
274 | Formatted `docker login ...`.
275 |
276 | ---
277 |
278 | ##### `region`Optional
279 |
280 | ```typescript
281 | public readonly region: string;
282 | ```
283 |
284 | - *Type:* string
285 | - *Default:* undefined if not an ECR repository
286 |
287 | Region of ECR repository.
288 |
289 | ---
290 |
291 | ### SourceConfig
292 |
293 | Source information.
294 |
295 | #### Initializer
296 |
297 | ```typescript
298 | import { SourceConfig } from 'cdk-docker-image-deployment'
299 |
300 | const sourceConfig: SourceConfig = { ... }
301 | ```
302 |
303 | #### Properties
304 |
305 | | **Name** | **Type** | **Description** |
306 | | --- | --- | --- |
307 | | imageTag | string | The source tag. |
308 | | imageUri | string | The source image URI. |
309 | | loginConfig | LoginConfig | The login command and region. |
310 |
311 | ---
312 |
313 | ##### `imageTag`Required
314 |
315 | ```typescript
316 | public readonly imageTag: string;
317 | ```
318 |
319 | - *Type:* string
320 |
321 | The source tag.
322 |
323 | ---
324 |
325 | ##### `imageUri`Required
326 |
327 | ```typescript
328 | public readonly imageUri: string;
329 | ```
330 |
331 | - *Type:* string
332 |
333 | The source image URI.
334 |
335 | ---
336 |
337 | ##### `loginConfig`Required
338 |
339 | ```typescript
340 | public readonly loginConfig: LoginConfig;
341 | ```
342 |
343 | - *Type:* LoginConfig
344 |
345 | The login command and region.
346 |
347 | ---
348 |
349 | ### SourceContext
350 |
351 | Bind context for Source.
352 |
353 | #### Initializer
354 |
355 | ```typescript
356 | import { SourceContext } from 'cdk-docker-image-deployment'
357 |
358 | const sourceContext: SourceContext = { ... }
359 | ```
360 |
361 | #### Properties
362 |
363 | | **Name** | **Type** | **Description** |
364 | | --- | --- | --- |
365 | | handlerRole | aws-cdk-lib.aws_iam.IRole | The role for the handler. |
366 |
367 | ---
368 |
369 | ##### `handlerRole`Required
370 |
371 | ```typescript
372 | public readonly handlerRole: IRole;
373 | ```
374 |
375 | - *Type:* aws-cdk-lib.aws_iam.IRole
376 |
377 | The role for the handler.
378 |
379 | ---
380 |
381 | ## Classes
382 |
383 | ### Destination
384 |
385 | Specifies docker image deployment destination.
386 |
387 | Usage:
388 |
389 | ```ts
390 | declare const repo: ecr.IRepository;
391 | const destinationEcr = dockerDeploy.Destination.ecr(repository, {
392 | tag: 'tag',
393 | });
394 | ```
395 |
396 | #### Initializers
397 |
398 | ```typescript
399 | import { Destination } from 'cdk-docker-image-deployment'
400 |
401 | new Destination()
402 | ```
403 |
404 | | **Name** | **Type** | **Description** |
405 | | --- | --- | --- |
406 |
407 | ---
408 |
409 | #### Methods
410 |
411 | | **Name** | **Description** |
412 | | --- | --- |
413 | | bind | Bind grants the CodeBuild role permissions to pull and push to a repository if necessary. |
414 |
415 | ---
416 |
417 | ##### `bind`
418 |
419 | ```typescript
420 | public bind(role: IGrantable): DestinationConfig
421 | ```
422 |
423 | Bind grants the CodeBuild role permissions to pull and push to a repository if necessary.
424 |
425 | Bind should be invoked by the caller to get the DestinationConfig.
426 |
427 | ###### `role`Required
428 |
429 | - *Type:* aws-cdk-lib.aws_iam.IGrantable
430 |
431 | ---
432 |
433 | #### Static Functions
434 |
435 | | **Name** | **Description** |
436 | | --- | --- |
437 | | ecr | Uses an ECR repository in the same account as the stack as the destination for the image. |
438 |
439 | ---
440 |
441 | ##### `ecr`
442 |
443 | ```typescript
444 | import { Destination } from 'cdk-docker-image-deployment'
445 |
446 | Destination.ecr(repository: IRepository, options?: EcrSourceOptions)
447 | ```
448 |
449 | Uses an ECR repository in the same account as the stack as the destination for the image.
450 |
451 | ###### `repository`Required
452 |
453 | - *Type:* aws-cdk-lib.aws_ecr.IRepository
454 |
455 | ---
456 |
457 | ###### `options`Optional
458 |
459 | - *Type:* EcrSourceOptions
460 |
461 | ---
462 |
463 |
464 |
465 | ### Source
466 |
467 | Specifies docker image deployment source.
468 |
469 | Usage:
470 |
471 | ```ts
472 | import * as path from 'path';
473 | const path = path.join(__dirname, 'path/to/directory');
474 | const sourceDirectory = Source.directory(path);
475 | ```
476 |
477 | #### Initializers
478 |
479 | ```typescript
480 | import { Source } from 'cdk-docker-image-deployment'
481 |
482 | new Source()
483 | ```
484 |
485 | | **Name** | **Type** | **Description** |
486 | | --- | --- | --- |
487 |
488 | ---
489 |
490 | #### Methods
491 |
492 | | **Name** | **Description** |
493 | | --- | --- |
494 | | bind | Bind grants the CodeBuild role permissions to pull from a repository if necessary. |
495 |
496 | ---
497 |
498 | ##### `bind`
499 |
500 | ```typescript
501 | public bind(scope: Construct, context: SourceContext): SourceConfig
502 | ```
503 |
504 | Bind grants the CodeBuild role permissions to pull from a repository if necessary.
505 |
506 | Bind should be invoked by the caller to get the SourceConfig.
507 |
508 | ###### `scope`Required
509 |
510 | - *Type:* constructs.Construct
511 |
512 | ---
513 |
514 | ###### `context`Required
515 |
516 | - *Type:* SourceContext
517 |
518 | ---
519 |
520 | #### Static Functions
521 |
522 | | **Name** | **Description** |
523 | | --- | --- |
524 | | directory | Uses a local image built from a Dockerfile in a local directory as the source. |
525 |
526 | ---
527 |
528 | ##### `directory`
529 |
530 | ```typescript
531 | import { Source } from 'cdk-docker-image-deployment'
532 |
533 | Source.directory(path: string)
534 | ```
535 |
536 | Uses a local image built from a Dockerfile in a local directory as the source.
537 |
538 | ###### `path`Required
539 |
540 | - *Type:* string
541 |
542 | path to the directory containing your Dockerfile (not a path to a file).
543 |
544 | ---
545 |
546 |
547 |
548 |
549 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | ## Code of Conduct
2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
4 | opensource-codeofconduct@amazon.com with any additional questions or comments.
5 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 |
3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional
4 | documentation, we greatly value feedback and contributions from our community.
5 |
6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary
7 | information to effectively respond to your bug report or contribution.
8 |
9 |
10 | ## Reporting Bugs/Feature Requests
11 |
12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features.
13 |
14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already
15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:
16 |
17 | * A reproducible test case or series of steps
18 | * The version of our code being used
19 | * Any modifications you've made relevant to the bug
20 | * Anything unusual about your environment or deployment
21 |
22 |
23 | ## Contributing via Pull Requests
24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:
25 |
26 | 1. You are working against the latest source on the *main* branch.
27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted.
29 |
30 | To send us a pull request, please:
31 |
32 | 1. Fork the repository.
33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change.
34 | 3. Ensure local tests pass.
35 | 4. Commit to your fork using clear commit messages.
36 | 5. Send us a pull request, answering any default questions in the pull request interface.
37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.
38 |
39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
41 |
42 |
43 | ## Finding contributions to work on
44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start.
45 |
46 |
47 | ## Code of Conduct
48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
50 | opensource-codeofconduct@amazon.com with any additional questions or comments.
51 |
52 |
53 | ## Security issue notifications
54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue.
55 |
56 |
57 | ## Licensing
58 |
59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
60 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## CDK Docker Image Deployment
2 |
3 | This module allows you to copy docker image assets to a repository you control.
4 | This can be necessary if you want to build a Docker image in one CDK app and consume it in a different app or outside the CDK,
5 | or if you want to apply a lifecycle policy to all images of a part of your application.
6 |
7 | ### Getting Started
8 |
9 | Below is a basic example for how to use the `DockerImageDeployment` API:
10 |
11 | ```ts
12 | import * as ecr from 'aws-cdk-lib/aws-ecr';
13 | import * as imagedeploy from 'cdk-docker-image-deployment';
14 |
15 | const repo = ecr.Repository.fromRepositoryName(this, 'MyRepository', 'myrepository');
16 |
17 | new imagedeploy.DockerImageDeployment(this, 'ExampleImageDeploymentWithTag', {
18 | source: imagedeploy.Source.directory('path/to/directory'),
19 | destination: imagedeploy.Destination.ecr(repo, {
20 | tag: 'myspecialtag',
21 | }),
22 | });
23 | ```
24 |
25 | ### Currently Supported Sources
26 |
27 | - `Source.directory()`: Supply a path to a local docker image as source.
28 |
29 | > Don't see a source listed? See if there is an open [issue](https://github.com/cdklabs/cdk-docker-image-deployment/issues)
30 | > or [PR](https://github.com/cdklabs/cdk-docker-image-deployment/pulls) already. If not, please open an issue asking for it
31 | > or better yet, submit a contribution!
32 |
33 | ### Currently Supported Destinations
34 |
35 | - `Destination.ecr(repo, options)`: Send your docker image to an ECR repository in your stack's account.
36 |
37 | > Don't see a destination listed? See if there is an open [issue](https://github.com/cdklabs/cdk-docker-image-deployment/issues)
38 | > or [PR](https://github.com/cdklabs/cdk-docker-image-deployment/pulls) already. If not, please open an issue asking for it
39 | > or better yet, submit a contribution!
40 |
41 | ### Under the Hood
42 |
43 | 1. When this stack is deployed (either via cdk deploy or via CI/CD), the contents of the local Docker image will be archived and uploaded to an intermediary assets ECR Repository using the cdk-assets mechanism.
44 |
45 | 2. The `DockerImageDeployment` construct synthesizes a CodeBuild Project which uses docker to pull the image from the intermediary repository, tag the image if a tag is provided, and push the image to the destination repository.
46 |
47 | 3. The deployment will wait until the CodeBuild Project completes successfully before finishing.
48 |
49 | The architecture of this construct can be seen here:
50 |
51 | 
52 |
53 |
54 | ## Security
55 |
56 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information.
57 |
58 | ## License
59 |
60 | This project is licensed under the Apache-2.0 License.
61 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cdk-docker-image-deployment",
3 | "description": "This module allows you to copy docker image assets to a repository you control. This can be necessary if you want to build a Docker image in one CDK app and consume it in a different app or outside the CDK.",
4 | "repository": {
5 | "type": "git",
6 | "url": "https://github.com/cdklabs/cdk-docker-image-deployment.git"
7 | },
8 | "scripts": {
9 | "build": "npx projen build",
10 | "bump": "npx projen bump",
11 | "clobber": "npx projen clobber",
12 | "compat": "npx projen compat",
13 | "compile": "npx projen compile",
14 | "default": "npx projen default",
15 | "docgen": "npx projen docgen",
16 | "eject": "npx projen eject",
17 | "eslint": "npx projen eslint",
18 | "package": "npx projen package",
19 | "package-all": "npx projen package-all",
20 | "package:dotnet": "npx projen package:dotnet",
21 | "package:java": "npx projen package:java",
22 | "package:js": "npx projen package:js",
23 | "package:python": "npx projen package:python",
24 | "post-compile": "npx projen post-compile",
25 | "post-upgrade": "npx projen post-upgrade",
26 | "pre-compile": "npx projen pre-compile",
27 | "release": "npx projen release",
28 | "test": "npx projen test",
29 | "test:watch": "npx projen test:watch",
30 | "unbump": "npx projen unbump",
31 | "upgrade": "npx projen upgrade",
32 | "watch": "npx projen watch",
33 | "projen": "npx projen"
34 | },
35 | "author": {
36 | "name": "Parker Scanlon",
37 | "url": "https://aws.amazon.com/",
38 | "organization": false
39 | },
40 | "devDependencies": {
41 | "@stylistic/eslint-plugin": "^2",
42 | "@types/jest": "^27",
43 | "@types/node": "^16 <= 16.18.78",
44 | "@typescript-eslint/eslint-plugin": "^8",
45 | "@typescript-eslint/parser": "^8",
46 | "aws-cdk-lib": "2.24.0",
47 | "commit-and-tag-version": "^12",
48 | "constructs": "10.0.5",
49 | "esbuild": "^0.25.11",
50 | "eslint": "^9",
51 | "eslint-import-resolver-typescript": "^3.10.1",
52 | "eslint-plugin-import": "^2.32.0",
53 | "jest": "^27",
54 | "jest-junit": "^16",
55 | "jsii": "~5.8.0",
56 | "jsii-diff": "^1.117.0",
57 | "jsii-docgen": "^10.5.0",
58 | "jsii-pacmak": "^1.117.0",
59 | "jsii-rosetta": "~5.8.0",
60 | "projen": "^0.98.4",
61 | "ts-jest": "^27",
62 | "ts-node": "^10.9.2",
63 | "typescript": "^4.9.5"
64 | },
65 | "peerDependencies": {
66 | "aws-cdk-lib": "^2.24.0",
67 | "constructs": "^10.0.5"
68 | },
69 | "dependencies": {
70 | "@types/aws-lambda": "^8.10.156",
71 | "aws-sdk": "^2.1692.0"
72 | },
73 | "bundledDependencies": [
74 | "@types/aws-lambda",
75 | "aws-sdk"
76 | ],
77 | "keywords": [
78 | "cdk"
79 | ],
80 | "main": "lib/index.js",
81 | "license": "Apache-2.0",
82 | "homepage": "https://github.com/cdklabs/cdk-docker-image-deployment#readme",
83 | "publishConfig": {
84 | "access": "public"
85 | },
86 | "version": "0.0.0",
87 | "jest": {
88 | "coverageProvider": "v8",
89 | "testMatch": [
90 | "/@(src|test)/**/*(*.)@(spec|test).ts?(x)",
91 | "/@(src|test)/**/__tests__/**/*.ts?(x)",
92 | "/@(projenrc)/**/*(*.)@(spec|test).ts?(x)",
93 | "/@(projenrc)/**/__tests__/**/*.ts?(x)"
94 | ],
95 | "clearMocks": true,
96 | "collectCoverage": true,
97 | "coverageReporters": [
98 | "json",
99 | "lcov",
100 | "clover",
101 | "cobertura",
102 | "text"
103 | ],
104 | "coverageDirectory": "coverage",
105 | "coveragePathIgnorePatterns": [
106 | "/node_modules/"
107 | ],
108 | "testPathIgnorePatterns": [
109 | "/node_modules/"
110 | ],
111 | "watchPathIgnorePatterns": [
112 | "/node_modules/"
113 | ],
114 | "reporters": [
115 | "default",
116 | [
117 | "jest-junit",
118 | {
119 | "outputDirectory": "test-reports"
120 | }
121 | ]
122 | ],
123 | "preset": "ts-jest",
124 | "globals": {
125 | "ts-jest": {
126 | "tsconfig": "tsconfig.dev.json"
127 | }
128 | }
129 | },
130 | "types": "lib/index.d.ts",
131 | "stability": "stable",
132 | "jsii": {
133 | "outdir": "dist",
134 | "targets": {
135 | "java": {
136 | "package": "io.github.cdklabs.cdk.docker.image.deployment",
137 | "maven": {
138 | "groupId": "io.github.cdklabs",
139 | "artifactId": "cdk-docker-image-deployment"
140 | }
141 | },
142 | "python": {
143 | "distName": "cdk-docker-image-deployment",
144 | "module": "cdk_docker_image_deployment"
145 | },
146 | "dotnet": {
147 | "namespace": "Cdklabs.CdkDockerImageDeployment",
148 | "packageId": "Cdklabs.CdkDockerImageDeployment"
149 | }
150 | },
151 | "tsc": {
152 | "outDir": "lib",
153 | "rootDir": "src"
154 | }
155 | },
156 | "//": "~~ Generated by projen. To modify, edit .projenrc.ts and run \"npx projen\"."
157 | }
158 |
--------------------------------------------------------------------------------
/src/codebuild-handler/index.ts:
--------------------------------------------------------------------------------
1 | import * as AWSLambda from 'aws-lambda';
2 | import * as AWS from 'aws-sdk';
3 |
4 | const cb = new AWS.CodeBuild();
5 |
6 | export async function onEventhandler(event: AWSLambda.CloudFormationCustomResourceEvent) {
7 | switch (event.RequestType) {
8 | case 'Create':
9 | case 'Update':
10 | return startBuild(event);
11 | case 'Delete':
12 | return onDelete();
13 | }
14 | }
15 |
16 | export async function startBuild(event: AWSLambda.CloudFormationCustomResourceEvent) {
17 | const projectName = event.ResourceProperties.projectName;
18 | const buildOutput = await cb.startBuild({
19 | projectName: projectName,
20 | }).promise();
21 |
22 | const buildId = buildOutput.build?.id;
23 |
24 | if (buildId) {
25 | return {
26 | BuildId: buildId, // pass to isComplete
27 | };
28 | } else {
29 | throw new Error('BuildId does not exist after CodeBuild:StartBuild call');
30 | }
31 | }
32 |
33 | // pass isComplete a value to indicate it does not need to wait on delete events
34 | export async function onDelete() {
35 | return {
36 | BuildId: 'onDelete',
37 | };
38 | }
39 |
40 | export async function isCompleteHandler(event: AWSLambda.CloudFormationCustomResourceEvent) {
41 |
42 | const buildId = (event as any).BuildId; // BuildId is passed in from onEvent CodeBuild:StartBuild call
43 |
44 | if (!buildId) {
45 | throw new Error('BuildId was not found or undefined');
46 | }
47 |
48 | // isComplete does not need to wait on delete events
49 | if (buildId === 'onDelete') {
50 | return { IsComplete: true };
51 | }
52 |
53 | const build = await cb.batchGetBuilds({
54 | ids: [buildId],
55 | }).promise();
56 |
57 | // we should always have a build since we have a valid buildId
58 | if (!build.builds || build.builds.length <= 0) {
59 | throw new Error(`Build does not exist for BuildId: ${buildId}`);
60 | }
61 |
62 | const buildResponse = build.builds[0];
63 | const currentPhase = buildResponse.currentPhase;
64 | const buildStatus = buildResponse.buildStatus;
65 |
66 | if (currentPhase === 'COMPLETED' && buildStatus === 'SUCCEEDED') {
67 | return {
68 | IsComplete: true,
69 | Data: {
70 | Status: 'CodeBuild completed successfully',
71 | LogsUrl: `${JSON.stringify(buildResponse.logs?.deepLink)}`,
72 | },
73 | };
74 | } else if (currentPhase === 'COMPLETED' && buildStatus === 'FAILED') {
75 | if (buildResponse.logs?.deepLink) {
76 | throw new Error(`CodeBuild failed, check the logs here: ${buildResponse.logs.deepLink}`);
77 | } else {
78 | throw new Error('CodeBuild failed'); // this case should never be reached
79 | }
80 | } else {
81 | return { IsComplete: false }; // not finished
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/destination.ts:
--------------------------------------------------------------------------------
1 | import * as ecr from 'aws-cdk-lib/aws-ecr';
2 | import * as iam from 'aws-cdk-lib/aws-iam';
3 | import { LoginConfig } from './login';
4 |
5 | /**
6 | * Destination information
7 | */
8 | export interface DestinationConfig {
9 | /**
10 | * The URI of the destination repository to deploy to.
11 | */
12 | readonly destinationUri: string;
13 |
14 | /**
15 | * The login command and region.
16 | */
17 | readonly loginConfig: LoginConfig;
18 |
19 | /**
20 | * The tag of the deployed image.
21 | *
22 | * @default - the tag of the source
23 | */
24 | readonly destinationTag?: string;
25 | }
26 |
27 | /**
28 | * Properties needed for Source.ecr
29 | */
30 | export interface EcrSourceOptions {
31 | /**
32 | * Tag of deployed image.
33 | *
34 | * @default - tag of source
35 | */
36 | readonly tag?: string;
37 | }
38 |
39 | /**
40 | * Specifies docker image deployment destination
41 | *
42 | * Usage:
43 | *
44 | * ```ts
45 | * declare const repo: ecr.IRepository;
46 | * const destinationEcr = dockerDeploy.Destination.ecr(repository, {
47 | * tag: 'tag',
48 | * });
49 | * ```
50 | *
51 | */
52 | export abstract class Destination {
53 | /**
54 | * Uses an ECR repository in the same account as the stack as the destination for the image.
55 | */
56 | public static ecr(repository: ecr.IRepository, options?: EcrSourceOptions): Destination {
57 | return new EcrDestination(repository, options);
58 | }
59 |
60 | /**
61 | * Bind grants the CodeBuild role permissions to pull and push to a repository if necessary.
62 | * Bind should be invoked by the caller to get the DestinationConfig.
63 | */
64 | public abstract bind(role: iam.IGrantable): DestinationConfig;
65 | }
66 |
67 | /**
68 | * Class used when the destination of docker image deployment is an ECR repository in the same account as the stack
69 | */
70 | class EcrDestination extends Destination {
71 | private repository: ecr.IRepository;
72 | private options?: EcrSourceOptions;
73 |
74 | constructor(repository: ecr.IRepository, options?: EcrSourceOptions) {
75 | super();
76 |
77 | this.repository = repository;
78 | this.options = options;
79 |
80 | }
81 |
82 | public bind(role: iam.IGrantable): DestinationConfig {
83 | const accountId = this.repository.env.account;
84 | const region = this.repository.env.region;
85 |
86 | this.repository.grantPullPush(role);
87 |
88 | return {
89 | destinationUri: this.repository.repositoryUri,
90 | loginConfig: {
91 | loginCommand: `aws ecr get-login-password --region ${region} | docker login --username AWS --password-stdin ${accountId}.dkr.ecr.${region}.amazonaws.com`,
92 | region: region,
93 | },
94 | destinationTag: this.options?.tag,
95 | };
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/docker-image-deployment.ts:
--------------------------------------------------------------------------------
1 | import * as path from 'path';
2 | import { CustomResource, Duration, CfnOutput, Token } from 'aws-cdk-lib';
3 | import * as codebuild from 'aws-cdk-lib/aws-codebuild';
4 | import * as iam from 'aws-cdk-lib/aws-iam';
5 | import { Runtime } from 'aws-cdk-lib/aws-lambda';
6 | import * as lambda from 'aws-cdk-lib/aws-lambda-nodejs';
7 | import * as cr from 'aws-cdk-lib/custom-resources';
8 | import { Construct } from 'constructs';
9 | import { Destination } from './destination';
10 | import { Source } from './source';
11 |
12 | export interface DockerImageDeploymentProps {
13 | /**
14 | * Source of the image to deploy.
15 | */
16 | readonly source: Source;
17 |
18 | /**
19 | * Destination repository to deploy the image to.
20 | */
21 | readonly destination: Destination;
22 | }
23 |
24 | /**
25 | * `DockerImageDeployment` pushes an image from a local or external source to a specified external destination
26 | */
27 | export class DockerImageDeployment extends Construct {
28 | private readonly cb: codebuild.Project;
29 |
30 | constructor(scope: Construct, id: string, props: DockerImageDeploymentProps) {
31 | super(scope, id);
32 |
33 | const handlerRole = new iam.Role(this, 'DockerImageDeployRole', {
34 | assumedBy: new iam.ServicePrincipal('codebuild.amazonaws.com'),
35 | });
36 |
37 | const sourceConfig = props.source.bind(this, { handlerRole });
38 | const destinationConfig = props.destination.bind(handlerRole);
39 |
40 | const sourceUri = sourceConfig.imageUri;
41 |
42 | const destTag = destinationConfig.destinationTag ?? sourceConfig.imageTag;
43 | this.validateTag(destTag);
44 |
45 | const destUri = `${destinationConfig.destinationUri}:${destTag}`;
46 |
47 | const commands = [
48 | sourceConfig.loginConfig.loginCommand,
49 | `docker pull ${sourceUri}`,
50 | `docker tag ${sourceUri} ${destUri}`,
51 | ];
52 |
53 | if (sourceConfig.loginConfig.region !== destinationConfig.loginConfig.region || !sourceConfig.loginConfig.region) { // different regions or either undefined should logout and login
54 | commands.push('docker logout');
55 | commands.push(destinationConfig.loginConfig.loginCommand);
56 | }
57 |
58 | commands.push(`docker push ${destUri}`);
59 | commands.push('docker logout');
60 |
61 | this.cb = new codebuild.Project(this, 'DockerImageDeployProject', {
62 | buildSpec: codebuild.BuildSpec.fromObject({
63 | version: '0.2',
64 | phases: {
65 | build: {
66 | commands: commands,
67 | },
68 | },
69 | }),
70 | environment: {
71 | privileged: true,
72 | buildImage: codebuild.LinuxBuildImage.STANDARD_5_0,
73 | },
74 | role: handlerRole,
75 | });
76 |
77 | const onEventHandler = new lambda.NodejsFunction(this, 'onEventHandler', {
78 | entry: path.join(__dirname, 'codebuild-handler/index.js'),
79 | handler: 'onEventhandler',
80 | runtime: Runtime.NODEJS_16_X,
81 | });
82 |
83 | const isCompleteHandler = new lambda.NodejsFunction(this, 'isCompleteHandler', {
84 | entry: path.join(__dirname, 'codebuild-handler/index.js'),
85 | handler: 'isCompleteHandler',
86 | runtime: Runtime.NODEJS_16_X,
87 | });
88 |
89 | // https://github.com/aws/aws-cdk/issues/21721 issue to add grant methods to codebuild
90 | const grantOnEvent = iam.Grant.addToPrincipal({
91 | grantee: onEventHandler,
92 | actions: ['codebuild:StartBuild'],
93 | resourceArns: [this.cb.projectArn],
94 | scope: this,
95 | });
96 |
97 | const grantIsComplete = iam.Grant.addToPrincipal({
98 | grantee: isCompleteHandler,
99 | actions: [
100 | 'codebuild:ListBuildsForProject',
101 | 'codebuild:BatchGetBuilds',
102 | ],
103 | resourceArns: [this.cb.projectArn],
104 | scope: this,
105 | });
106 |
107 | const crProvider = new cr.Provider(this, 'CRProvider', {
108 | onEventHandler: onEventHandler,
109 | isCompleteHandler: isCompleteHandler,
110 | queryInterval: Duration.seconds(30),
111 | totalTimeout: Duration.minutes(30),
112 | });
113 |
114 | const customResource = new CustomResource(this, `CustomResource${Date.now().toString()}`, {
115 | serviceToken: crProvider.serviceToken,
116 | properties: {
117 | projectName: this.cb.projectName,
118 | },
119 | });
120 |
121 | customResource.node.addDependency(grantOnEvent, grantIsComplete);
122 |
123 | try {
124 | new CfnOutput(this, 'CustomResourceReport', {
125 | value: `${customResource.getAttString('Status')}, see the logs here: ${customResource.getAtt('LogsUrl')}`,
126 | });
127 | } catch (error) {
128 | throw new Error('Error getting the report from the custom resource');
129 | }
130 | }
131 |
132 | private validateTag(tag: string): void {
133 | if (Token.isUnresolved(tag)) {
134 | return; // if token tag is likely from source, so assume it is valid
135 | }
136 | if (tag.length > 128) {
137 | throw new Error (`Invalid tag: tags may contain a maximum of 128 characters; your tag ${tag} has ${tag.length} characters`);
138 | }
139 | if (!/^[^-.][a-zA-Z0-9-_.]+$/.test(tag)) {
140 | throw new Error(`Invalid tag: tags must contain alphanumeric characters and \'-\' \'_\' \'.\' only and must not begin with \'.\' or \'-\'; your tag was ${tag}`);
141 | }
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './docker-image-deployment';
2 | export * from './source';
3 | export * from './destination';
4 | export * from './login';
5 |
--------------------------------------------------------------------------------
/src/login.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Login commands for specified registry
3 | */
4 | export interface LoginConfig {
5 | /**
6 | * Command to run in codebuild to login.
7 | * Formatted `docker login ...`.
8 | */
9 | readonly loginCommand: string;
10 |
11 | /**
12 | * Region of ECR repository.
13 | *
14 | * @default - undefined if not an ECR repository
15 | */
16 | readonly region?: string;
17 | }
18 |
--------------------------------------------------------------------------------
/src/source.ts:
--------------------------------------------------------------------------------
1 | import { Fn } from 'aws-cdk-lib';
2 | import * as ecr_assets from 'aws-cdk-lib/aws-ecr-assets';
3 | import * as iam from 'aws-cdk-lib/aws-iam';
4 | import { Construct } from 'constructs';
5 | import { LoginConfig } from './login';
6 |
7 | /**
8 | * Source information
9 | */
10 | export interface SourceConfig {
11 | /**
12 | * The source image URI.
13 | */
14 | readonly imageUri: string;
15 |
16 | /**
17 | * The login command and region.
18 | */
19 | readonly loginConfig: LoginConfig;
20 |
21 | /**
22 | * The source tag.
23 | */
24 | readonly imageTag: string;
25 | }
26 |
27 | /**
28 | * Bind context for Source
29 | */
30 | export interface SourceContext {
31 | /**
32 | * The role for the handler.
33 | */
34 | readonly handlerRole: iam.IRole;
35 | }
36 |
37 | /**
38 | * Specifies docker image deployment source
39 | *
40 | * Usage:
41 | *
42 | * ```ts
43 | * import * as path from 'path';
44 | * const path = path.join(__dirname, 'path/to/directory');
45 | * const sourceDirectory = Source.directory(path);
46 | * ```
47 | * or with additional `assetOptions`
48 | * ```ts
49 | * import * as path from 'path';
50 | * const path = path.join(__dirname, 'path/to/directory');
51 | * const sourceDirectory = Source.directory(path, {
52 | * file: 'Dockerfile.api',
53 | * buildArgs: {
54 | * HTTP_PROXY: 'http://10.20.30.2:1234'
55 | * }
56 | * })
57 | * ```
58 | */
59 | export abstract class Source {
60 | /**
61 | * Uses a local image built from a Dockerfile in a local directory as the source.
62 | *
63 | * @param path - path to the directory containing your Dockerfile (not a path to a file)
64 | * @param assetOptions - specify any additional [DockerImageAssetOptions](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ecr_assets.DockerImageAssetOptions.html) (except `path`)
65 | */
66 | public static directory(
67 | path: string,
68 | assetOptions?: ecr_assets.DockerImageAssetOptions,
69 | ): Source {
70 | return new DirectorySource(path, assetOptions);
71 | }
72 |
73 | /**
74 | * Bind grants the CodeBuild role permissions to pull from a repository if necessary.
75 | * Bind should be invoked by the caller to get the SourceConfig.
76 | */
77 | public abstract bind(scope: Construct, context: SourceContext): SourceConfig;
78 | }
79 |
80 | /**
81 | * Source of docker image deployment is a local image from a directory
82 | */
83 | class DirectorySource extends Source {
84 | private assetProps: ecr_assets.DockerImageAssetProps;
85 |
86 | constructor(path: string, assetOptions?: ecr_assets.DockerImageAssetOptions) {
87 | super();
88 | this.assetProps = {
89 | directory: path,
90 | ...assetOptions,
91 | };
92 | }
93 |
94 | public bind(scope: Construct, context: SourceContext): SourceConfig {
95 | const asset = new ecr_assets.DockerImageAsset(scope, 'asset', this.assetProps);
96 |
97 | const accountId = asset.repository.env.account;
98 | const region = asset.repository.env.region;
99 |
100 | asset.repository.grantPull(context.handlerRole);
101 |
102 | return {
103 | imageUri: asset.imageUri,
104 | loginConfig: {
105 | loginCommand: `aws ecr get-login-password --region ${region} | docker login --username AWS --password-stdin ${accountId}.dkr.ecr.${region}.amazonaws.com`,
106 | region: region,
107 | },
108 | imageTag: Fn.select(1, Fn.split(':', asset.imageUri)), // uri will be something like 'directory/of/image:tag' so this picks out the tag from the token
109 | };
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/test/assets/test1/Dockerfile:
--------------------------------------------------------------------------------
1 | # syntax=docker/dockerfile:1
2 | FROM node:12-alpine
3 | WORKDIR /app
4 | COPY . .
5 |
--------------------------------------------------------------------------------
/test/assets/test2/Dockerfile:
--------------------------------------------------------------------------------
1 | # syntax=docker/dockerfile:1
2 | FROM node:12-alpine
3 | WORKDIR /app
4 | COPY . .
5 | EXPOSE 1000
6 |
--------------------------------------------------------------------------------
/test/docker-image-deploy.test.ts:
--------------------------------------------------------------------------------
1 | // imports
2 | import * as path from 'path';
3 | import { Match, Template } from 'aws-cdk-lib/assertions';
4 | import * as ecr from 'aws-cdk-lib/aws-ecr';
5 | import * as cdk from 'aws-cdk-lib/core';
6 | import * as imagedeploy from '../lib/index';
7 |
8 | describe('DockerImageDeploy', () => {
9 | // GIVEN
10 | const stack = new cdk.Stack();
11 | describe('Source: directory', () => {
12 | // GIVEN
13 | const testSource = imagedeploy.Source.directory(path.join(__dirname, 'assets/test1'));
14 |
15 | describe('Destination: ecr', () => {
16 | // GIVEN
17 | const repo = new ecr.Repository(stack, 'TestRepository');
18 | const testDesination = imagedeploy.Destination.ecr(repo, { tag: 'testtag' });
19 | const testDesinationNoTag = imagedeploy.Destination.ecr(repo, {});
20 | const testDesinationNoOptions = imagedeploy.Destination.ecr(repo);
21 |
22 | // WHEN
23 | new imagedeploy.DockerImageDeployment(stack, 'TestDeployment', {
24 | source: testSource,
25 | destination: testDesination,
26 | });
27 |
28 | new imagedeploy.DockerImageDeployment(stack, 'TestDeploymentNoTag', {
29 | source: testSource,
30 | destination: testDesinationNoTag,
31 | });
32 |
33 | new imagedeploy.DockerImageDeployment(stack, 'TestDeploymentNoOptions', {
34 | source: testSource,
35 | destination: testDesinationNoOptions,
36 | });
37 |
38 | test('iam policy is granted correct permissions', () => {
39 | Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', {
40 | PolicyDocument: {
41 | Statement: Match.arrayWith([
42 | {
43 | Action: [
44 | 'ecr:BatchCheckLayerAvailability',
45 | 'ecr:GetDownloadUrlForLayer',
46 | 'ecr:BatchGetImage',
47 | ],
48 | Effect: 'Allow',
49 | Resource: {
50 | 'Fn::Join': [
51 | '',
52 | [
53 | 'arn:',
54 | {
55 | Ref: 'AWS::Partition',
56 | },
57 | ':ecr:',
58 | {
59 | Ref: 'AWS::Region',
60 | },
61 | ':',
62 | {
63 | Ref: 'AWS::AccountId',
64 | },
65 | ':repository/',
66 | {
67 | 'Fn::Sub': 'cdk-hnb659fds-container-assets-${AWS::AccountId}-${AWS::Region}',
68 | },
69 | ],
70 | ],
71 | },
72 | },
73 | {
74 | Action: 'ecr:GetAuthorizationToken',
75 | Effect: 'Allow',
76 | Resource: '*',
77 | },
78 | {
79 | Action: [
80 | 'ecr:BatchCheckLayerAvailability',
81 | 'ecr:GetDownloadUrlForLayer',
82 | 'ecr:BatchGetImage',
83 | ],
84 | Effect: 'Allow',
85 | Resource: {
86 | 'Fn::GetAtt': [
87 | 'TestRepositoryC0DA8195',
88 | 'Arn',
89 | ],
90 | },
91 | },
92 | {
93 | Action: [
94 | 'ecr:PutImage',
95 | 'ecr:InitiateLayerUpload',
96 | 'ecr:UploadLayerPart',
97 | 'ecr:CompleteLayerUpload',
98 | ],
99 | Effect: 'Allow',
100 | Resource: {
101 | 'Fn::GetAtt': [
102 | 'TestRepositoryC0DA8195',
103 | 'Arn',
104 | ],
105 | },
106 | },
107 | {
108 | Action: [
109 | 'logs:CreateLogGroup',
110 | 'logs:CreateLogStream',
111 | 'logs:PutLogEvents',
112 | ],
113 | Effect: 'Allow',
114 | Resource: [
115 | {
116 | 'Fn::Join': [
117 | '',
118 | [
119 | 'arn:',
120 | {
121 | Ref: 'AWS::Partition',
122 | },
123 | ':logs:',
124 | {
125 | Ref: 'AWS::Region',
126 | },
127 | ':',
128 | {
129 | Ref: 'AWS::AccountId',
130 | },
131 | ':log-group:/aws/codebuild/',
132 | {
133 | Ref: 'TestDeploymentDockerImageDeployProject0884B3B5',
134 | },
135 | ],
136 | ],
137 | },
138 | {
139 | 'Fn::Join': [
140 | '',
141 | [
142 | 'arn:',
143 | {
144 | Ref: 'AWS::Partition',
145 | },
146 | ':logs:',
147 | {
148 | Ref: 'AWS::Region',
149 | },
150 | ':',
151 | {
152 | Ref: 'AWS::AccountId',
153 | },
154 | ':log-group:/aws/codebuild/',
155 | {
156 | Ref: 'TestDeploymentDockerImageDeployProject0884B3B5',
157 | },
158 | ':*',
159 | ],
160 | ],
161 | },
162 | ],
163 | },
164 | {
165 | Action: [
166 | 'codebuild:CreateReportGroup',
167 | 'codebuild:CreateReport',
168 | 'codebuild:UpdateReport',
169 | 'codebuild:BatchPutTestCases',
170 | 'codebuild:BatchPutCodeCoverages',
171 | ],
172 | Effect: 'Allow',
173 | Resource: {
174 | 'Fn::Join': [
175 | '',
176 | [
177 | 'arn:',
178 | {
179 | Ref: 'AWS::Partition',
180 | },
181 | ':codebuild:',
182 | {
183 | Ref: 'AWS::Region',
184 | },
185 | ':',
186 | {
187 | Ref: 'AWS::AccountId',
188 | },
189 | ':report-group/',
190 | {
191 | Ref: 'TestDeploymentDockerImageDeployProject0884B3B5',
192 | },
193 | '-*',
194 | ],
195 | ],
196 | },
197 | },
198 | ]),
199 | },
200 | });
201 | });
202 |
203 | test('docker tag command is well formatted: tag provided', () => {
204 | Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', {
205 | Source: {
206 | BuildSpec: {
207 | 'Fn::Join': Match.arrayWith([
208 | Match.arrayWith([
209 | '",\n "docker tag ',
210 | { 'Fn::Sub': '${AWS::AccountId}.dkr.ecr.${AWS::Region}.${AWS::URLSuffix}/cdk-hnb659fds-container-assets-${AWS::AccountId}-${AWS::Region}:70d1a3115d17d2ad7210b272e45b7398a7661e7b0cf24b52e059ae3f1fa8f2c1' },
211 | ' ',
212 | {
213 | 'Fn::Select': [4, {
214 | 'Fn::Split': [':', {
215 | 'Fn::GetAtt': [
216 | 'TestRepositoryC0DA8195',
217 | 'Arn',
218 | ],
219 | }],
220 | }],
221 | },
222 | '.dkr.ecr.',
223 | {
224 | 'Fn::Select': [3, {
225 | 'Fn::Split': [':', {
226 | 'Fn::GetAtt': [
227 | 'TestRepositoryC0DA8195',
228 | 'Arn',
229 | ],
230 | }],
231 | }],
232 | },
233 | '.',
234 | { Ref: 'AWS::URLSuffix' },
235 | '/',
236 | { Ref: 'TestRepositoryC0DA8195' },
237 | Match.stringLikeRegexp('^:testtag",(.)*'),
238 | ]),
239 | ]),
240 | },
241 | },
242 | });
243 | });
244 |
245 | test('docker tag command is well formatted: no tag provided', () => {
246 | Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', {
247 | Source: {
248 | BuildSpec: {
249 | 'Fn::Join': Match.arrayWith([
250 | Match.arrayWith([
251 | '",\n "docker tag ',
252 | { 'Fn::Sub': '${AWS::AccountId}.dkr.ecr.${AWS::Region}.${AWS::URLSuffix}/cdk-hnb659fds-container-assets-${AWS::AccountId}-${AWS::Region}:70d1a3115d17d2ad7210b272e45b7398a7661e7b0cf24b52e059ae3f1fa8f2c1' },
253 | ' ',
254 | {
255 | 'Fn::Select': [4, {
256 | 'Fn::Split': [':', {
257 | 'Fn::GetAtt': [
258 | 'TestRepositoryC0DA8195',
259 | 'Arn',
260 | ],
261 | }],
262 | }],
263 | },
264 | '.dkr.ecr.',
265 | {
266 | 'Fn::Select': [3, {
267 | 'Fn::Split': [':', {
268 | 'Fn::GetAtt': [
269 | 'TestRepositoryC0DA8195',
270 | 'Arn',
271 | ],
272 | }],
273 | }],
274 | },
275 | '.',
276 | { Ref: 'AWS::URLSuffix' },
277 | '/',
278 | { Ref: 'TestRepositoryC0DA8195' },
279 | // need no-tag validation here, will be better once cdk 2.38.1 is recognized
280 | ]),
281 | ]),
282 | },
283 | },
284 | });
285 | });
286 |
287 | });
288 |
289 | test('ECR login and pull commands are well formatted', () => {
290 | Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', {
291 | Source: {
292 | BuildSpec: {
293 | 'Fn::Join': Match.arrayWith([
294 | Match.arrayWith([
295 | /*
296 | '{\n "version": "0.2",\n "phases": {\n "pre_build": {\n "commands": [\n "aws ecr get-login-password --region ',
297 | { Ref: 'AWS::Region' },
298 | ' | docker login --username AWS --password-stdin ',
299 | { Ref: 'AWS::AccountId' },
300 | '.dkr.ecr.',
301 | { Ref: 'AWS::Region' },
302 | '.amazonaws.com"\n ]\n },\n "build": {\n "commands": [\n "docker pull ',
303 | { 'Fn::Sub': '${AWS::AccountId}.dkr.ecr.${AWS::Region}.${AWS::URLSuffix}/cdk-hnb659fds-container-assets-${AWS::AccountId}-${AWS::Region}:70d1a3115d17d2ad7210b272e45b7398a7661e7b0cf24b52e059ae3f1fa8f2c1' },
304 | */
305 | '{\n "version": "0.2",\n "phases": {\n "build": {\n "commands": [\n "aws ecr get-login-password --region ',
306 | { Ref: 'AWS::Region' },
307 | ' | docker login --username AWS --password-stdin ',
308 | { Ref: 'AWS::AccountId' },
309 | '.dkr.ecr.',
310 | { Ref: 'AWS::Region' },
311 | '.amazonaws.com",\n "docker pull ',
312 | { 'Fn::Sub': '${AWS::AccountId}.dkr.ecr.${AWS::Region}.${AWS::URLSuffix}/cdk-hnb659fds-container-assets-${AWS::AccountId}-${AWS::Region}:70d1a3115d17d2ad7210b272e45b7398a7661e7b0cf24b52e059ae3f1fa8f2c1' },
313 | '",\n "docker tag ',
314 | { 'Fn::Sub': '${AWS::AccountId}.dkr.ecr.${AWS::Region}.${AWS::URLSuffix}/cdk-hnb659fds-container-assets-${AWS::AccountId}-${AWS::Region}:70d1a3115d17d2ad7210b272e45b7398a7661e7b0cf24b52e059ae3f1fa8f2c1' },
315 | ' ',
316 | { 'Fn::Select': [4, { 'Fn::Split': [':', { 'Fn::GetAtt': ['TestRepositoryC0DA8195', 'Arn'] }] }] },
317 | '.dkr.ecr.',
318 | { 'Fn::Select': [3, { 'Fn::Split': [':', { 'Fn::GetAtt': ['TestRepositoryC0DA8195', 'Arn'] }] }] },
319 | '.',
320 | { Ref: 'AWS::URLSuffix' },
321 | '/',
322 | { Ref: 'TestRepositoryC0DA8195' },
323 | ':testtag",\n "docker push ',
324 | { 'Fn::Select': [4, { 'Fn::Split': [':', { 'Fn::GetAtt': ['TestRepositoryC0DA8195', 'Arn'] }] }] },
325 | '.dkr.ecr.',
326 | { 'Fn::Select': [3, { 'Fn::Split': [':', { 'Fn::GetAtt': ['TestRepositoryC0DA8195', 'Arn'] }] }] },
327 | '.',
328 | { Ref: 'AWS::URLSuffix' },
329 | '/',
330 | { Ref: 'TestRepositoryC0DA8195' },
331 | ':testtag",\n "docker logout"\n ]\n }\n }\n}',
332 | ]),
333 | ]),
334 | },
335 | },
336 | });
337 | });
338 | });
339 |
340 | describe('Tag validation', () => {
341 | // GIVEN
342 | const repo = new ecr.Repository(stack, 'TestTagsRepository');
343 | const testSource = imagedeploy.Source.directory(path.join(__dirname, 'assets/test1'));
344 |
345 | test('valid tag', () => {
346 | new imagedeploy.DockerImageDeployment(stack, 'TestValidTag', {
347 | source: testSource,
348 | destination: imagedeploy.Destination.ecr(repo, { tag: '_test_TEST-1234.tag-' }),
349 | });
350 | });
351 |
352 | test('options not provided does not throw', () => {
353 | new imagedeploy.DockerImageDeployment(stack, 'TestNoOptions', {
354 | source: testSource,
355 | destination: imagedeploy.Destination.ecr(repo),
356 | });
357 | });
358 |
359 | test('tag not provided does not throw', () => {
360 | new imagedeploy.DockerImageDeployment(stack, 'TestNoTag', {
361 | source: testSource,
362 | destination: imagedeploy.Destination.ecr(repo, {}),
363 | });
364 | });
365 |
366 | test('empyty tag', () => {
367 | expect(() => {
368 | new imagedeploy.DockerImageDeployment(stack, 'TestEmptyTag', {
369 | source: testSource,
370 | destination: imagedeploy.Destination.ecr(repo, { tag: '' }),
371 | });
372 | }).toThrow('Invalid tag: tags must contain alphanumeric characters and \'-\' \'_\' \'.\' only and must not begin with \'.\' or \'-\'');
373 | });
374 |
375 | test('tag contains invalid character', () => {
376 | expect(() => {
377 | new imagedeploy.DockerImageDeployment(stack, 'TestInvalidCharacter', {
378 | source: testSource,
379 | destination: imagedeploy.Destination.ecr(repo, { tag: 'testTag123!' }),
380 | });
381 | }).toThrow('Invalid tag: tags must contain alphanumeric characters and \'-\' \'_\' \'.\' only and must not begin with \'.\' or \'-\'');
382 | });
383 |
384 | test('tag starts with invalid character \'-\'', () => {
385 | expect(() => {
386 | new imagedeploy.DockerImageDeployment(stack, 'TestStartInvalidCharacterDash', {
387 | source: testSource,
388 | destination: imagedeploy.Destination.ecr(repo, { tag: '-testTag123' }),
389 | });
390 | }).toThrow('Invalid tag: tags must contain alphanumeric characters and \'-\' \'_\' \'.\' only and must not begin with \'.\' or \'-\'');
391 | });
392 |
393 | test('tag starts with invalid character \'.\'', () => {
394 | expect(() => {
395 | new imagedeploy.DockerImageDeployment(stack, 'TestStartInvalidCharacterPeriod', {
396 | source: testSource,
397 | destination: imagedeploy.Destination.ecr(repo, { tag: '.testTag123' }),
398 | });
399 | }).toThrow('Invalid tag: tags must contain alphanumeric characters and \'-\' \'_\' \'.\' only and must not begin with \'.\' or \'-\'');
400 | });
401 |
402 | test('tag is over max length', () => {
403 | expect(() => {
404 | new imagedeploy.DockerImageDeployment(stack, 'TestMaxTagLength', {
405 | source: testSource,
406 | destination: imagedeploy.Destination.ecr(repo, { tag: 'longtag-10longtag-20longtag-30longtag-40longtag-50longtag-60longtag-70longtag-80longtag-90longtag-101logestTagOneCharacterOver128' }),
407 | });
408 | }).toThrow('Invalid tag: tags may contain a maximum of 128 characters');
409 | });
410 | });
411 |
412 | describe('Custom Resrouces', () => {
413 | test('onEventHandler has correct permissions', () => {
414 | Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', {
415 | PolicyDocument: {
416 | Statement: Match.arrayWith([
417 | {
418 | Action: 'codebuild:StartBuild',
419 | Effect: 'Allow',
420 | Resource: {
421 | 'Fn::GetAtt': [
422 | 'TestDeploymentDockerImageDeployProject0884B3B5',
423 | 'Arn',
424 | ],
425 | },
426 | },
427 | ]),
428 | },
429 | });
430 | });
431 |
432 | test('isCompleteHandler has correct permissions', () => {
433 | Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', {
434 | PolicyDocument: {
435 | Statement: Match.arrayWith([
436 | {
437 | Action: [
438 | 'codebuild:ListBuildsForProject',
439 | 'codebuild:BatchGetBuilds',
440 | ],
441 | Effect: 'Allow',
442 | Resource: {
443 | 'Fn::GetAtt': [
444 | 'TestDeploymentDockerImageDeployProject0884B3B5',
445 | 'Arn',
446 | ],
447 | },
448 | },
449 | ]),
450 | },
451 | });
452 | });
453 | });
454 | });
--------------------------------------------------------------------------------
/test/integ/app.ts:
--------------------------------------------------------------------------------
1 | import * as path from 'path';
2 | import { Stack, App } from 'aws-cdk-lib';
3 | import * as ecr from 'aws-cdk-lib/aws-ecr';
4 | import { Construct } from 'constructs';
5 | import * as imagedeploy from '../../src/index';
6 |
7 | const app = new App();
8 |
9 | class DockerImageDeploymentStack extends Stack {
10 | constructor(scope: Construct, id: string) {
11 | super(scope, id);
12 |
13 | const repo = new ecr.Repository(this, 'MyRepository', {
14 | repositoryName: 'myrepository',
15 | });
16 |
17 | new imagedeploy.DockerImageDeployment(this, 'MyImageDeployment', {
18 | source: imagedeploy.Source.directory(path.join(__dirname, '../assets/test1')),
19 | destination: imagedeploy.Destination.ecr(repo, {
20 | tag: 'myTag',
21 | }),
22 | });
23 | }
24 | }
25 |
26 | new DockerImageDeploymentStack(app, 'MyStack');
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------