# cspell:ignore codequality Micheh micheh webide updatedb stylelintrc unshallow ################ # Drupal GitLabCI template. # # Based off GitlabCI templates project: https://git.drupalcode.org/project/gitlab_templates # Guide: https://www.drupal.org/docs/develop/git/using-gitlab-to-contribute-to-drupal/gitlab-ci # # With thanks to: # - The GitLab Acceleration Initiative participants # - DrupalSpoons ################ ################ # Workflow # # Define conditions for when the pipeline will run. # For example: # * On commit # * On merge request # * On manual trigger # * etc. # https://docs.gitlab.com/ee/ci/jobs/job_control.html#specify-when-jobs-run-with-rules # # Pipelines can also be configured to run on a schedule,though they still must meet the conditions defined in Workflow and Rules. This can be used, for example, to do nightly regression testing: # https://gitlab.com/help/ci/pipelines/schedules ################ workflow: rules: # These 3 rules from https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Workflows/MergeRequest-Pipelines.gitlab-ci.yml # Run on merge requests - if: $CI_PIPELINE_SOURCE == 'merge_request_event' # Run when called from an upstream pipeline https://docs.gitlab.com/ee/ci/pipelines/downstream_pipelines.html?tab=Multi-project+pipeline#use-rules-to-control-downstream-pipeline-jobs - if: $CI_PIPELINE_SOURCE == 'pipeline' # Run when called from a parent pipeline (e.g. updated dependencies job) - if: $CI_PIPELINE_SOURCE == 'parent_pipeline' # Run on commits. - if: $CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_ROOT_NAMESPACE == "project" # The last rule above blocks manual and scheduled pipelines on non-default branch. The rule below allows them: - if: $CI_PIPELINE_SOURCE == "schedule" && $CI_PROJECT_ROOT_NAMESPACE == "project" # Run if triggered from Web using 'Run Pipelines' - if: $CI_PIPELINE_SOURCE == "web" # Run if triggered from WebIDE - if: $CI_PIPELINE_SOURCE == "webide" ################ # Variables # # Overriding variables # - To override one or more of these variables, simply declare your own variables keyword. # - Keywords declared directly in .gitlab-ci.yml take precedence over include files. # - Documentation: https://docs.gitlab.com/ee/ci/variables/ # - Predefined variables: https://docs.gitlab.com/ee/ci/variables/predefined_variables.html # ################ variables: _CONFIG_DOCKERHUB_ROOT: "drupalci" # Let composer know what self.version means. COMPOSER_ROOT_VERSION: "${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}${CI_COMMIT_BRANCH}-dev" COMPOSER_ALLOW_SUPERUSER: 1 CONCURRENCY: 24 GIT_DEPTH: "50" PARENT_PIPELINE_ID: $CI_PIPELINE_ID _TARGET_PHP: "8.3" ############# # Stages # ############# stages: - πŸͺ„ Lint - πŸ—œοΈ Test ############# # Defaults # ############# default: interruptible: true retry: max: 2 when: - unknown_failure - api_failure - stuck_or_timeout_failure - runner_system_failure - scheduler_failure image: name: $_CONFIG_DOCKERHUB_ROOT/php-$_TARGET_PHP-apache:production ############# # Templates # ############# .with-composer: &with-composer needs: - '🧹 PHP Coding standards (PHPCS)' .default-job-settings-lint: &default-job-settings-lint rules: - if: $PERFORMANCE_TEST != "1" ################ # Stages # # Each job is assigned to a stage, defining the order in which the jobs are executed. # Jobs in the same stage run in parallel. # # If all jobs in a stage succeed, the pipeline will proceed to the next stage. # If any job in the stage fails, the pipeline will exit early. ################ .default-stage: &default-stage stage: πŸ—œοΈ Test trigger: # Rely on the status of the child pipeline. strategy: depend include: - local: .gitlab-ci/pipeline.yml .run-on-commit: &run-on-commit rules: - if: $CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_ROOT_NAMESPACE == "project" allow_failure: true .run-daily: &run-daily rules: - if: $CI_PIPELINE_SOURCE == "schedule" && $CI_PROJECT_ROOT_NAMESPACE == "project" && $DAILY_TEST == "1" allow_failure: true .run-on-mr: &run-on-mr rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" when: manual allow_failure: true # Default configuration. 'DEFAULT: PHP 8.3 MySQL 8': <<: *default-stage variables: _TARGET_PHP: "8.3" _TARGET_DB: "mysql-8" PERFORMANCE_TEST: $PERFORMANCE_TEST OTEL_COLLECTOR: $OTEL_COLLECTOR # Run on MR, schedule, push, parent pipeline and performance test. rules: - if: $CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_ROOT_NAMESPACE == "project" allow_failure: true - if: $CI_PIPELINE_SOURCE == "schedule" && $CI_PROJECT_ROOT_NAMESPACE == "project" && $DAILY_TEST == "1" allow_failure: true - if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_PIPELINE_SOURCE == "parent_pipeline" - if: $PERFORMANCE_TEST == "1" # Re-run the pipeline, but with Composer updates. 'DEFAULT: Updated dependencies (PHP 8.3 MySQL 8)': <<: *default-stage # Run daily and allow manual runs on MRs. rules: - if: $CI_PIPELINE_SOURCE == "schedule" && $CI_PROJECT_ROOT_NAMESPACE == "project" && $DAILY_TEST == "1" allow_failure: true - if: $CI_PIPELINE_SOURCE == "merge_request_event" when: manual allow_failure: true variables: COMPOSER_UPDATE: "1" trigger: include: .gitlab-ci.yml # Special job for MRs for test-only checks. 'DEFAULT: Test-only (PHP 8.3 MySQL 8)': <<: [ *default-stage, *with-composer ] when: manual allow_failure: true variables: _TARGET_PHP: "8.3" _TARGET_DB: "mysql-8" rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" trigger: # Rely on the status of the child pipeline. strategy: depend include: - local: .gitlab-ci/pipeline-test-only.yml # Main listing of jobs. # All of these are available on Merge Requests and also work as base jobs for # on-commit and daily jobs to extend from. 'PHP 8.1 MySQL 5.7': <<: [ *default-stage, *run-on-mr ] variables: _TARGET_PHP: "8.1" _TARGET_DB: "mysql-5.7" 'PHP 8.2 MySQL 8': <<: [ *default-stage, *run-on-mr ] variables: _TARGET_PHP: "8.2" _TARGET_DB: "mysql-8" 'PHP 8.2 PostgreSQL 14.1': <<: [ *default-stage, *run-on-mr ] variables: _TARGET_PHP: "8.2" _TARGET_DB: "pgsql-14.1" 'PHP 8.2 PostgreSQL 15': <<: [ *default-stage, *run-on-mr ] variables: _TARGET_PHP: "8.2" _TARGET_DB: "pgsql-15" 'PHP 8.2 PostgreSQL 16': <<: [ *default-stage, *run-on-mr ] variables: _TARGET_PHP: "8.2" _TARGET_DB: "pgsql-16" 'PHP 8.2 SQLite 3': <<: [ *default-stage, *run-on-mr ] variables: _TARGET_PHP: "8.2" _TARGET_DB: "sqlite-3" 'PHP 8.3 PostgreSQL 16': <<: [ *default-stage, *run-on-mr ] variables: _TARGET_PHP: "8.3" _TARGET_DB: "pgsql-16" 'PHP 8.3 SQLite 3': <<: [ *default-stage, *run-on-mr ] variables: _TARGET_PHP: "8.3" _TARGET_DB: "sqlite-3" 'PHP 8.1 MariaDB 10.3.22': <<: [ *default-stage, *run-on-mr ] variables: _TARGET_PHP: "8.1" _TARGET_DB: "mariadb-10.3.22" 'PHP 8.1 PostgreSQL 14.1': <<: [ *default-stage, *run-on-mr ] variables: _TARGET_PHP: "8.1" _TARGET_DB: "pgsql-14.1" 'PHP 8.1 SQLite 3': <<: [ *default-stage, *run-on-mr ] variables: _TARGET_PHP: "8.1" _TARGET_DB: "sqlite-3" # Jobs running on commits. # The value set in the "needs" property will determine the order in the sequence. '[Commit] PHP 8.1 MySQL 5.7': extends: 'PHP 8.1 MySQL 5.7' needs: [ 'DEFAULT: PHP 8.3 MySQL 8' ] <<: [ *run-on-commit ] '[Commit] PHP 8.2 PostgreSQL 16': extends: 'PHP 8.2 PostgreSQL 16' needs: [ '[Commit] PHP 8.1 MySQL 5.7' ] <<: [ *run-on-commit ] '[Commit] PHP 8.2 SQLite 3': extends: 'PHP 8.2 SQLite 3' needs: [ '[Commit] PHP 8.2 PostgreSQL 16' ] <<: [ *run-on-commit ] # Jobs running daily. # The value set in the "needs" property will determine the order in the sequence. '[Daily] PHP 8.2 PostgreSQL 14.1': extends: 'PHP 8.2 PostgreSQL 14.1' needs: [ 'DEFAULT: PHP 8.3 MySQL 8' ] <<: [ *run-daily ] '[Daily] PHP 8.2 PostgreSQL 15': extends: 'PHP 8.2 PostgreSQL 15' needs: [ '[Daily] PHP 8.2 PostgreSQL 14.1' ] <<: [ *run-daily ] '[Daily] PHP 8.3 PostgreSQL 16': extends: 'PHP 8.3 PostgreSQL 16' needs: [ '[Daily] PHP 8.2 PostgreSQL 15' ] <<: [ *run-daily ] '[Daily] PHP 8.3 SQLite 3': extends: 'PHP 8.3 SQLite 3' needs: [ '[Daily] PHP 8.3 PostgreSQL 16' ] <<: [ *run-daily ] '[Daily] PHP 8.1 MariaDB 10.3.22': extends: 'PHP 8.1 MariaDB 10.3.22' needs: [ '[Daily] PHP 8.3 SQLite 3' ] <<: [ *run-daily ] '[Daily] PHP 8.1 PostgreSQL 14.1': extends: 'PHP 8.1 PostgreSQL 14.1' needs: [ '[Daily] PHP 8.1 MariaDB 10.3.22' ] <<: [ *run-daily ] '[Daily] PHP 8.1 SQLite 3': extends: 'PHP 8.1 SQLite 3' needs: [ '[Daily] PHP 8.1 PostgreSQL 14.1' ] <<: [ *run-daily ] ################ # Lint Jobs ################ '🧹 PHP Static Analysis (phpstan)': <<: [ *default-job-settings-lint ] stage: πŸͺ„ Lint variables: KUBERNETES_CPU_REQUEST: "16" script: - composer validate - composer install --optimize-autoloader - if [ -n "$COMPOSER_UPDATE" ]; then composer update --optimize-autoloader; composer outdated; fi - vendor/bin/phpstan --version # Rely on PHPStan caching to execute analysis multiple times without performance drawback. # Output a copy in junit. - php vendor/bin/phpstan analyze --configuration=./core/phpstan.neon.dist --error-format=gitlab > phpstan-quality-report.json || EXIT_CODE=$? - php vendor/bin/phpstan analyze --configuration=./core/phpstan.neon.dist --no-progress --error-format=junit > phpstan-junit.xml || true - | if [ -n "$EXIT_CODE" ]; then # Output a copy in plain text for human logs. php vendor/bin/phpstan analyze --configuration=./core/phpstan.neon.dist --no-progress || true # Generate a new baseline. echo "Generating an PHPStan baseline file (available as job artifact)." php vendor/bin/phpstan analyze --configuration=./core/phpstan.neon.dist --no-progress --generate-baseline=./core/.phpstan-baseline.php || true exit $EXIT_CODE fi artifacts: reports: codequality: phpstan-quality-report.json junit: phpstan-junit.xml # Only store the baseline if the job fails. when: on_failure paths: - core/.phpstan-baseline.php '🧹 PHP Coding standards (PHPCS)': <<: [ *default-job-settings-lint ] stage: πŸͺ„ Lint variables: KUBERNETES_CPU_REQUEST: "16" script: - composer validate - composer install --optimize-autoloader - if [ -n "$COMPOSER_UPDATE" ]; then composer update --optimize-autoloader; composer outdated; fi - vendor/bin/phpcs --version - composer phpcs -- -s --report-full --report-summary --report-\\Micheh\\PhpCodeSniffer\\Report\\Gitlab=phpcs-quality-report.json artifacts: expire_in: 1 week expose_as: 'web-vendor' paths: - vendor/ reports: codequality: phpcs-quality-report.json '🧹 JavaScript linting (eslint)': stage: πŸͺ„ Lint variables: KUBERNETES_CPU_REQUEST: "2" # Run on push, or on MRs if CSS files have changed, or manually. rules: - if: $CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_ROOT_NAMESPACE == "project" - if: $CI_PIPELINE_SOURCE == "merge_request_event" changes: - core/.eslint* - core/.prettier* - core/package.json - core/yarn.lock - "**/*.js" - "**/*.yml" - when: manual allow_failure: true script: - cd core - corepack enable - yarn install - yarn run -s check:ckeditor5 - yarn run -s lint:core-js-passing --format gitlab artifacts: reports: codequality: eslint-quality-report.json '🧹 CSS linting (stylelint)': stage: πŸͺ„ Lint variables: KUBERNETES_CPU_REQUEST: "2" # Run on push, or on MRs if CSS files have changed, or manually. rules: - if: $CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_ROOT_NAMESPACE == "project" - if: $CI_PIPELINE_SOURCE == "merge_request_event" changes: - core/.stylelintrc.json - core/.prettier* - core/package.json - core/yarn.lock - "**/*.css" - when: manual allow_failure: true script: - corepack enable - cd core - yarn install - yarn run build:css --check - yarn run lint:css --color --custom-formatter=node_modules/stylelint-formatter-gitlab artifacts: reports: codequality: stylelint-quality-report.json 'πŸ“” Spell-checking': <<: [ *default-job-settings-lint ] stage: πŸͺ„ Lint variables: KUBERNETES_CPU_REQUEST: "2" script: - if [ -n "$CI_MERGE_REQUEST_TARGET_BRANCH_SHA" ]; then echo "HEAD is $(git rev-parse HEAD). \$CI_MERGE_REQUEST_TARGET_BRANCH_SHA is ${CI_MERGE_REQUEST_TARGET_BRANCH_SHA}"; else echo "HEAD is $(git rev-parse HEAD). \$CI_MERGE_REQUEST_DIFF_BASE_SHA is ${CI_MERGE_REQUEST_DIFF_BASE_SHA}"; fi; - cd core - corepack enable - yarn install - git diff ${CI_MERGE_REQUEST_TARGET_BRANCH_SHA:-$CI_MERGE_REQUEST_DIFF_BASE_SHA} --name-only 2>1 > /dev/null || (echo "Warning, cannot find changed files, converting to full clone." & (git fetch --unshallow --quiet && echo "Fetch successful.")) - git diff ${CI_MERGE_REQUEST_TARGET_BRANCH_SHA:-$CI_MERGE_REQUEST_DIFF_BASE_SHA} --name-only | sed "s_^_../_" | yarn run -s spellcheck:core --no-must-find-files --file-list stdin cache: key: files: - ./core/package.json - ./core/yarn.lock paths: - ./core/node_modules artifacts: expire_in: 1 week expose_as: 'yarn-vendor' paths: - core/node_modules/ 'πŸ“” Validatable config': <<: [ *default-job-settings-lint ] stage: πŸͺ„ Lint variables: KUBERNETES_CPU_REQUEST: "2" # Run on MRs if config schema files have changed, or manually. rules: - if: $PERFORMANCE_TEST != "1" - if: $CI_PIPELINE_SOURCE == "merge_request_event" changes: - "**/config/schema/*.schema.yml" # Modules may alter config schema using hook_config_schema_info_alter(). - "**/*.module" - when: manual allow_failure: true artifacts: expire_in: 1 week expose_as: 'validatable-config' paths: - HEAD.json - MR.json # This job must pass, but must also not disrupt Drupal core's CI if dependencies are not core-compatible. allow_failure: exit_codes: # `composer require …` fails (implies no version available compatible with Drupal core) - 100 # `drush pm:install config_inspector …` fails (implies failure during module installation) - 101 # Temporarily allow this to fail as there's are bugs with adding/removing/modifying config schemas. - 1 script: # Revert back to the tip of the branch this MR started from. - git checkout -f $CI_MERGE_REQUEST_DIFF_BASE_SHA # Composer-install Drush & the Config Inspector module. - composer require drush/drush drupal/config_inspector || exit 100 # Install Drupal's Standard install profile + all core modules + the config inspector module. - php core/scripts/drupal install standard - ls core/modules | grep -v help_topics | xargs vendor/bin/drush pm:install --yes - vendor/bin/drush pm:install config_inspector --yes --quiet || exit 101 # Compute statistics for coverage of validatable config for HEAD. - vendor/bin/drush config:inspect --statistics > HEAD.json # Return to the MR commit being tested, conditionally install updates, always rebuild the container. - git checkout -f $CI_COMMIT_SHA - git diff $CI_MERGE_REQUEST_DIFF_BASE_SHA $CI_COMMIT_SHA --name-only | grep -q '.install$\|.post_update\.php$' && echo 'πŸ€– Installing DB updates…' && vendor/bin/drush updatedb --yes --quiet - vendor/bin/drush cr --quiet # Compute statistics for coverage of validatable config for MR. - vendor/bin/drush config:inspect --statistics > MR.json # Output diff, but never fail the job. - diff -u HEAD.json MR.json || true # Determine if this increased or decreased coverage. Fail the job if it is worse. All the # percentages must be equal or higher, with the exception of `typesInUse`. - | php -r ' $head = json_decode(file_get_contents("HEAD.json"), TRUE)["assessment"]; $mr = json_decode(file_get_contents("MR.json"), TRUE)["assessment"]; unset($head["_description"], $head["typesInUse"], $mr["_description"], $mr["typesInUse"]); $impact = array_map(fn (float $h, float $m) => $m-$h, $head, $mr); exit((int) (min($impact) < 0)); '