├── .eslintrc.json ├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── pull_request_template.md └── workflows │ ├── auto-approve.yml │ ├── build.yml │ ├── pull-request-lint.yml │ ├── release-cdkv1.yml │ ├── release.yml │ ├── upgrade-cdkv1.yml │ └── upgrade-master.yml ├── .gitignore ├── .gitpod.yml ├── .mergify.yml ├── .npmignore ├── .projen ├── deps.json ├── files.json └── tasks.json ├── .projenrc.js ├── API.md ├── CHANGELOG.md ├── JSIIREADME.md ├── LICENSE ├── README.md ├── assets ├── functions │ ├── autoscaling_events.py │ ├── index.py │ └── unregister_runner.py └── userdata │ └── amazon-cloudwatch-agent.json ├── image ├── cdk-gitlab-runner.png ├── gitlab-runner-new-register-1.png ├── gitlab-runner-new-register-2.png ├── gitlab-runner-new-register-3.jpg ├── gitlab-runner-new-register-project.png ├── gitlab-runner-new-register.png ├── group_runner2.png ├── group_runner_page.png ├── project_runner_page.png └── session.png ├── package.json ├── src ├── gitlab-runner-autoscaling.ts ├── gitlab-runner-instance.ts ├── gitlab-runner-interfaces.ts ├── index.ts ├── integ.api.ts └── integ.gitlab-runner-autoscaling.ts ├── test ├── gitlab-runner-autoscaling.test.ts └── gitlab-runner-instance.test.ts ├── tsconfig.dev.json ├── version.json └── yarn.lock /.eslintrc.json: -------------------------------------------------------------------------------- 1 | // ~~ Generated by projen. To modify, edit .projenrc.js 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.js" 44 | ], 45 | "rules": { 46 | "@stylistic/indent": [ 47 | "error", 48 | 2 49 | ], 50 | "@stylistic/quotes": [ 51 | "error", 52 | "single", 53 | { 54 | "avoidEscape": true 55 | } 56 | ], 57 | "@stylistic/comma-dangle": [ 58 | "error", 59 | "always-multiline" 60 | ], 61 | "@stylistic/comma-spacing": [ 62 | "error", 63 | { 64 | "before": false, 65 | "after": true 66 | } 67 | ], 68 | "@stylistic/no-multi-spaces": [ 69 | "error", 70 | { 71 | "ignoreEOLComments": false 72 | } 73 | ], 74 | "@stylistic/array-bracket-spacing": [ 75 | "error", 76 | "never" 77 | ], 78 | "@stylistic/array-bracket-newline": [ 79 | "error", 80 | "consistent" 81 | ], 82 | "@stylistic/object-curly-spacing": [ 83 | "error", 84 | "always" 85 | ], 86 | "@stylistic/object-curly-newline": [ 87 | "error", 88 | { 89 | "multiline": true, 90 | "consistent": true 91 | } 92 | ], 93 | "@stylistic/object-property-newline": [ 94 | "error", 95 | { 96 | "allowAllPropertiesOnSameLine": true 97 | } 98 | ], 99 | "@stylistic/keyword-spacing": [ 100 | "error" 101 | ], 102 | "@stylistic/brace-style": [ 103 | "error", 104 | "1tbs", 105 | { 106 | "allowSingleLine": true 107 | } 108 | ], 109 | "@stylistic/space-before-blocks": [ 110 | "error" 111 | ], 112 | "@stylistic/member-delimiter-style": [ 113 | "error" 114 | ], 115 | "@stylistic/semi": [ 116 | "error", 117 | "always" 118 | ], 119 | "@stylistic/max-len": [ 120 | "error", 121 | { 122 | "code": 150, 123 | "ignoreUrls": true, 124 | "ignoreStrings": true, 125 | "ignoreTemplateLiterals": true, 126 | "ignoreComments": true, 127 | "ignoreRegExpLiterals": true 128 | } 129 | ], 130 | "@stylistic/quote-props": [ 131 | "error", 132 | "consistent-as-needed" 133 | ], 134 | "@stylistic/key-spacing": [ 135 | "error" 136 | ], 137 | "@stylistic/no-multiple-empty-lines": [ 138 | "error" 139 | ], 140 | "@stylistic/no-trailing-spaces": [ 141 | "error" 142 | ], 143 | "curly": [ 144 | "error", 145 | "multi-line", 146 | "consistent" 147 | ], 148 | "@typescript-eslint/no-require-imports": "error", 149 | "import/no-extraneous-dependencies": [ 150 | "error", 151 | { 152 | "devDependencies": [ 153 | "**/test/**", 154 | "**/build-tools/**" 155 | ], 156 | "optionalDependencies": false, 157 | "peerDependencies": true 158 | } 159 | ], 160 | "import/no-unresolved": [ 161 | "error" 162 | ], 163 | "import/order": [ 164 | "warn", 165 | { 166 | "groups": [ 167 | "builtin", 168 | "external" 169 | ], 170 | "alphabetize": { 171 | "order": "asc", 172 | "caseInsensitive": true 173 | } 174 | } 175 | ], 176 | "import/no-duplicates": [ 177 | "error" 178 | ], 179 | "no-shadow": [ 180 | "off" 181 | ], 182 | "@typescript-eslint/no-shadow": "error", 183 | "@typescript-eslint/no-floating-promises": "error", 184 | "no-return-await": [ 185 | "off" 186 | ], 187 | "@typescript-eslint/return-await": "error", 188 | "dot-notation": [ 189 | "error" 190 | ], 191 | "no-bitwise": [ 192 | "error" 193 | ], 194 | "@typescript-eslint/member-ordering": [ 195 | "error", 196 | { 197 | "default": [ 198 | "public-static-field", 199 | "public-static-method", 200 | "protected-static-field", 201 | "protected-static-method", 202 | "private-static-field", 203 | "private-static-method", 204 | "field", 205 | "constructor", 206 | "method" 207 | ] 208 | } 209 | ] 210 | }, 211 | "overrides": [ 212 | { 213 | "files": [ 214 | ".projenrc.js" 215 | ], 216 | "rules": { 217 | "@typescript-eslint/no-require-imports": "off", 218 | "import/no-extraneous-dependencies": "off" 219 | } 220 | } 221 | ] 222 | } 223 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js 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-cdkv1.yml linguist-generated 12 | /.github/workflows/release.yml linguist-generated 13 | /.github/workflows/upgrade-cdkv1.yml linguist-generated 14 | /.github/workflows/upgrade-master.yml linguist-generated 15 | /.gitignore linguist-generated 16 | /.mergify.yml linguist-generated 17 | /.npmignore linguist-generated 18 | /.projen/** linguist-generated 19 | /.projen/deps.json linguist-generated 20 | /.projen/files.json linguist-generated 21 | /.projen/tasks.json linguist-generated 22 | /API.md linguist-generated 23 | /LICENSE linguist-generated 24 | /package.json linguist-generated 25 | /tsconfig.dev.json linguist-generated 26 | /yarn.lock linguist-generated -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: neilkuan 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F680 Feature Request" 3 | about: Request a new feature 4 | title: "(module name): short issue description" 5 | labels: feature-request, needs-triage 6 | --- 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | ### Use Case 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | ### Proposed Solution 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | ### Other 31 | 32 | 36 | 37 | 38 | 39 | 40 | 41 | * [ ] :wave: I may be able to implement this feature request 42 | * [ ] :warning: This feature might incur a breaking change 43 | 44 | --- 45 | 46 | This is a :rocket: Feature Request 47 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Fixes # -------------------------------------------------------------------------------- /.github/workflows/auto-approve.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js 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 == 'neilkuan') 18 | steps: 19 | - uses: hmarr/auto-approve-action@v2.2.1 20 | with: 21 | github-token: ${{ secrets.GITHUB_TOKEN }} 22 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | name: build 4 | on: 5 | pull_request: {} 6 | workflow_dispatch: {} 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | contents: write 12 | outputs: 13 | self_mutation_happened: ${{ steps.self_mutation.outputs.self_mutation_happened }} 14 | env: 15 | CI: "true" 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4 19 | with: 20 | ref: ${{ github.event.pull_request.head.ref }} 21 | repository: ${{ github.event.pull_request.head.repo.full_name }} 22 | - name: Setup Node.js 23 | uses: actions/setup-node@v4 24 | with: 25 | node-version: 20.10.0 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 | working-directory: ./ 36 | - name: Upload patch 37 | if: steps.self_mutation.outputs.self_mutation_happened 38 | uses: actions/upload-artifact@v4.4.0 39 | with: 40 | name: repo.patch 41 | path: repo.patch 42 | overwrite: true 43 | - name: Fail build on mutation 44 | if: steps.self_mutation.outputs.self_mutation_happened 45 | run: |- 46 | echo "::error::Files were changed during build (see build log). If this was triggered from a fork, you will need to update your branch." 47 | cat repo.patch 48 | exit 1 49 | - name: Backup artifact permissions 50 | run: cd dist && getfacl -R . > permissions-backup.acl 51 | continue-on-error: true 52 | - name: Upload artifact 53 | uses: actions/upload-artifact@v4.4.0 54 | with: 55 | name: build-artifact 56 | path: dist 57 | overwrite: true 58 | self-mutation: 59 | needs: build 60 | runs-on: ubuntu-latest 61 | permissions: 62 | contents: write 63 | if: always() && needs.build.outputs.self_mutation_happened && !(github.event.pull_request.head.repo.full_name != github.repository) 64 | steps: 65 | - name: Checkout 66 | uses: actions/checkout@v4 67 | with: 68 | token: ${{ secrets.PROJEN_GITHUB_TOKEN }} 69 | ref: ${{ github.event.pull_request.head.ref }} 70 | repository: ${{ github.event.pull_request.head.repo.full_name }} 71 | - name: Download patch 72 | uses: actions/download-artifact@v4 73 | with: 74 | name: repo.patch 75 | path: ${{ runner.temp }} 76 | - name: Apply patch 77 | run: '[ -s ${{ runner.temp }}/repo.patch ] && git apply ${{ runner.temp }}/repo.patch || echo "Empty patch. Skipping."' 78 | - name: Set git identity 79 | run: |- 80 | git config user.name "github-actions" 81 | git config user.email "github-actions@github.com" 82 | - name: Push changes 83 | env: 84 | PULL_REQUEST_REF: ${{ github.event.pull_request.head.ref }} 85 | run: |- 86 | git add . 87 | git commit -s -m "chore: self mutation" 88 | git push origin HEAD:$PULL_REQUEST_REF 89 | package-js: 90 | needs: build 91 | runs-on: ubuntu-latest 92 | permissions: 93 | contents: read 94 | if: ${{ !needs.build.outputs.self_mutation_happened }} 95 | steps: 96 | - uses: actions/setup-node@v4 97 | with: 98 | node-version: 20.10.0 99 | - name: Download build artifacts 100 | uses: actions/download-artifact@v4 101 | with: 102 | name: build-artifact 103 | path: dist 104 | - name: Restore build artifact permissions 105 | run: cd dist && setfacl --restore=permissions-backup.acl 106 | continue-on-error: true 107 | - name: Checkout 108 | uses: actions/checkout@v4 109 | with: 110 | ref: ${{ github.event.pull_request.head.ref }} 111 | repository: ${{ github.event.pull_request.head.repo.full_name }} 112 | path: .repo 113 | - name: Install Dependencies 114 | run: cd .repo && yarn install --check-files --frozen-lockfile 115 | - name: Extract build artifact 116 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo 117 | - name: Move build artifact out of the way 118 | run: mv dist dist.old 119 | - name: Create js artifact 120 | run: cd .repo && npx projen package:js 121 | - name: Collect js artifact 122 | run: mv .repo/dist dist 123 | package-python: 124 | needs: build 125 | runs-on: ubuntu-latest 126 | permissions: 127 | contents: read 128 | if: ${{ !needs.build.outputs.self_mutation_happened }} 129 | steps: 130 | - uses: actions/setup-node@v4 131 | with: 132 | node-version: 20.10.0 133 | - uses: actions/setup-python@v5 134 | with: 135 | python-version: 3.x 136 | - name: Download build artifacts 137 | uses: actions/download-artifact@v4 138 | with: 139 | name: build-artifact 140 | path: dist 141 | - name: Restore build artifact permissions 142 | run: cd dist && setfacl --restore=permissions-backup.acl 143 | continue-on-error: true 144 | - name: Checkout 145 | uses: actions/checkout@v4 146 | with: 147 | ref: ${{ github.event.pull_request.head.ref }} 148 | repository: ${{ github.event.pull_request.head.repo.full_name }} 149 | path: .repo 150 | - name: Install Dependencies 151 | run: cd .repo && yarn install --check-files --frozen-lockfile 152 | - name: Extract build artifact 153 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo 154 | - name: Move build artifact out of the way 155 | run: mv dist dist.old 156 | - name: Create python artifact 157 | run: cd .repo && npx projen package:python 158 | - name: Collect python artifact 159 | run: mv .repo/dist dist 160 | -------------------------------------------------------------------------------- /.github/workflows/pull-request-lint.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | name: pull-request-lint 4 | on: 5 | pull_request_target: 6 | types: 7 | - labeled 8 | - opened 9 | - synchronize 10 | - reopened 11 | - ready_for_review 12 | - edited 13 | merge_group: {} 14 | jobs: 15 | validate: 16 | name: Validate PR title 17 | runs-on: ubuntu-latest 18 | permissions: 19 | pull-requests: write 20 | if: (github.event_name == 'pull_request' || github.event_name == 'pull_request_target') 21 | steps: 22 | - uses: amannn/action-semantic-pull-request@v5.4.0 23 | env: 24 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 25 | with: 26 | types: |- 27 | feat 28 | fix 29 | chore 30 | requireScope: false 31 | -------------------------------------------------------------------------------- /.github/workflows/release-cdkv1.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | name: release-cdkv1 4 | on: 5 | push: 6 | branches: 7 | - cdkv1 8 | workflow_dispatch: {} 9 | concurrency: 10 | group: ${{ github.workflow }} 11 | cancel-in-progress: false 12 | jobs: 13 | release: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: write 17 | outputs: 18 | latest_commit: ${{ steps.git_remote.outputs.latest_commit }} 19 | tag_exists: ${{ steps.check_tag_exists.outputs.exists }} 20 | env: 21 | CI: "true" 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v4 25 | with: 26 | fetch-depth: 0 27 | - name: Set git identity 28 | run: |- 29 | git config user.name "github-actions" 30 | git config user.email "github-actions@github.com" 31 | - name: Setup Node.js 32 | uses: actions/setup-node@v4 33 | with: 34 | node-version: 20.10.0 35 | - name: Install dependencies 36 | run: yarn install --check-files --frozen-lockfile 37 | - name: release:cdkv1 38 | run: npx projen release:cdkv1 39 | - name: Check if version has already been tagged 40 | id: check_tag_exists 41 | run: |- 42 | TAG=$(cat dist/releasetag.txt) 43 | ([ ! -z "$TAG" ] && git ls-remote -q --exit-code --tags origin $TAG && (echo "exists=true" >> $GITHUB_OUTPUT)) || (echo "exists=false" >> $GITHUB_OUTPUT) 44 | cat $GITHUB_OUTPUT 45 | - name: Check for new commits 46 | id: git_remote 47 | run: |- 48 | echo "latest_commit=$(git ls-remote origin -h ${{ github.ref }} | cut -f1)" >> $GITHUB_OUTPUT 49 | cat $GITHUB_OUTPUT 50 | - name: Backup artifact permissions 51 | if: ${{ steps.git_remote.outputs.latest_commit == github.sha }} 52 | run: cd dist && getfacl -R . > permissions-backup.acl 53 | continue-on-error: true 54 | - name: Upload artifact 55 | if: ${{ steps.git_remote.outputs.latest_commit == github.sha }} 56 | uses: actions/upload-artifact@v4.4.0 57 | with: 58 | name: build-artifact 59 | path: dist 60 | overwrite: true 61 | release_github: 62 | name: Publish to GitHub Releases 63 | needs: 64 | - release 65 | - release_npm 66 | - release_pypi 67 | runs-on: ubuntu-latest 68 | permissions: 69 | contents: write 70 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha 71 | steps: 72 | - uses: actions/setup-node@v4 73 | with: 74 | node-version: 20.10.0 75 | - name: Download build artifacts 76 | uses: actions/download-artifact@v4 77 | with: 78 | name: build-artifact 79 | path: dist 80 | - name: Restore build artifact permissions 81 | run: cd dist && setfacl --restore=permissions-backup.acl 82 | continue-on-error: true 83 | - name: Release 84 | env: 85 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 86 | 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 87 | release_npm: 88 | name: Publish to npm 89 | needs: release 90 | runs-on: ubuntu-latest 91 | permissions: 92 | id-token: write 93 | contents: read 94 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha 95 | steps: 96 | - uses: actions/setup-node@v4 97 | with: 98 | node-version: 20.10.0 99 | - name: Download build artifacts 100 | uses: actions/download-artifact@v4 101 | with: 102 | name: build-artifact 103 | path: dist 104 | - name: Restore build artifact permissions 105 | run: cd dist && setfacl --restore=permissions-backup.acl 106 | continue-on-error: true 107 | - name: Checkout 108 | uses: actions/checkout@v4 109 | with: 110 | path: .repo 111 | - name: Install Dependencies 112 | run: cd .repo && yarn install --check-files --frozen-lockfile 113 | - name: Extract build artifact 114 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo 115 | - name: Move build artifact out of the way 116 | run: mv dist dist.old 117 | - name: Create js artifact 118 | run: cd .repo && npx projen package:js 119 | - name: Collect js artifact 120 | run: mv .repo/dist dist 121 | - name: Release 122 | env: 123 | NPM_DIST_TAG: cdkv1 124 | NPM_REGISTRY: registry.npmjs.org 125 | NPM_CONFIG_PROVENANCE: "true" 126 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 127 | run: npx -p publib@latest publib-npm 128 | release_pypi: 129 | name: Publish to PyPI 130 | needs: release 131 | runs-on: ubuntu-latest 132 | permissions: 133 | contents: read 134 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha 135 | steps: 136 | - uses: actions/setup-node@v4 137 | with: 138 | node-version: 20.10.0 139 | - uses: actions/setup-python@v5 140 | with: 141 | python-version: 3.x 142 | - name: Download build artifacts 143 | uses: actions/download-artifact@v4 144 | with: 145 | name: build-artifact 146 | path: dist 147 | - name: Restore build artifact permissions 148 | run: cd dist && setfacl --restore=permissions-backup.acl 149 | continue-on-error: true 150 | - name: Checkout 151 | uses: actions/checkout@v4 152 | with: 153 | path: .repo 154 | - name: Install Dependencies 155 | run: cd .repo && yarn install --check-files --frozen-lockfile 156 | - name: Extract build artifact 157 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo 158 | - name: Move build artifact out of the way 159 | run: mv dist dist.old 160 | - name: Create python artifact 161 | run: cd .repo && npx projen package:python 162 | - name: Collect python artifact 163 | run: mv .repo/dist dist 164 | - name: Release 165 | env: 166 | TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }} 167 | TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} 168 | run: npx -p publib@latest publib-pypi 169 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | name: release 4 | on: 5 | push: 6 | branches: 7 | - master 8 | workflow_dispatch: {} 9 | concurrency: 10 | group: ${{ github.workflow }} 11 | cancel-in-progress: false 12 | jobs: 13 | release: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: write 17 | outputs: 18 | latest_commit: ${{ steps.git_remote.outputs.latest_commit }} 19 | tag_exists: ${{ steps.check_tag_exists.outputs.exists }} 20 | env: 21 | CI: "true" 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v4 25 | with: 26 | fetch-depth: 0 27 | - name: Set git identity 28 | run: |- 29 | git config user.name "github-actions" 30 | git config user.email "github-actions@github.com" 31 | - name: Setup Node.js 32 | uses: actions/setup-node@v4 33 | with: 34 | node-version: 20.10.0 35 | - name: Install dependencies 36 | run: yarn install --check-files --frozen-lockfile 37 | - name: release 38 | run: npx projen release 39 | - name: Check if version has already been tagged 40 | id: check_tag_exists 41 | run: |- 42 | TAG=$(cat dist/releasetag.txt) 43 | ([ ! -z "$TAG" ] && git ls-remote -q --exit-code --tags origin $TAG && (echo "exists=true" >> $GITHUB_OUTPUT)) || (echo "exists=false" >> $GITHUB_OUTPUT) 44 | cat $GITHUB_OUTPUT 45 | - name: Check for new commits 46 | id: git_remote 47 | run: |- 48 | echo "latest_commit=$(git ls-remote origin -h ${{ github.ref }} | cut -f1)" >> $GITHUB_OUTPUT 49 | cat $GITHUB_OUTPUT 50 | - name: Backup artifact permissions 51 | if: ${{ steps.git_remote.outputs.latest_commit == github.sha }} 52 | run: cd dist && getfacl -R . > permissions-backup.acl 53 | continue-on-error: true 54 | - name: Upload artifact 55 | if: ${{ steps.git_remote.outputs.latest_commit == github.sha }} 56 | uses: actions/upload-artifact@v4.4.0 57 | with: 58 | name: build-artifact 59 | path: dist 60 | overwrite: true 61 | release_github: 62 | name: Publish to GitHub Releases 63 | needs: 64 | - release 65 | - release_npm 66 | - release_pypi 67 | runs-on: ubuntu-latest 68 | permissions: 69 | contents: write 70 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha 71 | steps: 72 | - uses: actions/setup-node@v4 73 | with: 74 | node-version: 20.10.0 75 | - name: Download build artifacts 76 | uses: actions/download-artifact@v4 77 | with: 78 | name: build-artifact 79 | path: dist 80 | - name: Restore build artifact permissions 81 | run: cd dist && setfacl --restore=permissions-backup.acl 82 | continue-on-error: true 83 | - name: Release 84 | env: 85 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 86 | 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 87 | release_npm: 88 | name: Publish to npm 89 | needs: release 90 | runs-on: ubuntu-latest 91 | permissions: 92 | id-token: write 93 | contents: read 94 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha 95 | steps: 96 | - uses: actions/setup-node@v4 97 | with: 98 | node-version: 20.10.0 99 | - name: Download build artifacts 100 | uses: actions/download-artifact@v4 101 | with: 102 | name: build-artifact 103 | path: dist 104 | - name: Restore build artifact permissions 105 | run: cd dist && setfacl --restore=permissions-backup.acl 106 | continue-on-error: true 107 | - name: Checkout 108 | uses: actions/checkout@v4 109 | with: 110 | path: .repo 111 | - name: Install Dependencies 112 | run: cd .repo && yarn install --check-files --frozen-lockfile 113 | - name: Extract build artifact 114 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo 115 | - name: Move build artifact out of the way 116 | run: mv dist dist.old 117 | - name: Create js artifact 118 | run: cd .repo && npx projen package:js 119 | - name: Collect js artifact 120 | run: mv .repo/dist dist 121 | - name: Release 122 | env: 123 | NPM_DIST_TAG: latest 124 | NPM_REGISTRY: registry.npmjs.org 125 | NPM_CONFIG_PROVENANCE: "true" 126 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 127 | run: npx -p publib@latest publib-npm 128 | release_pypi: 129 | name: Publish to PyPI 130 | needs: release 131 | runs-on: ubuntu-latest 132 | permissions: 133 | contents: read 134 | if: needs.release.outputs.tag_exists != 'true' && needs.release.outputs.latest_commit == github.sha 135 | steps: 136 | - uses: actions/setup-node@v4 137 | with: 138 | node-version: 20.10.0 139 | - uses: actions/setup-python@v5 140 | with: 141 | python-version: 3.x 142 | - name: Download build artifacts 143 | uses: actions/download-artifact@v4 144 | with: 145 | name: build-artifact 146 | path: dist 147 | - name: Restore build artifact permissions 148 | run: cd dist && setfacl --restore=permissions-backup.acl 149 | continue-on-error: true 150 | - name: Checkout 151 | uses: actions/checkout@v4 152 | with: 153 | path: .repo 154 | - name: Install Dependencies 155 | run: cd .repo && yarn install --check-files --frozen-lockfile 156 | - name: Extract build artifact 157 | run: tar --strip-components=1 -xzvf dist/js/*.tgz -C .repo 158 | - name: Move build artifact out of the way 159 | run: mv dist dist.old 160 | - name: Create python artifact 161 | run: cd .repo && npx projen package:python 162 | - name: Collect python artifact 163 | run: mv .repo/dist dist 164 | - name: Release 165 | env: 166 | TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }} 167 | TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} 168 | run: npx -p publib@latest publib-pypi 169 | -------------------------------------------------------------------------------- /.github/workflows/upgrade-cdkv1.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | name: upgrade-cdkv1 4 | on: 5 | workflow_dispatch: {} 6 | schedule: 7 | - cron: 0 0 * * * 8 | jobs: 9 | upgrade: 10 | name: Upgrade 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: read 14 | outputs: 15 | patch_created: ${{ steps.create_patch.outputs.patch_created }} 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4 19 | with: 20 | ref: cdkv1 21 | - name: Setup Node.js 22 | uses: actions/setup-node@v4 23 | with: 24 | node-version: 20.10.0 25 | - name: Install dependencies 26 | run: yarn install --check-files --frozen-lockfile 27 | - name: Upgrade dependencies 28 | run: npx projen upgrade 29 | - name: Find mutations 30 | id: create_patch 31 | run: |- 32 | git add . 33 | git diff --staged --patch --exit-code > repo.patch || echo "patch_created=true" >> $GITHUB_OUTPUT 34 | working-directory: ./ 35 | - name: Upload patch 36 | if: steps.create_patch.outputs.patch_created 37 | uses: actions/upload-artifact@v4.4.0 38 | with: 39 | name: repo.patch 40 | path: repo.patch 41 | overwrite: true 42 | pr: 43 | name: Create Pull Request 44 | needs: upgrade 45 | runs-on: ubuntu-latest 46 | permissions: 47 | contents: read 48 | if: ${{ needs.upgrade.outputs.patch_created }} 49 | steps: 50 | - name: Checkout 51 | uses: actions/checkout@v4 52 | with: 53 | ref: cdkv1 54 | - name: Download patch 55 | uses: actions/download-artifact@v4 56 | with: 57 | name: repo.patch 58 | path: ${{ runner.temp }} 59 | - name: Apply patch 60 | run: '[ -s ${{ runner.temp }}/repo.patch ] && git apply ${{ runner.temp }}/repo.patch || echo "Empty patch. Skipping."' 61 | - name: Set git identity 62 | run: |- 63 | git config user.name "github-actions" 64 | git config user.email "github-actions@github.com" 65 | - name: Create Pull Request 66 | id: create-pr 67 | uses: peter-evans/create-pull-request@v6 68 | with: 69 | token: ${{ secrets.PROJEN_GITHUB_TOKEN }} 70 | commit-message: |- 71 | chore(deps): upgrade dependencies 72 | 73 | Upgrades project dependencies. See details in [workflow run]. 74 | 75 | [Workflow Run]: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} 76 | 77 | ------ 78 | 79 | *Automatically created by projen via the "upgrade-cdkv1" workflow* 80 | branch: github-actions/upgrade-cdkv1 81 | title: "chore(deps): upgrade dependencies" 82 | labels: auto-approve,auto-merge 83 | body: |- 84 | Upgrades project dependencies. See details in [workflow run]. 85 | 86 | [Workflow Run]: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} 87 | 88 | ------ 89 | 90 | *Automatically created by projen via the "upgrade-cdkv1" workflow* 91 | author: github-actions 92 | committer: github-actions 93 | signoff: true 94 | -------------------------------------------------------------------------------- /.github/workflows/upgrade-master.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | name: upgrade-master 4 | on: 5 | workflow_dispatch: {} 6 | schedule: 7 | - cron: 0 0 * * * 8 | jobs: 9 | upgrade: 10 | name: Upgrade 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: read 14 | outputs: 15 | patch_created: ${{ steps.create_patch.outputs.patch_created }} 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4 19 | with: 20 | ref: master 21 | - name: Setup Node.js 22 | uses: actions/setup-node@v4 23 | with: 24 | node-version: 20.10.0 25 | - name: Install dependencies 26 | run: yarn install --check-files --frozen-lockfile 27 | - name: Upgrade dependencies 28 | run: npx projen upgrade 29 | - name: Find mutations 30 | id: create_patch 31 | run: |- 32 | git add . 33 | git diff --staged --patch --exit-code > repo.patch || echo "patch_created=true" >> $GITHUB_OUTPUT 34 | working-directory: ./ 35 | - name: Upload patch 36 | if: steps.create_patch.outputs.patch_created 37 | uses: actions/upload-artifact@v4.4.0 38 | with: 39 | name: repo.patch 40 | path: repo.patch 41 | overwrite: true 42 | pr: 43 | name: Create Pull Request 44 | needs: upgrade 45 | runs-on: ubuntu-latest 46 | permissions: 47 | contents: read 48 | if: ${{ needs.upgrade.outputs.patch_created }} 49 | steps: 50 | - name: Checkout 51 | uses: actions/checkout@v4 52 | with: 53 | ref: master 54 | - name: Download patch 55 | uses: actions/download-artifact@v4 56 | with: 57 | name: repo.patch 58 | path: ${{ runner.temp }} 59 | - name: Apply patch 60 | run: '[ -s ${{ runner.temp }}/repo.patch ] && git apply ${{ runner.temp }}/repo.patch || echo "Empty patch. Skipping."' 61 | - name: Set git identity 62 | run: |- 63 | git config user.name "github-actions" 64 | git config user.email "github-actions@github.com" 65 | - name: Create Pull Request 66 | id: create-pr 67 | uses: peter-evans/create-pull-request@v6 68 | with: 69 | token: ${{ secrets.PROJEN_GITHUB_TOKEN }} 70 | commit-message: |- 71 | chore(deps): upgrade dependencies 72 | 73 | Upgrades project dependencies. See details in [workflow run]. 74 | 75 | [Workflow Run]: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} 76 | 77 | ------ 78 | 79 | *Automatically created by projen via the "upgrade-master" workflow* 80 | branch: github-actions/upgrade-master 81 | title: "chore(deps): upgrade dependencies" 82 | labels: auto-approve,auto-merge 83 | body: |- 84 | Upgrades project dependencies. See details in [workflow run]. 85 | 86 | [Workflow Run]: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} 87 | 88 | ------ 89 | 90 | *Automatically created by projen via the "upgrade-master" workflow* 91 | author: github-actions 92 | committer: github-actions 93 | signoff: true 94 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js 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 | *.lcov 24 | .nyc_output 25 | build/Release 26 | node_modules/ 27 | jspm_packages/ 28 | *.tsbuildinfo 29 | .eslintcache 30 | *.tgz 31 | .yarn-integrity 32 | .cache 33 | /test-reports/ 34 | junit.xml 35 | /coverage/ 36 | !/.github/workflows/build.yml 37 | /dist/changelog.md 38 | /dist/version.txt 39 | !/.github/workflows/release.yml 40 | !/.github/workflows/release-cdkv1.yml 41 | !/.mergify.yml 42 | !/.github/workflows/upgrade-master.yml 43 | !/.github/workflows/upgrade-cdkv1.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 | cdk.out 55 | cdk.context.json 56 | yarn-error.log 57 | coverage 58 | venv 59 | !/.projenrc.js 60 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | # This configuration file was automatically generated by Gitpod. 2 | # Please adjust to your needs (see https://www.gitpod.io/docs/introduction/learn-gitpod/gitpod-yaml) 3 | # and commit this file to your remote git repository to share the goodness with others. 4 | 5 | # Learn more from ready-to-use templates: https://www.gitpod.io/docs/introduction/getting-started/quickstart 6 | 7 | tasks: 8 | - init: yarn install && yarn run build 9 | command: yarn run watch 10 | 11 | 12 | -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | 3 | queue_rules: 4 | - name: default 5 | update_method: merge 6 | conditions: 7 | - "#approved-reviews-by>=1" 8 | - -label~=(do-not-merge) 9 | - status-success=build 10 | - status-success=package-js 11 | - status-success=package-python 12 | merge_method: squash 13 | commit_message_template: |- 14 | {{ title }} (#{{ number }}) 15 | 16 | {{ body }} 17 | pull_request_rules: 18 | - name: Automatic merge on approval and successful build 19 | actions: 20 | delete_head_branch: {} 21 | queue: 22 | name: default 23 | conditions: 24 | - "#approved-reviews-by>=1" 25 | - -label~=(do-not-merge) 26 | - status-success=build 27 | - status-success=package-js 28 | - status-success=package-python 29 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js 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 | cdk.out 26 | cdk.context.json 27 | yarn-error.log 28 | coverage 29 | venv 30 | image 31 | /.gitattributes 32 | -------------------------------------------------------------------------------- /.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 | "version": "^29", 11 | "type": "build" 12 | }, 13 | { 14 | "name": "@types/node", 15 | "version": "^20", 16 | "type": "build" 17 | }, 18 | { 19 | "name": "@typescript-eslint/eslint-plugin", 20 | "version": "^8", 21 | "type": "build" 22 | }, 23 | { 24 | "name": "@typescript-eslint/parser", 25 | "version": "^8", 26 | "type": "build" 27 | }, 28 | { 29 | "name": "commit-and-tag-version", 30 | "version": "^12", 31 | "type": "build" 32 | }, 33 | { 34 | "name": "eslint-import-resolver-typescript", 35 | "type": "build" 36 | }, 37 | { 38 | "name": "eslint-plugin-import", 39 | "type": "build" 40 | }, 41 | { 42 | "name": "eslint", 43 | "version": "^8", 44 | "type": "build" 45 | }, 46 | { 47 | "name": "jest-junit", 48 | "version": "^16", 49 | "type": "build" 50 | }, 51 | { 52 | "name": "jest", 53 | "version": "^29", 54 | "type": "build" 55 | }, 56 | { 57 | "name": "jsii-diff", 58 | "type": "build" 59 | }, 60 | { 61 | "name": "jsii-docgen", 62 | "version": "^10.5.0", 63 | "type": "build" 64 | }, 65 | { 66 | "name": "jsii-pacmak", 67 | "type": "build" 68 | }, 69 | { 70 | "name": "jsii-rosetta", 71 | "version": "5.0.x", 72 | "type": "build" 73 | }, 74 | { 75 | "name": "jsii", 76 | "version": "5.7.x", 77 | "type": "build" 78 | }, 79 | { 80 | "name": "projen", 81 | "type": "build" 82 | }, 83 | { 84 | "name": "ts-jest", 85 | "version": "^29", 86 | "type": "build" 87 | }, 88 | { 89 | "name": "typescript", 90 | "version": "^5", 91 | "type": "build" 92 | }, 93 | { 94 | "name": "compare-versions", 95 | "type": "bundled" 96 | }, 97 | { 98 | "name": "aws-cdk-lib", 99 | "version": "^2.189.1", 100 | "type": "peer" 101 | }, 102 | { 103 | "name": "constructs", 104 | "version": "^10.0.5", 105 | "type": "peer" 106 | }, 107 | { 108 | "name": "compare-versions", 109 | "type": "runtime" 110 | } 111 | ], 112 | "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." 113 | } 114 | -------------------------------------------------------------------------------- /.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-cdkv1.yml", 10 | ".github/workflows/release.yml", 11 | ".github/workflows/upgrade-cdkv1.yml", 12 | ".github/workflows/upgrade-master.yml", 13 | ".gitignore", 14 | ".mergify.yml", 15 | ".projen/deps.json", 16 | ".projen/files.json", 17 | ".projen/tasks.json", 18 | "LICENSE", 19 | "tsconfig.dev.json" 20 | ], 21 | "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." 22 | } 23 | -------------------------------------------------------------------------------- /.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": "node .projenrc.js" 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 | }, 131 | "steps": [ 132 | { 133 | "exec": "eslint --ext .ts,.tsx --fix --no-error-on-unmatched-pattern $@ src test build-tools .projenrc.js", 134 | "receiveArgs": true 135 | } 136 | ] 137 | }, 138 | "install": { 139 | "name": "install", 140 | "description": "Install project dependencies and update lockfile (non-frozen)", 141 | "steps": [ 142 | { 143 | "exec": "yarn install --check-files" 144 | } 145 | ] 146 | }, 147 | "install:ci": { 148 | "name": "install:ci", 149 | "description": "Install project dependencies using frozen lockfile", 150 | "steps": [ 151 | { 152 | "exec": "yarn install --check-files --frozen-lockfile" 153 | } 154 | ] 155 | }, 156 | "package": { 157 | "name": "package", 158 | "description": "Creates the distribution package", 159 | "steps": [ 160 | { 161 | "spawn": "package:js", 162 | "condition": "node -e \"if (!process.env.CI) process.exit(1)\"" 163 | }, 164 | { 165 | "spawn": "package-all", 166 | "condition": "node -e \"if (process.env.CI) process.exit(1)\"" 167 | } 168 | ] 169 | }, 170 | "package-all": { 171 | "name": "package-all", 172 | "description": "Packages artifacts for all target languages", 173 | "steps": [ 174 | { 175 | "spawn": "package:js" 176 | }, 177 | { 178 | "spawn": "package:python" 179 | } 180 | ] 181 | }, 182 | "package:js": { 183 | "name": "package:js", 184 | "description": "Create js language bindings", 185 | "steps": [ 186 | { 187 | "exec": "jsii-pacmak -v --target js" 188 | } 189 | ] 190 | }, 191 | "package:python": { 192 | "name": "package:python", 193 | "description": "Create python language bindings", 194 | "steps": [ 195 | { 196 | "exec": "jsii-pacmak -v --target python" 197 | } 198 | ] 199 | }, 200 | "post-compile": { 201 | "name": "post-compile", 202 | "description": "Runs after successful compilation", 203 | "steps": [ 204 | { 205 | "spawn": "docgen" 206 | } 207 | ] 208 | }, 209 | "post-upgrade": { 210 | "name": "post-upgrade", 211 | "description": "Runs after upgrading dependencies" 212 | }, 213 | "pre-compile": { 214 | "name": "pre-compile", 215 | "description": "Prepare the project for compilation" 216 | }, 217 | "release": { 218 | "name": "release", 219 | "description": "Prepare a release from \"master\" branch", 220 | "env": { 221 | "RELEASE": "true", 222 | "MAJOR": "2" 223 | }, 224 | "steps": [ 225 | { 226 | "exec": "rm -fr dist" 227 | }, 228 | { 229 | "spawn": "bump" 230 | }, 231 | { 232 | "spawn": "build" 233 | }, 234 | { 235 | "spawn": "unbump" 236 | }, 237 | { 238 | "exec": "git diff --ignore-space-at-eol --exit-code" 239 | } 240 | ] 241 | }, 242 | "release:cdkv1": { 243 | "name": "release:cdkv1", 244 | "description": "Prepare a release from \"cdkv1\" branch", 245 | "env": { 246 | "RELEASE": "true", 247 | "MAJOR": "1" 248 | }, 249 | "steps": [ 250 | { 251 | "exec": "rm -fr dist" 252 | }, 253 | { 254 | "spawn": "bump" 255 | }, 256 | { 257 | "spawn": "build" 258 | }, 259 | { 260 | "spawn": "unbump" 261 | }, 262 | { 263 | "exec": "git diff --ignore-space-at-eol --exit-code" 264 | } 265 | ] 266 | }, 267 | "test": { 268 | "name": "test", 269 | "description": "Run tests", 270 | "steps": [ 271 | { 272 | "exec": "jest --passWithNoTests --updateSnapshot", 273 | "receiveArgs": true 274 | }, 275 | { 276 | "spawn": "eslint" 277 | } 278 | ] 279 | }, 280 | "test:watch": { 281 | "name": "test:watch", 282 | "description": "Run jest in watch mode", 283 | "steps": [ 284 | { 285 | "exec": "jest --watch" 286 | } 287 | ] 288 | }, 289 | "unbump": { 290 | "name": "unbump", 291 | "description": "Restores version to 0.0.0", 292 | "env": { 293 | "OUTFILE": "package.json", 294 | "CHANGELOG": "dist/changelog.md", 295 | "BUMPFILE": "dist/version.txt", 296 | "RELEASETAG": "dist/releasetag.txt", 297 | "RELEASE_TAG_PREFIX": "", 298 | "BUMP_PACKAGE": "commit-and-tag-version@^12" 299 | }, 300 | "steps": [ 301 | { 302 | "builtin": "release/reset-version" 303 | } 304 | ] 305 | }, 306 | "upgrade": { 307 | "name": "upgrade", 308 | "description": "upgrade dependencies", 309 | "env": { 310 | "CI": "0" 311 | }, 312 | "steps": [ 313 | { 314 | "exec": "npx npm-check-updates@16 --upgrade --target=minor --peer --no-deprecated --dep=dev,peer,prod,optional --filter=eslint-import-resolver-typescript,eslint-plugin-import,jsii-diff,jsii-pacmak,projen,compare-versions" 315 | }, 316 | { 317 | "exec": "yarn install --check-files" 318 | }, 319 | { 320 | "exec": "yarn upgrade @stylistic/eslint-plugin @types/jest @types/node @typescript-eslint/eslint-plugin @typescript-eslint/parser commit-and-tag-version eslint-import-resolver-typescript eslint-plugin-import eslint jest-junit jest jsii-diff jsii-docgen jsii-pacmak jsii-rosetta jsii projen ts-jest typescript compare-versions aws-cdk-lib constructs" 321 | }, 322 | { 323 | "exec": "npx projen" 324 | }, 325 | { 326 | "spawn": "post-upgrade" 327 | } 328 | ] 329 | }, 330 | "watch": { 331 | "name": "watch", 332 | "description": "Watch & compile in the background", 333 | "steps": [ 334 | { 335 | "exec": "jsii -w --silence-warnings=reserved-word" 336 | } 337 | ] 338 | } 339 | }, 340 | "env": { 341 | "PATH": "$(npx -c \"node --print process.env.PATH\")" 342 | }, 343 | "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." 344 | } 345 | -------------------------------------------------------------------------------- /.projenrc.js: -------------------------------------------------------------------------------- 1 | const { awscdk } = require('projen'); 2 | 3 | const PROJECT_NAME = 'cdk-gitlab-runner'; 4 | const PROJECT_DESCRIPTION = 'Use AWS CDK to create a gitlab runner, and use gitlab runner to help you execute your Gitlab pipeline job.'; 5 | 6 | const project = new awscdk.AwsCdkConstructLibrary({ 7 | name: PROJECT_NAME, 8 | description: PROJECT_DESCRIPTION, 9 | repository: 'https://github.com/neilkuan/cdk-gitlab-runner.git', 10 | authorName: 'Neil Kuan', 11 | authorEmail: 'guan840912@gmail.com', 12 | keywords: ['aws', 'gitlab', 'runner'], 13 | defaultReleaseBranch: 'master', 14 | catalog: { 15 | twitter: 'neil_kuan', 16 | announce: false, 17 | }, 18 | compat: true, 19 | stability: 'experimental', 20 | cdkVersion: '2.189.1', 21 | /** 22 | * we default release the main branch(cdkv2) with major version 2. 23 | */ 24 | majorVersion: 2, 25 | defaultReleaseBranch: 'master', 26 | /** 27 | * we also release the cdkv1 branch with major version 1. 28 | */ 29 | releaseBranches: { 30 | cdkv1: { npmDistTag: 'cdkv1', majorVersion: 1 }, 31 | }, 32 | autoDetectBin: false, 33 | depsUpgradeOptions: { 34 | ignoreProjen: false, 35 | workflowOptions: { 36 | labels: ['auto-approve', 'auto-merge'], 37 | }, 38 | }, 39 | autoApproveOptions: { 40 | secret: 'GITHUB_TOKEN', 41 | allowedUsernames: ['neilkuan'], 42 | }, 43 | publishToPypi: { 44 | distName: 'cdk-gitlab-runner', 45 | module: 'cdk_gitlab_runner', 46 | }, 47 | workflowNodeVersion: '^16.16.0', 48 | typescriptVersion: '^4.9', 49 | jsiiVersion: '~5.0.7', 50 | deps: [ 51 | 'compare-versions', 52 | ], 53 | bundledDeps: ['compare-versions'], 54 | minNodeVersion: '20.10.0', 55 | workflowNodeVersion: '20.10.0', 56 | typescriptVersion: '^5', 57 | jsiiVersion: '5.7.x', 58 | }); 59 | 60 | const common_exclude = ['cdk.out', 'cdk.context.json', 'yarn-error.log', 'coverage', 'venv']; 61 | project.gitignore.exclude(...common_exclude); 62 | 63 | project.npmignore.exclude(...common_exclude, 'image'); 64 | 65 | project.package.addDevDeps(...['jest@^29', '@types/jest@^29', 'ts-jest@^29', 'jsii-rosetta@5.0.x', 'eslint@^8']); 66 | project.synth(); 67 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [1.94.19](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.94.18...v1.94.19) (2021-04-21) 6 | 7 | ### [1.94.18](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.94.17...v1.94.18) (2021-04-20) 8 | 9 | ### [1.94.17](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.94.16...v1.94.17) (2021-04-19) 10 | 11 | ### [1.94.16](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.94.15...v1.94.16) (2021-04-18) 12 | 13 | ### [1.94.15](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.94.14...v1.94.15) (2021-04-17) 14 | 15 | ### [1.94.14](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.94.13...v1.94.14) (2021-04-16) 16 | 17 | ### [1.94.13](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.94.12...v1.94.13) (2021-04-15) 18 | 19 | ### [1.94.12](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.94.11...v1.94.12) (2021-04-14) 20 | 21 | ### [1.94.11](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.94.10...v1.94.11) (2021-04-13) 22 | 23 | ### [1.94.10](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.94.9...v1.94.10) (2021-04-12) 24 | 25 | ### [1.94.9](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.94.8...v1.94.9) (2021-04-11) 26 | 27 | ### [1.94.8](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.94.7...v1.94.8) (2021-04-10) 28 | 29 | ### [1.94.7](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.94.6...v1.94.7) (2021-04-09) 30 | 31 | ### [1.94.6](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.94.5...v1.94.6) (2021-04-08) 32 | 33 | ### [1.94.5](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.94.4...v1.94.5) (2021-04-07) 34 | 35 | ### [1.94.4](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.94.3...v1.94.4) (2021-04-06) 36 | 37 | ### [1.94.3](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.94.2...v1.94.3) (2021-04-05) 38 | 39 | ### [1.94.2](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.94.1...v1.94.2) (2021-03-29) 40 | 41 | ### [1.94.1](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.90.20...v1.94.1) (2021-03-21) 42 | 43 | ### [1.90.20](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.90.19...v1.90.20) (2021-03-17) 44 | 45 | ### [1.90.19](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.90.18...v1.90.19) (2021-03-16) 46 | 47 | ### [1.90.18](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.90.17...v1.90.18) (2021-03-09) 48 | 49 | ### [1.90.17](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.90.16...v1.90.17) (2021-03-08) 50 | 51 | ### [1.90.16](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.90.15...v1.90.16) (2021-03-07) 52 | 53 | ### [1.90.15](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.90.14...v1.90.15) (2021-03-06) 54 | 55 | ### [1.90.14](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.90.13...v1.90.14) (2021-03-05) 56 | 57 | ### [1.90.13](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.90.12...v1.90.13) (2021-03-04) 58 | 59 | ### [1.90.12](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.90.11...v1.90.12) (2021-03-03) 60 | 61 | ### [1.90.11](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.90.10...v1.90.11) (2021-03-02) 62 | 63 | ### [1.90.10](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.90.9...v1.90.10) (2021-03-01) 64 | 65 | ### [1.90.9](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.90.8...v1.90.9) (2021-02-28) 66 | 67 | ### [1.90.8](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.90.7...v1.90.8) (2021-02-27) 68 | 69 | ### [1.90.7](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.90.6...v1.90.7) (2021-02-26) 70 | 71 | ### [1.90.6](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.90.5...v1.90.6) (2021-02-25) 72 | 73 | ### [1.90.5](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.90.4...v1.90.5) (2021-02-24) 74 | 75 | ### [1.90.4](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.90.3...v1.90.4) (2021-02-23) 76 | 77 | ### [1.90.3](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.90.2...v1.90.3) (2021-02-22) 78 | 79 | 80 | ### Bug Fixes 81 | 82 | * **core:** remove unused git and use amazon-linux-extras to install docker ([675e0bd](https://github.com/guan840912/cdk-gitlab-runner/commit/675e0bd01150c1e87dc86553f1ed1cf5373f96f6)) 83 | 84 | ### [1.90.2](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.90.1...v1.90.2) (2021-02-21) 85 | 86 | ### [1.90.1](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.90.0...v1.90.1) (2021-02-20) 87 | 88 | ## [1.90.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.30...v1.90.0) (2021-02-19) 89 | 90 | ### [1.77.30](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.29...v1.77.30) (2021-02-18) 91 | 92 | ### [1.77.29](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.28...v1.77.29) (2021-02-17) 93 | 94 | ### [1.77.28](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.27...v1.77.28) (2021-02-16) 95 | 96 | ### [1.77.27](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.26...v1.77.27) (2021-02-15) 97 | 98 | ### [1.77.26](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.25...v1.77.26) (2021-02-14) 99 | 100 | ### [1.77.25](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.24...v1.77.25) (2021-02-13) 101 | 102 | ### [1.77.24](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.23...v1.77.24) (2021-02-12) 103 | 104 | ### [1.77.23](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.22...v1.77.23) (2021-02-11) 105 | 106 | ### [1.77.22](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.21...v1.77.22) (2021-02-10) 107 | 108 | ### [1.77.21](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.20...v1.77.21) (2021-02-09) 109 | 110 | ### [1.77.20](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.19...v1.77.20) (2021-02-08) 111 | 112 | ### [1.77.19](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.18...v1.77.19) (2021-02-07) 113 | 114 | ### [1.77.18](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.17...v1.77.18) (2021-02-06) 115 | 116 | ### [1.77.17](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.16...v1.77.17) (2021-02-05) 117 | 118 | ### [1.77.16](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.15...v1.77.16) (2021-02-04) 119 | 120 | ### [1.77.15](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.14...v1.77.15) (2021-02-02) 121 | 122 | ### [1.77.14](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.13...v1.77.14) (2021-02-01) 123 | 124 | ### [1.77.13](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.12...v1.77.13) (2021-01-31) 125 | 126 | ### [1.77.12](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.11...v1.77.12) (2021-01-30) 127 | 128 | ### [1.77.11](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.10...v1.77.11) (2021-01-18) 129 | 130 | ### [1.77.10](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.9...v1.77.10) (2021-01-16) 131 | 132 | ### [1.77.9](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.8...v1.77.9) (2021-01-15) 133 | 134 | ### [1.77.8](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.7...v1.77.8) (2021-01-14) 135 | 136 | ### [1.77.7](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.6...v1.77.7) (2021-01-14) 137 | 138 | ### [1.77.6](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.5...v1.77.6) (2021-01-13) 139 | 140 | ### [1.77.5](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.4...v1.77.5) (2021-01-12) 141 | 142 | ### [1.77.4](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.3...v1.77.4) (2021-01-11) 143 | 144 | ### [1.77.3](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.2...v1.77.3) (2021-01-10) 145 | 146 | ### [1.77.2](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.1...v1.77.2) (2021-01-09) 147 | 148 | ### [1.77.1](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.77.0...v1.77.1) (2021-01-08) 149 | 150 | ## [1.77.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.76.30...v1.77.0) (2021-01-07) 151 | 152 | 153 | ### Features 154 | 155 | * Add alarm on high disk usage ([01a2f6a](https://github.com/guan840912/cdk-gitlab-runner/commit/01a2f6a8c19e7761b5291cebf6d764e33394148b)) 156 | 157 | ### [1.76.30](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.76.29...v1.76.30) (2021-01-07) 158 | 159 | ### [1.76.29](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.76.28...v1.76.29) (2021-01-06) 160 | 161 | ### [1.76.28](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.76.27...v1.76.28) (2021-01-05) 162 | 163 | ### [1.76.27](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.76.26...v1.76.27) (2021-01-04) 164 | 165 | ### [1.76.26](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.76.25...v1.76.26) (2021-01-03) 166 | 167 | ### [1.76.25](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.76.24...v1.76.25) (2021-01-03) 168 | 169 | ### [1.76.24](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.76.23...v1.76.24) (2021-01-02) 170 | 171 | ### [1.76.23](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.76.22...v1.76.23) (2021-01-02) 172 | 173 | ### [1.76.22](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.76.21...v1.76.22) (2021-01-01) 174 | 175 | ### [1.76.21](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.76.20...v1.76.21) (2020-12-31) 176 | 177 | ### [1.76.20](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.76.19...v1.76.20) (2020-12-30) 178 | 179 | ### [1.76.19](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.76.18...v1.76.19) (2020-12-29) 180 | 181 | ### [1.76.18](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.76.17...v1.76.18) (2020-12-28) 182 | 183 | ### [1.76.17](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.76.16...v1.76.17) (2020-12-27) 184 | 185 | ### [1.76.16](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.76.15...v1.76.16) (2020-12-26) 186 | 187 | ### 1.76.15 (2020-12-21) 188 | 189 | ### 1.76.14 (2020-12-20) 190 | 191 | ### 1.76.13 (2020-12-19) 192 | 193 | ### 1.76.12 (2020-12-18) 194 | 195 | ### [1.76.11](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.76.10...v1.76.11) (2020-12-17) 196 | 197 | ### 1.76.10 (2020-12-13) 198 | 199 | ### 1.76.9 (2020-12-12) 200 | 201 | ### 1.76.8 (2020-12-09) 202 | 203 | ### 1.76.7 (2020-12-05) 204 | 205 | ### [1.76.6](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.76.5...v1.76.6) (2020-12-04) 206 | 207 | ### 1.76.5 (2020-12-02) 208 | 209 | ### [1.76.4](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.76.3...v1.76.4) (2020-12-01) 210 | 211 | ### 1.76.3 (2020-12-01) 212 | 213 | ### [1.76.2](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.76.1...v1.76.2) (2020-11-30) 214 | 215 | ### 1.76.1 (2020-11-30) 216 | 217 | ## [1.76.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.75.17...v1.76.0) (2020-11-30) 218 | 219 | 220 | ### Features 221 | 222 | * use launch template replace launchConfigure ([b1028aa](https://github.com/guan840912/cdk-gitlab-runner/commit/b1028aa04ac1de66cb01d90ceb4b13d2f2d640aa)) 223 | * use launch template replace launchConfigure ([f1ecf36](https://github.com/guan840912/cdk-gitlab-runner/commit/f1ecf3662d0edca10306a2f188f1f58dd1b88b32)) 224 | 225 | ### 1.75.17 (2020-11-30) 226 | 227 | ### 1.75.16 (2020-11-30) 228 | 229 | ### 1.75.15 (2020-11-29) 230 | 231 | ### 1.75.14 (2020-11-28) 232 | 233 | ### 1.75.13 (2020-11-28) 234 | 235 | ### 1.75.12 (2020-11-27) 236 | 237 | ### [1.75.11](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.75.10...v1.75.11) (2020-11-26) 238 | 239 | 240 | ### Bug Fixes 241 | 242 | * projen version issue ([c258aca](https://github.com/guan840912/cdk-gitlab-runner/commit/c258aca9d5acae99b6ab2ceedaf5f60258b16814)) 243 | * projen version issue ([85442db](https://github.com/guan840912/cdk-gitlab-runner/commit/85442db0c22ca3bd1239786a848256e6ab6f08f6)) 244 | 245 | ### 1.75.10 (2020-11-25) 246 | 247 | ### 1.75.9 (2020-11-24) 248 | 249 | ### [1.75.8](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.75.7...v1.75.8) (2020-11-24) 250 | 251 | 252 | ### Bug Fixes 253 | 254 | * gitlab-instance.ts typo ([9eb5a9d](https://github.com/guan840912/cdk-gitlab-runner/commit/9eb5a9ddc7459c8b3ee4a974068261e2f5e3c479)) 255 | * gitlab-instance.ts typo ([a81eee0](https://github.com/guan840912/cdk-gitlab-runner/commit/a81eee0519e8c0b1f39511c0b39b1f64c8759e1c)) 256 | 257 | ### [1.75.7](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.75.6...v1.75.7) (2020-11-24) 258 | 259 | ### 1.75.6 (2020-11-24) 260 | 261 | ### 1.75.5 (2020-11-23) 262 | 263 | ### 1.75.4 (2020-11-22) 264 | 265 | ### [1.75.3](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.75.2...v1.75.3) (2020-11-21) 266 | 267 | ### [1.75.2](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.75.1...v1.75.2) (2020-11-19) 268 | 269 | 270 | ### Bug Fixes 271 | 272 | * bucket dependency issue ([30e756c](https://github.com/guan840912/cdk-gitlab-runner/commit/30e756ce855c9cbbbd538473253256ae27d0863d)) 273 | 274 | ### [1.75.1](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.75.0...v1.75.1) (2020-11-19) 275 | 276 | 277 | ### Features 278 | 279 | * support unregister runner when destroy cdkapp ([16333dc](https://github.com/guan840912/cdk-gitlab-runner/commit/16333dca882bf3d98e65e62cd469227d6341ecca)) 280 | 281 | ## [1.75.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.74.0...v1.75.0) (2020-11-18) 282 | 283 | 284 | ### Features 285 | 286 | * support add another bind volumes ([8bbdb13](https://github.com/guan840912/cdk-gitlab-runner/commit/8bbdb13951276bc09d605d4ff3249992354ff0c2)) 287 | 288 | ## [1.74.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.73.0...v1.74.0) (2020-11-18) 289 | 290 | ## [1.73.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.72.11...v1.73.0) (2020-11-18) 291 | 292 | ### 1.72.11 (2020-11-18) 293 | 294 | ### 1.72.10 (2020-11-17) 295 | 296 | ### 1.72.9 (2020-11-16) 297 | 298 | ### 1.72.8 (2020-11-15) 299 | 300 | ### 1.72.7 (2020-11-14) 301 | 302 | ### 1.72.6 (2020-11-13) 303 | 304 | ### 1.72.5 (2020-11-12) 305 | 306 | ### 1.72.4 (2020-11-12) 307 | 308 | ### 1.72.3 (2020-11-11) 309 | 310 | ### 1.72.2 (2020-11-10) 311 | 312 | ### [1.72.1](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.72.0...v1.72.1) (2020-11-10) 313 | 314 | ## [1.72.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.71.14...v1.72.0) (2020-11-09) 315 | 316 | 317 | ### Features 318 | 319 | * add tags function ([20013a0](https://github.com/guan840912/cdk-gitlab-runner/commit/20013a08a65f97b327e75dd516c4337e05188c2e)) 320 | 321 | ### 1.71.14 (2020-11-09) 322 | 323 | ### 1.71.13 (2020-11-09) 324 | 325 | ### 1.71.12 (2020-11-08) 326 | 327 | ### 1.71.11 (2020-11-08) 328 | 329 | ### 1.71.10 (2020-11-07) 330 | 331 | ### 1.71.9 (2020-11-06) 332 | 333 | ### 1.71.8 (2020-11-05) 334 | 335 | ### 1.71.7 (2020-11-04) 336 | 337 | ### 1.71.6 (2020-11-03) 338 | 339 | ### 1.71.5 (2020-11-02) 340 | 341 | ### 1.71.4 (2020-11-01) 342 | 343 | ### [1.71.3](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.71.2...v1.71.3) (2020-10-31) 344 | 345 | 346 | ### Bug Fixes 347 | 348 | * **projen:** fix projen config & update readme ([b1f0a6f](https://github.com/guan840912/cdk-gitlab-runner/commit/b1f0a6f4d3a02737aa0fa6227dca7f197eb19fa3)) 349 | 350 | ### 1.71.2 (2020-10-31) 351 | 352 | ### 1.71.1 (2020-10-30) 353 | 354 | ## [1.71.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.70.0...v1.71.0) (2020-10-30) 355 | 356 | ## [1.70.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.69.23...v1.70.0) (2020-10-30) 357 | 358 | ### 1.69.23 (2020-10-30) 359 | 360 | ### 1.69.22 (2020-10-29) 361 | 362 | ### 1.69.21 (2020-10-29) 363 | 364 | ### 1.69.20 (2020-10-28) 365 | 366 | ### 1.69.19 (2020-10-28) 367 | 368 | ### 1.69.18 (2020-10-27) 369 | 370 | ### 1.69.17 (2020-10-27) 371 | 372 | ### 1.69.16 (2020-10-26) 373 | 374 | ### 1.69.15 (2020-10-25) 375 | 376 | ### 1.69.14 (2020-10-24) 377 | 378 | ### [1.69.13](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.69.12...v1.69.13) (2020-10-24) 379 | 380 | ### 1.69.12 (2020-10-23) 381 | 382 | ### 1.69.11 (2020-10-23) 383 | 384 | ### 1.69.10 (2020-10-22) 385 | 386 | ### 1.69.9 (2020-10-22) 387 | 388 | ### 1.69.8 (2020-10-22) 389 | 390 | ### 1.69.7 (2020-10-21) 391 | 392 | ### 1.69.6 (2020-10-21) 393 | 394 | ### 1.69.5 (2020-10-21) 395 | 396 | ### 1.69.4 (2020-10-20) 397 | 398 | ### 1.69.3 (2020-10-20) 399 | 400 | ### 1.69.2 (2020-10-20) 401 | 402 | ### 1.69.1 (2020-10-20) 403 | 404 | ## [1.69.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.68.5...v1.69.0) (2020-10-20) 405 | 406 | ### 1.68.5 (2020-10-19) 407 | 408 | ### 1.68.4 (2020-10-19) 409 | 410 | ### 1.68.3 (2020-10-18) 411 | 412 | ### 1.68.2 (2020-10-17) 413 | 414 | ### 1.68.1 (2020-10-16) 415 | 416 | ## [1.68.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.67.10...v1.68.0) (2020-10-16) 417 | 418 | ### 1.67.10 (2020-10-15) 419 | 420 | ### 1.67.9 (2020-10-14) 421 | 422 | ### 1.67.8 (2020-10-14) 423 | 424 | ### 1.67.7 (2020-10-13) 425 | 426 | ### 1.67.6 (2020-10-13) 427 | 428 | ### 1.67.5 (2020-10-12) 429 | 430 | ### 1.67.4 (2020-10-12) 431 | 432 | ### 1.67.3 (2020-10-11) 433 | 434 | ### 1.67.2 (2020-10-10) 435 | 436 | ### 1.67.1 (2020-10-09) 437 | 438 | ## [1.67.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.66.6...v1.67.0) (2020-10-08) 439 | 440 | ### 1.66.6 (2020-10-08) 441 | 442 | ### 1.66.5 (2020-10-07) 443 | 444 | ### 1.66.4 (2020-10-06) 445 | 446 | ### 1.66.3 (2020-10-06) 447 | 448 | ### [1.66.2](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.66.0...v1.66.2) (2020-10-06) 449 | 450 | ### 1.66.1 (2020-10-05) 451 | 452 | ## [1.66.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.65.1...v1.66.0) (2020-10-03) 453 | 454 | ### 1.65.1 (2020-10-02) 455 | 456 | ## [1.65.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.64.0...v1.65.0) (2020-10-01) 457 | 458 | ### 1.64.1 (2020-10-01) 459 | 460 | ## [1.64.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.63.0...v1.64.0) (2020-09-25) 461 | 462 | ## [1.63.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.62.0...v1.63.0) (2020-09-14) 463 | 464 | ## [1.62.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.61.6...v1.62.0) (2020-09-05) 465 | 466 | ### 1.61.6 (2020-09-03) 467 | 468 | ### 1.61.5 (2020-09-01) 469 | 470 | ### [1.61.4](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.61.3...v1.61.4) (2020-09-01) 471 | 472 | ### [1.61.3](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.61.2...v1.61.3) (2020-08-29) 473 | 474 | ### [1.61.3](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.61.2...v1.61.3) (2020-08-29) 475 | 476 | ### [1.61.3](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.61.2...v1.61.3) (2020-08-29) 477 | 478 | ### [1.61.2](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.61.1...v1.61.2) (2020-08-27) 479 | 480 | ### [1.61.1](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.61.0...v1.61.1) (2020-08-27) 481 | 482 | ## [1.61.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.60.3...v1.61.0) (2020-08-27) 483 | 484 | ### [1.60.3](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.60.2...v1.60.3) (2020-08-27) 485 | 486 | ### [1.60.2](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.60.1...v1.60.2) (2020-08-27) 487 | 488 | ### [1.60.1](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.60.0...v1.60.1) (2020-08-22) 489 | 490 | ## [1.60.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.59.0...v1.60.0) (2020-08-21) 491 | 492 | ## [1.59.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.58.0...v1.59.0) (2020-08-15) 493 | 494 | ## [1.58.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.57.0...v1.58.0) (2020-08-15) 495 | 496 | ## [1.57.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.56.0...v1.57.0) (2020-08-10) 497 | 498 | ## [1.56.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.55.0...v1.56.0) (2020-08-02) 499 | 500 | ## [1.55.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.54.0...v1.55.0) (2020-07-29) 501 | 502 | ## [1.54.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.53.0...v1.54.0) (2020-07-29) 503 | 504 | ## [1.53.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.52.0...v1.53.0) (2020-07-21) 505 | 506 | ## [1.52.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.51.0...v1.52.0) (2020-07-19) 507 | 508 | ### [1.51.1](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.51.0...v1.51.1) (2020-07-19) 509 | 510 | ## [1.51.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.49.1...v1.51.0) (2020-07-13) 511 | 512 | ### [1.49.2](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.49.1...v1.49.2) (2020-07-13) 513 | 514 | ### [1.49.1](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.47.2...v1.49.1) (2020-07-04) 515 | 516 | ### [1.47.2](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.47.1...v1.47.2) (2020-07-04) 517 | 518 | ### [1.47.1](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.47.0...v1.47.1) (2020-06-27) 519 | 520 | ## [1.47.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.46.2...v1.47.0) (2020-06-27) 521 | 522 | ### [1.46.2](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.46.1...v1.46.2) (2020-06-23) 523 | 524 | ### [1.46.1](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.46.0...v1.46.1) (2020-06-22) 525 | 526 | ## [1.46.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.0.3...v1.46.0) (2020-06-22) 527 | 528 | ### [1.0.3](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.0.2...v1.0.3) (2020-06-22) 529 | 530 | ### [1.0.2](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.0.1...v1.0.2) (2020-06-21) 531 | 532 | ### [1.0.1](https://github.com/guan840912/cdk-gitlab-runner/compare/v1.0.0...v1.0.1) (2020-06-15) 533 | 534 | ## [1.0.0](https://github.com/guan840912/cdk-gitlab-runner/compare/v0.0.8...v1.0.0) (2020-06-12) 535 | 536 | ### [0.0.8](https://github.com/guan840912/cdk-gitlab-runner/compare/v0.0.7...v0.0.8) (2020-06-12) 537 | 538 | ### [0.0.7](https://github.com/guan840912/cdk-gitlab-runner/compare/v0.0.6...v0.0.7) (2020-06-11) 539 | 540 | ### [0.0.6](https://github.com/guan840912/cdk-gitlab-runner/compare/v0.0.5...v0.0.6) (2020-06-11) 541 | 542 | ### [0.0.1](https://github.com/guan840912/cdk-gitlab-runner/compare/v0.0.5...v0.0.1) (2020-06-11) 543 | 544 | ### [0.0.5](https://github.com/guan840912/cdk-gitlab-runner/compare/v0.0.4...v0.0.5) (2020-06-11) 545 | 546 | ### [0.0.4](https://github.com/guan840912/cdk-gitlab-runner/compare/v0.0.3...v0.0.4) (2020-06-11) 547 | 548 | ### [0.0.3](https://github.com/guan840912/cdk-gitlab-runner/compare/v0.0.2...v0.0.3) (2020-06-11) 549 | 550 | ### [0.0.2](https://github.com/guan840912/cdk-gitlab-runner/compare/v0.0.1...v0.0.2) (2020-06-11) 551 | 552 | ### 0.0.1 (2020-06-11) 553 | -------------------------------------------------------------------------------- /JSIIREADME.md: -------------------------------------------------------------------------------- 1 | # Welcome to `awscdk-jsii-template` 2 | 3 | This repository template helps you generate JSII construct library for AWS CDK. 4 | 5 | 6 | ## Confiuguration 7 | 8 | 1. customize your `.projenrc.js` 9 | 1. run `npx projen` to generate the `package.json` and `.github/workflows` from `.projenrc.js` 10 | 2. `yarn install` to install all required npm packages 11 | 12 | 13 | ## Integration tests 14 | 15 | 1. run `yarn watch` in a seperate terminal 16 | 2. edit `test/integ.api.ts` 17 | 3. `cdk diff` and `cdk deploy` 18 | 19 | ```bash 20 | cdk --app 'test/integ.api.js' diff 21 | cdk --app 'test/integ.api.js' deploy 22 | ``` 23 | 24 | 4. validate the stack 25 | 26 | ## Unit tests 27 | 28 | 1. edit `test/*.test.ts` 29 | 2. run `yarn test` 30 | 31 | 32 | ## Usage 33 | 34 | | Command | Description | 35 | |------------------|---------------------------------------------------| 36 | |`yarn install` |Install dependencies | 37 | |`yarn compile` |Compile to JavaScript | 38 | |`yarn watch` |Watch for changes and compile | 39 | |`yarn test` |Run tests | 40 | |`yarn run package`|Create `dist` with bundles for all languages | 41 | |`yarn build` |Compile + test + package | 42 | |`yarn bump` |Bump a new version (based on conventional commits) | 43 | |`yarn compat` |Run API compatibility check against latest | 44 | 45 | ## GitHub Workflows 46 | 47 | - [Build](./.github/workflows/build.yml): when a PR is created/updated, runs `yarn build` 48 | - [Release](./.github/workflows/release.yml): `yarn build` and publish to all package managers for every commit to `master` (ignore if current version is already released). 49 | 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![NPM version](https://badge.fury.io/js/cdk-gitlab-runner.svg)](https://badge.fury.io/js/cdk-gitlab-runner) 2 | [![PyPI version](https://badge.fury.io/py/cdk-gitlab-runner.svg)](https://badge.fury.io/py/cdk-gitlab-runner) 3 | [![release](https://github.com/neilkuan/cdk-gitlab-runner/actions/workflows/release.yml/badge.svg)](https://github.com/neilkuan/cdk-gitlab-runner/actions/workflows/release.yml) 4 | 5 | ![Downloads](https://img.shields.io/badge/-DOWNLOADS:-brightgreen?color=gray) 6 | ![npm](https://img.shields.io/npm/dt/cdk-gitlab-runner?label=npm&color=orange) 7 | ![PyPI](https://img.shields.io/pypi/dm/cdk-gitlab-runner?label=pypi&color=blue) 8 | 9 | ![](https://img.shields.io/badge/iam_role_self-enable-green=?style=plastic&logo=appveyor) 10 | ![](https://img.shields.io/badge/vpc_self-enable-green=?style=plastic&logo=appveyor) 11 | ![](https://img.shields.io/badge/gitlab_url-customize-green=?style=plastic&logo=appveyor) 12 | ![](https://img.shields.io/badge/spotfleet-runner-green=?style=plastic&logo=appveyor) 13 | 14 | # Welcome to `cdk-gitlab-runner` 15 | Use AWS CDK to create gitlab runner, and use [gitlab runner](https://gitlab.com/gitlab-org/gitlab-runner) to help you execute your Gitlab Pipeline Job. 16 | > GitLab Runner is the open source project that is used to run your CI/CD jobs and send the results back to GitLab. [(source repo)](https://gitlab.com/gitlab-org/gitlab-runner) 17 | 18 | ## Why 19 | Gitlab provides [400 minutes per month for each free user](https://about.gitlab.com/pricing/), hosted Gitlab Runner to execute your gitlab pipeline job.That's pretty good and users don't need to manage gitlab runner. If it is just a simple ci job for test 400, it may be enough. 20 | But what if you want to deploy to your AWS production environment through pipeline job? 21 | Is there any security consideration for using the hosted gitlab runner?! 22 | 23 | But creating Gitlab Runner is not that simple, so I created this OSS so that you can quickly create Gitlab Runner and delete your Gitlab Runner via AWS CDK. 24 | It will be used with AWS IAM Role, so you don't need to put AKSK in Gitlab environment variables. 25 | 26 | ![](./image/cdk-gitlab-runner.png) 27 | 28 | 29 | ## Note 30 | ### Default will help you generate below services: 31 | 32 | - VPC 33 | - Public Subnet (2) 34 | - EC2 (1 T3.micro) 35 | 36 | ## Before start you need gitlab runner token in your `gitlab project` or `gitlab group` 37 | 38 | ## In Group before Gitlab 15.10 39 | This registration process is only supported in GitLab Runner 15.10 or later 40 | This registration process is not supported in GitLab Runner 15.9 or earlier and only available as an experimental feature in GitLab Runner 15.10 and 15.11. You should upgrade to GitLab Runner 16.0 or later to use a stable version of this registration process. [Check this issue](https://github.com/neilkuan/cdk-gitlab-runner/issues/1796) 41 | 42 | Group > Settings > CI/CD 43 | ![group](image/group_runner_page.png) 44 | 45 | ## In Group after Gitlab 15.10 46 | This registration process is only supported in GitLab Runner 15.10 or later 47 | This registration process is not supported in GitLab Runner 15.9 or earlier and only available as an experimental feature in GitLab Runner 15.10 and 15.11. You should upgrade to GitLab Runner 16.0 or later to use a stable version of this registration process. [Check this issue](https://github.com/neilkuan/cdk-gitlab-runner/issues/1796) 48 | 49 | Group > Build > Runners 50 | ![group](image/gitlab-runner-new-register.png) 51 | ![group](image/gitlab-runner-new-register-1.png) 52 | ![group](image/gitlab-runner-new-register-2.png) 53 | ![group](image/gitlab-runner-new-register-3.jpg) 54 | 55 | 56 | ### In Project before Gitlab 15.10 57 | This registration process is only supported in GitLab Runner 15.10 or later 58 | This registration process is not supported in GitLab Runner 15.9 or earlier and only available as an experimental feature in GitLab Runner 15.10 and 15.11. You should upgrade to GitLab Runner 16.0 or later to use a stable version of this registration process. [Check this issue](https://github.com/neilkuan/cdk-gitlab-runner/issues/1796) 59 | 60 | Project > Settings > CI/CD > Runners 61 | ![project](image/project_runner_page.png) 62 | 63 | ### In Project after Gitlab 15.10 64 | This registration process is only supported in GitLab Runner 15.10 or later 65 | This registration process is not supported in GitLab Runner 15.9 or earlier and only available as an experimental feature in GitLab Runner 15.10 and 15.11. You should upgrade to GitLab Runner 16.0 or later to use a stable version of this registration process. [Check this issue](https://github.com/neilkuan/cdk-gitlab-runner/issues/1796) 66 | 67 | Project > Settings > CI/CD > Runners 68 | ![project](image/gitlab-runner-new-register-project.png) 69 | 70 | ## Usage 71 | 72 | Replace your gitlab runner token in `$GITLABTOKEN` 73 | 74 | ## Install 75 | Use the npm dist tag to opt in CDKv1 or CDKv2: 76 | ```bash 77 | // for CDKv2 78 | npm install cdk-gitlab-runner 79 | or 80 | npm install cdk-gitlab-runner@latest 81 | 82 | // for CDKv1 83 | npm install cdk-gitlab-runner@cdkv1 84 | ``` 85 | 86 | ## 💡💡💡 please click [here](https://github.com/neilkuan/cdk-gitlab-runner/tree/cdkv1#readme), if you are using aws-cdk v1.x.x version.💡💡💡 87 | 88 | ### Instance Type 89 | 90 | ```typescript 91 | import { GitlabContainerRunner } from 'cdk-gitlab-runner'; 92 | 93 | // If want change instance type to t3.large . 94 | new GitlabContainerRunner(this, 'runner-instance', { gitlabtoken: 'glrt-GITLABTOKEN', ec2type:'t3.large',gitlabRunnerVersion: '15.10' }); 95 | // OR 96 | // Just create a gitlab runner , by default instance type is t3.micro . 97 | import { GitlabContainerRunner } from 'cdk-gitlab-runner'; 98 | 99 | new GitlabContainerRunner(this, 'runner-instance', { gitlabtoken: 'glrt-GITLABTOKEN', gitlabRunnerVersion: '15.10' }); 100 | ``` 101 | 102 | ### Gitlab Server Customize Url . 103 | 104 | If you want change what you want tag name . 105 | 106 | ```typescript 107 | // If you want change what your self Gitlab Server Url . 108 | import { GitlabContainerRunner } from 'cdk-gitlab-runner'; 109 | 110 | new GitlabContainerRunner(this, 'runner-instance-change-tag', { 111 | gitlabtoken: 'glrt-GITLABTOKEN', 112 | gitlaburl: 'https://gitlab.my.com/', 113 | gitlabRunnerVersion: '15.10' 114 | }); 115 | ``` 116 | 117 | ### Tags 118 | 119 | If you want change what you want tag name . 120 | !!! Not support Gitlab Runner 15.10 and later !!! 121 | ```typescript 122 | // If you want change what you want tag name . 123 | import { GitlabContainerRunner } from 'cdk-gitlab-runner'; 124 | 125 | new GitlabContainerRunner(this, 'runner-instance-change-tag', { 126 | gitlabtoken: 'glrt-GITLABTOKEN', 127 | gitlabRunnerVersion: '15.10', 128 | tags: ['aa', 'bb', 'cc'], 129 | }); 130 | ``` 131 | 132 | ### IAM Policy 133 | 134 | If you want add runner other IAM Policy like s3-readonly-access. 135 | 136 | ```typescript 137 | // If you want add runner other IAM Policy like s3-readonly-access. 138 | import { GitlabContainerRunner } from 'cdk-gitlab-runner'; 139 | import { ManagedPolicy } from 'aws-cdk-lib/aws-iam'; 140 | 141 | const runner = new GitlabContainerRunner(this, 'runner-instance-add-policy', { 142 | gitlabtoken: 'glrt-GITLABTOKEN', 143 | gitlabRunnerVersion: '15.10', 144 | tags: ['aa', 'bb', 'cc'], 145 | }); 146 | runner.runnerRole.addManagedPolicy( 147 | ManagedPolicy.fromAwsManagedPolicyName('AmazonS3ReadOnlyAccess'), 148 | ); 149 | ``` 150 | 151 | ### Security Group 152 | 153 | If you want add runner other SG Ingress . 154 | 155 | ```typescript 156 | // If you want add runner other SG Ingress . 157 | import { GitlabContainerRunner } from 'cdk-gitlab-runner'; 158 | import { Port, Peer } from 'aws-cdk-lib/aws-ec2'; 159 | 160 | const runner = new GitlabContainerRunner(this, 'runner-add-SG-ingress', { 161 | gitlabtoken: 'glrt-GITLABTOKEN', 162 | gitlabRunnerVersion: '15.10', 163 | tags: ['aa', 'bb', 'cc'], 164 | }); 165 | 166 | // you can add ingress in your runner SG . 167 | runner.defaultRunnerSG.connections.allowFrom( 168 | Peer.ipv4('0.0.0.0/0'), 169 | Port.tcp(80), 170 | ); 171 | ``` 172 | 173 | ### Use self VPC 174 | 175 | > 2020/06/27 , you can use your self exist VPC or new VPC , but please check your `vpc public Subnet` Auto-assign public IPv4 address must be Yes ,or `vpc private Subnet` route table associated `nat gateway` . 176 | 177 | ```typescript 178 | import { GitlabContainerRunner } from 'cdk-gitlab-runner'; 179 | import { Port, Peer, Vpc, SubnetType } from 'aws-cdk-lib/aws-ec2'; 180 | import { ManagedPolicy } from 'aws-cdk-lib/aws-iam'; 181 | 182 | const newvpc = new Vpc(stack, 'VPC', { 183 | ipAddresses: IpAddresses.cidr('10.0.0.0/16'), 184 | maxAzs: 2, 185 | subnetConfiguration: [ 186 | { 187 | cidrMask: 26, 188 | name: 'RunnerVPC', 189 | subnetType: SubnetType.PUBLIC, 190 | }, 191 | ], 192 | natGateways: 0, 193 | }); 194 | 195 | const runner = new GitlabContainerRunner(this, 'testing', { 196 | gitlabtoken: 'glrt-GITLABTOKEN', 197 | gitlabRunnerVersion: '15.10', 198 | ec2type: 't3.small', 199 | selfvpc: newvpc, 200 | }); 201 | ``` 202 | 203 | ### Use your self exist role 204 | 205 | > 2020/06/27 , you can use your self exist role assign to runner 206 | 207 | ```typescript 208 | import { GitlabContainerRunner } from 'cdk-gitlab-runner'; 209 | import { Port, Peer } from 'aws-cdk-lib/aws-ec2'; 210 | import { ManagedPolicy, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam'; 211 | 212 | const role = new Role(this, 'runner-role', { 213 | assumedBy: new ServicePrincipal('ec2.amazonaws.com'), 214 | description: 'For Gitlab EC2 Runner Test Role', 215 | roleName: 'TestRole', 216 | }); 217 | 218 | const runner = new GitlabContainerRunner(stack, 'testing', { 219 | gitlabtoken: 'glrt-GITLABTOKEN', 220 | gitlabRunnerVersion: '15.10', 221 | ec2iamrole: role, 222 | }); 223 | runner.runnerRole.addManagedPolicy( 224 | ManagedPolicy.fromAwsManagedPolicyName('AmazonS3ReadOnlyAccess'), 225 | ); 226 | ``` 227 | 228 | ### Custom Gitlab Runner EBS szie 229 | 230 | > 2025/01/03 , you can change you want ebs size. 231 | 232 | ```typescript 233 | import { GitlabContainerRunner } from 'cdk-gitlab-runner'; 234 | // on-demand instance 235 | new GitlabContainerRunner(stack, 'testing', { 236 | gitlabtoken: 'glrt-GITLABTOKEN', 237 | gitlabRunnerVersion: '15.10', 238 | onDemandEbsConfig: BlockDeviceVolume.ebs(60), 239 | }); 240 | 241 | // spotfleet instance 242 | new GitlabContainerRunner(stack, 'testing', { 243 | gitlabtoken: 'glrt-GITLABTOKEN', 244 | gitlabRunnerVersion: '15.10', 245 | spotFleet: true, 246 | spotEbsConfig: { 247 | volumeSize: 50, 248 | }, 249 | }); 250 | ``` 251 | 252 | ### Control the number of runners with AutoScalingGroup 253 | 254 | > 2020/11/25 , you can set the number of runners. 255 | 256 | ```typescript 257 | import { GitlabRunnerAutoscaling } from 'cdk-gitlab-runner'; 258 | 259 | new GitlabRunnerAutoscaling(stack, 'testing', { 260 | gitlabToken: 'glrt-GITLABTOKEN', 261 | gitlabRunnerVersion: '15.10', 262 | minCapacity: 2, 263 | maxCapacity: 2, 264 | }); 265 | ``` 266 | 267 | ### Support Spotfleet Gitlab Runner 268 | 269 | > 2020/08/27 , you can use spotfleet instance be your gitlab runner, 270 | > after create spotfleet instance will auto output instance id. 271 | 272 | ```typescript 273 | import { GitlabContainerRunner, BlockDuration } from 'cdk-gitlab-runner'; 274 | 275 | const runner = new GitlabContainerRunner(stack, 'testing', { 276 | gitlabToken: 'glrt-GITLABTOKEN', 277 | gitlabRunnerVersion: '15.10', 278 | ec2type: 't3.large', 279 | spotFleet: true, 280 | }); 281 | // configure the expiration after 1 hours 282 | runner.expireAfter(Duration.hours(1)); 283 | ``` 284 | 285 | > 2020/11/19, you setting job runtime bind host volumes. 286 | > see more https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runnersdocker-section 287 | 288 | ```typescript 289 | import { GitlabContainerRunner, BlockDuration } from 'cdk-gitlab-runner'; 290 | 291 | const runner = new GitlabContainerRunner(stack, 'testing', { 292 | gitlabToken: 'glrt-GITLABTOKEN', 293 | gitlabRunnerVersion: '15.10', 294 | ec2type: 't3.large', 295 | dockerVolumes: [ 296 | { 297 | hostPath: '/tmp/cache', 298 | containerPath: '/tmp/cache', 299 | }, 300 | ], 301 | }); 302 | ``` 303 | 304 | ## Wait about 6 mins , If success you will see your runner in that page . 305 | 306 | ![runner](image/group_runner2.png) 307 | 308 | #### you can use tag `gitlab` , `runner` , `awscdk` , 309 | > !!!!! Not Support Gitlab Runner after 15.10 and later 310 | ## Example _`gitlab-ci.yaml`_ 311 | 312 | [gitlab docs see more ...](https://docs.gitlab.com/ee/ci/yaml/README.html) 313 | 314 | ```yaml 315 | dockerjob: 316 | image: docker:18.09-dind 317 | variables: 318 | tags: 319 | - runner 320 | - awscdk 321 | - gitlab 322 | variables: 323 | DOCKER_TLS_CERTDIR: "" 324 | before_script: 325 | - docker info 326 | script: 327 | - docker info; 328 | - echo 'test 123'; 329 | - echo 'hello world 1228' 330 | ``` 331 | 332 | ### If your want to debug you can go to aws console 333 | 334 | # `In your runner region !!!` 335 | 336 | ## AWS Systems Manager > Session Manager > Start a session 337 | 338 | ![system manager](image/session.png) 339 | 340 | #### click your `runner` and click `start session` 341 | 342 | #### in the brower console in put `bash` 343 | 344 | ```bash 345 | # become to root 346 | sudo -i 347 | 348 | # list runner container . 349 | root# docker ps -a 350 | 351 | # modify gitlab-runner/config.toml 352 | 353 | root# cd /home/ec2-user/.gitlab-runner/ && ls 354 | config.toml 355 | ``` 356 | 357 | 358 | ## :clap: Supporters 359 | [![Stargazers repo roster for @neilkuan/cdk-gitlab-runner](https://reporoster.com/stars/neilkuan/cdk-gitlab-runner)](https://github.com/neilkuan/cdk-gitlab-runner/stargazers) 360 | -------------------------------------------------------------------------------- /assets/functions/autoscaling_events.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | import json 4 | 5 | import boto3 6 | 7 | autoscaling = boto3.client('autoscaling') 8 | cloudwatch = boto3.client('cloudwatch') 9 | ssm = boto3.client('ssm') 10 | 11 | logger = logging.getLogger(__name__) 12 | logger.setLevel(logging.INFO) 13 | 14 | 15 | def put_alarms(instance_id, alarms, topic_arns): 16 | logger.info(f'Putting alarms for the runner: {instance_id}') 17 | 18 | alarm_default_args = { 19 | 'Namespace': 'GitlabRunner', 20 | 'AlarmActions': topic_arns, 21 | 'Statistic': 'Average', 22 | 'Dimensions': [ 23 | { 24 | 'Name': 'InstanceId', 25 | 'Value': instance_id 26 | }, 27 | { 28 | 'Name': 'path', 29 | 'Value': '/' 30 | }, 31 | ], 32 | 'Period': 60, 33 | 'Unit': 'Percent', 34 | 'EvaluationPeriods': 1, 35 | 'DatapointsToAlarm': 1, 36 | 'Threshold': 75, 37 | 'ComparisonOperator': 'GreaterThanOrEqualToThreshold', 38 | 'TreatMissingData': 'ignore', 39 | } 40 | 41 | for alarm in alarms: 42 | cloudwatch.put_metric_alarm(**{ 43 | **alarm_default_args, 44 | **alarm, 45 | **{'AlarmName': f'{alarm["AlarmName"]}-{instance_id}'}, 46 | }) 47 | 48 | 49 | def delete_alarms(instance_id, alarms): 50 | logger.info(f'Deleting alarms for the runner: {instance_id}') 51 | 52 | alarm_names = [f'{alarm["AlarmName"]}-{instance_id}' for alarm in alarms] 53 | cloudwatch.delete_alarms(AlarmNames=alarm_names) 54 | 55 | 56 | def send_unregister_command(instance_ids): 57 | logger.info(f'Unregistering gitlab runners: {instance_ids}') 58 | ssm.send_command( 59 | InstanceIds=instance_ids, 60 | DocumentName='AWS-RunShellScript', 61 | Parameters={ 62 | # see - https://docs.gitlab.com/runner/commands/#gitlab-runner-unregister 63 | 'commands': ['docker exec gitlab-runner gitlab-runner unregister --all-runners'] 64 | } 65 | ) 66 | 67 | 68 | def register(event, context): 69 | logger.debug(event) 70 | alarms = json.loads(os.environ['ALARMS']) 71 | topic_arns = os.environ['SNS_TOPIC_ARN'] 72 | 73 | for record in event['Records']: 74 | instance_id = json.loads(record['Sns']['Message'])['EC2InstanceId'] 75 | put_alarms(instance_id, alarms, [topic_arns]) 76 | 77 | 78 | def unregister(event, context): 79 | logger.debug(event) 80 | alarms = json.loads(os.environ['ALARMS']) 81 | 82 | for record in event['Records']: 83 | instance_id = json.loads(record['Sns']['Message'])['EC2InstanceId'] 84 | send_unregister_command([instance_id]) 85 | delete_alarms(instance_id, alarms) 86 | 87 | 88 | def on_alarm(event, context): 89 | logger.debug(event) 90 | 91 | for record in event['Records']: 92 | message = json.loads(record['Sns']['Message']) 93 | for dimension in message['Trigger']['Dimensions']: 94 | if dimension['name'] == 'InstanceId': 95 | instance_id = dimension['value'] 96 | 97 | logger.info(f'Set instance status unhealthy: {instance_id}') 98 | 99 | autoscaling.set_instance_health( 100 | InstanceId=instance_id, 101 | HealthStatus='Unhealthy' 102 | ) 103 | 104 | 105 | def on_delete(event): 106 | logger.debug(event) 107 | alarms = json.loads(os.environ['ALARMS']) 108 | 109 | props = event['ResourceProperties'] 110 | auto_scaling_groups = autoscaling.describe_auto_scaling_groups( 111 | AutoScalingGroupNames=props['AutoScalingGroupNames'] 112 | ) 113 | 114 | logger.debug(auto_scaling_groups) 115 | 116 | for group in auto_scaling_groups['AutoScalingGroups']: 117 | instance_ids = [i['InstanceId'] for i in group['Instances']] 118 | send_unregister_command(instance_ids) 119 | for instance_id in instance_ids: 120 | delete_alarms(instance_id, alarms) 121 | 122 | 123 | def on_event(event, context): 124 | logger.debug(event) 125 | 126 | request_type = event['RequestType'] 127 | if request_type == 'Delete': 128 | return on_delete(event) 129 | -------------------------------------------------------------------------------- /assets/functions/index.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import time 3 | 4 | ec2 = boto3.client('ec2') 5 | 6 | 7 | def on_event(event, context): 8 | print(event) 9 | request_type = event['RequestType'] 10 | if request_type == 'Create': 11 | return on_create(event) 12 | if request_type == 'Update': 13 | return on_update(event) 14 | if request_type == 'Delete': 15 | return on_delete(event) 16 | raise Exception("Invalid request type: %s" % request_type) 17 | 18 | 19 | def on_create(event): 20 | props = event["ResourceProperties"] 21 | spotfleet_request_id = props['SpotFleetRequestId'] 22 | physical_id = 'describeInstances-{}'.format(spotfleet_request_id) 23 | print("create new resource with props %s" % props) 24 | data = {} 25 | while True: 26 | result = ec2.describe_spot_fleet_instances( 27 | SpotFleetRequestId=spotfleet_request_id) 28 | print(result) 29 | if 'ActiveInstances' in result and len(result['ActiveInstances']) > 0: 30 | data = { 31 | 'InstanceId': result['ActiveInstances'][0]['InstanceId'], 32 | 'InstanceType': result['ActiveInstances'][0]['InstanceType'], 33 | 'SpotInstanceRequestId': result['ActiveInstances'][0]['SpotInstanceRequestId'] 34 | } 35 | break 36 | else: 37 | time.sleep(3) 38 | continue 39 | return {'PhysicalResourceId': physical_id, 'Data': data} 40 | 41 | 42 | def on_update(event): 43 | return on_create(event) 44 | 45 | 46 | def on_delete(event): 47 | return 48 | 49 | 50 | def is_complete(event, context): 51 | # physical_id = event["PhysicalResourceId"] 52 | request_type = event["RequestType"] 53 | props = event["ResourceProperties"] 54 | # already returns true on delete 55 | if request_type == 'Delete': 56 | return {'IsComplete': True} 57 | spot_fleet_request_id = props['SpotFleetRequestId'] 58 | result = ec2.describe_spot_fleet_instances( 59 | SpotFleetRequestId=spot_fleet_request_id) 60 | print(result) 61 | is_ready = 'ActiveInstances' in result and len( 62 | result['ActiveInstances']) > 0 63 | return {'IsComplete': is_ready} 64 | -------------------------------------------------------------------------------- /assets/functions/unregister_runner.py: -------------------------------------------------------------------------------- 1 | from cmath import exp 2 | import boto3 3 | import urllib.request 4 | import urllib.parse 5 | from urllib.error import URLError, HTTPError 6 | import logging 7 | import json 8 | 9 | logger = logging.getLogger(__name__) 10 | logger.setLevel(logging.INFO) 11 | 12 | def slash_or_not_slash(url): 13 | try: 14 | aftercut = url.split('/') 15 | if not aftercut[-1]: 16 | print ('url is https://gitlab.com/') 17 | return url 18 | else: 19 | print ('url is https://gitlab.com will add / and then return') 20 | return url +'/' 21 | except: 22 | pass 23 | 24 | def on_event(event, context): 25 | print(event) 26 | request_type = event['RequestType'] 27 | if request_type == 'Create': 28 | return on_create(event) 29 | if request_type == 'Update': 30 | return on_update(event) 31 | if request_type == 'Delete': 32 | return on_delete(event) 33 | raise Exception("Invalid request type: %s" % request_type) 34 | 35 | 36 | def on_create(event): 37 | print("[INFO]", "Create Event") 38 | props = event["ResourceProperties"] 39 | print("update resource with props %s" % (props)) 40 | output = {'Status': 'Created'} 41 | return output 42 | 43 | 44 | def on_update(event): 45 | print("[INFO]", "Update Event") 46 | props = event["ResourceProperties"] 47 | print("update resource with props %s" % (props)) 48 | output = {'Status': 'Updated'} 49 | return output 50 | 51 | 52 | def on_delete(event): 53 | try: 54 | print("[INFO]", "Delete Event") 55 | props = event["ResourceProperties"] 56 | print("update resource with props %s" % (props)) 57 | ssm = boto3.client('ssm') 58 | tokenResp = ssm.get_parameter(Name=props.get('TokenParameterStoreName')) 59 | token = tokenResp.get('Parameter').get('Value') 60 | 61 | prepayload = {'token': str(token)} 62 | print(prepayload) 63 | 64 | print("encode prepayload to payload ") 65 | payload = urllib.parse.urlencode(prepayload).encode('utf8') 66 | print(payload) 67 | req = urllib.request.Request(slash_or_not_slash(props['GitlabUrl'])+'api/v4/runners',data=payload ,method='DELETE' ) 68 | print("run unregister runner") 69 | with urllib.request.urlopen(req) as res: 70 | print (res.read()) 71 | except: 72 | print('run unregister runner Fail!!! Please unregister runner by yourself') 73 | pass 74 | 75 | output = {'Status': 'deleted'} 76 | return output 77 | 78 | 79 | def is_complete(event): 80 | return {'IsComplete': True} 81 | -------------------------------------------------------------------------------- /assets/userdata/amazon-cloudwatch-agent.json: -------------------------------------------------------------------------------- 1 | { 2 | "agent": { 3 | "metrics_collection_interval": 60, 4 | "run_as_user": "root" 5 | }, 6 | "metrics": { 7 | "namespace": "GitlabRunner", 8 | "append_dimensions": { 9 | "AutoScalingGroupName": "${aws:AutoScalingGroupName}", 10 | "ImageId": "${aws:ImageId}", 11 | "InstanceId": "${aws:InstanceId}", 12 | "InstanceType": "${aws:InstanceType}" 13 | }, 14 | "aggregation_dimensions": [ 15 | ["InstanceId", "path"] 16 | ], 17 | "metrics_collected": { 18 | "disk": { 19 | "measurement": [ 20 | "used_percent" 21 | ], 22 | "metrics_collection_interval": 60, 23 | "resources": [ 24 | "*" 25 | ] 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /image/cdk-gitlab-runner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neilkuan/cdk-gitlab-runner/d0c37c38388214527a30d0625b59db2a48ae1a7c/image/cdk-gitlab-runner.png -------------------------------------------------------------------------------- /image/gitlab-runner-new-register-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neilkuan/cdk-gitlab-runner/d0c37c38388214527a30d0625b59db2a48ae1a7c/image/gitlab-runner-new-register-1.png -------------------------------------------------------------------------------- /image/gitlab-runner-new-register-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neilkuan/cdk-gitlab-runner/d0c37c38388214527a30d0625b59db2a48ae1a7c/image/gitlab-runner-new-register-2.png -------------------------------------------------------------------------------- /image/gitlab-runner-new-register-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neilkuan/cdk-gitlab-runner/d0c37c38388214527a30d0625b59db2a48ae1a7c/image/gitlab-runner-new-register-3.jpg -------------------------------------------------------------------------------- /image/gitlab-runner-new-register-project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neilkuan/cdk-gitlab-runner/d0c37c38388214527a30d0625b59db2a48ae1a7c/image/gitlab-runner-new-register-project.png -------------------------------------------------------------------------------- /image/gitlab-runner-new-register.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neilkuan/cdk-gitlab-runner/d0c37c38388214527a30d0625b59db2a48ae1a7c/image/gitlab-runner-new-register.png -------------------------------------------------------------------------------- /image/group_runner2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neilkuan/cdk-gitlab-runner/d0c37c38388214527a30d0625b59db2a48ae1a7c/image/group_runner2.png -------------------------------------------------------------------------------- /image/group_runner_page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neilkuan/cdk-gitlab-runner/d0c37c38388214527a30d0625b59db2a48ae1a7c/image/group_runner_page.png -------------------------------------------------------------------------------- /image/project_runner_page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neilkuan/cdk-gitlab-runner/d0c37c38388214527a30d0625b59db2a48ae1a7c/image/project_runner_page.png -------------------------------------------------------------------------------- /image/session.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neilkuan/cdk-gitlab-runner/d0c37c38388214527a30d0625b59db2a48ae1a7c/image/session.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cdk-gitlab-runner", 3 | "description": "Use AWS CDK to create a gitlab runner, and use gitlab runner to help you execute your Gitlab pipeline job.", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/neilkuan/cdk-gitlab-runner.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:js": "npx projen package:js", 21 | "package:python": "npx projen package:python", 22 | "post-compile": "npx projen post-compile", 23 | "post-upgrade": "npx projen post-upgrade", 24 | "pre-compile": "npx projen pre-compile", 25 | "release": "npx projen release", 26 | "release:cdkv1": "npx projen release:cdkv1", 27 | "test": "npx projen test", 28 | "test:watch": "npx projen test:watch", 29 | "unbump": "npx projen unbump", 30 | "upgrade": "npx projen upgrade", 31 | "watch": "npx projen watch", 32 | "projen": "npx projen" 33 | }, 34 | "author": { 35 | "name": "Neil Kuan", 36 | "email": "guan840912@gmail.com", 37 | "organization": false 38 | }, 39 | "devDependencies": { 40 | "@stylistic/eslint-plugin": "^2", 41 | "@types/jest": "^29", 42 | "@types/node": "^20", 43 | "@typescript-eslint/eslint-plugin": "^8", 44 | "@typescript-eslint/parser": "^8", 45 | "aws-cdk-lib": "2.189.1", 46 | "commit-and-tag-version": "^12", 47 | "constructs": "10.0.5", 48 | "eslint": "^8", 49 | "eslint-import-resolver-typescript": "^2.7.1", 50 | "eslint-plugin-import": "^2.31.0", 51 | "jest": "^29", 52 | "jest-junit": "^16", 53 | "jsii": "5.7.x", 54 | "jsii-diff": "^1.112.0", 55 | "jsii-docgen": "^10.5.0", 56 | "jsii-pacmak": "^1.112.0", 57 | "jsii-rosetta": "5.0.x", 58 | "projen": "^0.92.10", 59 | "ts-jest": "^29", 60 | "typescript": "^5" 61 | }, 62 | "peerDependencies": { 63 | "aws-cdk-lib": "^2.189.1", 64 | "constructs": "^10.0.5" 65 | }, 66 | "dependencies": { 67 | "compare-versions": "^6.1.1" 68 | }, 69 | "bundledDependencies": [ 70 | "compare-versions" 71 | ], 72 | "keywords": [ 73 | "aws", 74 | "cdk", 75 | "gitlab", 76 | "runner" 77 | ], 78 | "engines": { 79 | "node": ">= 20.10.0" 80 | }, 81 | "main": "lib/index.js", 82 | "license": "Apache-2.0", 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 | ], 93 | "clearMocks": true, 94 | "collectCoverage": true, 95 | "coverageReporters": [ 96 | "json", 97 | "lcov", 98 | "clover", 99 | "cobertura", 100 | "text" 101 | ], 102 | "coverageDirectory": "coverage", 103 | "coveragePathIgnorePatterns": [ 104 | "/node_modules/" 105 | ], 106 | "testPathIgnorePatterns": [ 107 | "/node_modules/" 108 | ], 109 | "watchPathIgnorePatterns": [ 110 | "/node_modules/" 111 | ], 112 | "reporters": [ 113 | "default", 114 | [ 115 | "jest-junit", 116 | { 117 | "outputDirectory": "test-reports" 118 | } 119 | ] 120 | ], 121 | "transform": { 122 | "^.+\\.[t]sx?$": [ 123 | "ts-jest", 124 | { 125 | "tsconfig": "tsconfig.dev.json" 126 | } 127 | ] 128 | } 129 | }, 130 | "types": "lib/index.d.ts", 131 | "stability": "experimental", 132 | "jsii": { 133 | "outdir": "dist", 134 | "targets": { 135 | "python": { 136 | "distName": "cdk-gitlab-runner", 137 | "module": "cdk_gitlab_runner" 138 | } 139 | }, 140 | "tsc": { 141 | "outDir": "lib", 142 | "rootDir": "src" 143 | } 144 | }, 145 | "awscdkio": { 146 | "twitter": "neil_kuan", 147 | "announce": false 148 | }, 149 | "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." 150 | } 151 | -------------------------------------------------------------------------------- /src/gitlab-runner-autoscaling.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as cdk from 'aws-cdk-lib'; 3 | import * as asg from 'aws-cdk-lib/aws-autoscaling'; 4 | import { FunctionHook } from 'aws-cdk-lib/aws-autoscaling-hooktargets'; 5 | import * as ec2 from 'aws-cdk-lib/aws-ec2'; 6 | import * as iam from 'aws-cdk-lib/aws-iam'; 7 | import * as lambda from 'aws-cdk-lib/aws-lambda'; 8 | import * as logs from 'aws-cdk-lib/aws-logs'; 9 | import * as assets from 'aws-cdk-lib/aws-s3-assets'; 10 | import * as sns from 'aws-cdk-lib/aws-sns'; 11 | import * as subscriptions from 'aws-cdk-lib/aws-sns-subscriptions'; 12 | import * as cr from 'aws-cdk-lib/custom-resources'; 13 | // eslint-disable-next-line import/no-extraneous-dependencies 14 | import { compare } from 'compare-versions'; 15 | import { Construct } from 'constructs'; 16 | import { DockerVolumes } from './gitlab-runner-interfaces'; 17 | 18 | /** 19 | * GitlabRunnerAutoscaling Props. 20 | */ 21 | export interface GitlabRunnerAutoscalingProps { 22 | /** 23 | * Gitlab Runner version 24 | * Please give me gitlab runner version. 25 | */ 26 | readonly gitlabRunnerVersion: string; 27 | /** 28 | * Gitlab token. 29 | * 30 | * @example 31 | * new GitlabRunnerAutoscaling(stack, 'runner', { gitlabToken: 'GITLAB_TOKEN' }); 32 | */ 33 | readonly gitlabToken: string; 34 | 35 | /** 36 | * Image URL of Gitlab Runner. 37 | * 38 | * @example 39 | * new GitlabRunnerAutoscaling(stack, 'runner', { gitlabToken: 'GITLAB_TOKEN', gitlabRunnerImage: 'gitlab/gitlab-runner:alpine' }); 40 | * 41 | * @default public.ecr.aws/gitlab/gitlab-runner:latest 42 | * 43 | */ 44 | readonly gitlabRunnerImage?: string; 45 | 46 | /** 47 | * Runner default EC2 instance type. 48 | * 49 | * @example 50 | * new GitlabRunnerAutoscaling(stack, 'runner', { gitlabToken: 'GITLAB_TOKEN', instanceType: 't3.small' }); 51 | * 52 | * @default - t3.micro 53 | * 54 | */ 55 | readonly instanceType?: string; 56 | 57 | /** 58 | * VPC for the Gitlab Runner . 59 | * 60 | * @example 61 | * const newVpc = new Vpc(stack, 'NewVPC', { 62 | * ipAddresses: IpAddresses.cidr('10.0.0.0/16'), 63 | * maxAzs: 2, 64 | * subnetConfiguration: [{ 65 | * cidrMask: 26, 66 | * name: 'RunnerVPC', 67 | * subnetType: SubnetType.PUBLIC, 68 | * }], 69 | * natGateways: 0, 70 | * }); 71 | * 72 | * new GitlabRunnerAutoscaling(stack, 'runner', { gitlabToken: 'GITLAB_TOKEN', vpc: newVpc }); 73 | * 74 | * @default - A new VPC will be created. 75 | * 76 | */ 77 | readonly vpc?: ec2.IVpc; 78 | 79 | /** 80 | * IAM role for the Gitlab Runner Instance . 81 | * 82 | * @example 83 | * const role = new Role(stack, 'runner-role', { 84 | * assumedBy: new ServicePrincipal('ec2.amazonaws.com'), 85 | * description: 'For Gitlab Runner Test Role', 86 | * roleName: 'Runner-Role', 87 | * }); 88 | * 89 | * new GitlabRunnerAutoscaling(stack, 'runner', { gitlabToken: 'GITLAB_TOKEN', instanceRole: role }); 90 | * 91 | * @default - new Role for Gitlab Runner Instance , attach AmazonSSMManagedInstanceCore Policy . 92 | * 93 | */ 94 | readonly instanceRole?: iam.IRole; 95 | 96 | /** 97 | * Run worker nodes as EC2 Spot 98 | * 99 | * @default - false 100 | */ 101 | readonly spotInstance?: boolean; 102 | 103 | /** 104 | * Minimum capacity limit for autoscaling group. 105 | * 106 | * @example 107 | * new GitlabRunnerAutoscaling(stack, 'runner', { gitlabToken: 'GITLAB_TOKEN', minCapacity: 2 }); 108 | * 109 | * @default - minCapacity: 1 110 | * 111 | */ 112 | readonly minCapacity?: number; 113 | 114 | /** 115 | * Maximum capacity limit for autoscaling group. 116 | * 117 | * @example 118 | * new GitlabRunnerAutoscaling(stack, 'runner', { gitlabToken: 'GITLAB_TOKEN', maxCapacity: 4 }); 119 | * 120 | * @default - desiredCapacity 121 | * 122 | */ 123 | readonly maxCapacity?: number; 124 | 125 | /** 126 | * Desired capacity limit for autoscaling group. 127 | * 128 | * @example 129 | * new GitlabRunnerAutoscaling(stack, 'runner', { gitlabToken: 'GITLAB_TOKEN', desiredCapacity: 2 }); 130 | * 131 | * @default - minCapacity, and leave unchanged during deployment 132 | * 133 | */ 134 | readonly desiredCapacity?: number; 135 | 136 | /** 137 | * tags for the runner 138 | * 139 | * @default - ['runner', 'gitlab', 'awscdk'] 140 | */ 141 | readonly tags?: string[]; 142 | 143 | /** 144 | * Gitlab Runner register url . 145 | * 146 | * @example 147 | * const runner = new GitlabRunnerAutoscaling(stack, 'runner', { gitlabToken: 'GITLAB_TOKEN',gitlabUrl: 'https://gitlab.com/'}); 148 | * 149 | * @default - https://gitlab.com/ , The trailing slash is mandatory. 150 | * 151 | */ 152 | readonly gitlabUrl?: string; 153 | 154 | /** 155 | * Gitlab Runner instance EBS size . 156 | * 157 | * @deprecated , use ebsConfig 158 | * 159 | */ 160 | readonly ebsSize?: number; 161 | 162 | /** 163 | * Gitlab Runner instance EBS config. 164 | * 165 | * @example 166 | * const runner = new GitlabRunnerAutoscaling(stack, 'runner', { gitlabToken: 'GITLAB_TOKEN', ebsConfig: { volumeSize: 60}}); 167 | * 168 | * @default - ebsConfig={ volumeSize: 60} 169 | * 170 | */ 171 | readonly ebsConfig?: ec2.CfnLaunchTemplate.EbsProperty; 172 | 173 | /** 174 | * VPC subnet 175 | * 176 | * @example 177 | * const vpc = new Vpc(stack, 'nat', { 178 | * natGateways: 1, 179 | * maxAzs: 2, 180 | * }); 181 | * const runner = new GitlabRunnerAutoscaling(stack, 'testing', { 182 | * gitlabToken: 'GITLAB_TOKEN', 183 | * instanceType: 't3.large', 184 | * instanceRole: role, 185 | * vpc: vpc, 186 | * vpcSubnet: { 187 | * subnetType: SubnetType.PUBLIC, 188 | * }, 189 | * }); 190 | * 191 | * @default - SubnetType.PRIVATE subnet 192 | */ 193 | readonly vpcSubnet?: ec2.SubnetSelection; 194 | 195 | /** 196 | * add another Gitlab Container Runner Docker Volumes Path at job runner runtime. 197 | * 198 | * more detail see https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runnersdocker-section 199 | * 200 | * @default - already mount "/var/run/docker.sock:/var/run/docker.sock" 201 | * 202 | * @example 203 | * dockerVolumes: [ 204 | * { 205 | * hostPath: '/tmp/cache', 206 | * containerPath: '/tmp/cache', 207 | * }, 208 | * ], 209 | */ 210 | readonly dockerVolumes?: DockerVolumes[]; 211 | 212 | /** 213 | * Parameters of put_metric_alarm function 214 | * 215 | * https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudwatch.html#CloudWatch.Client.put_metric_alarm 216 | * 217 | * @default - [{ 218 | * AlarmName: 'GitlabRunnerDiskUsage', 219 | * MetricName: 'disk_used_percent', 220 | * }] 221 | * 222 | */ 223 | readonly alarms?: object[]; 224 | } 225 | 226 | /** 227 | * GitlabRunnerAutoscaling Construct for create Autoscaling Gitlab Runner. 228 | */ 229 | export class GitlabRunnerAutoscaling extends Construct { 230 | /** 231 | * The IAM role assumed by the Runner instance. 232 | */ 233 | public readonly instanceRole: iam.IRole; 234 | 235 | /** 236 | * This represents a Runner Auto Scaling Group 237 | */ 238 | public readonly autoscalingGroup: asg.AutoScalingGroup; 239 | 240 | /** 241 | * The EC2 runner's VPC. 242 | */ 243 | public readonly vpc: ec2.IVpc; 244 | 245 | /** 246 | * The EC2 runner's default SecurityGroup. 247 | */ 248 | public readonly securityGroup: ec2.ISecurityGroup; 249 | 250 | /** 251 | * The SNS topic to suscribe alarms for EC2 runner's metrics. 252 | */ 253 | public readonly topicAlarm: sns.ITopic; 254 | 255 | 256 | constructor(scope: Construct, id: string, props: GitlabRunnerAutoscalingProps) { 257 | super(scope, id); 258 | const defaultProps = { 259 | instanceType: 't3.micro', 260 | tags: ['gitlab', 'awscdk', 'runner'], 261 | gitlabUrl: 'https://gitlab.com/', 262 | gitlabRunnerImage: 'public.ecr.aws/gitlab/gitlab-runner:latest', 263 | alarms: [ 264 | { 265 | AlarmName: 'GitlabRunnerDiskUsage', 266 | MetricName: 'disk_used_percent', 267 | }, 268 | ], 269 | }; 270 | const runnerProps = { ...defaultProps, ...props }; 271 | if (compare(props.gitlabRunnerVersion, '15.10', '>=') && props.gitlabToken.includes('glrt-') === false) { 272 | throw new Error('If gitlabRunnerVersion >= 15.10, gitlabtoken please give glrt-xxxxxxx @see https://docs.gitlab.com/ee/ci/runners/new_creation_workflow.html'); 273 | } 274 | const asset = new assets.Asset(this, 'GitlabRunnerUserDataAsset', { 275 | path: path.join(__dirname, '../assets/userdata/amazon-cloudwatch-agent.json'), 276 | }); 277 | 278 | const userData = ec2.UserData.forLinux(); 279 | userData.addS3DownloadCommand({ 280 | bucket: asset.bucket, 281 | bucketKey: asset.s3ObjectKey, 282 | localFile: '/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json', 283 | }); 284 | userData.addCommands(...this.createUserData(runnerProps)); 285 | 286 | this.instanceRole = 287 | runnerProps.instanceRole ?? 288 | new iam.Role(this, 'GitlabRunnerInstanceRole', { 289 | assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'), 290 | description: 'For EC2 Instance (Gitlab Runner) Role', 291 | managedPolicies: [ 292 | iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore'), 293 | iam.ManagedPolicy.fromAwsManagedPolicyName('CloudWatchAgentServerPolicy'), 294 | iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonS3ReadOnlyAccess'), 295 | ], 296 | }); 297 | 298 | this.vpc = runnerProps.vpc ?? new ec2.Vpc(this, 'VPC'); 299 | 300 | this.securityGroup = new ec2.SecurityGroup(this, 'GitlabRunnerSecurityGroup', { 301 | vpc: this.vpc, 302 | }); 303 | const instanceProfile = new iam.CfnInstanceProfile(this, 'InstanceProfile', { 304 | roles: [this.instanceRole.roleName], 305 | }); 306 | const lt = new ec2.CfnLaunchTemplate(this, 'GitlabRunnerLaunchTemplate', { 307 | launchTemplateData: { 308 | imageId: ec2.MachineImage.latestAmazonLinux2().getImage(this).imageId, 309 | instanceType: runnerProps.instanceType, 310 | instanceMarketOptions: { 311 | marketType: runnerProps.spotInstance ? 'spot' : undefined, 312 | spotOptions: runnerProps.spotInstance ? { 313 | spotInstanceType: 'one-time', 314 | } : undefined, 315 | }, 316 | userData: cdk.Fn.base64(userData.render()), 317 | blockDeviceMappings: [ 318 | { 319 | deviceName: '/dev/xvda', 320 | ebs: runnerProps.ebsConfig ?? { 321 | volumeSize: 60, 322 | }, 323 | }, 324 | ], 325 | iamInstanceProfile: { 326 | arn: instanceProfile.attrArn, 327 | }, 328 | securityGroupIds: this.securityGroup.connections.securityGroups.map( 329 | (m) => m.securityGroupId, 330 | ), 331 | }, 332 | }); 333 | 334 | this.autoscalingGroup = new asg.AutoScalingGroup(this, 'GitlabRunnerAutoscalingGroup', { 335 | instanceType: new ec2.InstanceType(runnerProps.instanceType), 336 | autoScalingGroupName: `Gitlab Runners (${runnerProps.instanceType})`, 337 | vpc: this.vpc, 338 | vpcSubnets: runnerProps.vpcSubnet, 339 | machineImage: ec2.MachineImage.latestAmazonLinux2(), 340 | minCapacity: runnerProps.minCapacity, 341 | maxCapacity: runnerProps.maxCapacity, 342 | desiredCapacity: runnerProps.desiredCapacity, 343 | }); 344 | 345 | const cfnAsg = this.autoscalingGroup.node.tryFindChild('ASG') as asg.CfnAutoScalingGroup; 346 | cfnAsg.addPropertyDeletionOverride('LaunchConfigurationName'); 347 | cfnAsg.addPropertyOverride('LaunchTemplate', { 348 | LaunchTemplateId: lt.ref, 349 | Version: lt.attrLatestVersionNumber, 350 | }); 351 | this.autoscalingGroup.node.tryRemoveChild('LaunchConfig'); 352 | 353 | this.topicAlarm = new sns.Topic(this, 'GitlabRunnerAlarm'); 354 | const alarms = JSON.stringify(runnerProps.alarms); 355 | 356 | // Put alarms at launch 357 | const registerFunction = new lambda.Function(this, 'GitlabRunnerRegisterFunction', { 358 | code: lambda.Code.fromAsset(path.join(__dirname, '../assets/functions')), 359 | handler: 'autoscaling_events.register', 360 | runtime: lambda.Runtime.PYTHON_3_8, 361 | timeout: cdk.Duration.seconds(60), 362 | logRetention: logs.RetentionDays.ONE_DAY, 363 | environment: { 364 | ALARMS: alarms, 365 | SNS_TOPIC_ARN: this.topicAlarm.topicArn, 366 | }, 367 | }); 368 | registerFunction.role?.addToPrincipalPolicy( 369 | new iam.PolicyStatement({ 370 | effect: iam.Effect.ALLOW, 371 | resources: ['*'], 372 | actions: [ 373 | 'cloudwatch:PutMetricAlarm', 374 | ], 375 | }), 376 | ); 377 | 378 | this.autoscalingGroup.addLifecycleHook('GitlabRunnerLifeCycleHookLaunching', { 379 | lifecycleTransition: asg.LifecycleTransition.INSTANCE_LAUNCHING, 380 | notificationTarget: new FunctionHook(registerFunction), 381 | defaultResult: asg.DefaultResult.CONTINUE, 382 | heartbeatTimeout: cdk.Duration.seconds(60), 383 | }); 384 | 385 | // Add an alarm action to terminate invalid instances 386 | const alarmAction = new lambda.Function(this, 'GitlabRunnerAlarmAction', { 387 | code: lambda.Code.fromAsset(path.join(__dirname, '../assets/functions')), 388 | handler: 'autoscaling_events.on_alarm', 389 | runtime: lambda.Runtime.PYTHON_3_8, 390 | timeout: cdk.Duration.seconds(60), 391 | logRetention: logs.RetentionDays.ONE_DAY, 392 | }); 393 | alarmAction.role?.addToPrincipalPolicy( 394 | new iam.PolicyStatement({ 395 | effect: iam.Effect.ALLOW, 396 | resources: ['*'], 397 | actions: [ 398 | 'autoscaling:SetInstanceHealth', 399 | ], 400 | }), 401 | ); 402 | const alarmSubscription = new subscriptions.LambdaSubscription(alarmAction); 403 | this.topicAlarm.addSubscription(alarmSubscription); 404 | 405 | // Unregister gitlab runners and remove alarms on instance termination or CFn stack deletion 406 | const unregisterRole = new iam.Role(this, 'GitlabRunnerUnregisterRole', { 407 | assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), 408 | description: 'For Gitlab Runner Unregistering Function Role', 409 | managedPolicies: [ 410 | iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'), 411 | ], 412 | }); 413 | unregisterRole.addToPrincipalPolicy( 414 | new iam.PolicyStatement({ 415 | effect: iam.Effect.ALLOW, 416 | resources: ['*'], 417 | actions: [ 418 | 'ssm:SendCommand', 419 | 'autoscaling:DescribeAutoScalingGroups', 420 | 'cloudwatch:DeleteAlarms', 421 | ], 422 | }), 423 | ); 424 | 425 | const unregisterFunction = new lambda.Function(this, 'GitlabRunnerUnregisterFunction', { 426 | code: lambda.Code.fromAsset(path.join(__dirname, '../assets/functions')), 427 | handler: 'autoscaling_events.unregister', 428 | runtime: lambda.Runtime.PYTHON_3_8, 429 | timeout: cdk.Duration.seconds(60), 430 | role: unregisterRole, 431 | logRetention: logs.RetentionDays.ONE_DAY, 432 | environment: { 433 | ALARMS: alarms, 434 | }, 435 | }); 436 | 437 | this.autoscalingGroup.addLifecycleHook('GitlabRunnerLifeCycleHookTerminating', { 438 | lifecycleTransition: asg.LifecycleTransition.INSTANCE_TERMINATING, 439 | notificationTarget: new FunctionHook(unregisterFunction), 440 | defaultResult: asg.DefaultResult.CONTINUE, 441 | heartbeatTimeout: cdk.Duration.seconds(60), 442 | }); 443 | 444 | const unregisterCustomResource = new lambda.Function(this, 'GitlabRunnerUnregisterCustomResource', { 445 | code: lambda.Code.fromAsset(path.join(__dirname, '../assets/functions')), 446 | handler: 'autoscaling_events.on_event', 447 | runtime: lambda.Runtime.PYTHON_3_8, 448 | role: unregisterRole, 449 | logRetention: logs.RetentionDays.ONE_DAY, 450 | environment: { 451 | ALARMS: alarms, 452 | }, 453 | }); 454 | 455 | const unregisterProvider = new cr.Provider(this, 'GitlabRunnerUnregisterProvider', { 456 | onEventHandler: unregisterCustomResource, 457 | }); 458 | 459 | const customResource = new cdk.CustomResource(this, 'GitlabRunnerCustomResource', { 460 | serviceToken: unregisterProvider.serviceToken, 461 | properties: { 462 | AutoScalingGroupNames: [this.autoscalingGroup.autoScalingGroupName], 463 | }, 464 | }); 465 | customResource.node.addDependency(unregisterProvider); 466 | 467 | new cdk.CfnOutput(this, 'GitlabRunnerAutoScalingGroupArn', { 468 | value: this.autoscalingGroup.autoScalingGroupArn, 469 | }); 470 | } 471 | 472 | private dockerVolumesList(dockerVolume: DockerVolumes[] | undefined): string { 473 | let tempString: string = '--docker-volumes "/var/run/docker.sock:/var/run/docker.sock"'; 474 | if (dockerVolume) { 475 | let tempList: string[] = []; 476 | dockerVolume.forEach(e => { 477 | tempList.push(`"${e.hostPath}:${e.containerPath}"`); 478 | }); 479 | tempList.forEach(e => { 480 | tempString = `${tempString} --docker-volumes ${e}`; 481 | }); 482 | } 483 | return tempString; 484 | } 485 | /** 486 | * @param props 487 | * @returns Array. 488 | */ 489 | public createUserData(props: GitlabRunnerAutoscalingProps): string[] { 490 | return [ 491 | 'yum update -y', 492 | 'sleep 15 && amazon-linux-extras install docker && yum install -y amazon-cloudwatch-agent && systemctl start docker && usermod -aG docker ec2-user && chmod 777 /var/run/docker.sock', 493 | 'systemctl restart docker && systemctl enable docker && systemctl start amazon-cloudwatch-agent && systemctl enable amazon-cloudwatch-agent', 494 | `docker run -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock \ 495 | --name gitlab-runner-register ${props.gitlabRunnerImage} register --non-interactive --url ${props.gitlabUrl} ${compare(props.gitlabRunnerVersion, '15.10', '>=') ? '--token' : '--registration-token'} ${props.gitlabToken} \ 496 | --docker-pull-policy if-not-present ${this.dockerVolumesList(props?.dockerVolumes)} \ 497 | --executor docker --docker-image "alpine:latest" --description "A Runner on EC2 Instance (${props.instanceType})" \ 498 | ${compare(props.gitlabRunnerVersion, '15.10', '>=') ? undefined : `--tag-list "${props.tags?.join(',')}" `} --docker-privileged`, 499 | `sleep 2 && docker run --restart always -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock --name gitlab-runner ${props.gitlabRunnerImage}`, 500 | ]; 501 | } 502 | } 503 | -------------------------------------------------------------------------------- /src/gitlab-runner-instance.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import { 3 | CfnOutput, 4 | Fn, 5 | Stack, 6 | Duration, 7 | Lazy, 8 | CustomResource, 9 | Token, 10 | Annotations, 11 | } from 'aws-cdk-lib'; 12 | import { 13 | Instance, 14 | InstanceType, 15 | MachineImage, 16 | UserData, 17 | BlockDeviceVolume, 18 | SubnetType, 19 | Vpc, 20 | IVpc, 21 | IInstance, 22 | SecurityGroup, 23 | CfnLaunchTemplate, 24 | CfnSpotFleet, 25 | ISecurityGroup, 26 | SubnetSelection, 27 | IpAddresses, 28 | CfnInstance, 29 | LaunchTemplate, 30 | LaunchTemplateHttpTokens, 31 | } from 'aws-cdk-lib/aws-ec2'; 32 | import { 33 | IRole, 34 | Role, 35 | ServicePrincipal, 36 | ManagedPolicy, 37 | CfnInstanceProfile, 38 | PolicyStatement, 39 | } from 'aws-cdk-lib/aws-iam'; 40 | import * as lambda from 'aws-cdk-lib/aws-lambda'; 41 | import * as logs from 'aws-cdk-lib/aws-logs'; 42 | import * as ssm from 'aws-cdk-lib/aws-ssm'; 43 | import * as cr from 'aws-cdk-lib/custom-resources'; 44 | // eslint-disable-next-line import/no-extraneous-dependencies 45 | import { compare } from 'compare-versions'; 46 | import { Construct } from 'constructs'; 47 | 48 | import { DockerVolumes } from './gitlab-runner-interfaces'; 49 | /** 50 | * GitlabContainerRunner Props. 51 | */ 52 | export interface GitlabContainerRunnerProps { 53 | /** 54 | * Gitlab Runner version 55 | * Please give me gitlab runner version. 56 | */ 57 | readonly gitlabRunnerVersion: string; 58 | /** 59 | * Gitlab token for the Register Runner . 60 | * 61 | * @example 62 | * new GitlabContainerRunner(stack, 'runner', { gitlabtoken: 'GITLAB_TOKEN' }); 63 | * 64 | * @default - You must to give the token !!! 65 | * 66 | */ 67 | readonly gitlabtoken: string; 68 | 69 | /** 70 | * Image URL of Gitlab Runner. 71 | * 72 | * @example 73 | * new GitlabRunnerAutoscaling(stack, 'runner', { gitlabToken: 'GITLAB_TOKEN', gitlabRunnerImage: 'gitlab/gitlab-runner:alpine' }); 74 | * 75 | * @default public.ecr.aws/gitlab/gitlab-runner:latest !!! <--- latest now > 16.0 Gitlab Runner version 76 | * 77 | */ 78 | readonly gitlabRunnerImage?: string; 79 | 80 | /** 81 | * Runner default EC2 instance type. 82 | * 83 | * @example 84 | * new GitlabContainerRunner(stack, 'runner', { gitlabtoken: 'GITLAB_TOKEN', ec2type: 't3.small' }); 85 | * 86 | * @default - t3.micro 87 | * 88 | */ 89 | readonly ec2type?: string; 90 | 91 | /** 92 | * VPC for the Gitlab Runner . 93 | * 94 | * @example 95 | * const newvpc = new Vpc(stack, 'NEWVPC', { 96 | * ipAddresses: IpAddresses.cidr('10.0.0.0/16'), 97 | * maxAzs: 2, 98 | * subnetConfiguration: [{ 99 | * cidrMask: 26, 100 | * name: 'RunnerVPC', 101 | * subnetType: SubnetType.PUBLIC, 102 | * }], 103 | * natGateways: 0, 104 | * }); 105 | * 106 | * new GitlabContainerRunner(stack, 'runner', { gitlabtoken: 'GITLAB_TOKEN', selfvpc: newvpc }); 107 | * 108 | * @default - new VPC will be created , 1 Vpc , 2 Public Subnet . 109 | * 110 | */ 111 | readonly selfvpc?: IVpc; 112 | 113 | /** 114 | * IAM role for the Gitlab Runner Instance . 115 | * 116 | * @example 117 | * const role = new Role(stack, 'runner-role', { 118 | * assumedBy: new ServicePrincipal('ec2.amazonaws.com'), 119 | * description: 'For Gitlab EC2 Runner Test Role', 120 | * roleName: 'Myself-Runner-Role', 121 | * }); 122 | * 123 | * new GitlabContainerRunner(stack, 'runner', { gitlabtoken: 'GITLAB_TOKEN', ec2iamrole: role }); 124 | * 125 | * @default - new Role for Gitlab Runner Instance , attach AmazonSSMManagedInstanceCore Policy . 126 | * 127 | */ 128 | readonly ec2iamrole?: IRole; 129 | /** 130 | * tags for the runner 131 | * Unsupported Gitlab Runner 15.10 and later 132 | * @see - https://docs.gitlab.com/ee/ci/runners/new_creation_workflow.html 133 | * @default - ['runner', 'gitlab', 'awscdk'] 134 | */ 135 | readonly tags?: string[]; 136 | 137 | /** 138 | * Gitlab Runner register url . 139 | * 140 | * @example 141 | * const runner = new GitlabContainerRunner(stack, 'runner', { gitlabtoken: 'GITLAB_TOKEN',gitlaburl: 'https://gitlab.com/'}); 142 | * 143 | * @default - gitlaburl='https://gitlab.com/' , please use https://yourgitlab.com/ do not use https://yourgitlab.com 144 | * 145 | */ 146 | readonly gitlaburl?: string; 147 | 148 | /** 149 | * Gitlab Runner instance EBS size . 150 | * 151 | * @deprecated , use ebsConfig 152 | * 153 | */ 154 | readonly ebsSize?: number; 155 | 156 | /** 157 | * Gitlab Runner instance EBS config. 158 | * 159 | * @example 160 | * const runner = new GitlabContainerRunner(stack, 'runner', { gitlabToken: 'GITLAB_TOKEN', ebsConfig: { volumeSize: 60}}); 161 | * 162 | * @default - spotEbsConfig={ volumeSize: 60} 163 | * 164 | */ 165 | readonly spotEbsConfig?: CfnLaunchTemplate.EbsProperty; 166 | 167 | /** 168 | * Gitlab Runner instance EBS config. 169 | * 170 | * @example 171 | * const runner = new GitlabContainerRunner(stack, 'runner', { gitlabToken: 'GITLAB_TOKEN', onDemandEbsConfig: BlockDeviceVolume.ebs(60)}); 172 | * 173 | * @default - onDemandEbsConfig=BlockDeviceVolume.ebs(60) 174 | * 175 | */ 176 | readonly onDemandEbsConfig?: BlockDeviceVolume; 177 | 178 | /** 179 | * Gitlab Runner instance Use Spot Fleet or not ?!. 180 | * 181 | * @example 182 | * const runner = new GitlabContainerRunner(stack, 'runner', { gitlabtoken: 'GITLAB_TOKEN',spotFleet: true}); 183 | * 184 | * @default - spotFleet=false 185 | * 186 | */ 187 | readonly spotFleet?: boolean; 188 | 189 | /** 190 | * Gitlab Runner concurrent job configuration. 191 | * 192 | * @example 193 | * const runner = new GitlabContainerRunner(stack, 'runner', { gitlabtoken: 'GITLAB_TOKEN',concurrentJobs: 3}); 194 | * 195 | * @default - concurrentJobs=1 196 | */ 197 | readonly concurrentJobs?: number; 198 | 199 | /** 200 | * Gitlab Runner description. 201 | * 202 | * @example 203 | * const runner = new GitlabContainerRunner(stack, 'runner', { gitlabtoken: 'GITLAB_TOKEN',runnerDescription: 'Simple GitLab Runner'}); 204 | * 205 | * @default - runnerDescription='Docker Runner' 206 | */ 207 | readonly runnerDescription?: string; 208 | 209 | /** 210 | * SSH key name 211 | * 212 | * @default - no ssh key will be assigned , !!! only support spotfleet runner !!! . 213 | */ 214 | readonly keyName?: string; 215 | 216 | /** 217 | * The behavior when a Spot Runner Instance is interrupted 218 | * 219 | * @default - InstanceInterruptionBehavior.TERMINATE , !!! only support spotfleet runner !!! . 220 | */ 221 | readonly instanceInterruptionBehavior?: InstanceInterruptionBehavior; 222 | 223 | /** 224 | * the time when the spot fleet allocation expires 225 | * 226 | * @default - no expiration , !!! only support spotfleet runner !!! . 227 | */ 228 | readonly validUntil?: string; 229 | 230 | /** 231 | * VPC subnet for the spot fleet 232 | * 233 | * @example 234 | * const vpc = new Vpc(stack, 'nat', { 235 | * natGateways: 1, 236 | * maxAzs: 2, 237 | * }); 238 | * const runner = new GitlabContainerRunner(stack, 'testing', { 239 | * gitlabtoken: 'GITLAB_TOKEN', 240 | * ec2type: 't3.large', 241 | * ec2iamrole: role, 242 | * selfvpc: vpc, 243 | * vpcSubnet: { 244 | * subnetType: SubnetType.PUBLIC, 245 | * }, 246 | * }); 247 | * 248 | * @default - public subnet 249 | */ 250 | readonly vpcSubnet?: SubnetSelection; 251 | 252 | /** 253 | * add another Gitlab Container Runner Docker Volumes Path at job runner runtime. 254 | * 255 | * more detail see https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runnersdocker-section 256 | * 257 | * @default - already mount "/var/run/docker.sock:/var/run/docker.sock" 258 | * 259 | * @example 260 | * dockerVolumes: [ 261 | * { 262 | * hostPath: '/tmp/cache', 263 | * containerPath: '/tmp/cache', 264 | * }, 265 | * ], 266 | */ 267 | readonly dockerVolumes?: DockerVolumes[]; 268 | 269 | /** 270 | * Enabled IMDSv2. 271 | * 272 | * more detail see https://docs.aws.amazon.com/zh_tw/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html 273 | * 274 | * @default - false 275 | * 276 | */ 277 | readonly enabledIMDSv2?: boolean; 278 | } 279 | 280 | /** 281 | * InstanceInterruptionBehavior enum. 282 | */ 283 | export enum InstanceInterruptionBehavior { 284 | /** 285 | * hibernate 286 | */ 287 | HIBERNATE = 'hibernate', 288 | /** 289 | * stop 290 | */ 291 | STOP = 'stop', 292 | /** 293 | * terminate 294 | */ 295 | TERMINATE = 'terminate', 296 | } 297 | 298 | /** 299 | * GitlabContainerRunner Construct for create a Gitlab Runner. 300 | */ 301 | export class GitlabContainerRunner extends Construct { 302 | /** 303 | * The IAM role assumed by the Runner instance . 304 | */ 305 | public readonly runnerRole: IRole; 306 | 307 | /** 308 | * This represents a Runner EC2 instance , !!! only support On-demand runner instance !!! 309 | */ 310 | public readonly runnerEc2!: IInstance; 311 | 312 | /** 313 | * The EC2 runner's vpc. 314 | */ 315 | public readonly vpc!: IVpc; 316 | 317 | /** 318 | * The time when the the fleet allocation will expire , !!! only support spotfleet runner !!! 319 | */ 320 | private validUntil?: string; 321 | 322 | /** 323 | * The EC2 runner's default SecurityGroup. 324 | */ 325 | public readonly defaultRunnerSG!: ISecurityGroup; 326 | 327 | /** 328 | * SpotFleetRequestId for this spot fleet , !!! only support spotfleet runner !!! 329 | */ 330 | public readonly spotFleetRequestId!: string; 331 | 332 | /** 333 | * the first instance id in this fleet , !!! only support spotfleet runner !!! 334 | */ 335 | readonly spotFleetInstanceId!: string; 336 | 337 | private cfnSpotFleet?: CfnSpotFleet; 338 | constructor(scope: Construct, id: string, props: GitlabContainerRunnerProps) { 339 | super(scope, id); 340 | if (!props.gitlabRunnerImage) { 341 | Annotations.of(this).addWarning('Default Gitlab Runner Image use public.ecr.aws/gitlab/gitlab-runner:latest GitlabRunner Version Maybe > 16.0'); 342 | } 343 | 344 | const spotFleetId = id; 345 | 346 | const defaultProps = { 347 | gitlabRunnerImage: 'public.ecr.aws/gitlab/gitlab-runner:latest', 348 | gitlaburl: 'https://gitlab.com/', 349 | ec2type: 't3.micro', 350 | tags: ['gitlab', 'awscdk', 'runner'], 351 | concurrentJobs: 1, 352 | runnerDescription: 'Docker Runner', 353 | }; 354 | 355 | const runnerProps = { ...defaultProps, ...props }; 356 | if (Token.isUnresolved(props.gitlabtoken)) { 357 | Annotations.of(this).addWarning(` 358 | Your Gitlab Token pass by AWS Systems Manager Parameter Store or AWS Secret Manager can not check gitlab token. 359 | @see https://docs.gitlab.com/ee/ci/runners/new_creation_workflow.html`); 360 | } else { 361 | if (compare(props.gitlabRunnerVersion, '15.10', '>=') && props.gitlabtoken.includes('glrt-') === false) { 362 | throw new Error('If gitlabRunnerVersion >= 15.10, gitlabtoken please give glrt-xxxxxxx @see https://docs.gitlab.com/ee/ci/runners/new_creation_workflow.html'); 363 | } 364 | } 365 | 366 | const tokenParameterStore = new ssm.StringParameter(this, 'GitlabTokenParameter', { 367 | stringValue: 'GITLAB_TOKEN', 368 | }); 369 | 370 | const shell = UserData.forLinux(); 371 | shell.addCommands(...this.createUserData(runnerProps, tokenParameterStore.parameterName)); 372 | 373 | this.runnerRole = 374 | runnerProps.ec2iamrole ?? 375 | new Role(this, 'runner-role', { 376 | assumedBy: new ServicePrincipal('ec2.amazonaws.com'), 377 | description: 'For Gitlab EC2 Runner Role', 378 | }); 379 | this.validUntil = runnerProps.validUntil; 380 | tokenParameterStore.grantWrite(this.runnerRole); 381 | tokenParameterStore.grantRead(this.runnerRole); 382 | this.vpc = 383 | runnerProps.selfvpc ?? 384 | new Vpc(this, 'VPC', { 385 | ipAddresses: IpAddresses.cidr('10.0.0.0/16'), 386 | maxAzs: 2, 387 | subnetConfiguration: [ 388 | { 389 | cidrMask: 26, 390 | name: 'RunnerVPC', 391 | subnetType: SubnetType.PUBLIC, 392 | }, 393 | ], 394 | natGateways: 0, 395 | }); 396 | this.defaultRunnerSG = new SecurityGroup(this, 'SpotFleetSg', { 397 | vpc: this.vpc, 398 | }); 399 | const spotOrOnDemand = runnerProps.spotFleet ?? false; 400 | if (spotOrOnDemand) { 401 | //--token Error('yes new spotfleet'); 402 | 403 | const instanceProfile = new CfnInstanceProfile(this, 'InstanceProfile', { 404 | roles: [this.runnerRole.roleName], 405 | }); 406 | 407 | const imageId = MachineImage.latestAmazonLinux2().getImage(this).imageId; 408 | const lt = new CfnLaunchTemplate(this, 'LaunchTemplate', { 409 | launchTemplateData: { 410 | imageId, 411 | instanceType: runnerProps.ec2type, 412 | blockDeviceMappings: [ 413 | { 414 | deviceName: '/dev/xvda', 415 | ebs: runnerProps.spotEbsConfig ?? { 416 | volumeSize: 60, 417 | }, 418 | }, 419 | ], 420 | userData: Fn.base64(shell.render()), 421 | keyName: runnerProps.keyName, 422 | tagSpecifications: [ 423 | { 424 | resourceType: 'instance', 425 | tags: [ 426 | { 427 | key: 'Name', 428 | value: `${Stack.of(this).stackName 429 | }/spotFleetGitlabRunner/${spotFleetId}`, 430 | }, 431 | ], 432 | }, 433 | ], 434 | instanceMarketOptions: { 435 | marketType: 'spot', 436 | spotOptions: { 437 | instanceInterruptionBehavior: 438 | runnerProps.instanceInterruptionBehavior ?? 439 | InstanceInterruptionBehavior.TERMINATE, 440 | }, 441 | }, 442 | securityGroupIds: this.defaultRunnerSG.connections.securityGroups.map( 443 | (m) => m.securityGroupId, 444 | ), 445 | iamInstanceProfile: { 446 | arn: instanceProfile.attrArn, 447 | }, 448 | metadataOptions: runnerProps.enabledIMDSv2 ? { 449 | httpTokens: 'required', 450 | httpPutResponseHopLimit: 2, 451 | } : undefined, 452 | }, 453 | }); 454 | 455 | const spotFleetRole = new Role(this, 'FleetRole', { 456 | assumedBy: new ServicePrincipal('spotfleet.amazonaws.com'), 457 | managedPolicies: [ 458 | ManagedPolicy.fromAwsManagedPolicyName( 459 | 'service-role/AmazonEC2SpotFleetTaggingRole', 460 | ), 461 | ], 462 | }); 463 | 464 | const vpcSubnetSelection = runnerProps.vpcSubnet ?? { 465 | subnetType: SubnetType.PUBLIC, 466 | }; 467 | const subnetConfig = this.vpc 468 | .selectSubnets(vpcSubnetSelection) 469 | .subnets.map((s) => ({ 470 | subnetId: s.subnetId, 471 | })); 472 | 473 | this.cfnSpotFleet = new CfnSpotFleet(this, id, { 474 | spotFleetRequestConfigData: { 475 | launchTemplateConfigs: [ 476 | { 477 | launchTemplateSpecification: { 478 | launchTemplateId: lt.ref, 479 | version: lt.attrLatestVersionNumber, 480 | }, 481 | overrides: subnetConfig, 482 | }, 483 | ], 484 | iamFleetRole: spotFleetRole.roleArn, 485 | targetCapacity: 1, 486 | validUntil: Lazy.string({ produce: () => this.validUntil }), 487 | terminateInstancesWithExpiration: true, 488 | }, 489 | }); 490 | const onEvent = new lambda.Function(this, 'OnEvent', { 491 | code: lambda.Code.fromAsset(path.join(__dirname, '../assets/functions')), 492 | handler: 'index.on_event', 493 | runtime: lambda.Runtime.PYTHON_3_8, 494 | timeout: Duration.seconds(60), 495 | }); 496 | 497 | const isComplete = new lambda.Function(this, 'IsComplete', { 498 | code: lambda.Code.fromAsset(path.join(__dirname, '../assets/functions')), 499 | handler: 'index.is_complete', 500 | runtime: lambda.Runtime.PYTHON_3_8, 501 | timeout: Duration.seconds(60), 502 | role: onEvent.role, 503 | }); 504 | 505 | const myProvider = new cr.Provider(this, 'MyProvider', { 506 | onEventHandler: onEvent, 507 | isCompleteHandler: isComplete, 508 | logRetention: logs.RetentionDays.ONE_DAY, 509 | }); 510 | 511 | onEvent.addToRolePolicy( 512 | new PolicyStatement({ 513 | actions: ['ec2:DescribeSpotFleetInstances'], 514 | resources: ['*'], 515 | }), 516 | ); 517 | 518 | const fleetInstancesId = new CustomResource(this, 'GetInstanceId', { 519 | serviceToken: myProvider.serviceToken, 520 | properties: { 521 | SpotFleetRequestId: this.cfnSpotFleet.ref, 522 | }, 523 | }); 524 | 525 | fleetInstancesId.node.addDependency(this.cfnSpotFleet); 526 | this.spotFleetInstanceId = Token.asString( 527 | fleetInstancesId.getAtt('InstanceId'), 528 | ); 529 | this.spotFleetRequestId = Token.asString( 530 | fleetInstancesId.getAtt('SpotInstanceRequestId'), 531 | ); 532 | new CfnOutput(this, 'InstanceId', { value: this.spotFleetInstanceId }); 533 | new CfnOutput(this, 'SpotFleetId', { value: this.cfnSpotFleet.ref }); 534 | } else { 535 | this.runnerEc2 = new Instance(this, 'GitlabRunner', { 536 | instanceType: new InstanceType(runnerProps.ec2type), 537 | instanceName: 'Gitlab-Runner', 538 | vpc: this.vpc, 539 | vpcSubnets: runnerProps.vpcSubnet ?? { 540 | subnetType: SubnetType.PUBLIC, 541 | }, 542 | machineImage: MachineImage.latestAmazonLinux2(), 543 | role: this.runnerRole, 544 | userData: shell, 545 | securityGroup: this.defaultRunnerSG, 546 | blockDevices: [ 547 | { 548 | deviceName: '/dev/xvda', 549 | volume: runnerProps.onDemandEbsConfig ?? BlockDeviceVolume.ebs(60), 550 | }, 551 | ], 552 | }); 553 | if (runnerProps.enabledIMDSv2) { 554 | const template = new LaunchTemplate(this, 'Template', { 555 | httpTokens: LaunchTemplateHttpTokens.REQUIRED, 556 | httpPutResponseHopLimit: 2, 557 | }); 558 | const cfnInstance = this.runnerEc2.node.defaultChild as CfnInstance; 559 | cfnInstance.launchTemplate = { 560 | launchTemplateId: template.launchTemplateId, 561 | version: template.latestVersionNumber, 562 | }; 563 | } 564 | new CfnOutput(this, 'Runner-Instance-ID', { 565 | value: this.runnerEc2.instanceId, 566 | }); 567 | } 568 | 569 | const unregisterRunnerOnEvent = new lambda.Function(this, 'unregisterRunnerOnEvent', { 570 | code: lambda.Code.fromAsset(path.join(__dirname, '../assets/functions')), 571 | handler: 'unregister_runner.on_event', 572 | runtime: lambda.Runtime.PYTHON_3_8, 573 | timeout: Duration.seconds(60), 574 | }); 575 | 576 | const unregisterRunnerProvider = new cr.Provider(this, 'unregisterRunnerProvider', { 577 | onEventHandler: unregisterRunnerOnEvent, 578 | logRetention: logs.RetentionDays.ONE_DAY, 579 | }); 580 | 581 | const unregisterRunnerCR = new CustomResource(this, 'unregisterRunnerCR', { 582 | resourceType: 'Custom::unregisterRunnerProvider', 583 | serviceToken: unregisterRunnerProvider.serviceToken, 584 | properties: { 585 | TokenParameterStoreName: tokenParameterStore.parameterName, 586 | GitlabUrl: runnerProps.gitlaburl, 587 | }, 588 | }); 589 | 590 | tokenParameterStore.grantRead(unregisterRunnerOnEvent); 591 | unregisterRunnerCR.node.addDependency(tokenParameterStore); 592 | this.runnerRole.addManagedPolicy( 593 | ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore'), 594 | ); 595 | 596 | new CfnOutput(this, 'Runner-Role-Arn', { 597 | value: this.runnerRole.roleArn, 598 | }); 599 | } 600 | 601 | /** 602 | * Add expire time function for spotfleet runner !!! . 603 | * 604 | * @param duration - Block duration. 605 | */ 606 | public expireAfter(duration: Duration) { 607 | const date = new Date(); 608 | date.setSeconds(date.getSeconds() + duration.toSeconds()); 609 | this.validUntil = date.toISOString(); 610 | } 611 | 612 | private dockerVolumesList(dockerVolume: DockerVolumes[] | undefined): string { 613 | let tempString: string = '--docker-volumes "/var/run/docker.sock:/var/run/docker.sock"'; 614 | if (dockerVolume) { 615 | let tempList: string[] = []; 616 | dockerVolume.forEach(e => { 617 | tempList.push(`"${e.hostPath}:${e.containerPath}"`); 618 | }); 619 | tempList.forEach(e => { 620 | tempString = `${tempString} --docker-volumes ${e}`; 621 | }); 622 | } 623 | return tempString; 624 | } 625 | /** 626 | * @param props 627 | * @param tokenParameterStoreName - the tokenParameterStoreName to put gitlab runner token. 628 | * @returns Array. 629 | */ 630 | public createUserData(props: GitlabContainerRunnerProps, tokenParameterStoreName: string): string[] { 631 | return [ 632 | 'yum update -y ', 633 | 'sleep 15 && amazon-linux-extras install docker && yum install -y amazon-cloudwatch-agent && systemctl start docker && usermod -aG docker ec2-user && chmod 777 /var/run/docker.sock', 634 | 'systemctl restart docker && systemctl enable docker', 635 | `docker run -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock \ 636 | --name gitlab-runner-register ${props.gitlabRunnerImage} register --non-interactive --url ${props.gitlaburl} ${compare(props.gitlabRunnerVersion, '15.10', '>=') ? '--token' : '--registration-token'} ${props.gitlabtoken} \ 637 | --docker-pull-policy if-not-present ${this.dockerVolumesList(props?.dockerVolumes)} \ 638 | --executor docker --docker-image "alpine:latest" --description "${props.runnerDescription}" \ 639 | ${compare(props.gitlabRunnerVersion, '15.10', '>=') ? undefined :`--tag-list "${props.tags?.join(',')}" `} --docker-privileged`, 640 | `sleep 2 && docker run --restart always -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock --name gitlab-runner ${props.gitlabRunnerImage}`, 641 | `sed -i 's/concurrent = .*/concurrent = ${props.concurrentJobs}/g' /home/ec2-user/.gitlab-runner/config.toml`, 642 | 'TOKEN=$(cat /home/ec2-user/.gitlab-runner/config.toml | grep \'token \' | awk \'{print $3}\'| tr -d \'"\')', 643 | `aws ssm put-parameter --name ${tokenParameterStoreName} --value $TOKEN --overwrite --region ${Stack.of(this).region}`, 644 | ]; 645 | } 646 | } 647 | -------------------------------------------------------------------------------- /src/gitlab-runner-interfaces.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Docker Volumes interface. 3 | */ 4 | export interface DockerVolumes { 5 | /** 6 | * EC2 Runner Host Path 7 | * 8 | * @example - /tmp/cahce 9 | * more detail see https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runnersdocker-section 10 | */ 11 | readonly hostPath: string; 12 | 13 | /** 14 | * Job Runtime Container Path Host Path 15 | * 16 | * @example - /tmp/cahce 17 | * more detail see https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runnersdocker-section 18 | */ 19 | readonly containerPath: string; 20 | } -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './gitlab-runner-instance'; 2 | export * from './gitlab-runner-autoscaling'; 3 | export * from './gitlab-runner-interfaces'; -------------------------------------------------------------------------------- /src/integ.api.ts: -------------------------------------------------------------------------------- 1 | import { App, Stack, CfnOutput } from 'aws-cdk-lib'; 2 | import { BlockDeviceVolume, Port, Vpc } from 'aws-cdk-lib/aws-ec2'; 3 | import { ManagedPolicy, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam'; 4 | import { GitlabContainerRunner } from './index'; 5 | const env = { 6 | region: process.env.CDK_DEFAULT_REGION, 7 | account: process.env.CDK_DEFAULT_ACCOUNT, 8 | }; 9 | const mockApp = new App(); 10 | const stack = new Stack(mockApp, 'testing-stack', { env }); 11 | const role = new Role(stack, 'runner-role', { 12 | assumedBy: new ServicePrincipal('ec2.amazonaws.com'), 13 | description: 'For Gitlab EC2 Runner Test Role', 14 | roleName: 'TestRole', 15 | }); 16 | 17 | const vpc = Vpc.fromLookup(stack, 'defaultVpc', { 18 | isDefault: true, 19 | }); 20 | const runner = new GitlabContainerRunner(stack, 'testing', { 21 | gitlabtoken: stack.node.tryGetContext('GITLAB_TOKEN') ?? 'glrt-GITLAB_TOKEN', 22 | ec2type: 't3.large', 23 | ec2iamrole: role, 24 | onDemandEbsConfig: BlockDeviceVolume.ebs(100), 25 | selfvpc: vpc, 26 | // spotFleet: true, 27 | dockerVolumes: [ 28 | { 29 | hostPath: '/tmp/cahce', 30 | containerPath: '/tmp/cahce', 31 | }, 32 | ], 33 | gitlabRunnerVersion: '15.10', 34 | enabledIMDSv2: true, 35 | }); 36 | 37 | //runner.expireAfter(Duration.hours(1)); 38 | runner.runnerRole.addManagedPolicy( 39 | ManagedPolicy.fromAwsManagedPolicyName('AmazonS3ReadOnlyAccess'), 40 | ); 41 | runner.defaultRunnerSG.connections.allowFromAnyIpv4(Port.tcp(80)); 42 | runner.defaultRunnerSG.connections.allowFromAnyIpv4(Port.tcp(443)); 43 | 44 | //runner.runnerEc2.connections.allowFrom(Peer.ipv4('0.0.0.0/0'), Port.tcp(8080)); 45 | new CfnOutput(stack, 'role', { value: runner.runnerRole.roleArn }); 46 | //new CfnOutput(stack, 'InstanceID', { value: runner.runnerEc2.instanceId }); 47 | -------------------------------------------------------------------------------- /src/integ.gitlab-runner-autoscaling.ts: -------------------------------------------------------------------------------- 1 | import { App, Stack, CfnOutput } from 'aws-cdk-lib'; 2 | import * as asg from 'aws-cdk-lib/aws-autoscaling'; 3 | import * as ec2 from 'aws-cdk-lib/aws-ec2'; 4 | import { GitlabRunnerAutoscaling } from './gitlab-runner-autoscaling'; 5 | 6 | const env = { 7 | region: process.env.CDK_DEFAULT_REGION, 8 | account: process.env.CDK_DEFAULT_ACCOUNT, 9 | }; 10 | 11 | const app = new App(); 12 | const stack = new Stack(app, 'TestStackAutoscaling', { env }); 13 | 14 | const vpc = ec2.Vpc.fromLookup(stack, 'DefaultVpc', { 15 | isDefault: true, 16 | }); 17 | const defaultCapacity = parseInt(stack.node.tryGetContext('CAPACITY')); 18 | const runner = new GitlabRunnerAutoscaling(stack, 'TestRunnerAutoscaling', { 19 | gitlabToken: stack.node.tryGetContext('GITLAB_TOKEN'), 20 | ebsConfig: { 21 | volumeSize: 10, 22 | }, 23 | vpc: vpc, 24 | dockerVolumes: [{ 25 | hostPath: '/tmp/cache', 26 | containerPath: '/tmp/cache', 27 | }], 28 | minCapacity: defaultCapacity, 29 | maxCapacity: defaultCapacity, 30 | spotInstance: true, 31 | alarms: [ 32 | { 33 | AlarmName: 'GitlabRunnerDiskUsage', 34 | MetricName: 'disk_used_percent', 35 | Threshold: 50, 36 | }, 37 | ], 38 | gitlabRunnerVersion: '15.10', 39 | }); 40 | 41 | /** 42 | * Scheduled scaling 43 | * https://docs.aws.amazon.com/cdk/api/latest/docs/aws-autoscaling-readme.html#scheduled-scaling 44 | */ 45 | runner.autoscalingGroup.scaleOnSchedule('StopOnWeekends', { 46 | schedule: asg.Schedule.cron({ weekDay: 'fri', hour: '18', minute: '0' }), 47 | minCapacity: 0, 48 | maxCapacity: 0, 49 | }); 50 | 51 | runner.autoscalingGroup.scaleOnSchedule('WorkOnWeekdays', { 52 | schedule: asg.Schedule.cron({ weekDay: 'mon', hour: '9', minute: '0' }), 53 | minCapacity: defaultCapacity, 54 | maxCapacity: defaultCapacity, 55 | }); 56 | 57 | 58 | new CfnOutput(stack, 'role', { value: runner.instanceRole.roleArn }); 59 | -------------------------------------------------------------------------------- /test/gitlab-runner-autoscaling.test.ts: -------------------------------------------------------------------------------- 1 | import { App, Stack } from 'aws-cdk-lib'; 2 | import * as assertions from 'aws-cdk-lib/assertions'; 3 | import { GitlabRunnerAutoscaling } from '../src/index'; 4 | 5 | test('Can set autoscaling capacities', () => { 6 | const app = new App(); 7 | const stack = new Stack(app, 'testing-stack'); 8 | new GitlabRunnerAutoscaling(stack, 'testing', { 9 | gitlabToken: 'GITLAB_TOKEN', 10 | minCapacity: 2, 11 | maxCapacity: 4, 12 | desiredCapacity: 3, 13 | gitlabRunnerVersion: '15.9', 14 | }); 15 | 16 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { 17 | MinSize: '2', 18 | MaxSize: '4', 19 | DesiredCapacity: '3', 20 | }); 21 | }); 22 | 23 | test('Can set User Data', () => { 24 | const app = new App(); 25 | const stack = new Stack(app, 'testing-stack'); 26 | const props = { 27 | gitlabToken: 'GITLAB_TOKEN', 28 | gitlabRunnerVersion: '15.9', 29 | }; 30 | new GitlabRunnerAutoscaling(stack, 'testing', props); 31 | 32 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::LaunchTemplate', { 33 | LaunchTemplateData: { 34 | BlockDeviceMappings: [ 35 | { 36 | DeviceName: '/dev/xvda', 37 | Ebs: { 38 | VolumeSize: 60, 39 | }, 40 | }, 41 | ], 42 | IamInstanceProfile: { 43 | Arn: { 44 | 'Fn::GetAtt': [ 45 | 'testingInstanceProfile8AC9DD14', 46 | 'Arn', 47 | ], 48 | }, 49 | }, 50 | ImageId: { 51 | Ref: 'SsmParameterValueawsserviceamiamazonlinuxlatestamzn2amikernel510hvmx8664gp2C96584B6F00A464EAD1953AFF4B05118Parameter', 52 | }, 53 | InstanceMarketOptions: {}, 54 | InstanceType: 't3.micro', 55 | SecurityGroupIds: [ 56 | { 57 | 'Fn::GetAtt': [ 58 | 'testingGitlabRunnerSecurityGroup88DBF615', 59 | 'GroupId', 60 | ], 61 | }, 62 | ], 63 | UserData: { 64 | 'Fn::Base64': { 65 | 'Fn::Join': [ 66 | '', 67 | [ 68 | "#!/bin/bash\nmkdir -p $(dirname '/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json')\naws s3 cp 's3://", 69 | { 70 | 'Fn::Sub': 'cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}', 71 | }, 72 | "/95f5b9af7e6bb7ab466d97a46fb388d47cc2708cd61d933d91c43d026b74b164.json' '/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json'\nyum update -y\nsleep 15 && amazon-linux-extras install docker && yum install -y amazon-cloudwatch-agent && systemctl start docker && usermod -aG docker ec2-user && chmod 777 /var/run/docker.sock\nsystemctl restart docker && systemctl enable docker && systemctl start amazon-cloudwatch-agent && systemctl enable amazon-cloudwatch-agent\ndocker run -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock --name gitlab-runner-register public.ecr.aws/gitlab/gitlab-runner:latest register --non-interactive --url https://gitlab.com/ --registration-token GITLAB_TOKEN --docker-pull-policy if-not-present --docker-volumes \"/var/run/docker.sock:/var/run/docker.sock\" --executor docker --docker-image \"alpine:latest\" --description \"A Runner on EC2 Instance (t3.micro)\" --tag-list \"gitlab,awscdk,runner\" --docker-privileged\nsleep 2 && docker run --restart always -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock --name gitlab-runner public.ecr.aws/gitlab/gitlab-runner:latest", 73 | ], 74 | ], 75 | }, 76 | }, 77 | }, 78 | }); 79 | }); 80 | 81 | test('Can set Docker Volumes', () => { 82 | const app = new App(); 83 | const stack = new Stack(app, 'testing-stack'); 84 | const props = { 85 | gitlabToken: 'GITLAB_TOKEN', 86 | dockerVolumes: [{ 87 | hostPath: '/tmp/cache', 88 | containerPath: '/tmp/cache', 89 | }], 90 | gitlabRunnerVersion: '15.9', 91 | }; 92 | new GitlabRunnerAutoscaling(stack, 'testing', props); 93 | 94 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::LaunchTemplate', { 95 | LaunchTemplateData: { 96 | BlockDeviceMappings: [ 97 | { 98 | DeviceName: '/dev/xvda', 99 | Ebs: { 100 | VolumeSize: 60, 101 | }, 102 | }, 103 | ], 104 | IamInstanceProfile: { 105 | Arn: { 106 | 'Fn::GetAtt': [ 107 | 'testingInstanceProfile8AC9DD14', 108 | 'Arn', 109 | ], 110 | }, 111 | }, 112 | ImageId: { 113 | Ref: 'SsmParameterValueawsserviceamiamazonlinuxlatestamzn2amikernel510hvmx8664gp2C96584B6F00A464EAD1953AFF4B05118Parameter', 114 | }, 115 | InstanceMarketOptions: {}, 116 | InstanceType: 't3.micro', 117 | SecurityGroupIds: [ 118 | { 119 | 'Fn::GetAtt': [ 120 | 'testingGitlabRunnerSecurityGroup88DBF615', 121 | 'GroupId', 122 | ], 123 | }, 124 | ], 125 | UserData: { 126 | 'Fn::Base64': { 127 | 'Fn::Join': [ 128 | '', 129 | [ 130 | "#!/bin/bash\nmkdir -p $(dirname '/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json')\naws s3 cp 's3://", 131 | { 132 | 'Fn::Sub': 'cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}', 133 | }, 134 | "/95f5b9af7e6bb7ab466d97a46fb388d47cc2708cd61d933d91c43d026b74b164.json' '/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json'\nyum update -y\nsleep 15 && amazon-linux-extras install docker && yum install -y amazon-cloudwatch-agent && systemctl start docker && usermod -aG docker ec2-user && chmod 777 /var/run/docker.sock\nsystemctl restart docker && systemctl enable docker && systemctl start amazon-cloudwatch-agent && systemctl enable amazon-cloudwatch-agent\ndocker run -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock --name gitlab-runner-register public.ecr.aws/gitlab/gitlab-runner:latest register --non-interactive --url https://gitlab.com/ --registration-token GITLAB_TOKEN --docker-pull-policy if-not-present --docker-volumes \"/var/run/docker.sock:/var/run/docker.sock\" --docker-volumes \"/tmp/cache:/tmp/cache\" --executor docker --docker-image \"alpine:latest\" --description \"A Runner on EC2 Instance (t3.micro)\" --tag-list \"gitlab,awscdk,runner\" --docker-privileged\nsleep 2 && docker run --restart always -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock --name gitlab-runner public.ecr.aws/gitlab/gitlab-runner:latest", 135 | ], 136 | ], 137 | }, 138 | }, 139 | }, 140 | }); 141 | 142 | }); 143 | 144 | test('Can launch as spot instance', () => { 145 | const app = new App(); 146 | const stack = new Stack(app, 'testing-stack'); 147 | new GitlabRunnerAutoscaling(stack, 'testing', { 148 | gitlabToken: 'GITLAB_TOKEN', 149 | spotInstance: true, 150 | gitlabRunnerVersion: '15.9', 151 | }); 152 | 153 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::LaunchTemplate', { 154 | LaunchTemplateData: { 155 | BlockDeviceMappings: [ 156 | { 157 | DeviceName: '/dev/xvda', 158 | Ebs: { 159 | VolumeSize: 60, 160 | }, 161 | }, 162 | ], 163 | IamInstanceProfile: { 164 | Arn: { 165 | 'Fn::GetAtt': [ 166 | 'testingInstanceProfile8AC9DD14', 167 | 'Arn', 168 | ], 169 | }, 170 | }, 171 | ImageId: { 172 | Ref: 'SsmParameterValueawsserviceamiamazonlinuxlatestamzn2amikernel510hvmx8664gp2C96584B6F00A464EAD1953AFF4B05118Parameter', 173 | }, 174 | InstanceMarketOptions: {}, 175 | InstanceType: 't3.micro', 176 | SecurityGroupIds: [ 177 | { 178 | 'Fn::GetAtt': [ 179 | 'testingGitlabRunnerSecurityGroup88DBF615', 180 | 'GroupId', 181 | ], 182 | }, 183 | ], 184 | UserData: { 185 | 'Fn::Base64': { 186 | 'Fn::Join': [ 187 | '', 188 | [ 189 | "#!/bin/bash\nmkdir -p $(dirname '/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json')\naws s3 cp 's3://", 190 | { 191 | 'Fn::Sub': 'cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}', 192 | }, 193 | "/95f5b9af7e6bb7ab466d97a46fb388d47cc2708cd61d933d91c43d026b74b164.json' '/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json'\nyum update -y\nsleep 15 && amazon-linux-extras install docker && yum install -y amazon-cloudwatch-agent && systemctl start docker && usermod -aG docker ec2-user && chmod 777 /var/run/docker.sock\nsystemctl restart docker && systemctl enable docker && systemctl start amazon-cloudwatch-agent && systemctl enable amazon-cloudwatch-agent\ndocker run -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock --name gitlab-runner-register public.ecr.aws/gitlab/gitlab-runner:latest register --non-interactive --url https://gitlab.com/ --registration-token GITLAB_TOKEN --docker-pull-policy if-not-present --docker-volumes \"/var/run/docker.sock:/var/run/docker.sock\" --executor docker --docker-image \"alpine:latest\" --description \"A Runner on EC2 Instance (t3.micro)\" --tag-list \"gitlab,awscdk,runner\" --docker-privileged\nsleep 2 && docker run --restart always -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock --name gitlab-runner public.ecr.aws/gitlab/gitlab-runner:latest", 194 | ], 195 | ], 196 | }, 197 | }, 198 | }, 199 | }); 200 | }); 201 | 202 | test('Can set instance type', () => { 203 | const app = new App(); 204 | const stack = new Stack(app, 'testing-stack'); 205 | new GitlabRunnerAutoscaling(stack, 'testing', { 206 | gitlabToken: 'GITLAB_TOKEN', 207 | instanceType: 't3.large', 208 | gitlabRunnerVersion: '15.9', 209 | }); 210 | 211 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::LaunchTemplate', { 212 | LaunchTemplateData: { 213 | InstanceType: 't3.large', 214 | }, 215 | }); 216 | }); 217 | 218 | test('Can set EBS size', () => { 219 | const app = new App(); 220 | const stack = new Stack(app, 'testing-stack'); 221 | new GitlabRunnerAutoscaling(stack, 'testing', { 222 | gitlabToken: 'GITLAB_TOKEN', 223 | ebsConfig: { 224 | volumeSize: 100, 225 | }, 226 | gitlabRunnerVersion: '15.9', 227 | }); 228 | 229 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::LaunchTemplate', { 230 | LaunchTemplateData: { 231 | BlockDeviceMappings: [ 232 | { 233 | DeviceName: '/dev/xvda', 234 | Ebs: { 235 | VolumeSize: 100, 236 | }, 237 | }, 238 | ], 239 | }, 240 | }); 241 | }); 242 | 243 | test('Can set alarm settings', () => { 244 | const app = new App(); 245 | const stack = new Stack(app, 'testing-stack'); 246 | const alarmUserDefined = [ 247 | { 248 | AlarmName: 'TestAlarm', 249 | MetricName: 'test_metric', 250 | Threshold: 50, 251 | Period: 300, 252 | }, 253 | ]; 254 | 255 | new GitlabRunnerAutoscaling(stack, 'testing', { 256 | gitlabToken: 'GITLAB_TOKEN', 257 | alarms: alarmUserDefined, 258 | gitlabRunnerVersion: '15.9', 259 | }); 260 | 261 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { 262 | Handler: 'autoscaling_events.on_event', 263 | Environment: { 264 | Variables: { 265 | ALARMS: JSON.stringify(alarmUserDefined), 266 | }, 267 | }, 268 | }); 269 | }); 270 | 271 | test('Can overwrite props', () => { 272 | const app = new App(); 273 | const stack = new Stack(app, 'testing-stack'); 274 | new GitlabRunnerAutoscaling(stack, 'testing', { 275 | gitlabToken: 'GITLAB_TOKEN', 276 | minCapacity: 2, 277 | ebsConfig: { 278 | volumeSize: 100, 279 | encrypted: true, 280 | }, 281 | instanceType: 't3.large', 282 | spotInstance: true, 283 | gitlabRunnerVersion: '15.9', 284 | }); 285 | 286 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { 287 | MinSize: '2', 288 | MaxSize: '2', 289 | }); 290 | 291 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::LaunchTemplate', { 292 | LaunchTemplateData: { 293 | BlockDeviceMappings: [ 294 | { 295 | DeviceName: '/dev/xvda', 296 | Ebs: { 297 | VolumeSize: 100, 298 | Encrypted: true, 299 | }, 300 | }, 301 | ], 302 | InstanceMarketOptions: { 303 | MarketType: 'spot', 304 | SpotOptions: { 305 | SpotInstanceType: 'one-time', 306 | }, 307 | }, 308 | InstanceType: 't3.large', 309 | }, 310 | }); 311 | }); 312 | 313 | 314 | test('Can launch as spot instance at gitlab runner 15.10', () => { 315 | const app = new App(); 316 | const stack = new Stack(app, 'testing-stack'); 317 | new GitlabRunnerAutoscaling(stack, 'testing', { 318 | gitlabToken: 'glrt-GITLAB_TOKEN', 319 | spotInstance: true, 320 | gitlabRunnerVersion: '15.10', 321 | }); 322 | 323 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::LaunchTemplate', { 324 | LaunchTemplateData: { 325 | BlockDeviceMappings: [ 326 | { 327 | DeviceName: '/dev/xvda', 328 | Ebs: { 329 | VolumeSize: 60, 330 | }, 331 | }, 332 | ], 333 | IamInstanceProfile: { 334 | Arn: { 335 | 'Fn::GetAtt': [ 336 | 'testingInstanceProfile8AC9DD14', 337 | 'Arn', 338 | ], 339 | }, 340 | }, 341 | ImageId: { 342 | Ref: 'SsmParameterValueawsserviceamiamazonlinuxlatestamzn2amikernel510hvmx8664gp2C96584B6F00A464EAD1953AFF4B05118Parameter', 343 | }, 344 | InstanceMarketOptions: {}, 345 | InstanceType: 't3.micro', 346 | SecurityGroupIds: [ 347 | { 348 | 'Fn::GetAtt': [ 349 | 'testingGitlabRunnerSecurityGroup88DBF615', 350 | 'GroupId', 351 | ], 352 | }, 353 | ], 354 | UserData: { 355 | 'Fn::Base64': { 356 | 'Fn::Join': [ 357 | '', 358 | [ 359 | "#!/bin/bash\nmkdir -p $(dirname '/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json')\naws s3 cp 's3://", 360 | { 361 | 'Fn::Sub': 'cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}', 362 | }, 363 | "/95f5b9af7e6bb7ab466d97a46fb388d47cc2708cd61d933d91c43d026b74b164.json' '/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json'\nyum update -y\nsleep 15 && amazon-linux-extras install docker && yum install -y amazon-cloudwatch-agent && systemctl start docker && usermod -aG docker ec2-user && chmod 777 /var/run/docker.sock\nsystemctl restart docker && systemctl enable docker && systemctl start amazon-cloudwatch-agent && systemctl enable amazon-cloudwatch-agent\ndocker run -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock --name gitlab-runner-register public.ecr.aws/gitlab/gitlab-runner:latest register --non-interactive --url https://gitlab.com/ --token glrt-GITLAB_TOKEN --docker-pull-policy if-not-present --docker-volumes \"/var/run/docker.sock:/var/run/docker.sock\" --executor docker --docker-image \"alpine:latest\" --description \"A Runner on EC2 Instance (t3.micro)\" undefined --docker-privileged\nsleep 2 && docker run --restart always -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock --name gitlab-runner public.ecr.aws/gitlab/gitlab-runner:latest", 364 | ], 365 | ], 366 | }, 367 | }, 368 | }, 369 | }); 370 | }); 371 | 372 | test('Test gitlabRunnerVersion 15.10 use not glrt-xxxxx gitlabToken', () => { 373 | const app = new App(); 374 | const stack = new Stack(app, 'testing-stack'); 375 | expect(() => { 376 | new GitlabRunnerAutoscaling(stack, 'testing', { 377 | gitlabToken: 'GITLAB_TOKEN', 378 | spotInstance: true, 379 | gitlabRunnerVersion: '15.10', 380 | }); 381 | }).toThrow('If gitlabRunnerVersion >= 15.10, gitlabtoken please give glrt-xxxxxxx @see https://docs.gitlab.com/ee/ci/runners/new_creation_workflow.html'); 382 | 383 | }); -------------------------------------------------------------------------------- /test/gitlab-runner-instance.test.ts: -------------------------------------------------------------------------------- 1 | import { App, Stack, Duration } from 'aws-cdk-lib'; 2 | import * as assertions from 'aws-cdk-lib/assertions'; 3 | import { Peer, Port, Vpc, SubnetType, IpAddresses, BlockDeviceVolume } from 'aws-cdk-lib/aws-ec2'; 4 | import { Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam'; 5 | import { 6 | GitlabContainerRunner, 7 | InstanceInterruptionBehavior, 8 | } from '../src/index'; 9 | 10 | 11 | test('Create the Runner', () => { 12 | const mockApp = new App(); 13 | const stack = new Stack(mockApp, 'testing-stack'); 14 | new GitlabContainerRunner(stack, 'testing', { gitlabtoken: 'GITLAB_TOKEN', gitlabRunnerVersion: '15.9' }); 15 | assertions.Template.fromStack(stack).findResources('AWS::EC2::Instance'); 16 | assertions.Template.fromStack(stack).findResources('AWS::IAM::Role'); 17 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroup', { 18 | SecurityGroupEgress: [ 19 | { 20 | CidrIp: '0.0.0.0/0', 21 | Description: 'Allow all outbound traffic by default', 22 | IpProtocol: '-1', 23 | }, 24 | ], 25 | }); 26 | }); 27 | 28 | test('Testing runner tag change ', () => { 29 | const mockApp = new App(); 30 | const stack = new Stack(mockApp, 'testing-stack'); 31 | new GitlabContainerRunner(stack, 'testing-have-type-tag', { 32 | gitlabtoken: 'GITLAB_TOKEN', 33 | tags: ['aa', 'bb', 'cc'], 34 | gitlabRunnerVersion: '15.9', 35 | }); 36 | assertions.Template.fromStack(stack).findResources('AWS::EC2::Instance'); 37 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroup', { 38 | SecurityGroupEgress: [ 39 | { 40 | CidrIp: '0.0.0.0/0', 41 | Description: 'Allow all outbound traffic by default', 42 | IpProtocol: '-1', 43 | }, 44 | ], 45 | }); 46 | }); 47 | 48 | test('Testing Runner Instance Type Change ', () => { 49 | const mockApp = new App(); 50 | const stack = new Stack(mockApp, 'testing-stack'); 51 | new GitlabContainerRunner(stack, 'testing', { 52 | gitlabtoken: 'GITLAB_TOKEN', 53 | ec2type: 't2.micro', 54 | gitlabRunnerVersion: '15.9', 55 | }); 56 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::Instance', { 57 | InstanceType: 't2.micro', 58 | }); 59 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroup', { 60 | SecurityGroupEgress: [ 61 | { 62 | CidrIp: '0.0.0.0/0', 63 | Description: 'Allow all outbound traffic by default', 64 | IpProtocol: '-1', 65 | }, 66 | ], 67 | }); 68 | }); 69 | 70 | test('Runner Can Add Ingress ', () => { 71 | const mockApp = new App(); 72 | const stack = new Stack(mockApp, 'testing-stack'); 73 | const runner = new GitlabContainerRunner(stack, 'testing', { 74 | gitlabtoken: 'GITLAB_TOKEN', 75 | ec2type: 't2.micro', 76 | tags: ['aa', 'bb', 'cc'], 77 | gitlabRunnerVersion: '15.9', 78 | }); 79 | runner.runnerEc2.connections.allowFrom(Peer.ipv4('1.2.3.4/8'), Port.tcp(80)); 80 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::Instance', { 81 | InstanceType: 't2.micro', 82 | }); 83 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::VPC', { 84 | CidrBlock: '10.0.0.0/16', 85 | }); 86 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroup', { 87 | SecurityGroupIngress: [ 88 | { 89 | CidrIp: '1.2.3.4/8', 90 | Description: 'from 1.2.3.4/8:80', 91 | FromPort: 80, 92 | IpProtocol: 'tcp', 93 | ToPort: 80, 94 | }, 95 | ], 96 | }); 97 | }); 98 | 99 | test('Runner Can Use Self VPC ', () => { 100 | const mockApp = new App(); 101 | const stack = new Stack(mockApp, 'testing-stack'); 102 | const newvpc = new Vpc(stack, 'NEWVPC', { 103 | ipAddresses: IpAddresses.cidr('10.1.0.0/16'), 104 | maxAzs: 2, 105 | subnetConfiguration: [ 106 | { 107 | cidrMask: 26, 108 | name: 'RunnerVPC', 109 | subnetType: SubnetType.PUBLIC, 110 | }, 111 | ], 112 | natGateways: 0, 113 | }); 114 | new GitlabContainerRunner(stack, 'testing', { 115 | gitlabtoken: 'GITLAB_TOKEN', 116 | ec2type: 't2.micro', 117 | selfvpc: newvpc, 118 | gitlabRunnerVersion: '15.9', 119 | }); 120 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::Instance', { 121 | InstanceType: 't2.micro', 122 | }); 123 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::VPC', { 124 | CidrBlock: '10.1.0.0/16', 125 | }); 126 | }); 127 | 128 | test('Runner Can Use Self Role ', () => { 129 | const mockApp = new App(); 130 | const stack = new Stack(mockApp, 'testing-stack'); 131 | const role = new Role(stack, 'runner-role', { 132 | assumedBy: new ServicePrincipal('ec2.amazonaws.com'), 133 | description: 'For Gitlab EC2 Runner Test Role', 134 | roleName: 'TestRole', 135 | }); 136 | new GitlabContainerRunner(stack, 'testing', { 137 | gitlabtoken: 'GITLAB_TOKEN', 138 | ec2type: 't2.micro', 139 | ec2iamrole: role, 140 | gitlabRunnerVersion: '15.9', 141 | }); 142 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::Instance', { 143 | InstanceType: 't2.micro', 144 | }); 145 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { 146 | RoleName: 'TestRole', 147 | }); 148 | }); 149 | 150 | test('Can Use Coustom Gitlab Url', () => { 151 | const mockApp = new App(); 152 | const stack = new Stack(mockApp, 'testing-stack'); 153 | const newvpc = new Vpc(stack, 'NEWVPC', { 154 | ipAddresses: IpAddresses.cidr('10.0.0.0/16'), 155 | maxAzs: 2, 156 | natGateways: 1, 157 | }); 158 | new GitlabContainerRunner(stack, 'testing', { 159 | gitlabtoken: 'GITLAB_TOKEN', 160 | gitlaburl: 'https://gitlab.my.com/', 161 | selfvpc: newvpc, 162 | vpcSubnet: { 163 | subnetType: SubnetType.PRIVATE_WITH_EGRESS, 164 | }, 165 | gitlabRunnerVersion: '15.9', 166 | }); 167 | 168 | assertions.Template.fromStack(stack).findResources('AWS::EC2::Instance'); 169 | }); 170 | 171 | test('Can Use Coustom EBS Size', () => { 172 | const mockApp = new App(); 173 | const stack = new Stack(mockApp, 'testing-stack'); 174 | new GitlabContainerRunner(stack, 'testing', { 175 | gitlabtoken: 'GITLAB_TOKEN', 176 | onDemandEbsConfig: BlockDeviceVolume.ebs(50, { encrypted: true }), 177 | gitlabRunnerVersion: '15.9', 178 | }); 179 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::Instance', { 180 | BlockDeviceMappings: [ 181 | { 182 | DeviceName: '/dev/xvda', 183 | Ebs: { 184 | VolumeSize: 50, 185 | Encrypted: true, 186 | }, 187 | }, 188 | ], 189 | }); 190 | }); 191 | 192 | test('Can Use Spotfleet Runner', () => { 193 | const mockApp = new App(); 194 | const stack = new Stack(mockApp, 'testing-spotfleet'); 195 | const testspot = new GitlabContainerRunner(stack, 'testing', { 196 | gitlabtoken: 'GITLAB_TOKEN', 197 | spotFleet: true, 198 | instanceInterruptionBehavior: InstanceInterruptionBehavior.HIBERNATE, 199 | spotEbsConfig: { 200 | volumeSize: 100, 201 | encrypted: true, 202 | }, 203 | vpcSubnet: { 204 | subnetType: SubnetType.PUBLIC, 205 | }, 206 | gitlabRunnerVersion: '15.9', 207 | }); 208 | testspot.expireAfter(Duration.hours(6)); 209 | assertions.Template.fromStack(stack).findResources('AWS::EC2::SpotFleet'); 210 | }); 211 | 212 | test('Can Use Spotfleet Runner None ', () => { 213 | const mockApp = new App(); 214 | const stack = new Stack(mockApp, 'testing-spotfleet'); 215 | const newvpc = new Vpc(stack, 'NEWVPC', { 216 | ipAddresses: IpAddresses.cidr('10.0.0.0/16'), 217 | maxAzs: 2, 218 | natGateways: 1, 219 | }); 220 | const testspot = new GitlabContainerRunner(stack, 'testing', { 221 | gitlabtoken: 'GITLAB_TOKEN', 222 | spotFleet: true, 223 | selfvpc: newvpc, 224 | vpcSubnet: { 225 | subnetType: SubnetType.PRIVATE_WITH_EGRESS, 226 | }, 227 | gitlabRunnerVersion: '15.9', 228 | }); 229 | testspot.expireAfter(Duration.hours(6)); 230 | assertions.Template.fromStack(stack).findResources('AWS::EC2::SpotFleet'); 231 | }); 232 | 233 | test('User data with additional docker volumes', () => { 234 | const mockApp = new App(); 235 | const stack = new Stack(mockApp, 'testing-spotfleet'); 236 | const props = { 237 | gitlabtoken: 'GITLAB_TOKEN', 238 | dockerVolumes: [ 239 | { 240 | hostPath: '/tmp/cahce', 241 | containerPath: '/tmp/cahce', 242 | }, 243 | ], 244 | gitlabRunnerVersion: '15.9', 245 | }; 246 | new GitlabContainerRunner(stack, 'testing', props); 247 | 248 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::Instance', { 249 | UserData: { 250 | 'Fn::Base64': { 251 | 'Fn::Join': [ 252 | '', 253 | [ 254 | "#!/bin/bash\nyum update -y \nsleep 15 && amazon-linux-extras install docker && yum install -y amazon-cloudwatch-agent && systemctl start docker && usermod -aG docker ec2-user && chmod 777 /var/run/docker.sock\nsystemctl restart docker && systemctl enable docker\ndocker run -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock --name gitlab-runner-register public.ecr.aws/gitlab/gitlab-runner:latest register --non-interactive --url https://gitlab.com/ --registration-token GITLAB_TOKEN --docker-pull-policy if-not-present --docker-volumes \"/var/run/docker.sock:/var/run/docker.sock\" --docker-volumes \"/tmp/cahce:/tmp/cahce\" --executor docker --docker-image \"alpine:latest\" --description \"Docker Runner\" --tag-list \"gitlab,awscdk,runner\" --docker-privileged\nsleep 2 && docker run --restart always -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock --name gitlab-runner public.ecr.aws/gitlab/gitlab-runner:latest\nsed -i 's/concurrent = .*/concurrent = 1/g' /home/ec2-user/.gitlab-runner/config.toml\nTOKEN=$(cat /home/ec2-user/.gitlab-runner/config.toml | grep 'token ' | awk '{print $3}'| tr -d '\"')\naws ssm put-parameter --name ", 255 | { 256 | Ref: 'testingGitlabTokenParameterD6C98250', 257 | }, 258 | ' --value $TOKEN --overwrite --region ', 259 | { 260 | Ref: 'AWS::Region', 261 | }, 262 | ], 263 | ], 264 | }, 265 | }, 266 | }); 267 | }); 268 | 269 | test('User data with the default docker volume', () => { 270 | const mockApp = new App(); 271 | const stack = new Stack(mockApp, 'testing-spotfleet'); 272 | const props = { 273 | gitlabtoken: 'GITLAB_TOKEN', 274 | gitlabRunnerVersion: '15.9', 275 | }; 276 | new GitlabContainerRunner(stack, 'testing', props); 277 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::Instance', { 278 | UserData: { 279 | 'Fn::Base64': { 280 | 'Fn::Join': [ 281 | '', 282 | [ 283 | "#!/bin/bash\nyum update -y \nsleep 15 && amazon-linux-extras install docker && yum install -y amazon-cloudwatch-agent && systemctl start docker && usermod -aG docker ec2-user && chmod 777 /var/run/docker.sock\nsystemctl restart docker && systemctl enable docker\ndocker run -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock --name gitlab-runner-register public.ecr.aws/gitlab/gitlab-runner:latest register --non-interactive --url https://gitlab.com/ --registration-token GITLAB_TOKEN --docker-pull-policy if-not-present --docker-volumes \"/var/run/docker.sock:/var/run/docker.sock\" --executor docker --docker-image \"alpine:latest\" --description \"Docker Runner\" --tag-list \"gitlab,awscdk,runner\" --docker-privileged\nsleep 2 && docker run --restart always -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock --name gitlab-runner public.ecr.aws/gitlab/gitlab-runner:latest\nsed -i 's/concurrent = .*/concurrent = 1/g' /home/ec2-user/.gitlab-runner/config.toml\nTOKEN=$(cat /home/ec2-user/.gitlab-runner/config.toml | grep 'token ' | awk '{print $3}'| tr -d '\"')\naws ssm put-parameter --name ", 284 | { 285 | Ref: 'testingGitlabTokenParameterD6C98250', 286 | }, 287 | ' --value $TOKEN --overwrite --region ', 288 | { 289 | Ref: 'AWS::Region', 290 | }, 291 | ], 292 | ], 293 | }, 294 | }, 295 | }); 296 | }); 297 | 298 | test('Use dockerhub.io container image', () => { 299 | const mockApp = new App(); 300 | const stack = new Stack(mockApp, 'testing-spotfleet'); 301 | const props = { 302 | gitlabtoken: 'GITLAB_TOKEN', 303 | gitlabRunnerImage: 'gitlab/gitlab-runner:alpine', 304 | gitlabRunnerVersion: '15.9', 305 | }; 306 | new GitlabContainerRunner(stack, 'testing', props); 307 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::Instance', { 308 | UserData: { 309 | 'Fn::Base64': { 310 | 'Fn::Join': [ 311 | '', 312 | [ 313 | "#!/bin/bash\nyum update -y \nsleep 15 && amazon-linux-extras install docker && yum install -y amazon-cloudwatch-agent && systemctl start docker && usermod -aG docker ec2-user && chmod 777 /var/run/docker.sock\nsystemctl restart docker && systemctl enable docker\ndocker run -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock --name gitlab-runner-register gitlab/gitlab-runner:alpine register --non-interactive --url https://gitlab.com/ --registration-token GITLAB_TOKEN --docker-pull-policy if-not-present --docker-volumes \"/var/run/docker.sock:/var/run/docker.sock\" --executor docker --docker-image \"alpine:latest\" --description \"Docker Runner\" --tag-list \"gitlab,awscdk,runner\" --docker-privileged\nsleep 2 && docker run --restart always -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock --name gitlab-runner gitlab/gitlab-runner:alpine\nsed -i 's/concurrent = .*/concurrent = 1/g' /home/ec2-user/.gitlab-runner/config.toml\nTOKEN=$(cat /home/ec2-user/.gitlab-runner/config.toml | grep 'token ' | awk '{print $3}'| tr -d '\"')\naws ssm put-parameter --name ", 314 | { 315 | Ref: 'testingGitlabTokenParameterD6C98250', 316 | }, 317 | ' --value $TOKEN --overwrite --region ', 318 | { 319 | Ref: 'AWS::Region', 320 | }, 321 | ], 322 | ], 323 | }, 324 | }, 325 | }); 326 | }); 327 | 328 | test('User data with custom description', () => { 329 | const mockApp = new App(); 330 | const stack = new Stack(mockApp, 'testing-spotfleet'); 331 | const props = { 332 | gitlabtoken: 'GITLAB_TOKEN', 333 | runnerDescription: 'TEST RUNNER NAME', 334 | gitlabRunnerVersion: '15.9', 335 | }; 336 | new GitlabContainerRunner(stack, 'testing', props); 337 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::Instance', { 338 | UserData: { 339 | 'Fn::Base64': { 340 | 'Fn::Join': [ 341 | '', 342 | [ 343 | "#!/bin/bash\nyum update -y \nsleep 15 && amazon-linux-extras install docker && yum install -y amazon-cloudwatch-agent && systemctl start docker && usermod -aG docker ec2-user && chmod 777 /var/run/docker.sock\nsystemctl restart docker && systemctl enable docker\ndocker run -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock --name gitlab-runner-register public.ecr.aws/gitlab/gitlab-runner:latest register --non-interactive --url https://gitlab.com/ --registration-token GITLAB_TOKEN --docker-pull-policy if-not-present --docker-volumes \"/var/run/docker.sock:/var/run/docker.sock\" --executor docker --docker-image \"alpine:latest\" --description \"TEST RUNNER NAME\" --tag-list \"gitlab,awscdk,runner\" --docker-privileged\nsleep 2 && docker run --restart always -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock --name gitlab-runner public.ecr.aws/gitlab/gitlab-runner:latest\nsed -i 's/concurrent = .*/concurrent = 1/g' /home/ec2-user/.gitlab-runner/config.toml\nTOKEN=$(cat /home/ec2-user/.gitlab-runner/config.toml | grep 'token ' | awk '{print $3}'| tr -d '\"')\naws ssm put-parameter --name ", 344 | { 345 | Ref: 'testingGitlabTokenParameterD6C98250', 346 | }, 347 | ' --value $TOKEN --overwrite --region ', 348 | { 349 | Ref: 'AWS::Region', 350 | }, 351 | ], 352 | ], 353 | }, 354 | }, 355 | }); 356 | }); 357 | 358 | test('User data with the configured concurrent jobs', () => { 359 | const mockApp = new App(); 360 | const stack = new Stack(mockApp, 'testing-spotfleet'); 361 | const props = { 362 | gitlabtoken: 'GITLAB_TOKEN', 363 | concurrentJobs: 5, 364 | gitlabRunnerVersion: '15.9', 365 | }; 366 | new GitlabContainerRunner(stack, 'testing', props); 367 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::Instance', { 368 | UserData: { 369 | 'Fn::Base64': { 370 | 'Fn::Join': [ 371 | '', 372 | [ 373 | "#!/bin/bash\nyum update -y \nsleep 15 && amazon-linux-extras install docker && yum install -y amazon-cloudwatch-agent && systemctl start docker && usermod -aG docker ec2-user && chmod 777 /var/run/docker.sock\nsystemctl restart docker && systemctl enable docker\ndocker run -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock --name gitlab-runner-register public.ecr.aws/gitlab/gitlab-runner:latest register --non-interactive --url https://gitlab.com/ --registration-token GITLAB_TOKEN --docker-pull-policy if-not-present --docker-volumes \"/var/run/docker.sock:/var/run/docker.sock\" --executor docker --docker-image \"alpine:latest\" --description \"Docker Runner\" --tag-list \"gitlab,awscdk,runner\" --docker-privileged\nsleep 2 && docker run --restart always -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock --name gitlab-runner public.ecr.aws/gitlab/gitlab-runner:latest\nsed -i 's/concurrent = .*/concurrent = 5/g' /home/ec2-user/.gitlab-runner/config.toml\nTOKEN=$(cat /home/ec2-user/.gitlab-runner/config.toml | grep 'token ' | awk '{print $3}'| tr -d '\"')\naws ssm put-parameter --name ", 374 | { 375 | Ref: 'testingGitlabTokenParameterD6C98250', 376 | }, 377 | ' --value $TOKEN --overwrite --region ', 378 | { 379 | Ref: 'AWS::Region', 380 | }, 381 | ], 382 | ], 383 | }, 384 | }, 385 | }); 386 | }); 387 | 388 | test('User data with the configured concurrent jobs gitlabRunnerVersion 15.10', () => { 389 | const mockApp = new App(); 390 | const stack = new Stack(mockApp, 'testing-spotfleet'); 391 | const props = { 392 | gitlabtoken: 'glrt-GITLAB_TOKEN', 393 | concurrentJobs: 5, 394 | gitlabRunnerVersion: '15.10', 395 | }; 396 | new GitlabContainerRunner(stack, 'testing', props); 397 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::Instance', { 398 | UserData: { 399 | 'Fn::Base64': { 400 | 'Fn::Join': [ 401 | '', 402 | [ 403 | "#!/bin/bash\nyum update -y \nsleep 15 && amazon-linux-extras install docker && yum install -y amazon-cloudwatch-agent && systemctl start docker && usermod -aG docker ec2-user && chmod 777 /var/run/docker.sock\nsystemctl restart docker && systemctl enable docker\ndocker run -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock --name gitlab-runner-register public.ecr.aws/gitlab/gitlab-runner:latest register --non-interactive --url https://gitlab.com/ --token glrt-GITLAB_TOKEN --docker-pull-policy if-not-present --docker-volumes \"/var/run/docker.sock:/var/run/docker.sock\" --executor docker --docker-image \"alpine:latest\" --description \"Docker Runner\" undefined --docker-privileged\nsleep 2 && docker run --restart always -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock --name gitlab-runner public.ecr.aws/gitlab/gitlab-runner:latest\nsed -i 's/concurrent = .*/concurrent = 5/g' /home/ec2-user/.gitlab-runner/config.toml\nTOKEN=$(cat /home/ec2-user/.gitlab-runner/config.toml | grep 'token ' | awk '{print $3}'| tr -d '\"')\naws ssm put-parameter --name ", 404 | { 405 | Ref: 'testingGitlabTokenParameterD6C98250', 406 | }, 407 | ' --value $TOKEN --overwrite --region ', 408 | { 409 | Ref: 'AWS::Region', 410 | }, 411 | ], 412 | ], 413 | }, 414 | }, 415 | }); 416 | }); 417 | 418 | test('Test gitlabRunnerVersion 15.10 use not glrt-xxxxx gitlabToken', () => { 419 | const mockApp = new App(); 420 | const stack = new Stack(mockApp, 'testing-spotfleet'); 421 | const props = { 422 | gitlabtoken: 'GITLAB_TOKEN', 423 | concurrentJobs: 5, 424 | gitlabRunnerVersion: '15.10', 425 | }; 426 | expect(() => { 427 | new GitlabContainerRunner(stack, 'testing', props); 428 | }).toThrow('If gitlabRunnerVersion >= 15.10, gitlabtoken please give glrt-xxxxxxx @see https://docs.gitlab.com/ee/ci/runners/new_creation_workflow.html'); 429 | }); 430 | 431 | test('Enabled IMDSv2', () => { 432 | const mockApp = new App(); 433 | const stack = new Stack(mockApp, 'testing-spotfleet'); 434 | const props = { 435 | gitlabtoken: 'glrt-GITLAB_TOKEN', 436 | concurrentJobs: 5, 437 | gitlabRunnerVersion: '15.10', 438 | enabledIMDSv2: true, 439 | }; 440 | new GitlabContainerRunner(stack, 'testing', props); 441 | 442 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::LaunchTemplate', 443 | assertions.Match.objectLike({ 444 | LaunchTemplateData: { 445 | MetadataOptions: { 446 | HttpPutResponseHopLimit: 2, 447 | HttpTokens: 'required', 448 | }, 449 | }, 450 | })); 451 | assertions.Template.fromStack(stack).hasResourceProperties('AWS::EC2::Instance', { 452 | LaunchTemplate: { 453 | LaunchTemplateId: { 454 | Ref: 'testingTemplate00432C5D', 455 | }, 456 | Version: { 457 | 'Fn::GetAtt': [ 458 | 'testingTemplate00432C5D', 459 | 'LatestVersionNumber', 460 | ], 461 | }, 462 | }, 463 | UserData: { 464 | 'Fn::Base64': { 465 | 'Fn::Join': [ 466 | '', 467 | [ 468 | "#!/bin/bash\nyum update -y \nsleep 15 && amazon-linux-extras install docker && yum install -y amazon-cloudwatch-agent && systemctl start docker && usermod -aG docker ec2-user && chmod 777 /var/run/docker.sock\nsystemctl restart docker && systemctl enable docker\ndocker run -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock --name gitlab-runner-register public.ecr.aws/gitlab/gitlab-runner:latest register --non-interactive --url https://gitlab.com/ --token glrt-GITLAB_TOKEN --docker-pull-policy if-not-present --docker-volumes \"/var/run/docker.sock:/var/run/docker.sock\" --executor docker --docker-image \"alpine:latest\" --description \"Docker Runner\" undefined --docker-privileged\nsleep 2 && docker run --restart always -d -v /home/ec2-user/.gitlab-runner:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock --name gitlab-runner public.ecr.aws/gitlab/gitlab-runner:latest\nsed -i 's/concurrent = .*/concurrent = 5/g' /home/ec2-user/.gitlab-runner/config.toml\nTOKEN=$(cat /home/ec2-user/.gitlab-runner/config.toml | grep 'token ' | awk '{print $3}'| tr -d '\"')\naws ssm put-parameter --name ", 469 | { 470 | Ref: 'testingGitlabTokenParameterD6C98250', 471 | }, 472 | ' --value $TOKEN --overwrite --region ', 473 | { 474 | Ref: 'AWS::Region', 475 | }, 476 | ], 477 | ], 478 | }, 479 | }, 480 | }); 481 | }); 482 | -------------------------------------------------------------------------------- /tsconfig.dev.json: -------------------------------------------------------------------------------- 1 | // ~~ Generated by projen. To modify, edit .projenrc.js 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.js" 32 | ], 33 | "exclude": [ 34 | "node_modules" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /version.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.95.0" 3 | } 4 | --------------------------------------------------------------------------------