# GitHub Actions configuration file. # # This configuration file uses a custom "container" executor to run the # Docker stack to speed up the build process. #; #; Comments starting with '#;<' and '#;>' are internal Vortex comments #; and will be removed during installation or update of Vortex. name: Database, Build, Test and Deploy on: push: # Pushes to the following branches will trigger the workflow. branches: - production - main - master - develop - release/** - hotfix/** - project/** #;< VORTEX_DEV - '*.x' #;> VORTEX_DEV # Pushes of tags will also trigger the workflow. tags: - '*' # Pull requests to the following branches will trigger the workflow. pull_request: types: - opened - synchronize - reopened branches: - production - main - master - develop - release/** - hotfix/** - feature/** - bugfix/** - project/** #;< VORTEX_DEV - '*.x' #;> VORTEX_DEV workflow_dispatch: inputs: deploy_target: type: string description: 'Deploy target: "" or "PR-"' required: false default: '' override_db: type: boolean description: 'Override existing database' required: false default: false enable_terminal: type: boolean description: 'Enable terminal session for CI jobs' required: false default: false #;< !PROVISION_TYPE_PROFILE schedule: - cron: '0 18 * * *' #;> !PROVISION_TYPE_PROFILE defaults: run: shell: bash # Workaround for the Actions runner creating /root/.docker/config.json # with permissions that prevent the container from reading it. # https://github.com/actions/runner/issues/863 env: DOCKER_CONFIG: /tmp/.docker permissions: contents: read jobs: lint: runs-on: ubuntu-latest #;< !PROVISION_TYPE_PROFILE if: ${{ !inputs.deploy_target && github.event_name != 'schedule' && (github.event_name == 'push' || !startsWith(github.head_ref, 'project/')) }} #;> !PROVISION_TYPE_PROFILE container: # https://hub.docker.com/r/drevops/ci-runner image: drevops/ci-runner:26.10.0@sha256:36db4ee12da3372c86966fccd543e49c9470bca12e86eb5d4aad7904dd84a0dd env: PACKAGE_TOKEN: ${{ secrets.PACKAGE_TOKEN }} VORTEX_CONTAINER_REGISTRY_USER: ${{ secrets.VORTEX_CONTAINER_REGISTRY_USER }} VORTEX_CONTAINER_REGISTRY_PASS: ${{ secrets.VORTEX_CONTAINER_REGISTRY_PASS }} TZ: ${{ vars.TZ || 'UTC' }} TERM: xterm-256color VORTEX_DEBUG: ${{ vars.VORTEX_DEBUG }} steps: - name: Preserve $HOME set in the container run: echo HOME=/root >> "$GITHUB_ENV" # https://github.com/actions/runner/issues/863 - name: Checkout code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: persist-credentials: false - name: Fix Git ownership permissions run: git config --global --add safe.directory "$GITHUB_WORKSPACE" - name: Process the codebase to run in CI run: find . -name "docker-compose.yml" -print0 | xargs -0 -I {} sh -c "sed -i -e '/###/d' {} && sed -i -e 's/##//' {}" - name: Load environment variables from .env run: t=$(mktemp) && export -p >"${t}" && set -a && . ./.env && set +a && . "${t}" && env >> "$GITHUB_ENV" - name: Validate Composer configuration run: composer validate --strict continue-on-error: ${{ vars.VORTEX_CI_COMPOSER_VALIDATE_IGNORE_FAILURE == '1' }} - name: Install Vortex tooling run: ./scripts/vortex-tooling.sh - name: Login to container registry run: ./vendor/bin/vortex-login-container-registry #;< TOOL_HADOLINT - name: Lint Dockerfiles with Hadolint run: | find .docker \( -name 'Dockerfile' -o -name '*.dockerfile' \) | while read -r file; do echo "Linting ${file}" && cat "${file}" | docker run --rm -i hadolint/hadolint:v2.15.1 done continue-on-error: ${{ vars.VORTEX_CI_HADOLINT_IGNORE_FAILURE == '1' }} #;> TOOL_HADOLINT #;< TOOL_DCLINT - name: Lint Docker Compose files with DCLint run: docker run --rm --volumes-from "${HOSTNAME}" -w "${PWD}" zavoloklom/dclint:3.1.0 . continue-on-error: ${{ vars.VORTEX_CI_DCLINT_IGNORE_FAILURE == '1' }} #;> TOOL_DCLINT - name: Build stack run: docker compose up --no-deps --detach cli - name: Install development dependencies run: | docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c " \ if [ -n \"\${PACKAGE_TOKEN:-}\" ]; then composer config --global --auth github-oauth.github.com \"\${PACKAGE_TOKEN}\"; fi && \ COMPOSER_MEMORY_LIMIT=-1 composer --ansi install --prefer-dist" #;< TOOL_ESLINT_STYLELINT docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c "npm ci" #;> TOOL_ESLINT_STYLELINT - name: Validate Composer configuration is normalized run: docker compose exec -T cli composer normalize --dry-run continue-on-error: ${{ vars.VORTEX_CI_COMPOSER_NORMALIZE_IGNORE_FAILURE == '1' }} #;< TOOL_PHPCS - name: Lint code with PHPCS run: docker compose exec -T cli vendor/bin/phpcs continue-on-error: ${{ vars.VORTEX_CI_PHPCS_IGNORE_FAILURE == '1' }} #;> TOOL_PHPCS #;< TOOL_PHPSTAN - name: Lint code with PHPStan run: docker compose exec -T cli vendor/bin/phpstan continue-on-error: ${{ vars.VORTEX_CI_PHPSTAN_IGNORE_FAILURE == '1' }} #;> TOOL_PHPSTAN #;< TOOL_RECTOR - name: Lint code with Rector run: docker compose exec -T cli vendor/bin/rector --dry-run continue-on-error: ${{ vars.VORTEX_CI_RECTOR_IGNORE_FAILURE == '1' }} #;> TOOL_RECTOR #;< TOOL_TWIG_CS_FIXER - name: Lint code with Twig CS Fixer run: docker compose exec -T cli vendor/bin/twig-cs-fixer continue-on-error: ${{ vars.VORTEX_CI_TWIG_CS_FIXER_IGNORE_FAILURE == '1' }} #;> TOOL_TWIG_CS_FIXER #;< TOOL_BEHAT - name: Lint code with Gherkin Lint run: docker compose exec -T cli vendor/bin/gherkinlint lint tests/behat/features continue-on-error: ${{ vars.VORTEX_CI_GHERKIN_LINT_IGNORE_FAILURE == '1' }} #;> TOOL_BEHAT #;< TOOL_ESLINT_STYLELINT - name: Lint module code with NodeJS linters run: docker compose exec -T cli bash -c "npm run lint" continue-on-error: ${{ vars.VORTEX_CI_NODEJS_LINT_IGNORE_FAILURE == '1' }} #;< DRUPAL_THEME - name: Lint theme code with NodeJS linters if: ${{ vars.VORTEX_FRONTEND_BUILD_SKIP != '1' }} run: docker compose exec -T cli bash -c "npm --prefix=\${WEBROOT}/themes/custom/\${DRUPAL_THEME} run lint" continue-on-error: ${{ vars.VORTEX_CI_NODEJS_LINT_IGNORE_FAILURE == '1' }} #;> DRUPAL_THEME #;> TOOL_ESLINT_STYLELINT #;< !PROVISION_TYPE_PROFILE database: runs-on: ubuntu-latest if: ${{ !inputs.deploy_target && (github.event_name == 'push' || !startsWith(github.head_ref, 'project/')) }} container: # https://hub.docker.com/r/drevops/ci-runner image: drevops/ci-runner:26.10.0@sha256:36db4ee12da3372c86966fccd543e49c9470bca12e86eb5d4aad7904dd84a0dd env: PACKAGE_TOKEN: ${{ secrets.PACKAGE_TOKEN }} VORTEX_CONTAINER_REGISTRY_USER: ${{ secrets.VORTEX_CONTAINER_REGISTRY_USER }} VORTEX_CONTAINER_REGISTRY_PASS: ${{ secrets.VORTEX_CONTAINER_REGISTRY_PASS }} TZ: ${{ vars.TZ || 'UTC' }} TERM: xterm-256color VORTEX_SSH_DISABLE_STRICT_HOST_KEY_CHECKING: "1" VORTEX_SSH_REMOVE_ALL_KEYS: "1" VORTEX_DEBUG: ${{ vars.VORTEX_DEBUG }} # How often to refresh the cache of the DB dump. Refer to `date` command. VORTEX_CI_DB_CACHE_TIMESTAMP: +%Y%m%d # Use previous database caches on this branch as a fallback if the above cache # does not match (for example, the cache is available only from the previous # day). If "no" is set, the cache will be rebuilt from scratch. VORTEX_CI_DB_CACHE_FALLBACK: "yes" # Which branch to use as a source of DB caches. VORTEX_CI_DB_CACHE_BRANCH: "develop" steps: # Frees disk space by removing preinstalled toolchains unused by this project. Steps run in a container, so the host is reached via the Docker socket. - name: Free up disk space on the runner if: ${{ vars.VORTEX_CI_FREE_DISK_SPACE == '1' }} run: | set -- /host/usr/local/.ghcup /host/usr/share/swift /host/usr/local/share/powershell /host/usr/share/dotnet /host/opt/hostedtoolcache/CodeQL # set -- "$@" /host/usr/local/lib/android # Removes about 10GB more, but takes about 50s. Uncomment to enable. job_image=$(docker inspect --format '{{.Config.Image}}' "${HOSTNAME}" 2>/dev/null || echo alpine:3) docker run --rm -v /:/host "${job_image}" rm -rf "$@" || true - name: Report disk usage run: df -h - name: Preserve $HOME set in the container run: echo HOME=/root >> "$GITHUB_ENV" # https://github.com/actions/runner/issues/863 - name: Checkout code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: # Do not keep SSH credentials after checkout to allow adding custom SSH keys. persist-credentials: false - name: Fix Git ownership permissions run: git config --global --add safe.directory "$GITHUB_WORKSPACE" - name: Add SSH private key used to fetch database if: ${{ env.VORTEX_FETCH_DB_SSH_PRIVATE_KEY != '' }} uses: shimataro/ssh-key-action@87a8f067114a8ce263df83e9ed5c849953548bc3 # v2.8.1 with: key: ${{ secrets.VORTEX_FETCH_DB_SSH_PRIVATE_KEY }} known_hosts: ${{ vars.VORTEX_FETCH_DB_SSH_KNOWN_HOSTS || 'unnecessary' }} env: VORTEX_FETCH_DB_SSH_PRIVATE_KEY: ${{ secrets.VORTEX_FETCH_DB_SSH_PRIVATE_KEY }} - name: Process the codebase to run in CI run: find . -name "docker-compose.yml" -print0 | xargs -0 -I {} sh -c "sed -i -e '/###/d' {} && sed -i -e 's/##//' {}" - name: Load environment variables from .env run: t=$(mktemp) && export -p >"${t}" && set -a && . ./.env && set +a && . "${t}" && env >> "$GITHUB_ENV" - name: Install Vortex tooling run: ./scripts/vortex-tooling.sh - name: Adjust variables for a scheduled run if: github.event_name == 'schedule' run: | { echo "VORTEX_CI_DB_CACHE_FALLBACK=no" echo "VORTEX_FRONTEND_BUILD_SKIP=1" # Import the fetched dump into a clean base image (if database-in-image storage is used). echo "VORTEX_DB_IMAGE_BASE=drevops/mariadb-drupal-data:26.10.0" # Deploy container image (if database-in-image storage is used). echo "VORTEX_EXPORT_DB_CONTAINER_REGISTRY_PUSH_PROCEED=1" } >> "$GITHUB_ENV" - name: Create cache keys files for database caching run: | echo "${VORTEX_CI_DB_CACHE_BRANCH}" | tee db_cache_branch echo "${VORTEX_CI_DB_CACHE_FALLBACK/no/"${GITHUB_RUN_NUMBER}"}" | tee db_cache_fallback date "${VORTEX_CI_DB_CACHE_TIMESTAMP}" | tee db_cache_timestamp echo "yes" | tee db_cache_fallback_yes # Restore DB cache based on the cache strategy set by the cache keys below. # Bump the last segment of the version prefix below to force a cache reset. # Lookup cache based on the default branch and a timestamp. Allows # to use cache from the very first build on the day (sanitized database dump, for example). - name: Restore DB cache uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6 with: path: .data key: v26.10.0-${{ hashFiles('db_cache_branch') }}-${{ hashFiles('db_cache_fallback') }}-${{ hashFiles('db_cache_timestamp') }} # Fallback to caching by default branch name only. Allows to use # cache from the branch build on the previous day. restore-keys: | v26.10.0-${{ hashFiles('db_cache_branch') }}-${{ hashFiles('db_cache_fallback') }}- - name: Fetch DB run: | VORTEX_FETCH_DB_SEMAPHORE=/tmp/fetch-db-fresh ./vendor/bin/vortex-fetch-db echo "db_hash=${{ hashFiles('.data') }}" >> "$GITHUB_ENV" timeout-minutes: 30 #;< DB_FETCH_SOURCE_ACQUIA env: VORTEX_ACQUIA_KEY: ${{ secrets.VORTEX_ACQUIA_KEY }} VORTEX_ACQUIA_SECRET: ${{ secrets.VORTEX_ACQUIA_SECRET }} #;> DB_FETCH_SOURCE_ACQUIA #;< MIGRATION - name: Fetch migration DB run: VORTEX_DB_INDEX=2 ./vendor/bin/vortex-fetch-db #;> MIGRATION - name: Export DB run: | if [ ! -f /tmp/fetch-db-fresh ]; then echo "==> Database fetch semaphore file is missing. DB export will not proceed."; exit 0; fi ./vendor/bin/vortex-login-container-registry # A database built from the base image is empty, so it is # provisioned from the dump file without the image. if [ -n "${VORTEX_DB_IMAGE:-}" ] && [ -n "${VORTEX_DB_IMAGE_BASE:-}" ] && [ "${VORTEX_FETCH_DB_SOURCE:-url}" != "container_registry" ]; then VORTEX_FETCH_DB_SOURCE=container_registry VORTEX_FETCH_DB_CONTAINER_REGISTRY_IMAGE_BASE="${VORTEX_DB_IMAGE_BASE}" ./vendor/bin/vortex-fetch-db provision_opts="VORTEX_DB_IMAGE=" fi docker compose up --detach && sleep 15 # The 'container_registry' source fetches an image without a dump file. if [ -f .data/db.sql ]; then docker compose exec cli mkdir -p .data docker compose --progress quiet cp -L .data/db.sql cli:/app/.data/db.sql fi docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c "VORTEX_PROVISION_POST_OPERATIONS_SKIP=1 ${provision_opts:-} ./vendor/bin/vortex-provision" # Database-in-image storage caches the exported image archive instead of the dump. if [ -n "${VORTEX_DB_IMAGE:-}" ]; then rm -f .data/db.sql; fi ./vendor/bin/vortex-export-db db.sql if [ -z "${VORTEX_DB_IMAGE:-}" ] && docker compose exec -T cli test -f /app/.data/db.sql; then docker compose --progress quiet cp -L cli:/app/.data/db.sql .data/db.sql; fi # 'hashFiles()' runs as the runner user outside the container, so the # root-owned files in '.data' must be readable by others. chmod -R a+rX .data timeout-minutes: 30 # Deploy the freshly-exported database image to the container registry. # No-op unless a fresh DB was fetched (the fetch semaphore), the project # uses database-in-image storage (VORTEX_DB_IMAGE), and deployment was # requested (VORTEX_EXPORT_DB_CONTAINER_REGISTRY_PUSH_PROCEED, in the script). - name: Deploy DB image run: | if [ -z "${VORTEX_DB_IMAGE:-}" ]; then echo "==> Database-in-image storage is not used. DB image deployment will not proceed."; exit 0; fi if [ ! -f /tmp/fetch-db-fresh ]; then echo "==> No freshly fetched database. DB image deployment will not proceed."; exit 0; fi ./vendor/bin/vortex-push-db-image timeout-minutes: 30 # Save cache per default branch and the timestamp. # The cache will not be saved if it already exists. # Note that the cache fallback flag is enabled for this case in order # to save cache even if the fallback is not used when restoring it. - name: Save DB cache uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6 if: env.db_hash != hashFiles('.data') with: path: .data key: v26.10.0-${{ hashFiles('db_cache_branch') }}-${{ hashFiles('db_cache_fallback_yes') }}-${{ hashFiles('db_cache_timestamp') }} #;> !PROVISION_TYPE_PROFILE build: runs-on: ubuntu-latest #;< !PROVISION_TYPE_PROFILE needs: database if: ${{ !inputs.deploy_target && github.event_name != 'schedule' && (github.event_name == 'push' || !startsWith(github.head_ref, 'project/')) }} #;> !PROVISION_TYPE_PROFILE permissions: contents: read checks: write pull-requests: write strategy: # A matrix to run multiple jobs in parallel. matrix: instance: [0, 1] fail-fast: false env: # Which tools run on this instance. Tools that only need to run once are # pinned to the first instance. Add a line for every additional tool and # reference it in that tool's step. # https://www.vortextemplate.com/docs/continuous-integration#test-parallelism VORTEX_CI_RUNNER_INDEX: ${{ strategy.job-index }} VORTEX_CI_RUNNER_TOTAL: ${{ strategy.job-total }} #;< TOOL_JEST VORTEX_CI_IS_JEST_RUNNER: ${{ matrix.instance == 0 || strategy.job-total == 1 }} #;> TOOL_JEST #;< TOOL_PHPUNIT VORTEX_CI_IS_PHPUNIT_RUNNER: ${{ matrix.instance == 0 || strategy.job-total == 1 }} #;> TOOL_PHPUNIT #;< DRUPAL_THEME #;< MODULE_SDC_DEVEL VORTEX_CI_IS_SDC_DEVEL_RUNNER: ${{ matrix.instance == 0 || strategy.job-total == 1 }} #;> MODULE_SDC_DEVEL #;> DRUPAL_THEME #;< TOOL_BEHAT # Runs on every instance, using the `p` profile. VORTEX_CI_IS_BEHAT_RUNNER: true # Subtracted from the instance index to derive the profile number. Raise # it by one for every leading instance that does not run Behat, so the # first Behat instance still selects the `p0` catch-all profile. VORTEX_CI_BEHAT_PROFILE_OFFSET: 0 #;> TOOL_BEHAT container: # https://hub.docker.com/r/drevops/ci-runner image: drevops/ci-runner:26.10.0@sha256:36db4ee12da3372c86966fccd543e49c9470bca12e86eb5d4aad7904dd84a0dd env: PACKAGE_TOKEN: ${{ secrets.PACKAGE_TOKEN }} VORTEX_CONTAINER_REGISTRY_USER: ${{ secrets.VORTEX_CONTAINER_REGISTRY_USER }} VORTEX_CONTAINER_REGISTRY_PASS: ${{ secrets.VORTEX_CONTAINER_REGISTRY_PASS }} TZ: ${{ vars.TZ || 'UTC' }} TERM: xterm-256color # Disable strict host key checking for SSH connections. VORTEX_SSH_DISABLE_STRICT_HOST_KEY_CHECKING: "1" VORTEX_SSH_REMOVE_ALL_KEYS: "1" VORTEX_DEBUG: ${{ vars.VORTEX_DEBUG }} #;< !PROVISION_TYPE_PROFILE # How often to refresh the cache of the DB dump. Refer to `date` command. VORTEX_CI_DB_CACHE_TIMESTAMP: +%Y%m%d # Use previous database caches on this branch as a fallback if the above cache # does not match (for example, the cache is available only from the previous # day). If "no" is set, the cache will be rebuilt from scratch. VORTEX_CI_DB_CACHE_FALLBACK: "yes" # Which branch to use as a source of DB caches. VORTEX_CI_DB_CACHE_BRANCH: "develop" #;> !PROVISION_TYPE_PROFILE steps: # Frees disk space by removing preinstalled toolchains unused by this project. Steps run in a container, so the host is reached via the Docker socket. - name: Free up disk space on the runner if: ${{ vars.VORTEX_CI_FREE_DISK_SPACE == '1' }} run: | set -- /host/usr/local/.ghcup /host/usr/share/swift /host/usr/local/share/powershell /host/usr/share/dotnet /host/opt/hostedtoolcache/CodeQL # set -- "$@" /host/usr/local/lib/android # Removes about 10GB more, but takes about 50s. Uncomment to enable. job_image=$(docker inspect --format '{{.Config.Image}}' "${HOSTNAME}" 2>/dev/null || echo alpine:3) docker run --rm -v /:/host "${job_image}" rm -rf "$@" || true - name: Report disk usage run: df -h - name: Preserve $HOME set in the container run: echo HOME=/root >> "$GITHUB_ENV" # https://github.com/actions/runner/issues/863 - name: Checkout code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: persist-credentials: false - name: Fix Git ownership permissions run: git config --global --add safe.directory "$GITHUB_WORKSPACE" - name: Process the codebase to run in CI run: find . -name "docker-compose.yml" -print0 | xargs -0 -I {} sh -c "sed -i -e '/###/d' {} && sed -i -e 's/##//' {}" - name: Load environment variables from .env run: t=$(mktemp) && export -p >"${t}" && set -a && . ./.env && set +a && . "${t}" && env >> "$GITHUB_ENV" - name: Validate Composer configuration run: composer validate --strict continue-on-error: ${{ vars.VORTEX_CI_COMPOSER_VALIDATE_IGNORE_FAILURE == '1' }} - name: Install Vortex tooling run: ./scripts/vortex-tooling.sh #;< !PROVISION_TYPE_PROFILE - name: Create cache keys files for database caching run: | echo "${VORTEX_CI_DB_CACHE_BRANCH}" | tee db_cache_branch echo "yes" | tee db_cache_fallback_yes date "${VORTEX_CI_DB_CACHE_TIMESTAMP}" | tee db_cache_timestamp - name: Show cache key for database caching run: echo 'v26.10.0-${{ hashFiles('db_cache_branch') }}-${{ hashFiles('db_cache_fallback_yes') }}-${{ hashFiles('db_cache_timestamp') }}' # Restore DB cache based on the cache strategy set by the cache keys below. # Bump the last segment of the version prefix below to force a cache reset. # Lookup cache based on the default branch and a timestamp. Allows # to use cache from the very first build on the day (sanitized database dump, for example). - name: Restore DB cache uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6 with: path: .data fail-on-cache-miss: true # Use cached database from previous builds of this branch. key: v26.10.0-${{ hashFiles('db_cache_branch') }}-${{ hashFiles('db_cache_fallback_yes') }}-${{ hashFiles('db_cache_timestamp') }} restore-keys: | v26.10.0-${{ hashFiles('db_cache_branch') }}-${{ hashFiles('db_cache_fallback_yes') }}- #;> !PROVISION_TYPE_PROFILE - name: Login to container registry run: ./vendor/bin/vortex-login-container-registry - name: Build stack run: docker compose up --detach && docker builder prune --all --force && docker image prune --force - name: Export built codebase if: ${{ matrix.instance == 0 && !startsWith(github.head_ref || github.ref_name, 'deps/') && contains(env.VORTEX_DEPLOY_TYPES, 'artifact') }} run: | mkdir -p "/tmp/workspace/code" "/tmp/artifacts" docker compose --progress quiet cp -L cli:"/app/." "/tmp/workspace/code" tar -C /tmp/workspace -cpf /tmp/artifacts/code.tar code du -sh "/tmp/artifacts" - name: Install development dependencies run: | docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c " \ if [ -n \"\${PACKAGE_TOKEN:-}\" ]; then composer config --global --auth github-oauth.github.com \"\${PACKAGE_TOKEN}\"; fi && \ COMPOSER_MEMORY_LIMIT=-1 composer --ansi install --prefer-dist" #;< TOOL_JEST docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c "npm ci" #;> TOOL_JEST - name: Provision site run: | # Database dumps are removed from the runner once they are inside the # container to avoid holding two copies for the rest of the job. if [ -f .data/db.sql ]; then docker compose exec cli mkdir -p .data docker compose --progress quiet cp -L .data/db.sql cli:/app/.data/db.sql rm -f .data/db.sql fi #;< MIGRATION if [ -f ".data/${VORTEX_FETCH_DB2_FILE:-db2.sql}" ]; then docker compose exec -T cli mkdir -p .data docker compose --progress quiet cp -L ".data/${VORTEX_FETCH_DB2_FILE:-db2.sql}" cli:"/app/.data/${VORTEX_FETCH_DB2_FILE:-db2.sql}" rm -f ".data/${VORTEX_FETCH_DB2_FILE:-db2.sql}" fi #;> MIGRATION docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli ./vendor/bin/vortex-provision timeout-minutes: 30 #;< TOOL_JEST - name: Test with Jest if: ${{ env.VORTEX_CI_IS_JEST_RUNNER == 'true' }} run: docker compose exec -T cli bash -c "npm test" continue-on-error: ${{ vars.VORTEX_CI_JEST_IGNORE_FAILURE == '1' }} #;> TOOL_JEST #;< TOOL_PHPUNIT - name: Test with PHPUnit if: ${{ env.VORTEX_CI_IS_PHPUNIT_RUNNER == 'true' }} run: docker compose exec -T cli vendor/bin/phpunit continue-on-error: ${{ vars.VORTEX_CI_PHPUNIT_IGNORE_FAILURE == '1' }} - name: Process PHPUnit logs and coverage if: ${{ env.VORTEX_CI_IS_PHPUNIT_RUNNER == 'true' }} run: | mkdir -p ".logs" if docker compose ps --services --filter "status=running" | grep -q cli && docker compose exec cli test -d /app/.logs; then docker compose --progress quiet cp cli:/app/.logs/. ".logs/" fi - name: Extract code coverage if: ${{ env.VORTEX_CI_IS_PHPUNIT_RUNNER == 'true' }} run: | RATE=$(grep -om1 'line-rate="[0-9.]*"' .logs/coverage/phpunit/cobertura.xml | tr -cd '0-9.') PERCENT=$(awk "BEGIN {printf \"%.2f\", $RATE*100}") echo "Coverage: $PERCENT% (threshold: $VORTEX_CI_CODE_COVERAGE_THRESHOLD%)" | tee -a "$GITHUB_STEP_SUMMARY" { echo "COVERAGE_PERCENT=${PERCENT}" echo "COVERAGE_SUMMARY<> "$GITHUB_ENV" env: VORTEX_CI_CODE_COVERAGE_THRESHOLD: ${{ vars.VORTEX_CI_CODE_COVERAGE_THRESHOLD || '90' }} - name: Post coverage summary as PR comment if: ${{ github.event_name == 'pull_request' && env.VORTEX_CI_IS_PHPUNIT_RUNNER == 'true' && vars.VORTEX_CI_CODE_COVERAGE_PR_COMMENT_SKIP != '1' }} uses: marocchino/sticky-pull-request-comment@5770ad5eb8f42dd2c4f34da00c94c5381e49af88 # v3.0.5 with: header: coverage-gha message: | **Code coverage** (threshold: ${{ vars.VORTEX_CI_CODE_COVERAGE_THRESHOLD || '90' }}%) ``` ${{ env.COVERAGE_SUMMARY }} ```
Per-class coverage ``` ${{ env.COVERAGE_DETAILS }} ```
hide_and_recreate: true #;< CODE_COVERAGE_PROVIDER_CODECOV - name: Upload coverage report to Codecov uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7 if: ${{ env.VORTEX_CI_IS_PHPUNIT_RUNNER == 'true' && env.CODECOV_TOKEN != '' }} continue-on-error: true with: directory: .logs/coverage fail_ci_if_error: true token: ${{ secrets.CODECOV_TOKEN }} env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} #;> CODE_COVERAGE_PROVIDER_CODECOV - name: Check code coverage threshold if: ${{ env.VORTEX_CI_IS_PHPUNIT_RUNNER == 'true' }} run: | if [ "${COVERAGE_PERCENT//.}" -lt "$((VORTEX_CI_CODE_COVERAGE_THRESHOLD*100))" ]; then echo "FAIL: coverage ${COVERAGE_PERCENT}% is below threshold ${VORTEX_CI_CODE_COVERAGE_THRESHOLD}%" exit 1 fi env: VORTEX_CI_CODE_COVERAGE_THRESHOLD: ${{ vars.VORTEX_CI_CODE_COVERAGE_THRESHOLD || '90' }} #;> TOOL_PHPUNIT #;< DRUPAL_THEME #;< MODULE_SDC_DEVEL - name: Validate Single Directory Components if: ${{ env.VORTEX_CI_IS_SDC_DEVEL_RUNNER == 'true' }} run: | [ "${VORTEX_FRONTEND_BUILD_SKIP:-0}" -eq 1 ] && exit 0 output=$(docker compose exec -T cli vendor/bin/drush sdc-devel:validate "${DRUPAL_THEME}" 2>&1) echo "${output}" if echo "${output}" | grep -qE 'Critical|Errors?|Warnings?'; then echo "SDC validation reported problems." exit 1 fi continue-on-error: ${{ vars.VORTEX_CI_SDC_DEVEL_IGNORE_FAILURE == '1' }} #;> MODULE_SDC_DEVEL #;> DRUPAL_THEME #;< TOOL_BEHAT - name: Test with Behat if: ${{ env.VORTEX_CI_IS_BEHAT_RUNNER == 'true' }} run: | # shellcheck disable=SC2170 if [ "${VORTEX_CI_RUNNER_TOTAL}" -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p$((VORTEX_CI_RUNNER_INDEX - VORTEX_CI_BEHAT_PROFILE_OFFSET))}"; fi echo "Running with ${VORTEX_CI_BEHAT_PROFILE:-default} profile" docker compose exec -T -e BEHAT_SCREENSHOT_ANIMATION_SKIP=1 cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ docker compose exec -T -e BEHAT_SCREENSHOT_ANIMATION_SKIP=1 cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" env: VORTEX_CI_BEHAT_PROFILE: ${{ vars.VORTEX_CI_BEHAT_PROFILE }} continue-on-error: ${{ vars.VORTEX_CI_BEHAT_IGNORE_FAILURE == '1' }} timeout-minutes: 30 #;> TOOL_BEHAT - name: Process test logs and artifacts if: always() run: | mkdir -p ".logs" if docker compose ps --services --filter "status=running" | grep -q cli && docker compose exec cli test -d /app/.logs; then docker compose --progress quiet cp cli:/app/.logs/. ".logs/" fi - name: Upload test artifacts uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7 if: always() with: name: test-artifacts-${{ matrix.instance }} path: .logs include-hidden-files: true if-no-files-found: error #;< TOOL_PHPUNIT_BEHAT - name: Publish test results to GitHub Checks if: ${{ !cancelled() && vars.VORTEX_CI_TEST_RESULTS_SKIP != '1' }} uses: EnricoMi/publish-unit-test-result-action@7622c5bf3d112a72ab4fa3951c2aa5723d8ab92d # v2.25.0 with: check_name: Test results (${{ matrix.instance }}) comment_mode: "off" test_file_prefix: "-/app/" files: | .logs/test_results/**/*.xml #;> TOOL_PHPUNIT_BEHAT - name: Upload exported codebase as an artifact uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7 if: ${{ matrix.instance == 0 && !startsWith(github.head_ref || github.ref_name, 'deps/') && contains(env.VORTEX_DEPLOY_TYPES, 'artifact') }} with: name: code-artifact path: "/tmp/artifacts/code.tar" include-hidden-files: true if-no-files-found: error retention-days: 1 # Placed after the test steps so a dirty tree never suppresses them. - name: Check that the build left a clean working tree run: ./vendor/bin/vortex-check-worktree-changes continue-on-error: ${{ vars.VORTEX_CI_WORKTREE_CHANGES_IGNORE_FAILURE == '1' }} - name: Setup tmate session if: ${{ !cancelled() && github.event.inputs.enable_terminal }} uses: mxschmitt/action-tmate@35b54afac29c97fb54faba5b513f8fbd1882f113 # v3 timeout-minutes: 120 # Cancel the action after 120 minutes, regardless of whether a connection has been established. with: detached: true #;< DEPLOYMENT deploy: runs-on: ubuntu-latest needs: [build, lint] #;< !PROVISION_TYPE_PROFILE if: ${{ !cancelled() && (inputs.deploy_target || (success() && github.event_name != 'schedule' && !startsWith(github.head_ref || github.ref_name, 'deps/') && (github.event_name == 'push' || !startsWith(github.head_ref, 'project/')))) }} #;> !PROVISION_TYPE_PROFILE permissions: contents: read pull-requests: read container: # https://hub.docker.com/r/drevops/ci-runner image: drevops/ci-runner:26.10.0@sha256:36db4ee12da3372c86966fccd543e49c9470bca12e86eb5d4aad7904dd84a0dd env: TZ: ${{ vars.TZ || 'UTC' }} TERM: xterm-256color VORTEX_SSH_DISABLE_STRICT_HOST_KEY_CHECKING: "1" VORTEX_DEBUG: ${{ vars.VORTEX_DEBUG }} steps: - name: Preserve $HOME set in the container run: echo HOME=/root >> "$GITHUB_ENV" # https://github.com/actions/runner/issues/863 - name: Resolve deploy target if: ${{ inputs.deploy_target }} env: DEPLOY_TARGET: ${{ inputs.deploy_target }} GH_TOKEN: ${{ github.token }} run: | { echo "DEPLOY_BRANCH=${DEPLOY_TARGET}" if echo "${DEPLOY_TARGET}" | grep -iq '^pr-'; then echo "DEPLOY_PR_NUMBER=${DEPLOY_TARGET#*-}" echo "DEPLOY_PR_HEAD_SHA=$(gh pr view "${DEPLOY_TARGET#*-}" --repo "${{ github.repository }}" --json headRefOid --jq '.headRefOid')" echo "DEPLOY_BRANCH=$(gh pr view "${DEPLOY_TARGET#*-}" --repo "${{ github.repository }}" --json headRefName --jq '.headRefName')" fi } >> "$GITHUB_ENV" - name: Checkout code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: # Fetch all history for git repository. fetch-depth: 0 # Do not persist credentials after checkout # to allow using the custom credentials to push to a remote repo. persist-credentials: false ref: ${{ env.DEPLOY_BRANCH || github.head_ref || github.ref_name }} - name: Fix Git ownership permissions run: git config --global --add safe.directory "$GITHUB_WORKSPACE" - name: Load environment variables from .env run: t=$(mktemp) && export -p >"${t}" && set -a && . ./.env && set +a && . "${t}" && env >> "$GITHUB_ENV" # Artifact deployments do not work for manual deploys as the build job is skipped. - name: Download exported codebase as an artifact uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8 if: ${{ !inputs.deploy_target && contains(env.VORTEX_DEPLOY_TYPES, 'artifact') }} with: name: code-artifact path: "/tmp/artifacts" - name: Unpack downloaded exported codebase if: ${{ !inputs.deploy_target && contains(env.VORTEX_DEPLOY_TYPES, 'artifact') }} run: | mkdir -p /tmp/workspace tar -xpf /tmp/artifacts/code.tar -C /tmp/workspace - name: Add SSH private key to the runner if: ${{ env.VORTEX_DEPLOY_SSH_PRIVATE_KEY != '' }} uses: shimataro/ssh-key-action@87a8f067114a8ce263df83e9ed5c849953548bc3 # v2.8.1 with: key: ${{ secrets.VORTEX_DEPLOY_SSH_PRIVATE_KEY }} known_hosts: ${{ vars.VORTEX_DEPLOY_SSH_KNOWN_HOSTS || 'unnecessary' }} env: VORTEX_DEPLOY_SSH_PRIVATE_KEY: ${{ secrets.VORTEX_DEPLOY_SSH_PRIVATE_KEY }} - name: Install Vortex tooling run: ./scripts/vortex-tooling.sh - name: Deploy run: ./vendor/bin/vortex-deploy env: VORTEX_DEPLOY_MODE: ${{ startsWith(github.ref, 'refs/tags/') && 'tag' || 'branch' }} VORTEX_DEPLOY_BRANCH: ${{ env.DEPLOY_BRANCH || github.head_ref || github.ref_name }} VORTEX_DEPLOY_PR: ${{ env.DEPLOY_PR_NUMBER || github.event.number }} VORTEX_DEPLOY_PR_HEAD: ${{ env.DEPLOY_PR_HEAD_SHA || github.event.pull_request.head.sha }} VORTEX_DEPLOY_PR_LABELS: ${{ join(github.event.pull_request.labels.*.name, ',') }} VORTEX_DEPLOY_ARTIFACT_SRC: /tmp/workspace/code VORTEX_DEPLOY_ARTIFACT_ROOT: ${{ github.workspace }} VORTEX_DEPLOY_ARTIFACT_GIT_REMOTE: ${{ vars.VORTEX_DEPLOY_ARTIFACT_GIT_REMOTE }} VORTEX_DEPLOY_ARTIFACT_GIT_USER_EMAIL: ${{ vars.VORTEX_DEPLOY_ARTIFACT_GIT_USER_EMAIL }} VORTEX_DEPLOY_ARTIFACT_GIT_USER_NAME: ${{ vars.VORTEX_DEPLOY_ARTIFACT_GIT_USER_NAME }} VORTEX_DEPLOY_WEBHOOK_URL: ${{ vars.VORTEX_DEPLOY_WEBHOOK_URL }} VORTEX_DEPLOY_SKIP: ${{ vars.VORTEX_DEPLOY_SKIP }} VORTEX_DEPLOY_ALLOW_SKIP: ${{ vars.VORTEX_DEPLOY_ALLOW_SKIP }} VORTEX_DEPLOY_SKIP_PRS: ${{ vars.VORTEX_DEPLOY_SKIP_PRS }} VORTEX_DEPLOY_SKIP_BRANCHES: ${{ vars.VORTEX_DEPLOY_SKIP_BRANCHES }} VORTEX_DEPLOY_ALLOW_LABEL: ${{ vars.VORTEX_DEPLOY_ALLOW_LABEL }} VORTEX_DEPLOY_ACTION: ${{ inputs.override_db && 'deploy_override_db' || '' }} timeout-minutes: 30 #;> DEPLOYMENT