├── LICENSE ├── README.md ├── commitlint.yml ├── common.yml ├── docker.yml ├── examples ├── containerize-rails.yml ├── gitops │ └── kustomize.yml ├── review.yml └── review │ └── docker-compose.yml ├── javascript.yml ├── kustomize.yml ├── rails.yml ├── ruby.yml ├── sentry.yml ├── swarm.yml └── trivy.yml /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2021 ZhengXian Qiu 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Ruby GitLab CI 2 | === 3 | 4 | This is GitLab CI templates for Ruby and Rails project. 5 | 6 | ## Requirements 7 | 8 | GitLab 15.0+ 9 | 10 | > The `cobertura` is not supported by GitLab 15.0 to continue work with 14.3+ override `artifacts` to let it worked. 11 | 12 | ## Usage 13 | 14 | Include YAML in your `.gitlab-ci.yml` and apply `variables` and `rules` to control it. 15 | 16 | ```yaml 17 | include: 18 | remote: https://github.com/elct9620/ruby-gitlab-ci/raw/main/rails.yml 19 | 20 | variables: 21 | RUBY_VERSION: 2.7.4 22 | ASSETS_PRECOMPILE: 'yes' 23 | 24 | brakeman: 25 | rules: 26 | - if: $CI_MERGE_REQUEST_ID 27 | ``` 28 | 29 | > [!WARNING] 30 | > For previous version use `raw/v1` instead of `raw/main` in the URL. 31 | 32 | ### Webdrivers 33 | 34 | To support E2E testing, the default `WD_INSTALL_DIR` will be configured to `tmp/webdrivers` with the cache. You can use `webdrivers` gem without extra download cost with Capybara or others which depend on `webdrivers`. 35 | 36 | The Capybara should register customize driver with `--no-sandbox` options 37 | 38 | ```ruby 39 | Capybara.register_driver :gitlab_ci do |app| 40 | version = Capybara::Selenium::Driver.load_selenium 41 | options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options 42 | browser_options = ::Selenium::WebDriver::Chrome::Options.new.tap do |opts| 43 | opts.add_argument('--headless') 44 | opts.add_argument('--disable-gpu') if Gem.win_platform? 45 | # Workaround https://bugs.chromium.org/p/chromedriver/issues/detail?id=2650&q=load&sort=-id&colspec=ID%20Status%20Pri%20Owner%20Summary 46 | opts.add_argument('--disable-site-isolation-trials') 47 | opts.add_argument('--no-sandbox') 48 | end 49 | 50 | Capybara::Selenium::Driver.new(app, **{ :browser => :chrome, options_key => browser_options }) 51 | end 52 | 53 | Capybara.default_driver = if ENV.fetch('CI', false) 54 | :gitlab_ci 55 | else 56 | :selenium_chrome_headless 57 | end 58 | ``` 59 | 60 | ## Options 61 | 62 | The options are usually based on the `rules` keyword to enable the task. If you overwrite the `rules` the variables are not necessary to configure. 63 | 64 | | Type | Environment Name | Default | Description | 65 | |------------|-------------------------|-----------|-------------------------------------------------------------------------------------------------------------------| 66 | | Ruby | `RUBY_VERSION` | `3.2.2` | The ruby image version | 67 | | Ruby | `SORBET_ENABLED` | Unset | Enable Sorbet gem to type check | 68 | | Ruby | `RSPEC_JUNIT_REPORT` | Unset | Export JUnit report for GitLab CI with [RSpec JUnit Formatter](https://github.com/sj26/rspec_junit_formatter) gem | 69 | | JavaScript | `NODE_PACKAGE_REQUIRED` | `yes` | If not use Webpack the node packages are not required for Rails that can be disabled | 70 | | Node | `NODE_VERSION` | `18.16.0` | The node image version | 71 | | Rails | `ASSETS_PRECOMPILE` | Unset | Run Rails Assets Precompile and save into artifacts | 72 | | Rails | `RAILS_PRODUCTION_KEY` | Unset | When assets precompile we may need to replace `RAILS_MASTER_KEY` to production version | 73 | | Docker | `DOCKER_VERSION` | `24.0` | The docker version used to build docker image | 74 | | Docker | `DOCKER_ENABLED` | Unset | Run `docker build .` | 75 | | Docker | `TRIVY_ENABLED` | Unset | Use [trivy](https://github.com/aquasecurity/trivy) to scan container | 76 | | E2E | `BROWSER_REQUIRED` | `no` | Install Browser for E2E testing | 77 | | E2E | `INSTALL_CHROME` | `yes` | Install Chrome for Cucumber E2E testing | 78 | | E2E | `CHROME_VERSION` | Unset | Specify Chrome version that match chromedriver version, e.g. `114.0.5735.90-1` 79 | 80 | ### S3 81 | 82 | Upload to AWS S3 or Minio to provide CDN for your applicatoin. 83 | 84 | | Environment Name | Default | Description | 85 | |------------------------|---------|-----------------------------------------------------------------------------------------------| 86 | | `UPLOAD_TO_S3` | Unset | When set to `yes` and `ASSETS_PRECOMPILE` is `yes` will run `assets:s3` job | 87 | | `S3_ENDPOINT` | Unset | If use Minio, set to your Minio endpoint | 88 | | `S3_ACCESS_KEY_ID` | Unset | If you have another `AWS_ACCESS_KEY_ID` in your tasks, use `S3_` version to overwrite it. | 89 | | `S3_SECRET_ACCESS_KEY` | Unset | If you have another `AWS_SECRET_ACCESS_KEY` in your tasks, use `S3_` version to overwrite it. | 90 | | `S3_BUCKET` | Unset | The bucket name to upload your static assets | 91 | | `S3_SYNC_DELETE` | `no` | Delete remote bucket files if local source not present | 92 | 93 | ## Deployment 94 | 95 | The GitLab allows to create Review Apps when you create a merge request, we can use it for better QA flow. 96 | 97 | ### Options 98 | 99 | | Environment Name | Default | Description | 100 | |----------------------|---------------------------------------|---------------------------------------------------------------------------------| 101 | | `DEPLOY_BASE_DOMAIN` | `127.0.0.1.xip.io` | When deploy we will use it as a base domain, e.g. `100-branch.127.0.0.1.xip.io` | 102 | | `DEPLOY_NAME` | `$CI_PROJECT_ID-$CI-ENVIRONMENT_SLUG` | The name used to be Docker Swarm stack name or Kubernetes namespace | 103 | | `DEPLOY_DOMAIN` | `$DEPLOY_NAME.$DEPLOY_BASE_DOMAIN` | Only work for Docker Swarm with Traefik will be set to environment url | 104 | 105 | ### Docker Swarm 106 | 107 | Based on [Docker Swarm Rocks](https://dockerswarm.rocks/) example, we can use [Traefik](https://dockerswarm.rocks/traefik/) and [GitLab Runner](https://dockerswarm.rocks/gitlab-ci/) runs on Docker Swarm to support Review Apps. 108 | 109 | P.S. You have to run a GitLab CI runner in the same host with the Swarm manager and use it to deploy to the Swarm cluster. 110 | 111 | Please reference to the `examples/review.yml` as example to configure your GitLab CI and `examples/review/docker-compose.yml` for you stack file. 112 | 113 | #### Docker Swarm Options 114 | 115 | | Environment Name | Default | Description | 116 | |----------------------|---------------------------------------|---------------------------------------------------------------------------------| 117 | | `DEPLOY_STACK_FILE` | `docker-compose.yml` | The Docker Swarm stack file for deployment | 118 | | `DEPLOY_WAIT_TIME` | `60` | Time to wait for check Docker Swarm deploy status | 119 | 120 | ### Sentry 121 | 122 | The Sentry can associate commit with repository, we can use it to track the error and performance. 123 | 124 | | Environment Name | Default | Description | 125 | |---------------------|---------|-----------------------------------------------------------------| 126 | | `SENTRY_AUTH_TOKEN` | Unset | The Sentry Auth Token to upload source map and associate commit | 127 | 128 | ### GitOps 129 | 130 | The [GitOps](https://about.gitlab.com/topics/gitops/) allow us to management deployment by git and make it trackable. 131 | 132 | #### GitOps Options 133 | 134 | | Environment Name | Default | Description | 135 | |--------------------|----------|------------------------------------------------------------| 136 | | `CI_GITOPS_USER` | `gitops` | The username to access Git repository | 137 | | `CI_GITOPS_TOKEN` | `""` | The token or password to access Git repository | 138 | | `CI_GITOPS_REPO` | Unset | The repository URL (e.g. `gitlab.com/elct9620/gitops.git`) | 139 | | `CI_GITOPS_BRANCH` | `main` | The branch to push | 140 | 141 | #### Examples 142 | 143 | * `examples/gitops/kustomize.yml` 144 | 145 | ## Roadmap 146 | 147 | * [x] Ruby support 148 | * [x] Rubocop 149 | * [x] RSpec 150 | * [x] Cucumber 151 | * [x] Bundler Audit 152 | * [x] Bundler Leak 153 | * [ ] Add GitLab CI `workflow` to control jobs 154 | * [ ] Rails support 155 | * [x] Brakeman 156 | * [x] Assets Precompile 157 | * [x] S3 Upload for CDN 158 | * [ ] Database 159 | * [x] PostgreSQL 160 | * [ ] MySQL 161 | * [ ] JavaScript support 162 | * [ ] ESLint 163 | * [x] Yarn Audit 164 | * [ ] Jest 165 | * [ ] Containerize support 166 | * [x] Docker 167 | * [x] Trivy Scanner 168 | * [ ] Replace with GitLab version to generate report 169 | * [ ] Registry 170 | * [x] GitLab Registry 171 | * [ ] AWS ECR 172 | * [ ] Deployment 173 | * [x] Docker Swarm 174 | * [x] Kubernetes 175 | * [x] ArgoCD (Kustomize) 176 | -------------------------------------------------------------------------------- /commitlint.yml: -------------------------------------------------------------------------------- 1 | spec: 2 | inputs: 3 | stage: 4 | description: 'The stage to run the job' 5 | type: string 6 | default: 'lint' 7 | allow_failures: 8 | description: 'Allow the job to fail' 9 | type: boolean 10 | default: false 11 | --- 12 | 13 | commitlint: 14 | image: 15 | name: commitlint/commitlint:latest 16 | entrypoint: [''] 17 | stage: lint 18 | script: 19 | - if [ -n "${CI_MERGE_REQUEST_DIFF_BASE_SHA}" ]; then 20 | echo "Linting from ${CI_MERGE_REQUEST_DIFF_BASE_SHA} to ${CI_COMMIT_SHA}"; 21 | else 22 | echo "Linting last commit"; 23 | fi 24 | - if [ -n "${CI_MERGE_REQUEST_DIFF_BASE_SHA}" ]; then 25 | commitlint --from=${CI_MERGE_REQUEST_DIFF_BASE_SHA} --to=${CI_COMMIT_SHA}; 26 | else 27 | echo "${CI_COMMIT_MESSAGE}" | commitlint; 28 | fi 29 | allow_failure: $[[ inputs.allow_failures ]] 30 | -------------------------------------------------------------------------------- /common.yml: -------------------------------------------------------------------------------- 1 | spec: 2 | inputs: 3 | platform: 4 | description: 'The Ruby platform to use' 5 | type: string 6 | options: 7 | - 'ruby' 8 | - 'jruby' 9 | default: 'ruby' 10 | --- 11 | 12 | stages: 13 | - prepare 14 | - lint 15 | - test 16 | - compile 17 | - build 18 | - scan 19 | - deploy 20 | - release 21 | 22 | variables: 23 | NODE_VERSION: 18.16.0 24 | NODE_PACKAGE_REQUIRED: 'yes' 25 | 26 | .common-node-cache: &common-node-cache 27 | key: 28 | files: 29 | - yarn.lock 30 | - package-lock.json 31 | prefix: node-$CI_COMMIT_REF_NAME 32 | paths: 33 | - node_modules 34 | policy: pull 35 | 36 | .ruby-cache: &ruby-cache 37 | key: 38 | files: 39 | - Gemfile.lock 40 | prefix: ruby-$CI_COMMIT_REF_NAME 41 | paths: 42 | - vendor/ruby 43 | policy: pull 44 | 45 | .ruby: 46 | image: $[[ inputs.platform ]]:$RUBY_VERSION 47 | before_script: 48 | - export LANG=C.UTF-8 49 | - export LC_ALL=C.UTF-8 50 | - echo -e "\e[0Ksection_start:`date +%s`:bundle_install[collapsed=true]\r\e[0KBundle Install" 51 | - gem install bundler -v ${BUNDLER_VERSION:-2.3.20} 52 | - bundle config set path 'vendor' 53 | - bundle install # Ensure Gem installed 54 | - echo -e "\e[0Ksection_end:`date +%s`:bundle_install\r\e[0K" 55 | cache: 56 | - <<: *ruby-cache 57 | 58 | .webdrivers-cache: &webdrivers-cache 59 | paths: 60 | - tmp/webdrivers 61 | policy: pull 62 | 63 | .install_node: 64 | # TODO: Improve Node.js install in Ruby image 65 | - if [[ "$NODE_PACKAGE_REQUIRED" == "yes" ]]; 66 | then echo -e "\e[0Ksection_start:`date +%s`:setup_node[collapsed=true]\r\e[0KSetup Node.js" 67 | && curl -SLO https://nodejs.org/dist/v$NODE_VERSION/node-v${NODE_VERSION}-linux-x64.tar.xz 68 | && tar -xJf node-v${NODE_VERSION}-linux-x64.tar.xz -C /usr/local --strip-components=1 69 | && curl -o- -L https://yarnpkg.com/install.sh | bash 70 | && export PATH=$HOME/.yarn/bin:$HOME/.config/yarn/global/node_modules/.bin:$PATH 71 | && echo -e "\e[0Ksection_end:`date +%s`:setup_node\r\e[0K"; 72 | fi 73 | 74 | .rails: 75 | extends: .ruby 76 | before_script: 77 | - !reference [.install_node] 78 | - if [[ "$NODE_PACKAGE_REQUIRED" == "yes" ]]; 79 | then echo -e "\e[0Ksection_start:`date +%s`:yarn_install[collapsed=true]\r\e[0KYarn Install" 80 | && yarn install 81 | && echo -e "\e[0Ksection_end:`date +%s`:yarn_install\r\e[0K"; 82 | fi 83 | - !reference [.ruby, before_script] 84 | cache: 85 | - <<: *ruby-cache 86 | - <<: *common-node-cache 87 | - <<: *webdrivers-cache 88 | -------------------------------------------------------------------------------- /docker.yml: -------------------------------------------------------------------------------- 1 | spec: 2 | inputs: 3 | version: 4 | description: 'The version of the Docker image' 5 | type: string 6 | default: '24.0' 7 | stage: 8 | description: 'The stage to run the job' 9 | type: string 10 | default: 'build' 11 | image_name: 12 | description: 'The image name to build' 13 | type: string 14 | default: '$CI_REGISTRY_IMAGE' 15 | image_tag: 16 | description: 'The image tag to build' 17 | type: string 18 | default: '$CI_COMMIT_SHORT_SHA' 19 | --- 20 | 21 | variables: 22 | DOCKER_BUILDX_VERSION: '0.12.0' 23 | 24 | .docker: 25 | image: docker:stable 26 | cache: {} 27 | services: 28 | - docker:$[[ inputs.version ]]-dind 29 | variables: 30 | DOCKER_HOST: tcp://docker:2376 31 | before_script: 32 | - mkdir -p $HOME/.docker 33 | - echo -e "\e[0Ksection_start:`date +%s`:setup_docker[collapsed=true]\r\e[0KSetup Docker Buildx" 34 | - mkdir -p /usr/libexec/docker/cli-plugins 35 | - wget https://github.com/docker/buildx/releases/download/v${DOCKER_BUILDX_VERSION}/buildx-v${DOCKER_BUILDX_VERSION}.linux-amd64 36 | -O /usr/libexec/docker/cli-plugins/docker-buildx 37 | - chmod +x /usr/libexec/docker/cli-plugins/docker-buildx 38 | - docker context create gitlab-ci 39 | - docker buildx create --use gitlab-ci # Enable cache export support 40 | - echo -e "\e[0Ksection_end:`date +%s`:setup_docker\r\e[0K"; 41 | - echo "$CI_JOB_TOKEN" | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY 42 | 43 | .docker:base: 44 | extends: .docker 45 | variables: 46 | IMAGE_NAME: $[[ inputs.image_name | expand_vars ]] 47 | IMAGE_TAG: $[[ inputs.image_tag | expand_vars ]] 48 | REVISION: $CI_COMMIT_SHORT_SHA 49 | script: 50 | - export DOCKER_TAG_OPTIONS="--tag ${IMAGE_NAME}:${CI_COMMIT_REF_SLUG} --tag ${IMAGE_NAME}:${IMAGE_TAG}" 51 | - if [ "$LATEST_IMAGE" == "yes" ]; then export DOCKER_TAG_OPTIONS="--tag ${IMAGE_NAME}:latest ${DOCKER_TAG_OPTIONS}"; fi 52 | # Workaround for missing manifest digest to add --provenance false 53 | - docker buildx build 54 | --cache-from type=registry,ref=${IMAGE_NAME}:buildcache 55 | --cache-to type=registry,ref=${IMAGE_NAME}:buildcache,mode=max,image-manifest=true 56 | --build-arg REVISION=${REVISION} 57 | --provenance false 58 | ${DOCKER_TAG_OPTIONS} 59 | --push . 60 | interruptible: true 61 | cache: 62 | key: 63 | files: 64 | - Gemfile.lock 65 | prefix: docker-$CI_COMMIT_REF_NAME 66 | 67 | docker: 68 | extends: .docker:base 69 | stage: $[[ inputs.stage ]] 70 | retry: 71 | max: 1 72 | rules: 73 | - if: '$DOCKER_ENABLED == "yes" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' 74 | variables: 75 | LATEST_IMAGE: 'yes' 76 | - if: '$DOCKER_ENABLED == "yes" && $CI_COMMIT_TAG' 77 | variables: 78 | REVISION: $CI_COMMIT_TAG 79 | - if: '$DOCKER_ENABLED == "yes"' 80 | -------------------------------------------------------------------------------- /examples/containerize-rails.yml: -------------------------------------------------------------------------------- 1 | include: 2 | remote: https://github.com/elct9620/ruby-gitlab-ci/raw/main/rails.yml 3 | 4 | node_modules: 5 | rules: 6 | - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $NODE_PACKAGE_REQUIRED == "yes"' 7 | - if: '$CI_MERGE_REQUEST_ID && $NODE_PACKAGE_REQUIRED == "yes"' 8 | - if: '$CI_COMMIT_TAG && $NODE_PACKAGE_REQUIRED == "yes"' 9 | 10 | bundler-audit: 11 | rules: 12 | - if: $CI_MERGE_REQUEST_ID 13 | - if: $CI_PIPELINE_SOURCE == 'schedule' 14 | 15 | bundler-leak: 16 | rules: 17 | - if: $CI_MERGE_REQUEST_ID 18 | - if: $CI_PIPELINE_SOURCE == 'schedule' 19 | 20 | yarn-audit: 21 | rules: 22 | - if: $CI_MERGE_REQUEST_ID 23 | - if: $CI_PIPELINE_SOURCE == 'schedule' 24 | 25 | rubygems: 26 | rules: 27 | - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH 28 | - if: $CI_MERGE_REQUEST_ID 29 | - if: $CI_COMMIT_TAG 30 | 31 | rspec: 32 | rules: 33 | - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH 34 | - if: $CI_MERGE_REQUEST_ID 35 | 36 | rubocop: 37 | rules: 38 | - if: $CI_MERGE_REQUEST_ID 39 | 40 | brakeman: 41 | rules: 42 | - if: $CI_MERGE_REQUEST_ID 43 | 44 | assets:precompile: 45 | rules: 46 | - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH 47 | - if: $CI_COMMIT_TAG 48 | 49 | docker: 50 | needs: 51 | - job: assets:precompile 52 | artifacts: true 53 | rules: 54 | - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH 55 | variables: 56 | LATEST_IMAGE: 'yes' 57 | - if: $CI_COMMIT_TAG 58 | variables: 59 | REVISION: $CI_COMMIT_TAG 60 | 61 | trivy: 62 | rules: 63 | - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH 64 | - if: $CI_COMMIT_TAG 65 | - if: $CI_PIPELINE_SOURCE == 'schedule' 66 | -------------------------------------------------------------------------------- /examples/gitops/kustomize.yml: -------------------------------------------------------------------------------- 1 | include: 2 | - remote: https://github.com/elct9620/ruby-gitlab-ci/raw/main/docker.yml 3 | - remote: https://github.com/elct9620/ruby-gitlab-ci/raw/main/kustomize.yml 4 | 5 | variables: 6 | DOCKER_ENABLED: 'yes' 7 | # Configured in GitLab CI/CD settings 8 | CI_GITOPS_TOKEN: 'example' 9 | CI_GITOPS_REPO: 'gitlab.com/elct9620/gitops.git' 10 | 11 | deploy: 12 | extends: .kustomize 13 | environment: 14 | name: production 15 | rules: 16 | - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH 17 | variables: 18 | DEPLOY_DOMAIN: https://gitops.example.com/ 19 | -------------------------------------------------------------------------------- /examples/review.yml: -------------------------------------------------------------------------------- 1 | include: 2 | - remote: https://github.com/elct9620/ruby-gitlab-ci/raw/main/rails.yml 3 | - remote: https://github.com/elct9620/ruby-gitlab-ci/raw/main/swarm.yml 4 | 5 | node_modules: 6 | rules: 7 | - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $NODE_PACKAGE_REQUIRED == "yes"' 8 | - if: '$CI_MERGE_REQUEST_ID && $NODE_PACKAGE_REQUIRED == "yes"' 9 | - if: '$CI_COMMIT_TAG && $NODE_PACKAGE_REQUIRED == "yes"' 10 | 11 | bundler-audit: 12 | rules: 13 | - if: $CI_MERGE_REQUEST_ID 14 | - if: $CI_PIPELINE_SOURCE == 'schedule' 15 | 16 | bundler-leak: 17 | rules: 18 | - if: $CI_MERGE_REQUEST_ID 19 | - if: $CI_PIPELINE_SOURCE == 'schedule' 20 | 21 | yarn-audit: 22 | rules: 23 | - if: $CI_MERGE_REQUEST_ID 24 | - if: $CI_PIPELINE_SOURCE == 'schedule' 25 | 26 | rubygems: 27 | rules: 28 | - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH 29 | - if: $CI_MERGE_REQUEST_ID 30 | - if: $CI_COMMIT_TAG 31 | 32 | rspec: 33 | rules: 34 | - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH 35 | - if: $CI_MERGE_REQUEST_ID 36 | 37 | rubocop: 38 | rules: 39 | - if: $CI_MERGE_REQUEST_ID 40 | 41 | brakeman: 42 | rules: 43 | - if: $CI_MERGE_REQUEST_ID 44 | 45 | assets:precompile: 46 | rules: 47 | - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH 48 | - if: $CI_COMMIT_TAG 49 | - if: $CI_MERGE_REQUEST_ID 50 | 51 | docker: 52 | needs: 53 | - job: assets:precompile 54 | artifacts: true 55 | rules: 56 | - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH 57 | variables: 58 | LATEST_IMAGE: 'yes' 59 | - if: $CI_COMMIT_TAG 60 | variables: 61 | REVISION: $CI_COMMIT_TAG 62 | - if: $CI_MERGE_REQUEST_ID 63 | 64 | trivy: 65 | rules: 66 | - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH 67 | - if: $CI_COMMIT_TAG 68 | - if: $CI_PIPELINE_SOURCE == 'schedule' 69 | 70 | review:deploy: 71 | extends: .deploy 72 | stage: deploy 73 | environment: 74 | name: review/$CI_COMMIT_REF_NAME 75 | on_stop: review:stop 76 | needs: 77 | - docker 78 | tags: # Ensure Run on Docker Swarm 79 | - swarm 80 | only: 81 | - merge_requests 82 | 83 | review:stop: 84 | extends: .deploy:stop 85 | stage: deploy 86 | environment: 87 | name: review/$CI_COMMIT_REF_NAME 88 | needs: 89 | - docker 90 | tags: 91 | - swarm 92 | only: 93 | - merge_requests 94 | when: manual 95 | -------------------------------------------------------------------------------- /examples/review/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | postgres: 5 | image: postgres:13.1 6 | environment: 7 | - POSTGRES_DB=postgres 8 | - POSTGRES_USER=postgres 9 | - POSTGRES_PASSWORD=postgres 10 | deploy: 11 | placement: 12 | constraints: 13 | - node.labels.role == database 14 | volumes: 15 | - postgres_data:/var/lib/postgresql/data 16 | networks: 17 | - net 18 | application: 19 | image: "${IMAGE_NAME}:${IMAGE_TAG}" 20 | environment: 21 | - AUTO_MIGRATION=yes # Provided by "openbox" gem 22 | - DATABASE_URL=postgres://postgres:postgres@postgres/postgres 23 | - RAILS_MASTER_KEY 24 | deploy: 25 | placement: 26 | constraints: 27 | - node.labels.role == worker 28 | labels: 29 | - traefik.enable=true 30 | - traefik.docker.network=traefik-public 31 | # Hosts 32 | - "traefik.http.routers.${DEPLOY_NAME}-http.rule=Host(`${DEPLOY_DOMAIN}`)" 33 | - "traefik.http.routers.${DEPLOY_NAME}-http.entrypoints=web" 34 | - "traefik.http.routers.${DEPLOY_NAME}-http.middlewares=https-redirect" 35 | - "traefik.http.routers.${DEPLOY_NAME}-https.rule=Host(`${DEPLOY_DOMAIN}`)" 36 | - "traefik.http.routers.${DEPLOY_NAME}-https.entrypoints=websecure" 37 | - "traefik.http.routers.${DEPLOY_NAME}-https.tls=true" 38 | - "traefik.http.routers.${DEPLOY_NAME}-https.tls.certresolver=letsencrypt" 39 | - "traefik.http.routers.${DEPLOY_NAME}-https.tls.domains[0].sans=*.${DEPLOY_BASE_DOMAIN}" 40 | - "traefik.http.services.${DEPLOY_NAME}.loadbalancer.server.port=3000" 41 | networks: 42 | - net 43 | - traefik-public 44 | depends_on: 45 | - postgres 46 | 47 | volumes: 48 | postgres_data: 49 | 50 | networks: 51 | net: 52 | driver: overlay 53 | attachable: true 54 | traefik-public: 55 | external: true 56 | -------------------------------------------------------------------------------- /javascript.yml: -------------------------------------------------------------------------------- 1 | spec: 2 | inputs: 3 | platform: 4 | description: 'The JavaScript platform to use' 5 | type: string 6 | options: 7 | - 'node' 8 | default: 'node' 9 | 10 | version: 11 | description: 'The JavaScript platform version to use' 12 | type: string 13 | default: '18.16.0' 14 | 15 | prepare_stage: 16 | description: 'The stage to prepare the node_modules' 17 | type: string 18 | default: 'prepare' 19 | 20 | lint_stage: 21 | description: 'The stage to run the linting' 22 | type: string 23 | default: 'lint' 24 | --- 25 | 26 | .node-cache: &node-cache 27 | key: 28 | files: 29 | - yarn.lock 30 | - package-lock.json 31 | prefix: node-$CI_COMMIT_REF_NAME 32 | paths: 33 | - node_modules 34 | policy: pull 35 | 36 | .node: 37 | image: $[[ inputs.platform ]]:$[[ inputs.version ]] 38 | before_script: 39 | cache: 40 | - <<: *node-cache 41 | 42 | node_modules: 43 | extends: .node 44 | stage: $[[ inputs.prepare_stage ]] 45 | script: 46 | - yarn install 47 | interruptible: true 48 | retry: 49 | max: 1 50 | cache: 51 | - key: !reference [.node-cache, key] 52 | paths: !reference [.node-cache, paths] 53 | policy: pull-push 54 | rules: 55 | - if: '$NODE_PACKAGE_REQUIRED == "yes"' 56 | 57 | yarn-audit: 58 | extends: .node 59 | stage: $[[ inputs.lint_stage ]] 60 | script: 61 | - yarn audit 62 | interruptible: true 63 | allow_failure: true 64 | rules: 65 | - if: '$NODE_PACKAGE_REQUIRED == "yes"' 66 | -------------------------------------------------------------------------------- /kustomize.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | DEPLOY_BASE_DOMAIN: 127.0.0.1.xip.io 3 | DEPLOY_NAME: $CI_PROJECT_ID-$CI_ENVIRONMENT_SLUG 4 | DEPLOY_DOMAIN: $DEPLOY_NAME.$DEPLOY_BASE_DOMAIN 5 | CI_GITOPS_USER: gitops 6 | CI_GITOPS_TOKEN: '' 7 | CI_GITOPS_BRANCH: main 8 | 9 | .kustomize: 10 | image: line/kubectl-kustomize 11 | stage: deploy 12 | environment: 13 | url: $DEPLOY_DOMAIN 14 | before_script: 15 | - apk add git 16 | - git config --global user.email "$GITLAB_USER_EMAIL" 17 | - git config --global user.name "$GITLAB_USER_NAME" 18 | - git clone https://${CI_GITOPS_USER}:${CI_GITOPS_TOKEN}@${CI_GITOPS_REPO} gitops 19 | - cd gitops 20 | script: 21 | - kustomize edit set image ${IMAGE_NAME}:${IMAGE_TAG} 22 | - git commit -am "[${CI_PROJECT_PATH}] release by GitLab CI - Version@${IMAGE_TAG}" 23 | - git push origin ${CI_GITOPS_BRANCH} 24 | resource_group: deploy 25 | -------------------------------------------------------------------------------- /rails.yml: -------------------------------------------------------------------------------- 1 | spec: 2 | inputs: 3 | platform: 4 | description: 'The Ruby platform to use' 5 | type: string 6 | options: 7 | - 'ruby' 8 | - 'jruby' 9 | default: 'ruby' 10 | --- 11 | 12 | include: 13 | - remote: https://github.com/elct9620/ruby-gitlab-ci/raw/main/ruby.yml 14 | inputs: 15 | platform: $[[ inputs.platform ]] 16 | - remote: https://github.com/elct9620/ruby-gitlab-ci/raw/main/javascript.yml 17 | - remote: https://github.com/elct9620/ruby-gitlab-ci/raw/main/docker.yml 18 | - remote: https://github.com/elct9620/ruby-gitlab-ci/raw/main/trivy.yml 19 | 20 | variables: 21 | # Database 22 | POSTGRES_VERSION: 15-alpine 23 | POSTGRES_DB: application 24 | POSTGRES_USER: postgres 25 | POSTGRES_PASSWORD: postgres 26 | DATABASE_URL: "postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/$POSTGRES_DB" 27 | # Webdrivers 28 | WD_INSTALL_DIR: $CI_PROJECT_DIR/tmp/webdrivers 29 | BROWSER_REQUIRED: 'no' 30 | INSTALL_CHROME: 'yes' 31 | # SAST 32 | GITLAB_SECURITY_REGISTRY: registry.gitlab.com/security-products 33 | GITLAB_BRAKEMAN_VERSION: 3 34 | 35 | .rails:test: 36 | extends: .rails 37 | services: 38 | - "postgres:$POSTGRES_VERSION" 39 | variables: 40 | RAILS_ENV: test 41 | before_script: 42 | - !reference [.rails, before_script] 43 | - if [[ "$BROWSER_REQUIRED" == "yes" && "$INSTALL_CHROME" == "yes" ]]; then 44 | echo -e "\e[0Ksection_start:`date +%s`:setup_chrome[collapsed=true]\r\e[0KSetup Chrome" 45 | && curl -sSL https://dl.google.com/linux/linux_signing_key.pub | apt-key add - 46 | && echo "deb [arch=amd64] https://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list 47 | && apt-get update; 48 | fi 49 | - if [[ "$BROWSER_REQUIRED" == "yes" && "$INSTALL_CHROME" == "yes" && -v CHROME_VERSION ]]; then 50 | wget --no-verbose -O /tmp/chrome.deb https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_${CHROME_VERSION}_amd64.deb 51 | && apt install -y /tmp/chrome.deb --no-install-recommends 52 | && rm /tmp/chrome.deb; 53 | fi 54 | - if [[ "$BROWSER_REQUIRED" == "yes" && "$INSTALL_CHROME" == "yes" && ! -v CHROME_VERSION ]]; then 55 | apt-get install -y google-chrome-stable --no-install-recommends; 56 | fi 57 | - if [[ "$BROWSER_REQUIRED" == "yes" && "$INSTALL_CHROME" == "yes" ]]; then 58 | echo -e "\e[0Ksection_end:`date +%s`:setup_chrome\r\e[0K"; 59 | fi 60 | - echo -e "\e[0Ksection_start:`date +%s`:prepare_rails_test[collapsed=true]\r\e[0KPrepare Rails Test" 61 | - bundle exec rake db:migrate 62 | - bundle exec rake test:prepare || true 63 | - echo -e "\e[0Ksection_end:`date +%s`:prepare_rails_test\r\e[0K" 64 | 65 | brakeman: 66 | image: $GITLAB_SECURITY_REGISTRY/brakeman:$GITLAB_BRAKEMAN_VERSION 67 | stage: lint 68 | needs: ["rubygems"] 69 | script: 70 | - /analyzer run 71 | interruptible: true 72 | artifacts: 73 | reports: 74 | sast: gl-sast-report.json 75 | 76 | rspec: 77 | extends: .rails:test 78 | 79 | cucumber: 80 | extends: .rails:test 81 | 82 | assets:precompile: 83 | extends: .rails 84 | stage: compile 85 | services: 86 | - "postgres:$POSTGRES_VERSION" 87 | variables: 88 | RAILS_ENV: production 89 | SECRET_KEY_BASE_DUMMY: 1 90 | script: 91 | - RAILS_MASTER_KEY=${RAILS_PRODUCTION_KEY:-$RAILS_MASTER_KEY} bundle exec rails assets:precompile 92 | interruptible: true 93 | retry: 94 | max: 1 95 | artifacts: 96 | paths: 97 | - public/vite 98 | - public/packs 99 | - public/assets 100 | rules: 101 | - if: '$ASSETS_PRECOMPILE == "yes"' 102 | 103 | assets:s3: 104 | image: 105 | name: amazon/aws-cli:latest 106 | entrypoint: [""] 107 | stage: deploy 108 | variables: 109 | # NOTE: The space will split by shell, avoid to use it 110 | S3_SYNC_OPTIONS: '--cache-control "public,max-age=31536000" --acl public-read' 111 | SE_SYNC_DELETE: 'no' 112 | before_script: 113 | - echo -e "\e[0Ksection_start:`date +%s`:setup_s3[collapsed=true]\r\e[0KSetup S3 Options" 114 | - if [[ -v S3_ENDPOINT ]]; then export AWS_EXTRA_OPTIONS="${AWS_EXTRA_OPTIONS} --endpoint-url ${S3_ENDPOINT}"; fi 115 | - if [[ -v S3_ACCESS_KEY_ID ]]; then export AWS_ACCESS_KEY_ID=$S3_ACCESS_KEY_ID; fi 116 | - if [[ -v S3_SECRET_ACCESS_KEY ]]; then export AWS_SECRET_ACCESS_KEY=$S3_SECRET_ACCESS_KEY; fi 117 | - if [[ "$S3_SYNC_DELETE" == "yes" ]]; then export S3_SYNC_OPTIONS="$S3_SYNC_OPTIONS --delete"; fi 118 | - echo -e "\e[0Ksection_end:`date +%s`:setup_s3\r\e[0K" 119 | script: 120 | - aws $AWS_EXTRA_OPTIONS s3 sync ./public s3://$S3_BUCKET ${S3_SYNC_OPTIONS} 121 | needs: 122 | - job: assets:precompile 123 | artifacts: true 124 | rules: 125 | - if: '$ASSETS_PRECOMPILE == "yes" && $UPLOAD_TO_S3 == "yes"' 126 | -------------------------------------------------------------------------------- /ruby.yml: -------------------------------------------------------------------------------- 1 | spec: 2 | inputs: 3 | platform: 4 | description: 'The Ruby platform to use' 5 | type: string 6 | options: 7 | - 'ruby' 8 | - 'jruby' 9 | default: 'ruby' 10 | --- 11 | 12 | include: 13 | - remote: https://github.com/elct9620/ruby-gitlab-ci/raw/main/common.yml#ruby.yml 14 | inputs: 15 | platform: $[[ inputs.platform ]] 16 | 17 | variables: 18 | RUBY_VERSION: 3.2.2 19 | CUCUMBER_PUBLISH_QUIET: 'true' 20 | 21 | rubygems: 22 | extends: .ruby 23 | stage: prepare 24 | script: 25 | - bundle install 26 | retry: 27 | max: 1 28 | interruptible: true 29 | cache: 30 | - key: !reference [.ruby-cache, key] 31 | paths: !reference [.ruby-cache, paths] 32 | policy: pull-push 33 | 34 | rubocop: 35 | extends: .ruby 36 | stage: lint 37 | needs: ["rubygems"] 38 | script: 39 | - bundle exec rubocop 40 | interruptible: true 41 | rules: 42 | - exists: 43 | - .rubocop.yml 44 | 45 | sorbet: 46 | extends: .ruby 47 | stage: lint 48 | needs: ["rubygems"] 49 | script: 50 | - bundle exec srb tc 51 | interruptible: true 52 | rules: 53 | - if: '$SORBET_ENABLED == "yes"' 54 | exists: 55 | - sorbet/config 56 | 57 | bundler-audit: 58 | extends: .ruby 59 | stage: lint 60 | needs: ["rubygems"] 61 | before_script: 62 | - gem install bundler-audit 63 | - bundle audit --update 64 | script: 65 | - bundle audit 66 | interruptible: true 67 | 68 | bundler-leak: 69 | extends: .ruby 70 | stage: lint 71 | needs: ["rubygems"] 72 | before_script: 73 | - gem install bundler-leak 74 | - bundle leak check --update 75 | script: 76 | - bundle leak 77 | interruptible: true 78 | 79 | rspec: 80 | extends: .ruby 81 | stage: test 82 | needs: ["rubygems"] 83 | script: 84 | - if [[ "$RSPEC_JUNIT_REPORT" == "yes" ]]; then bundle exec rspec --format progress --format RspecJunitFormatter --out rspec.xml; else bundle exec rspec; fi 85 | interruptible: true 86 | artifacts: 87 | paths: 88 | - coverage 89 | - rspec.xml 90 | reports: 91 | junit: rspec.xml 92 | coverage_report: 93 | coverage_format: cobertura 94 | path: coverage/coverage.xml 95 | coverage: '/\(\d+.\d+%\)/' 96 | rules: 97 | - exists: 98 | - spec/spec_helper.rb 99 | 100 | cucumber: 101 | extends: .ruby 102 | stage: test 103 | needs: ["rubygems"] 104 | script: 105 | - bundle exec cucumber -f junit,fileattribute=true -o tmp/cucumber 106 | interruptible: true 107 | artifacts: 108 | paths: 109 | - coverage 110 | reports: 111 | junit: tmp/cucumber/**/TEST-*.xml 112 | coverage_report: 113 | coverage_format: cobertura 114 | path: coverage/coverage.xml 115 | coverage: '/\(\d+.\d+%\)/' 116 | rules: 117 | - exists: 118 | - features/support/env.rb 119 | -------------------------------------------------------------------------------- /sentry.yml: -------------------------------------------------------------------------------- 1 | spec: 2 | inputs: 3 | stage: 4 | description: 'The stage to run the Sentry' 5 | type: string 6 | default: 'deploy' 7 | --- 8 | 9 | .sentry:base: 10 | image: getsentry/sentry-cli 11 | stage: $[[ inputs.stage ]] 12 | variables: 13 | SENTRY_ENVIRONMENT: production 14 | REVISION: $CI_COMMIT_SHORT_SHA 15 | 16 | .sentry:new-release: 17 | extends: .sentry:base 18 | script: 19 | - sentry-cli releases new ${REVISION} 20 | 21 | .sentry:release: 22 | extends: .sentry:base 23 | script: 24 | - sentry-cli releases set-commits --auto ${REVISION} 25 | - sentry-cli releases finalize ${REVISION} 26 | - sentry-cli releases deploys ${REVISION} new -e ${SENTRY_ENVIRONMENT} 27 | -------------------------------------------------------------------------------- /swarm.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | DEPLOY_BASE_DOMAIN: 127.0.0.1.xip.io 3 | DEPLOY_NAME: $CI_PROJECT_ID-$CI_ENVIRONMENT_SLUG 4 | DEPLOY_DOMAIN: $DEPLOY_NAME.$DEPLOY_BASE_DOMAIN 5 | DEPLOY_STACK_FILE: docker-compose.yml 6 | DEPLOY_USER: $CI_REGISTRY_USER 7 | DEPLOY_PASSWORD: $CI_JOB_TOKEN 8 | DEPLOY_WAIT_TIME: 9 | value: '30' 10 | description: 'The time to wait docker swarm download image' 11 | 12 | .deploy: 13 | image: docker:stable 14 | stage: deploy 15 | environment: 16 | url: $DEPLOY_DOMAIN 17 | before_script: 18 | - echo -e "\e[0Ksection_start:`date +%s`:setup_docker[collapsed=true]\r\e[0KSetup Docker" 19 | - if [[ -v CI_DEPLOY_USER ]] && [[ -v CI_DEPLOY_PASSWORD ]]; then export DEPLOY_USER=$CI_DEPLOY_USER; DEPLOY_PASSWORD=$CI_DEPLOY_PASSWORD; fi 20 | - echo "$DEPLOY_PASSWORD" | docker login -u $DEPLOY_USER --password-stdin $CI_REGISTRY 21 | - apk -Uuv add curl 22 | - curl -LO https://github.com/sudo-bmitch/docker-stack-wait/raw/main/docker-stack-wait.sh 23 | - chmod +x docker-stack-wait.sh 24 | - if [[ "$CI_ENVIRONMENT_NAME" == "production" ]]; then export RAILS_MASTER_KEY=${RAILS_PRODUCTION_KEY:-$RAILS_MASTER_KEY}; fi 25 | - echo -e "\e[0Ksection_end:`date +%s`:setup_docker\r\e[0K" 26 | script: 27 | - docker stack deploy -c $DEPLOY_STACK_FILE --with-registry-auth --prune $DEPLOY_NAME 28 | - sleep $DEPLOY_WAIT_TIME 29 | - ./docker-stack-wait.sh -r $DEPLOY_NAME 30 | - echo "DEPLOY_DOMAIN=http://$DEPLOY_DOMAIN" >> deploy.env 31 | resource_group: deploy 32 | artifacts: 33 | reports: 34 | dotenv: deploy.env 35 | 36 | .deploy:stop: 37 | image: docker:stable 38 | stage: deploy 39 | environment: 40 | action: stop 41 | script: 42 | - docker stack rm $DEPLOY_NAME 43 | -------------------------------------------------------------------------------- /trivy.yml: -------------------------------------------------------------------------------- 1 | spec: 2 | inputs: 3 | stage: 4 | description: 'The stage to run Trivy scan' 5 | type: string 6 | default: 'scan' 7 | image_name: 8 | description: 'The image name to scan' 9 | type: string 10 | default: '$CI_REGISTRY_IMAGE' 11 | image_tag: 12 | description: 'The image tag to scan' 13 | type: string 14 | default: '$CI_COMMIT_SHORT_SHA' 15 | options: 16 | description: 'The options to pass to Trivy' 17 | type: string 18 | default: '--severity HIGH,CRITICAL' 19 | timeout: 20 | description: 'The timeout for the scan' 21 | type: string 22 | default: '15m' 23 | --- 24 | 25 | trivy: 26 | image: 27 | name: docker.io/aquasec/trivy:latest 28 | entrypoint: [""] 29 | stage: $[[ inputs.stage ]] 30 | variables: 31 | # Exclude built-in rubygems 32 | TRIVY_OPTIONS: --skip-dirs /usr/local/lib/ruby/gems 33 | # No need to clone the repo, we exclusively work on artifacts. See 34 | # https://docs.gitlab.com/ee/ci/runners/README.html#git-strategy 35 | GIT_STRATEGY: none 36 | TRIVY_USERNAME: "$CI_REGISTRY_USER" 37 | TRIVY_PASSWORD: "$CI_REGISTRY_PASSWORD" 38 | TRIVY_AUTH_URL: "$CI_REGISTRY" 39 | FULL_IMAGE_NAME: "$[[ inputs.image_name | expand_vars ]]:$[[ inputs.image_tag | expand_vars ]]" 40 | TRIVY_NO_PROGRESS: "true" 41 | TRIVY_CACHE_DIR: ".trivycache/" 42 | cache: 43 | key: trivy-shared 44 | paths: 45 | - .trivycache/ 46 | needs: 47 | - docker 48 | script: 49 | - trivy --version 50 | # cache cleanup is needed when scanning images with the same tags, it does not remove the database 51 | - time trivy clean --scan-cache 52 | # update vulnerabilities db 53 | - time trivy image --download-db-only 54 | # Builds report and puts it in the default workdir $CI_PROJECT_DIR, so `artifacts:` can take it from there 55 | - time trivy image --exit-code 0 --format template --template "@/contrib/gitlab.tpl" 56 | --output "$CI_PROJECT_DIR/gl-container-scanning-report.json" "$FULL_IMAGE_NAME" 57 | --timeout $[[ inputs.timeout ]] 58 | # Prints full report 59 | - time trivy image --exit-code 0 "$FULL_IMAGE_NAME" 60 | # Fail on critical vulnerabilities 61 | - time trivy image --exit-code 1 --skip-java-db-update $TRIVY_OPTIONS $[[ inputs.options ]] "$FULL_IMAGE_NAME" 62 | interruptible: true 63 | artifacts: 64 | when: 65 | reports: 66 | container_scanning: gl-container-scanning-report.json 67 | rules: 68 | - if: '$CI_COMMIT_TAG' 69 | --------------------------------------------------------------------------------