include: - project: gitlab-org/frontend/untamper-my-lockfile file: templates/merge_request_pipelines.yml - template: Security/Dependency-Scanning.gitlab-ci.yml - template: Security/SAST.gitlab-ci.yml - local: .gitlab/ci/*.gitlab-ci.yml - project: 'gitlab-org/quality/pipeline-common' file: - '/ci/danger-review.yml' default: image: registry.gitlab.com/gitlab-org/gitlab-docs/base:alpine-3.16-ruby-2.7.6-0bc327a4 tags: - gitlab-org # Check Ruby, RubyGems, and Bundler versions and install gems before_script: - ruby -v - gem -v - bundle -v - bundle config set --local deployment true # Install dependencies into ./vendor/ruby - bundle install --jobs 4 .yarn: before_script: - yarn install --cache-folder .yarn-cache stages: - build-images - build - security - test - pre-deploy - deploy - post-deploy # # Pick the remote branch, by default master (see the Rakefile for more info) # variables: BRANCH_EE: 'master' BRANCH_OMNIBUS: 'master' BRANCH_RUNNER: 'main' BRANCH_CHARTS: 'master' BRANCH_OPERATOR: 'master' BUNDLE_PATH__SYSTEM: 'false' GIT_DEPTH: '20' ALPINE_VERSION: '3.16' VALE_VERSION: '2.17.0' MARKDOWNLINT_VERSION: '0.31.1' RUBY_VERSION: '2.7.6' # # Retry a job automatically if it fails (2 times) # .retry: retry: 2 # # workflow:rules to prevent duplicate pipelines when pushing to a branch with an open MR. # workflow: rules: # Prevent branch pipelines if an MR is open on the branch. - if: $CI_COMMIT_BRANCH && $CI_PIPELINE_SOURCE == "push" && $CI_OPEN_MERGE_REQUESTS when: never # Allow merge request and scheduled pipelines. - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: '$CI_PIPELINE_SOURCE == "schedule"' - if: '$CI_PIPELINE_SOURCE == "pipeline"' - if: '$CI_PIPELINE_SOURCE == "trigger"' # Allow branch pipelines for the default branch, stable branches named XX.X, and review app branches. - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' - if: '$CI_COMMIT_BRANCH == "main"' - if: '$CI_COMMIT_BRANCH =~ /^\d{1,2}\.\d{1,2}$/' - if: '$CI_COMMIT_BRANCH =~ /docs-preview/' # # Rules to determine which pipelines jobs will run in. # .rules_scheduled: rules: - if: $CHORES_PIPELINE == "true" || $CLEAN_REVIEW_APPS_DAYS when: never - if: '$CI_PIPELINE_SOURCE != "schedule"' when: never - if: '$PIPELINE_SCHEDULE_TIMING == "weekly"' - if: '$PIPELINE_SCHEDULE_TIMING == "hourly"' when: manual allow_failure: true - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' when: manual allow_failure: true - if: '$CI_COMMIT_BRANCH == "main"' when: manual allow_failure: true .rules_scheduled_manual: rules: - if: $CHORES_PIPELINE == "true" || $CLEAN_REVIEW_APPS_DAYS when: never - if: '$CI_PIPELINE_SOURCE == "schedule"' when: manual allow_failure: true .rules_chores: rules: - if: '$CLEAN_REVIEW_APPS_DAYS' when: never - if: '$CI_PIPELINE_SOURCE == "schedule" && $CHORES_PIPELINE == "true"' when: manual allow_failure: true .rules_site_tests: rules: - if: $CHORES_PIPELINE == "true" || $CLEAN_REVIEW_APPS_DAYS when: never # Don't run site tests for review apps. - if: '$CI_PIPELINE_SOURCE == "pipeline" || $CI_PIPELINE_SOURCE == "trigger"' when: never - if: '$CI_PIPELINE_SOURCE == "schedule"' - if: '$CI_MERGE_REQUEST_ID' - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' - if: '$CI_COMMIT_BRANCH == "main"' - if: '$CI_COMMIT_BRANCH =~ /^\d{1,2}\.\d{1,2}$/' .rules_prod: rules: - if: $CHORES_PIPELINE == "true" || $CLEAN_REVIEW_APPS_DAYS when: never # Don't deploy to production for trigerred pipelines (usually review apps) - if: '$CI_PIPELINE_SOURCE == "pipeline" || $CI_PIPELINE_SOURCE == "trigger"' when: never - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' - if: '$CI_COMMIT_BRANCH =~ /^\d{1,2}\.\d{1,2}$/' .rules_pages: rules: - if: $CHORES_PIPELINE == "true" || $CLEAN_REVIEW_APPS_DAYS when: never # Don't deploy to production for trigerred pipelines (usually review apps) - if: '$CI_PIPELINE_SOURCE == "pipeline"|| $CI_PIPELINE_SOURCE == "trigger"' when: never - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' - if: '$CI_COMMIT_BRANCH == "main"' .rules_dev: rules: - if: '$CLEAN_REVIEW_APPS_DAYS' when: never - if: '$CI_MERGE_REQUEST_ID' - if: '$CI_PIPELINE_SOURCE == "pipeline" || $CI_PIPELINE_SOURCE == "trigger"' - if: '$CI_COMMIT_BRANCH =~ /docs-preview/' # TODO: Remove once no projects create such branch - if: '$CI_PIPELINE_SOURCE == "schedule" && $CHORES_PIPELINE == "true"' # # Caching keys # .cache_gem: cache: key: files: - Gemfile.lock paths: - vendor/ruby .cache_gem_yarn: cache: key: files: - Gemfile.lock - yarn.lock paths: - vendor/ruby - .yarn-cache/ .cache_yarn: cache: key: files: - yarn.lock paths: - .yarn-cache/ ############################################### # Build the website # ############################################### .build_base: stage: build extends: - .cache_gem_yarn - .retry script: - yarn install --cache-folder .yarn-cache - bundle exec rake setup_git default - bundle exec nanoc compile -VV # Create _redirects for Pages redirects - bundle exec rake redirects # Calculate sizes before and after minifying/gzipping the static files (HTML, CSS, JS) - SIZE_BEFORE=$(du -sh public/ | awk '{print $1}') # Minify the assets of the resulting site - ./scripts/minify-assets.sh ./ public/ - SIZE_AFTER_MINIFY=$(du -sh public/ | awk '{print $1}') # Use gzip to compress static content for faster web serving # https://docs.gitlab.com/ee/user/project/pages/introduction.html#serving-compressed-assets - find public/ -type f \( -iname "*.html" -o -iname "*.js" -o -iname "*.css" -o -iname "*.svg" \) -exec gzip --keep --best --force --verbose {} \; - SIZE_AFTER_GZIP=$(du -sh public/ | awk '{print $1}') # Print size results - echo "Minify and compress the static assets (HTML, CSS, JS)" - echo - echo -e "Size before minifying and gzipping ..... $SIZE_BEFORE\nSize after minifying ................... $SIZE_AFTER_MINIFY\nSize after adding gzipped versions ..... $SIZE_AFTER_GZIP" artifacts: paths: - public expire_in: 1d # # Compile only on the default and stable branches # compile_prod: extends: - .rules_prod - .build_base variables: NANOC_ENV: 'production' # # Compile on all branches except the default branch # compile_dev: extends: - .rules_dev - .build_base ############################################### # Test the website # ############################################### # # Test the links in the global nav # test_global_nav_links: image: registry.gitlab.com/gitlab-org/gitlab-docs/lint-html:alpine-3.16-ruby-2.7.6-0bc327a4 extends: - .rules_site_tests - .cache_gem stage: test script: # Only check files in top-level directories. This means a much faster and less repetitive check of global navigation links. - echo " all:" >> nanoc.yaml - echo " exclude_files:" >> nanoc.yaml - echo " - '\/(ee|runner|omnibus|charts|operator)\/.*\/.*'" >> nanoc.yaml - "parallel time bundle exec nanoc check ::: internal_links internal_anchors" # # Check the redirect file for duplicates # check_duplicate_redirects: image: busybox extends: - .rules_site_tests needs: [] before_script: [] stage: test script: - grep -Ir " - from:" content/_data/redirects.yaml | sort | uniq -d | tee output.txt - exit $(cat output.txt | wc -l) # # Check for index.html in navigation.yaml # check_index_html: image: busybox extends: - .rules_site_tests needs: [] before_script: [] stage: test script: # Check for index.html in navigation.yaml and write the output in output.txt - grep -Ir "/index.html" content/_data/navigation.yaml | sed -e '/^#/d' | tee output.txt - | echo "***************************************************************************" echo "* If this job failed, it is because a navbar entry is using 'index.html'. *" echo "* Link to just '/' instead, For example 'ee/user/' *" echo "***************************************************************************" - exit $(cat output.txt | wc -l) # # Run rspec tests # rspec: extends: - .rules_site_tests - .cache_gem_yarn needs: [] stage: test script: - yarn install --cache-folder .yarn-cache - make rspec-tests # # Run JavaScript tests # jest: extends: - .rules_site_tests - .cache_yarn - .yarn needs: [] stage: test script: - make jest-tests # # Lint JavaScript # js_lint: extends: - .rules_site_tests - .cache_yarn - .yarn needs: [] stage: test script: - make eslint-tests - make prettier-tests # # Lint SCSS # stylelint: extends: - .rules_site_tests - .cache_yarn - .yarn needs: [] stage: test script: - make stylelint-tests # # Yamllint of *.yml for .gitlab-ci.yml. # This uses rules from project root `.yamllint`. # yaml_lint: extends: - .rules_site_tests needs: [] stage: test image: sdesbure/yamllint:latest before_script: [] script: - yamllint .gitlab-ci.yml content/_data # # Run markdownlint tests # markdownlint: extends: - .rules_site_tests - .cache_yarn - .yarn needs: [] stage: test script: - make markdownlint-tests # # Check for broken external links # test_external_links: extends: - .cache_gem - .rules_chores stage: test script: - bundle exec nanoc check external_links # # Run markdownlint to find EOL whitespace to clean up # test_EOL_whitespace: extends: - .rules_chores - .cache_gem stage: test image: registry.gitlab.com/gitlab-org/gitlab-docs/lint-html:alpine-3.16-ruby-2.7.6-0bc327a4 needs: [] before_script: [] dependencies: [] script: - yarn global add markdownlint-cli@$MARKDOWNLINT_VERSION - apk add jq - bundle config set --local deployment true # Install dependencies into ./vendor/ruby - bundle install - bundle exec rake setup_git default - markdownlint --config tasks/whitespace_task.yml '../gitlab/doc/**/*.md' '../gitlab-runner/doc/**/*.md' '../omnibus-gitlab/doc/**/*.md' '../charts-gitlab/doc/**/*.md' '../gitlab-operator/doc/**/*.md' test_unlinked_images: extends: - .rules_chores - .cache_gem stage: test image: registry.gitlab.com/gitlab-org/gitlab-docs/lint-html:alpine-3.16-ruby-2.7.6-0bc327a4 needs: [] before_script: [] dependencies: [] script: - apk add jq - bundle config set --local deployment true # Install dependencies into ./vendor/ruby - bundle install - bundle exec rake setup_git default - cp tasks/unlinked-images.sh ../gitlab/unlinked-images.sh - cp tasks/unlinked-images.sh ../charts-gitlab/unlinked-images.sh - cp tasks/unlinked-images.sh ../omnibus-gitlab/unlinked-images.sh - cp tasks/unlinked-images.sh ../gitlab-runner/unlinked-images.sh - cd ../omnibus-gitlab - ./unlinked-images.sh doc - cd ../charts-gitlab - ./unlinked-images.sh doc - cd ../gitlab-runner - ./unlinked-images.sh docs - cd ../gitlab - ./unlinked-images.sh doc lint_dockerfiles: extends: - .rules_site_tests image: hadolint/hadolint:latest-alpine needs: [] before_script: [] dependencies: [] script: - hadolint latest.Dockerfile .gitpod.Dockerfile **/*.Dockerfile ############################################### # Review Apps # ############################################### # # Deploy the Review App on a dev server # review: stage: deploy extends: - .retry variables: GIT_STRATEGY: none needs: - compile_dev before_script: [] cache: {} script: # Rsync to the Pages dir - rsync -av --delete public /srv/nginx/pages/$CI_COMMIT_REF_SLUG$REVIEW_SLUG # Remove public directory so that the next review app can run in a # clean environment (limitation of the shell executor). - rm -rf public environment: name: review/$CI_COMMIT_REF_SLUG$REVIEW_SLUG url: http://$CI_COMMIT_REF_SLUG$REVIEW_SLUG.$APPS_DOMAIN on_stop: review_stop rules: - if: '$CI_PROJECT_PATH == "gitlab-renovate-forks/gitlab-docs"' when: manual - if: '$CI_PROJECT_PATH !~ /^gitlab-org/' when: never - if: '$CI_MERGE_REQUEST_ID' - if: '$CI_PIPELINE_SOURCE == "pipeline" || $CI_PIPELINE_SOURCE == "trigger"' - if: '$CI_COMMIT_BRANCH =~ /docs-preview/' # TODO: Remove once no projects create such branch tags: - nginx - review-apps # # Stop the Review App # review_stop: stage: deploy extends: - .retry variables: GIT_STRATEGY: none needs: [] artifacts: {} before_script: [] cache: {} script: - rm -rf public /srv/nginx/pages/$CI_COMMIT_REF_SLUG$REVIEW_SLUG environment: name: review/$CI_COMMIT_REF_SLUG$REVIEW_SLUG action: stop rules: - if: '$CI_PROJECT_PATH == "gitlab-renovate-forks/gitlab-docs"' allow_failure: true when: manual - if: '$CI_PROJECT_PATH !~ /^gitlab-org/' when: never - if: '$CI_MERGE_REQUEST_ID || $CI_PIPELINE_SOURCE == "pipeline"|| $CI_PIPELINE_SOURCE == "trigger"' allow_failure: true when: manual # TODO: Remove once no projects create such branch - if: '$CI_COMMIT_BRANCH =~ /docs-preview/' allow_failure: true when: manual tags: - nginx - review-apps # # Clean up review apps and free disk space # clean-pages: stage: deploy variables: GIT_STRATEGY: none needs: [] artifacts: {} before_script: [] cache: {} script: - /home/gitlab-runner/clean-pages ${CLEAN_REVIEW_APPS_DAYS} - df -h rules: - if: '$CI_PIPELINE_SOURCE == "schedule" && $CLEAN_REVIEW_APPS_DAYS' when: manual allow_failure: true tags: - nginx - review-apps # # Clean up stopped review app environments. Done once a month in a scheduled pipeline, # only deletes stopped environments that are over 30 days old. # delete_stopped_environments: image: alpine:latest needs: [] before_script: [] dependencies: [] rules: - if: $CI_PIPELINE_SOURCE == "schedule" && $PIPELINE_SCHEDULE_TIMING == "monthly" stage: test script: - apk --update add curl jq - curl --request DELETE "https://gitlab.com/api/v4/projects/1794617/environments/review_apps?dry_run=false&private_token=$DELETE_ENVIRONMENTS_TOKEN" | jq ############################################### # GitLab Pages (production) # ############################################### # # Deploy to production with GitLab Pages # pages: resource_group: pages extends: - .rules_pages - .retry image: registry.gitlab.com/gitlab-org/gitlab-docs:latest stage: deploy variables: GIT_STRATEGY: none before_script: [] cache: {} environment: name: production url: https://docs.gitlab.com # We are using dependencies, because we do not want to # re-deploy if the previous stages failed. dependencies: - compile_prod # Contains the public directory script: # # We want to use the artifacts of the compile_prod job as # the latest docs deployment, and the other versions are # taken from /usr/share/nginx/html which are included in # the image we pull from. # - mv /usr/share/nginx/html/1* public/ artifacts: paths: - public expire_in: 1d ############################################### # Security # ############################################### # # Override Security scanning defaults to ensure specific scanners work in this pipeline # .security-scanning-overrides: stage: security dependencies: [] needs: [] before_script: [] # # Dependency scanning job overrides # gemnasium-dependency_scanning: extends: - .ds-analyzer - .security-scanning-overrides # # SAST job overrides # brakeman-sast: extends: - .sast-analyzer - .security-scanning-overrides nodejs-scan-sast: extends: - .sast-analyzer - .security-scanning-overrides semgrep-sast: extends: - .sast-analyzer - .security-scanning-overrides # eslint-sast scans html too, so run after html files are generated eslint-sast: stage: security before_script: []