diff --git a/.ahoy.yml b/.ahoy.yml index 2b1472a1f..43d957352 100644 --- a/.ahoy.yml +++ b/.ahoy.yml @@ -80,12 +80,12 @@ commands: usage: Show container logs for services. cmd: docker compose logs "$@" + # Drop into a shell if no arguments are supplied, otherwise run the command. + # Environment variables are passed from the host and filtered by prefix. + # Use \" (slash followed by a double quote) to escape double quotes in + # arguments that contain spaces. cli: usage: Start a shell or run a command inside the CLI service container. - # Drop into a shell if no arguments are supplied, otherwise run the command. - # Environment variables are passed from the host and filtered by prefix. - # Use \" (slash followed by a double quote) to escape double quotes in - # arguments that contain spaces. cmd: | if [ "${#}" -ne 0 ]; then docker compose exec $(env | cut -f1 -d= | grep "TERM\|COMPOSE_\|GITHUB_\|PACKAGE_\|DOCKER_\|DRUPAL_\|VORTEX_" | sed 's/^/-e /') cli bash -c "$*" @@ -122,7 +122,7 @@ commands: #;< !PROVISION_TYPE_PROFILE download-db: usage: Download database. Run with "--no-cache" option to force fresh database backup. - aliases: ['fetch-db'] + aliases: [fetch-db] cmd: | case " $* " in *" --no-cache "*) export VORTEX_DB_DOWNLOAD_NO_CACHE=1;; esac ./scripts/vortex/download-db.sh @@ -158,7 +158,7 @@ commands: cmd: \[ -n "${VORTEX_DB_IMAGE}" \] && docker pull ${VORTEX_DB_IMAGE} || true reset: - usage: "Remove containers, all build files. Use with `hard` to reset repository to the last commit." + usage: Remove containers, all build files. Use with `hard` to reset repository to the last commit. cmd: | ahoy confirm "All containers and build files will be removed. Proceed?" || exit 0 AHOY_CONFIRM_RESPONSE=y ahoy down @@ -190,7 +190,10 @@ commands: lint: usage: Lint back-end and front-end code. - cmd: ahoy lint-be && ahoy lint-fe && ahoy lint-tests + cmd: | + ahoy lint-be + ahoy lint-fe + ahoy lint-tests lint-be: usage: Lint back-end code. @@ -211,11 +214,14 @@ commands: lint-tests: usage: Lint tests code. - cmd: ahoy cli vendor/bin/gherkinlint lint tests/behat/features + cmd: | + ahoy cli vendor/bin/gherkinlint lint tests/behat/features lint-fix: usage: Fix lint issues of back-end and front-end code. - cmd: ahoy lint-be-fix && ahoy lint-fe-fix + cmd: | + ahoy lint-be-fix + ahoy lint-fe-fix lint-be-fix: usage: Fix lint issues of back-end code. @@ -234,7 +240,11 @@ commands: test: usage: Run all tests. - cmd: ahoy test-unit && ahoy test-kernel && ahoy test-functional && ahoy test-bdd + cmd: | + ahoy test-unit + ahoy test-kernel + ahoy test-functional + ahoy test-bdd test-unit: usage: Run PHPUnit unit tests. @@ -250,7 +260,7 @@ commands: test-bdd: usage: Run BDD tests. - aliases: ['test-behat'] + aliases: [test-behat] cmd: ahoy cli php -d memory_limit=-1 vendor/bin/behat --colors "$@" debug: @@ -274,8 +284,7 @@ commands: local: usage: Custom local commands. See `ahoy local help`. optional: true - imports: - - .ahoy.local.yml + imports: [.ahoy.local.yml] # ---------------------------------------------------------------------------- # Utilities. @@ -307,15 +316,14 @@ commands: hide: true # Override entrypoint to alter default behavior of Ahoy. +# 1. Exit the script if any statement returns a non-true return value. +# 2. Read variables from .env file (while respecting existing values) to load +# and pass updated environment variables' values into already running +# containers. entrypoint: - bash - -c - # Exit the script if any statement returns a non-true return value. - -e - # Read variables from .env file, respecting existing values. - # - Used to load and pass updated environment variables' values into already - # running containers. - # - Use `ahoy up cli` in cases when changes require container restart. - | t=$(mktemp) && export -p > "$t" && set -a && . ./.env && if [ -f ./.env.local ];then . ./.env.local;fi && set +a && . "$t" && rm "$t" && unset t bash -e -c "$0" "$@" diff --git a/.circleci/config.yml b/.circleci/config.yml index 31581c398..6b3b394bc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -286,29 +286,37 @@ jobs: [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 docker compose exec -T cli composer normalize --dry-run || [ "${VORTEX_CI_COMPOSER_NORMALIZE_IGNORE_FAILURE:-0}" -eq 1 ] + #;< TOOL_PHPCS - run: name: Lint code with PHPCS command: | [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 docker compose exec -T cli vendor/bin/phpcs || [ "${VORTEX_CI_PHPCS_IGNORE_FAILURE:-0}" -eq 1 ] + #;> TOOL_PHPCS + #;< TOOL_PHPSTAN - run: name: Lint code with PHPStan command: | [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 docker compose exec -T cli vendor/bin/phpstan || [ "${VORTEX_CI_PHPSTAN_IGNORE_FAILURE:-0}" -eq 1 ] + #;> TOOL_PHPSTAN + #;< TOOL_RECTOR - run: name: Lint code with Rector command: | [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 docker compose exec -T cli vendor/bin/rector --clear-cache --dry-run || [ "${VORTEX_CI_RECTOR_IGNORE_FAILURE:-0}" -eq 1 ] + #;> TOOL_RECTOR + #;< TOOL_PHPMD - run: name: Lint code with PHPMD command: | [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 docker compose exec -T cli vendor/bin/phpmd . text phpmd.xml || [ "${VORTEX_CI_PHPMD_IGNORE_FAILURE:-0}" -eq 1 ] + #;> TOOL_PHPMD - run: name: Lint code with Twig CS Fixer @@ -316,11 +324,13 @@ jobs: [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 docker compose exec -T cli vendor/bin/twig-cs-fixer || [ "${VORTEX_CI_TWIG_CS_FIXER_IGNORE_FAILURE:-0}" -eq 1 ] + #;< TOOL_BEHAT - run: name: Lint code with Gherkin Lint command: | [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 docker compose exec -T cli vendor/bin/gherkinlint lint tests/behat/features || [ "${VORTEX_CI_GHERKIN_LINT_IGNORE_FAILURE:-0}" -eq 1 ] + #;> TOOL_BEHAT - run: name: Lint module code with NodeJS linters @@ -346,10 +356,13 @@ jobs: docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli ./scripts/vortex/provision.sh no_output_timeout: 30m + #;< TOOL_PHPUNIT - run: name: Test with PHPUnit command: docker compose exec -T cli vendor/bin/phpunit || [ "${VORTEX_CI_PHPUNIT_IGNORE_FAILURE:-0}" -eq 1 ] + #;> TOOL_PHPUNIT + #;< TOOL_BEHAT - run: name: Test with Behat command: | @@ -359,6 +372,7 @@ jobs: docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ [ "${VORTEX_CI_BEHAT_IGNORE_FAILURE:-0}" -eq 1 ] no_output_timeout: 30m + #;> TOOL_BEHAT - run: name: Process test logs and artifacts diff --git a/.github/workflows/build-test-deploy.yml b/.github/workflows/build-test-deploy.yml index 60c4ba102..6422631d2 100644 --- a/.github/workflows/build-test-deploy.yml +++ b/.github/workflows/build-test-deploy.yml @@ -269,35 +269,45 @@ jobs: 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 if: ${{ matrix.instance == 0 || strategy.job-total == 1 }} 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 if: ${{ matrix.instance == 0 || strategy.job-total == 1 }} 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 if: ${{ matrix.instance == 0 || strategy.job-total == 1 }} run: docker compose exec -T cli vendor/bin/rector --clear-cache --dry-run continue-on-error: ${{ vars.VORTEX_CI_RECTOR_IGNORE_FAILURE == '1' }} + #;> TOOL_RECTOR + #;< TOOL_PHPMD - name: Lint code with PHPMD if: ${{ matrix.instance == 0 || strategy.job-total == 1 }} run: docker compose exec -T cli vendor/bin/phpmd . text phpmd.xml continue-on-error: ${{ vars.VORTEX_CI_PHPMD_IGNORE_FAILURE == '1' }} + #;> TOOL_PHPMD - name: Lint code with Twig CS Fixer if: ${{ matrix.instance == 0 || strategy.job-total == 1 }} run: docker compose exec -T cli vendor/bin/twig-cs-fixer continue-on-error: ${{ vars.VORTEX_CI_TWIG_CS_FIXER_IGNORE_FAILURE == '1' }} + #;< TOOL_BEHAT - name: Lint code with Gherkin Lint if: ${{ matrix.instance == 0 || strategy.job-total == 1 }} 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 - name: Lint module code with NodeJS linters if: ${{ matrix.instance == 0 || strategy.job-total == 1 }} @@ -320,10 +330,13 @@ jobs: docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli ./scripts/vortex/provision.sh timeout-minutes: 30 + #;< TOOL_PHPUNIT - name: Test with PHPUnit run: docker compose exec -T cli vendor/bin/phpunit continue-on-error: ${{ vars.VORTEX_CI_PHPUNIT_IGNORE_FAILURE == '1' }} + #;> TOOL_PHPUNIT + #;< TOOL_BEHAT - name: Test with Behat run: | # shellcheck disable=SC2170 @@ -335,6 +348,7 @@ jobs: 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() diff --git a/.github/workflows/vortex-test-installer.yml b/.github/workflows/vortex-test-installer.yml index e4636ade5..96a1e588e 100644 --- a/.github/workflows/vortex-test-installer.yml +++ b/.github/workflows/vortex-test-installer.yml @@ -37,6 +37,10 @@ jobs: run: composer install working-directory: .vortex/installer + - name: Validate Composer configuration is normalized + run: composer normalize --dry-run + working-directory: .vortex/installer + - name: Check coding standards run: composer lint working-directory: .vortex/installer diff --git a/.vortex/installer/composer.json b/.vortex/installer/composer.json index 8a25deec2..e9ab128b6 100644 --- a/.vortex/installer/composer.json +++ b/.vortex/installer/composer.json @@ -18,11 +18,13 @@ }, "require": { "php": ">=8.2", - "alexskrypnyk/file": "^0.8.0", + "alexskrypnyk/file": "^0.9", "alexskrypnyk/str2name": "^1.4", + "composer/composer": "^2.8", "cweagans/composer-patches": "^1.7", "czproject/git-php": "^4.3", "laravel/prompts": "^0.3.5", + "nikic/iter": "^2.4", "sebastian/diff": "*", "symfony/console": "^7.3", "symfony/filesystem": "^7.2", @@ -48,8 +50,8 @@ }, "autoload-dev": { "psr-4": { - "DrevOps\\VortexInstaller\\Tests\\": "tests", - "AlexSkrypnyk\\File\\Tests\\": "vendor/alexskrypnyk/file/tests" + "AlexSkrypnyk\\File\\Tests\\": "vendor/alexskrypnyk/file/tests", + "DrevOps\\VortexInstaller\\Tests\\": "tests" }, "classmap": [ "tests" @@ -92,6 +94,7 @@ ], "reset": "rm -Rf vendor vendor-bin", "test": "phpunit --no-coverage", + "test-baseline": "UPDATE_FIXTURES=1 phpunit --no-coverage --filter=testInstall@baseline || UPDATE_FIXTURES=1 phpunit --no-coverage --filter=testInstall@baseline", "test-coverage": "php -d pcov.directory=. vendor/bin/phpunit", "test-fixtures": "UPDATE_FIXTURES=1 phpunit --no-coverage --filter=testInstall" } diff --git a/.vortex/installer/composer.lock b/.vortex/installer/composer.lock index f32adfeaf..52eddf58a 100644 --- a/.vortex/installer/composer.lock +++ b/.vortex/installer/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "88d3235935bc7c71df6b996d8e55a9cc", + "content-hash": "54be44ac372765b171d1dfed26f33f30", "packages": [ { "name": "alexskrypnyk/file", - "version": "0.8.2", + "version": "0.9.0", "source": { "type": "git", "url": "https://github.com/AlexSkrypnyk/file.git", - "reference": "997707e315bed73541ce18ec4ae8e88ba466dd5e" + "reference": "df98e9ba70357433061533e9cc747708ce92500c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/AlexSkrypnyk/file/zipball/997707e315bed73541ce18ec4ae8e88ba466dd5e", - "reference": "997707e315bed73541ce18ec4ae8e88ba466dd5e", + "url": "https://api.github.com/repos/AlexSkrypnyk/file/zipball/df98e9ba70357433061533e9cc747708ce92500c", + "reference": "df98e9ba70357433061533e9cc747708ce92500c", "shasum": "" }, "require": { @@ -70,7 +70,7 @@ "type": "patreon" } ], - "time": "2025-07-28T05:16:25+00:00" + "time": "2025-08-24T03:16:48+00:00" }, { "name": "alexskrypnyk/str2name", @@ -134,194 +134,287 @@ "time": "2025-04-11T02:15:41+00:00" }, { - "name": "cweagans/composer-patches", - "version": "1.7.3", + "name": "composer/ca-bundle", + "version": "1.5.8", "source": { "type": "git", - "url": "https://github.com/cweagans/composer-patches.git", - "reference": "e190d4466fe2b103a55467dfa83fc2fecfcaf2db" + "url": "https://github.com/composer/ca-bundle.git", + "reference": "719026bb30813accb68271fee7e39552a58e9f65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cweagans/composer-patches/zipball/e190d4466fe2b103a55467dfa83fc2fecfcaf2db", - "reference": "e190d4466fe2b103a55467dfa83fc2fecfcaf2db", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/719026bb30813accb68271fee7e39552a58e9f65", + "reference": "719026bb30813accb68271fee7e39552a58e9f65", "shasum": "" }, "require": { - "composer-plugin-api": "^1.0 || ^2.0", - "php": ">=5.3.0" + "ext-openssl": "*", + "ext-pcre": "*", + "php": "^7.2 || ^8.0" }, "require-dev": { - "composer/composer": "~1.0 || ~2.0", - "phpunit/phpunit": "~4.6" + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8 || ^9", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "symfony/process": "^4.0 || ^5.0 || ^6.0 || ^7.0" }, - "type": "composer-plugin", + "type": "library", "extra": { - "class": "cweagans\\Composer\\Patches" + "branch-alias": { + "dev-main": "1.x-dev" + } }, "autoload": { "psr-4": { - "cweagans\\Composer\\": "src" + "Composer\\CaBundle\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Cameron Eagans", - "email": "me@cweagans.net" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" } ], - "description": "Provides a way to patch Composer packages.", + "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.", + "keywords": [ + "cabundle", + "cacert", + "certificate", + "ssl", + "tls" + ], "support": { - "issues": "https://github.com/cweagans/composer-patches/issues", - "source": "https://github.com/cweagans/composer-patches/tree/1.7.3" + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/ca-bundle/issues", + "source": "https://github.com/composer/ca-bundle/tree/1.5.8" }, - "time": "2022-12-20T22:53:13+00:00" + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + } + ], + "time": "2025-08-20T18:49:47+00:00" }, { - "name": "czproject/git-php", - "version": "v4.5.0", + "name": "composer/class-map-generator", + "version": "1.6.2", "source": { "type": "git", - "url": "https://github.com/czproject/git-php.git", - "reference": "3ea910e188849d5e239d65167010c05196310915" + "url": "https://github.com/composer/class-map-generator.git", + "reference": "ba9f089655d4cdd64e762a6044f411ccdaec0076" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/czproject/git-php/zipball/3ea910e188849d5e239d65167010c05196310915", - "reference": "3ea910e188849d5e239d65167010c05196310915", + "url": "https://api.github.com/repos/composer/class-map-generator/zipball/ba9f089655d4cdd64e762a6044f411ccdaec0076", + "reference": "ba9f089655d4cdd64e762a6044f411ccdaec0076", "shasum": "" }, "require": { - "php": "8.0 - 8.4" + "composer/pcre": "^2.1 || ^3.1", + "php": "^7.2 || ^8.0", + "symfony/finder": "^4.4 || ^5.3 || ^6 || ^7" }, "require-dev": { - "nette/tester": "^2.4" + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-deprecation-rules": "^1 || ^2", + "phpstan/phpstan-phpunit": "^1 || ^2", + "phpstan/phpstan-strict-rules": "^1.1 || ^2", + "phpunit/phpunit": "^8", + "symfony/filesystem": "^5.4 || ^6" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Composer\\ClassMapGenerator\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Jan Pecha", - "email": "janpecha@email.cz" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" } ], - "description": "Library for work with Git repository in PHP.", + "description": "Utilities to scan PHP code and generate class maps.", "keywords": [ - "git" + "classmap" ], "support": { - "issues": "https://github.com/czproject/git-php/issues", - "source": "https://github.com/czproject/git-php/tree/v4.5.0" + "issues": "https://github.com/composer/class-map-generator/issues", + "source": "https://github.com/composer/class-map-generator/tree/1.6.2" }, "funding": [ { - "url": "https://www.janpecha.cz/donate/git-php/", - "type": "other" + "url": "https://packagist.com", + "type": "custom" }, { - "url": "https://donate.stripe.com/7sIcO2a9maTSg2A9AA", - "type": "stripe" + "url": "https://github.com/composer", + "type": "github" } ], - "time": "2025-06-10T18:32:14+00:00" + "time": "2025-08-20T18:52:43+00:00" }, { - "name": "laravel/prompts", - "version": "v0.3.6", + "name": "composer/composer", + "version": "2.8.11", "source": { "type": "git", - "url": "https://github.com/laravel/prompts.git", - "reference": "86a8b692e8661d0fb308cec64f3d176821323077" + "url": "https://github.com/composer/composer.git", + "reference": "00e1a3396eea67033775c4a49c772376f45acd73" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/prompts/zipball/86a8b692e8661d0fb308cec64f3d176821323077", - "reference": "86a8b692e8661d0fb308cec64f3d176821323077", + "url": "https://api.github.com/repos/composer/composer/zipball/00e1a3396eea67033775c4a49c772376f45acd73", + "reference": "00e1a3396eea67033775c4a49c772376f45acd73", "shasum": "" }, "require": { - "composer-runtime-api": "^2.2", - "ext-mbstring": "*", - "php": "^8.1", - "symfony/console": "^6.2|^7.0" - }, - "conflict": { - "illuminate/console": ">=10.17.0 <10.25.0", - "laravel/framework": ">=10.17.0 <10.25.0" + "composer/ca-bundle": "^1.5", + "composer/class-map-generator": "^1.4.0", + "composer/metadata-minifier": "^1.0", + "composer/pcre": "^2.2 || ^3.2", + "composer/semver": "^3.3", + "composer/spdx-licenses": "^1.5.7", + "composer/xdebug-handler": "^2.0.2 || ^3.0.3", + "justinrainbow/json-schema": "^6.3.1", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "react/promise": "^2.11 || ^3.3", + "seld/jsonlint": "^1.4", + "seld/phar-utils": "^1.2", + "seld/signal-handler": "^2.0", + "symfony/console": "^5.4.35 || ^6.3.12 || ^7.0.3", + "symfony/filesystem": "^5.4.35 || ^6.3.12 || ^7.0.3", + "symfony/finder": "^5.4.35 || ^6.3.12 || ^7.0.3", + "symfony/polyfill-php73": "^1.24", + "symfony/polyfill-php80": "^1.24", + "symfony/polyfill-php81": "^1.24", + "symfony/process": "^5.4.35 || ^6.3.12 || ^7.0.3" }, "require-dev": { - "illuminate/collections": "^10.0|^11.0|^12.0", - "mockery/mockery": "^1.5", - "pestphp/pest": "^2.3|^3.4", - "phpstan/phpstan": "^1.11", - "phpstan/phpstan-mockery": "^1.1" + "phpstan/phpstan": "^1.11.8", + "phpstan/phpstan-deprecation-rules": "^1.2.0", + "phpstan/phpstan-phpunit": "^1.4.0", + "phpstan/phpstan-strict-rules": "^1.6.0", + "phpstan/phpstan-symfony": "^1.4.0", + "symfony/phpunit-bridge": "^6.4.3 || ^7.0.1" }, "suggest": { - "ext-pcntl": "Required for the spinner to be animated." + "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages", + "ext-zip": "Enabling the zip extension allows you to unzip archives", + "ext-zlib": "Allow gzip compression of HTTP requests" }, + "bin": [ + "bin/composer" + ], "type": "library", "extra": { + "phpstan": { + "includes": [ + "phpstan/rules.neon" + ] + }, "branch-alias": { - "dev-main": "0.3.x-dev" + "dev-main": "2.8-dev" } }, "autoload": { - "files": [ - "src/helpers.php" - ], "psr-4": { - "Laravel\\Prompts\\": "src/" + "Composer\\": "src/Composer/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "Add beautiful and user-friendly forms to your command-line applications.", + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "https://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Composer helps you declare, manage and install dependencies of PHP projects. It ensures you have the right stack everywhere.", + "homepage": "https://getcomposer.org/", + "keywords": [ + "autoload", + "dependency", + "package" + ], "support": { - "issues": "https://github.com/laravel/prompts/issues", - "source": "https://github.com/laravel/prompts/tree/v0.3.6" + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/composer/issues", + "security": "https://github.com/composer/composer/security/policy", + "source": "https://github.com/composer/composer/tree/2.8.11" }, - "time": "2025-07-07T14:17:42+00:00" + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + } + ], + "time": "2025-08-21T09:29:39+00:00" }, { - "name": "psr/container", - "version": "2.0.2", + "name": "composer/metadata-minifier", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + "url": "https://github.com/composer/metadata-minifier.git", + "reference": "c549d23829536f0d0e984aaabbf02af91f443207" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "url": "https://api.github.com/repos/composer/metadata-minifier/zipball/c549d23829536f0d0e984aaabbf02af91f443207", + "reference": "c549d23829536f0d0e984aaabbf02af91f443207", "shasum": "" }, "require": { - "php": ">=7.4.0" + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "composer/composer": "^2", + "phpstan/phpstan": "^0.12.55", + "symfony/phpunit-bridge": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-main": "1.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Container\\": "src/" + "Composer\\MetadataMinifier\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -330,144 +423,146 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" } ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", + "description": "Small utility library that handles metadata minification and expansion.", "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" + "composer", + "compression" ], "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/2.0.2" + "issues": "https://github.com/composer/metadata-minifier/issues", + "source": "https://github.com/composer/metadata-minifier/tree/1.0.0" }, - "time": "2021-11-05T16:47:00+00:00" + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2021-04-07T13:37:33+00:00" }, { - "name": "sebastian/diff", - "version": "6.0.2", + "name": "composer/pcre", + "version": "3.3.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", - "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", "shasum": "" }, "require": { - "php": ">=8.2" + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" }, "require-dev": { - "phpunit/phpunit": "^11.0", - "symfony/process": "^4.2 || ^5" + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" }, "type": "library", "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "3.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Composer\\Pcre\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" } ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" + "PCRE", + "preg", + "regex", + "regular expression" ], "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" } ], - "time": "2024-07-03T04:53:05+00:00" + "time": "2024-11-12T16:29:46+00:00" }, { - "name": "symfony/console", - "version": "v7.3.2", + "name": "composer/semver", + "version": "3.4.4", "source": { "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1" + "url": "https://github.com/composer/semver.git", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/5f360ebc65c55265a74d23d7fe27f957870158a1", - "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1", + "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.2" - }, - "conflict": { - "symfony/dependency-injection": "<6.4", - "symfony/dotenv": "<6.4", - "symfony/event-dispatcher": "<6.4", - "symfony/lock": "<6.4", - "symfony/process": "<6.4" - }, - "provide": { - "psr/log-implementation": "1.0|2.0|3.0" + "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, "autoload": { "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Composer\\Semver\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -475,32 +570,1192 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" } ], - "description": "Eases the creation of beautiful and testable command line interfaces", - "homepage": "https://symfony.com", + "description": "Semver library that offers utilities, version constraint parsing and validation.", "keywords": [ - "cli", - "command-line", - "console", - "terminal" + "semantic", + "semver", + "validation", + "versioning" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.2" + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.4" }, "funding": [ { - "url": "https://symfony.com/sponsor", + "url": "https://packagist.com", "type": "custom" }, { - "url": "https://github.com/fabpot", + "url": "https://github.com/composer", + "type": "github" + } + ], + "time": "2025-08-20T19:15:30+00:00" + }, + { + "name": "composer/spdx-licenses", + "version": "1.5.9", + "source": { + "type": "git", + "url": "https://github.com/composer/spdx-licenses.git", + "reference": "edf364cefe8c43501e21e88110aac10b284c3c9f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/edf364cefe8c43501e21e88110aac10b284c3c9f", + "reference": "edf364cefe8c43501e21e88110aac10b284c3c9f", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Spdx\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "SPDX licenses list and validation library.", + "keywords": [ + "license", + "spdx", + "validator" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/spdx-licenses/issues", + "source": "https://github.com/composer/spdx-licenses/tree/1.5.9" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2025-05-12T21:07:07+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.5", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-05-06T16:37:16+00:00" + }, + { + "name": "cweagans/composer-patches", + "version": "1.7.3", + "source": { + "type": "git", + "url": "https://github.com/cweagans/composer-patches.git", + "reference": "e190d4466fe2b103a55467dfa83fc2fecfcaf2db" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cweagans/composer-patches/zipball/e190d4466fe2b103a55467dfa83fc2fecfcaf2db", + "reference": "e190d4466fe2b103a55467dfa83fc2fecfcaf2db", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0", + "php": ">=5.3.0" + }, + "require-dev": { + "composer/composer": "~1.0 || ~2.0", + "phpunit/phpunit": "~4.6" + }, + "type": "composer-plugin", + "extra": { + "class": "cweagans\\Composer\\Patches" + }, + "autoload": { + "psr-4": { + "cweagans\\Composer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Cameron Eagans", + "email": "me@cweagans.net" + } + ], + "description": "Provides a way to patch Composer packages.", + "support": { + "issues": "https://github.com/cweagans/composer-patches/issues", + "source": "https://github.com/cweagans/composer-patches/tree/1.7.3" + }, + "time": "2022-12-20T22:53:13+00:00" + }, + { + "name": "czproject/git-php", + "version": "v4.5.0", + "source": { + "type": "git", + "url": "https://github.com/czproject/git-php.git", + "reference": "3ea910e188849d5e239d65167010c05196310915" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/czproject/git-php/zipball/3ea910e188849d5e239d65167010c05196310915", + "reference": "3ea910e188849d5e239d65167010c05196310915", + "shasum": "" + }, + "require": { + "php": "8.0 - 8.4" + }, + "require-dev": { + "nette/tester": "^2.4" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jan Pecha", + "email": "janpecha@email.cz" + } + ], + "description": "Library for work with Git repository in PHP.", + "keywords": [ + "git" + ], + "support": { + "issues": "https://github.com/czproject/git-php/issues", + "source": "https://github.com/czproject/git-php/tree/v4.5.0" + }, + "funding": [ + { + "url": "https://www.janpecha.cz/donate/git-php/", + "type": "other" + }, + { + "url": "https://donate.stripe.com/7sIcO2a9maTSg2A9AA", + "type": "stripe" + } + ], + "time": "2025-06-10T18:32:14+00:00" + }, + { + "name": "justinrainbow/json-schema", + "version": "6.4.2", + "source": { + "type": "git", + "url": "https://github.com/jsonrainbow/json-schema.git", + "reference": "ce1fd2d47799bb60668643bc6220f6278a4c1d02" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/ce1fd2d47799bb60668643bc6220f6278a4c1d02", + "reference": "ce1fd2d47799bb60668643bc6220f6278a4c1d02", + "shasum": "" + }, + "require": { + "ext-json": "*", + "marc-mabe/php-enum": "^4.0", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "3.3.0", + "json-schema/json-schema-test-suite": "1.2.0", + "marc-mabe/php-enum-phpstan": "^2.0", + "phpspec/prophecy": "^1.19", + "phpstan/phpstan": "^1.12", + "phpunit/phpunit": "^8.5" + }, + "bin": [ + "bin/validate-json" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.x-dev" + } + }, + "autoload": { + "psr-4": { + "JsonSchema\\": "src/JsonSchema/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Robert Schönthal", + "email": "seroscho@googlemail.com" + } + ], + "description": "A library to validate a json schema.", + "homepage": "https://github.com/jsonrainbow/json-schema", + "keywords": [ + "json", + "schema" + ], + "support": { + "issues": "https://github.com/jsonrainbow/json-schema/issues", + "source": "https://github.com/jsonrainbow/json-schema/tree/6.4.2" + }, + "time": "2025-06-03T18:27:04+00:00" + }, + { + "name": "laravel/prompts", + "version": "v0.3.6", + "source": { + "type": "git", + "url": "https://github.com/laravel/prompts.git", + "reference": "86a8b692e8661d0fb308cec64f3d176821323077" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/prompts/zipball/86a8b692e8661d0fb308cec64f3d176821323077", + "reference": "86a8b692e8661d0fb308cec64f3d176821323077", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.2", + "ext-mbstring": "*", + "php": "^8.1", + "symfony/console": "^6.2|^7.0" + }, + "conflict": { + "illuminate/console": ">=10.17.0 <10.25.0", + "laravel/framework": ">=10.17.0 <10.25.0" + }, + "require-dev": { + "illuminate/collections": "^10.0|^11.0|^12.0", + "mockery/mockery": "^1.5", + "pestphp/pest": "^2.3|^3.4", + "phpstan/phpstan": "^1.11", + "phpstan/phpstan-mockery": "^1.1" + }, + "suggest": { + "ext-pcntl": "Required for the spinner to be animated." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.3.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Laravel\\Prompts\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Add beautiful and user-friendly forms to your command-line applications.", + "support": { + "issues": "https://github.com/laravel/prompts/issues", + "source": "https://github.com/laravel/prompts/tree/v0.3.6" + }, + "time": "2025-07-07T14:17:42+00:00" + }, + { + "name": "marc-mabe/php-enum", + "version": "v4.7.1", + "source": { + "type": "git", + "url": "https://github.com/marc-mabe/php-enum.git", + "reference": "7159809e5cfa041dca28e61f7f7ae58063aae8ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/marc-mabe/php-enum/zipball/7159809e5cfa041dca28e61f7f7ae58063aae8ed", + "reference": "7159809e5cfa041dca28e61f7f7ae58063aae8ed", + "shasum": "" + }, + "require": { + "ext-reflection": "*", + "php": "^7.1 | ^8.0" + }, + "require-dev": { + "phpbench/phpbench": "^0.16.10 || ^1.0.4", + "phpstan/phpstan": "^1.3.1", + "phpunit/phpunit": "^7.5.20 | ^8.5.22 | ^9.5.11", + "vimeo/psalm": "^4.17.0 | ^5.26.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-3.x": "3.2-dev", + "dev-master": "4.7-dev" + } + }, + "autoload": { + "psr-4": { + "MabeEnum\\": "src/" + }, + "classmap": [ + "stubs/Stringable.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Marc Bennewitz", + "email": "dev@mabe.berlin", + "homepage": "https://mabe.berlin/", + "role": "Lead" + } + ], + "description": "Simple and fast implementation of enumerations with native PHP", + "homepage": "https://github.com/marc-mabe/php-enum", + "keywords": [ + "enum", + "enum-map", + "enum-set", + "enumeration", + "enumerator", + "enummap", + "enumset", + "map", + "set", + "type", + "type-hint", + "typehint" + ], + "support": { + "issues": "https://github.com/marc-mabe/php-enum/issues", + "source": "https://github.com/marc-mabe/php-enum/tree/v4.7.1" + }, + "time": "2024-11-28T04:54:44+00:00" + }, + { + "name": "nikic/iter", + "version": "v2.4.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/iter.git", + "reference": "3f031ae08d82c4394410e76b88b441331a6fa15f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/iter/zipball/3f031ae08d82c4394410e76b88b441331a6fa15f", + "reference": "3f031ae08d82c4394410e76b88b441331a6fa15f", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "vimeo/psalm": "^4.18 || ^5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/iter.func.php", + "src/iter.php", + "src/iter.rewindable.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov", + "email": "nikic@php.net" + } + ], + "description": "Iteration primitives using generators", + "keywords": [ + "functional", + "generator", + "iterator" + ], + "support": { + "issues": "https://github.com/nikic/iter/issues", + "source": "https://github.com/nikic/iter/tree/v2.4.1" + }, + "time": "2024-03-19T20:45:05+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "react/promise", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/23444f53a813a3296c1368bb104793ce8d88f04a", + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a", + "shasum": "" + }, + "require": { + "php": ">=7.1.0" + }, + "require-dev": { + "phpstan/phpstan": "1.12.28 || 1.4.10", + "phpunit/phpunit": "^9.6 || ^7.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "React\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ], + "support": { + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v3.3.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-08-19T18:57:03+00:00" + }, + { + "name": "sebastian/diff", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:53:05+00:00" + }, + { + "name": "seld/jsonlint", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/jsonlint.git", + "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/1748aaf847fc731cfad7725aec413ee46f0cc3a2", + "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2", + "shasum": "" + }, + "require": { + "php": "^5.3 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.11", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^8.5.13" + }, + "bin": [ + "bin/jsonlint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Seld\\JsonLint\\": "src/Seld/JsonLint/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "JSON Linter", + "keywords": [ + "json", + "linter", + "parser", + "validator" + ], + "support": { + "issues": "https://github.com/Seldaek/jsonlint/issues", + "source": "https://github.com/Seldaek/jsonlint/tree/1.11.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint", + "type": "tidelift" + } + ], + "time": "2024-07-11T14:55:45+00:00" + }, + { + "name": "seld/phar-utils", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/phar-utils.git", + "reference": "ea2f4014f163c1be4c601b9b7bd6af81ba8d701c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/ea2f4014f163c1be4c601b9b7bd6af81ba8d701c", + "reference": "ea2f4014f163c1be4c601b9b7bd6af81ba8d701c", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Seld\\PharUtils\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "PHAR file format utilities, for when PHP phars you up", + "keywords": [ + "phar" + ], + "support": { + "issues": "https://github.com/Seldaek/phar-utils/issues", + "source": "https://github.com/Seldaek/phar-utils/tree/1.2.1" + }, + "time": "2022-08-31T10:31:18+00:00" + }, + { + "name": "seld/signal-handler", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/signal-handler.git", + "reference": "04a6112e883ad76c0ada8e4a9f7520bbfdb6bb98" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/signal-handler/zipball/04a6112e883ad76c0ada8e4a9f7520bbfdb6bb98", + "reference": "04a6112e883ad76c0ada8e4a9f7520bbfdb6bb98", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "require-dev": { + "phpstan/phpstan": "^1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1", + "phpstan/phpstan-strict-rules": "^1.3", + "phpunit/phpunit": "^7.5.20 || ^8.5.23", + "psr/log": "^1 || ^2 || ^3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Seld\\Signal\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Simple unix signal handler that silently fails where signals are not supported for easy cross-platform development", + "keywords": [ + "posix", + "sigint", + "signal", + "sigterm", + "unix" + ], + "support": { + "issues": "https://github.com/Seldaek/signal-handler/issues", + "source": "https://github.com/Seldaek/signal-handler/tree/2.0.2" + }, + "time": "2023-09-03T09:24:00+00:00" + }, + { + "name": "symfony/console", + "version": "v7.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/5f360ebc65c55265a74d23d7fe27f957870158a1", + "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.2" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-30T17:13:41+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v7.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/edcbb768a186b5c3f25d0643159a787d3e63b7fd", + "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v7.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", "type": "github" }, { @@ -512,39 +1767,193 @@ "type": "tidelift" } ], - "time": "2025-07-30T17:13:41+00:00" + "time": "2025-07-07T08:17:47+00:00" }, { - "name": "symfony/deprecation-contracts", - "version": "v3.6.0", + "name": "symfony/finder", + "version": "v7.3.2", "source": { "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + "url": "https://github.com/symfony/finder.git", + "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "url": "https://api.github.com/repos/symfony/finder/zipball/2a6614966ba1074fa93dae0bc804227422df4dfe", + "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v7.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-15T13:41:35+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" }, "type": "library", "extra": { "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" }, - "branch-alias": { - "dev-main": "3.6-dev" + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { "files": [ - "function.php" - ] + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -560,10 +1969,18 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "A generic function and convention to trigger deprecation notices", + "description": "Symfony polyfill for intl's grapheme_* functions", "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" }, "funding": [ { @@ -574,42 +1991,53 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2025-06-27T09:58:17+00:00" }, { - "name": "symfony/filesystem", - "version": "v7.3.2", + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd" + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/edcbb768a186b5c3f25d0643159a787d3e63b7fd", - "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8" + "php": ">=7.2" }, - "require-dev": { - "symfony/process": "^6.4|^7.0" + "suggest": { + "ext-intl": "For best performance" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\Filesystem\\": "" + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -617,19 +2045,27 @@ "MIT" ], "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Provides basic utilities for the filesystem", + "description": "Symfony polyfill for intl's Normalizer class and related functions", "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.3.2" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" }, "funding": [ { @@ -649,30 +2085,31 @@ "type": "tidelift" } ], - "time": "2025-07-07T08:17:47+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/polyfill-ctype", - "version": "v1.32.0", + "name": "symfony/polyfill-mbstring", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", "shasum": "" }, "require": { + "ext-iconv": "*", "php": ">=7.2" }, "provide": { - "ext-ctype": "*" + "ext-mbstring": "*" }, "suggest": { - "ext-ctype": "For best performance" + "ext-mbstring": "For best performance" }, "type": "library", "extra": { @@ -686,7 +2123,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" + "Symfony\\Polyfill\\Mbstring\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -695,24 +2132,25 @@ ], "authors": [ { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for ctype functions", + "description": "Symfony polyfill for the Mbstring extension", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "ctype", + "mbstring", "polyfill", - "portable" + "portable", + "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, "funding": [ { @@ -723,33 +2161,34 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2024-12-23T08:48:59+00:00" }, { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.32.0", + "name": "symfony/polyfill-php73", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f68c03565dcaaf25a890667542e8bd75fe7e5bb", + "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb", "shasum": "" }, "require": { "php": ">=7.2" }, - "suggest": { - "ext-intl": "For best performance" - }, "type": "library", "extra": { "thanks": { @@ -762,8 +2201,11 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - } + "Symfony\\Polyfill\\Php73\\": "" + }, + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -779,18 +2221,16 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for intl's grapheme_* functions", + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "grapheme", - "intl", "polyfill", "portable", "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php73/tree/v1.33.0" }, "funding": [ { @@ -801,6 +2241,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -809,25 +2253,22 @@ "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.32.0", + "name": "symfony/polyfill-php80", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "3833d7255cc303546435cb650316bff708a1c75c" + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", - "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", "shasum": "" }, "require": { "php": ">=7.2" }, - "suggest": { - "ext-intl": "For best performance" - }, "type": "library", "extra": { "thanks": { @@ -840,7 +2281,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + "Symfony\\Polyfill\\Php80\\": "" }, "classmap": [ "Resources/stubs" @@ -851,6 +2292,10 @@ "MIT" ], "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, { "name": "Nicolas Grekas", "email": "p@tchwork.com" @@ -860,18 +2305,16 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "intl", - "normalizer", "polyfill", "portable", "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" }, "funding": [ { @@ -882,37 +2325,34 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-01-02T08:10:11+00:00" }, { - "name": "symfony/polyfill-mbstring", - "version": "v1.32.0", + "name": "symfony/polyfill-php81", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", "shasum": "" }, "require": { - "ext-iconv": "*", "php": ">=7.2" }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, "type": "library", "extra": { "thanks": { @@ -925,8 +2365,11 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -942,17 +2385,16 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for the Mbstring extension", + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "mbstring", "polyfill", "portable", "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.33.0" }, "funding": [ { @@ -963,12 +2405,77 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-12-23T08:48:59+00:00" + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/process", + "version": "v7.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", + "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v7.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-04-17T09:11:12+00:00" }, { "name": "symfony/service-contracts", @@ -1055,16 +2562,16 @@ }, { "name": "symfony/string", - "version": "v7.3.0", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125" + "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f3570b8c61ca887a9e2938e85cb6458515d2b125", - "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125", + "url": "https://api.github.com/repos/symfony/string/zipball/42f505aff654e62ac7ac2ce21033818297ca89ca", + "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca", "shasum": "" }, "require": { @@ -1122,7 +2629,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.0" + "source": "https://github.com/symfony/string/tree/v7.3.2" }, "funding": [ { @@ -1133,12 +2640,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-20T20:19:01+00:00" + "time": "2025-07-10T08:47:49+00:00" }, { "name": "symfony/yaml", @@ -1574,23 +3085,24 @@ }, { "name": "ergebnis/json", - "version": "1.4.0", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/ergebnis/json.git", - "reference": "7656ac2aa6c2ca4408f96f599e9a17a22c464f69" + "reference": "fc4f1bdac2d54a3050dca16ef1fe16ae50993f7d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ergebnis/json/zipball/7656ac2aa6c2ca4408f96f599e9a17a22c464f69", - "reference": "7656ac2aa6c2ca4408f96f599e9a17a22c464f69", + "url": "https://api.github.com/repos/ergebnis/json/zipball/fc4f1bdac2d54a3050dca16ef1fe16ae50993f7d", + "reference": "fc4f1bdac2d54a3050dca16ef1fe16ae50993f7d", "shasum": "" }, "require": { "ext-json": "*", - "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "require-dev": { + "ergebnis/composer-normalize": "^2.44.0", "ergebnis/data-provider": "^3.3.0", "ergebnis/license": "^2.5.0", "ergebnis/php-cs-fixer-config": "^6.37.0", @@ -1607,6 +3119,9 @@ }, "type": "library", "extra": { + "branch-alias": { + "dev-main": "1.4-dev" + }, "composer-normalize": { "indent-size": 2, "indent-style": "space" @@ -1638,20 +3153,20 @@ "security": "https://github.com/ergebnis/json/blob/main/.github/SECURITY.md", "source": "https://github.com/ergebnis/json" }, - "time": "2024-11-17T11:51:22+00:00" + "time": "2025-08-19T10:33:00+00:00" }, { "name": "ergebnis/json-normalizer", - "version": "4.9.0", + "version": "4.10.0", "source": { "type": "git", "url": "https://github.com/ergebnis/json-normalizer.git", - "reference": "cc4dcf3890448572a2d9bea97133c4d860e59fb1" + "reference": "91b9914b13937926d3b1916fc72475703ce42156" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ergebnis/json-normalizer/zipball/cc4dcf3890448572a2d9bea97133c4d860e59fb1", - "reference": "cc4dcf3890448572a2d9bea97133c4d860e59fb1", + "url": "https://api.github.com/repos/ergebnis/json-normalizer/zipball/91b9914b13937926d3b1916fc72475703ce42156", + "reference": "91b9914b13937926d3b1916fc72475703ce42156", "shasum": "" }, "require": { @@ -1661,7 +3176,7 @@ "ergebnis/json-schema-validator": "^4.2.0", "ext-json": "*", "justinrainbow/json-schema": "^5.2.12 || ^6.0.0", - "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "require-dev": { "composer/semver": "^3.4.3", @@ -1720,24 +3235,24 @@ "security": "https://github.com/ergebnis/json-normalizer/blob/main/.github/SECURITY.md", "source": "https://github.com/ergebnis/json-normalizer" }, - "time": "2025-04-10T13:13:04+00:00" + "time": "2025-08-19T10:29:01+00:00" }, { "name": "ergebnis/json-pointer", - "version": "3.6.0", + "version": "3.7.0", "source": { "type": "git", "url": "https://github.com/ergebnis/json-pointer.git", - "reference": "4fc85d8edb74466d282119d8d9541ec7cffc0798" + "reference": "cf3301710c99b54b844426c9cc46037d264fdf70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ergebnis/json-pointer/zipball/4fc85d8edb74466d282119d8d9541ec7cffc0798", - "reference": "4fc85d8edb74466d282119d8d9541ec7cffc0798", + "url": "https://api.github.com/repos/ergebnis/json-pointer/zipball/cf3301710c99b54b844426c9cc46037d264fdf70", + "reference": "cf3301710c99b54b844426c9cc46037d264fdf70", "shasum": "" }, "require": { - "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "require-dev": { "ergebnis/composer-normalize": "^2.43.0", @@ -1793,28 +3308,29 @@ "security": "https://github.com/ergebnis/json-pointer/blob/main/.github/SECURITY.md", "source": "https://github.com/ergebnis/json-pointer" }, - "time": "2024-11-17T12:37:06+00:00" + "time": "2025-08-19T10:23:17+00:00" }, { "name": "ergebnis/json-printer", - "version": "3.7.0", + "version": "3.8.0", "source": { "type": "git", "url": "https://github.com/ergebnis/json-printer.git", - "reference": "ced41fce7854152f0e8f38793c2ffe59513cdd82" + "reference": "fe48d523392ab885bf581eb57f61526dcde44fbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ergebnis/json-printer/zipball/ced41fce7854152f0e8f38793c2ffe59513cdd82", - "reference": "ced41fce7854152f0e8f38793c2ffe59513cdd82", + "url": "https://api.github.com/repos/ergebnis/json-printer/zipball/fe48d523392ab885bf581eb57f61526dcde44fbc", + "reference": "fe48d523392ab885bf581eb57f61526dcde44fbc", "shasum": "" }, "require": { "ext-json": "*", "ext-mbstring": "*", - "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "require-dev": { + "ergebnis/composer-normalize": "^2.44.0", "ergebnis/data-provider": "^3.3.0", "ergebnis/license": "^2.5.0", "ergebnis/php-cs-fixer-config": "^6.37.0", @@ -1830,6 +3346,15 @@ "rector/rector": "^1.2.10" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.7-dev" + }, + "composer-normalize": { + "indent-size": 2, + "indent-style": "space" + } + }, "autoload": { "psr-4": { "Ergebnis\\Json\\Printer\\": "src/" @@ -1858,20 +3383,20 @@ "security": "https://github.com/ergebnis/json-printer/blob/main/.github/SECURITY.md", "source": "https://github.com/ergebnis/json-printer" }, - "time": "2024-11-17T11:20:51+00:00" + "time": "2025-08-19T10:18:47+00:00" }, { "name": "ergebnis/json-schema-validator", - "version": "4.4.0", + "version": "4.5.0", "source": { "type": "git", "url": "https://github.com/ergebnis/json-schema-validator.git", - "reference": "85f90c81f718aebba1d738800af83eeb447dc7ec" + "reference": "0c8349baba70fd2449b799bc73650985c27ed321" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ergebnis/json-schema-validator/zipball/85f90c81f718aebba1d738800af83eeb447dc7ec", - "reference": "85f90c81f718aebba1d738800af83eeb447dc7ec", + "url": "https://api.github.com/repos/ergebnis/json-schema-validator/zipball/0c8349baba70fd2449b799bc73650985c27ed321", + "reference": "0c8349baba70fd2449b799bc73650985c27ed321", "shasum": "" }, "require": { @@ -1879,7 +3404,7 @@ "ergebnis/json-pointer": "^3.4.0", "ext-json": "*", "justinrainbow/json-schema": "^5.2.12 || ^6.0.0", - "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "require-dev": { "ergebnis/composer-normalize": "^2.44.0", @@ -1935,7 +3460,7 @@ "security": "https://github.com/ergebnis/json-schema-validator/blob/main/.github/SECURITY.md", "source": "https://github.com/ergebnis/json-schema-validator" }, - "time": "2024-11-18T06:32:28+00:00" + "time": "2025-08-19T08:41:00+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -1966,102 +3491,27 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1-dev" - } - }, - "autoload": { - "classmap": [ - "hamcrest" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "This is the PHP port of Hamcrest Matchers", - "keywords": [ - "test" - ], - "support": { - "issues": "https://github.com/hamcrest/hamcrest-php/issues", - "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1" - }, - "time": "2025-04-30T06:54:44+00:00" - }, - { - "name": "justinrainbow/json-schema", - "version": "6.4.2", - "source": { - "type": "git", - "url": "https://github.com/jsonrainbow/json-schema.git", - "reference": "ce1fd2d47799bb60668643bc6220f6278a4c1d02" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/ce1fd2d47799bb60668643bc6220f6278a4c1d02", - "reference": "ce1fd2d47799bb60668643bc6220f6278a4c1d02", - "shasum": "" - }, - "require": { - "ext-json": "*", - "marc-mabe/php-enum": "^4.0", - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "3.3.0", - "json-schema/json-schema-test-suite": "1.2.0", - "marc-mabe/php-enum-phpstan": "^2.0", - "phpspec/prophecy": "^1.19", - "phpstan/phpstan": "^1.12", - "phpunit/phpunit": "^8.5" - }, - "bin": [ - "bin/validate-json" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.x-dev" + "dev-master": "2.1-dev" } }, "autoload": { - "psr-4": { - "JsonSchema\\": "src/JsonSchema/" - } + "classmap": [ + "hamcrest" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bruno Prieto Reis", - "email": "bruno.p.reis@gmail.com" - }, - { - "name": "Justin Rainbow", - "email": "justin.rainbow@gmail.com" - }, - { - "name": "Igor Wiedler", - "email": "igor@wiedler.ch" - }, - { - "name": "Robert Schönthal", - "email": "seroscho@googlemail.com" - } + "BSD-3-Clause" ], - "description": "A library to validate a json schema.", - "homepage": "https://github.com/jsonrainbow/json-schema", + "description": "This is the PHP port of Hamcrest Matchers", "keywords": [ - "json", - "schema" + "test" ], "support": { - "issues": "https://github.com/jsonrainbow/json-schema/issues", - "source": "https://github.com/jsonrainbow/json-schema/tree/6.4.2" + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1" }, - "time": "2025-06-03T18:27:04+00:00" + "time": "2025-04-30T06:54:44+00:00" }, { "name": "laravel/serializable-closure", @@ -2179,79 +3629,6 @@ }, "time": "2024-12-04T14:16:01+00:00" }, - { - "name": "marc-mabe/php-enum", - "version": "v4.7.1", - "source": { - "type": "git", - "url": "https://github.com/marc-mabe/php-enum.git", - "reference": "7159809e5cfa041dca28e61f7f7ae58063aae8ed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/marc-mabe/php-enum/zipball/7159809e5cfa041dca28e61f7f7ae58063aae8ed", - "reference": "7159809e5cfa041dca28e61f7f7ae58063aae8ed", - "shasum": "" - }, - "require": { - "ext-reflection": "*", - "php": "^7.1 | ^8.0" - }, - "require-dev": { - "phpbench/phpbench": "^0.16.10 || ^1.0.4", - "phpstan/phpstan": "^1.3.1", - "phpunit/phpunit": "^7.5.20 | ^8.5.22 | ^9.5.11", - "vimeo/psalm": "^4.17.0 | ^5.26.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-3.x": "3.2-dev", - "dev-master": "4.7-dev" - } - }, - "autoload": { - "psr-4": { - "MabeEnum\\": "src/" - }, - "classmap": [ - "stubs/Stringable.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Marc Bennewitz", - "email": "dev@mabe.berlin", - "homepage": "https://mabe.berlin/", - "role": "Lead" - } - ], - "description": "Simple and fast implementation of enumerations with native PHP", - "homepage": "https://github.com/marc-mabe/php-enum", - "keywords": [ - "enum", - "enum-map", - "enum-set", - "enumeration", - "enumerator", - "enummap", - "enumset", - "map", - "set", - "type", - "type-hint", - "typehint" - ], - "support": { - "issues": "https://github.com/marc-mabe/php-enum/issues", - "source": "https://github.com/marc-mabe/php-enum/tree/v4.7.1" - }, - "time": "2024-11-28T04:54:44+00:00" - }, { "name": "mockery/mockery", "version": "1.6.12", @@ -2397,16 +3774,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.6.0", + "version": "v5.6.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56" + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/221b0d0fdf1369c71047ad1d18bb5880017bbc56", - "reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", "shasum": "" }, "require": { @@ -2425,7 +3802,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -2449,9 +3826,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1" }, - "time": "2025-07-27T20:03:57+00:00" + "time": "2025-08-13T20:13:15+00:00" }, { "name": "phar-io/manifest", @@ -3013,16 +4390,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.5.31", + "version": "11.5.34", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "fc44414e0779e94640663b809557b0b599548260" + "reference": "3e4c6ef395f7cb61a6206c23e0e04b31724174f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fc44414e0779e94640663b809557b0b599548260", - "reference": "fc44414e0779e94640663b809557b0b599548260", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3e4c6ef395f7cb61a6206c23e0e04b31724174f2", + "reference": "3e4c6ef395f7cb61a6206c23e0e04b31724174f2", "shasum": "" }, "require": { @@ -3094,7 +4471,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.31" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.34" }, "funding": [ { @@ -3118,20 +4495,20 @@ "type": "tidelift" } ], - "time": "2025-08-11T05:27:39+00:00" + "time": "2025-08-20T14:41:45+00:00" }, { "name": "rector/rector", - "version": "2.1.2", + "version": "2.1.4", "source": { "type": "git", "url": "https://github.com/rectorphp/rector.git", - "reference": "40a71441dd73fa150a66102f5ca1364c44fc8fff" + "reference": "fe613c528819222f8686a9a037a315ef9d4915b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rectorphp/rector/zipball/40a71441dd73fa150a66102f5ca1364c44fc8fff", - "reference": "40a71441dd73fa150a66102f5ca1364c44fc8fff", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/fe613c528819222f8686a9a037a315ef9d4915b3", + "reference": "fe613c528819222f8686a9a037a315ef9d4915b3", "shasum": "" }, "require": { @@ -3170,7 +4547,7 @@ ], "support": { "issues": "https://github.com/rectorphp/rector/issues", - "source": "https://github.com/rectorphp/rector/tree/2.1.2" + "source": "https://github.com/rectorphp/rector/tree/2.1.4" }, "funding": [ { @@ -3178,7 +4555,7 @@ "type": "github" } ], - "time": "2025-07-17T19:30:06+00:00" + "time": "2025-08-15T14:41:36+00:00" }, { "name": "sebastian/cli-parser", @@ -3890,23 +5267,23 @@ }, { "name": "sebastian/recursion-context", - "version": "6.0.2", + "version": "6.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "694d156164372abbd149a4b85ccda2e4670c0e16" + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/694d156164372abbd149a4b85ccda2e4670c0e16", - "reference": "694d156164372abbd149a4b85ccda2e4670c0e16", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc", "shasum": "" }, "require": { "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { @@ -3942,15 +5319,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.2" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2024-07-03T05:10:34+00:00" + "time": "2025-08-13T04:42:22+00:00" }, { "name": "sebastian/type", @@ -4333,135 +5722,6 @@ ], "time": "2024-10-20T05:08:20+00:00" }, - { - "name": "symfony/finder", - "version": "v7.3.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/2a6614966ba1074fa93dae0bc804227422df4dfe", - "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "symfony/filesystem": "^6.4|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Finds files and directories via an intuitive fluent interface", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/finder/tree/v7.3.2" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-07-15T13:41:35+00:00" - }, - { - "name": "symfony/process", - "version": "v7.3.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", - "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Executes commands in sub-processes", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/process/tree/v7.3.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-04-17T09:11:12+00:00" - }, { "name": "theseer/tokenizer", "version": "1.2.3", diff --git a/.vortex/installer/phpstan.neon b/.vortex/installer/phpstan.neon index f69dc33f6..0e764f9b1 100644 --- a/.vortex/installer/phpstan.neon +++ b/.vortex/installer/phpstan.neon @@ -23,3 +23,8 @@ parameters: # this error. message: '#.*no value type specified in iterable type array.#' reportUnmatched: false + - + # Bound callbacks do not provide docblocks for parameters. + message: '#Undefined variable: \$this#' + paths: + - src/Prompts/Handlers/Tools.php diff --git a/.vortex/installer/src/Command/InstallCommand.php b/.vortex/installer/src/Command/InstallCommand.php index bf523a664..d85b07528 100644 --- a/.vortex/installer/src/Command/InstallCommand.php +++ b/.vortex/installer/src/Command/InstallCommand.php @@ -56,7 +56,7 @@ class InstallCommand extends Command { */ protected function configure(): void { $this->setName('Vortex Installer'); - $this->setDescription('Install Vortex CLI from remote or local repository.'); + $this->setDescription('Install Vortex from remote or local repository.'); $this->setHelp(<<getExtension(), ['yml', 'yaml'], TRUE)) { + $content = Yaml::collapseFirstEmptyLinesInLiteralBlock($content); + } } return $content; diff --git a/.vortex/installer/src/Prompts/Handlers/MachineName.php b/.vortex/installer/src/Prompts/Handlers/MachineName.php index 888eba75f..c69c10ee1 100644 --- a/.vortex/installer/src/Prompts/Handlers/MachineName.php +++ b/.vortex/installer/src/Prompts/Handlers/MachineName.php @@ -4,7 +4,7 @@ namespace DrevOps\VortexInstaller\Prompts\Handlers; -use DrevOps\VortexInstaller\Utils\Composer; +use DrevOps\VortexInstaller\Utils\JsonManipulator; use DrevOps\VortexInstaller\Utils\Converter; use DrevOps\VortexInstaller\Utils\Env; use DrevOps\VortexInstaller\Utils\File; @@ -54,20 +54,18 @@ public function default(array $responses): null|string|bool|array { * {@inheritdoc} */ public function discover(): null|string|bool|array { - $value = NULL; + $v = Env::getFromDotenv('VORTEX_PROJECT', $this->dstDir); - $from_env = Env::getFromDotenv('VORTEX_PROJECT', $this->dstDir); - if ($from_env) { - $value = $from_env; + if (!empty($v)) { + return $v; } - else { - $from_composerjson = Composer::getJsonValue('name', $this->dstDir . DIRECTORY_SEPARATOR . 'composer.json'); - if ($from_composerjson && preg_match('/([^\/]+)\/(.+)/', (string) $from_composerjson, $matches) && !empty($matches[2])) { - $value = $matches[2]; - } + + $v = JsonManipulator::fromFile($this->dstDir . '/composer.json')?->getProperty('name'); + if ($v && preg_match('/([^\/]+)\/(.+)/', (string) $v, $matches) && !empty($matches[2])) { + return trim($matches[2]); } - return $value; + return NULL; } /** diff --git a/.vortex/installer/src/Prompts/Handlers/Name.php b/.vortex/installer/src/Prompts/Handlers/Name.php index 4cd88940a..e54d1c34e 100644 --- a/.vortex/installer/src/Prompts/Handlers/Name.php +++ b/.vortex/installer/src/Prompts/Handlers/Name.php @@ -4,7 +4,7 @@ namespace DrevOps\VortexInstaller\Prompts\Handlers; -use DrevOps\VortexInstaller\Utils\Composer; +use DrevOps\VortexInstaller\Utils\JsonManipulator; use DrevOps\VortexInstaller\Utils\Converter; use DrevOps\VortexInstaller\Utils\File; @@ -50,14 +50,13 @@ public function default(array $responses): null|string|bool|array { * {@inheritdoc} */ public function discover(): null|string|bool|array { - $value = NULL; + $v = JsonManipulator::fromFile($this->dstDir . '/composer.json')?->getProperty('description'); - $description = Composer::getJsonValue('description', $this->dstDir . DIRECTORY_SEPARATOR . 'composer.json'); - if ($description && preg_match('/Drupal \d+ .* of ([0-9a-zA-Z\- ]+)(\s?\.|for)/', (string) $description, $matches) && !empty($matches[1])) { - $value = trim($matches[1]); + if ($v && preg_match('/Drupal \d+ .* of ([0-9a-zA-Z\- ]+)(\s?\.|for)/', (string) $v, $matches) && !empty($matches[1])) { + return trim($matches[1]); } - return $value; + return NULL; } /** diff --git a/.vortex/installer/src/Prompts/Handlers/Org.php b/.vortex/installer/src/Prompts/Handlers/Org.php index 2d9410bc4..054b213ab 100644 --- a/.vortex/installer/src/Prompts/Handlers/Org.php +++ b/.vortex/installer/src/Prompts/Handlers/Org.php @@ -4,7 +4,7 @@ namespace DrevOps\VortexInstaller\Prompts\Handlers; -use DrevOps\VortexInstaller\Utils\Composer; +use DrevOps\VortexInstaller\Utils\JsonManipulator; use DrevOps\VortexInstaller\Utils\Converter; use DrevOps\VortexInstaller\Utils\File; @@ -53,14 +53,13 @@ public function default(array $responses): null|string|bool|array { * {@inheritdoc} */ public function discover(): null|string|bool|array { - $value = NULL; + $v = JsonManipulator::fromFile($this->dstDir . '/composer.json')?->getProperty('description'); - $description = Composer::getJsonValue('description', $this->dstDir . DIRECTORY_SEPARATOR . 'composer.json'); - if ($description && preg_match('/Drupal \d+ .* of ([0-9a-zA-Z\- ]+) for ([0-9a-zA-Z\- ]+)/', (string) $description, $matches) && !empty($matches[2])) { - $value = $matches[2]; + if ($v && preg_match('/Drupal \d+ .* of ([0-9a-zA-Z\- ]+) for ([0-9a-zA-Z\- ]+)/', (string) $v, $matches) && !empty($matches[2])) { + return $matches[2]; } - return $value; + return NULL; } /** diff --git a/.vortex/installer/src/Prompts/Handlers/OrgMachineName.php b/.vortex/installer/src/Prompts/Handlers/OrgMachineName.php index 37296b56e..aa39b0a29 100644 --- a/.vortex/installer/src/Prompts/Handlers/OrgMachineName.php +++ b/.vortex/installer/src/Prompts/Handlers/OrgMachineName.php @@ -4,7 +4,7 @@ namespace DrevOps\VortexInstaller\Prompts\Handlers; -use DrevOps\VortexInstaller\Utils\Composer; +use DrevOps\VortexInstaller\Utils\JsonManipulator; use DrevOps\VortexInstaller\Utils\Converter; use DrevOps\VortexInstaller\Utils\File; @@ -53,14 +53,13 @@ public function default(array $responses): null|string|bool|array { * {@inheritdoc} */ public function discover(): null|string|bool|array { - $value = NULL; + $v = JsonManipulator::fromFile($this->dstDir . '/composer.json')?->getProperty('name'); - $from_composerjson = Composer::getJsonValue('name', $this->dstDir . DIRECTORY_SEPARATOR . 'composer.json'); - if ($from_composerjson && preg_match('/([^\/]+)\/(.+)/', (string) $from_composerjson, $matches) && !empty($matches[1])) { - $value = $matches[1]; + if ($v && preg_match('/([^\/]+)\/(.+)/', (string) $v, $matches) && !empty($matches[1])) { + return $matches[1]; } - return $value; + return NULL; } /** diff --git a/.vortex/installer/src/Prompts/Handlers/Services.php b/.vortex/installer/src/Prompts/Handlers/Services.php index ea237ed83..009858198 100644 --- a/.vortex/installer/src/Prompts/Handlers/Services.php +++ b/.vortex/installer/src/Prompts/Handlers/Services.php @@ -5,7 +5,7 @@ namespace DrevOps\VortexInstaller\Prompts\Handlers; use DrevOps\VortexInstaller\Utils\File; -use Symfony\Component\Yaml\Yaml; +use DrevOps\VortexInstaller\Utils\Yaml; class Services extends AbstractHandler { @@ -26,7 +26,7 @@ public function label(): string { * {@inheritdoc} */ public function hint(array $responses): ?string { - return 'Select the services you want to use in the project.'; + return 'Use up and down arrows to select tools. Press space to toggle selection.'; } /** diff --git a/.vortex/installer/src/Prompts/Handlers/Tools.php b/.vortex/installer/src/Prompts/Handlers/Tools.php new file mode 100644 index 000000000..7c8505ab9 --- /dev/null +++ b/.vortex/installer/src/Prompts/Handlers/Tools.php @@ -0,0 +1,337 @@ + $config) { + $options[$tool] = $config['title']; + } + return $options; + } + + /** + * {@inheritdoc} + */ + public function default(array $responses): null|string|bool|array { + return [self::PHPCS, self::PHPMD, self::PHPSTAN, self::RECTOR, self::PHPUNIT, self::BEHAT]; + } + + /** + * {@inheritdoc} + */ + public function discover(): null|string|bool|array { + if (!$this->isInstalled()) { + return NULL; + } + + $tools = []; + + $cj = JsonManipulator::fromFile($this->dstDir . '/composer.json'); + if ($cj instanceof JsonManipulator) { + foreach (static::getToolDefinitions('tools') as $tool => $config) { + if (isset($config['present']) && $config['present'] instanceof \Closure && $config['present']->bindTo($this)()) { + $tools[] = $tool; + } + } + + sort($tools); + } + + return $tools; + } + + /** + * {@inheritdoc} + */ + public function process(): void { + $selected_tools = $this->getResponseAsArray(); + + $tools = static::getToolDefinitions('tools'); + $groups = static::getToolDefinitions('groups'); + + $missing_tools = array_diff_key($tools, array_flip($selected_tools)); + + foreach (array_keys($missing_tools) as $name) { + $this->processTool($name); + } + + foreach (array_keys($groups) as $name) { + $this->processGroup($name); + } + } + + protected function processTool(string $name): void { + $tool = static::getToolDefinitions('tools')[$name]; + + // Remove associated files. + if (isset($tool['files'])) { + if ($tool['files'] instanceof \Closure) { + $files = $tool['files']->bindTo($this)(); + $files = flatten($files); + } + else { + $files = $tool['files']; + $files = array_map(fn($file): string => $this->tmpDir . '/' . $file, $files); + } + File::remove($files); + } + + // Remove dependencies from composer.json. + if (isset($tool['composer.json']) && is_callable($tool['composer.json'])) { + $composer_path = $this->tmpDir . '/composer.json'; + $cj = JsonManipulator::fromFile($composer_path); + if ($cj instanceof JsonManipulator) { + $tool['composer.json']($cj); + file_put_contents($composer_path, $cj->getContents()); + } + } + + // Remove command definitions from Ahoy. + if (isset($tool['ahoy'])) { + foreach ($tool['ahoy'] as $string) { + File::replaceContentCallbackInFile($this->tmpDir . '/.ahoy.yml', function (string $content) use ($string): string { + $content = File::replaceContent($content, $string, ''); + return Yaml::collapseEmptyLinesInLiteralBlock($content); + }); + } + } + + File::replaceContentAsync( + function (string $content, ExtendedSplFileInfo $file) use ($tool): string { + if (isset($tool['strings'])) { + foreach ($tool['strings'] as $string) { + if (Strings::isRegex($string)) { + $replaced = preg_replace($string, '', $content, -1, $count); + + if ($count > 0) { + $content = $replaced; + } + } + else { + $content = str_replace($string, '', $content); + } + } + } + return $content; + } + ); + + File::removeTokenAsync('TOOL_' . strtoupper($name)); + } + + protected function processGroup(string $name): void { + $config = static::getToolDefinitions('goups')[$name]; + $selected_tools = $this->getResponseAsArray(); + + // Remove group Ahoy commands if no tools are selected. + if (isset($config['tools']) && !array_intersect($config['tools'], $selected_tools) && isset($config['ahoy'])) { + foreach ($config['ahoy'] as $string) { + File::replaceContentCallbackInFile($this->tmpDir . '/.ahoy.yml', function (string $content) use ($string): string { + $content = File::replaceContent($content, $string, ''); + return Yaml::collapseEmptyLinesInLiteralBlock($content); + }); + } + } + } + + public static function getToolDefinitions(string $filter = 'all'): array { + $filter = in_array($filter, ['all', 'tools', 'groups']) ? $filter : 'all'; + + $map = [ + self::PHPCS => [ + 'title' => '📏 PHP CodeSniffer', + 'present' => function (): mixed { + return File::contains($this->dstDir . '/composer.json', 'dealerdirect/phpcodesniffer-composer-installer') || + File::contains($this->dstDir . '/composer.json', 'drupal/coder') || + File::contains($this->dstDir . '/composer.json', 'squizlabs/php_codesniffer'); + }, + 'composer.json' => function (JsonManipulator $cj): void { + $cj->removeSubNode('require-dev', 'dealerdirect/phpcodesniffer-composer-installer'); + $cj->removeSubNode('require-dev', 'drupal/coder'); + $cj->removeSubNode('require-dev', 'squizlabs/php_codesniffer'); + }, + 'files' => ['phpcs.xml'], + 'strings' => [ + '/^.*\bphpcs\b.*\n?/m', + '/^.*\bphpcbf\b.*\n?/m', + ], + 'ahoy' => ['ahoy cli vendor/bin/phpcs', 'ahoy cli vendor/bin/phpcbf'], + ], + + self::PHPSTAN => [ + 'title' => '🔬 PHPStan', + 'present' => function (): mixed { + return File::contains($this->dstDir . '/composer.json', 'phpstan/phpstan') || + File::contains($this->dstDir . '/composer.json', 'mglaman/phpstan-drupal'); + }, + 'composer.json' => function (JsonManipulator $cj): void { + $cj->removeSubNode('require-dev', 'phpstan/phpstan'); + $cj->removeSubNode('require-dev', 'mglaman/phpstan-drupal'); + }, + 'files' => ['phpstan.neon'], + 'strings' => [ + '/^.*\bphpstan\b.*\n?/m', + '/^.*@phpstan.*\n?/m', + ], + 'ahoy' => ['ahoy cli vendor/bin/phpstan'], + ], + + self::RECTOR => [ + 'title' => '🔄 Rector', + 'present' => function (): mixed { + return File::contains($this->dstDir . '/composer.json', 'rector/rector'); + }, + 'composer.json' => function (JsonManipulator $cj): void { + $cj->removeSubNode('require-dev', 'rector/rector'); + }, + 'files' => ['rector.php'], + 'strings' => ['/^.*\brector\b.*\n?/m'], + 'ahoy' => ['ahoy cli vendor/bin/rector --clear-cache --dry-run', 'ahoy cli vendor/bin/rector --clear-cache'], + ], + + self::PHPMD => [ + 'title' => '🔍 PHP Mess Detector', + 'present' => function (): mixed { + return File::contains($this->dstDir . '/composer.json', 'phpmd/phpmd'); + }, + 'composer.json' => function (JsonManipulator $cj): void { + $cj->removeSubNode('require-dev', 'phpmd/phpmd'); + }, + 'files' => ['phpmd.xml'], + 'strings' => [ + '/^.*phpmd.*\n?/m', + '/^.*@SuppressWarnings.*\n?/m', + ], + 'ahoy' => ['ahoy cli vendor/bin/phpmd . text phpmd.xml'], + ], + + self::PHPUNIT => [ + 'title' => '🧪 PHPUnit', + 'present' => function (): mixed { + return File::contains($this->dstDir . '/composer.json', 'phpunit/phpunit'); + }, + 'composer.json' => function (JsonManipulator $cj): void { + $cj->removeSubNode('require-dev', 'phpunit/phpunit'); + $cj->removeProperty('autoload-dev.classmap'); + $cj->removeMainKeyIfEmpty('autoload-dev'); + }, + 'files' => fn(): array => [ + $this->tmpDir . '/phpunit.xml', + $this->tmpDir . '/tests/phpunit', + glob($this->tmpDir . '/' . $this->webroot . '/profiles/custom/*/tests', GLOB_ONLYDIR), + glob($this->tmpDir . '/' . $this->webroot . '/modules/custom/*/tests', GLOB_ONLYDIR), + glob($this->tmpDir . '/' . $this->webroot . '/themes/custom/*/tests', GLOB_ONLYDIR), + ], + 'strings' => ['/^.*phpunit.*\n?/m'], + 'ahoy' => [ + '/^.*phpunit.*\n?/m', + 'ahoy test-unit', + '/^\h*test-unit:\R\h*usage:\h*Run PHPUnit unit tests\.$/um', + 'ahoy test-kernel', + '/^\h*test-kernel:\R\h*usage:\h*Run PHPUnit kernel tests\.$/um', + 'ahoy test-functional', + '/^\h*test-functional:\R\h*usage:\h*Run PHPUnit functional tests\.$/um', + ], + ], + + self::BEHAT => [ + 'title' => '🥒 Behat', + 'present' => function (): mixed { + return File::contains($this->dstDir . '/composer.json', 'behat/behat') || + File::contains($this->dstDir . '/composer.json', 'drupal/drupal-extension'); + }, + 'composer.json' => function (JsonManipulator $cj): void { + $cj->removeSubNode('require-dev', 'behat/behat'); + $cj->removeSubNode('require-dev', 'drupal/drupal-extension'); + $cj->removeSubNode('require-dev', 'dantleech/gherkin-lint'); + }, + 'files' => [ + 'behat.yml', + 'tests/behat', + 'gherkinlint.json', + ], + 'strings' => [ + '/^.*\bbehat\b.*\n?/m', + '/^.*\bgherkinlint\b.*\n?/m', + ], + 'ahoy' => [ + '/^.*behat.*\n?/m', + 'ahoy test-bdd', + 'ahoy lint-tests', + '/^\h*test-bdd:\R\h*usage:\h*Run BDD tests\.$/um', + 'ahoy cli vendor/bin/gherkinlint lint tests/behat/features', + ], + ], + + // Tool groups with shared resources. + 'backend_linting' => [ + 'tools' => [self::PHPCS, self::PHPSTAN, self::RECTOR, self::PHPMD], + 'ahoy' => [ + 'ahoy lint-be-fix', + 'ahoy lint-be', + '/^\h*lint-be:\R\h*usage:\h*Lint back-end code\.\R\h*cmd:\h*\|\h*\R\h*$\R\h*$/um', + '/^\h*lint-be-fix:\R\h*usage:\h*Fix lint issues of back-end code\.\R\h*cmd:\h*\|\h*\R^\h*$/um', + '/^\h*lint:\R\h*usage:\h*Lint back-end and front-end code\.\R\h*cmd:\h*\|\h*\R\h*$\R\h*$/um', + ], + ], + 'test' => [ + 'tools' => [self::PHPUNIT, self::BEHAT], + 'ahoy' => [ + '/^\h*test:\R\h*usage:\h*Run all tests\.\R\h*cmd:\h*\|$/um', + '/^\h*lint-tests:\R\h*usage:\h*Lint tests code\.\R\h*cmd:\h*\|\h*\R^\h*$/um', + ], + ], + ]; + + if ($filter === 'tools') { + $map = array_filter($map, fn(array $tool): bool => !isset($tool['tools'])); + } + elseif ($filter === 'groups') { + $map = array_filter($map, fn(array $tool): bool => isset($tool['tools'])); + } + + return $map; + } + +} diff --git a/.vortex/installer/src/Prompts/Handlers/Webroot.php b/.vortex/installer/src/Prompts/Handlers/Webroot.php index 875bd00ee..2313f8fd6 100644 --- a/.vortex/installer/src/Prompts/Handlers/Webroot.php +++ b/.vortex/installer/src/Prompts/Handlers/Webroot.php @@ -4,7 +4,7 @@ namespace DrevOps\VortexInstaller\Prompts\Handlers; -use DrevOps\VortexInstaller\Utils\Composer; +use DrevOps\VortexInstaller\Utils\JsonManipulator; use DrevOps\VortexInstaller\Utils\Env; use DrevOps\VortexInstaller\Utils\File; use DrevOps\VortexInstaller\Utils\Validator; @@ -63,17 +63,19 @@ public function default(array $responses): null|string|bool|array { * {@inheritdoc} */ public function discover(): null|string|bool|array { - $value = Env::getFromDotenv('WEBROOT', $this->dstDir); + $v = Env::getFromDotenv('WEBROOT', $this->dstDir); - if (empty($value)) { - // Try from composer.json. - $extra = Composer::getJsonValue('extra', $this->dstDir . DIRECTORY_SEPARATOR . 'composer.json'); - if (!empty($extra)) { - $value = $extra['drupal-scaffold']['drupal-scaffold']['locations']['web-root'] ?? NULL; - } + if (!empty($v)) { + return $v; } - return $value; + $v = JsonManipulator::fromFile($this->dstDir . '/composer.json')?->getProperty('extra.drupal-scaffold.locations.web-root'); + + if (!empty($v)) { + return $v; + } + + return NULL; } /** diff --git a/.vortex/installer/src/Prompts/PromptManager.php b/.vortex/installer/src/Prompts/PromptManager.php index e009e48be..c74296038 100644 --- a/.vortex/installer/src/Prompts/PromptManager.php +++ b/.vortex/installer/src/Prompts/PromptManager.php @@ -32,6 +32,7 @@ use DrevOps\VortexInstaller\Prompts\Handlers\Services; use DrevOps\VortexInstaller\Prompts\Handlers\Theme; use DrevOps\VortexInstaller\Prompts\Handlers\Timezone; +use DrevOps\VortexInstaller\Prompts\Handlers\Tools; use DrevOps\VortexInstaller\Prompts\Handlers\Webroot; use DrevOps\VortexInstaller\Utils\Config; use DrevOps\VortexInstaller\Utils\Converter; @@ -60,7 +61,7 @@ class PromptManager { * * Used to display the progress of the prompts. */ - const TOTAL_RESPONSES = 23; + const TOTAL_RESPONSES = 24; /** * Array of responses. @@ -139,6 +140,7 @@ public function runPrompts(): void { ->intro('Environment') ->add(fn($r, $pr, $n): string => suggest(...$this->args(Timezone::class)), Timezone::id()) ->add(fn($r, $pr, $n): array => multiselect(...$this->args(Services::class)), Services::id()) + ->add(fn($r, $pr, $n): array => multiselect(...$this->args(Tools::class)), Tools::id()) ->intro('Hosting') ->add(fn($r, $pr, $n): int|string => select(...$this->args(HostingProvider::class)), HostingProvider::id()) @@ -257,6 +259,7 @@ public function runProcessors(): void { ProvisionType::id(), DeployType::id(), HostingProvider::id(), + Tools::id(), Services::id(), Timezone::id(), CodeProvider::id(), @@ -335,6 +338,12 @@ public function getResponsesSummary(): array { $values['ClamAV'] = Converter::bool(in_array(Services::CLAMAV, $responses[Services::id()])); $values['Solr'] = Converter::bool(in_array(Services::SOLR, $responses[Services::id()])); $values['Valkey'] = Converter::bool(in_array(Services::VALKEY, $responses[Services::id()])); + $values['PHP CodeSniffer'] = Converter::bool(in_array(Tools::PHPCS, $responses[Tools::id()])); + $values['PHP Mess Detector'] = Converter::bool(in_array(Tools::PHPMD, $responses[Tools::id()])); + $values['PHPStan'] = Converter::bool(in_array(Tools::PHPSTAN, $responses[Tools::id()])); + $values['Rector'] = Converter::bool(in_array(Tools::RECTOR, $responses[Tools::id()])); + $values['PHPUnit'] = Converter::bool(in_array(Tools::PHPUNIT, $responses[Tools::id()])); + $values['Behat'] = Converter::bool(in_array(Tools::BEHAT, $responses[Tools::id()])); $values['Hosting'] = Tui::LIST_SECTION_TITLE; $values['Hosting provider'] = $responses[HostingProvider::id()]; @@ -423,7 +432,7 @@ protected function initHandlers(): void { throw new \RuntimeException(sprintf('Could not read the directory "%s".', $dir)); } - $handler_files = array_filter($files, function ($file): bool { + $handler_files = array_filter($files, function (string $file): bool { return !in_array($file, ['.', '..']); }); @@ -492,6 +501,7 @@ private function args(string $handler_class, mixed $default_override = NULL, arr $options = $handler->options($responses); if (is_array($options) && $options !== []) { $args['options'] = $options; + $args['scroll'] = 10; } // Find appropriate default value. diff --git a/.vortex/installer/src/Utils/Composer.php b/.vortex/installer/src/Utils/Composer.php deleted file mode 100644 index 304877680..000000000 --- a/.vortex/installer/src/Utils/Composer.php +++ /dev/null @@ -1,36 +0,0 @@ -contents = $contents; + parent::__construct($contents); + } + + public static function fromFile(string $composer_json): ?self { + if (!is_readable($composer_json) || !is_file($composer_json)) { + return NULL; + } + + $contents = file_get_contents($composer_json); + if ($contents === FALSE) { + throw new \RuntimeException('Failed to read composer.json from ' . $composer_json); + } + + return new self($contents); + } + + /** + * Get the value of a composer.json key. + * + * @param string $name + * Name of the key. + * + * @return mixed|null + * Value of the key or NULL if not found. + */ + public function getProperty(string $name): mixed { + $sub = explode('.', $name); + $main = array_shift($sub); + + $decoded = JsonFile::parseJson($this->contents); + + if (!isset($decoded[$main])) { + return NULL; + } + + if (empty($sub)) { + return $decoded[$main]; + } + + // Collect from the sub-keys. + $arr = $decoded[$main]; + + foreach ($sub as $key) { + if (is_array($arr) && array_key_exists($key, $arr)) { + $arr = $arr[$key]; + } + else { + return NULL; + } + } + + return $arr; + } + +} diff --git a/.vortex/installer/src/Utils/Strings.php b/.vortex/installer/src/Utils/Strings.php index 869d40170..4bf3d98cd 100644 --- a/.vortex/installer/src/Utils/Strings.php +++ b/.vortex/installer/src/Utils/Strings.php @@ -16,4 +16,140 @@ public static function strlenPlain(string $text): int { return mb_strwidth($clean_text, 'UTF-8'); } + /** + * Checks if a string is a valid regular expression. + * + * @param string $string + * The string to check. + * + * @return bool + * TRUE if the string is a valid regex, FALSE otherwise. + */ + public static function isRegex(string $string): bool { + if ($string === '' || strlen($string) < 3) { + return FALSE; + } + + // Extract the first character as the delimiter. + $delimiter = $string[0]; + + if (!in_array($delimiter, ['/', '#', '~'])) { + return FALSE; + } + + $last_char = substr($string, -1); + $before_last_char = substr($string, -2, 1); + if ( + ($last_char !== $delimiter && !in_array($last_char, ['i', 'm', 's'])) + || ($before_last_char !== $delimiter && in_array($before_last_char, ['i', 'm', 's'])) + ) { + return FALSE; + } + + // Test the regex. + $result = preg_match($string, ''); + return $result !== FALSE && preg_last_error() === PREG_NO_ERROR; + } + + /** + * Collapse consecutive empty lines within PHP block comments. + * + * Also removes leading/trailing empty lines and removes entirely empty + * docblocks. + */ + public static function collapsePhpBlockCommentsEmptyLines(string $content): string { + // Use simpler regex approach with direct string replacement. + return preg_replace_callback( + '/^(\s*)\/\*\*(.*?)\*\/(\n)?/ms', + [self::class, 'processDocblock'], + $content + ); + } + + private static function processDocblock(array $matches): string { + $full_match = $matches[0]; + $leading_whitespace = $matches[1]; + $comment_content = $matches[2]; + $following_newline = $matches[3] ?? ''; + + // Single-line docblocks - return unchanged. + if (!str_contains($comment_content, "\n")) { + return $full_match; + } + + // Split into lines and process. + $lines = explode("\n", $comment_content); + + // Remove leading/trailing empty lines and check for content in one pass. + $start = 0; + $end = count($lines) - 1; + $has_content = FALSE; + + // Find first non-empty line. + while ($start <= $end) { + $line = trim($lines[$start]); + if ($line !== '' && !preg_match('/^\*\s*$/', $line)) { + $has_content = TRUE; + break; + } + $start++; + } + + // Find last non-empty line. + while ($end >= $start) { + $line = trim($lines[$end]); + if ($line !== '' && !preg_match('/^\*\s*$/', $line)) { + break; + } + $end--; + } + + // No content - remove entire docblock. + if (!$has_content) { + return ''; + } + + // Extract working lines. + $work_lines = array_slice($lines, $start, $end - $start + 1); + + // Get indentation pattern from first line. + $indent_pattern = ' *'; + if (!empty($work_lines) && preg_match('/^(\s*\*)/', $work_lines[0], $indent_matches)) { + $indent_pattern = $indent_matches[1]; + } + + // Collapse consecutive empty lines. + $result_lines = []; + $prev_empty = FALSE; + + foreach ($work_lines as $line) { + $line_content = preg_replace('/^\s*\*\s*/', '', $line); + $is_empty = trim($line_content) === ''; + + if ($is_empty) { + if (!$prev_empty) { + $result_lines[] = $indent_pattern; + } + $prev_empty = TRUE; + } + else { + $result_lines[] = $line; + $prev_empty = FALSE; + } + } + + // Reconstruct docblock. + if (empty($result_lines)) { + return ''; + } + + // Get closing indentation from original match. + $closing_indent = ' '; + if (preg_match('/\n(\s*)\*\/$/', $full_match, $close_matches)) { + $closing_indent = $close_matches[1]; + } + + return $leading_whitespace . '/**' . "\n" . implode("\n", $result_lines) . "\n" . $closing_indent . '*/' . $following_newline; + } + } diff --git a/.vortex/installer/src/Utils/Tui.php b/.vortex/installer/src/Utils/Tui.php index ec11e6396..7714c8362 100644 --- a/.vortex/installer/src/Utils/Tui.php +++ b/.vortex/installer/src/Utils/Tui.php @@ -205,7 +205,7 @@ public static function undim(string $text): string { public static function escapeMultiline(string $text, int $color_code, int $end_code = 39): string { $lines = explode("\n", $text); - $colored_lines = array_map(function ($line) use ($color_code, $end_code): string { + $colored_lines = array_map(function (string $line) use ($color_code, $end_code): string { return sprintf("\033[%sm%s\033[%sm", $color_code, $line, $end_code); }, $lines); return implode("\n", $colored_lines); @@ -258,7 +258,7 @@ public static function normalizeText(string $text): string { preg_match_all('/\X/u', $text, $matches); $utf8_chars = $matches[0]; - $utf8_chars = array_map(fn($char): string => Strings::isAsciiStart($char) === 0 ? $char . static::utfPadding($char) : $char, $utf8_chars); + $utf8_chars = array_map(fn(string $char): string => Strings::isAsciiStart($char) === 0 ? $char . static::utfPadding($char) : $char, $utf8_chars); return implode('', $utf8_chars); } diff --git a/.vortex/installer/src/Utils/Yaml.php b/.vortex/installer/src/Utils/Yaml.php new file mode 100644 index 000000000..a00616aa3 --- /dev/null +++ b/.vortex/installer/src/Utils/Yaml.php @@ -0,0 +1,115 @@ + $line, + 'is_empty' => $trimmed === '', + 'indent' => strlen($line) - strlen(ltrim($line)), + 'is_literal_start' => preg_match('/^(\h*)\w+:\h*\|/', $line, $matches) ? strlen($matches[1]) : -1, + 'is_yaml_key' => $trimmed !== '' && preg_match('/^(\h*)\w+:/', $line, $matches) ? strlen($matches[1]) : -1, + ]; + } + + // Track current literal block state to avoid repeated lookups. + $current_block_indent = -1; + $current_block_start = -1; + + for ($i = 0; $i < $line_count; $i++) { + $data = $line_data[$i]; + + // Update block state when encountering literal block starts. + if ($data['is_literal_start'] >= 0) { + $current_block_indent = $data['is_literal_start']; + $current_block_start = $i; + } + // Update block state when encountering lines that end the block. + elseif ($current_block_indent >= 0 && !$data['is_empty'] && $data['indent'] <= $current_block_indent) { + $current_block_indent = -1; + $current_block_start = -1; + } + + // Handle empty lines. + if ($data['is_empty']) { + // If not in a literal block, keep the line. + if ($current_block_indent < 0) { + $result_lines[] = $data['line']; + continue; + } + + // In a literal block - check if next non-empty line ends the block. + $block_ends = FALSE; + for ($j = $i + 1; $j < $line_count; $j++) { + $ahead_data = $line_data[$j]; + if (!$ahead_data['is_empty']) { + if ($ahead_data['indent'] <= $current_block_indent) { + $block_ends = TRUE; + $current_block_indent = -1; + $current_block_start = -1; + } + break; + } + } + + // Keep line if block ends, skip if within block. + if ($block_ends) { + $result_lines[] = $data['line']; + } + continue; + } + + $result_lines[] = $data['line']; + } + + return implode("\n", $result_lines); + } + + /** + * Collapse *first* repeated empty lines within YAML literal block. + * + * This function specifically targets YAML literal block indicated by the + * pipe character (|) and collapses multiple consecutive empty lines that + * occur immediately after the pipe. Empty lines in other parts of the + * content block are left unchanged. + * + * @param string $content + * The YAML content to process, which may contain literal blocks. + * + * @return string + * The processed content. + */ + public static function collapseFirstEmptyLinesInLiteralBlock(string $content): string { + return preg_replace('/(?<=\|)(\n\s*\n)+/', "\n", $content); + } + +} diff --git a/.vortex/installer/tests/Fixtures/install/_baseline/.ahoy.yml b/.vortex/installer/tests/Fixtures/install/_baseline/.ahoy.yml index bcf79ec04..f8c81cbb5 100644 --- a/.vortex/installer/tests/Fixtures/install/_baseline/.ahoy.yml +++ b/.vortex/installer/tests/Fixtures/install/_baseline/.ahoy.yml @@ -75,12 +75,12 @@ commands: usage: Show container logs for services. cmd: docker compose logs "$@" + # Drop into a shell if no arguments are supplied, otherwise run the command. + # Environment variables are passed from the host and filtered by prefix. + # Use \" (slash followed by a double quote) to escape double quotes in + # arguments that contain spaces. cli: usage: Start a shell or run a command inside the CLI service container. - # Drop into a shell if no arguments are supplied, otherwise run the command. - # Environment variables are passed from the host and filtered by prefix. - # Use \" (slash followed by a double quote) to escape double quotes in - # arguments that contain spaces. cmd: | if [ "${#}" -ne 0 ]; then docker compose exec $(env | cut -f1 -d= | grep "TERM\|COMPOSE_\|GITHUB_\|PACKAGE_\|DOCKER_\|DRUPAL_\|VORTEX_" | sed 's/^/-e /') cli bash -c "$*" @@ -114,7 +114,7 @@ commands: download-db: usage: Download database. Run with "--no-cache" option to force fresh database backup. - aliases: ['fetch-db'] + aliases: [fetch-db] cmd: | case " $* " in *" --no-cache "*) export VORTEX_DB_DOWNLOAD_NO_CACHE=1;; esac ./scripts/vortex/download-db.sh @@ -149,7 +149,7 @@ commands: cmd: \[ -n "${VORTEX_DB_IMAGE}" \] && docker pull ${VORTEX_DB_IMAGE} || true reset: - usage: "Remove containers, all build files. Use with `hard` to reset repository to the last commit." + usage: Remove containers, all build files. Use with `hard` to reset repository to the last commit. cmd: | ahoy confirm "All containers and build files will be removed. Proceed?" || exit 0 AHOY_CONFIRM_RESPONSE=y ahoy down @@ -177,7 +177,10 @@ commands: lint: usage: Lint back-end and front-end code. - cmd: ahoy lint-be && ahoy lint-fe && ahoy lint-tests + cmd: | + ahoy lint-be + ahoy lint-fe + ahoy lint-tests lint-be: usage: Lint back-end code. @@ -196,11 +199,14 @@ commands: lint-tests: usage: Lint tests code. - cmd: ahoy cli vendor/bin/gherkinlint lint tests/behat/features + cmd: | + ahoy cli vendor/bin/gherkinlint lint tests/behat/features lint-fix: usage: Fix lint issues of back-end and front-end code. - cmd: ahoy lint-be-fix && ahoy lint-fe-fix + cmd: | + ahoy lint-be-fix + ahoy lint-fe-fix lint-be-fix: usage: Fix lint issues of back-end code. @@ -217,7 +223,11 @@ commands: test: usage: Run all tests. - cmd: ahoy test-unit && ahoy test-kernel && ahoy test-functional && ahoy test-bdd + cmd: | + ahoy test-unit + ahoy test-kernel + ahoy test-functional + ahoy test-bdd test-unit: usage: Run PHPUnit unit tests. @@ -233,7 +243,7 @@ commands: test-bdd: usage: Run BDD tests. - aliases: ['test-behat'] + aliases: [test-behat] cmd: ahoy cli php -d memory_limit=-1 vendor/bin/behat --colors "$@" debug: @@ -257,8 +267,7 @@ commands: local: usage: Custom local commands. See `ahoy local help`. optional: true - imports: - - .ahoy.local.yml + imports: [.ahoy.local.yml] # ---------------------------------------------------------------------------- # Utilities. @@ -290,15 +299,14 @@ commands: hide: true # Override entrypoint to alter default behavior of Ahoy. +# 1. Exit the script if any statement returns a non-true return value. +# 2. Read variables from .env file (while respecting existing values) to load +# and pass updated environment variables' values into already running +# containers. entrypoint: - bash - -c - # Exit the script if any statement returns a non-true return value. - -e - # Read variables from .env file, respecting existing values. - # - Used to load and pass updated environment variables' values into already - # running containers. - # - Use `ahoy up cli` in cases when changes require container restart. - | t=$(mktemp) && export -p > "$t" && set -a && . ./.env && if [ -f ./.env.local ];then . ./.env.local;fi && set +a && . "$t" && rm "$t" && unset t bash -e -c "$0" "$@" diff --git a/.vortex/installer/tests/Fixtures/install/_baseline/web/sites/default/includes/providers/settings.container.php b/.vortex/installer/tests/Fixtures/install/_baseline/web/sites/default/includes/providers/settings.container.php index 96d0ac9cc..4c85649c5 100644 --- a/.vortex/installer/tests/Fixtures/install/_baseline/web/sites/default/includes/providers/settings.container.php +++ b/.vortex/installer/tests/Fixtures/install/_baseline/web/sites/default/includes/providers/settings.container.php @@ -20,6 +20,6 @@ ], $vortex_localdev_url); $settings['trusted_host_patterns'][] = '^' . $patterns . '$'; - // URL when accessed from Behat tests. + // URL for internal container access (e.g., via drush, in tests etc.). $settings['trusted_host_patterns'][] = '^nginx$'; } diff --git a/.vortex/installer/tests/Fixtures/install/hosting_acquia/docroot/sites/default/includes/providers/settings.container.php b/.vortex/installer/tests/Fixtures/install/hosting_acquia/docroot/sites/default/includes/providers/settings.container.php index 96d0ac9cc..4c85649c5 100644 --- a/.vortex/installer/tests/Fixtures/install/hosting_acquia/docroot/sites/default/includes/providers/settings.container.php +++ b/.vortex/installer/tests/Fixtures/install/hosting_acquia/docroot/sites/default/includes/providers/settings.container.php @@ -20,6 +20,6 @@ ], $vortex_localdev_url); $settings['trusted_host_patterns'][] = '^' . $patterns . '$'; - // URL when accessed from Behat tests. + // URL for internal container access (e.g., via drush, in tests etc.). $settings['trusted_host_patterns'][] = '^nginx$'; } diff --git a/.vortex/installer/tests/Fixtures/install/provision_profile/.ahoy.yml b/.vortex/installer/tests/Fixtures/install/provision_profile/.ahoy.yml index f674ed387..5ebb8f70d 100644 --- a/.vortex/installer/tests/Fixtures/install/provision_profile/.ahoy.yml +++ b/.vortex/installer/tests/Fixtures/install/provision_profile/.ahoy.yml @@ -4,7 +4,7 @@ - download-db: - usage: Download database. Run with "--no-cache" option to force fresh database backup. -- aliases: ['fetch-db'] +- aliases: [fetch-db] - cmd: | - case " $* " in *" --no-cache "*) export VORTEX_DB_DOWNLOAD_NO_CACHE=1;; esac - ./scripts/vortex/download-db.sh diff --git a/.vortex/installer/tests/Fixtures/install/theme_absent/.ahoy.yml b/.vortex/installer/tests/Fixtures/install/theme_absent/.ahoy.yml index a1e7b8e96..9d6b0d1d6 100644 --- a/.vortex/installer/tests/Fixtures/install/theme_absent/.ahoy.yml +++ b/.vortex/installer/tests/Fixtures/install/theme_absent/.ahoy.yml @@ -6,7 +6,7 @@ ahoy provision # Provision the site. VORTEX_SHOW_LOGIN=1 ahoy info # Show information and a login link. -@@ -159,25 +158,10 @@ +@@ -159,22 +158,7 @@ usage: Install front-end assets. cmd: | ahoy cli "yarn install --frozen-lockfile" @@ -28,12 +28,8 @@ - lint: usage: Lint back-end and front-end code. -- cmd: ahoy lint-be && ahoy lint-fe && ahoy lint-tests -+ cmd: ahoy lint-be && ahoy lint-tests - - lint-be: - usage: Lint back-end code. -@@ -192,7 +176,6 @@ + cmd: | +@@ -195,7 +179,6 @@ cmd: | ahoy cli vendor/bin/twig-cs-fixer lint ahoy cli "yarn run lint" @@ -41,16 +37,7 @@ lint-tests: usage: Lint tests code. -@@ -200,7 +183,7 @@ - - lint-fix: - usage: Fix lint issues of back-end and front-end code. -- cmd: ahoy lint-be-fix && ahoy lint-fe-fix -+ cmd: ahoy lint-be-fix - - lint-be-fix: - usage: Fix lint issues of back-end code. -@@ -213,7 +196,6 @@ +@@ -219,7 +202,6 @@ cmd: | ahoy cli vendor/bin/twig-cs-fixer lint --fix ahoy cli "yarn run lint-fix" diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/-phpcs.xml b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/-phpcs.xml new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/-phpmd.xml b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/-phpmd.xml new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/-phpstan.neon b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/-phpstan.neon new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/-rector.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/-rector.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/.ahoy.yml b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/.ahoy.yml new file mode 100644 index 000000000..0eec4e46f --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/.ahoy.yml @@ -0,0 +1,34 @@ +@@ -178,18 +178,9 @@ + lint: + usage: Lint back-end and front-end code. + cmd: | +- ahoy lint-be + ahoy lint-fe + ahoy lint-tests + +- lint-be: +- usage: Lint back-end code. +- cmd: | +- ahoy cli vendor/bin/phpcs +- ahoy cli vendor/bin/phpstan +- ahoy cli vendor/bin/rector --clear-cache --dry-run +- ahoy cli vendor/bin/phpmd . text phpmd.xml +- + lint-fe: + usage: Lint front-end code. + cmd: | +@@ -205,14 +196,7 @@ + lint-fix: + usage: Fix lint issues of back-end and front-end code. + cmd: | +- ahoy lint-be-fix + ahoy lint-fe-fix +- +- lint-be-fix: +- usage: Fix lint issues of back-end code. +- cmd: | +- ahoy cli vendor/bin/rector --clear-cache +- ahoy cli vendor/bin/phpcbf + + lint-fe-fix: + usage: Fix lint issues of front-end code. diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/.dockerignore b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/.dockerignore new file mode 100644 index 000000000..9c0a97823 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/.dockerignore @@ -0,0 +1,11 @@ +@@ -42,10 +42,6 @@ + !package.json + !package-lock.json + !patches +-!phpcs.xml +-!phpmd.xml +-!phpstan.neon + !phpunit.xml +-!rector.php + !scripts + !tests diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/.github/workflows/build-test-deploy.yml b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/.github/workflows/build-test-deploy.yml new file mode 100644 index 000000000..81f0197e9 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/.github/workflows/build-test-deploy.yml @@ -0,0 +1,27 @@ +@@ -256,26 +256,6 @@ + run: docker compose exec -T cli composer normalize --dry-run + continue-on-error: ${{ vars.VORTEX_CI_COMPOSER_NORMALIZE_IGNORE_FAILURE == '1' }} + +- - name: Lint code with PHPCS +- if: ${{ matrix.instance == 0 || strategy.job-total == 1 }} +- run: docker compose exec -T cli vendor/bin/phpcs +- continue-on-error: ${{ vars.VORTEX_CI_PHPCS_IGNORE_FAILURE == '1' }} +- +- - name: Lint code with PHPStan +- if: ${{ matrix.instance == 0 || strategy.job-total == 1 }} +- run: docker compose exec -T cli vendor/bin/phpstan +- continue-on-error: ${{ vars.VORTEX_CI_PHPSTAN_IGNORE_FAILURE == '1' }} +- +- - name: Lint code with Rector +- if: ${{ matrix.instance == 0 || strategy.job-total == 1 }} +- run: docker compose exec -T cli vendor/bin/rector --clear-cache --dry-run +- continue-on-error: ${{ vars.VORTEX_CI_RECTOR_IGNORE_FAILURE == '1' }} +- +- - name: Lint code with PHPMD +- if: ${{ matrix.instance == 0 || strategy.job-total == 1 }} +- run: docker compose exec -T cli vendor/bin/phpmd . text phpmd.xml +- continue-on-error: ${{ vars.VORTEX_CI_PHPMD_IGNORE_FAILURE == '1' }} +- + - name: Lint code with Twig CS Fixer + if: ${{ matrix.instance == 0 || strategy.job-total == 1 }} + run: docker compose exec -T cli vendor/bin/twig-cs-fixer diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/composer.json b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/composer.json new file mode 100644 index 000000000..01b2927d0 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/composer.json @@ -0,0 +1,32 @@ +@@ -31,7 +31,6 @@ + "require-dev": { + "behat/behat": "__VERSION__", + "dantleech/gherkin-lint": "__VERSION__", +- "dealerdirect/phpcodesniffer-composer-installer": "__VERSION__", + "drevops/behat-format-progress-fail": "__VERSION__", + "drevops/behat-screenshot": "__VERSION__", + "drevops/behat-steps": "__VERSION__", +@@ -38,15 +37,9 @@ + "drupal/core-dev": "__VERSION__", + "drupal/drupal-extension": "__VERSION__", + "ergebnis/composer-normalize": "__VERSION__", +- "mglaman/phpstan-drupal": "__VERSION__", +- "palantirnet/drupal-rector": "__VERSION__", + "phpcompatibility/php-compatibility": "__VERSION__", +- "phpmd/phpmd": "__VERSION__", + "phpspec/prophecy-phpunit": "__VERSION__", +- "phpstan/extension-installer": "__VERSION__", +- "phpstan/phpstan": "__VERSION__", + "pyrech/composer-changelogs": "__VERSION__", +- "rector/rector": "__VERSION__", + "vincentlanglet/twig-cs-fixer": "__VERSION__" + }, + "conflict": { +@@ -79,7 +72,6 @@ + "ergebnis/composer-normalize": true, + "oomphinc/composer-installers-extender": true, + "php-http/discovery": true, +- "phpstan/extension-installer": true, + "pyrech/composer-changelogs": true, + "tbachert/spi": true + }, diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/scripts/composer/ScriptHandler.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/scripts/composer/ScriptHandler.php new file mode 100644 index 000000000..2fac00f59 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/scripts/composer/ScriptHandler.php @@ -0,0 +1,8 @@ +@@ -5,7 +5,6 @@ + * Contains \DrupalProject\composer\ScriptHandler. + * + * Clone of https://github.com/drupal-composer/drupal-project/blob/10.x/scripts/composer/ScriptHandler.php +- * phpcs:disable + */ + + namespace DrupalProject\composer; diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/tests/phpunit/Drupal/EnvironmentSettingsTest.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/tests/phpunit/Drupal/EnvironmentSettingsTest.php new file mode 100644 index 000000000..d4f30fe9a --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/tests/phpunit/Drupal/EnvironmentSettingsTest.php @@ -0,0 +1,11 @@ +@@ -15,10 +15,6 @@ + * The main purpose of these tests is to ensure that the settings and configs + * appear in every environment as expected. + * +- * phpcs:disable Squiz.WhiteSpace.FunctionSpacing.Before +- * phpcs:disable Squiz.WhiteSpace.FunctionSpacing.After +- * phpcs:disable Squiz.WhiteSpace.FunctionSpacing.AfterLast +- * phpcs:disable Drupal.Classes.ClassDeclaration.CloseBraceAfterBody + */ + #[Group('drupal_settings')] + class EnvironmentSettingsTest extends SettingsTestCase { diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/tests/phpunit/Drupal/SettingsTestCase.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/tests/phpunit/Drupal/SettingsTestCase.php new file mode 100644 index 000000000..89e8663a3 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/tests/phpunit/Drupal/SettingsTestCase.php @@ -0,0 +1,24 @@ +@@ -11,7 +11,6 @@ + * + * Base class for testing Drupal settings. + * +- * phpcs:disable Drupal.NamingConventions.ValidVariableName.LowerCamelName + */ + abstract class SettingsTestCase extends TestCase { + +@@ -129,7 +128,6 @@ + * @param array $vars + * Array of environment variables. + * +- * @SuppressWarnings("PHPMD.ElseExpression") + */ + protected function setEnvVars(array $vars): void { + // Unset the existing environment variable if not set in the test. +@@ -308,7 +306,6 @@ + * @param string $message + * Message to display on failure. + * +- * @SuppressWarnings("PHPMD.ElseExpression") + */ + protected function assertArraySubset(array $subset, array $haystack, string $message = ''): void { + foreach ($subset as $key => $value) { diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/modules/custom/sw_base/src/Plugin/Block/CounterBlock.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/modules/custom/sw_base/src/Plugin/Block/CounterBlock.php new file mode 100644 index 000000000..4301ae253 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/modules/custom/sw_base/src/Plugin/Block/CounterBlock.php @@ -0,0 +1,8 @@ +@@ -20,7 +20,6 @@ + /** + * {@inheritdoc} + * +- * @phpstan-ignore-next-line + */ + public function build(): array { + return [ diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/modules/custom/sw_base/sw_base.module b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/modules/custom/sw_base/sw_base.module new file mode 100644 index 000000000..f573df9a1 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/modules/custom/sw_base/sw_base.module @@ -0,0 +1,8 @@ +@@ -12,7 +12,6 @@ + /** + * Implements hook_theme(). + * +- * @phpstan-ignore-next-line + */ + function sw_base_theme(): array { + return [ diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/modules/custom/sw_base/tests/src/Functional/ExampleTest.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/modules/custom/sw_base/tests/src/Functional/ExampleTest.php new file mode 100644 index 000000000..e59020263 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/modules/custom/sw_base/tests/src/Functional/ExampleTest.php @@ -0,0 +1,8 @@ +@@ -19,7 +19,6 @@ + /** + * {@inheritdoc} + * +- * @phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod.Found + */ + protected function setUp(): void { + parent::setUp(); diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/modules/custom/sw_base/tests/src/Traits/MockTrait.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/modules/custom/sw_base/tests/src/Traits/MockTrait.php new file mode 100644 index 000000000..b1b8e022a --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/modules/custom/sw_base/tests/src/Traits/MockTrait.php @@ -0,0 +1,9 @@ +@@ -30,8 +30,6 @@ + * @return \PHPUnit\Framework\MockObject\MockObject + * An instance of the mock. + * +- * @SuppressWarnings("PHPMD.CyclomaticComplexity") +- * @SuppressWarnings("PHPMD.ElseExpression") + */ + protected function prepareMock(string $class, array $methods_map = [], array|bool $args = []): MockObject { + $methods = array_values(array_filter(array_keys($methods_map))); diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/sites/default/default.settings.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/sites/default/default.settings.php new file mode 100644 index 000000000..33d928f7b --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/sites/default/default.settings.php @@ -0,0 +1,7 @@ +@@ -1,6 +1,5 @@ + 404 Not Found

Not Found

The requested URL "@path" was not found on this server.

'; + include_once $contrib_path . '/fast404/fast404.inc'; +- // @phpstan-ignore-next-line + fast404_preboot($settings); + // @codeCoverageIgnoreEnd + } diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/sites/default/includes/modules/settings.redis.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/sites/default/includes/modules/settings.redis.php new file mode 100644 index 000000000..aaf1104b3 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/sites/default/includes/modules/settings.redis.php @@ -0,0 +1,10 @@ +@@ -8,9 +8,6 @@ + * interchangeable. We use `DRUPAL_REDIS_` environment variables as the Drupal + * module name is `redis`. + * +- * @phpcs:disable DrupalPractice.Commenting.CommentEmptyLine.SpacingAfter +- * @phpcs:disable Drupal.Commenting.InlineComment.SpacingAfter +- * @phpcs:disable Drupal.Commenting.InlineComment.InvalidEndChar + */ + + declare(strict_types=1); diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/sites/default/settings.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/sites/default/settings.php new file mode 100644 index 000000000..6c05abc0f --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/sites/default/settings.php @@ -0,0 +1,11 @@ +@@ -14,10 +14,6 @@ + * environments. + * @see https://www.vortextemplate.com/docs/drupal/settings + * +- * phpcs:disable Drupal.Commenting.InlineComment.NoSpaceBefore +- * phpcs:disable Drupal.Commenting.InlineComment.SpacingAfter +- * phpcs:disable DrupalPractice.Commenting.CommentEmptyLine.SpacingAfter +- * phpcs:disable DrupalPractice.CodeAnalysis.VariableAnalysis.UnusedVariable + */ + + declare(strict_types=1); diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/themes/custom/star_wars/Gruntfile.js b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/themes/custom/star_wars/Gruntfile.js new file mode 100644 index 000000000..b9f88bce5 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/themes/custom/star_wars/Gruntfile.js @@ -0,0 +1,8 @@ +@@ -5,7 +5,6 @@ + * Run `grunt` for to process with dev settings. + * Run `grunt prod` to process with prod settings. + * Run `grunt watch` to start watching with dev settings. +- * phpcs:ignoreFile + */ + + /* global module */ diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/themes/custom/star_wars/tests/src/Functional/ExampleTest.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/themes/custom/star_wars/tests/src/Functional/ExampleTest.php new file mode 100644 index 000000000..e59020263 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint/web/themes/custom/star_wars/tests/src/Functional/ExampleTest.php @@ -0,0 +1,8 @@ +@@ -19,7 +19,6 @@ + /** + * {@inheritdoc} + * +- * @phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod.Found + */ + protected function setUp(): void { + parent::setUp(); diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/-phpcs.xml b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/-phpcs.xml new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/-phpmd.xml b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/-phpmd.xml new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/-phpstan.neon b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/-phpstan.neon new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/-rector.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/-rector.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/.ahoy.yml b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/.ahoy.yml new file mode 100644 index 000000000..0eec4e46f --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/.ahoy.yml @@ -0,0 +1,34 @@ +@@ -178,18 +178,9 @@ + lint: + usage: Lint back-end and front-end code. + cmd: | +- ahoy lint-be + ahoy lint-fe + ahoy lint-tests + +- lint-be: +- usage: Lint back-end code. +- cmd: | +- ahoy cli vendor/bin/phpcs +- ahoy cli vendor/bin/phpstan +- ahoy cli vendor/bin/rector --clear-cache --dry-run +- ahoy cli vendor/bin/phpmd . text phpmd.xml +- + lint-fe: + usage: Lint front-end code. + cmd: | +@@ -205,14 +196,7 @@ + lint-fix: + usage: Fix lint issues of back-end and front-end code. + cmd: | +- ahoy lint-be-fix + ahoy lint-fe-fix +- +- lint-be-fix: +- usage: Fix lint issues of back-end code. +- cmd: | +- ahoy cli vendor/bin/rector --clear-cache +- ahoy cli vendor/bin/phpcbf + + lint-fe-fix: + usage: Fix lint issues of front-end code. diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/.circleci/README.md b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/.circleci/README.md new file mode 100644 index 000000000..533a875e9 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/.circleci/README.md @@ -0,0 +1,2 @@ +You may add custom scripts, which would run only in CI, to this directory and +reference them from `config.yml` file. diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/.circleci/config.yml b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/.circleci/config.yml new file mode 100644 index 000000000..4c4790e0c --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/.circleci/config.yml @@ -0,0 +1,523 @@ +# CircleCI 2.0 configuration file. +# +# This configuration file uses the "docker" executor to run the Docker stack. +# +# A "runner" container, created from a specified container image, is used to +# checkout source code and run commands defined in this file. Application Docker +# containers defined in `docker-compose.yml` run on a *remote* Docker server +# controlled by CircleCI. +# The "runner" container uses Docker client to control the remote Docker server. +version: '2.1' + +aliases: + # SSH key fingerprint to download the database. + # Replace this key fingerprint with your own and remove this comment. + - &db_ssh_fingerprint "SHA256:6d+U5QubT0eAWz+4N2wt+WM2qx6o4cvyvQ6xILETJ84" + + # SSH key fingerprint to deploy code. + # Replace this key fingerprint with your own and remove this comment. + - &deploy_ssh_fingerprint "SHA256:6d+U5QubT0eAWz+4N2wt+WM2qx6o4cvyvQ6xILETJ84" + + # Schedule to run nightly database build (to cache the database for the next day). + - &nightly_db_schedule "0 18 * * *" + + # Shared runner container configuration applied to each job. + - &runner_config + working_directory: &working_directory ~/project + environment: + VORTEX_DB_DOWNLOAD_SSH_FINGERPRINT: *db_ssh_fingerprint + VORTEX_DEPLOY_SSH_FINGERPRINT: *deploy_ssh_fingerprint + docker: + # Using the 'runner' container where each job will be executed. + # This container has all the necessary tools to run a dockerized environment. + # https://github.com/drevops/ci-runner + # https://hub.docker.com/repository/docker/drevops/ci-runner/tags + - image: drevops/ci-runner:__VERSION__ + auth: + username: ${VORTEX_CONTAINER_REGISTRY_USER} + password: ${VORTEX_CONTAINER_REGISTRY_PASS} + environment: + # Set runner timezone via UI to ensure that executed operations use correct timestamps. + # https://en.wikipedia.org/wiki/List_of_tz_database_time_zones + TZ: UTC + # Set runner terminal capabilities. + TERM: xterm-256color + # Disable strict host key checking for SSH connections. + VORTEX_SSH_DISABLE_STRICT_HOST_KEY_CHECKING: "1" + # Remove all SSH keys from the runner container. + VORTEX_SSH_REMOVE_ALL_KEYS: "1" + # 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" + # Directory to store test results. + VORTEX_CI_TEST_RESULTS: &test_results /tmp/tests + # Directory to store test artifacts. + VORTEX_CI_ARTIFACTS: &artifacts /tmp/artifacts + # Directory to use for artifact deployments. + VORTEX_DEPLOY_ARTIFACT_SRC: /tmp/workspace/code + # Source code location for artifact deployments. + VORTEX_DEPLOY_ARTIFACT_ROOT: *working_directory + # Report file location for artifact deployments. + VORTEX_DEPLOY_ARTIFACT_LOG: /tmp/artifacts/deployment_log.txt + # Check only minimal stack requirements. + VORTEX_DOCTOR_CHECK_MINIMAL: 1 + # CI runner resource class. + # https://circleci.com/docs/2.0/configuration-reference/#resource_class + # Change to 'large' for faster builds. + resource_class: medium + + - &step_setup_remote_docker + setup_remote_docker: + # Docker Layer Caching allows to significantly speed up builds by caching + # images built during previous runs. + # https://circleci.com/docs/2.0/docker-layer-caching/ + docker_layer_caching: false + version: default + + - &step_process_codebase_for_ci + run: + name: Process codebase to run in CI + command: | + find . -name "docker-compose.yml" -print0 | xargs -0 -I {} sh -c "sed -i -e ''/###/d'' {} && sed -i -e ''s/##//'' {}" + mkdir -p /tmp/workspace/code + + - &load_variables_from_dotenv + run: + name: Load environment variables from .env file + # Load variables from .env file, respecting existing values, and make them available for the next steps. + command: t=$(mktemp) && export -p >"${t}" && set -a && . ./.env && set +a && . "${t}" && export -p >> "$BASH_ENV" + +################################################################################ +# PARAMETERS +################################################################################ + +parameters: + run_update_dependencies: + type: boolean + default: false + +################################################################################ +# JOBS +################################################################################ + +jobs: + # Database handling is a first step of the build. + # - $VORTEX_CI_DB_CACHE_TIMESTAMP is used to determine if a fresh DB dump + # should be downloaded for the current build. Usually, a daily database dump + # is sufficient for development activities. + # - $VORTEX_CI_DB_CACHE_FALLBACK is used if the cache did not match $VORTEX_CI_DB_CACHE_TIMESTAMP. + # This allows to rely on the cache from the previous days within the same branch. + database: &job-database + <<: *runner_config + steps: + - attach_workspace: + at: /tmp/workspace + + - add_ssh_keys: + fingerprints: + - *db_ssh_fingerprint + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + - *step_setup_remote_docker + + - run: + name: Create cache keys for database caching as files + command: | + echo "${VORTEX_CI_DB_CACHE_BRANCH}" | tee /tmp/db_cache_branch + echo "${VORTEX_CI_DB_CACHE_FALLBACK/no/${CIRCLE_BUILD_NUM}}" | tee /tmp/db_cache_fallback + date "${VORTEX_CI_DB_CACHE_TIMESTAMP}" | tee /tmp/db_cache_timestamp + echo "yes" | tee /tmp/db_cache_fallback_yes + + - restore_cache: + keys: + # Restore DB cache based on the cache strategy set by the cache keys below. + # https://circleci.com/docs/2.0/caching/#restoring-cache + # Change 'v1' to 'v2', 'v3' etc., commit and push to force 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). + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback" }}-{{ checksum "/tmp/db_cache_timestamp" }} + # Fallback to caching by default branch name only. Allows to use + # cache from the branch build on the previous day. + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback" }}- + + - run: + name: Download DB + command: VORTEX_DB_DOWNLOAD_SEMAPHORE=/tmp/download-db-success ./scripts/vortex/download-db.sh + no_output_timeout: 30m + + # Execute commands after database download script finished: if the + # DB dump was downloaded - build the site (to ensure that the DB dump + # is valid) and export the DB using selected method (to support + # "file-to-image" or "image-to-file" conversions). + # Note that configuration changes and the DB updates are not applied, so + # the database will be cached in the same state as downloaded. + - run: + name: Export DB after download + command: | + [ ! -f /tmp/download-db-success ] && echo "==> Database download semaphore file is missing. DB export will not proceed." && exit 0 + ./scripts/vortex/login-container-registry.sh + docker compose up --detach && sleep 15 + docker compose exec cli mkdir -p .data && docker compose cp -L .data/db.sql cli:/app/.data/db.sql || true + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c "VORTEX_PROVISION_POST_OPERATIONS_SKIP=1 ./scripts/vortex/provision.sh" + grep -q ^VORTEX_DB_IMAGE .env && rm .data/db.sql || true + ./scripts/vortex/export-db.sh db.sql + no_output_timeout: 30m + + - save_cache: + # 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. + key: __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback_yes" }}-{{ checksum "/tmp/db_cache_timestamp" }} + paths: + - /root/project/.data + + # Nightly database job. Same as above, but with additional variables set. + database-nightly: + <<: *job-database + environment: + VORTEX_DB_DOWNLOAD_SSH_FINGERPRINT: *db_ssh_fingerprint + VORTEX_DEPLOY_SSH_FINGERPRINT: *deploy_ssh_fingerprint + # Enforce fresh DB build (do not rely on fallback caches). + VORTEX_CI_DB_CACHE_FALLBACK: 'no' + # Always use fresh base image for the database (if database-in-image storage is used). + VORTEX_DB_IMAGE_BASE: drevops/mariadb-drupal-data:__VERSION__ + # Deploy container image (if database-in-image storage is used). + VORTEX_EXPORT_DB_CONTAINER_REGISTRY_DEPLOY_PROCEED: 1 + # Do not build the Drupal front-end. + VORTEX_FRONTEND_BUILD_SKIP: 1 + + # Build and test is a second step of the build. The testing is performed + # within the same job to save time on provisioning during the job. + build: &job_build + <<: *runner_config + parallelism: 2 + steps: + - attach_workspace: + at: /tmp/workspace + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + + - run: + name: Validate Composer configuration + command: composer validate --strict || [ "${VORTEX_CI_COMPOSER_VALIDATE_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Set cache keys for database caching + command: | + echo "${VORTEX_CI_DB_CACHE_BRANCH}" | tee /tmp/db_cache_branch + echo "yes" | tee /tmp/db_cache_fallback_yes + echo "$(date ${VORTEX_CI_DB_CACHE_TIMESTAMP})" | tee /tmp/db_cache_timestamp + + - restore_cache: + keys: + # Use cached artifacts from previous builds of this branch. + # https://circleci.com/docs/2.0/caching/#restoring-cache + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback_yes" }}-{{ checksum "/tmp/db_cache_timestamp" }} + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback_yes" }}- + + - *step_setup_remote_docker + + - run: + name: Login to container registry + command: ./scripts/vortex/login-container-registry.sh + + - run: + name: Lint Dockerfiles with Hadolint + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + for file in $(find .docker -name 'Dockerfile' -o -name '*.dockerfile'); do + echo "Linting ${file}" && cat "${file}" | docker run --rm -i hadolint/hadolint || [ "${VORTEX_CI_HADOLINT_IGNORE_FAILURE:-0}" -eq 1 ] + done + + - run: + name: Lint Docker Compose files with DCLint + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker run --rm -v "${PWD}":/app zavoloklom/dclint:__VERSION__ . || [ "${VORTEX_CI_DCLINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Build stack + command: docker compose up -d + + - run: + name: Export built codebase + command: | + echo "${VORTEX_DEPLOY_TYPES:-}" | grep -vq "artifact" && exit 0 || true + mkdir -p "/tmp/workspace/code" + docker compose cp -L cli:"/app/." "/tmp/workspace/code" + du -sh "/tmp/workspace/code" + + - run: + name: Install development dependencies + command: | + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c " \ + if [ -n \"${PACKAGE_TOKEN:-}\" ]; then export COMPOSER_AUTH='{\"github-oauth\": {\"github.com\": \"${PACKAGE_TOKEN-}\"}}'; fi && \ + COMPOSER_MEMORY_LIMIT=-1 composer --ansi install --prefer-dist" + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c "yarn install --frozen-lockfile" + + - run: + name: Validate Composer configuration is normalized + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli composer normalize --dry-run || [ "${VORTEX_CI_COMPOSER_NORMALIZE_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with Twig CS Fixer + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/twig-cs-fixer || [ "${VORTEX_CI_TWIG_CS_FIXER_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with Gherkin Lint + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/gherkinlint lint tests/behat/features || [ "${VORTEX_CI_GHERKIN_LINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint module code with NodeJS linters + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli bash -c "yarn run lint" || [ "${VORTEX_CI_NODEJS_LINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint theme code with NodeJS linters + command: | + { [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ]; } || [ "${VORTEX_FRONTEND_BUILD_SKIP:-0}" -eq 1 ] && exit 0 + docker compose exec -T cli bash -c "yarn --cwd=\${WEBROOT}/themes/custom/\${DRUPAL_THEME} run lint" || [ "${VORTEX_CI_NODEJS_LINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Provision site + command: | + if [ -f .data/db.sql ]; then + docker compose exec cli mkdir -p .data + docker compose cp -L .data/db.sql cli:/app/.data/db.sql + fi + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli ./scripts/vortex/provision.sh + no_output_timeout: 30m + + - run: + name: Test with PHPUnit + command: docker compose exec -T cli vendor/bin/phpunit || [ "${VORTEX_CI_PHPUNIT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Test with Behat + command: | + if [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p${CIRCLE_NODE_INDEX}}"; fi + echo "Running with ${VORTEX_CI_BEHAT_PROFILE:-default} profile" + docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ + docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ + [ "${VORTEX_CI_BEHAT_IGNORE_FAILURE:-0}" -eq 1 ] + no_output_timeout: 30m + + - run: + name: Process test logs and artifacts + command: | + mkdir -p "${VORTEX_CI_TEST_RESULTS}" "${VORTEX_CI_ARTIFACTS}" + if docker compose ps --services --filter "status=running" | grep -q cli && docker compose exec cli test -d /app/.logs; then + docker compose cp cli:/app/.logs/. "${VORTEX_CI_ARTIFACTS}/" + if docker compose exec -T cli sh -c '[ -d /app/.logs/test_results/ ]'; then + docker compose cp cli:/app/.logs/test_results/. "${VORTEX_CI_TEST_RESULTS}/" + fi + fi + when: always + + - store_test_results: + path: *test_results + + - store_artifacts: + path: *artifacts + + - run: + name: Upload code coverage reports to Codecov + command: | + if [ -n "${CODECOV_TOKEN}" ] && [ -d /tmp/artifacts/coverage ] && ! echo "${CIRCLE_BRANCH}" | grep -q '^deps/'; then + codecov -Z -s /tmp/artifacts/coverage; + fi + + - persist_to_workspace: + root: /tmp/workspace + paths: + - code + + # Deploy primary branches. + deploy: &job_deploy + <<: *runner_config + steps: + - attach_workspace: + at: /tmp/workspace + + - add_ssh_keys: + fingerprints: + - *deploy_ssh_fingerprint + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + + - run: + name: Deploy + command: | + VORTEX_DEPLOY_BRANCH="${CIRCLE_BRANCH}" \ + VORTEX_DEPLOY_PR="$(echo ${CIRCLE_PULL_REQUEST} | cut -d'/' -f 7)" \ + VORTEX_DEPLOY_PR_HEAD=${CIRCLE_SHA1} \ + ./scripts/vortex/deploy.sh + no_output_timeout: 30m + + - store_artifacts: + path: *artifacts + + # Deploy tags. + deploy-tags: &job-deploy-tags + <<: *runner_config + steps: + - attach_workspace: + at: /tmp/workspace + + - add_ssh_keys: + fingerprints: + - *deploy_ssh_fingerprint + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + + - run: + name: Deploy + command: VORTEX_DEPLOY_MODE="tag" ./scripts/vortex/deploy.sh + no_output_timeout: 30m + + - store_artifacts: + path: *artifacts + + # Self-hosted dependency updates. + # Add the following environment variables to the CircleCI project: + # - RENOVATE_TOKEN: GitHub access token. + # - RENOVATE_REPOSITORIES: Repository to run Renovate on as `vendor/repository`. + # - RENOVATE_GIT_AUTHOR: Author for Renovate commits as `Name `. + # Variables provided below can be overridden in the CircleCI project settings. + update-dependencies: + docker: + - image: renovate/renovate:__VERSION__ + environment: + RENOVATE_PLATFORM: 'github' + RENOVATE_AUTODISCOVER: false + RENOVATE_DEPENDENCY_DASHBOARD_TITLE: 'Renovate Dependency Dashboard (self-hosted) by CircleCI' + RENOVATE_DEPENDENCY_DASHBOARD: false + RENOVATE_DRY_RUN: false + LOG_LEVEL: 'debug' + + steps: + - checkout + - run: + name: Check if RENOVATE_TOKEN is set + command: | + if [ -z "${RENOVATE_TOKEN}" ]; then + echo "RENOVATE_TOKEN is not set. Skipping job." + circleci-agent step halt + fi + + if [ -z "${RENOVATE_REPOSITORIES}" ]; then + echo "Renovate repository is not set. Skipping job." + circleci-agent step halt + fi + + if [ -z "${RENOVATE_GIT_AUTHOR}" ]; then + echo "Renovate git author is not set. Skipping job." + circleci-agent step halt + fi + + - run: + name: Validate Renovate configuration + command: renovate-config-validator + + - run: + name: Run Renovate + command: renovate + +################################################################################ +# WORKFLOWS +################################################################################ + +workflows: + version: 2 + # Commit workflow. Runs for every commit push to the remote repository. + commit: + jobs: + - database: + filters: + tags: + only: /.*/ + - build: + requires: + - database + filters: + tags: + only: /.*/ + - deploy: + requires: + - build + filters: + branches: + # Allowed branches: + # - production, main, master, develop, ci, cisomething + # - project/description + # - deps/* + # - feature/description, feature/123-description + # - bugfix/description, bugfix/123-description + # - release/__VERSION__, release/__VERSION__ (per https://semver.org/) + # - release/2023-04-17, release/2023-04-17.123 (date-based) + # - hotfix/__VERSION__, hotfix/__VERSION__ (per https://semver.org/) + # - hotfix/2023-04-17, hotfix/2023-04-17.123 (date-based) + only: /^(production|main|master|develop)$|^project\/[a-zA-z0-9\-\.]+|^(feature|bugfix)\/[a-zA-Z0-9\-\.\,_]+$|^ci.*|^(release|hotfix)\/[0-9]+(\.[0-9]+){2}(-rc\.[0-9]+)?$|^(release|hotfix)\/[0-9]{4}-[0-9]{2}-[0-9]{2}(\.[0-9]+)?$/ + tags: + ignore: /.*/ + - deploy-tags: + requires: + - build + filters: + branches: + ignore: /.*/ + tags: + # Allowed tags: + # - __VERSION__, __VERSION__ (per https://semver.org/) + # - 2023-04-17, 2023-04-17.123 (date-based) + only: /^[0-9]+(\.[0-9]+){2}(-rc\.[0-9]+)?$|^[0-9]{4}-[0-9]{2}-[0-9]{2}(\.[0-9]+)?$/ + + # Nightly database workflow runs overnight to capture fresh database and cache it. + nightly-db: + triggers: + - schedule: + cron: *nightly_db_schedule + filters: + branches: + only: + - develop + jobs: + - database-nightly + + # Self-hosted Renovate workflow. + update-dependencies: + triggers: + - schedule: + cron: "5 11,23 * * *" + filters: + branches: + only: + - develop + jobs: + - update-dependencies + + update-dependencies-manual: + when: << pipeline.parameters.run_update_dependencies >> + jobs: + - update-dependencies diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/.dockerignore b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/.dockerignore new file mode 100644 index 000000000..9c0a97823 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/.dockerignore @@ -0,0 +1,11 @@ +@@ -42,10 +42,6 @@ + !package.json + !package-lock.json + !patches +-!phpcs.xml +-!phpmd.xml +-!phpstan.neon + !phpunit.xml +-!rector.php + !scripts + !tests diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/.github/workflows/-build-test-deploy.yml b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/.github/workflows/-build-test-deploy.yml new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/README.md b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/README.md new file mode 100644 index 000000000..78e06291b --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/README.md @@ -0,0 +1,9 @@ +@@ -4,7 +4,7 @@ + + Drupal 11 implementation of star wars for star wars Org + +-[![Database, Build, Test and Deploy](https://github.com/star_wars_org/star_wars/actions/workflows/build-test-deploy.yml/badge.svg)](https://github.com/star_wars_org/star_wars/actions/workflows/build-test-deploy.yml) ++[![CircleCI](https://circleci.com/gh/star_wars_org/star_wars.svg?style=shield)](https://circleci.com/gh/star_wars_org/star_wars) + + ![Drupal 11](https://img.shields.io/badge/Drupal-11-blue.svg) + [![codecov](https://codecov.io/gh/star_wars_org/star_wars/graph/badge.svg)](https://codecov.io/gh/star_wars_org/star_wars) diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/composer.json b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/composer.json new file mode 100644 index 000000000..01b2927d0 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/composer.json @@ -0,0 +1,32 @@ +@@ -31,7 +31,6 @@ + "require-dev": { + "behat/behat": "__VERSION__", + "dantleech/gherkin-lint": "__VERSION__", +- "dealerdirect/phpcodesniffer-composer-installer": "__VERSION__", + "drevops/behat-format-progress-fail": "__VERSION__", + "drevops/behat-screenshot": "__VERSION__", + "drevops/behat-steps": "__VERSION__", +@@ -38,15 +37,9 @@ + "drupal/core-dev": "__VERSION__", + "drupal/drupal-extension": "__VERSION__", + "ergebnis/composer-normalize": "__VERSION__", +- "mglaman/phpstan-drupal": "__VERSION__", +- "palantirnet/drupal-rector": "__VERSION__", + "phpcompatibility/php-compatibility": "__VERSION__", +- "phpmd/phpmd": "__VERSION__", + "phpspec/prophecy-phpunit": "__VERSION__", +- "phpstan/extension-installer": "__VERSION__", +- "phpstan/phpstan": "__VERSION__", + "pyrech/composer-changelogs": "__VERSION__", +- "rector/rector": "__VERSION__", + "vincentlanglet/twig-cs-fixer": "__VERSION__" + }, + "conflict": { +@@ -79,7 +72,6 @@ + "ergebnis/composer-normalize": true, + "oomphinc/composer-installers-extender": true, + "php-http/discovery": true, +- "phpstan/extension-installer": true, + "pyrech/composer-changelogs": true, + "tbachert/spi": true + }, diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/docs/ci.md b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/docs/ci.md new file mode 100644 index 000000000..7d29e25c6 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/docs/ci.md @@ -0,0 +1,31 @@ +@@ -5,12 +5,12 @@ + Before feature changes can be merged into a shared mainline, a complete build + must run and pass all tests on CI server. + +-## GitHub Actions ++## Circle CI + +-This project uses [GitHub Actions](https://github.com/features/actions) as a +-CI server: it imports production backups into fully built codebase and runs +-code linting and tests. When tests pass, a deployment process is triggered for +-nominated branches (usually, `main` and `develop`). ++This project uses [Circle CI](https://circleci.com/) as a CI server: it imports ++production backups into fully built codebase and runs code linting and tests. ++When tests pass, a deployment process is triggered for nominated branches ++(usually, `main` and `develop`). + + Refer to https://www.vortextemplate.com/docs/continuous-integration for more information. + +@@ -21,9 +21,6 @@ + + ### SSH + +-GitHub Actions does not supports shell access to the build, but there is an +-action provided withing the `build` job that allows you to run a build with SSH +-support. +- +-Use "Run workflow" button in GitHub Actions UI to start build with SSH support +-that will be available for 120 minutes after the build is finished. ++Circle CI supports shell access to the build for 120 minutes after the build is ++finished when the build is started with SSH support. Use "Rerun job with SSH" ++button in Circle CI UI to start build with SSH support. diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/scripts/composer/ScriptHandler.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/scripts/composer/ScriptHandler.php new file mode 100644 index 000000000..2fac00f59 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/scripts/composer/ScriptHandler.php @@ -0,0 +1,8 @@ +@@ -5,7 +5,6 @@ + * Contains \DrupalProject\composer\ScriptHandler. + * + * Clone of https://github.com/drupal-composer/drupal-project/blob/10.x/scripts/composer/ScriptHandler.php +- * phpcs:disable + */ + + namespace DrupalProject\composer; diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/tests/phpunit/CircleCiConfigTest.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/tests/phpunit/CircleCiConfigTest.php new file mode 100644 index 000000000..1fa6be888 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/tests/phpunit/CircleCiConfigTest.php @@ -0,0 +1,256 @@ +config = Yaml::decode($file); + } + + /** + * Tests for deploy branch regex. + * + * @see https://semver.org/ + */ + #[DataProvider('dataProviderDeployBranchRegex')] + public function testDeployBranchRegex(string $branch, bool $expected = TRUE): void { + $this->assertEquals($expected, preg_match($this->config['workflows']['commit']['jobs'][2]['deploy']['filters']['branches']['only'], $branch)); + } + + /** + * Data provider for testDeployBranchRegex(). + */ + public static function dataProviderDeployBranchRegex(): array { + return [ + // Positive branches. + ['production'], + ['main'], + ['master'], + ['develop'], + + ['ci'], + ['cisomething'], + + ['release/123.456.789'], + ['release/123.456.789-rc.123'], + ['hotfix/123.456.789'], + ['hotfix/123.456.789-rc.123'], + + ['release/2023-04-17'], + ['release/2023-04-17.1'], + ['hotfix/2023-04-17'], + ['hotfix/2023-04-17.1'], + + ['feature/description'], + ['feature/Description'], + ['feature/Description-With-Hyphens'], + ['feature/Description-With_Underscores'], + ['feature/123-description'], + ['feature/123-Description'], + ['feature/UNDERSCORES_UNDERSCORES'], + ['feature/123-Description-With_UNDERSCORES'], + ['feature/1.x'], + ['feature/0.x'], + ['feature/0.1.x'], + ['feature/0.1.2.x'], + ['feature/1.x-description'], + ['feature/0.x-description'], + ['feature/0.1.x-description'], + ['feature/0.1.2.x-description'], + + ['bugfix/description'], + ['bugfix/Description'], + ['bugfix/Description-With-Hyphens'], + ['bugfix/Description-With_Underscores'], + ['bugfix/123-description'], + ['bugfix/123-Description'], + ['bugfix/UNDERSCORES_UNDERSCORES'], + ['bugfix/123-Description-With_UNDERSCORES'], + ['bugfix/1.x'], + ['bugfix/0.x'], + ['bugfix/0.1.x'], + ['bugfix/0.1.2.x'], + ['bugfix/1.x-description'], + ['bugfix/0.x-description'], + ['bugfix/0.1.x-description'], + ['bugfix/0.1.2.x-description'], + + ['project/description'], + ['project/Description'], + ['project/Description-With-Hyphens'], + ['project/123-description'], + ['project/123-Description'], + ['project/1.x'], + ['project/0.x'], + ['project/0.1.x'], + ['project/0.1.2.x'], + ['project/1.x-description'], + ['project/0.x-description'], + ['project/0.1.x-description'], + ['project/0.1.2.x-description'], + + // Negative branches. + ['something', FALSE], + ['premain', FALSE], + ['premaster', FALSE], + ['predevelop', FALSE], + ['mainpost', FALSE], + ['masterpost', FALSE], + ['developpost', FALSE], + ['premainpost', FALSE], + ['premasterpost', FALSE], + ['predeveloppost', FALSE], + + ['preci', FALSE], + ['precipost', FALSE], + + ['deps/something', FALSE], + ['deps', FALSE], + ['predeps', FALSE], + ['depspost', FALSE], + ['predepspost', FALSE], + + ['feature', FALSE], + ['release', FALSE], + ['hotfix', FALSE], + ['prefeature', FALSE], + ['prerelease', FALSE], + ['prehotfix', FALSE], + ['featurepost', FALSE], + ['releasepost', FALSE], + ['hotfixpost', FALSE], + ['prefeaturepost', FALSE], + ['prereleasepost', FALSE], + ['prehotfixpost', FALSE], + + ['release/123', FALSE], + ['release/123.456', FALSE], + ['hotfix/123', FALSE], + ['hotfix/123.456', FALSE], + + ['release/202-04-17', FALSE], + ['release/2023-4-17', FALSE], + ['release/2023-04-1', FALSE], + ['release/pre2023-04-17', FALSE], + ['release/2023-04-17post', FALSE], + ['release/pre2023-04-17post', FALSE], + + ['hotfix/202-04-17', FALSE], + ['hotfix/2023-4-17', FALSE], + ['hotfix/2023-04-1', FALSE], + ['hotfix/pre2023-04-17', FALSE], + ['hotfix/2023-04-17post', FALSE], + ['hotfix/pre2023-04-17post', FALSE], + + ['release/123.456.789-something', FALSE], + ['release/123.456.789-rc', FALSE], + ['release/123.456.789-rc123', FALSE], + ['release/123.456.789-rc-123', FALSE], + ['release/123.456.789-prerc123', FALSE], + ['release/123.456.789-rcpost123', FALSE], + ['release/123.456.789-prercpost123', FALSE], + ['release/123.456.789-rc123something', FALSE], + ['release/123.456.789-rc.123something', FALSE], + ['release/123.456.789-rc.123-something', FALSE], + + ['hotfix/123.456.789-something', FALSE], + ['hotfix/123.456.789-rc', FALSE], + ['hotfix/123.456.789-rc123', FALSE], + ['hotfix/123.456.789-rc-123', FALSE], + ['hotfix/123.456.789-prerc123', FALSE], + ['hotfix/123.456.789-rcpost123', FALSE], + ['hotfix/123.456.789-prercpost123', FALSE], + ['hotfix/123.456.789-rc123something', FALSE], + ['hotfix/123.456.789-rc.123something', FALSE], + ['hotfix/123.456.789-rc.123-something', FALSE], + + ['prefeature/something', FALSE], + ['prefbugfix/something', FALSE], + ['prerelease/something', FALSE], + ['prehotfix/something', FALSE], + ['featurepost/something', FALSE], + ['bugfixpost/something', FALSE], + ['releasepost/something', FALSE], + ['hotfixpost/something', FALSE], + ['prefeaturepost/something', FALSE], + ['prebugfixpost/something', FALSE], + ['prereleasepost/something', FALSE], + ['prehotfixpost/something', FALSE], + ['preproject/something', FALSE], + ['projectpost/something', FALSE], + ]; + } + + /** + * Tests for deploy tag regex. + * + * @see https://semver.org/ + */ + #[DataProvider('dataProviderDeployTagRegex')] + public function testDeployTagRegex(string $branch, bool $expected = TRUE): void { + $this->assertEquals($expected, preg_match($this->config['workflows']['commit']['jobs'][3]['deploy-tags']['filters']['tags']['only'], $branch)); + } + + /** + * Data provider for testDeployTagRegex(). + */ + public static function dataProviderDeployTagRegex(): array { + return [ + // Positive tags. + ['1.2.3'], + ['1.2.3-rc.123'], + ['2023-04-17'], + ['2023-04-17.123'], + + // Negative tags. + ['123', FALSE], + ['123.456', FALSE], + ['1.2.3-rc123', FALSE], + ['1.2.3-rc.123post', FALSE], + ['1.2.3-prerc.123', FALSE], + ['1.2.3-rcpost.123', FALSE], + ['1.2.3-prercpost.123', FALSE], + + ['202-04-17', FALSE], + ['2023-0-17', FALSE], + ['2023-04-1', FALSE], + ['pre2023-04-17', FALSE], + ['2023-04-17post', FALSE], + ['pre2023-04-17post', FALSE], + ['2023-04-17.123.', FALSE], + ['2023-04-17.pre123', FALSE], + ['2023-04-17.pre123post', FALSE], + ['2023-04-17.123post', FALSE], + ]; + } + +} diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/tests/phpunit/Drupal/EnvironmentSettingsTest.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/tests/phpunit/Drupal/EnvironmentSettingsTest.php new file mode 100644 index 000000000..bcf03b497 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/tests/phpunit/Drupal/EnvironmentSettingsTest.php @@ -0,0 +1,23 @@ +@@ -15,10 +15,6 @@ + * The main purpose of these tests is to ensure that the settings and configs + * appear in every environment as expected. + * +- * phpcs:disable Squiz.WhiteSpace.FunctionSpacing.Before +- * phpcs:disable Squiz.WhiteSpace.FunctionSpacing.After +- * phpcs:disable Squiz.WhiteSpace.FunctionSpacing.AfterLast +- * phpcs:disable Drupal.Classes.ClassDeclaration.CloseBraceAfterBody + */ + #[Group('drupal_settings')] + class EnvironmentSettingsTest extends SettingsTestCase { +@@ -263,9 +259,9 @@ + } + + /** +- * Test per-environment settings for GitHub Actions. ++ * Test per-environment settings for CircleCI. + */ +- public function testEnvironmentGha(): void { ++ public function testEnvironmentCircleCi(): void { + $this->setEnvVars([ + 'CI' => TRUE, + ]); diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/tests/phpunit/Drupal/SettingsTestCase.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/tests/phpunit/Drupal/SettingsTestCase.php new file mode 100644 index 000000000..89e8663a3 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/tests/phpunit/Drupal/SettingsTestCase.php @@ -0,0 +1,24 @@ +@@ -11,7 +11,6 @@ + * + * Base class for testing Drupal settings. + * +- * phpcs:disable Drupal.NamingConventions.ValidVariableName.LowerCamelName + */ + abstract class SettingsTestCase extends TestCase { + +@@ -129,7 +128,6 @@ + * @param array $vars + * Array of environment variables. + * +- * @SuppressWarnings("PHPMD.ElseExpression") + */ + protected function setEnvVars(array $vars): void { + // Unset the existing environment variable if not set in the test. +@@ -308,7 +306,6 @@ + * @param string $message + * Message to display on failure. + * +- * @SuppressWarnings("PHPMD.ElseExpression") + */ + protected function assertArraySubset(array $subset, array $haystack, string $message = ''): void { + foreach ($subset as $key => $value) { diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/web/modules/custom/sw_base/src/Plugin/Block/CounterBlock.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/web/modules/custom/sw_base/src/Plugin/Block/CounterBlock.php new file mode 100644 index 000000000..4301ae253 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/web/modules/custom/sw_base/src/Plugin/Block/CounterBlock.php @@ -0,0 +1,8 @@ +@@ -20,7 +20,6 @@ + /** + * {@inheritdoc} + * +- * @phpstan-ignore-next-line + */ + public function build(): array { + return [ diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/web/modules/custom/sw_base/sw_base.module b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/web/modules/custom/sw_base/sw_base.module new file mode 100644 index 000000000..f573df9a1 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/web/modules/custom/sw_base/sw_base.module @@ -0,0 +1,8 @@ +@@ -12,7 +12,6 @@ + /** + * Implements hook_theme(). + * +- * @phpstan-ignore-next-line + */ + function sw_base_theme(): array { + return [ diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/web/modules/custom/sw_base/tests/src/Functional/ExampleTest.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/web/modules/custom/sw_base/tests/src/Functional/ExampleTest.php new file mode 100644 index 000000000..e59020263 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/web/modules/custom/sw_base/tests/src/Functional/ExampleTest.php @@ -0,0 +1,8 @@ +@@ -19,7 +19,6 @@ + /** + * {@inheritdoc} + * +- * @phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod.Found + */ + protected function setUp(): void { + parent::setUp(); diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/web/modules/custom/sw_base/tests/src/Traits/MockTrait.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/web/modules/custom/sw_base/tests/src/Traits/MockTrait.php new file mode 100644 index 000000000..b1b8e022a --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/web/modules/custom/sw_base/tests/src/Traits/MockTrait.php @@ -0,0 +1,9 @@ +@@ -30,8 +30,6 @@ + * @return \PHPUnit\Framework\MockObject\MockObject + * An instance of the mock. + * +- * @SuppressWarnings("PHPMD.CyclomaticComplexity") +- * @SuppressWarnings("PHPMD.ElseExpression") + */ + protected function prepareMock(string $class, array $methods_map = [], array|bool $args = []): MockObject { + $methods = array_values(array_filter(array_keys($methods_map))); diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/web/sites/default/default.settings.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/web/sites/default/default.settings.php new file mode 100644 index 000000000..33d928f7b --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/web/sites/default/default.settings.php @@ -0,0 +1,7 @@ +@@ -1,6 +1,5 @@ + 404 Not Found

Not Found

The requested URL "@path" was not found on this server.

'; + include_once $contrib_path . '/fast404/fast404.inc'; +- // @phpstan-ignore-next-line + fast404_preboot($settings); + // @codeCoverageIgnoreEnd + } diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/web/sites/default/includes/modules/settings.redis.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/web/sites/default/includes/modules/settings.redis.php new file mode 100644 index 000000000..aaf1104b3 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/web/sites/default/includes/modules/settings.redis.php @@ -0,0 +1,10 @@ +@@ -8,9 +8,6 @@ + * interchangeable. We use `DRUPAL_REDIS_` environment variables as the Drupal + * module name is `redis`. + * +- * @phpcs:disable DrupalPractice.Commenting.CommentEmptyLine.SpacingAfter +- * @phpcs:disable Drupal.Commenting.InlineComment.SpacingAfter +- * @phpcs:disable Drupal.Commenting.InlineComment.InvalidEndChar + */ + + declare(strict_types=1); diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/web/sites/default/includes/providers/-settings.gha.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/web/sites/default/includes/providers/-settings.gha.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/web/sites/default/includes/providers/settings.circleci.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/web/sites/default/includes/providers/settings.circleci.php new file mode 100644 index 000000000..7cad35cf8 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_lint_circleci/web/sites/default/includes/providers/settings.circleci.php @@ -0,0 +1,17 @@ +*.test + + +- +- +- *\/tests\/behat\/bootstrap/*\.php +- + diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/phpstan.neon b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/phpstan.neon new file mode 100644 index 000000000..9245e8fa5 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/phpstan.neon @@ -0,0 +1,8 @@ +@@ -38,7 +38,6 @@ + paths: + - web/modules/custom/*/tests/* + - web/themes/custom/*/tests/* +- - tests/phpunit/* + reportUnmatched: false + - + # Hook implementations do not provide docblocks for parameters, so there diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/tests/behat/bootstrap/-FeatureContext.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/tests/behat/bootstrap/-FeatureContext.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/tests/behat/features/-clamav.feature b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/tests/behat/features/-clamav.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/tests/behat/features/-counter.feature b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/tests/behat/features/-counter.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/tests/behat/features/-homepage.feature b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/tests/behat/features/-homepage.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/tests/behat/features/-login.feature b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/tests/behat/features/-login.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/tests/behat/features/-redis.feature b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/tests/behat/features/-redis.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/tests/behat/features/-robotstxt.feature b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/tests/behat/features/-robotstxt.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/tests/behat/features/-search.feature b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/tests/behat/features/-search.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/tests/phpunit/Drupal/-DatabaseSettingsTest.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/tests/phpunit/Drupal/-DatabaseSettingsTest.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/tests/phpunit/Drupal/-EnvironmentSettingsTest.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/tests/phpunit/Drupal/-EnvironmentSettingsTest.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/tests/phpunit/Drupal/-SettingsTestCase.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/tests/phpunit/Drupal/-SettingsTestCase.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/tests/phpunit/Drupal/-SwitchableSettingsTest.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/tests/phpunit/Drupal/-SwitchableSettingsTest.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/modules/custom/sw_base/tests/src/Functional/-ExampleTest.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/modules/custom/sw_base/tests/src/Functional/-ExampleTest.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/modules/custom/sw_base/tests/src/Functional/-SwBaseFunctionalTestBase.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/modules/custom/sw_base/tests/src/Functional/-SwBaseFunctionalTestBase.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/modules/custom/sw_base/tests/src/Kernel/-ExampleTest.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/modules/custom/sw_base/tests/src/Kernel/-ExampleTest.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/modules/custom/sw_base/tests/src/Kernel/-SwBaseKernelTestBase.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/modules/custom/sw_base/tests/src/Kernel/-SwBaseKernelTestBase.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/modules/custom/sw_base/tests/src/Traits/-ArrayTrait.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/modules/custom/sw_base/tests/src/Traits/-ArrayTrait.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/modules/custom/sw_base/tests/src/Traits/-AssertTrait.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/modules/custom/sw_base/tests/src/Traits/-AssertTrait.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/modules/custom/sw_base/tests/src/Traits/-MockTrait.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/modules/custom/sw_base/tests/src/Traits/-MockTrait.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/modules/custom/sw_base/tests/src/Traits/-ReflectionTrait.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/modules/custom/sw_base/tests/src/Traits/-ReflectionTrait.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/modules/custom/sw_base/tests/src/Unit/-ExampleTest.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/modules/custom/sw_base/tests/src/Unit/-ExampleTest.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/modules/custom/sw_base/tests/src/Unit/-SwBaseUnitTestBase.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/modules/custom/sw_base/tests/src/Unit/-SwBaseUnitTestBase.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/themes/custom/star_wars/tests/src/Functional/-ExampleTest.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/themes/custom/star_wars/tests/src/Functional/-ExampleTest.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/themes/custom/star_wars/tests/src/Functional/-StarWarsFunctionalTestBase.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/themes/custom/star_wars/tests/src/Functional/-StarWarsFunctionalTestBase.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/themes/custom/star_wars/tests/src/Kernel/-ExampleTest.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/themes/custom/star_wars/tests/src/Kernel/-ExampleTest.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/themes/custom/star_wars/tests/src/Kernel/-StarWarsKernelTestBase.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/themes/custom/star_wars/tests/src/Kernel/-StarWarsKernelTestBase.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/themes/custom/star_wars/tests/src/Unit/-ExampleTest.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/themes/custom/star_wars/tests/src/Unit/-ExampleTest.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/themes/custom/star_wars/tests/src/Unit/-StarWarsUnitTestBase.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests/web/themes/custom/star_wars/tests/src/Unit/-StarWarsUnitTestBase.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/-behat.yml b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/-behat.yml new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/-gherkinlint.json b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/-gherkinlint.json new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/-phpunit.xml b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/-phpunit.xml new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/.ahoy.yml b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/.ahoy.yml new file mode 100644 index 000000000..059c28491 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/.ahoy.yml @@ -0,0 +1,52 @@ +@@ -180,7 +180,6 @@ + cmd: | + ahoy lint-be + ahoy lint-fe +- ahoy lint-tests + + lint-be: + usage: Lint back-end code. +@@ -197,11 +196,6 @@ + ahoy cli "yarn run lint" + ahoy cli "yarn run --cwd=\${WEBROOT}/themes/custom/\${DRUPAL_THEME} lint" + +- lint-tests: +- usage: Lint tests code. +- cmd: | +- ahoy cli vendor/bin/gherkinlint lint tests/behat/features +- + lint-fix: + usage: Fix lint issues of back-end and front-end code. + cmd: | +@@ -220,31 +214,6 @@ + ahoy cli vendor/bin/twig-cs-fixer lint --fix + ahoy cli "yarn run lint-fix" + ahoy cli "yarn run --cwd=\${WEBROOT}/themes/custom/\${DRUPAL_THEME} lint-fix" +- +- test: +- usage: Run all tests. +- cmd: | +- ahoy test-unit +- ahoy test-kernel +- ahoy test-functional +- ahoy test-bdd +- +- test-unit: +- usage: Run PHPUnit unit tests. +- cmd: ahoy cli vendor/bin/phpunit --testsuite=unit "$@" +- +- test-kernel: +- usage: Run PHPUnit kernel tests. +- cmd: ahoy cli vendor/bin/phpunit --testsuite=kernel "$@" +- +- test-functional: +- usage: Run PHPUnit functional tests. +- cmd: ahoy cli vendor/bin/phpunit --testsuite=functional "$@" +- +- test-bdd: +- usage: Run BDD tests. +- aliases: [test-behat] +- cmd: ahoy cli php -d memory_limit=-1 vendor/bin/behat --colors "$@" + + debug: + usage: Enable PHP Xdebug. diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/.circleci/README.md b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/.circleci/README.md new file mode 100644 index 000000000..533a875e9 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/.circleci/README.md @@ -0,0 +1,2 @@ +You may add custom scripts, which would run only in CI, to this directory and +reference them from `config.yml` file. diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/.circleci/config.yml b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/.circleci/config.yml new file mode 100644 index 000000000..d3c92eba8 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/.circleci/config.yml @@ -0,0 +1,527 @@ +# CircleCI 2.0 configuration file. +# +# This configuration file uses the "docker" executor to run the Docker stack. +# +# A "runner" container, created from a specified container image, is used to +# checkout source code and run commands defined in this file. Application Docker +# containers defined in `docker-compose.yml` run on a *remote* Docker server +# controlled by CircleCI. +# The "runner" container uses Docker client to control the remote Docker server. +version: '2.1' + +aliases: + # SSH key fingerprint to download the database. + # Replace this key fingerprint with your own and remove this comment. + - &db_ssh_fingerprint "SHA256:6d+U5QubT0eAWz+4N2wt+WM2qx6o4cvyvQ6xILETJ84" + + # SSH key fingerprint to deploy code. + # Replace this key fingerprint with your own and remove this comment. + - &deploy_ssh_fingerprint "SHA256:6d+U5QubT0eAWz+4N2wt+WM2qx6o4cvyvQ6xILETJ84" + + # Schedule to run nightly database build (to cache the database for the next day). + - &nightly_db_schedule "0 18 * * *" + + # Shared runner container configuration applied to each job. + - &runner_config + working_directory: &working_directory ~/project + environment: + VORTEX_DB_DOWNLOAD_SSH_FINGERPRINT: *db_ssh_fingerprint + VORTEX_DEPLOY_SSH_FINGERPRINT: *deploy_ssh_fingerprint + docker: + # Using the 'runner' container where each job will be executed. + # This container has all the necessary tools to run a dockerized environment. + # https://github.com/drevops/ci-runner + # https://hub.docker.com/repository/docker/drevops/ci-runner/tags + - image: drevops/ci-runner:__VERSION__ + auth: + username: ${VORTEX_CONTAINER_REGISTRY_USER} + password: ${VORTEX_CONTAINER_REGISTRY_PASS} + environment: + # Set runner timezone via UI to ensure that executed operations use correct timestamps. + # https://en.wikipedia.org/wiki/List_of_tz_database_time_zones + TZ: UTC + # Set runner terminal capabilities. + TERM: xterm-256color + # Disable strict host key checking for SSH connections. + VORTEX_SSH_DISABLE_STRICT_HOST_KEY_CHECKING: "1" + # Remove all SSH keys from the runner container. + VORTEX_SSH_REMOVE_ALL_KEYS: "1" + # 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" + # Directory to store test results. + VORTEX_CI_TEST_RESULTS: &test_results /tmp/tests + # Directory to store test artifacts. + VORTEX_CI_ARTIFACTS: &artifacts /tmp/artifacts + # Directory to use for artifact deployments. + VORTEX_DEPLOY_ARTIFACT_SRC: /tmp/workspace/code + # Source code location for artifact deployments. + VORTEX_DEPLOY_ARTIFACT_ROOT: *working_directory + # Report file location for artifact deployments. + VORTEX_DEPLOY_ARTIFACT_LOG: /tmp/artifacts/deployment_log.txt + # Check only minimal stack requirements. + VORTEX_DOCTOR_CHECK_MINIMAL: 1 + # CI runner resource class. + # https://circleci.com/docs/2.0/configuration-reference/#resource_class + # Change to 'large' for faster builds. + resource_class: medium + + - &step_setup_remote_docker + setup_remote_docker: + # Docker Layer Caching allows to significantly speed up builds by caching + # images built during previous runs. + # https://circleci.com/docs/2.0/docker-layer-caching/ + docker_layer_caching: false + version: default + + - &step_process_codebase_for_ci + run: + name: Process codebase to run in CI + command: | + find . -name "docker-compose.yml" -print0 | xargs -0 -I {} sh -c "sed -i -e ''/###/d'' {} && sed -i -e ''s/##//'' {}" + mkdir -p /tmp/workspace/code + + - &load_variables_from_dotenv + run: + name: Load environment variables from .env file + # Load variables from .env file, respecting existing values, and make them available for the next steps. + command: t=$(mktemp) && export -p >"${t}" && set -a && . ./.env && set +a && . "${t}" && export -p >> "$BASH_ENV" + +################################################################################ +# PARAMETERS +################################################################################ + +parameters: + run_update_dependencies: + type: boolean + default: false + +################################################################################ +# JOBS +################################################################################ + +jobs: + # Database handling is a first step of the build. + # - $VORTEX_CI_DB_CACHE_TIMESTAMP is used to determine if a fresh DB dump + # should be downloaded for the current build. Usually, a daily database dump + # is sufficient for development activities. + # - $VORTEX_CI_DB_CACHE_FALLBACK is used if the cache did not match $VORTEX_CI_DB_CACHE_TIMESTAMP. + # This allows to rely on the cache from the previous days within the same branch. + database: &job-database + <<: *runner_config + steps: + - attach_workspace: + at: /tmp/workspace + + - add_ssh_keys: + fingerprints: + - *db_ssh_fingerprint + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + - *step_setup_remote_docker + + - run: + name: Create cache keys for database caching as files + command: | + echo "${VORTEX_CI_DB_CACHE_BRANCH}" | tee /tmp/db_cache_branch + echo "${VORTEX_CI_DB_CACHE_FALLBACK/no/${CIRCLE_BUILD_NUM}}" | tee /tmp/db_cache_fallback + date "${VORTEX_CI_DB_CACHE_TIMESTAMP}" | tee /tmp/db_cache_timestamp + echo "yes" | tee /tmp/db_cache_fallback_yes + + - restore_cache: + keys: + # Restore DB cache based on the cache strategy set by the cache keys below. + # https://circleci.com/docs/2.0/caching/#restoring-cache + # Change 'v1' to 'v2', 'v3' etc., commit and push to force 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). + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback" }}-{{ checksum "/tmp/db_cache_timestamp" }} + # Fallback to caching by default branch name only. Allows to use + # cache from the branch build on the previous day. + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback" }}- + + - run: + name: Download DB + command: VORTEX_DB_DOWNLOAD_SEMAPHORE=/tmp/download-db-success ./scripts/vortex/download-db.sh + no_output_timeout: 30m + + # Execute commands after database download script finished: if the + # DB dump was downloaded - build the site (to ensure that the DB dump + # is valid) and export the DB using selected method (to support + # "file-to-image" or "image-to-file" conversions). + # Note that configuration changes and the DB updates are not applied, so + # the database will be cached in the same state as downloaded. + - run: + name: Export DB after download + command: | + [ ! -f /tmp/download-db-success ] && echo "==> Database download semaphore file is missing. DB export will not proceed." && exit 0 + ./scripts/vortex/login-container-registry.sh + docker compose up --detach && sleep 15 + docker compose exec cli mkdir -p .data && docker compose cp -L .data/db.sql cli:/app/.data/db.sql || true + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c "VORTEX_PROVISION_POST_OPERATIONS_SKIP=1 ./scripts/vortex/provision.sh" + grep -q ^VORTEX_DB_IMAGE .env && rm .data/db.sql || true + ./scripts/vortex/export-db.sh db.sql + no_output_timeout: 30m + + - save_cache: + # 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. + key: __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback_yes" }}-{{ checksum "/tmp/db_cache_timestamp" }} + paths: + - /root/project/.data + + # Nightly database job. Same as above, but with additional variables set. + database-nightly: + <<: *job-database + environment: + VORTEX_DB_DOWNLOAD_SSH_FINGERPRINT: *db_ssh_fingerprint + VORTEX_DEPLOY_SSH_FINGERPRINT: *deploy_ssh_fingerprint + # Enforce fresh DB build (do not rely on fallback caches). + VORTEX_CI_DB_CACHE_FALLBACK: 'no' + # Always use fresh base image for the database (if database-in-image storage is used). + VORTEX_DB_IMAGE_BASE: drevops/mariadb-drupal-data:__VERSION__ + # Deploy container image (if database-in-image storage is used). + VORTEX_EXPORT_DB_CONTAINER_REGISTRY_DEPLOY_PROCEED: 1 + # Do not build the Drupal front-end. + VORTEX_FRONTEND_BUILD_SKIP: 1 + + # Build and test is a second step of the build. The testing is performed + # within the same job to save time on provisioning during the job. + build: &job_build + <<: *runner_config + parallelism: 2 + steps: + - attach_workspace: + at: /tmp/workspace + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + + - run: + name: Validate Composer configuration + command: composer validate --strict || [ "${VORTEX_CI_COMPOSER_VALIDATE_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Set cache keys for database caching + command: | + echo "${VORTEX_CI_DB_CACHE_BRANCH}" | tee /tmp/db_cache_branch + echo "yes" | tee /tmp/db_cache_fallback_yes + echo "$(date ${VORTEX_CI_DB_CACHE_TIMESTAMP})" | tee /tmp/db_cache_timestamp + + - restore_cache: + keys: + # Use cached artifacts from previous builds of this branch. + # https://circleci.com/docs/2.0/caching/#restoring-cache + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback_yes" }}-{{ checksum "/tmp/db_cache_timestamp" }} + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback_yes" }}- + + - *step_setup_remote_docker + + - run: + name: Login to container registry + command: ./scripts/vortex/login-container-registry.sh + + - run: + name: Lint Dockerfiles with Hadolint + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + for file in $(find .docker -name 'Dockerfile' -o -name '*.dockerfile'); do + echo "Linting ${file}" && cat "${file}" | docker run --rm -i hadolint/hadolint || [ "${VORTEX_CI_HADOLINT_IGNORE_FAILURE:-0}" -eq 1 ] + done + + - run: + name: Lint Docker Compose files with DCLint + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker run --rm -v "${PWD}":/app zavoloklom/dclint:__VERSION__ . || [ "${VORTEX_CI_DCLINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Build stack + command: docker compose up -d + + - run: + name: Export built codebase + command: | + echo "${VORTEX_DEPLOY_TYPES:-}" | grep -vq "artifact" && exit 0 || true + mkdir -p "/tmp/workspace/code" + docker compose cp -L cli:"/app/." "/tmp/workspace/code" + du -sh "/tmp/workspace/code" + + - run: + name: Install development dependencies + command: | + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c " \ + if [ -n \"${PACKAGE_TOKEN:-}\" ]; then export COMPOSER_AUTH='{\"github-oauth\": {\"github.com\": \"${PACKAGE_TOKEN-}\"}}'; fi && \ + COMPOSER_MEMORY_LIMIT=-1 composer --ansi install --prefer-dist" + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c "yarn install --frozen-lockfile" + + - run: + name: Validate Composer configuration is normalized + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli composer normalize --dry-run || [ "${VORTEX_CI_COMPOSER_NORMALIZE_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with PHPCS + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/phpcs || [ "${VORTEX_CI_PHPCS_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with PHPStan + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/phpstan || [ "${VORTEX_CI_PHPSTAN_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with Rector + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/rector --clear-cache --dry-run || [ "${VORTEX_CI_RECTOR_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with PHPMD + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/phpmd . text phpmd.xml || [ "${VORTEX_CI_PHPMD_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with Twig CS Fixer + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/twig-cs-fixer || [ "${VORTEX_CI_TWIG_CS_FIXER_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint module code with NodeJS linters + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli bash -c "yarn run lint" || [ "${VORTEX_CI_NODEJS_LINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint theme code with NodeJS linters + command: | + { [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ]; } || [ "${VORTEX_FRONTEND_BUILD_SKIP:-0}" -eq 1 ] && exit 0 + docker compose exec -T cli bash -c "yarn --cwd=\${WEBROOT}/themes/custom/\${DRUPAL_THEME} run lint" || [ "${VORTEX_CI_NODEJS_LINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Provision site + command: | + if [ -f .data/db.sql ]; then + docker compose exec cli mkdir -p .data + docker compose cp -L .data/db.sql cli:/app/.data/db.sql + fi + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli ./scripts/vortex/provision.sh + no_output_timeout: 30m + + - run: + name: Process test logs and artifacts + command: | + mkdir -p "${VORTEX_CI_TEST_RESULTS}" "${VORTEX_CI_ARTIFACTS}" + if docker compose ps --services --filter "status=running" | grep -q cli && docker compose exec cli test -d /app/.logs; then + docker compose cp cli:/app/.logs/. "${VORTEX_CI_ARTIFACTS}/" + if docker compose exec -T cli sh -c '[ -d /app/.logs/test_results/ ]'; then + docker compose cp cli:/app/.logs/test_results/. "${VORTEX_CI_TEST_RESULTS}/" + fi + fi + when: always + + - store_test_results: + path: *test_results + + - store_artifacts: + path: *artifacts + + - run: + name: Upload code coverage reports to Codecov + command: | + if [ -n "${CODECOV_TOKEN}" ] && [ -d /tmp/artifacts/coverage ] && ! echo "${CIRCLE_BRANCH}" | grep -q '^deps/'; then + codecov -Z -s /tmp/artifacts/coverage; + fi + + - persist_to_workspace: + root: /tmp/workspace + paths: + - code + + # Deploy primary branches. + deploy: &job_deploy + <<: *runner_config + steps: + - attach_workspace: + at: /tmp/workspace + + - add_ssh_keys: + fingerprints: + - *deploy_ssh_fingerprint + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + + - run: + name: Deploy + command: | + VORTEX_DEPLOY_BRANCH="${CIRCLE_BRANCH}" \ + VORTEX_DEPLOY_PR="$(echo ${CIRCLE_PULL_REQUEST} | cut -d'/' -f 7)" \ + VORTEX_DEPLOY_PR_HEAD=${CIRCLE_SHA1} \ + ./scripts/vortex/deploy.sh + no_output_timeout: 30m + + - store_artifacts: + path: *artifacts + + # Deploy tags. + deploy-tags: &job-deploy-tags + <<: *runner_config + steps: + - attach_workspace: + at: /tmp/workspace + + - add_ssh_keys: + fingerprints: + - *deploy_ssh_fingerprint + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + + - run: + name: Deploy + command: VORTEX_DEPLOY_MODE="tag" ./scripts/vortex/deploy.sh + no_output_timeout: 30m + + - store_artifacts: + path: *artifacts + + # Self-hosted dependency updates. + # Add the following environment variables to the CircleCI project: + # - RENOVATE_TOKEN: GitHub access token. + # - RENOVATE_REPOSITORIES: Repository to run Renovate on as `vendor/repository`. + # - RENOVATE_GIT_AUTHOR: Author for Renovate commits as `Name `. + # Variables provided below can be overridden in the CircleCI project settings. + update-dependencies: + docker: + - image: renovate/renovate:__VERSION__ + environment: + RENOVATE_PLATFORM: 'github' + RENOVATE_AUTODISCOVER: false + RENOVATE_DEPENDENCY_DASHBOARD_TITLE: 'Renovate Dependency Dashboard (self-hosted) by CircleCI' + RENOVATE_DEPENDENCY_DASHBOARD: false + RENOVATE_DRY_RUN: false + LOG_LEVEL: 'debug' + + steps: + - checkout + - run: + name: Check if RENOVATE_TOKEN is set + command: | + if [ -z "${RENOVATE_TOKEN}" ]; then + echo "RENOVATE_TOKEN is not set. Skipping job." + circleci-agent step halt + fi + + if [ -z "${RENOVATE_REPOSITORIES}" ]; then + echo "Renovate repository is not set. Skipping job." + circleci-agent step halt + fi + + if [ -z "${RENOVATE_GIT_AUTHOR}" ]; then + echo "Renovate git author is not set. Skipping job." + circleci-agent step halt + fi + + - run: + name: Validate Renovate configuration + command: renovate-config-validator + + - run: + name: Run Renovate + command: renovate + +################################################################################ +# WORKFLOWS +################################################################################ + +workflows: + version: 2 + # Commit workflow. Runs for every commit push to the remote repository. + commit: + jobs: + - database: + filters: + tags: + only: /.*/ + - build: + requires: + - database + filters: + tags: + only: /.*/ + - deploy: + requires: + - build + filters: + branches: + # Allowed branches: + # - production, main, master, develop, ci, cisomething + # - project/description + # - deps/* + # - feature/description, feature/123-description + # - bugfix/description, bugfix/123-description + # - release/__VERSION__, release/__VERSION__ (per https://semver.org/) + # - release/2023-04-17, release/2023-04-17.123 (date-based) + # - hotfix/__VERSION__, hotfix/__VERSION__ (per https://semver.org/) + # - hotfix/2023-04-17, hotfix/2023-04-17.123 (date-based) + only: /^(production|main|master|develop)$|^project\/[a-zA-z0-9\-\.]+|^(feature|bugfix)\/[a-zA-Z0-9\-\.\,_]+$|^ci.*|^(release|hotfix)\/[0-9]+(\.[0-9]+){2}(-rc\.[0-9]+)?$|^(release|hotfix)\/[0-9]{4}-[0-9]{2}-[0-9]{2}(\.[0-9]+)?$/ + tags: + ignore: /.*/ + - deploy-tags: + requires: + - build + filters: + branches: + ignore: /.*/ + tags: + # Allowed tags: + # - __VERSION__, __VERSION__ (per https://semver.org/) + # - 2023-04-17, 2023-04-17.123 (date-based) + only: /^[0-9]+(\.[0-9]+){2}(-rc\.[0-9]+)?$|^[0-9]{4}-[0-9]{2}-[0-9]{2}(\.[0-9]+)?$/ + + # Nightly database workflow runs overnight to capture fresh database and cache it. + nightly-db: + triggers: + - schedule: + cron: *nightly_db_schedule + filters: + branches: + only: + - develop + jobs: + - database-nightly + + # Self-hosted Renovate workflow. + update-dependencies: + triggers: + - schedule: + cron: "5 11,23 * * *" + filters: + branches: + only: + - develop + jobs: + - update-dependencies + + update-dependencies-manual: + when: << pipeline.parameters.run_update_dependencies >> + jobs: + - update-dependencies diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/.dockerignore b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/.dockerignore new file mode 100644 index 000000000..ff8157939 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/.dockerignore @@ -0,0 +1,19 @@ +@@ -32,10 +32,8 @@ + !.sass-lint.yml + !.stylelintrc.js + !.twig-cs-fixer.php +-!gherkinlint.json + !Gruntfile.js + !auth.json +-!behat.yml + !composer.json + !composer.lock + !yarn.lock +@@ -45,7 +43,6 @@ + !phpcs.xml + !phpmd.xml + !phpstan.neon +-!phpunit.xml + !rector.php + !scripts + !tests diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/.github/workflows/-build-test-deploy.yml b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/.github/workflows/-build-test-deploy.yml new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/.gitignore b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/.gitignore new file mode 100644 index 000000000..ebbf780cb --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/.gitignore @@ -0,0 +1,8 @@ +@@ -48,7 +48,6 @@ + web/themes/**/build + .data + .logs +-.phpunit.cache + .twig-cs-fixer.cache + + # Ignore local override files. diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/README.md b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/README.md new file mode 100644 index 000000000..78e06291b --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/README.md @@ -0,0 +1,9 @@ +@@ -4,7 +4,7 @@ + + Drupal 11 implementation of star wars for star wars Org + +-[![Database, Build, Test and Deploy](https://github.com/star_wars_org/star_wars/actions/workflows/build-test-deploy.yml/badge.svg)](https://github.com/star_wars_org/star_wars/actions/workflows/build-test-deploy.yml) ++[![CircleCI](https://circleci.com/gh/star_wars_org/star_wars.svg?style=shield)](https://circleci.com/gh/star_wars_org/star_wars) + + ![Drupal 11](https://img.shields.io/badge/Drupal-11-blue.svg) + [![codecov](https://codecov.io/gh/star_wars_org/star_wars/graph/badge.svg)](https://codecov.io/gh/star_wars_org/star_wars) diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/composer.json b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/composer.json new file mode 100644 index 000000000..1c02ed659 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/composer.json @@ -0,0 +1,33 @@ +@@ -29,20 +29,13 @@ + "webflo/drupal-finder": "__VERSION__" + }, + "require-dev": { +- "behat/behat": "__VERSION__", +- "dantleech/gherkin-lint": "__VERSION__", + "dealerdirect/phpcodesniffer-composer-installer": "__VERSION__", +- "drevops/behat-format-progress-fail": "__VERSION__", +- "drevops/behat-screenshot": "__VERSION__", +- "drevops/behat-steps": "__VERSION__", + "drupal/core-dev": "__VERSION__", +- "drupal/drupal-extension": "__VERSION__", + "ergebnis/composer-normalize": "__VERSION__", + "mglaman/phpstan-drupal": "__VERSION__", + "palantirnet/drupal-rector": "__VERSION__", + "phpcompatibility/php-compatibility": "__VERSION__", + "phpmd/phpmd": "__VERSION__", +- "phpspec/prophecy-phpunit": "__VERSION__", + "phpstan/extension-installer": "__VERSION__", + "phpstan/phpstan": "__VERSION__", + "pyrech/composer-changelogs": "__VERSION__", +@@ -63,11 +56,6 @@ + "autoload": { + "classmap": [ + "scripts/composer/ScriptHandler.php" +- ] +- }, +- "autoload-dev": { +- "classmap": [ +- "tests/phpunit/" + ] + }, + "config": { diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/docs/ci.md b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/docs/ci.md new file mode 100644 index 000000000..7d29e25c6 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/docs/ci.md @@ -0,0 +1,31 @@ +@@ -5,12 +5,12 @@ + Before feature changes can be merged into a shared mainline, a complete build + must run and pass all tests on CI server. + +-## GitHub Actions ++## Circle CI + +-This project uses [GitHub Actions](https://github.com/features/actions) as a +-CI server: it imports production backups into fully built codebase and runs +-code linting and tests. When tests pass, a deployment process is triggered for +-nominated branches (usually, `main` and `develop`). ++This project uses [Circle CI](https://circleci.com/) as a CI server: it imports ++production backups into fully built codebase and runs code linting and tests. ++When tests pass, a deployment process is triggered for nominated branches ++(usually, `main` and `develop`). + + Refer to https://www.vortextemplate.com/docs/continuous-integration for more information. + +@@ -21,9 +21,6 @@ + + ### SSH + +-GitHub Actions does not supports shell access to the build, but there is an +-action provided withing the `build` job that allows you to run a build with SSH +-support. +- +-Use "Run workflow" button in GitHub Actions UI to start build with SSH support +-that will be available for 120 minutes after the build is finished. ++Circle CI supports shell access to the build for 120 minutes after the build is ++finished when the build is started with SSH support. Use "Rerun job with SSH" ++button in Circle CI UI to start build with SSH support. diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/docs/faqs.md b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/docs/faqs.md new file mode 100644 index 000000000..5dded30ab --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/docs/faqs.md @@ -0,0 +1,28 @@ +@@ -56,16 +56,6 @@ + + To disable, run `ahoy up`. + +-## How to use Xdebug on Behat scripts? +- +-1. Enable debugging: `ahoy debug` +-2. Enter CLI container: `ahoy cli` +-3. Run Behat tests: +- +-```bash +-vendor/bin/behat path/to/test.feature +-``` +- + ## What should I do to switch to a "clean" branch environment? + + Provided that your stack is already running: +@@ -130,10 +120,3 @@ + This theme will be used when Drupal is in maintenance mode. If `DRUPAL_MAINTENANCE_THEME` is not set, the system will fall back to using the value of `DRUPAL_THEME`. + + The maintenance theme should be a valid Drupal theme that is already installed and enabled on your site. +- +-## Behat tests with `@javascript` tag sometimes get stuck +- +-Behat tests with `@javascript` tag sometimes get stuck for about 10min then fail. +-The Chrome container randomly get stuck for an unknown reason. +- +-Restart the Chrome container: `docker compose restart chrome` diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/phpcs.xml b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/phpcs.xml new file mode 100644 index 000000000..044ddebde --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/phpcs.xml @@ -0,0 +1,9 @@ +@@ -52,8 +52,4 @@ + *.test + + +- +- +- *\/tests\/behat\/bootstrap/*\.php +- + diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/phpstan.neon b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/phpstan.neon new file mode 100644 index 000000000..9245e8fa5 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/phpstan.neon @@ -0,0 +1,8 @@ +@@ -38,7 +38,6 @@ + paths: + - web/modules/custom/*/tests/* + - web/themes/custom/*/tests/* +- - tests/phpunit/* + reportUnmatched: false + - + # Hook implementations do not provide docblocks for parameters, so there diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/tests/behat/bootstrap/-FeatureContext.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/tests/behat/bootstrap/-FeatureContext.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/tests/behat/features/-clamav.feature b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/tests/behat/features/-clamav.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/tests/behat/features/-counter.feature b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/tests/behat/features/-counter.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/tests/behat/features/-homepage.feature b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/tests/behat/features/-homepage.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/tests/behat/features/-login.feature b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/tests/behat/features/-login.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/tests/behat/features/-redis.feature b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/tests/behat/features/-redis.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/tests/behat/features/-robotstxt.feature b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/tests/behat/features/-robotstxt.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/tests/behat/features/-search.feature b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/tests/behat/features/-search.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/tests/phpunit/Drupal/-DatabaseSettingsTest.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/tests/phpunit/Drupal/-DatabaseSettingsTest.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/tests/phpunit/Drupal/-EnvironmentSettingsTest.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/tests/phpunit/Drupal/-EnvironmentSettingsTest.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/tests/phpunit/Drupal/-SettingsTestCase.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/tests/phpunit/Drupal/-SettingsTestCase.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/tests/phpunit/Drupal/-SwitchableSettingsTest.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/tests/phpunit/Drupal/-SwitchableSettingsTest.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/web/modules/custom/sw_base/tests/src/Functional/-ExampleTest.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/web/modules/custom/sw_base/tests/src/Functional/-ExampleTest.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/web/modules/custom/sw_base/tests/src/Functional/-SwBaseFunctionalTestBase.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/web/modules/custom/sw_base/tests/src/Functional/-SwBaseFunctionalTestBase.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/web/modules/custom/sw_base/tests/src/Kernel/-ExampleTest.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/web/modules/custom/sw_base/tests/src/Kernel/-ExampleTest.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/web/modules/custom/sw_base/tests/src/Kernel/-SwBaseKernelTestBase.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/web/modules/custom/sw_base/tests/src/Kernel/-SwBaseKernelTestBase.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/web/modules/custom/sw_base/tests/src/Traits/-ArrayTrait.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/web/modules/custom/sw_base/tests/src/Traits/-ArrayTrait.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/web/modules/custom/sw_base/tests/src/Traits/-AssertTrait.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/web/modules/custom/sw_base/tests/src/Traits/-AssertTrait.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/web/modules/custom/sw_base/tests/src/Traits/-MockTrait.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/web/modules/custom/sw_base/tests/src/Traits/-MockTrait.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/web/modules/custom/sw_base/tests/src/Traits/-ReflectionTrait.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/web/modules/custom/sw_base/tests/src/Traits/-ReflectionTrait.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/web/modules/custom/sw_base/tests/src/Unit/-ExampleTest.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/web/modules/custom/sw_base/tests/src/Unit/-ExampleTest.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/web/modules/custom/sw_base/tests/src/Unit/-SwBaseUnitTestBase.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/web/modules/custom/sw_base/tests/src/Unit/-SwBaseUnitTestBase.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/web/sites/default/includes/providers/-settings.gha.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/web/sites/default/includes/providers/-settings.gha.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/web/sites/default/includes/providers/settings.circleci.php b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/web/sites/default/includes/providers/settings.circleci.php new file mode 100644 index 000000000..7cad35cf8 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_groups_no_be_tests_circleci/web/sites/default/includes/providers/settings.circleci.php @@ -0,0 +1,17 @@ +*.test + + +- +- +- *\/tests\/behat\/bootstrap/*\.php +- + diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat/tests/behat/bootstrap/-FeatureContext.php b/.vortex/installer/tests/Fixtures/install/tools_no_behat/tests/behat/bootstrap/-FeatureContext.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat/tests/behat/features/-clamav.feature b/.vortex/installer/tests/Fixtures/install/tools_no_behat/tests/behat/features/-clamav.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat/tests/behat/features/-counter.feature b/.vortex/installer/tests/Fixtures/install/tools_no_behat/tests/behat/features/-counter.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat/tests/behat/features/-homepage.feature b/.vortex/installer/tests/Fixtures/install/tools_no_behat/tests/behat/features/-homepage.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat/tests/behat/features/-login.feature b/.vortex/installer/tests/Fixtures/install/tools_no_behat/tests/behat/features/-login.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat/tests/behat/features/-redis.feature b/.vortex/installer/tests/Fixtures/install/tools_no_behat/tests/behat/features/-redis.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat/tests/behat/features/-robotstxt.feature b/.vortex/installer/tests/Fixtures/install/tools_no_behat/tests/behat/features/-robotstxt.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat/tests/behat/features/-search.feature b/.vortex/installer/tests/Fixtures/install/tools_no_behat/tests/behat/features/-search.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/-behat.yml b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/-behat.yml new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/-gherkinlint.json b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/-gherkinlint.json new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/.ahoy.yml b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/.ahoy.yml new file mode 100644 index 000000000..1dea8902e --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/.ahoy.yml @@ -0,0 +1,37 @@ +@@ -180,7 +180,6 @@ + cmd: | + ahoy lint-be + ahoy lint-fe +- ahoy lint-tests + + lint-be: + usage: Lint back-end code. +@@ -200,8 +199,6 @@ + lint-tests: + usage: Lint tests code. + cmd: | +- ahoy cli vendor/bin/gherkinlint lint tests/behat/features +- + lint-fix: + usage: Fix lint issues of back-end and front-end code. + cmd: | +@@ -227,7 +224,6 @@ + ahoy test-unit + ahoy test-kernel + ahoy test-functional +- ahoy test-bdd + + test-unit: + usage: Run PHPUnit unit tests. +@@ -240,11 +236,6 @@ + test-functional: + usage: Run PHPUnit functional tests. + cmd: ahoy cli vendor/bin/phpunit --testsuite=functional "$@" +- +- test-bdd: +- usage: Run BDD tests. +- aliases: [test-behat] +- cmd: ahoy cli php -d memory_limit=-1 vendor/bin/behat --colors "$@" + + debug: + usage: Enable PHP Xdebug. diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/.circleci/README.md b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/.circleci/README.md new file mode 100644 index 000000000..533a875e9 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/.circleci/README.md @@ -0,0 +1,2 @@ +You may add custom scripts, which would run only in CI, to this directory and +reference them from `config.yml` file. diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/.circleci/config.yml b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/.circleci/config.yml new file mode 100644 index 000000000..59b0f8333 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/.circleci/config.yml @@ -0,0 +1,531 @@ +# CircleCI 2.0 configuration file. +# +# This configuration file uses the "docker" executor to run the Docker stack. +# +# A "runner" container, created from a specified container image, is used to +# checkout source code and run commands defined in this file. Application Docker +# containers defined in `docker-compose.yml` run on a *remote* Docker server +# controlled by CircleCI. +# The "runner" container uses Docker client to control the remote Docker server. +version: '2.1' + +aliases: + # SSH key fingerprint to download the database. + # Replace this key fingerprint with your own and remove this comment. + - &db_ssh_fingerprint "SHA256:6d+U5QubT0eAWz+4N2wt+WM2qx6o4cvyvQ6xILETJ84" + + # SSH key fingerprint to deploy code. + # Replace this key fingerprint with your own and remove this comment. + - &deploy_ssh_fingerprint "SHA256:6d+U5QubT0eAWz+4N2wt+WM2qx6o4cvyvQ6xILETJ84" + + # Schedule to run nightly database build (to cache the database for the next day). + - &nightly_db_schedule "0 18 * * *" + + # Shared runner container configuration applied to each job. + - &runner_config + working_directory: &working_directory ~/project + environment: + VORTEX_DB_DOWNLOAD_SSH_FINGERPRINT: *db_ssh_fingerprint + VORTEX_DEPLOY_SSH_FINGERPRINT: *deploy_ssh_fingerprint + docker: + # Using the 'runner' container where each job will be executed. + # This container has all the necessary tools to run a dockerized environment. + # https://github.com/drevops/ci-runner + # https://hub.docker.com/repository/docker/drevops/ci-runner/tags + - image: drevops/ci-runner:__VERSION__ + auth: + username: ${VORTEX_CONTAINER_REGISTRY_USER} + password: ${VORTEX_CONTAINER_REGISTRY_PASS} + environment: + # Set runner timezone via UI to ensure that executed operations use correct timestamps. + # https://en.wikipedia.org/wiki/List_of_tz_database_time_zones + TZ: UTC + # Set runner terminal capabilities. + TERM: xterm-256color + # Disable strict host key checking for SSH connections. + VORTEX_SSH_DISABLE_STRICT_HOST_KEY_CHECKING: "1" + # Remove all SSH keys from the runner container. + VORTEX_SSH_REMOVE_ALL_KEYS: "1" + # 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" + # Directory to store test results. + VORTEX_CI_TEST_RESULTS: &test_results /tmp/tests + # Directory to store test artifacts. + VORTEX_CI_ARTIFACTS: &artifacts /tmp/artifacts + # Directory to use for artifact deployments. + VORTEX_DEPLOY_ARTIFACT_SRC: /tmp/workspace/code + # Source code location for artifact deployments. + VORTEX_DEPLOY_ARTIFACT_ROOT: *working_directory + # Report file location for artifact deployments. + VORTEX_DEPLOY_ARTIFACT_LOG: /tmp/artifacts/deployment_log.txt + # Check only minimal stack requirements. + VORTEX_DOCTOR_CHECK_MINIMAL: 1 + # CI runner resource class. + # https://circleci.com/docs/2.0/configuration-reference/#resource_class + # Change to 'large' for faster builds. + resource_class: medium + + - &step_setup_remote_docker + setup_remote_docker: + # Docker Layer Caching allows to significantly speed up builds by caching + # images built during previous runs. + # https://circleci.com/docs/2.0/docker-layer-caching/ + docker_layer_caching: false + version: default + + - &step_process_codebase_for_ci + run: + name: Process codebase to run in CI + command: | + find . -name "docker-compose.yml" -print0 | xargs -0 -I {} sh -c "sed -i -e ''/###/d'' {} && sed -i -e ''s/##//'' {}" + mkdir -p /tmp/workspace/code + + - &load_variables_from_dotenv + run: + name: Load environment variables from .env file + # Load variables from .env file, respecting existing values, and make them available for the next steps. + command: t=$(mktemp) && export -p >"${t}" && set -a && . ./.env && set +a && . "${t}" && export -p >> "$BASH_ENV" + +################################################################################ +# PARAMETERS +################################################################################ + +parameters: + run_update_dependencies: + type: boolean + default: false + +################################################################################ +# JOBS +################################################################################ + +jobs: + # Database handling is a first step of the build. + # - $VORTEX_CI_DB_CACHE_TIMESTAMP is used to determine if a fresh DB dump + # should be downloaded for the current build. Usually, a daily database dump + # is sufficient for development activities. + # - $VORTEX_CI_DB_CACHE_FALLBACK is used if the cache did not match $VORTEX_CI_DB_CACHE_TIMESTAMP. + # This allows to rely on the cache from the previous days within the same branch. + database: &job-database + <<: *runner_config + steps: + - attach_workspace: + at: /tmp/workspace + + - add_ssh_keys: + fingerprints: + - *db_ssh_fingerprint + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + - *step_setup_remote_docker + + - run: + name: Create cache keys for database caching as files + command: | + echo "${VORTEX_CI_DB_CACHE_BRANCH}" | tee /tmp/db_cache_branch + echo "${VORTEX_CI_DB_CACHE_FALLBACK/no/${CIRCLE_BUILD_NUM}}" | tee /tmp/db_cache_fallback + date "${VORTEX_CI_DB_CACHE_TIMESTAMP}" | tee /tmp/db_cache_timestamp + echo "yes" | tee /tmp/db_cache_fallback_yes + + - restore_cache: + keys: + # Restore DB cache based on the cache strategy set by the cache keys below. + # https://circleci.com/docs/2.0/caching/#restoring-cache + # Change 'v1' to 'v2', 'v3' etc., commit and push to force 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). + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback" }}-{{ checksum "/tmp/db_cache_timestamp" }} + # Fallback to caching by default branch name only. Allows to use + # cache from the branch build on the previous day. + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback" }}- + + - run: + name: Download DB + command: VORTEX_DB_DOWNLOAD_SEMAPHORE=/tmp/download-db-success ./scripts/vortex/download-db.sh + no_output_timeout: 30m + + # Execute commands after database download script finished: if the + # DB dump was downloaded - build the site (to ensure that the DB dump + # is valid) and export the DB using selected method (to support + # "file-to-image" or "image-to-file" conversions). + # Note that configuration changes and the DB updates are not applied, so + # the database will be cached in the same state as downloaded. + - run: + name: Export DB after download + command: | + [ ! -f /tmp/download-db-success ] && echo "==> Database download semaphore file is missing. DB export will not proceed." && exit 0 + ./scripts/vortex/login-container-registry.sh + docker compose up --detach && sleep 15 + docker compose exec cli mkdir -p .data && docker compose cp -L .data/db.sql cli:/app/.data/db.sql || true + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c "VORTEX_PROVISION_POST_OPERATIONS_SKIP=1 ./scripts/vortex/provision.sh" + grep -q ^VORTEX_DB_IMAGE .env && rm .data/db.sql || true + ./scripts/vortex/export-db.sh db.sql + no_output_timeout: 30m + + - save_cache: + # 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. + key: __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback_yes" }}-{{ checksum "/tmp/db_cache_timestamp" }} + paths: + - /root/project/.data + + # Nightly database job. Same as above, but with additional variables set. + database-nightly: + <<: *job-database + environment: + VORTEX_DB_DOWNLOAD_SSH_FINGERPRINT: *db_ssh_fingerprint + VORTEX_DEPLOY_SSH_FINGERPRINT: *deploy_ssh_fingerprint + # Enforce fresh DB build (do not rely on fallback caches). + VORTEX_CI_DB_CACHE_FALLBACK: 'no' + # Always use fresh base image for the database (if database-in-image storage is used). + VORTEX_DB_IMAGE_BASE: drevops/mariadb-drupal-data:__VERSION__ + # Deploy container image (if database-in-image storage is used). + VORTEX_EXPORT_DB_CONTAINER_REGISTRY_DEPLOY_PROCEED: 1 + # Do not build the Drupal front-end. + VORTEX_FRONTEND_BUILD_SKIP: 1 + + # Build and test is a second step of the build. The testing is performed + # within the same job to save time on provisioning during the job. + build: &job_build + <<: *runner_config + parallelism: 2 + steps: + - attach_workspace: + at: /tmp/workspace + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + + - run: + name: Validate Composer configuration + command: composer validate --strict || [ "${VORTEX_CI_COMPOSER_VALIDATE_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Set cache keys for database caching + command: | + echo "${VORTEX_CI_DB_CACHE_BRANCH}" | tee /tmp/db_cache_branch + echo "yes" | tee /tmp/db_cache_fallback_yes + echo "$(date ${VORTEX_CI_DB_CACHE_TIMESTAMP})" | tee /tmp/db_cache_timestamp + + - restore_cache: + keys: + # Use cached artifacts from previous builds of this branch. + # https://circleci.com/docs/2.0/caching/#restoring-cache + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback_yes" }}-{{ checksum "/tmp/db_cache_timestamp" }} + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback_yes" }}- + + - *step_setup_remote_docker + + - run: + name: Login to container registry + command: ./scripts/vortex/login-container-registry.sh + + - run: + name: Lint Dockerfiles with Hadolint + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + for file in $(find .docker -name 'Dockerfile' -o -name '*.dockerfile'); do + echo "Linting ${file}" && cat "${file}" | docker run --rm -i hadolint/hadolint || [ "${VORTEX_CI_HADOLINT_IGNORE_FAILURE:-0}" -eq 1 ] + done + + - run: + name: Lint Docker Compose files with DCLint + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker run --rm -v "${PWD}":/app zavoloklom/dclint:__VERSION__ . || [ "${VORTEX_CI_DCLINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Build stack + command: docker compose up -d + + - run: + name: Export built codebase + command: | + echo "${VORTEX_DEPLOY_TYPES:-}" | grep -vq "artifact" && exit 0 || true + mkdir -p "/tmp/workspace/code" + docker compose cp -L cli:"/app/." "/tmp/workspace/code" + du -sh "/tmp/workspace/code" + + - run: + name: Install development dependencies + command: | + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c " \ + if [ -n \"${PACKAGE_TOKEN:-}\" ]; then export COMPOSER_AUTH='{\"github-oauth\": {\"github.com\": \"${PACKAGE_TOKEN-}\"}}'; fi && \ + COMPOSER_MEMORY_LIMIT=-1 composer --ansi install --prefer-dist" + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c "yarn install --frozen-lockfile" + + - run: + name: Validate Composer configuration is normalized + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli composer normalize --dry-run || [ "${VORTEX_CI_COMPOSER_NORMALIZE_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with PHPCS + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/phpcs || [ "${VORTEX_CI_PHPCS_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with PHPStan + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/phpstan || [ "${VORTEX_CI_PHPSTAN_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with Rector + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/rector --clear-cache --dry-run || [ "${VORTEX_CI_RECTOR_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with PHPMD + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/phpmd . text phpmd.xml || [ "${VORTEX_CI_PHPMD_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with Twig CS Fixer + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/twig-cs-fixer || [ "${VORTEX_CI_TWIG_CS_FIXER_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint module code with NodeJS linters + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli bash -c "yarn run lint" || [ "${VORTEX_CI_NODEJS_LINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint theme code with NodeJS linters + command: | + { [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ]; } || [ "${VORTEX_FRONTEND_BUILD_SKIP:-0}" -eq 1 ] && exit 0 + docker compose exec -T cli bash -c "yarn --cwd=\${WEBROOT}/themes/custom/\${DRUPAL_THEME} run lint" || [ "${VORTEX_CI_NODEJS_LINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Provision site + command: | + if [ -f .data/db.sql ]; then + docker compose exec cli mkdir -p .data + docker compose cp -L .data/db.sql cli:/app/.data/db.sql + fi + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli ./scripts/vortex/provision.sh + no_output_timeout: 30m + + - run: + name: Test with PHPUnit + command: docker compose exec -T cli vendor/bin/phpunit || [ "${VORTEX_CI_PHPUNIT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Process test logs and artifacts + command: | + mkdir -p "${VORTEX_CI_TEST_RESULTS}" "${VORTEX_CI_ARTIFACTS}" + if docker compose ps --services --filter "status=running" | grep -q cli && docker compose exec cli test -d /app/.logs; then + docker compose cp cli:/app/.logs/. "${VORTEX_CI_ARTIFACTS}/" + if docker compose exec -T cli sh -c '[ -d /app/.logs/test_results/ ]'; then + docker compose cp cli:/app/.logs/test_results/. "${VORTEX_CI_TEST_RESULTS}/" + fi + fi + when: always + + - store_test_results: + path: *test_results + + - store_artifacts: + path: *artifacts + + - run: + name: Upload code coverage reports to Codecov + command: | + if [ -n "${CODECOV_TOKEN}" ] && [ -d /tmp/artifacts/coverage ] && ! echo "${CIRCLE_BRANCH}" | grep -q '^deps/'; then + codecov -Z -s /tmp/artifacts/coverage; + fi + + - persist_to_workspace: + root: /tmp/workspace + paths: + - code + + # Deploy primary branches. + deploy: &job_deploy + <<: *runner_config + steps: + - attach_workspace: + at: /tmp/workspace + + - add_ssh_keys: + fingerprints: + - *deploy_ssh_fingerprint + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + + - run: + name: Deploy + command: | + VORTEX_DEPLOY_BRANCH="${CIRCLE_BRANCH}" \ + VORTEX_DEPLOY_PR="$(echo ${CIRCLE_PULL_REQUEST} | cut -d'/' -f 7)" \ + VORTEX_DEPLOY_PR_HEAD=${CIRCLE_SHA1} \ + ./scripts/vortex/deploy.sh + no_output_timeout: 30m + + - store_artifacts: + path: *artifacts + + # Deploy tags. + deploy-tags: &job-deploy-tags + <<: *runner_config + steps: + - attach_workspace: + at: /tmp/workspace + + - add_ssh_keys: + fingerprints: + - *deploy_ssh_fingerprint + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + + - run: + name: Deploy + command: VORTEX_DEPLOY_MODE="tag" ./scripts/vortex/deploy.sh + no_output_timeout: 30m + + - store_artifacts: + path: *artifacts + + # Self-hosted dependency updates. + # Add the following environment variables to the CircleCI project: + # - RENOVATE_TOKEN: GitHub access token. + # - RENOVATE_REPOSITORIES: Repository to run Renovate on as `vendor/repository`. + # - RENOVATE_GIT_AUTHOR: Author for Renovate commits as `Name `. + # Variables provided below can be overridden in the CircleCI project settings. + update-dependencies: + docker: + - image: renovate/renovate:__VERSION__ + environment: + RENOVATE_PLATFORM: 'github' + RENOVATE_AUTODISCOVER: false + RENOVATE_DEPENDENCY_DASHBOARD_TITLE: 'Renovate Dependency Dashboard (self-hosted) by CircleCI' + RENOVATE_DEPENDENCY_DASHBOARD: false + RENOVATE_DRY_RUN: false + LOG_LEVEL: 'debug' + + steps: + - checkout + - run: + name: Check if RENOVATE_TOKEN is set + command: | + if [ -z "${RENOVATE_TOKEN}" ]; then + echo "RENOVATE_TOKEN is not set. Skipping job." + circleci-agent step halt + fi + + if [ -z "${RENOVATE_REPOSITORIES}" ]; then + echo "Renovate repository is not set. Skipping job." + circleci-agent step halt + fi + + if [ -z "${RENOVATE_GIT_AUTHOR}" ]; then + echo "Renovate git author is not set. Skipping job." + circleci-agent step halt + fi + + - run: + name: Validate Renovate configuration + command: renovate-config-validator + + - run: + name: Run Renovate + command: renovate + +################################################################################ +# WORKFLOWS +################################################################################ + +workflows: + version: 2 + # Commit workflow. Runs for every commit push to the remote repository. + commit: + jobs: + - database: + filters: + tags: + only: /.*/ + - build: + requires: + - database + filters: + tags: + only: /.*/ + - deploy: + requires: + - build + filters: + branches: + # Allowed branches: + # - production, main, master, develop, ci, cisomething + # - project/description + # - deps/* + # - feature/description, feature/123-description + # - bugfix/description, bugfix/123-description + # - release/__VERSION__, release/__VERSION__ (per https://semver.org/) + # - release/2023-04-17, release/2023-04-17.123 (date-based) + # - hotfix/__VERSION__, hotfix/__VERSION__ (per https://semver.org/) + # - hotfix/2023-04-17, hotfix/2023-04-17.123 (date-based) + only: /^(production|main|master|develop)$|^project\/[a-zA-z0-9\-\.]+|^(feature|bugfix)\/[a-zA-Z0-9\-\.\,_]+$|^ci.*|^(release|hotfix)\/[0-9]+(\.[0-9]+){2}(-rc\.[0-9]+)?$|^(release|hotfix)\/[0-9]{4}-[0-9]{2}-[0-9]{2}(\.[0-9]+)?$/ + tags: + ignore: /.*/ + - deploy-tags: + requires: + - build + filters: + branches: + ignore: /.*/ + tags: + # Allowed tags: + # - __VERSION__, __VERSION__ (per https://semver.org/) + # - 2023-04-17, 2023-04-17.123 (date-based) + only: /^[0-9]+(\.[0-9]+){2}(-rc\.[0-9]+)?$|^[0-9]{4}-[0-9]{2}-[0-9]{2}(\.[0-9]+)?$/ + + # Nightly database workflow runs overnight to capture fresh database and cache it. + nightly-db: + triggers: + - schedule: + cron: *nightly_db_schedule + filters: + branches: + only: + - develop + jobs: + - database-nightly + + # Self-hosted Renovate workflow. + update-dependencies: + triggers: + - schedule: + cron: "5 11,23 * * *" + filters: + branches: + only: + - develop + jobs: + - update-dependencies + + update-dependencies-manual: + when: << pipeline.parameters.run_update_dependencies >> + jobs: + - update-dependencies diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/.dockerignore b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/.dockerignore new file mode 100644 index 000000000..c3e373586 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/.dockerignore @@ -0,0 +1,11 @@ +@@ -32,10 +32,8 @@ + !.sass-lint.yml + !.stylelintrc.js + !.twig-cs-fixer.php +-!gherkinlint.json + !Gruntfile.js + !auth.json +-!behat.yml + !composer.json + !composer.lock + !yarn.lock diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/.github/workflows/-build-test-deploy.yml b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/.github/workflows/-build-test-deploy.yml new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/README.md b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/README.md new file mode 100644 index 000000000..78e06291b --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/README.md @@ -0,0 +1,9 @@ +@@ -4,7 +4,7 @@ + + Drupal 11 implementation of star wars for star wars Org + +-[![Database, Build, Test and Deploy](https://github.com/star_wars_org/star_wars/actions/workflows/build-test-deploy.yml/badge.svg)](https://github.com/star_wars_org/star_wars/actions/workflows/build-test-deploy.yml) ++[![CircleCI](https://circleci.com/gh/star_wars_org/star_wars.svg?style=shield)](https://circleci.com/gh/star_wars_org/star_wars) + + ![Drupal 11](https://img.shields.io/badge/Drupal-11-blue.svg) + [![codecov](https://codecov.io/gh/star_wars_org/star_wars/graph/badge.svg)](https://codecov.io/gh/star_wars_org/star_wars) diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/composer.json b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/composer.json new file mode 100644 index 000000000..00b1818eb --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/composer.json @@ -0,0 +1,15 @@ +@@ -29,14 +29,8 @@ + "webflo/drupal-finder": "__VERSION__" + }, + "require-dev": { +- "behat/behat": "__VERSION__", +- "dantleech/gherkin-lint": "__VERSION__", + "dealerdirect/phpcodesniffer-composer-installer": "__VERSION__", +- "drevops/behat-format-progress-fail": "__VERSION__", +- "drevops/behat-screenshot": "__VERSION__", +- "drevops/behat-steps": "__VERSION__", + "drupal/core-dev": "__VERSION__", +- "drupal/drupal-extension": "__VERSION__", + "ergebnis/composer-normalize": "__VERSION__", + "mglaman/phpstan-drupal": "__VERSION__", + "palantirnet/drupal-rector": "__VERSION__", diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/docs/ci.md b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/docs/ci.md new file mode 100644 index 000000000..7d29e25c6 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/docs/ci.md @@ -0,0 +1,31 @@ +@@ -5,12 +5,12 @@ + Before feature changes can be merged into a shared mainline, a complete build + must run and pass all tests on CI server. + +-## GitHub Actions ++## Circle CI + +-This project uses [GitHub Actions](https://github.com/features/actions) as a +-CI server: it imports production backups into fully built codebase and runs +-code linting and tests. When tests pass, a deployment process is triggered for +-nominated branches (usually, `main` and `develop`). ++This project uses [Circle CI](https://circleci.com/) as a CI server: it imports ++production backups into fully built codebase and runs code linting and tests. ++When tests pass, a deployment process is triggered for nominated branches ++(usually, `main` and `develop`). + + Refer to https://www.vortextemplate.com/docs/continuous-integration for more information. + +@@ -21,9 +21,6 @@ + + ### SSH + +-GitHub Actions does not supports shell access to the build, but there is an +-action provided withing the `build` job that allows you to run a build with SSH +-support. +- +-Use "Run workflow" button in GitHub Actions UI to start build with SSH support +-that will be available for 120 minutes after the build is finished. ++Circle CI supports shell access to the build for 120 minutes after the build is ++finished when the build is started with SSH support. Use "Rerun job with SSH" ++button in Circle CI UI to start build with SSH support. diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/docs/faqs.md b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/docs/faqs.md new file mode 100644 index 000000000..5dded30ab --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/docs/faqs.md @@ -0,0 +1,28 @@ +@@ -56,16 +56,6 @@ + + To disable, run `ahoy up`. + +-## How to use Xdebug on Behat scripts? +- +-1. Enable debugging: `ahoy debug` +-2. Enter CLI container: `ahoy cli` +-3. Run Behat tests: +- +-```bash +-vendor/bin/behat path/to/test.feature +-``` +- + ## What should I do to switch to a "clean" branch environment? + + Provided that your stack is already running: +@@ -130,10 +120,3 @@ + This theme will be used when Drupal is in maintenance mode. If `DRUPAL_MAINTENANCE_THEME` is not set, the system will fall back to using the value of `DRUPAL_THEME`. + + The maintenance theme should be a valid Drupal theme that is already installed and enabled on your site. +- +-## Behat tests with `@javascript` tag sometimes get stuck +- +-Behat tests with `@javascript` tag sometimes get stuck for about 10min then fail. +-The Chrome container randomly get stuck for an unknown reason. +- +-Restart the Chrome container: `docker compose restart chrome` diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/phpcs.xml b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/phpcs.xml new file mode 100644 index 000000000..044ddebde --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/phpcs.xml @@ -0,0 +1,9 @@ +@@ -52,8 +52,4 @@ + *.test + + +- +- +- *\/tests\/behat\/bootstrap/*\.php +- + diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/tests/behat/bootstrap/-FeatureContext.php b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/tests/behat/bootstrap/-FeatureContext.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/tests/behat/features/-clamav.feature b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/tests/behat/features/-clamav.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/tests/behat/features/-counter.feature b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/tests/behat/features/-counter.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/tests/behat/features/-homepage.feature b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/tests/behat/features/-homepage.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/tests/behat/features/-login.feature b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/tests/behat/features/-login.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/tests/behat/features/-redis.feature b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/tests/behat/features/-redis.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/tests/behat/features/-robotstxt.feature b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/tests/behat/features/-robotstxt.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/tests/behat/features/-search.feature b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/tests/behat/features/-search.feature new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/tests/phpunit/CircleCiConfigTest.php b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/tests/phpunit/CircleCiConfigTest.php new file mode 100644 index 000000000..d07086d21 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/tests/phpunit/CircleCiConfigTest.php @@ -0,0 +1,261 @@ +config = Yaml::decode($file); + } + + /** + * Tests for deploy branch regex. + * + * @see https://semver.org/ + */ + #[DataProvider('dataProviderDeployBranchRegex')] + public function testDeployBranchRegex(string $branch, bool $expected = TRUE): void { + $this->assertEquals($expected, preg_match($this->config['workflows']['commit']['jobs'][2]['deploy']['filters']['branches']['only'], $branch)); + } + + /** + * Data provider for testDeployBranchRegex(). + */ + public static function dataProviderDeployBranchRegex(): array { + return [ + // Positive branches. + ['production'], + ['main'], + ['master'], + ['develop'], + + ['ci'], + ['cisomething'], + + ['release/123.456.789'], + ['release/123.456.789-rc.123'], + ['hotfix/123.456.789'], + ['hotfix/123.456.789-rc.123'], + + ['release/2023-04-17'], + ['release/2023-04-17.1'], + ['hotfix/2023-04-17'], + ['hotfix/2023-04-17.1'], + + ['feature/description'], + ['feature/Description'], + ['feature/Description-With-Hyphens'], + ['feature/Description-With_Underscores'], + ['feature/123-description'], + ['feature/123-Description'], + ['feature/UNDERSCORES_UNDERSCORES'], + ['feature/123-Description-With_UNDERSCORES'], + ['feature/1.x'], + ['feature/0.x'], + ['feature/0.1.x'], + ['feature/0.1.2.x'], + ['feature/1.x-description'], + ['feature/0.x-description'], + ['feature/0.1.x-description'], + ['feature/0.1.2.x-description'], + + ['bugfix/description'], + ['bugfix/Description'], + ['bugfix/Description-With-Hyphens'], + ['bugfix/Description-With_Underscores'], + ['bugfix/123-description'], + ['bugfix/123-Description'], + ['bugfix/UNDERSCORES_UNDERSCORES'], + ['bugfix/123-Description-With_UNDERSCORES'], + ['bugfix/1.x'], + ['bugfix/0.x'], + ['bugfix/0.1.x'], + ['bugfix/0.1.2.x'], + ['bugfix/1.x-description'], + ['bugfix/0.x-description'], + ['bugfix/0.1.x-description'], + ['bugfix/0.1.2.x-description'], + + ['project/description'], + ['project/Description'], + ['project/Description-With-Hyphens'], + ['project/123-description'], + ['project/123-Description'], + ['project/1.x'], + ['project/0.x'], + ['project/0.1.x'], + ['project/0.1.2.x'], + ['project/1.x-description'], + ['project/0.x-description'], + ['project/0.1.x-description'], + ['project/0.1.2.x-description'], + + // Negative branches. + ['something', FALSE], + ['premain', FALSE], + ['premaster', FALSE], + ['predevelop', FALSE], + ['mainpost', FALSE], + ['masterpost', FALSE], + ['developpost', FALSE], + ['premainpost', FALSE], + ['premasterpost', FALSE], + ['predeveloppost', FALSE], + + ['preci', FALSE], + ['precipost', FALSE], + + ['deps/something', FALSE], + ['deps', FALSE], + ['predeps', FALSE], + ['depspost', FALSE], + ['predepspost', FALSE], + + ['feature', FALSE], + ['release', FALSE], + ['hotfix', FALSE], + ['prefeature', FALSE], + ['prerelease', FALSE], + ['prehotfix', FALSE], + ['featurepost', FALSE], + ['releasepost', FALSE], + ['hotfixpost', FALSE], + ['prefeaturepost', FALSE], + ['prereleasepost', FALSE], + ['prehotfixpost', FALSE], + + ['release/123', FALSE], + ['release/123.456', FALSE], + ['hotfix/123', FALSE], + ['hotfix/123.456', FALSE], + + ['release/202-04-17', FALSE], + ['release/2023-4-17', FALSE], + ['release/2023-04-1', FALSE], + ['release/pre2023-04-17', FALSE], + ['release/2023-04-17post', FALSE], + ['release/pre2023-04-17post', FALSE], + + ['hotfix/202-04-17', FALSE], + ['hotfix/2023-4-17', FALSE], + ['hotfix/2023-04-1', FALSE], + ['hotfix/pre2023-04-17', FALSE], + ['hotfix/2023-04-17post', FALSE], + ['hotfix/pre2023-04-17post', FALSE], + + ['release/123.456.789-something', FALSE], + ['release/123.456.789-rc', FALSE], + ['release/123.456.789-rc123', FALSE], + ['release/123.456.789-rc-123', FALSE], + ['release/123.456.789-prerc123', FALSE], + ['release/123.456.789-rcpost123', FALSE], + ['release/123.456.789-prercpost123', FALSE], + ['release/123.456.789-rc123something', FALSE], + ['release/123.456.789-rc.123something', FALSE], + ['release/123.456.789-rc.123-something', FALSE], + + ['hotfix/123.456.789-something', FALSE], + ['hotfix/123.456.789-rc', FALSE], + ['hotfix/123.456.789-rc123', FALSE], + ['hotfix/123.456.789-rc-123', FALSE], + ['hotfix/123.456.789-prerc123', FALSE], + ['hotfix/123.456.789-rcpost123', FALSE], + ['hotfix/123.456.789-prercpost123', FALSE], + ['hotfix/123.456.789-rc123something', FALSE], + ['hotfix/123.456.789-rc.123something', FALSE], + ['hotfix/123.456.789-rc.123-something', FALSE], + + ['prefeature/something', FALSE], + ['prefbugfix/something', FALSE], + ['prerelease/something', FALSE], + ['prehotfix/something', FALSE], + ['featurepost/something', FALSE], + ['bugfixpost/something', FALSE], + ['releasepost/something', FALSE], + ['hotfixpost/something', FALSE], + ['prefeaturepost/something', FALSE], + ['prebugfixpost/something', FALSE], + ['prereleasepost/something', FALSE], + ['prehotfixpost/something', FALSE], + ['preproject/something', FALSE], + ['projectpost/something', FALSE], + ]; + } + + /** + * Tests for deploy tag regex. + * + * @see https://semver.org/ + */ + #[DataProvider('dataProviderDeployTagRegex')] + public function testDeployTagRegex(string $branch, bool $expected = TRUE): void { + $this->assertEquals($expected, preg_match($this->config['workflows']['commit']['jobs'][3]['deploy-tags']['filters']['tags']['only'], $branch)); + } + + /** + * Data provider for testDeployTagRegex(). + */ + public static function dataProviderDeployTagRegex(): array { + return [ + // Positive tags. + ['1.2.3'], + ['1.2.3-rc.123'], + ['2023-04-17'], + ['2023-04-17.123'], + + // Negative tags. + ['123', FALSE], + ['123.456', FALSE], + ['1.2.3-rc123', FALSE], + ['1.2.3-rc.123post', FALSE], + ['1.2.3-prerc.123', FALSE], + ['1.2.3-rcpost.123', FALSE], + ['1.2.3-prercpost.123', FALSE], + + ['202-04-17', FALSE], + ['2023-0-17', FALSE], + ['2023-04-1', FALSE], + ['pre2023-04-17', FALSE], + ['2023-04-17post', FALSE], + ['pre2023-04-17post', FALSE], + ['2023-04-17.123.', FALSE], + ['2023-04-17.pre123', FALSE], + ['2023-04-17.pre123post', FALSE], + ['2023-04-17.123post', FALSE], + ]; + } + +} diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/tests/phpunit/Drupal/EnvironmentSettingsTest.php b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/tests/phpunit/Drupal/EnvironmentSettingsTest.php new file mode 100644 index 000000000..155b2a0b2 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/tests/phpunit/Drupal/EnvironmentSettingsTest.php @@ -0,0 +1,12 @@ +@@ -263,9 +263,9 @@ + } + + /** +- * Test per-environment settings for GitHub Actions. ++ * Test per-environment settings for CircleCI. + */ +- public function testEnvironmentGha(): void { ++ public function testEnvironmentCircleCi(): void { + $this->setEnvVars([ + 'CI' => TRUE, + ]); diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/web/sites/default/includes/providers/-settings.gha.php b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/web/sites/default/includes/providers/-settings.gha.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/web/sites/default/includes/providers/settings.circleci.php b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/web/sites/default/includes/providers/settings.circleci.php new file mode 100644 index 000000000..7cad35cf8 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_behat_circleci/web/sites/default/includes/providers/settings.circleci.php @@ -0,0 +1,17 @@ +"${t}" && set -a && . ./.env && set +a && . "${t}" && export -p >> "$BASH_ENV" + +################################################################################ +# PARAMETERS +################################################################################ + +parameters: + run_update_dependencies: + type: boolean + default: false + +################################################################################ +# JOBS +################################################################################ + +jobs: + # Database handling is a first step of the build. + # - $VORTEX_CI_DB_CACHE_TIMESTAMP is used to determine if a fresh DB dump + # should be downloaded for the current build. Usually, a daily database dump + # is sufficient for development activities. + # - $VORTEX_CI_DB_CACHE_FALLBACK is used if the cache did not match $VORTEX_CI_DB_CACHE_TIMESTAMP. + # This allows to rely on the cache from the previous days within the same branch. + database: &job-database + <<: *runner_config + steps: + - attach_workspace: + at: /tmp/workspace + + - add_ssh_keys: + fingerprints: + - *db_ssh_fingerprint + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + - *step_setup_remote_docker + + - run: + name: Create cache keys for database caching as files + command: | + echo "${VORTEX_CI_DB_CACHE_BRANCH}" | tee /tmp/db_cache_branch + echo "${VORTEX_CI_DB_CACHE_FALLBACK/no/${CIRCLE_BUILD_NUM}}" | tee /tmp/db_cache_fallback + date "${VORTEX_CI_DB_CACHE_TIMESTAMP}" | tee /tmp/db_cache_timestamp + echo "yes" | tee /tmp/db_cache_fallback_yes + + - restore_cache: + keys: + # Restore DB cache based on the cache strategy set by the cache keys below. + # https://circleci.com/docs/2.0/caching/#restoring-cache + # Change 'v1' to 'v2', 'v3' etc., commit and push to force 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). + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback" }}-{{ checksum "/tmp/db_cache_timestamp" }} + # Fallback to caching by default branch name only. Allows to use + # cache from the branch build on the previous day. + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback" }}- + + - run: + name: Download DB + command: VORTEX_DB_DOWNLOAD_SEMAPHORE=/tmp/download-db-success ./scripts/vortex/download-db.sh + no_output_timeout: 30m + + # Execute commands after database download script finished: if the + # DB dump was downloaded - build the site (to ensure that the DB dump + # is valid) and export the DB using selected method (to support + # "file-to-image" or "image-to-file" conversions). + # Note that configuration changes and the DB updates are not applied, so + # the database will be cached in the same state as downloaded. + - run: + name: Export DB after download + command: | + [ ! -f /tmp/download-db-success ] && echo "==> Database download semaphore file is missing. DB export will not proceed." && exit 0 + ./scripts/vortex/login-container-registry.sh + docker compose up --detach && sleep 15 + docker compose exec cli mkdir -p .data && docker compose cp -L .data/db.sql cli:/app/.data/db.sql || true + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c "VORTEX_PROVISION_POST_OPERATIONS_SKIP=1 ./scripts/vortex/provision.sh" + grep -q ^VORTEX_DB_IMAGE .env && rm .data/db.sql || true + ./scripts/vortex/export-db.sh db.sql + no_output_timeout: 30m + + - save_cache: + # 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. + key: __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback_yes" }}-{{ checksum "/tmp/db_cache_timestamp" }} + paths: + - /root/project/.data + + # Nightly database job. Same as above, but with additional variables set. + database-nightly: + <<: *job-database + environment: + VORTEX_DB_DOWNLOAD_SSH_FINGERPRINT: *db_ssh_fingerprint + VORTEX_DEPLOY_SSH_FINGERPRINT: *deploy_ssh_fingerprint + # Enforce fresh DB build (do not rely on fallback caches). + VORTEX_CI_DB_CACHE_FALLBACK: 'no' + # Always use fresh base image for the database (if database-in-image storage is used). + VORTEX_DB_IMAGE_BASE: drevops/mariadb-drupal-data:__VERSION__ + # Deploy container image (if database-in-image storage is used). + VORTEX_EXPORT_DB_CONTAINER_REGISTRY_DEPLOY_PROCEED: 1 + # Do not build the Drupal front-end. + VORTEX_FRONTEND_BUILD_SKIP: 1 + + # Build and test is a second step of the build. The testing is performed + # within the same job to save time on provisioning during the job. + build: &job_build + <<: *runner_config + parallelism: 2 + steps: + - attach_workspace: + at: /tmp/workspace + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + + - run: + name: Validate Composer configuration + command: composer validate --strict || [ "${VORTEX_CI_COMPOSER_VALIDATE_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Set cache keys for database caching + command: | + echo "${VORTEX_CI_DB_CACHE_BRANCH}" | tee /tmp/db_cache_branch + echo "yes" | tee /tmp/db_cache_fallback_yes + echo "$(date ${VORTEX_CI_DB_CACHE_TIMESTAMP})" | tee /tmp/db_cache_timestamp + + - restore_cache: + keys: + # Use cached artifacts from previous builds of this branch. + # https://circleci.com/docs/2.0/caching/#restoring-cache + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback_yes" }}-{{ checksum "/tmp/db_cache_timestamp" }} + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback_yes" }}- + + - *step_setup_remote_docker + + - run: + name: Login to container registry + command: ./scripts/vortex/login-container-registry.sh + + - run: + name: Lint Dockerfiles with Hadolint + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + for file in $(find .docker -name 'Dockerfile' -o -name '*.dockerfile'); do + echo "Linting ${file}" && cat "${file}" | docker run --rm -i hadolint/hadolint || [ "${VORTEX_CI_HADOLINT_IGNORE_FAILURE:-0}" -eq 1 ] + done + + - run: + name: Lint Docker Compose files with DCLint + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker run --rm -v "${PWD}":/app zavoloklom/dclint:__VERSION__ . || [ "${VORTEX_CI_DCLINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Build stack + command: docker compose up -d + + - run: + name: Export built codebase + command: | + echo "${VORTEX_DEPLOY_TYPES:-}" | grep -vq "artifact" && exit 0 || true + mkdir -p "/tmp/workspace/code" + docker compose cp -L cli:"/app/." "/tmp/workspace/code" + du -sh "/tmp/workspace/code" + + - run: + name: Install development dependencies + command: | + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c " \ + if [ -n \"${PACKAGE_TOKEN:-}\" ]; then export COMPOSER_AUTH='{\"github-oauth\": {\"github.com\": \"${PACKAGE_TOKEN-}\"}}'; fi && \ + COMPOSER_MEMORY_LIMIT=-1 composer --ansi install --prefer-dist" + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c "yarn install --frozen-lockfile" + + - run: + name: Validate Composer configuration is normalized + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli composer normalize --dry-run || [ "${VORTEX_CI_COMPOSER_NORMALIZE_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with PHPStan + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/phpstan || [ "${VORTEX_CI_PHPSTAN_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with Rector + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/rector --clear-cache --dry-run || [ "${VORTEX_CI_RECTOR_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with PHPMD + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/phpmd . text phpmd.xml || [ "${VORTEX_CI_PHPMD_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with Twig CS Fixer + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/twig-cs-fixer || [ "${VORTEX_CI_TWIG_CS_FIXER_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with Gherkin Lint + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/gherkinlint lint tests/behat/features || [ "${VORTEX_CI_GHERKIN_LINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint module code with NodeJS linters + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli bash -c "yarn run lint" || [ "${VORTEX_CI_NODEJS_LINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint theme code with NodeJS linters + command: | + { [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ]; } || [ "${VORTEX_FRONTEND_BUILD_SKIP:-0}" -eq 1 ] && exit 0 + docker compose exec -T cli bash -c "yarn --cwd=\${WEBROOT}/themes/custom/\${DRUPAL_THEME} run lint" || [ "${VORTEX_CI_NODEJS_LINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Provision site + command: | + if [ -f .data/db.sql ]; then + docker compose exec cli mkdir -p .data + docker compose cp -L .data/db.sql cli:/app/.data/db.sql + fi + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli ./scripts/vortex/provision.sh + no_output_timeout: 30m + + - run: + name: Test with PHPUnit + command: docker compose exec -T cli vendor/bin/phpunit || [ "${VORTEX_CI_PHPUNIT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Test with Behat + command: | + if [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p${CIRCLE_NODE_INDEX}}"; fi + echo "Running with ${VORTEX_CI_BEHAT_PROFILE:-default} profile" + docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ + docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ + [ "${VORTEX_CI_BEHAT_IGNORE_FAILURE:-0}" -eq 1 ] + no_output_timeout: 30m + + - run: + name: Process test logs and artifacts + command: | + mkdir -p "${VORTEX_CI_TEST_RESULTS}" "${VORTEX_CI_ARTIFACTS}" + if docker compose ps --services --filter "status=running" | grep -q cli && docker compose exec cli test -d /app/.logs; then + docker compose cp cli:/app/.logs/. "${VORTEX_CI_ARTIFACTS}/" + if docker compose exec -T cli sh -c '[ -d /app/.logs/test_results/ ]'; then + docker compose cp cli:/app/.logs/test_results/. "${VORTEX_CI_TEST_RESULTS}/" + fi + fi + when: always + + - store_test_results: + path: *test_results + + - store_artifacts: + path: *artifacts + + - run: + name: Upload code coverage reports to Codecov + command: | + if [ -n "${CODECOV_TOKEN}" ] && [ -d /tmp/artifacts/coverage ] && ! echo "${CIRCLE_BRANCH}" | grep -q '^deps/'; then + codecov -Z -s /tmp/artifacts/coverage; + fi + + - persist_to_workspace: + root: /tmp/workspace + paths: + - code + + # Deploy primary branches. + deploy: &job_deploy + <<: *runner_config + steps: + - attach_workspace: + at: /tmp/workspace + + - add_ssh_keys: + fingerprints: + - *deploy_ssh_fingerprint + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + + - run: + name: Deploy + command: | + VORTEX_DEPLOY_BRANCH="${CIRCLE_BRANCH}" \ + VORTEX_DEPLOY_PR="$(echo ${CIRCLE_PULL_REQUEST} | cut -d'/' -f 7)" \ + VORTEX_DEPLOY_PR_HEAD=${CIRCLE_SHA1} \ + ./scripts/vortex/deploy.sh + no_output_timeout: 30m + + - store_artifacts: + path: *artifacts + + # Deploy tags. + deploy-tags: &job-deploy-tags + <<: *runner_config + steps: + - attach_workspace: + at: /tmp/workspace + + - add_ssh_keys: + fingerprints: + - *deploy_ssh_fingerprint + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + + - run: + name: Deploy + command: VORTEX_DEPLOY_MODE="tag" ./scripts/vortex/deploy.sh + no_output_timeout: 30m + + - store_artifacts: + path: *artifacts + + # Self-hosted dependency updates. + # Add the following environment variables to the CircleCI project: + # - RENOVATE_TOKEN: GitHub access token. + # - RENOVATE_REPOSITORIES: Repository to run Renovate on as `vendor/repository`. + # - RENOVATE_GIT_AUTHOR: Author for Renovate commits as `Name `. + # Variables provided below can be overridden in the CircleCI project settings. + update-dependencies: + docker: + - image: renovate/renovate:__VERSION__ + environment: + RENOVATE_PLATFORM: 'github' + RENOVATE_AUTODISCOVER: false + RENOVATE_DEPENDENCY_DASHBOARD_TITLE: 'Renovate Dependency Dashboard (self-hosted) by CircleCI' + RENOVATE_DEPENDENCY_DASHBOARD: false + RENOVATE_DRY_RUN: false + LOG_LEVEL: 'debug' + + steps: + - checkout + - run: + name: Check if RENOVATE_TOKEN is set + command: | + if [ -z "${RENOVATE_TOKEN}" ]; then + echo "RENOVATE_TOKEN is not set. Skipping job." + circleci-agent step halt + fi + + if [ -z "${RENOVATE_REPOSITORIES}" ]; then + echo "Renovate repository is not set. Skipping job." + circleci-agent step halt + fi + + if [ -z "${RENOVATE_GIT_AUTHOR}" ]; then + echo "Renovate git author is not set. Skipping job." + circleci-agent step halt + fi + + - run: + name: Validate Renovate configuration + command: renovate-config-validator + + - run: + name: Run Renovate + command: renovate + +################################################################################ +# WORKFLOWS +################################################################################ + +workflows: + version: 2 + # Commit workflow. Runs for every commit push to the remote repository. + commit: + jobs: + - database: + filters: + tags: + only: /.*/ + - build: + requires: + - database + filters: + tags: + only: /.*/ + - deploy: + requires: + - build + filters: + branches: + # Allowed branches: + # - production, main, master, develop, ci, cisomething + # - project/description + # - deps/* + # - feature/description, feature/123-description + # - bugfix/description, bugfix/123-description + # - release/__VERSION__, release/__VERSION__ (per https://semver.org/) + # - release/2023-04-17, release/2023-04-17.123 (date-based) + # - hotfix/__VERSION__, hotfix/__VERSION__ (per https://semver.org/) + # - hotfix/2023-04-17, hotfix/2023-04-17.123 (date-based) + only: /^(production|main|master|develop)$|^project\/[a-zA-z0-9\-\.]+|^(feature|bugfix)\/[a-zA-Z0-9\-\.\,_]+$|^ci.*|^(release|hotfix)\/[0-9]+(\.[0-9]+){2}(-rc\.[0-9]+)?$|^(release|hotfix)\/[0-9]{4}-[0-9]{2}-[0-9]{2}(\.[0-9]+)?$/ + tags: + ignore: /.*/ + - deploy-tags: + requires: + - build + filters: + branches: + ignore: /.*/ + tags: + # Allowed tags: + # - __VERSION__, __VERSION__ (per https://semver.org/) + # - 2023-04-17, 2023-04-17.123 (date-based) + only: /^[0-9]+(\.[0-9]+){2}(-rc\.[0-9]+)?$|^[0-9]{4}-[0-9]{2}-[0-9]{2}(\.[0-9]+)?$/ + + # Nightly database workflow runs overnight to capture fresh database and cache it. + nightly-db: + triggers: + - schedule: + cron: *nightly_db_schedule + filters: + branches: + only: + - develop + jobs: + - database-nightly + + # Self-hosted Renovate workflow. + update-dependencies: + triggers: + - schedule: + cron: "5 11,23 * * *" + filters: + branches: + only: + - develop + jobs: + - update-dependencies + + update-dependencies-manual: + when: << pipeline.parameters.run_update_dependencies >> + jobs: + - update-dependencies diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/.dockerignore b/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/.dockerignore new file mode 100644 index 000000000..5891722d4 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/.dockerignore @@ -0,0 +1,8 @@ +@@ -42,7 +42,6 @@ + !package.json + !package-lock.json + !patches +-!phpcs.xml + !phpmd.xml + !phpstan.neon + !phpunit.xml diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/.github/workflows/-build-test-deploy.yml b/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/.github/workflows/-build-test-deploy.yml new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/README.md b/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/README.md new file mode 100644 index 000000000..78e06291b --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/README.md @@ -0,0 +1,9 @@ +@@ -4,7 +4,7 @@ + + Drupal 11 implementation of star wars for star wars Org + +-[![Database, Build, Test and Deploy](https://github.com/star_wars_org/star_wars/actions/workflows/build-test-deploy.yml/badge.svg)](https://github.com/star_wars_org/star_wars/actions/workflows/build-test-deploy.yml) ++[![CircleCI](https://circleci.com/gh/star_wars_org/star_wars.svg?style=shield)](https://circleci.com/gh/star_wars_org/star_wars) + + ![Drupal 11](https://img.shields.io/badge/Drupal-11-blue.svg) + [![codecov](https://codecov.io/gh/star_wars_org/star_wars/graph/badge.svg)](https://codecov.io/gh/star_wars_org/star_wars) diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/composer.json b/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/composer.json new file mode 100644 index 000000000..8ad592cec --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/composer.json @@ -0,0 +1,8 @@ +@@ -31,7 +31,6 @@ + "require-dev": { + "behat/behat": "__VERSION__", + "dantleech/gherkin-lint": "__VERSION__", +- "dealerdirect/phpcodesniffer-composer-installer": "__VERSION__", + "drevops/behat-format-progress-fail": "__VERSION__", + "drevops/behat-screenshot": "__VERSION__", + "drevops/behat-steps": "__VERSION__", diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/docs/ci.md b/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/docs/ci.md new file mode 100644 index 000000000..7d29e25c6 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/docs/ci.md @@ -0,0 +1,31 @@ +@@ -5,12 +5,12 @@ + Before feature changes can be merged into a shared mainline, a complete build + must run and pass all tests on CI server. + +-## GitHub Actions ++## Circle CI + +-This project uses [GitHub Actions](https://github.com/features/actions) as a +-CI server: it imports production backups into fully built codebase and runs +-code linting and tests. When tests pass, a deployment process is triggered for +-nominated branches (usually, `main` and `develop`). ++This project uses [Circle CI](https://circleci.com/) as a CI server: it imports ++production backups into fully built codebase and runs code linting and tests. ++When tests pass, a deployment process is triggered for nominated branches ++(usually, `main` and `develop`). + + Refer to https://www.vortextemplate.com/docs/continuous-integration for more information. + +@@ -21,9 +21,6 @@ + + ### SSH + +-GitHub Actions does not supports shell access to the build, but there is an +-action provided withing the `build` job that allows you to run a build with SSH +-support. +- +-Use "Run workflow" button in GitHub Actions UI to start build with SSH support +-that will be available for 120 minutes after the build is finished. ++Circle CI supports shell access to the build for 120 minutes after the build is ++finished when the build is started with SSH support. Use "Rerun job with SSH" ++button in Circle CI UI to start build with SSH support. diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/scripts/composer/ScriptHandler.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/scripts/composer/ScriptHandler.php new file mode 100644 index 000000000..2fac00f59 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/scripts/composer/ScriptHandler.php @@ -0,0 +1,8 @@ +@@ -5,7 +5,6 @@ + * Contains \DrupalProject\composer\ScriptHandler. + * + * Clone of https://github.com/drupal-composer/drupal-project/blob/10.x/scripts/composer/ScriptHandler.php +- * phpcs:disable + */ + + namespace DrupalProject\composer; diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/tests/phpunit/CircleCiConfigTest.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/tests/phpunit/CircleCiConfigTest.php new file mode 100644 index 000000000..96fa2d640 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/tests/phpunit/CircleCiConfigTest.php @@ -0,0 +1,257 @@ +config = Yaml::decode($file); + } + + /** + * Tests for deploy branch regex. + * + * @see https://semver.org/ + */ + #[DataProvider('dataProviderDeployBranchRegex')] + public function testDeployBranchRegex(string $branch, bool $expected = TRUE): void { + $this->assertEquals($expected, preg_match($this->config['workflows']['commit']['jobs'][2]['deploy']['filters']['branches']['only'], $branch)); + } + + /** + * Data provider for testDeployBranchRegex(). + */ + public static function dataProviderDeployBranchRegex(): array { + return [ + // Positive branches. + ['production'], + ['main'], + ['master'], + ['develop'], + + ['ci'], + ['cisomething'], + + ['release/123.456.789'], + ['release/123.456.789-rc.123'], + ['hotfix/123.456.789'], + ['hotfix/123.456.789-rc.123'], + + ['release/2023-04-17'], + ['release/2023-04-17.1'], + ['hotfix/2023-04-17'], + ['hotfix/2023-04-17.1'], + + ['feature/description'], + ['feature/Description'], + ['feature/Description-With-Hyphens'], + ['feature/Description-With_Underscores'], + ['feature/123-description'], + ['feature/123-Description'], + ['feature/UNDERSCORES_UNDERSCORES'], + ['feature/123-Description-With_UNDERSCORES'], + ['feature/1.x'], + ['feature/0.x'], + ['feature/0.1.x'], + ['feature/0.1.2.x'], + ['feature/1.x-description'], + ['feature/0.x-description'], + ['feature/0.1.x-description'], + ['feature/0.1.2.x-description'], + + ['bugfix/description'], + ['bugfix/Description'], + ['bugfix/Description-With-Hyphens'], + ['bugfix/Description-With_Underscores'], + ['bugfix/123-description'], + ['bugfix/123-Description'], + ['bugfix/UNDERSCORES_UNDERSCORES'], + ['bugfix/123-Description-With_UNDERSCORES'], + ['bugfix/1.x'], + ['bugfix/0.x'], + ['bugfix/0.1.x'], + ['bugfix/0.1.2.x'], + ['bugfix/1.x-description'], + ['bugfix/0.x-description'], + ['bugfix/0.1.x-description'], + ['bugfix/0.1.2.x-description'], + + ['project/description'], + ['project/Description'], + ['project/Description-With-Hyphens'], + ['project/123-description'], + ['project/123-Description'], + ['project/1.x'], + ['project/0.x'], + ['project/0.1.x'], + ['project/0.1.2.x'], + ['project/1.x-description'], + ['project/0.x-description'], + ['project/0.1.x-description'], + ['project/0.1.2.x-description'], + + // Negative branches. + ['something', FALSE], + ['premain', FALSE], + ['premaster', FALSE], + ['predevelop', FALSE], + ['mainpost', FALSE], + ['masterpost', FALSE], + ['developpost', FALSE], + ['premainpost', FALSE], + ['premasterpost', FALSE], + ['predeveloppost', FALSE], + + ['preci', FALSE], + ['precipost', FALSE], + + ['deps/something', FALSE], + ['deps', FALSE], + ['predeps', FALSE], + ['depspost', FALSE], + ['predepspost', FALSE], + + ['feature', FALSE], + ['release', FALSE], + ['hotfix', FALSE], + ['prefeature', FALSE], + ['prerelease', FALSE], + ['prehotfix', FALSE], + ['featurepost', FALSE], + ['releasepost', FALSE], + ['hotfixpost', FALSE], + ['prefeaturepost', FALSE], + ['prereleasepost', FALSE], + ['prehotfixpost', FALSE], + + ['release/123', FALSE], + ['release/123.456', FALSE], + ['hotfix/123', FALSE], + ['hotfix/123.456', FALSE], + + ['release/202-04-17', FALSE], + ['release/2023-4-17', FALSE], + ['release/2023-04-1', FALSE], + ['release/pre2023-04-17', FALSE], + ['release/2023-04-17post', FALSE], + ['release/pre2023-04-17post', FALSE], + + ['hotfix/202-04-17', FALSE], + ['hotfix/2023-4-17', FALSE], + ['hotfix/2023-04-1', FALSE], + ['hotfix/pre2023-04-17', FALSE], + ['hotfix/2023-04-17post', FALSE], + ['hotfix/pre2023-04-17post', FALSE], + + ['release/123.456.789-something', FALSE], + ['release/123.456.789-rc', FALSE], + ['release/123.456.789-rc123', FALSE], + ['release/123.456.789-rc-123', FALSE], + ['release/123.456.789-prerc123', FALSE], + ['release/123.456.789-rcpost123', FALSE], + ['release/123.456.789-prercpost123', FALSE], + ['release/123.456.789-rc123something', FALSE], + ['release/123.456.789-rc.123something', FALSE], + ['release/123.456.789-rc.123-something', FALSE], + + ['hotfix/123.456.789-something', FALSE], + ['hotfix/123.456.789-rc', FALSE], + ['hotfix/123.456.789-rc123', FALSE], + ['hotfix/123.456.789-rc-123', FALSE], + ['hotfix/123.456.789-prerc123', FALSE], + ['hotfix/123.456.789-rcpost123', FALSE], + ['hotfix/123.456.789-prercpost123', FALSE], + ['hotfix/123.456.789-rc123something', FALSE], + ['hotfix/123.456.789-rc.123something', FALSE], + ['hotfix/123.456.789-rc.123-something', FALSE], + + ['prefeature/something', FALSE], + ['prefbugfix/something', FALSE], + ['prerelease/something', FALSE], + ['prehotfix/something', FALSE], + ['featurepost/something', FALSE], + ['bugfixpost/something', FALSE], + ['releasepost/something', FALSE], + ['hotfixpost/something', FALSE], + ['prefeaturepost/something', FALSE], + ['prebugfixpost/something', FALSE], + ['prereleasepost/something', FALSE], + ['prehotfixpost/something', FALSE], + ['preproject/something', FALSE], + ['projectpost/something', FALSE], + ]; + } + + /** + * Tests for deploy tag regex. + * + * @see https://semver.org/ + */ + #[DataProvider('dataProviderDeployTagRegex')] + public function testDeployTagRegex(string $branch, bool $expected = TRUE): void { + $this->assertEquals($expected, preg_match($this->config['workflows']['commit']['jobs'][3]['deploy-tags']['filters']['tags']['only'], $branch)); + } + + /** + * Data provider for testDeployTagRegex(). + */ + public static function dataProviderDeployTagRegex(): array { + return [ + // Positive tags. + ['1.2.3'], + ['1.2.3-rc.123'], + ['2023-04-17'], + ['2023-04-17.123'], + + // Negative tags. + ['123', FALSE], + ['123.456', FALSE], + ['1.2.3-rc123', FALSE], + ['1.2.3-rc.123post', FALSE], + ['1.2.3-prerc.123', FALSE], + ['1.2.3-rcpost.123', FALSE], + ['1.2.3-prercpost.123', FALSE], + + ['202-04-17', FALSE], + ['2023-0-17', FALSE], + ['2023-04-1', FALSE], + ['pre2023-04-17', FALSE], + ['2023-04-17post', FALSE], + ['pre2023-04-17post', FALSE], + ['2023-04-17.123.', FALSE], + ['2023-04-17.pre123', FALSE], + ['2023-04-17.pre123post', FALSE], + ['2023-04-17.123post', FALSE], + ]; + } + +} diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/tests/phpunit/Drupal/EnvironmentSettingsTest.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/tests/phpunit/Drupal/EnvironmentSettingsTest.php new file mode 100644 index 000000000..bcf03b497 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/tests/phpunit/Drupal/EnvironmentSettingsTest.php @@ -0,0 +1,23 @@ +@@ -15,10 +15,6 @@ + * The main purpose of these tests is to ensure that the settings and configs + * appear in every environment as expected. + * +- * phpcs:disable Squiz.WhiteSpace.FunctionSpacing.Before +- * phpcs:disable Squiz.WhiteSpace.FunctionSpacing.After +- * phpcs:disable Squiz.WhiteSpace.FunctionSpacing.AfterLast +- * phpcs:disable Drupal.Classes.ClassDeclaration.CloseBraceAfterBody + */ + #[Group('drupal_settings')] + class EnvironmentSettingsTest extends SettingsTestCase { +@@ -263,9 +259,9 @@ + } + + /** +- * Test per-environment settings for GitHub Actions. ++ * Test per-environment settings for CircleCI. + */ +- public function testEnvironmentGha(): void { ++ public function testEnvironmentCircleCi(): void { + $this->setEnvVars([ + 'CI' => TRUE, + ]); diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/tests/phpunit/Drupal/SettingsTestCase.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/tests/phpunit/Drupal/SettingsTestCase.php new file mode 100644 index 000000000..0f4cf74d7 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/tests/phpunit/Drupal/SettingsTestCase.php @@ -0,0 +1,8 @@ +@@ -11,7 +11,6 @@ + * + * Base class for testing Drupal settings. + * +- * phpcs:disable Drupal.NamingConventions.ValidVariableName.LowerCamelName + */ + abstract class SettingsTestCase extends TestCase { + diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/web/modules/custom/sw_base/tests/src/Functional/ExampleTest.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/web/modules/custom/sw_base/tests/src/Functional/ExampleTest.php new file mode 100644 index 000000000..e59020263 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/web/modules/custom/sw_base/tests/src/Functional/ExampleTest.php @@ -0,0 +1,8 @@ +@@ -19,7 +19,6 @@ + /** + * {@inheritdoc} + * +- * @phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod.Found + */ + protected function setUp(): void { + parent::setUp(); diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/web/sites/default/default.settings.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/web/sites/default/default.settings.php new file mode 100644 index 000000000..33d928f7b --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpcs_circleci/web/sites/default/default.settings.php @@ -0,0 +1,7 @@ +@@ -1,6 +1,5 @@ + $value) { diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpmd/web/modules/custom/sw_base/tests/src/Traits/MockTrait.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd/web/modules/custom/sw_base/tests/src/Traits/MockTrait.php new file mode 100644 index 000000000..b1b8e022a --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd/web/modules/custom/sw_base/tests/src/Traits/MockTrait.php @@ -0,0 +1,9 @@ +@@ -30,8 +30,6 @@ + * @return \PHPUnit\Framework\MockObject\MockObject + * An instance of the mock. + * +- * @SuppressWarnings("PHPMD.CyclomaticComplexity") +- * @SuppressWarnings("PHPMD.ElseExpression") + */ + protected function prepareMock(string $class, array $methods_map = [], array|bool $args = []): MockObject { + $methods = array_values(array_filter(array_keys($methods_map))); diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/-phpmd.xml b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/-phpmd.xml new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/.ahoy.yml b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/.ahoy.yml new file mode 100644 index 000000000..057d397d7 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/.ahoy.yml @@ -0,0 +1,8 @@ +@@ -188,7 +188,6 @@ + ahoy cli vendor/bin/phpcs + ahoy cli vendor/bin/phpstan + ahoy cli vendor/bin/rector --clear-cache --dry-run +- ahoy cli vendor/bin/phpmd . text phpmd.xml + + lint-fe: + usage: Lint front-end code. diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/.circleci/README.md b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/.circleci/README.md new file mode 100644 index 000000000..533a875e9 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/.circleci/README.md @@ -0,0 +1,2 @@ +You may add custom scripts, which would run only in CI, to this directory and +reference them from `config.yml` file. diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/.circleci/config.yml b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/.circleci/config.yml new file mode 100644 index 000000000..dc5661425 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/.circleci/config.yml @@ -0,0 +1,541 @@ +# CircleCI 2.0 configuration file. +# +# This configuration file uses the "docker" executor to run the Docker stack. +# +# A "runner" container, created from a specified container image, is used to +# checkout source code and run commands defined in this file. Application Docker +# containers defined in `docker-compose.yml` run on a *remote* Docker server +# controlled by CircleCI. +# The "runner" container uses Docker client to control the remote Docker server. +version: '2.1' + +aliases: + # SSH key fingerprint to download the database. + # Replace this key fingerprint with your own and remove this comment. + - &db_ssh_fingerprint "SHA256:6d+U5QubT0eAWz+4N2wt+WM2qx6o4cvyvQ6xILETJ84" + + # SSH key fingerprint to deploy code. + # Replace this key fingerprint with your own and remove this comment. + - &deploy_ssh_fingerprint "SHA256:6d+U5QubT0eAWz+4N2wt+WM2qx6o4cvyvQ6xILETJ84" + + # Schedule to run nightly database build (to cache the database for the next day). + - &nightly_db_schedule "0 18 * * *" + + # Shared runner container configuration applied to each job. + - &runner_config + working_directory: &working_directory ~/project + environment: + VORTEX_DB_DOWNLOAD_SSH_FINGERPRINT: *db_ssh_fingerprint + VORTEX_DEPLOY_SSH_FINGERPRINT: *deploy_ssh_fingerprint + docker: + # Using the 'runner' container where each job will be executed. + # This container has all the necessary tools to run a dockerized environment. + # https://github.com/drevops/ci-runner + # https://hub.docker.com/repository/docker/drevops/ci-runner/tags + - image: drevops/ci-runner:__VERSION__ + auth: + username: ${VORTEX_CONTAINER_REGISTRY_USER} + password: ${VORTEX_CONTAINER_REGISTRY_PASS} + environment: + # Set runner timezone via UI to ensure that executed operations use correct timestamps. + # https://en.wikipedia.org/wiki/List_of_tz_database_time_zones + TZ: UTC + # Set runner terminal capabilities. + TERM: xterm-256color + # Disable strict host key checking for SSH connections. + VORTEX_SSH_DISABLE_STRICT_HOST_KEY_CHECKING: "1" + # Remove all SSH keys from the runner container. + VORTEX_SSH_REMOVE_ALL_KEYS: "1" + # 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" + # Directory to store test results. + VORTEX_CI_TEST_RESULTS: &test_results /tmp/tests + # Directory to store test artifacts. + VORTEX_CI_ARTIFACTS: &artifacts /tmp/artifacts + # Directory to use for artifact deployments. + VORTEX_DEPLOY_ARTIFACT_SRC: /tmp/workspace/code + # Source code location for artifact deployments. + VORTEX_DEPLOY_ARTIFACT_ROOT: *working_directory + # Report file location for artifact deployments. + VORTEX_DEPLOY_ARTIFACT_LOG: /tmp/artifacts/deployment_log.txt + # Check only minimal stack requirements. + VORTEX_DOCTOR_CHECK_MINIMAL: 1 + # CI runner resource class. + # https://circleci.com/docs/2.0/configuration-reference/#resource_class + # Change to 'large' for faster builds. + resource_class: medium + + - &step_setup_remote_docker + setup_remote_docker: + # Docker Layer Caching allows to significantly speed up builds by caching + # images built during previous runs. + # https://circleci.com/docs/2.0/docker-layer-caching/ + docker_layer_caching: false + version: default + + - &step_process_codebase_for_ci + run: + name: Process codebase to run in CI + command: | + find . -name "docker-compose.yml" -print0 | xargs -0 -I {} sh -c "sed -i -e ''/###/d'' {} && sed -i -e ''s/##//'' {}" + mkdir -p /tmp/workspace/code + + - &load_variables_from_dotenv + run: + name: Load environment variables from .env file + # Load variables from .env file, respecting existing values, and make them available for the next steps. + command: t=$(mktemp) && export -p >"${t}" && set -a && . ./.env && set +a && . "${t}" && export -p >> "$BASH_ENV" + +################################################################################ +# PARAMETERS +################################################################################ + +parameters: + run_update_dependencies: + type: boolean + default: false + +################################################################################ +# JOBS +################################################################################ + +jobs: + # Database handling is a first step of the build. + # - $VORTEX_CI_DB_CACHE_TIMESTAMP is used to determine if a fresh DB dump + # should be downloaded for the current build. Usually, a daily database dump + # is sufficient for development activities. + # - $VORTEX_CI_DB_CACHE_FALLBACK is used if the cache did not match $VORTEX_CI_DB_CACHE_TIMESTAMP. + # This allows to rely on the cache from the previous days within the same branch. + database: &job-database + <<: *runner_config + steps: + - attach_workspace: + at: /tmp/workspace + + - add_ssh_keys: + fingerprints: + - *db_ssh_fingerprint + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + - *step_setup_remote_docker + + - run: + name: Create cache keys for database caching as files + command: | + echo "${VORTEX_CI_DB_CACHE_BRANCH}" | tee /tmp/db_cache_branch + echo "${VORTEX_CI_DB_CACHE_FALLBACK/no/${CIRCLE_BUILD_NUM}}" | tee /tmp/db_cache_fallback + date "${VORTEX_CI_DB_CACHE_TIMESTAMP}" | tee /tmp/db_cache_timestamp + echo "yes" | tee /tmp/db_cache_fallback_yes + + - restore_cache: + keys: + # Restore DB cache based on the cache strategy set by the cache keys below. + # https://circleci.com/docs/2.0/caching/#restoring-cache + # Change 'v1' to 'v2', 'v3' etc., commit and push to force 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). + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback" }}-{{ checksum "/tmp/db_cache_timestamp" }} + # Fallback to caching by default branch name only. Allows to use + # cache from the branch build on the previous day. + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback" }}- + + - run: + name: Download DB + command: VORTEX_DB_DOWNLOAD_SEMAPHORE=/tmp/download-db-success ./scripts/vortex/download-db.sh + no_output_timeout: 30m + + # Execute commands after database download script finished: if the + # DB dump was downloaded - build the site (to ensure that the DB dump + # is valid) and export the DB using selected method (to support + # "file-to-image" or "image-to-file" conversions). + # Note that configuration changes and the DB updates are not applied, so + # the database will be cached in the same state as downloaded. + - run: + name: Export DB after download + command: | + [ ! -f /tmp/download-db-success ] && echo "==> Database download semaphore file is missing. DB export will not proceed." && exit 0 + ./scripts/vortex/login-container-registry.sh + docker compose up --detach && sleep 15 + docker compose exec cli mkdir -p .data && docker compose cp -L .data/db.sql cli:/app/.data/db.sql || true + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c "VORTEX_PROVISION_POST_OPERATIONS_SKIP=1 ./scripts/vortex/provision.sh" + grep -q ^VORTEX_DB_IMAGE .env && rm .data/db.sql || true + ./scripts/vortex/export-db.sh db.sql + no_output_timeout: 30m + + - save_cache: + # 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. + key: __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback_yes" }}-{{ checksum "/tmp/db_cache_timestamp" }} + paths: + - /root/project/.data + + # Nightly database job. Same as above, but with additional variables set. + database-nightly: + <<: *job-database + environment: + VORTEX_DB_DOWNLOAD_SSH_FINGERPRINT: *db_ssh_fingerprint + VORTEX_DEPLOY_SSH_FINGERPRINT: *deploy_ssh_fingerprint + # Enforce fresh DB build (do not rely on fallback caches). + VORTEX_CI_DB_CACHE_FALLBACK: 'no' + # Always use fresh base image for the database (if database-in-image storage is used). + VORTEX_DB_IMAGE_BASE: drevops/mariadb-drupal-data:__VERSION__ + # Deploy container image (if database-in-image storage is used). + VORTEX_EXPORT_DB_CONTAINER_REGISTRY_DEPLOY_PROCEED: 1 + # Do not build the Drupal front-end. + VORTEX_FRONTEND_BUILD_SKIP: 1 + + # Build and test is a second step of the build. The testing is performed + # within the same job to save time on provisioning during the job. + build: &job_build + <<: *runner_config + parallelism: 2 + steps: + - attach_workspace: + at: /tmp/workspace + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + + - run: + name: Validate Composer configuration + command: composer validate --strict || [ "${VORTEX_CI_COMPOSER_VALIDATE_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Set cache keys for database caching + command: | + echo "${VORTEX_CI_DB_CACHE_BRANCH}" | tee /tmp/db_cache_branch + echo "yes" | tee /tmp/db_cache_fallback_yes + echo "$(date ${VORTEX_CI_DB_CACHE_TIMESTAMP})" | tee /tmp/db_cache_timestamp + + - restore_cache: + keys: + # Use cached artifacts from previous builds of this branch. + # https://circleci.com/docs/2.0/caching/#restoring-cache + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback_yes" }}-{{ checksum "/tmp/db_cache_timestamp" }} + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback_yes" }}- + + - *step_setup_remote_docker + + - run: + name: Login to container registry + command: ./scripts/vortex/login-container-registry.sh + + - run: + name: Lint Dockerfiles with Hadolint + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + for file in $(find .docker -name 'Dockerfile' -o -name '*.dockerfile'); do + echo "Linting ${file}" && cat "${file}" | docker run --rm -i hadolint/hadolint || [ "${VORTEX_CI_HADOLINT_IGNORE_FAILURE:-0}" -eq 1 ] + done + + - run: + name: Lint Docker Compose files with DCLint + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker run --rm -v "${PWD}":/app zavoloklom/dclint:__VERSION__ . || [ "${VORTEX_CI_DCLINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Build stack + command: docker compose up -d + + - run: + name: Export built codebase + command: | + echo "${VORTEX_DEPLOY_TYPES:-}" | grep -vq "artifact" && exit 0 || true + mkdir -p "/tmp/workspace/code" + docker compose cp -L cli:"/app/." "/tmp/workspace/code" + du -sh "/tmp/workspace/code" + + - run: + name: Install development dependencies + command: | + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c " \ + if [ -n \"${PACKAGE_TOKEN:-}\" ]; then export COMPOSER_AUTH='{\"github-oauth\": {\"github.com\": \"${PACKAGE_TOKEN-}\"}}'; fi && \ + COMPOSER_MEMORY_LIMIT=-1 composer --ansi install --prefer-dist" + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c "yarn install --frozen-lockfile" + + - run: + name: Validate Composer configuration is normalized + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli composer normalize --dry-run || [ "${VORTEX_CI_COMPOSER_NORMALIZE_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with PHPCS + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/phpcs || [ "${VORTEX_CI_PHPCS_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with PHPStan + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/phpstan || [ "${VORTEX_CI_PHPSTAN_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with Rector + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/rector --clear-cache --dry-run || [ "${VORTEX_CI_RECTOR_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with Twig CS Fixer + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/twig-cs-fixer || [ "${VORTEX_CI_TWIG_CS_FIXER_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with Gherkin Lint + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/gherkinlint lint tests/behat/features || [ "${VORTEX_CI_GHERKIN_LINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint module code with NodeJS linters + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli bash -c "yarn run lint" || [ "${VORTEX_CI_NODEJS_LINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint theme code with NodeJS linters + command: | + { [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ]; } || [ "${VORTEX_FRONTEND_BUILD_SKIP:-0}" -eq 1 ] && exit 0 + docker compose exec -T cli bash -c "yarn --cwd=\${WEBROOT}/themes/custom/\${DRUPAL_THEME} run lint" || [ "${VORTEX_CI_NODEJS_LINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Provision site + command: | + if [ -f .data/db.sql ]; then + docker compose exec cli mkdir -p .data + docker compose cp -L .data/db.sql cli:/app/.data/db.sql + fi + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli ./scripts/vortex/provision.sh + no_output_timeout: 30m + + - run: + name: Test with PHPUnit + command: docker compose exec -T cli vendor/bin/phpunit || [ "${VORTEX_CI_PHPUNIT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Test with Behat + command: | + if [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p${CIRCLE_NODE_INDEX}}"; fi + echo "Running with ${VORTEX_CI_BEHAT_PROFILE:-default} profile" + docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ + docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ + [ "${VORTEX_CI_BEHAT_IGNORE_FAILURE:-0}" -eq 1 ] + no_output_timeout: 30m + + - run: + name: Process test logs and artifacts + command: | + mkdir -p "${VORTEX_CI_TEST_RESULTS}" "${VORTEX_CI_ARTIFACTS}" + if docker compose ps --services --filter "status=running" | grep -q cli && docker compose exec cli test -d /app/.logs; then + docker compose cp cli:/app/.logs/. "${VORTEX_CI_ARTIFACTS}/" + if docker compose exec -T cli sh -c '[ -d /app/.logs/test_results/ ]'; then + docker compose cp cli:/app/.logs/test_results/. "${VORTEX_CI_TEST_RESULTS}/" + fi + fi + when: always + + - store_test_results: + path: *test_results + + - store_artifacts: + path: *artifacts + + - run: + name: Upload code coverage reports to Codecov + command: | + if [ -n "${CODECOV_TOKEN}" ] && [ -d /tmp/artifacts/coverage ] && ! echo "${CIRCLE_BRANCH}" | grep -q '^deps/'; then + codecov -Z -s /tmp/artifacts/coverage; + fi + + - persist_to_workspace: + root: /tmp/workspace + paths: + - code + + # Deploy primary branches. + deploy: &job_deploy + <<: *runner_config + steps: + - attach_workspace: + at: /tmp/workspace + + - add_ssh_keys: + fingerprints: + - *deploy_ssh_fingerprint + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + + - run: + name: Deploy + command: | + VORTEX_DEPLOY_BRANCH="${CIRCLE_BRANCH}" \ + VORTEX_DEPLOY_PR="$(echo ${CIRCLE_PULL_REQUEST} | cut -d'/' -f 7)" \ + VORTEX_DEPLOY_PR_HEAD=${CIRCLE_SHA1} \ + ./scripts/vortex/deploy.sh + no_output_timeout: 30m + + - store_artifacts: + path: *artifacts + + # Deploy tags. + deploy-tags: &job-deploy-tags + <<: *runner_config + steps: + - attach_workspace: + at: /tmp/workspace + + - add_ssh_keys: + fingerprints: + - *deploy_ssh_fingerprint + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + + - run: + name: Deploy + command: VORTEX_DEPLOY_MODE="tag" ./scripts/vortex/deploy.sh + no_output_timeout: 30m + + - store_artifacts: + path: *artifacts + + # Self-hosted dependency updates. + # Add the following environment variables to the CircleCI project: + # - RENOVATE_TOKEN: GitHub access token. + # - RENOVATE_REPOSITORIES: Repository to run Renovate on as `vendor/repository`. + # - RENOVATE_GIT_AUTHOR: Author for Renovate commits as `Name `. + # Variables provided below can be overridden in the CircleCI project settings. + update-dependencies: + docker: + - image: renovate/renovate:__VERSION__ + environment: + RENOVATE_PLATFORM: 'github' + RENOVATE_AUTODISCOVER: false + RENOVATE_DEPENDENCY_DASHBOARD_TITLE: 'Renovate Dependency Dashboard (self-hosted) by CircleCI' + RENOVATE_DEPENDENCY_DASHBOARD: false + RENOVATE_DRY_RUN: false + LOG_LEVEL: 'debug' + + steps: + - checkout + - run: + name: Check if RENOVATE_TOKEN is set + command: | + if [ -z "${RENOVATE_TOKEN}" ]; then + echo "RENOVATE_TOKEN is not set. Skipping job." + circleci-agent step halt + fi + + if [ -z "${RENOVATE_REPOSITORIES}" ]; then + echo "Renovate repository is not set. Skipping job." + circleci-agent step halt + fi + + if [ -z "${RENOVATE_GIT_AUTHOR}" ]; then + echo "Renovate git author is not set. Skipping job." + circleci-agent step halt + fi + + - run: + name: Validate Renovate configuration + command: renovate-config-validator + + - run: + name: Run Renovate + command: renovate + +################################################################################ +# WORKFLOWS +################################################################################ + +workflows: + version: 2 + # Commit workflow. Runs for every commit push to the remote repository. + commit: + jobs: + - database: + filters: + tags: + only: /.*/ + - build: + requires: + - database + filters: + tags: + only: /.*/ + - deploy: + requires: + - build + filters: + branches: + # Allowed branches: + # - production, main, master, develop, ci, cisomething + # - project/description + # - deps/* + # - feature/description, feature/123-description + # - bugfix/description, bugfix/123-description + # - release/__VERSION__, release/__VERSION__ (per https://semver.org/) + # - release/2023-04-17, release/2023-04-17.123 (date-based) + # - hotfix/__VERSION__, hotfix/__VERSION__ (per https://semver.org/) + # - hotfix/2023-04-17, hotfix/2023-04-17.123 (date-based) + only: /^(production|main|master|develop)$|^project\/[a-zA-z0-9\-\.]+|^(feature|bugfix)\/[a-zA-Z0-9\-\.\,_]+$|^ci.*|^(release|hotfix)\/[0-9]+(\.[0-9]+){2}(-rc\.[0-9]+)?$|^(release|hotfix)\/[0-9]{4}-[0-9]{2}-[0-9]{2}(\.[0-9]+)?$/ + tags: + ignore: /.*/ + - deploy-tags: + requires: + - build + filters: + branches: + ignore: /.*/ + tags: + # Allowed tags: + # - __VERSION__, __VERSION__ (per https://semver.org/) + # - 2023-04-17, 2023-04-17.123 (date-based) + only: /^[0-9]+(\.[0-9]+){2}(-rc\.[0-9]+)?$|^[0-9]{4}-[0-9]{2}-[0-9]{2}(\.[0-9]+)?$/ + + # Nightly database workflow runs overnight to capture fresh database and cache it. + nightly-db: + triggers: + - schedule: + cron: *nightly_db_schedule + filters: + branches: + only: + - develop + jobs: + - database-nightly + + # Self-hosted Renovate workflow. + update-dependencies: + triggers: + - schedule: + cron: "5 11,23 * * *" + filters: + branches: + only: + - develop + jobs: + - update-dependencies + + update-dependencies-manual: + when: << pipeline.parameters.run_update_dependencies >> + jobs: + - update-dependencies diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/.dockerignore b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/.dockerignore new file mode 100644 index 000000000..e17ab76ad --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/.dockerignore @@ -0,0 +1,8 @@ +@@ -43,7 +43,6 @@ + !package-lock.json + !patches + !phpcs.xml +-!phpmd.xml + !phpstan.neon + !phpunit.xml + !rector.php diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/.github/workflows/-build-test-deploy.yml b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/.github/workflows/-build-test-deploy.yml new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/README.md b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/README.md new file mode 100644 index 000000000..78e06291b --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/README.md @@ -0,0 +1,9 @@ +@@ -4,7 +4,7 @@ + + Drupal 11 implementation of star wars for star wars Org + +-[![Database, Build, Test and Deploy](https://github.com/star_wars_org/star_wars/actions/workflows/build-test-deploy.yml/badge.svg)](https://github.com/star_wars_org/star_wars/actions/workflows/build-test-deploy.yml) ++[![CircleCI](https://circleci.com/gh/star_wars_org/star_wars.svg?style=shield)](https://circleci.com/gh/star_wars_org/star_wars) + + ![Drupal 11](https://img.shields.io/badge/Drupal-11-blue.svg) + [![codecov](https://codecov.io/gh/star_wars_org/star_wars/graph/badge.svg)](https://codecov.io/gh/star_wars_org/star_wars) diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/composer.json b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/composer.json new file mode 100644 index 000000000..d8bc9e817 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/composer.json @@ -0,0 +1,8 @@ +@@ -41,7 +41,6 @@ + "mglaman/phpstan-drupal": "__VERSION__", + "palantirnet/drupal-rector": "__VERSION__", + "phpcompatibility/php-compatibility": "__VERSION__", +- "phpmd/phpmd": "__VERSION__", + "phpspec/prophecy-phpunit": "__VERSION__", + "phpstan/extension-installer": "__VERSION__", + "phpstan/phpstan": "__VERSION__", diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/docs/ci.md b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/docs/ci.md new file mode 100644 index 000000000..7d29e25c6 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/docs/ci.md @@ -0,0 +1,31 @@ +@@ -5,12 +5,12 @@ + Before feature changes can be merged into a shared mainline, a complete build + must run and pass all tests on CI server. + +-## GitHub Actions ++## Circle CI + +-This project uses [GitHub Actions](https://github.com/features/actions) as a +-CI server: it imports production backups into fully built codebase and runs +-code linting and tests. When tests pass, a deployment process is triggered for +-nominated branches (usually, `main` and `develop`). ++This project uses [Circle CI](https://circleci.com/) as a CI server: it imports ++production backups into fully built codebase and runs code linting and tests. ++When tests pass, a deployment process is triggered for nominated branches ++(usually, `main` and `develop`). + + Refer to https://www.vortextemplate.com/docs/continuous-integration for more information. + +@@ -21,9 +21,6 @@ + + ### SSH + +-GitHub Actions does not supports shell access to the build, but there is an +-action provided withing the `build` job that allows you to run a build with SSH +-support. +- +-Use "Run workflow" button in GitHub Actions UI to start build with SSH support +-that will be available for 120 minutes after the build is finished. ++Circle CI supports shell access to the build for 120 minutes after the build is ++finished when the build is started with SSH support. Use "Rerun job with SSH" ++button in Circle CI UI to start build with SSH support. diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/tests/phpunit/CircleCiConfigTest.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/tests/phpunit/CircleCiConfigTest.php new file mode 100644 index 000000000..63149e2fa --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/tests/phpunit/CircleCiConfigTest.php @@ -0,0 +1,260 @@ +config = Yaml::decode($file); + } + + /** + * Tests for deploy branch regex. + * + * @see https://semver.org/ + */ + #[DataProvider('dataProviderDeployBranchRegex')] + public function testDeployBranchRegex(string $branch, bool $expected = TRUE): void { + $this->assertEquals($expected, preg_match($this->config['workflows']['commit']['jobs'][2]['deploy']['filters']['branches']['only'], $branch)); + } + + /** + * Data provider for testDeployBranchRegex(). + */ + public static function dataProviderDeployBranchRegex(): array { + return [ + // Positive branches. + ['production'], + ['main'], + ['master'], + ['develop'], + + ['ci'], + ['cisomething'], + + ['release/123.456.789'], + ['release/123.456.789-rc.123'], + ['hotfix/123.456.789'], + ['hotfix/123.456.789-rc.123'], + + ['release/2023-04-17'], + ['release/2023-04-17.1'], + ['hotfix/2023-04-17'], + ['hotfix/2023-04-17.1'], + + ['feature/description'], + ['feature/Description'], + ['feature/Description-With-Hyphens'], + ['feature/Description-With_Underscores'], + ['feature/123-description'], + ['feature/123-Description'], + ['feature/UNDERSCORES_UNDERSCORES'], + ['feature/123-Description-With_UNDERSCORES'], + ['feature/1.x'], + ['feature/0.x'], + ['feature/0.1.x'], + ['feature/0.1.2.x'], + ['feature/1.x-description'], + ['feature/0.x-description'], + ['feature/0.1.x-description'], + ['feature/0.1.2.x-description'], + + ['bugfix/description'], + ['bugfix/Description'], + ['bugfix/Description-With-Hyphens'], + ['bugfix/Description-With_Underscores'], + ['bugfix/123-description'], + ['bugfix/123-Description'], + ['bugfix/UNDERSCORES_UNDERSCORES'], + ['bugfix/123-Description-With_UNDERSCORES'], + ['bugfix/1.x'], + ['bugfix/0.x'], + ['bugfix/0.1.x'], + ['bugfix/0.1.2.x'], + ['bugfix/1.x-description'], + ['bugfix/0.x-description'], + ['bugfix/0.1.x-description'], + ['bugfix/0.1.2.x-description'], + + ['project/description'], + ['project/Description'], + ['project/Description-With-Hyphens'], + ['project/123-description'], + ['project/123-Description'], + ['project/1.x'], + ['project/0.x'], + ['project/0.1.x'], + ['project/0.1.2.x'], + ['project/1.x-description'], + ['project/0.x-description'], + ['project/0.1.x-description'], + ['project/0.1.2.x-description'], + + // Negative branches. + ['something', FALSE], + ['premain', FALSE], + ['premaster', FALSE], + ['predevelop', FALSE], + ['mainpost', FALSE], + ['masterpost', FALSE], + ['developpost', FALSE], + ['premainpost', FALSE], + ['premasterpost', FALSE], + ['predeveloppost', FALSE], + + ['preci', FALSE], + ['precipost', FALSE], + + ['deps/something', FALSE], + ['deps', FALSE], + ['predeps', FALSE], + ['depspost', FALSE], + ['predepspost', FALSE], + + ['feature', FALSE], + ['release', FALSE], + ['hotfix', FALSE], + ['prefeature', FALSE], + ['prerelease', FALSE], + ['prehotfix', FALSE], + ['featurepost', FALSE], + ['releasepost', FALSE], + ['hotfixpost', FALSE], + ['prefeaturepost', FALSE], + ['prereleasepost', FALSE], + ['prehotfixpost', FALSE], + + ['release/123', FALSE], + ['release/123.456', FALSE], + ['hotfix/123', FALSE], + ['hotfix/123.456', FALSE], + + ['release/202-04-17', FALSE], + ['release/2023-4-17', FALSE], + ['release/2023-04-1', FALSE], + ['release/pre2023-04-17', FALSE], + ['release/2023-04-17post', FALSE], + ['release/pre2023-04-17post', FALSE], + + ['hotfix/202-04-17', FALSE], + ['hotfix/2023-4-17', FALSE], + ['hotfix/2023-04-1', FALSE], + ['hotfix/pre2023-04-17', FALSE], + ['hotfix/2023-04-17post', FALSE], + ['hotfix/pre2023-04-17post', FALSE], + + ['release/123.456.789-something', FALSE], + ['release/123.456.789-rc', FALSE], + ['release/123.456.789-rc123', FALSE], + ['release/123.456.789-rc-123', FALSE], + ['release/123.456.789-prerc123', FALSE], + ['release/123.456.789-rcpost123', FALSE], + ['release/123.456.789-prercpost123', FALSE], + ['release/123.456.789-rc123something', FALSE], + ['release/123.456.789-rc.123something', FALSE], + ['release/123.456.789-rc.123-something', FALSE], + + ['hotfix/123.456.789-something', FALSE], + ['hotfix/123.456.789-rc', FALSE], + ['hotfix/123.456.789-rc123', FALSE], + ['hotfix/123.456.789-rc-123', FALSE], + ['hotfix/123.456.789-prerc123', FALSE], + ['hotfix/123.456.789-rcpost123', FALSE], + ['hotfix/123.456.789-prercpost123', FALSE], + ['hotfix/123.456.789-rc123something', FALSE], + ['hotfix/123.456.789-rc.123something', FALSE], + ['hotfix/123.456.789-rc.123-something', FALSE], + + ['prefeature/something', FALSE], + ['prefbugfix/something', FALSE], + ['prerelease/something', FALSE], + ['prehotfix/something', FALSE], + ['featurepost/something', FALSE], + ['bugfixpost/something', FALSE], + ['releasepost/something', FALSE], + ['hotfixpost/something', FALSE], + ['prefeaturepost/something', FALSE], + ['prebugfixpost/something', FALSE], + ['prereleasepost/something', FALSE], + ['prehotfixpost/something', FALSE], + ['preproject/something', FALSE], + ['projectpost/something', FALSE], + ]; + } + + /** + * Tests for deploy tag regex. + * + * @see https://semver.org/ + */ + #[DataProvider('dataProviderDeployTagRegex')] + public function testDeployTagRegex(string $branch, bool $expected = TRUE): void { + $this->assertEquals($expected, preg_match($this->config['workflows']['commit']['jobs'][3]['deploy-tags']['filters']['tags']['only'], $branch)); + } + + /** + * Data provider for testDeployTagRegex(). + */ + public static function dataProviderDeployTagRegex(): array { + return [ + // Positive tags. + ['1.2.3'], + ['1.2.3-rc.123'], + ['2023-04-17'], + ['2023-04-17.123'], + + // Negative tags. + ['123', FALSE], + ['123.456', FALSE], + ['1.2.3-rc123', FALSE], + ['1.2.3-rc.123post', FALSE], + ['1.2.3-prerc.123', FALSE], + ['1.2.3-rcpost.123', FALSE], + ['1.2.3-prercpost.123', FALSE], + + ['202-04-17', FALSE], + ['2023-0-17', FALSE], + ['2023-04-1', FALSE], + ['pre2023-04-17', FALSE], + ['2023-04-17post', FALSE], + ['pre2023-04-17post', FALSE], + ['2023-04-17.123.', FALSE], + ['2023-04-17.pre123', FALSE], + ['2023-04-17.pre123post', FALSE], + ['2023-04-17.123post', FALSE], + ]; + } + +} diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/tests/phpunit/Drupal/EnvironmentSettingsTest.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/tests/phpunit/Drupal/EnvironmentSettingsTest.php new file mode 100644 index 000000000..155b2a0b2 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/tests/phpunit/Drupal/EnvironmentSettingsTest.php @@ -0,0 +1,12 @@ +@@ -263,9 +263,9 @@ + } + + /** +- * Test per-environment settings for GitHub Actions. ++ * Test per-environment settings for CircleCI. + */ +- public function testEnvironmentGha(): void { ++ public function testEnvironmentCircleCi(): void { + $this->setEnvVars([ + 'CI' => TRUE, + ]); diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/tests/phpunit/Drupal/SettingsTestCase.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/tests/phpunit/Drupal/SettingsTestCase.php new file mode 100644 index 000000000..c6a7c3578 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/tests/phpunit/Drupal/SettingsTestCase.php @@ -0,0 +1,16 @@ +@@ -129,7 +129,6 @@ + * @param array $vars + * Array of environment variables. + * +- * @SuppressWarnings("PHPMD.ElseExpression") + */ + protected function setEnvVars(array $vars): void { + // Unset the existing environment variable if not set in the test. +@@ -308,7 +307,6 @@ + * @param string $message + * Message to display on failure. + * +- * @SuppressWarnings("PHPMD.ElseExpression") + */ + protected function assertArraySubset(array $subset, array $haystack, string $message = ''): void { + foreach ($subset as $key => $value) { diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/web/modules/custom/sw_base/tests/src/Traits/MockTrait.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/web/modules/custom/sw_base/tests/src/Traits/MockTrait.php new file mode 100644 index 000000000..b1b8e022a --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/web/modules/custom/sw_base/tests/src/Traits/MockTrait.php @@ -0,0 +1,9 @@ +@@ -30,8 +30,6 @@ + * @return \PHPUnit\Framework\MockObject\MockObject + * An instance of the mock. + * +- * @SuppressWarnings("PHPMD.CyclomaticComplexity") +- * @SuppressWarnings("PHPMD.ElseExpression") + */ + protected function prepareMock(string $class, array $methods_map = [], array|bool $args = []): MockObject { + $methods = array_values(array_filter(array_keys($methods_map))); diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/web/sites/default/includes/providers/-settings.gha.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/web/sites/default/includes/providers/-settings.gha.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/web/sites/default/includes/providers/settings.circleci.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/web/sites/default/includes/providers/settings.circleci.php new file mode 100644 index 000000000..7cad35cf8 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpmd_circleci/web/sites/default/includes/providers/settings.circleci.php @@ -0,0 +1,17 @@ +404 Not Found

Not Found

The requested URL "@path" was not found on this server.

'; + include_once $contrib_path . '/fast404/fast404.inc'; +- // @phpstan-ignore-next-line + fast404_preboot($settings); + // @codeCoverageIgnoreEnd + } diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/-phpstan.neon b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/-phpstan.neon new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/.ahoy.yml b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/.ahoy.yml new file mode 100644 index 000000000..470e6e6b6 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/.ahoy.yml @@ -0,0 +1,8 @@ +@@ -186,7 +186,6 @@ + usage: Lint back-end code. + cmd: | + ahoy cli vendor/bin/phpcs +- ahoy cli vendor/bin/phpstan + ahoy cli vendor/bin/rector --clear-cache --dry-run + ahoy cli vendor/bin/phpmd . text phpmd.xml + diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/.circleci/README.md b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/.circleci/README.md new file mode 100644 index 000000000..533a875e9 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/.circleci/README.md @@ -0,0 +1,2 @@ +You may add custom scripts, which would run only in CI, to this directory and +reference them from `config.yml` file. diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/.circleci/config.yml b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/.circleci/config.yml new file mode 100644 index 000000000..27662d99b --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/.circleci/config.yml @@ -0,0 +1,541 @@ +# CircleCI 2.0 configuration file. +# +# This configuration file uses the "docker" executor to run the Docker stack. +# +# A "runner" container, created from a specified container image, is used to +# checkout source code and run commands defined in this file. Application Docker +# containers defined in `docker-compose.yml` run on a *remote* Docker server +# controlled by CircleCI. +# The "runner" container uses Docker client to control the remote Docker server. +version: '2.1' + +aliases: + # SSH key fingerprint to download the database. + # Replace this key fingerprint with your own and remove this comment. + - &db_ssh_fingerprint "SHA256:6d+U5QubT0eAWz+4N2wt+WM2qx6o4cvyvQ6xILETJ84" + + # SSH key fingerprint to deploy code. + # Replace this key fingerprint with your own and remove this comment. + - &deploy_ssh_fingerprint "SHA256:6d+U5QubT0eAWz+4N2wt+WM2qx6o4cvyvQ6xILETJ84" + + # Schedule to run nightly database build (to cache the database for the next day). + - &nightly_db_schedule "0 18 * * *" + + # Shared runner container configuration applied to each job. + - &runner_config + working_directory: &working_directory ~/project + environment: + VORTEX_DB_DOWNLOAD_SSH_FINGERPRINT: *db_ssh_fingerprint + VORTEX_DEPLOY_SSH_FINGERPRINT: *deploy_ssh_fingerprint + docker: + # Using the 'runner' container where each job will be executed. + # This container has all the necessary tools to run a dockerized environment. + # https://github.com/drevops/ci-runner + # https://hub.docker.com/repository/docker/drevops/ci-runner/tags + - image: drevops/ci-runner:__VERSION__ + auth: + username: ${VORTEX_CONTAINER_REGISTRY_USER} + password: ${VORTEX_CONTAINER_REGISTRY_PASS} + environment: + # Set runner timezone via UI to ensure that executed operations use correct timestamps. + # https://en.wikipedia.org/wiki/List_of_tz_database_time_zones + TZ: UTC + # Set runner terminal capabilities. + TERM: xterm-256color + # Disable strict host key checking for SSH connections. + VORTEX_SSH_DISABLE_STRICT_HOST_KEY_CHECKING: "1" + # Remove all SSH keys from the runner container. + VORTEX_SSH_REMOVE_ALL_KEYS: "1" + # 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" + # Directory to store test results. + VORTEX_CI_TEST_RESULTS: &test_results /tmp/tests + # Directory to store test artifacts. + VORTEX_CI_ARTIFACTS: &artifacts /tmp/artifacts + # Directory to use for artifact deployments. + VORTEX_DEPLOY_ARTIFACT_SRC: /tmp/workspace/code + # Source code location for artifact deployments. + VORTEX_DEPLOY_ARTIFACT_ROOT: *working_directory + # Report file location for artifact deployments. + VORTEX_DEPLOY_ARTIFACT_LOG: /tmp/artifacts/deployment_log.txt + # Check only minimal stack requirements. + VORTEX_DOCTOR_CHECK_MINIMAL: 1 + # CI runner resource class. + # https://circleci.com/docs/2.0/configuration-reference/#resource_class + # Change to 'large' for faster builds. + resource_class: medium + + - &step_setup_remote_docker + setup_remote_docker: + # Docker Layer Caching allows to significantly speed up builds by caching + # images built during previous runs. + # https://circleci.com/docs/2.0/docker-layer-caching/ + docker_layer_caching: false + version: default + + - &step_process_codebase_for_ci + run: + name: Process codebase to run in CI + command: | + find . -name "docker-compose.yml" -print0 | xargs -0 -I {} sh -c "sed -i -e ''/###/d'' {} && sed -i -e ''s/##//'' {}" + mkdir -p /tmp/workspace/code + + - &load_variables_from_dotenv + run: + name: Load environment variables from .env file + # Load variables from .env file, respecting existing values, and make them available for the next steps. + command: t=$(mktemp) && export -p >"${t}" && set -a && . ./.env && set +a && . "${t}" && export -p >> "$BASH_ENV" + +################################################################################ +# PARAMETERS +################################################################################ + +parameters: + run_update_dependencies: + type: boolean + default: false + +################################################################################ +# JOBS +################################################################################ + +jobs: + # Database handling is a first step of the build. + # - $VORTEX_CI_DB_CACHE_TIMESTAMP is used to determine if a fresh DB dump + # should be downloaded for the current build. Usually, a daily database dump + # is sufficient for development activities. + # - $VORTEX_CI_DB_CACHE_FALLBACK is used if the cache did not match $VORTEX_CI_DB_CACHE_TIMESTAMP. + # This allows to rely on the cache from the previous days within the same branch. + database: &job-database + <<: *runner_config + steps: + - attach_workspace: + at: /tmp/workspace + + - add_ssh_keys: + fingerprints: + - *db_ssh_fingerprint + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + - *step_setup_remote_docker + + - run: + name: Create cache keys for database caching as files + command: | + echo "${VORTEX_CI_DB_CACHE_BRANCH}" | tee /tmp/db_cache_branch + echo "${VORTEX_CI_DB_CACHE_FALLBACK/no/${CIRCLE_BUILD_NUM}}" | tee /tmp/db_cache_fallback + date "${VORTEX_CI_DB_CACHE_TIMESTAMP}" | tee /tmp/db_cache_timestamp + echo "yes" | tee /tmp/db_cache_fallback_yes + + - restore_cache: + keys: + # Restore DB cache based on the cache strategy set by the cache keys below. + # https://circleci.com/docs/2.0/caching/#restoring-cache + # Change 'v1' to 'v2', 'v3' etc., commit and push to force 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). + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback" }}-{{ checksum "/tmp/db_cache_timestamp" }} + # Fallback to caching by default branch name only. Allows to use + # cache from the branch build on the previous day. + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback" }}- + + - run: + name: Download DB + command: VORTEX_DB_DOWNLOAD_SEMAPHORE=/tmp/download-db-success ./scripts/vortex/download-db.sh + no_output_timeout: 30m + + # Execute commands after database download script finished: if the + # DB dump was downloaded - build the site (to ensure that the DB dump + # is valid) and export the DB using selected method (to support + # "file-to-image" or "image-to-file" conversions). + # Note that configuration changes and the DB updates are not applied, so + # the database will be cached in the same state as downloaded. + - run: + name: Export DB after download + command: | + [ ! -f /tmp/download-db-success ] && echo "==> Database download semaphore file is missing. DB export will not proceed." && exit 0 + ./scripts/vortex/login-container-registry.sh + docker compose up --detach && sleep 15 + docker compose exec cli mkdir -p .data && docker compose cp -L .data/db.sql cli:/app/.data/db.sql || true + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c "VORTEX_PROVISION_POST_OPERATIONS_SKIP=1 ./scripts/vortex/provision.sh" + grep -q ^VORTEX_DB_IMAGE .env && rm .data/db.sql || true + ./scripts/vortex/export-db.sh db.sql + no_output_timeout: 30m + + - save_cache: + # 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. + key: __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback_yes" }}-{{ checksum "/tmp/db_cache_timestamp" }} + paths: + - /root/project/.data + + # Nightly database job. Same as above, but with additional variables set. + database-nightly: + <<: *job-database + environment: + VORTEX_DB_DOWNLOAD_SSH_FINGERPRINT: *db_ssh_fingerprint + VORTEX_DEPLOY_SSH_FINGERPRINT: *deploy_ssh_fingerprint + # Enforce fresh DB build (do not rely on fallback caches). + VORTEX_CI_DB_CACHE_FALLBACK: 'no' + # Always use fresh base image for the database (if database-in-image storage is used). + VORTEX_DB_IMAGE_BASE: drevops/mariadb-drupal-data:__VERSION__ + # Deploy container image (if database-in-image storage is used). + VORTEX_EXPORT_DB_CONTAINER_REGISTRY_DEPLOY_PROCEED: 1 + # Do not build the Drupal front-end. + VORTEX_FRONTEND_BUILD_SKIP: 1 + + # Build and test is a second step of the build. The testing is performed + # within the same job to save time on provisioning during the job. + build: &job_build + <<: *runner_config + parallelism: 2 + steps: + - attach_workspace: + at: /tmp/workspace + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + + - run: + name: Validate Composer configuration + command: composer validate --strict || [ "${VORTEX_CI_COMPOSER_VALIDATE_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Set cache keys for database caching + command: | + echo "${VORTEX_CI_DB_CACHE_BRANCH}" | tee /tmp/db_cache_branch + echo "yes" | tee /tmp/db_cache_fallback_yes + echo "$(date ${VORTEX_CI_DB_CACHE_TIMESTAMP})" | tee /tmp/db_cache_timestamp + + - restore_cache: + keys: + # Use cached artifacts from previous builds of this branch. + # https://circleci.com/docs/2.0/caching/#restoring-cache + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback_yes" }}-{{ checksum "/tmp/db_cache_timestamp" }} + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback_yes" }}- + + - *step_setup_remote_docker + + - run: + name: Login to container registry + command: ./scripts/vortex/login-container-registry.sh + + - run: + name: Lint Dockerfiles with Hadolint + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + for file in $(find .docker -name 'Dockerfile' -o -name '*.dockerfile'); do + echo "Linting ${file}" && cat "${file}" | docker run --rm -i hadolint/hadolint || [ "${VORTEX_CI_HADOLINT_IGNORE_FAILURE:-0}" -eq 1 ] + done + + - run: + name: Lint Docker Compose files with DCLint + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker run --rm -v "${PWD}":/app zavoloklom/dclint:__VERSION__ . || [ "${VORTEX_CI_DCLINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Build stack + command: docker compose up -d + + - run: + name: Export built codebase + command: | + echo "${VORTEX_DEPLOY_TYPES:-}" | grep -vq "artifact" && exit 0 || true + mkdir -p "/tmp/workspace/code" + docker compose cp -L cli:"/app/." "/tmp/workspace/code" + du -sh "/tmp/workspace/code" + + - run: + name: Install development dependencies + command: | + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c " \ + if [ -n \"${PACKAGE_TOKEN:-}\" ]; then export COMPOSER_AUTH='{\"github-oauth\": {\"github.com\": \"${PACKAGE_TOKEN-}\"}}'; fi && \ + COMPOSER_MEMORY_LIMIT=-1 composer --ansi install --prefer-dist" + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c "yarn install --frozen-lockfile" + + - run: + name: Validate Composer configuration is normalized + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli composer normalize --dry-run || [ "${VORTEX_CI_COMPOSER_NORMALIZE_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with PHPCS + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/phpcs || [ "${VORTEX_CI_PHPCS_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with Rector + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/rector --clear-cache --dry-run || [ "${VORTEX_CI_RECTOR_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with PHPMD + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/phpmd . text phpmd.xml || [ "${VORTEX_CI_PHPMD_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with Twig CS Fixer + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/twig-cs-fixer || [ "${VORTEX_CI_TWIG_CS_FIXER_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with Gherkin Lint + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/gherkinlint lint tests/behat/features || [ "${VORTEX_CI_GHERKIN_LINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint module code with NodeJS linters + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli bash -c "yarn run lint" || [ "${VORTEX_CI_NODEJS_LINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint theme code with NodeJS linters + command: | + { [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ]; } || [ "${VORTEX_FRONTEND_BUILD_SKIP:-0}" -eq 1 ] && exit 0 + docker compose exec -T cli bash -c "yarn --cwd=\${WEBROOT}/themes/custom/\${DRUPAL_THEME} run lint" || [ "${VORTEX_CI_NODEJS_LINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Provision site + command: | + if [ -f .data/db.sql ]; then + docker compose exec cli mkdir -p .data + docker compose cp -L .data/db.sql cli:/app/.data/db.sql + fi + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli ./scripts/vortex/provision.sh + no_output_timeout: 30m + + - run: + name: Test with PHPUnit + command: docker compose exec -T cli vendor/bin/phpunit || [ "${VORTEX_CI_PHPUNIT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Test with Behat + command: | + if [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p${CIRCLE_NODE_INDEX}}"; fi + echo "Running with ${VORTEX_CI_BEHAT_PROFILE:-default} profile" + docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ + docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ + [ "${VORTEX_CI_BEHAT_IGNORE_FAILURE:-0}" -eq 1 ] + no_output_timeout: 30m + + - run: + name: Process test logs and artifacts + command: | + mkdir -p "${VORTEX_CI_TEST_RESULTS}" "${VORTEX_CI_ARTIFACTS}" + if docker compose ps --services --filter "status=running" | grep -q cli && docker compose exec cli test -d /app/.logs; then + docker compose cp cli:/app/.logs/. "${VORTEX_CI_ARTIFACTS}/" + if docker compose exec -T cli sh -c '[ -d /app/.logs/test_results/ ]'; then + docker compose cp cli:/app/.logs/test_results/. "${VORTEX_CI_TEST_RESULTS}/" + fi + fi + when: always + + - store_test_results: + path: *test_results + + - store_artifacts: + path: *artifacts + + - run: + name: Upload code coverage reports to Codecov + command: | + if [ -n "${CODECOV_TOKEN}" ] && [ -d /tmp/artifacts/coverage ] && ! echo "${CIRCLE_BRANCH}" | grep -q '^deps/'; then + codecov -Z -s /tmp/artifacts/coverage; + fi + + - persist_to_workspace: + root: /tmp/workspace + paths: + - code + + # Deploy primary branches. + deploy: &job_deploy + <<: *runner_config + steps: + - attach_workspace: + at: /tmp/workspace + + - add_ssh_keys: + fingerprints: + - *deploy_ssh_fingerprint + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + + - run: + name: Deploy + command: | + VORTEX_DEPLOY_BRANCH="${CIRCLE_BRANCH}" \ + VORTEX_DEPLOY_PR="$(echo ${CIRCLE_PULL_REQUEST} | cut -d'/' -f 7)" \ + VORTEX_DEPLOY_PR_HEAD=${CIRCLE_SHA1} \ + ./scripts/vortex/deploy.sh + no_output_timeout: 30m + + - store_artifacts: + path: *artifacts + + # Deploy tags. + deploy-tags: &job-deploy-tags + <<: *runner_config + steps: + - attach_workspace: + at: /tmp/workspace + + - add_ssh_keys: + fingerprints: + - *deploy_ssh_fingerprint + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + + - run: + name: Deploy + command: VORTEX_DEPLOY_MODE="tag" ./scripts/vortex/deploy.sh + no_output_timeout: 30m + + - store_artifacts: + path: *artifacts + + # Self-hosted dependency updates. + # Add the following environment variables to the CircleCI project: + # - RENOVATE_TOKEN: GitHub access token. + # - RENOVATE_REPOSITORIES: Repository to run Renovate on as `vendor/repository`. + # - RENOVATE_GIT_AUTHOR: Author for Renovate commits as `Name `. + # Variables provided below can be overridden in the CircleCI project settings. + update-dependencies: + docker: + - image: renovate/renovate:__VERSION__ + environment: + RENOVATE_PLATFORM: 'github' + RENOVATE_AUTODISCOVER: false + RENOVATE_DEPENDENCY_DASHBOARD_TITLE: 'Renovate Dependency Dashboard (self-hosted) by CircleCI' + RENOVATE_DEPENDENCY_DASHBOARD: false + RENOVATE_DRY_RUN: false + LOG_LEVEL: 'debug' + + steps: + - checkout + - run: + name: Check if RENOVATE_TOKEN is set + command: | + if [ -z "${RENOVATE_TOKEN}" ]; then + echo "RENOVATE_TOKEN is not set. Skipping job." + circleci-agent step halt + fi + + if [ -z "${RENOVATE_REPOSITORIES}" ]; then + echo "Renovate repository is not set. Skipping job." + circleci-agent step halt + fi + + if [ -z "${RENOVATE_GIT_AUTHOR}" ]; then + echo "Renovate git author is not set. Skipping job." + circleci-agent step halt + fi + + - run: + name: Validate Renovate configuration + command: renovate-config-validator + + - run: + name: Run Renovate + command: renovate + +################################################################################ +# WORKFLOWS +################################################################################ + +workflows: + version: 2 + # Commit workflow. Runs for every commit push to the remote repository. + commit: + jobs: + - database: + filters: + tags: + only: /.*/ + - build: + requires: + - database + filters: + tags: + only: /.*/ + - deploy: + requires: + - build + filters: + branches: + # Allowed branches: + # - production, main, master, develop, ci, cisomething + # - project/description + # - deps/* + # - feature/description, feature/123-description + # - bugfix/description, bugfix/123-description + # - release/__VERSION__, release/__VERSION__ (per https://semver.org/) + # - release/2023-04-17, release/2023-04-17.123 (date-based) + # - hotfix/__VERSION__, hotfix/__VERSION__ (per https://semver.org/) + # - hotfix/2023-04-17, hotfix/2023-04-17.123 (date-based) + only: /^(production|main|master|develop)$|^project\/[a-zA-z0-9\-\.]+|^(feature|bugfix)\/[a-zA-Z0-9\-\.\,_]+$|^ci.*|^(release|hotfix)\/[0-9]+(\.[0-9]+){2}(-rc\.[0-9]+)?$|^(release|hotfix)\/[0-9]{4}-[0-9]{2}-[0-9]{2}(\.[0-9]+)?$/ + tags: + ignore: /.*/ + - deploy-tags: + requires: + - build + filters: + branches: + ignore: /.*/ + tags: + # Allowed tags: + # - __VERSION__, __VERSION__ (per https://semver.org/) + # - 2023-04-17, 2023-04-17.123 (date-based) + only: /^[0-9]+(\.[0-9]+){2}(-rc\.[0-9]+)?$|^[0-9]{4}-[0-9]{2}-[0-9]{2}(\.[0-9]+)?$/ + + # Nightly database workflow runs overnight to capture fresh database and cache it. + nightly-db: + triggers: + - schedule: + cron: *nightly_db_schedule + filters: + branches: + only: + - develop + jobs: + - database-nightly + + # Self-hosted Renovate workflow. + update-dependencies: + triggers: + - schedule: + cron: "5 11,23 * * *" + filters: + branches: + only: + - develop + jobs: + - update-dependencies + + update-dependencies-manual: + when: << pipeline.parameters.run_update_dependencies >> + jobs: + - update-dependencies diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/.dockerignore b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/.dockerignore new file mode 100644 index 000000000..808326ac1 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/.dockerignore @@ -0,0 +1,8 @@ +@@ -44,7 +44,6 @@ + !patches + !phpcs.xml + !phpmd.xml +-!phpstan.neon + !phpunit.xml + !rector.php + !scripts diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/.github/workflows/-build-test-deploy.yml b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/.github/workflows/-build-test-deploy.yml new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/README.md b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/README.md new file mode 100644 index 000000000..78e06291b --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/README.md @@ -0,0 +1,9 @@ +@@ -4,7 +4,7 @@ + + Drupal 11 implementation of star wars for star wars Org + +-[![Database, Build, Test and Deploy](https://github.com/star_wars_org/star_wars/actions/workflows/build-test-deploy.yml/badge.svg)](https://github.com/star_wars_org/star_wars/actions/workflows/build-test-deploy.yml) ++[![CircleCI](https://circleci.com/gh/star_wars_org/star_wars.svg?style=shield)](https://circleci.com/gh/star_wars_org/star_wars) + + ![Drupal 11](https://img.shields.io/badge/Drupal-11-blue.svg) + [![codecov](https://codecov.io/gh/star_wars_org/star_wars/graph/badge.svg)](https://codecov.io/gh/star_wars_org/star_wars) diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/composer.json b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/composer.json new file mode 100644 index 000000000..f84927496 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/composer.json @@ -0,0 +1,22 @@ +@@ -38,13 +38,10 @@ + "drupal/core-dev": "__VERSION__", + "drupal/drupal-extension": "__VERSION__", + "ergebnis/composer-normalize": "__VERSION__", +- "mglaman/phpstan-drupal": "__VERSION__", + "palantirnet/drupal-rector": "__VERSION__", + "phpcompatibility/php-compatibility": "__VERSION__", + "phpmd/phpmd": "__VERSION__", + "phpspec/prophecy-phpunit": "__VERSION__", +- "phpstan/extension-installer": "__VERSION__", +- "phpstan/phpstan": "__VERSION__", + "pyrech/composer-changelogs": "__VERSION__", + "rector/rector": "__VERSION__", + "vincentlanglet/twig-cs-fixer": "__VERSION__" +@@ -79,7 +76,6 @@ + "ergebnis/composer-normalize": true, + "oomphinc/composer-installers-extender": true, + "php-http/discovery": true, +- "phpstan/extension-installer": true, + "pyrech/composer-changelogs": true, + "tbachert/spi": true + }, diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/docs/ci.md b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/docs/ci.md new file mode 100644 index 000000000..7d29e25c6 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/docs/ci.md @@ -0,0 +1,31 @@ +@@ -5,12 +5,12 @@ + Before feature changes can be merged into a shared mainline, a complete build + must run and pass all tests on CI server. + +-## GitHub Actions ++## Circle CI + +-This project uses [GitHub Actions](https://github.com/features/actions) as a +-CI server: it imports production backups into fully built codebase and runs +-code linting and tests. When tests pass, a deployment process is triggered for +-nominated branches (usually, `main` and `develop`). ++This project uses [Circle CI](https://circleci.com/) as a CI server: it imports ++production backups into fully built codebase and runs code linting and tests. ++When tests pass, a deployment process is triggered for nominated branches ++(usually, `main` and `develop`). + + Refer to https://www.vortextemplate.com/docs/continuous-integration for more information. + +@@ -21,9 +21,6 @@ + + ### SSH + +-GitHub Actions does not supports shell access to the build, but there is an +-action provided withing the `build` job that allows you to run a build with SSH +-support. +- +-Use "Run workflow" button in GitHub Actions UI to start build with SSH support +-that will be available for 120 minutes after the build is finished. ++Circle CI supports shell access to the build for 120 minutes after the build is ++finished when the build is started with SSH support. Use "Rerun job with SSH" ++button in Circle CI UI to start build with SSH support. diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/tests/phpunit/CircleCiConfigTest.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/tests/phpunit/CircleCiConfigTest.php new file mode 100644 index 000000000..d07086d21 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/tests/phpunit/CircleCiConfigTest.php @@ -0,0 +1,261 @@ +config = Yaml::decode($file); + } + + /** + * Tests for deploy branch regex. + * + * @see https://semver.org/ + */ + #[DataProvider('dataProviderDeployBranchRegex')] + public function testDeployBranchRegex(string $branch, bool $expected = TRUE): void { + $this->assertEquals($expected, preg_match($this->config['workflows']['commit']['jobs'][2]['deploy']['filters']['branches']['only'], $branch)); + } + + /** + * Data provider for testDeployBranchRegex(). + */ + public static function dataProviderDeployBranchRegex(): array { + return [ + // Positive branches. + ['production'], + ['main'], + ['master'], + ['develop'], + + ['ci'], + ['cisomething'], + + ['release/123.456.789'], + ['release/123.456.789-rc.123'], + ['hotfix/123.456.789'], + ['hotfix/123.456.789-rc.123'], + + ['release/2023-04-17'], + ['release/2023-04-17.1'], + ['hotfix/2023-04-17'], + ['hotfix/2023-04-17.1'], + + ['feature/description'], + ['feature/Description'], + ['feature/Description-With-Hyphens'], + ['feature/Description-With_Underscores'], + ['feature/123-description'], + ['feature/123-Description'], + ['feature/UNDERSCORES_UNDERSCORES'], + ['feature/123-Description-With_UNDERSCORES'], + ['feature/1.x'], + ['feature/0.x'], + ['feature/0.1.x'], + ['feature/0.1.2.x'], + ['feature/1.x-description'], + ['feature/0.x-description'], + ['feature/0.1.x-description'], + ['feature/0.1.2.x-description'], + + ['bugfix/description'], + ['bugfix/Description'], + ['bugfix/Description-With-Hyphens'], + ['bugfix/Description-With_Underscores'], + ['bugfix/123-description'], + ['bugfix/123-Description'], + ['bugfix/UNDERSCORES_UNDERSCORES'], + ['bugfix/123-Description-With_UNDERSCORES'], + ['bugfix/1.x'], + ['bugfix/0.x'], + ['bugfix/0.1.x'], + ['bugfix/0.1.2.x'], + ['bugfix/1.x-description'], + ['bugfix/0.x-description'], + ['bugfix/0.1.x-description'], + ['bugfix/0.1.2.x-description'], + + ['project/description'], + ['project/Description'], + ['project/Description-With-Hyphens'], + ['project/123-description'], + ['project/123-Description'], + ['project/1.x'], + ['project/0.x'], + ['project/0.1.x'], + ['project/0.1.2.x'], + ['project/1.x-description'], + ['project/0.x-description'], + ['project/0.1.x-description'], + ['project/0.1.2.x-description'], + + // Negative branches. + ['something', FALSE], + ['premain', FALSE], + ['premaster', FALSE], + ['predevelop', FALSE], + ['mainpost', FALSE], + ['masterpost', FALSE], + ['developpost', FALSE], + ['premainpost', FALSE], + ['premasterpost', FALSE], + ['predeveloppost', FALSE], + + ['preci', FALSE], + ['precipost', FALSE], + + ['deps/something', FALSE], + ['deps', FALSE], + ['predeps', FALSE], + ['depspost', FALSE], + ['predepspost', FALSE], + + ['feature', FALSE], + ['release', FALSE], + ['hotfix', FALSE], + ['prefeature', FALSE], + ['prerelease', FALSE], + ['prehotfix', FALSE], + ['featurepost', FALSE], + ['releasepost', FALSE], + ['hotfixpost', FALSE], + ['prefeaturepost', FALSE], + ['prereleasepost', FALSE], + ['prehotfixpost', FALSE], + + ['release/123', FALSE], + ['release/123.456', FALSE], + ['hotfix/123', FALSE], + ['hotfix/123.456', FALSE], + + ['release/202-04-17', FALSE], + ['release/2023-4-17', FALSE], + ['release/2023-04-1', FALSE], + ['release/pre2023-04-17', FALSE], + ['release/2023-04-17post', FALSE], + ['release/pre2023-04-17post', FALSE], + + ['hotfix/202-04-17', FALSE], + ['hotfix/2023-4-17', FALSE], + ['hotfix/2023-04-1', FALSE], + ['hotfix/pre2023-04-17', FALSE], + ['hotfix/2023-04-17post', FALSE], + ['hotfix/pre2023-04-17post', FALSE], + + ['release/123.456.789-something', FALSE], + ['release/123.456.789-rc', FALSE], + ['release/123.456.789-rc123', FALSE], + ['release/123.456.789-rc-123', FALSE], + ['release/123.456.789-prerc123', FALSE], + ['release/123.456.789-rcpost123', FALSE], + ['release/123.456.789-prercpost123', FALSE], + ['release/123.456.789-rc123something', FALSE], + ['release/123.456.789-rc.123something', FALSE], + ['release/123.456.789-rc.123-something', FALSE], + + ['hotfix/123.456.789-something', FALSE], + ['hotfix/123.456.789-rc', FALSE], + ['hotfix/123.456.789-rc123', FALSE], + ['hotfix/123.456.789-rc-123', FALSE], + ['hotfix/123.456.789-prerc123', FALSE], + ['hotfix/123.456.789-rcpost123', FALSE], + ['hotfix/123.456.789-prercpost123', FALSE], + ['hotfix/123.456.789-rc123something', FALSE], + ['hotfix/123.456.789-rc.123something', FALSE], + ['hotfix/123.456.789-rc.123-something', FALSE], + + ['prefeature/something', FALSE], + ['prefbugfix/something', FALSE], + ['prerelease/something', FALSE], + ['prehotfix/something', FALSE], + ['featurepost/something', FALSE], + ['bugfixpost/something', FALSE], + ['releasepost/something', FALSE], + ['hotfixpost/something', FALSE], + ['prefeaturepost/something', FALSE], + ['prebugfixpost/something', FALSE], + ['prereleasepost/something', FALSE], + ['prehotfixpost/something', FALSE], + ['preproject/something', FALSE], + ['projectpost/something', FALSE], + ]; + } + + /** + * Tests for deploy tag regex. + * + * @see https://semver.org/ + */ + #[DataProvider('dataProviderDeployTagRegex')] + public function testDeployTagRegex(string $branch, bool $expected = TRUE): void { + $this->assertEquals($expected, preg_match($this->config['workflows']['commit']['jobs'][3]['deploy-tags']['filters']['tags']['only'], $branch)); + } + + /** + * Data provider for testDeployTagRegex(). + */ + public static function dataProviderDeployTagRegex(): array { + return [ + // Positive tags. + ['1.2.3'], + ['1.2.3-rc.123'], + ['2023-04-17'], + ['2023-04-17.123'], + + // Negative tags. + ['123', FALSE], + ['123.456', FALSE], + ['1.2.3-rc123', FALSE], + ['1.2.3-rc.123post', FALSE], + ['1.2.3-prerc.123', FALSE], + ['1.2.3-rcpost.123', FALSE], + ['1.2.3-prercpost.123', FALSE], + + ['202-04-17', FALSE], + ['2023-0-17', FALSE], + ['2023-04-1', FALSE], + ['pre2023-04-17', FALSE], + ['2023-04-17post', FALSE], + ['pre2023-04-17post', FALSE], + ['2023-04-17.123.', FALSE], + ['2023-04-17.pre123', FALSE], + ['2023-04-17.pre123post', FALSE], + ['2023-04-17.123post', FALSE], + ]; + } + +} diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/tests/phpunit/Drupal/EnvironmentSettingsTest.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/tests/phpunit/Drupal/EnvironmentSettingsTest.php new file mode 100644 index 000000000..155b2a0b2 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/tests/phpunit/Drupal/EnvironmentSettingsTest.php @@ -0,0 +1,12 @@ +@@ -263,9 +263,9 @@ + } + + /** +- * Test per-environment settings for GitHub Actions. ++ * Test per-environment settings for CircleCI. + */ +- public function testEnvironmentGha(): void { ++ public function testEnvironmentCircleCi(): void { + $this->setEnvVars([ + 'CI' => TRUE, + ]); diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/web/modules/custom/sw_base/src/Plugin/Block/CounterBlock.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/web/modules/custom/sw_base/src/Plugin/Block/CounterBlock.php new file mode 100644 index 000000000..4301ae253 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/web/modules/custom/sw_base/src/Plugin/Block/CounterBlock.php @@ -0,0 +1,8 @@ +@@ -20,7 +20,6 @@ + /** + * {@inheritdoc} + * +- * @phpstan-ignore-next-line + */ + public function build(): array { + return [ diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/web/modules/custom/sw_base/sw_base.module b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/web/modules/custom/sw_base/sw_base.module new file mode 100644 index 000000000..f573df9a1 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/web/modules/custom/sw_base/sw_base.module @@ -0,0 +1,8 @@ +@@ -12,7 +12,6 @@ + /** + * Implements hook_theme(). + * +- * @phpstan-ignore-next-line + */ + function sw_base_theme(): array { + return [ diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/web/sites/default/includes/modules/settings.fast404.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/web/sites/default/includes/modules/settings.fast404.php new file mode 100644 index 000000000..74b826db0 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/web/sites/default/includes/modules/settings.fast404.php @@ -0,0 +1,8 @@ +@@ -22,7 +22,6 @@ + $settings['fast404_string_whitelisting'] = ['/advagg_']; + $settings['fast404_html'] = '404 Not Found

Not Found

The requested URL "@path" was not found on this server.

'; + include_once $contrib_path . '/fast404/fast404.inc'; +- // @phpstan-ignore-next-line + fast404_preboot($settings); + // @codeCoverageIgnoreEnd + } diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/web/sites/default/includes/providers/-settings.gha.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/web/sites/default/includes/providers/-settings.gha.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/web/sites/default/includes/providers/settings.circleci.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/web/sites/default/includes/providers/settings.circleci.php new file mode 100644 index 000000000..7cad35cf8 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpstan_circleci/web/sites/default/includes/providers/settings.circleci.php @@ -0,0 +1,17 @@ +"${t}" && set -a && . ./.env && set +a && . "${t}" && export -p >> "$BASH_ENV" + +################################################################################ +# PARAMETERS +################################################################################ + +parameters: + run_update_dependencies: + type: boolean + default: false + +################################################################################ +# JOBS +################################################################################ + +jobs: + # Database handling is a first step of the build. + # - $VORTEX_CI_DB_CACHE_TIMESTAMP is used to determine if a fresh DB dump + # should be downloaded for the current build. Usually, a daily database dump + # is sufficient for development activities. + # - $VORTEX_CI_DB_CACHE_FALLBACK is used if the cache did not match $VORTEX_CI_DB_CACHE_TIMESTAMP. + # This allows to rely on the cache from the previous days within the same branch. + database: &job-database + <<: *runner_config + steps: + - attach_workspace: + at: /tmp/workspace + + - add_ssh_keys: + fingerprints: + - *db_ssh_fingerprint + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + - *step_setup_remote_docker + + - run: + name: Create cache keys for database caching as files + command: | + echo "${VORTEX_CI_DB_CACHE_BRANCH}" | tee /tmp/db_cache_branch + echo "${VORTEX_CI_DB_CACHE_FALLBACK/no/${CIRCLE_BUILD_NUM}}" | tee /tmp/db_cache_fallback + date "${VORTEX_CI_DB_CACHE_TIMESTAMP}" | tee /tmp/db_cache_timestamp + echo "yes" | tee /tmp/db_cache_fallback_yes + + - restore_cache: + keys: + # Restore DB cache based on the cache strategy set by the cache keys below. + # https://circleci.com/docs/2.0/caching/#restoring-cache + # Change 'v1' to 'v2', 'v3' etc., commit and push to force 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). + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback" }}-{{ checksum "/tmp/db_cache_timestamp" }} + # Fallback to caching by default branch name only. Allows to use + # cache from the branch build on the previous day. + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback" }}- + + - run: + name: Download DB + command: VORTEX_DB_DOWNLOAD_SEMAPHORE=/tmp/download-db-success ./scripts/vortex/download-db.sh + no_output_timeout: 30m + + # Execute commands after database download script finished: if the + # DB dump was downloaded - build the site (to ensure that the DB dump + # is valid) and export the DB using selected method (to support + # "file-to-image" or "image-to-file" conversions). + # Note that configuration changes and the DB updates are not applied, so + # the database will be cached in the same state as downloaded. + - run: + name: Export DB after download + command: | + [ ! -f /tmp/download-db-success ] && echo "==> Database download semaphore file is missing. DB export will not proceed." && exit 0 + ./scripts/vortex/login-container-registry.sh + docker compose up --detach && sleep 15 + docker compose exec cli mkdir -p .data && docker compose cp -L .data/db.sql cli:/app/.data/db.sql || true + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c "VORTEX_PROVISION_POST_OPERATIONS_SKIP=1 ./scripts/vortex/provision.sh" + grep -q ^VORTEX_DB_IMAGE .env && rm .data/db.sql || true + ./scripts/vortex/export-db.sh db.sql + no_output_timeout: 30m + + - save_cache: + # 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. + key: __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback_yes" }}-{{ checksum "/tmp/db_cache_timestamp" }} + paths: + - /root/project/.data + + # Nightly database job. Same as above, but with additional variables set. + database-nightly: + <<: *job-database + environment: + VORTEX_DB_DOWNLOAD_SSH_FINGERPRINT: *db_ssh_fingerprint + VORTEX_DEPLOY_SSH_FINGERPRINT: *deploy_ssh_fingerprint + # Enforce fresh DB build (do not rely on fallback caches). + VORTEX_CI_DB_CACHE_FALLBACK: 'no' + # Always use fresh base image for the database (if database-in-image storage is used). + VORTEX_DB_IMAGE_BASE: drevops/mariadb-drupal-data:__VERSION__ + # Deploy container image (if database-in-image storage is used). + VORTEX_EXPORT_DB_CONTAINER_REGISTRY_DEPLOY_PROCEED: 1 + # Do not build the Drupal front-end. + VORTEX_FRONTEND_BUILD_SKIP: 1 + + # Build and test is a second step of the build. The testing is performed + # within the same job to save time on provisioning during the job. + build: &job_build + <<: *runner_config + parallelism: 2 + steps: + - attach_workspace: + at: /tmp/workspace + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + + - run: + name: Validate Composer configuration + command: composer validate --strict || [ "${VORTEX_CI_COMPOSER_VALIDATE_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Set cache keys for database caching + command: | + echo "${VORTEX_CI_DB_CACHE_BRANCH}" | tee /tmp/db_cache_branch + echo "yes" | tee /tmp/db_cache_fallback_yes + echo "$(date ${VORTEX_CI_DB_CACHE_TIMESTAMP})" | tee /tmp/db_cache_timestamp + + - restore_cache: + keys: + # Use cached artifacts from previous builds of this branch. + # https://circleci.com/docs/2.0/caching/#restoring-cache + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback_yes" }}-{{ checksum "/tmp/db_cache_timestamp" }} + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback_yes" }}- + + - *step_setup_remote_docker + + - run: + name: Login to container registry + command: ./scripts/vortex/login-container-registry.sh + + - run: + name: Lint Dockerfiles with Hadolint + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + for file in $(find .docker -name 'Dockerfile' -o -name '*.dockerfile'); do + echo "Linting ${file}" && cat "${file}" | docker run --rm -i hadolint/hadolint || [ "${VORTEX_CI_HADOLINT_IGNORE_FAILURE:-0}" -eq 1 ] + done + + - run: + name: Lint Docker Compose files with DCLint + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker run --rm -v "${PWD}":/app zavoloklom/dclint:__VERSION__ . || [ "${VORTEX_CI_DCLINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Build stack + command: docker compose up -d + + - run: + name: Export built codebase + command: | + echo "${VORTEX_DEPLOY_TYPES:-}" | grep -vq "artifact" && exit 0 || true + mkdir -p "/tmp/workspace/code" + docker compose cp -L cli:"/app/." "/tmp/workspace/code" + du -sh "/tmp/workspace/code" + + - run: + name: Install development dependencies + command: | + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c " \ + if [ -n \"${PACKAGE_TOKEN:-}\" ]; then export COMPOSER_AUTH='{\"github-oauth\": {\"github.com\": \"${PACKAGE_TOKEN-}\"}}'; fi && \ + COMPOSER_MEMORY_LIMIT=-1 composer --ansi install --prefer-dist" + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c "yarn install --frozen-lockfile" + + - run: + name: Validate Composer configuration is normalized + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli composer normalize --dry-run || [ "${VORTEX_CI_COMPOSER_NORMALIZE_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with PHPCS + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/phpcs || [ "${VORTEX_CI_PHPCS_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with PHPStan + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/phpstan || [ "${VORTEX_CI_PHPSTAN_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with Rector + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/rector --clear-cache --dry-run || [ "${VORTEX_CI_RECTOR_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with PHPMD + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/phpmd . text phpmd.xml || [ "${VORTEX_CI_PHPMD_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with Twig CS Fixer + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/twig-cs-fixer || [ "${VORTEX_CI_TWIG_CS_FIXER_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with Gherkin Lint + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/gherkinlint lint tests/behat/features || [ "${VORTEX_CI_GHERKIN_LINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint module code with NodeJS linters + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli bash -c "yarn run lint" || [ "${VORTEX_CI_NODEJS_LINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint theme code with NodeJS linters + command: | + { [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ]; } || [ "${VORTEX_FRONTEND_BUILD_SKIP:-0}" -eq 1 ] && exit 0 + docker compose exec -T cli bash -c "yarn --cwd=\${WEBROOT}/themes/custom/\${DRUPAL_THEME} run lint" || [ "${VORTEX_CI_NODEJS_LINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Provision site + command: | + if [ -f .data/db.sql ]; then + docker compose exec cli mkdir -p .data + docker compose cp -L .data/db.sql cli:/app/.data/db.sql + fi + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli ./scripts/vortex/provision.sh + no_output_timeout: 30m + + - run: + name: Test with Behat + command: | + if [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p${CIRCLE_NODE_INDEX}}"; fi + echo "Running with ${VORTEX_CI_BEHAT_PROFILE:-default} profile" + docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ + docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ + [ "${VORTEX_CI_BEHAT_IGNORE_FAILURE:-0}" -eq 1 ] + no_output_timeout: 30m + + - run: + name: Process test logs and artifacts + command: | + mkdir -p "${VORTEX_CI_TEST_RESULTS}" "${VORTEX_CI_ARTIFACTS}" + if docker compose ps --services --filter "status=running" | grep -q cli && docker compose exec cli test -d /app/.logs; then + docker compose cp cli:/app/.logs/. "${VORTEX_CI_ARTIFACTS}/" + if docker compose exec -T cli sh -c '[ -d /app/.logs/test_results/ ]'; then + docker compose cp cli:/app/.logs/test_results/. "${VORTEX_CI_TEST_RESULTS}/" + fi + fi + when: always + + - store_test_results: + path: *test_results + + - store_artifacts: + path: *artifacts + + - run: + name: Upload code coverage reports to Codecov + command: | + if [ -n "${CODECOV_TOKEN}" ] && [ -d /tmp/artifacts/coverage ] && ! echo "${CIRCLE_BRANCH}" | grep -q '^deps/'; then + codecov -Z -s /tmp/artifacts/coverage; + fi + + - persist_to_workspace: + root: /tmp/workspace + paths: + - code + + # Deploy primary branches. + deploy: &job_deploy + <<: *runner_config + steps: + - attach_workspace: + at: /tmp/workspace + + - add_ssh_keys: + fingerprints: + - *deploy_ssh_fingerprint + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + + - run: + name: Deploy + command: | + VORTEX_DEPLOY_BRANCH="${CIRCLE_BRANCH}" \ + VORTEX_DEPLOY_PR="$(echo ${CIRCLE_PULL_REQUEST} | cut -d'/' -f 7)" \ + VORTEX_DEPLOY_PR_HEAD=${CIRCLE_SHA1} \ + ./scripts/vortex/deploy.sh + no_output_timeout: 30m + + - store_artifacts: + path: *artifacts + + # Deploy tags. + deploy-tags: &job-deploy-tags + <<: *runner_config + steps: + - attach_workspace: + at: /tmp/workspace + + - add_ssh_keys: + fingerprints: + - *deploy_ssh_fingerprint + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + + - run: + name: Deploy + command: VORTEX_DEPLOY_MODE="tag" ./scripts/vortex/deploy.sh + no_output_timeout: 30m + + - store_artifacts: + path: *artifacts + + # Self-hosted dependency updates. + # Add the following environment variables to the CircleCI project: + # - RENOVATE_TOKEN: GitHub access token. + # - RENOVATE_REPOSITORIES: Repository to run Renovate on as `vendor/repository`. + # - RENOVATE_GIT_AUTHOR: Author for Renovate commits as `Name `. + # Variables provided below can be overridden in the CircleCI project settings. + update-dependencies: + docker: + - image: renovate/renovate:__VERSION__ + environment: + RENOVATE_PLATFORM: 'github' + RENOVATE_AUTODISCOVER: false + RENOVATE_DEPENDENCY_DASHBOARD_TITLE: 'Renovate Dependency Dashboard (self-hosted) by CircleCI' + RENOVATE_DEPENDENCY_DASHBOARD: false + RENOVATE_DRY_RUN: false + LOG_LEVEL: 'debug' + + steps: + - checkout + - run: + name: Check if RENOVATE_TOKEN is set + command: | + if [ -z "${RENOVATE_TOKEN}" ]; then + echo "RENOVATE_TOKEN is not set. Skipping job." + circleci-agent step halt + fi + + if [ -z "${RENOVATE_REPOSITORIES}" ]; then + echo "Renovate repository is not set. Skipping job." + circleci-agent step halt + fi + + if [ -z "${RENOVATE_GIT_AUTHOR}" ]; then + echo "Renovate git author is not set. Skipping job." + circleci-agent step halt + fi + + - run: + name: Validate Renovate configuration + command: renovate-config-validator + + - run: + name: Run Renovate + command: renovate + +################################################################################ +# WORKFLOWS +################################################################################ + +workflows: + version: 2 + # Commit workflow. Runs for every commit push to the remote repository. + commit: + jobs: + - database: + filters: + tags: + only: /.*/ + - build: + requires: + - database + filters: + tags: + only: /.*/ + - deploy: + requires: + - build + filters: + branches: + # Allowed branches: + # - production, main, master, develop, ci, cisomething + # - project/description + # - deps/* + # - feature/description, feature/123-description + # - bugfix/description, bugfix/123-description + # - release/__VERSION__, release/__VERSION__ (per https://semver.org/) + # - release/2023-04-17, release/2023-04-17.123 (date-based) + # - hotfix/__VERSION__, hotfix/__VERSION__ (per https://semver.org/) + # - hotfix/2023-04-17, hotfix/2023-04-17.123 (date-based) + only: /^(production|main|master|develop)$|^project\/[a-zA-z0-9\-\.]+|^(feature|bugfix)\/[a-zA-Z0-9\-\.\,_]+$|^ci.*|^(release|hotfix)\/[0-9]+(\.[0-9]+){2}(-rc\.[0-9]+)?$|^(release|hotfix)\/[0-9]{4}-[0-9]{2}-[0-9]{2}(\.[0-9]+)?$/ + tags: + ignore: /.*/ + - deploy-tags: + requires: + - build + filters: + branches: + ignore: /.*/ + tags: + # Allowed tags: + # - __VERSION__, __VERSION__ (per https://semver.org/) + # - 2023-04-17, 2023-04-17.123 (date-based) + only: /^[0-9]+(\.[0-9]+){2}(-rc\.[0-9]+)?$|^[0-9]{4}-[0-9]{2}-[0-9]{2}(\.[0-9]+)?$/ + + # Nightly database workflow runs overnight to capture fresh database and cache it. + nightly-db: + triggers: + - schedule: + cron: *nightly_db_schedule + filters: + branches: + only: + - develop + jobs: + - database-nightly + + # Self-hosted Renovate workflow. + update-dependencies: + triggers: + - schedule: + cron: "5 11,23 * * *" + filters: + branches: + only: + - develop + jobs: + - update-dependencies + + update-dependencies-manual: + when: << pipeline.parameters.run_update_dependencies >> + jobs: + - update-dependencies diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/.dockerignore b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/.dockerignore new file mode 100644 index 000000000..98d29c72b --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/.dockerignore @@ -0,0 +1,8 @@ +@@ -45,7 +45,6 @@ + !phpcs.xml + !phpmd.xml + !phpstan.neon +-!phpunit.xml + !rector.php + !scripts + !tests diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/.github/workflows/-build-test-deploy.yml b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/.github/workflows/-build-test-deploy.yml new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/.gitignore b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/.gitignore new file mode 100644 index 000000000..ebbf780cb --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/.gitignore @@ -0,0 +1,8 @@ +@@ -48,7 +48,6 @@ + web/themes/**/build + .data + .logs +-.phpunit.cache + .twig-cs-fixer.cache + + # Ignore local override files. diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/README.md b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/README.md new file mode 100644 index 000000000..78e06291b --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/README.md @@ -0,0 +1,9 @@ +@@ -4,7 +4,7 @@ + + Drupal 11 implementation of star wars for star wars Org + +-[![Database, Build, Test and Deploy](https://github.com/star_wars_org/star_wars/actions/workflows/build-test-deploy.yml/badge.svg)](https://github.com/star_wars_org/star_wars/actions/workflows/build-test-deploy.yml) ++[![CircleCI](https://circleci.com/gh/star_wars_org/star_wars.svg?style=shield)](https://circleci.com/gh/star_wars_org/star_wars) + + ![Drupal 11](https://img.shields.io/badge/Drupal-11-blue.svg) + [![codecov](https://codecov.io/gh/star_wars_org/star_wars/graph/badge.svg)](https://codecov.io/gh/star_wars_org/star_wars) diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/composer.json b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/composer.json new file mode 100644 index 000000000..6581e9f1f --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/composer.json @@ -0,0 +1,20 @@ +@@ -42,7 +42,6 @@ + "palantirnet/drupal-rector": "__VERSION__", + "phpcompatibility/php-compatibility": "__VERSION__", + "phpmd/phpmd": "__VERSION__", +- "phpspec/prophecy-phpunit": "__VERSION__", + "phpstan/extension-installer": "__VERSION__", + "phpstan/phpstan": "__VERSION__", + "pyrech/composer-changelogs": "__VERSION__", +@@ -63,11 +62,6 @@ + "autoload": { + "classmap": [ + "scripts/composer/ScriptHandler.php" +- ] +- }, +- "autoload-dev": { +- "classmap": [ +- "tests/phpunit/" + ] + }, + "config": { diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/docs/ci.md b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/docs/ci.md new file mode 100644 index 000000000..7d29e25c6 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/docs/ci.md @@ -0,0 +1,31 @@ +@@ -5,12 +5,12 @@ + Before feature changes can be merged into a shared mainline, a complete build + must run and pass all tests on CI server. + +-## GitHub Actions ++## Circle CI + +-This project uses [GitHub Actions](https://github.com/features/actions) as a +-CI server: it imports production backups into fully built codebase and runs +-code linting and tests. When tests pass, a deployment process is triggered for +-nominated branches (usually, `main` and `develop`). ++This project uses [Circle CI](https://circleci.com/) as a CI server: it imports ++production backups into fully built codebase and runs code linting and tests. ++When tests pass, a deployment process is triggered for nominated branches ++(usually, `main` and `develop`). + + Refer to https://www.vortextemplate.com/docs/continuous-integration for more information. + +@@ -21,9 +21,6 @@ + + ### SSH + +-GitHub Actions does not supports shell access to the build, but there is an +-action provided withing the `build` job that allows you to run a build with SSH +-support. +- +-Use "Run workflow" button in GitHub Actions UI to start build with SSH support +-that will be available for 120 minutes after the build is finished. ++Circle CI supports shell access to the build for 120 minutes after the build is ++finished when the build is started with SSH support. Use "Rerun job with SSH" ++button in Circle CI UI to start build with SSH support. diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/phpstan.neon b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/phpstan.neon new file mode 100644 index 000000000..9245e8fa5 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/phpstan.neon @@ -0,0 +1,8 @@ +@@ -38,7 +38,6 @@ + paths: + - web/modules/custom/*/tests/* + - web/themes/custom/*/tests/* +- - tests/phpunit/* + reportUnmatched: false + - + # Hook implementations do not provide docblocks for parameters, so there diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/tests/phpunit/Drupal/-DatabaseSettingsTest.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/tests/phpunit/Drupal/-DatabaseSettingsTest.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/tests/phpunit/Drupal/-EnvironmentSettingsTest.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/tests/phpunit/Drupal/-EnvironmentSettingsTest.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/tests/phpunit/Drupal/-SettingsTestCase.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/tests/phpunit/Drupal/-SettingsTestCase.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/tests/phpunit/Drupal/-SwitchableSettingsTest.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/tests/phpunit/Drupal/-SwitchableSettingsTest.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/web/modules/custom/sw_base/tests/src/Functional/-ExampleTest.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/web/modules/custom/sw_base/tests/src/Functional/-ExampleTest.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/web/modules/custom/sw_base/tests/src/Functional/-SwBaseFunctionalTestBase.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/web/modules/custom/sw_base/tests/src/Functional/-SwBaseFunctionalTestBase.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/web/modules/custom/sw_base/tests/src/Kernel/-ExampleTest.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/web/modules/custom/sw_base/tests/src/Kernel/-ExampleTest.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/web/modules/custom/sw_base/tests/src/Kernel/-SwBaseKernelTestBase.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/web/modules/custom/sw_base/tests/src/Kernel/-SwBaseKernelTestBase.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/web/modules/custom/sw_base/tests/src/Traits/-ArrayTrait.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/web/modules/custom/sw_base/tests/src/Traits/-ArrayTrait.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/web/modules/custom/sw_base/tests/src/Traits/-AssertTrait.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/web/modules/custom/sw_base/tests/src/Traits/-AssertTrait.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/web/modules/custom/sw_base/tests/src/Traits/-MockTrait.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/web/modules/custom/sw_base/tests/src/Traits/-MockTrait.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/web/modules/custom/sw_base/tests/src/Traits/-ReflectionTrait.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/web/modules/custom/sw_base/tests/src/Traits/-ReflectionTrait.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/web/modules/custom/sw_base/tests/src/Unit/-ExampleTest.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/web/modules/custom/sw_base/tests/src/Unit/-ExampleTest.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/web/modules/custom/sw_base/tests/src/Unit/-SwBaseUnitTestBase.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/web/modules/custom/sw_base/tests/src/Unit/-SwBaseUnitTestBase.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/web/sites/default/includes/providers/-settings.gha.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/web/sites/default/includes/providers/-settings.gha.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/web/sites/default/includes/providers/settings.circleci.php b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/web/sites/default/includes/providers/settings.circleci.php new file mode 100644 index 000000000..7cad35cf8 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_phpunit_circleci/web/sites/default/includes/providers/settings.circleci.php @@ -0,0 +1,17 @@ +"${t}" && set -a && . ./.env && set +a && . "${t}" && export -p >> "$BASH_ENV" + +################################################################################ +# PARAMETERS +################################################################################ + +parameters: + run_update_dependencies: + type: boolean + default: false + +################################################################################ +# JOBS +################################################################################ + +jobs: + # Database handling is a first step of the build. + # - $VORTEX_CI_DB_CACHE_TIMESTAMP is used to determine if a fresh DB dump + # should be downloaded for the current build. Usually, a daily database dump + # is sufficient for development activities. + # - $VORTEX_CI_DB_CACHE_FALLBACK is used if the cache did not match $VORTEX_CI_DB_CACHE_TIMESTAMP. + # This allows to rely on the cache from the previous days within the same branch. + database: &job-database + <<: *runner_config + steps: + - attach_workspace: + at: /tmp/workspace + + - add_ssh_keys: + fingerprints: + - *db_ssh_fingerprint + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + - *step_setup_remote_docker + + - run: + name: Create cache keys for database caching as files + command: | + echo "${VORTEX_CI_DB_CACHE_BRANCH}" | tee /tmp/db_cache_branch + echo "${VORTEX_CI_DB_CACHE_FALLBACK/no/${CIRCLE_BUILD_NUM}}" | tee /tmp/db_cache_fallback + date "${VORTEX_CI_DB_CACHE_TIMESTAMP}" | tee /tmp/db_cache_timestamp + echo "yes" | tee /tmp/db_cache_fallback_yes + + - restore_cache: + keys: + # Restore DB cache based on the cache strategy set by the cache keys below. + # https://circleci.com/docs/2.0/caching/#restoring-cache + # Change 'v1' to 'v2', 'v3' etc., commit and push to force 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). + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback" }}-{{ checksum "/tmp/db_cache_timestamp" }} + # Fallback to caching by default branch name only. Allows to use + # cache from the branch build on the previous day. + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback" }}- + + - run: + name: Download DB + command: VORTEX_DB_DOWNLOAD_SEMAPHORE=/tmp/download-db-success ./scripts/vortex/download-db.sh + no_output_timeout: 30m + + # Execute commands after database download script finished: if the + # DB dump was downloaded - build the site (to ensure that the DB dump + # is valid) and export the DB using selected method (to support + # "file-to-image" or "image-to-file" conversions). + # Note that configuration changes and the DB updates are not applied, so + # the database will be cached in the same state as downloaded. + - run: + name: Export DB after download + command: | + [ ! -f /tmp/download-db-success ] && echo "==> Database download semaphore file is missing. DB export will not proceed." && exit 0 + ./scripts/vortex/login-container-registry.sh + docker compose up --detach && sleep 15 + docker compose exec cli mkdir -p .data && docker compose cp -L .data/db.sql cli:/app/.data/db.sql || true + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c "VORTEX_PROVISION_POST_OPERATIONS_SKIP=1 ./scripts/vortex/provision.sh" + grep -q ^VORTEX_DB_IMAGE .env && rm .data/db.sql || true + ./scripts/vortex/export-db.sh db.sql + no_output_timeout: 30m + + - save_cache: + # 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. + key: __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback_yes" }}-{{ checksum "/tmp/db_cache_timestamp" }} + paths: + - /root/project/.data + + # Nightly database job. Same as above, but with additional variables set. + database-nightly: + <<: *job-database + environment: + VORTEX_DB_DOWNLOAD_SSH_FINGERPRINT: *db_ssh_fingerprint + VORTEX_DEPLOY_SSH_FINGERPRINT: *deploy_ssh_fingerprint + # Enforce fresh DB build (do not rely on fallback caches). + VORTEX_CI_DB_CACHE_FALLBACK: 'no' + # Always use fresh base image for the database (if database-in-image storage is used). + VORTEX_DB_IMAGE_BASE: drevops/mariadb-drupal-data:__VERSION__ + # Deploy container image (if database-in-image storage is used). + VORTEX_EXPORT_DB_CONTAINER_REGISTRY_DEPLOY_PROCEED: 1 + # Do not build the Drupal front-end. + VORTEX_FRONTEND_BUILD_SKIP: 1 + + # Build and test is a second step of the build. The testing is performed + # within the same job to save time on provisioning during the job. + build: &job_build + <<: *runner_config + parallelism: 2 + steps: + - attach_workspace: + at: /tmp/workspace + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + + - run: + name: Validate Composer configuration + command: composer validate --strict || [ "${VORTEX_CI_COMPOSER_VALIDATE_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Set cache keys for database caching + command: | + echo "${VORTEX_CI_DB_CACHE_BRANCH}" | tee /tmp/db_cache_branch + echo "yes" | tee /tmp/db_cache_fallback_yes + echo "$(date ${VORTEX_CI_DB_CACHE_TIMESTAMP})" | tee /tmp/db_cache_timestamp + + - restore_cache: + keys: + # Use cached artifacts from previous builds of this branch. + # https://circleci.com/docs/2.0/caching/#restoring-cache + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback_yes" }}-{{ checksum "/tmp/db_cache_timestamp" }} + - __VERSION__{{ checksum "/tmp/db_cache_branch" }}-{{ checksum "/tmp/db_cache_fallback_yes" }}- + + - *step_setup_remote_docker + + - run: + name: Login to container registry + command: ./scripts/vortex/login-container-registry.sh + + - run: + name: Lint Dockerfiles with Hadolint + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + for file in $(find .docker -name 'Dockerfile' -o -name '*.dockerfile'); do + echo "Linting ${file}" && cat "${file}" | docker run --rm -i hadolint/hadolint || [ "${VORTEX_CI_HADOLINT_IGNORE_FAILURE:-0}" -eq 1 ] + done + + - run: + name: Lint Docker Compose files with DCLint + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker run --rm -v "${PWD}":/app zavoloklom/dclint:__VERSION__ . || [ "${VORTEX_CI_DCLINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Build stack + command: docker compose up -d + + - run: + name: Export built codebase + command: | + echo "${VORTEX_DEPLOY_TYPES:-}" | grep -vq "artifact" && exit 0 || true + mkdir -p "/tmp/workspace/code" + docker compose cp -L cli:"/app/." "/tmp/workspace/code" + du -sh "/tmp/workspace/code" + + - run: + name: Install development dependencies + command: | + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c " \ + if [ -n \"${PACKAGE_TOKEN:-}\" ]; then export COMPOSER_AUTH='{\"github-oauth\": {\"github.com\": \"${PACKAGE_TOKEN-}\"}}'; fi && \ + COMPOSER_MEMORY_LIMIT=-1 composer --ansi install --prefer-dist" + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli bash -c "yarn install --frozen-lockfile" + + - run: + name: Validate Composer configuration is normalized + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli composer normalize --dry-run || [ "${VORTEX_CI_COMPOSER_NORMALIZE_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with PHPCS + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/phpcs || [ "${VORTEX_CI_PHPCS_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with PHPStan + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/phpstan || [ "${VORTEX_CI_PHPSTAN_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with PHPMD + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/phpmd . text phpmd.xml || [ "${VORTEX_CI_PHPMD_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with Twig CS Fixer + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/twig-cs-fixer || [ "${VORTEX_CI_TWIG_CS_FIXER_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint code with Gherkin Lint + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli vendor/bin/gherkinlint lint tests/behat/features || [ "${VORTEX_CI_GHERKIN_LINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint module code with NodeJS linters + command: | + [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ] && exit 0 + docker compose exec -T cli bash -c "yarn run lint" || [ "${VORTEX_CI_NODEJS_LINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Lint theme code with NodeJS linters + command: | + { [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ] && [ "${CIRCLE_NODE_INDEX:-0}" -ne 0 ]; } || [ "${VORTEX_FRONTEND_BUILD_SKIP:-0}" -eq 1 ] && exit 0 + docker compose exec -T cli bash -c "yarn --cwd=\${WEBROOT}/themes/custom/\${DRUPAL_THEME} run lint" || [ "${VORTEX_CI_NODEJS_LINT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Provision site + command: | + if [ -f .data/db.sql ]; then + docker compose exec cli mkdir -p .data + docker compose cp -L .data/db.sql cli:/app/.data/db.sql + fi + docker compose exec $(env | cut -f1 -d= | sed 's/^/-e /') -T cli ./scripts/vortex/provision.sh + no_output_timeout: 30m + + - run: + name: Test with PHPUnit + command: docker compose exec -T cli vendor/bin/phpunit || [ "${VORTEX_CI_PHPUNIT_IGNORE_FAILURE:-0}" -eq 1 ] + + - run: + name: Test with Behat + command: | + if [ "${CIRCLE_NODE_TOTAL:-1}" -gt 1 ]; then export VORTEX_CI_BEHAT_PROFILE="${VORTEX_CI_BEHAT_PROFILE:-p${CIRCLE_NODE_INDEX}}"; fi + echo "Running with ${VORTEX_CI_BEHAT_PROFILE:-default} profile" + docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ + docker compose exec -T cli php -d memory_limit=-1 vendor/bin/behat --colors --strict --rerun --profile="${VORTEX_CI_BEHAT_PROFILE:-default}" || \ + [ "${VORTEX_CI_BEHAT_IGNORE_FAILURE:-0}" -eq 1 ] + no_output_timeout: 30m + + - run: + name: Process test logs and artifacts + command: | + mkdir -p "${VORTEX_CI_TEST_RESULTS}" "${VORTEX_CI_ARTIFACTS}" + if docker compose ps --services --filter "status=running" | grep -q cli && docker compose exec cli test -d /app/.logs; then + docker compose cp cli:/app/.logs/. "${VORTEX_CI_ARTIFACTS}/" + if docker compose exec -T cli sh -c '[ -d /app/.logs/test_results/ ]'; then + docker compose cp cli:/app/.logs/test_results/. "${VORTEX_CI_TEST_RESULTS}/" + fi + fi + when: always + + - store_test_results: + path: *test_results + + - store_artifacts: + path: *artifacts + + - run: + name: Upload code coverage reports to Codecov + command: | + if [ -n "${CODECOV_TOKEN}" ] && [ -d /tmp/artifacts/coverage ] && ! echo "${CIRCLE_BRANCH}" | grep -q '^deps/'; then + codecov -Z -s /tmp/artifacts/coverage; + fi + + - persist_to_workspace: + root: /tmp/workspace + paths: + - code + + # Deploy primary branches. + deploy: &job_deploy + <<: *runner_config + steps: + - attach_workspace: + at: /tmp/workspace + + - add_ssh_keys: + fingerprints: + - *deploy_ssh_fingerprint + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + + - run: + name: Deploy + command: | + VORTEX_DEPLOY_BRANCH="${CIRCLE_BRANCH}" \ + VORTEX_DEPLOY_PR="$(echo ${CIRCLE_PULL_REQUEST} | cut -d'/' -f 7)" \ + VORTEX_DEPLOY_PR_HEAD=${CIRCLE_SHA1} \ + ./scripts/vortex/deploy.sh + no_output_timeout: 30m + + - store_artifacts: + path: *artifacts + + # Deploy tags. + deploy-tags: &job-deploy-tags + <<: *runner_config + steps: + - attach_workspace: + at: /tmp/workspace + + - add_ssh_keys: + fingerprints: + - *deploy_ssh_fingerprint + + - checkout + - *step_process_codebase_for_ci + - *load_variables_from_dotenv + + - run: + name: Deploy + command: VORTEX_DEPLOY_MODE="tag" ./scripts/vortex/deploy.sh + no_output_timeout: 30m + + - store_artifacts: + path: *artifacts + + # Self-hosted dependency updates. + # Add the following environment variables to the CircleCI project: + # - RENOVATE_TOKEN: GitHub access token. + # - RENOVATE_REPOSITORIES: Repository to run Renovate on as `vendor/repository`. + # - RENOVATE_GIT_AUTHOR: Author for Renovate commits as `Name `. + # Variables provided below can be overridden in the CircleCI project settings. + update-dependencies: + docker: + - image: renovate/renovate:__VERSION__ + environment: + RENOVATE_PLATFORM: 'github' + RENOVATE_AUTODISCOVER: false + RENOVATE_DEPENDENCY_DASHBOARD_TITLE: 'Renovate Dependency Dashboard (self-hosted) by CircleCI' + RENOVATE_DEPENDENCY_DASHBOARD: false + RENOVATE_DRY_RUN: false + LOG_LEVEL: 'debug' + + steps: + - checkout + - run: + name: Check if RENOVATE_TOKEN is set + command: | + if [ -z "${RENOVATE_TOKEN}" ]; then + echo "RENOVATE_TOKEN is not set. Skipping job." + circleci-agent step halt + fi + + if [ -z "${RENOVATE_REPOSITORIES}" ]; then + echo "Renovate repository is not set. Skipping job." + circleci-agent step halt + fi + + if [ -z "${RENOVATE_GIT_AUTHOR}" ]; then + echo "Renovate git author is not set. Skipping job." + circleci-agent step halt + fi + + - run: + name: Validate Renovate configuration + command: renovate-config-validator + + - run: + name: Run Renovate + command: renovate + +################################################################################ +# WORKFLOWS +################################################################################ + +workflows: + version: 2 + # Commit workflow. Runs for every commit push to the remote repository. + commit: + jobs: + - database: + filters: + tags: + only: /.*/ + - build: + requires: + - database + filters: + tags: + only: /.*/ + - deploy: + requires: + - build + filters: + branches: + # Allowed branches: + # - production, main, master, develop, ci, cisomething + # - project/description + # - deps/* + # - feature/description, feature/123-description + # - bugfix/description, bugfix/123-description + # - release/__VERSION__, release/__VERSION__ (per https://semver.org/) + # - release/2023-04-17, release/2023-04-17.123 (date-based) + # - hotfix/__VERSION__, hotfix/__VERSION__ (per https://semver.org/) + # - hotfix/2023-04-17, hotfix/2023-04-17.123 (date-based) + only: /^(production|main|master|develop)$|^project\/[a-zA-z0-9\-\.]+|^(feature|bugfix)\/[a-zA-Z0-9\-\.\,_]+$|^ci.*|^(release|hotfix)\/[0-9]+(\.[0-9]+){2}(-rc\.[0-9]+)?$|^(release|hotfix)\/[0-9]{4}-[0-9]{2}-[0-9]{2}(\.[0-9]+)?$/ + tags: + ignore: /.*/ + - deploy-tags: + requires: + - build + filters: + branches: + ignore: /.*/ + tags: + # Allowed tags: + # - __VERSION__, __VERSION__ (per https://semver.org/) + # - 2023-04-17, 2023-04-17.123 (date-based) + only: /^[0-9]+(\.[0-9]+){2}(-rc\.[0-9]+)?$|^[0-9]{4}-[0-9]{2}-[0-9]{2}(\.[0-9]+)?$/ + + # Nightly database workflow runs overnight to capture fresh database and cache it. + nightly-db: + triggers: + - schedule: + cron: *nightly_db_schedule + filters: + branches: + only: + - develop + jobs: + - database-nightly + + # Self-hosted Renovate workflow. + update-dependencies: + triggers: + - schedule: + cron: "5 11,23 * * *" + filters: + branches: + only: + - develop + jobs: + - update-dependencies + + update-dependencies-manual: + when: << pipeline.parameters.run_update_dependencies >> + jobs: + - update-dependencies diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_rector_circleci/.dockerignore b/.vortex/installer/tests/Fixtures/install/tools_no_rector_circleci/.dockerignore new file mode 100644 index 000000000..8c3de37a1 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_rector_circleci/.dockerignore @@ -0,0 +1,7 @@ +@@ -46,6 +46,5 @@ + !phpmd.xml + !phpstan.neon + !phpunit.xml +-!rector.php + !scripts + !tests diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_rector_circleci/.github/workflows/-build-test-deploy.yml b/.vortex/installer/tests/Fixtures/install/tools_no_rector_circleci/.github/workflows/-build-test-deploy.yml new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_rector_circleci/README.md b/.vortex/installer/tests/Fixtures/install/tools_no_rector_circleci/README.md new file mode 100644 index 000000000..78e06291b --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_rector_circleci/README.md @@ -0,0 +1,9 @@ +@@ -4,7 +4,7 @@ + + Drupal 11 implementation of star wars for star wars Org + +-[![Database, Build, Test and Deploy](https://github.com/star_wars_org/star_wars/actions/workflows/build-test-deploy.yml/badge.svg)](https://github.com/star_wars_org/star_wars/actions/workflows/build-test-deploy.yml) ++[![CircleCI](https://circleci.com/gh/star_wars_org/star_wars.svg?style=shield)](https://circleci.com/gh/star_wars_org/star_wars) + + ![Drupal 11](https://img.shields.io/badge/Drupal-11-blue.svg) + [![codecov](https://codecov.io/gh/star_wars_org/star_wars/graph/badge.svg)](https://codecov.io/gh/star_wars_org/star_wars) diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_rector_circleci/composer.json b/.vortex/installer/tests/Fixtures/install/tools_no_rector_circleci/composer.json new file mode 100644 index 000000000..da28be9b9 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_rector_circleci/composer.json @@ -0,0 +1,16 @@ +@@ -39,7 +39,6 @@ + "drupal/drupal-extension": "__VERSION__", + "ergebnis/composer-normalize": "__VERSION__", + "mglaman/phpstan-drupal": "__VERSION__", +- "palantirnet/drupal-rector": "__VERSION__", + "phpcompatibility/php-compatibility": "__VERSION__", + "phpmd/phpmd": "__VERSION__", + "phpspec/prophecy-phpunit": "__VERSION__", +@@ -46,7 +45,6 @@ + "phpstan/extension-installer": "__VERSION__", + "phpstan/phpstan": "__VERSION__", + "pyrech/composer-changelogs": "__VERSION__", +- "rector/rector": "__VERSION__", + "vincentlanglet/twig-cs-fixer": "__VERSION__" + }, + "conflict": { diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_rector_circleci/docs/ci.md b/.vortex/installer/tests/Fixtures/install/tools_no_rector_circleci/docs/ci.md new file mode 100644 index 000000000..7d29e25c6 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_rector_circleci/docs/ci.md @@ -0,0 +1,31 @@ +@@ -5,12 +5,12 @@ + Before feature changes can be merged into a shared mainline, a complete build + must run and pass all tests on CI server. + +-## GitHub Actions ++## Circle CI + +-This project uses [GitHub Actions](https://github.com/features/actions) as a +-CI server: it imports production backups into fully built codebase and runs +-code linting and tests. When tests pass, a deployment process is triggered for +-nominated branches (usually, `main` and `develop`). ++This project uses [Circle CI](https://circleci.com/) as a CI server: it imports ++production backups into fully built codebase and runs code linting and tests. ++When tests pass, a deployment process is triggered for nominated branches ++(usually, `main` and `develop`). + + Refer to https://www.vortextemplate.com/docs/continuous-integration for more information. + +@@ -21,9 +21,6 @@ + + ### SSH + +-GitHub Actions does not supports shell access to the build, but there is an +-action provided withing the `build` job that allows you to run a build with SSH +-support. +- +-Use "Run workflow" button in GitHub Actions UI to start build with SSH support +-that will be available for 120 minutes after the build is finished. ++Circle CI supports shell access to the build for 120 minutes after the build is ++finished when the build is started with SSH support. Use "Rerun job with SSH" ++button in Circle CI UI to start build with SSH support. diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_rector_circleci/tests/phpunit/CircleCiConfigTest.php b/.vortex/installer/tests/Fixtures/install/tools_no_rector_circleci/tests/phpunit/CircleCiConfigTest.php new file mode 100644 index 000000000..d07086d21 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_rector_circleci/tests/phpunit/CircleCiConfigTest.php @@ -0,0 +1,261 @@ +config = Yaml::decode($file); + } + + /** + * Tests for deploy branch regex. + * + * @see https://semver.org/ + */ + #[DataProvider('dataProviderDeployBranchRegex')] + public function testDeployBranchRegex(string $branch, bool $expected = TRUE): void { + $this->assertEquals($expected, preg_match($this->config['workflows']['commit']['jobs'][2]['deploy']['filters']['branches']['only'], $branch)); + } + + /** + * Data provider for testDeployBranchRegex(). + */ + public static function dataProviderDeployBranchRegex(): array { + return [ + // Positive branches. + ['production'], + ['main'], + ['master'], + ['develop'], + + ['ci'], + ['cisomething'], + + ['release/123.456.789'], + ['release/123.456.789-rc.123'], + ['hotfix/123.456.789'], + ['hotfix/123.456.789-rc.123'], + + ['release/2023-04-17'], + ['release/2023-04-17.1'], + ['hotfix/2023-04-17'], + ['hotfix/2023-04-17.1'], + + ['feature/description'], + ['feature/Description'], + ['feature/Description-With-Hyphens'], + ['feature/Description-With_Underscores'], + ['feature/123-description'], + ['feature/123-Description'], + ['feature/UNDERSCORES_UNDERSCORES'], + ['feature/123-Description-With_UNDERSCORES'], + ['feature/1.x'], + ['feature/0.x'], + ['feature/0.1.x'], + ['feature/0.1.2.x'], + ['feature/1.x-description'], + ['feature/0.x-description'], + ['feature/0.1.x-description'], + ['feature/0.1.2.x-description'], + + ['bugfix/description'], + ['bugfix/Description'], + ['bugfix/Description-With-Hyphens'], + ['bugfix/Description-With_Underscores'], + ['bugfix/123-description'], + ['bugfix/123-Description'], + ['bugfix/UNDERSCORES_UNDERSCORES'], + ['bugfix/123-Description-With_UNDERSCORES'], + ['bugfix/1.x'], + ['bugfix/0.x'], + ['bugfix/0.1.x'], + ['bugfix/0.1.2.x'], + ['bugfix/1.x-description'], + ['bugfix/0.x-description'], + ['bugfix/0.1.x-description'], + ['bugfix/0.1.2.x-description'], + + ['project/description'], + ['project/Description'], + ['project/Description-With-Hyphens'], + ['project/123-description'], + ['project/123-Description'], + ['project/1.x'], + ['project/0.x'], + ['project/0.1.x'], + ['project/0.1.2.x'], + ['project/1.x-description'], + ['project/0.x-description'], + ['project/0.1.x-description'], + ['project/0.1.2.x-description'], + + // Negative branches. + ['something', FALSE], + ['premain', FALSE], + ['premaster', FALSE], + ['predevelop', FALSE], + ['mainpost', FALSE], + ['masterpost', FALSE], + ['developpost', FALSE], + ['premainpost', FALSE], + ['premasterpost', FALSE], + ['predeveloppost', FALSE], + + ['preci', FALSE], + ['precipost', FALSE], + + ['deps/something', FALSE], + ['deps', FALSE], + ['predeps', FALSE], + ['depspost', FALSE], + ['predepspost', FALSE], + + ['feature', FALSE], + ['release', FALSE], + ['hotfix', FALSE], + ['prefeature', FALSE], + ['prerelease', FALSE], + ['prehotfix', FALSE], + ['featurepost', FALSE], + ['releasepost', FALSE], + ['hotfixpost', FALSE], + ['prefeaturepost', FALSE], + ['prereleasepost', FALSE], + ['prehotfixpost', FALSE], + + ['release/123', FALSE], + ['release/123.456', FALSE], + ['hotfix/123', FALSE], + ['hotfix/123.456', FALSE], + + ['release/202-04-17', FALSE], + ['release/2023-4-17', FALSE], + ['release/2023-04-1', FALSE], + ['release/pre2023-04-17', FALSE], + ['release/2023-04-17post', FALSE], + ['release/pre2023-04-17post', FALSE], + + ['hotfix/202-04-17', FALSE], + ['hotfix/2023-4-17', FALSE], + ['hotfix/2023-04-1', FALSE], + ['hotfix/pre2023-04-17', FALSE], + ['hotfix/2023-04-17post', FALSE], + ['hotfix/pre2023-04-17post', FALSE], + + ['release/123.456.789-something', FALSE], + ['release/123.456.789-rc', FALSE], + ['release/123.456.789-rc123', FALSE], + ['release/123.456.789-rc-123', FALSE], + ['release/123.456.789-prerc123', FALSE], + ['release/123.456.789-rcpost123', FALSE], + ['release/123.456.789-prercpost123', FALSE], + ['release/123.456.789-rc123something', FALSE], + ['release/123.456.789-rc.123something', FALSE], + ['release/123.456.789-rc.123-something', FALSE], + + ['hotfix/123.456.789-something', FALSE], + ['hotfix/123.456.789-rc', FALSE], + ['hotfix/123.456.789-rc123', FALSE], + ['hotfix/123.456.789-rc-123', FALSE], + ['hotfix/123.456.789-prerc123', FALSE], + ['hotfix/123.456.789-rcpost123', FALSE], + ['hotfix/123.456.789-prercpost123', FALSE], + ['hotfix/123.456.789-rc123something', FALSE], + ['hotfix/123.456.789-rc.123something', FALSE], + ['hotfix/123.456.789-rc.123-something', FALSE], + + ['prefeature/something', FALSE], + ['prefbugfix/something', FALSE], + ['prerelease/something', FALSE], + ['prehotfix/something', FALSE], + ['featurepost/something', FALSE], + ['bugfixpost/something', FALSE], + ['releasepost/something', FALSE], + ['hotfixpost/something', FALSE], + ['prefeaturepost/something', FALSE], + ['prebugfixpost/something', FALSE], + ['prereleasepost/something', FALSE], + ['prehotfixpost/something', FALSE], + ['preproject/something', FALSE], + ['projectpost/something', FALSE], + ]; + } + + /** + * Tests for deploy tag regex. + * + * @see https://semver.org/ + */ + #[DataProvider('dataProviderDeployTagRegex')] + public function testDeployTagRegex(string $branch, bool $expected = TRUE): void { + $this->assertEquals($expected, preg_match($this->config['workflows']['commit']['jobs'][3]['deploy-tags']['filters']['tags']['only'], $branch)); + } + + /** + * Data provider for testDeployTagRegex(). + */ + public static function dataProviderDeployTagRegex(): array { + return [ + // Positive tags. + ['1.2.3'], + ['1.2.3-rc.123'], + ['2023-04-17'], + ['2023-04-17.123'], + + // Negative tags. + ['123', FALSE], + ['123.456', FALSE], + ['1.2.3-rc123', FALSE], + ['1.2.3-rc.123post', FALSE], + ['1.2.3-prerc.123', FALSE], + ['1.2.3-rcpost.123', FALSE], + ['1.2.3-prercpost.123', FALSE], + + ['202-04-17', FALSE], + ['2023-0-17', FALSE], + ['2023-04-1', FALSE], + ['pre2023-04-17', FALSE], + ['2023-04-17post', FALSE], + ['pre2023-04-17post', FALSE], + ['2023-04-17.123.', FALSE], + ['2023-04-17.pre123', FALSE], + ['2023-04-17.pre123post', FALSE], + ['2023-04-17.123post', FALSE], + ]; + } + +} diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_rector_circleci/tests/phpunit/Drupal/EnvironmentSettingsTest.php b/.vortex/installer/tests/Fixtures/install/tools_no_rector_circleci/tests/phpunit/Drupal/EnvironmentSettingsTest.php new file mode 100644 index 000000000..155b2a0b2 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_rector_circleci/tests/phpunit/Drupal/EnvironmentSettingsTest.php @@ -0,0 +1,12 @@ +@@ -263,9 +263,9 @@ + } + + /** +- * Test per-environment settings for GitHub Actions. ++ * Test per-environment settings for CircleCI. + */ +- public function testEnvironmentGha(): void { ++ public function testEnvironmentCircleCi(): void { + $this->setEnvVars([ + 'CI' => TRUE, + ]); diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_rector_circleci/web/sites/default/includes/providers/-settings.gha.php b/.vortex/installer/tests/Fixtures/install/tools_no_rector_circleci/web/sites/default/includes/providers/-settings.gha.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_no_rector_circleci/web/sites/default/includes/providers/settings.circleci.php b/.vortex/installer/tests/Fixtures/install/tools_no_rector_circleci/web/sites/default/includes/providers/settings.circleci.php new file mode 100644 index 000000000..7cad35cf8 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_no_rector_circleci/web/sites/default/includes/providers/settings.circleci.php @@ -0,0 +1,17 @@ +404 Not Found

Not Found

The requested URL "@path" was not found on this server.

'; + include_once $contrib_path . '/fast404/fast404.inc'; +- // @phpstan-ignore-next-line + fast404_preboot($settings); + // @codeCoverageIgnoreEnd + } diff --git a/.vortex/installer/tests/Fixtures/install/tools_none/web/sites/default/includes/modules/settings.redis.php b/.vortex/installer/tests/Fixtures/install/tools_none/web/sites/default/includes/modules/settings.redis.php new file mode 100644 index 000000000..aaf1104b3 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_none/web/sites/default/includes/modules/settings.redis.php @@ -0,0 +1,10 @@ +@@ -8,9 +8,6 @@ + * interchangeable. We use `DRUPAL_REDIS_` environment variables as the Drupal + * module name is `redis`. + * +- * @phpcs:disable DrupalPractice.Commenting.CommentEmptyLine.SpacingAfter +- * @phpcs:disable Drupal.Commenting.InlineComment.SpacingAfter +- * @phpcs:disable Drupal.Commenting.InlineComment.InvalidEndChar + */ + + declare(strict_types=1); diff --git a/.vortex/installer/tests/Fixtures/install/tools_none/web/sites/default/settings.php b/.vortex/installer/tests/Fixtures/install/tools_none/web/sites/default/settings.php new file mode 100644 index 000000000..6c05abc0f --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_none/web/sites/default/settings.php @@ -0,0 +1,11 @@ +@@ -14,10 +14,6 @@ + * environments. + * @see https://www.vortextemplate.com/docs/drupal/settings + * +- * phpcs:disable Drupal.Commenting.InlineComment.NoSpaceBefore +- * phpcs:disable Drupal.Commenting.InlineComment.SpacingAfter +- * phpcs:disable DrupalPractice.Commenting.CommentEmptyLine.SpacingAfter +- * phpcs:disable DrupalPractice.CodeAnalysis.VariableAnalysis.UnusedVariable + */ + + declare(strict_types=1); diff --git a/.vortex/installer/tests/Fixtures/install/tools_none/web/themes/custom/star_wars/Gruntfile.js b/.vortex/installer/tests/Fixtures/install/tools_none/web/themes/custom/star_wars/Gruntfile.js new file mode 100644 index 000000000..b9f88bce5 --- /dev/null +++ b/.vortex/installer/tests/Fixtures/install/tools_none/web/themes/custom/star_wars/Gruntfile.js @@ -0,0 +1,8 @@ +@@ -5,7 +5,6 @@ + * Run `grunt` for to process with dev settings. + * Run `grunt prod` to process with prod settings. + * Run `grunt watch` to start watching with dev settings. +- * phpcs:ignoreFile + */ + + /* global module */ diff --git a/.vortex/installer/tests/Fixtures/install/tools_none/web/themes/custom/star_wars/tests/src/Functional/-ExampleTest.php b/.vortex/installer/tests/Fixtures/install/tools_none/web/themes/custom/star_wars/tests/src/Functional/-ExampleTest.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_none/web/themes/custom/star_wars/tests/src/Functional/-StarWarsFunctionalTestBase.php b/.vortex/installer/tests/Fixtures/install/tools_none/web/themes/custom/star_wars/tests/src/Functional/-StarWarsFunctionalTestBase.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_none/web/themes/custom/star_wars/tests/src/Kernel/-ExampleTest.php b/.vortex/installer/tests/Fixtures/install/tools_none/web/themes/custom/star_wars/tests/src/Kernel/-ExampleTest.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_none/web/themes/custom/star_wars/tests/src/Kernel/-StarWarsKernelTestBase.php b/.vortex/installer/tests/Fixtures/install/tools_none/web/themes/custom/star_wars/tests/src/Kernel/-StarWarsKernelTestBase.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_none/web/themes/custom/star_wars/tests/src/Unit/-ExampleTest.php b/.vortex/installer/tests/Fixtures/install/tools_none/web/themes/custom/star_wars/tests/src/Unit/-ExampleTest.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Fixtures/install/tools_none/web/themes/custom/star_wars/tests/src/Unit/-StarWarsUnitTestBase.php b/.vortex/installer/tests/Fixtures/install/tools_none/web/themes/custom/star_wars/tests/src/Unit/-StarWarsUnitTestBase.php new file mode 100644 index 000000000..e69de29bb diff --git a/.vortex/installer/tests/Functional/FunctionalTestCase.php b/.vortex/installer/tests/Functional/FunctionalTestCase.php index 7471a2c3c..92b40d15a 100644 --- a/.vortex/installer/tests/Functional/FunctionalTestCase.php +++ b/.vortex/installer/tests/Functional/FunctionalTestCase.php @@ -13,6 +13,7 @@ use DrevOps\VortexInstaller\Utils\Config; use DrevOps\VortexInstaller\Utils\Env; use DrevOps\VortexInstaller\Utils\File; +use DrevOps\VortexInstaller\Utils\Strings; use PHPUnit\Framework\TestStatus\Error; use PHPUnit\Framework\TestStatus\Failure; @@ -122,9 +123,16 @@ protected function assertSutContains(string|array $needles): void { $needles = is_array($needles) ? $needles : [$needles]; foreach ($needles as $needle) { - $this->assertDirectoryContainsWord($needle, static::$sut, [ - 'scripts/vortex', - ]); + if (Strings::isRegex($needle)) { + $this->assertDirectoryContainsString($needle, static::$sut, [ + 'scripts/vortex', + ]); + } + else { + $this->assertDirectoryContainsWord($needle, static::$sut, [ + 'scripts/vortex', + ]); + } } } @@ -132,9 +140,16 @@ protected function assertSutNotContains(string|array $needles): void { $needles = is_array($needles) ? $needles : [$needles]; foreach ($needles as $needle) { - $this->assertDirectoryNotContainsWord($needle, static::$sut, [ - 'scripts/vortex', - ]); + if (Strings::isRegex($needle)) { + $this->assertDirectoryNotContainsString($needle, static::$sut, [ + 'scripts/vortex', + ]); + } + else { + $this->assertDirectoryNotContainsWord($needle, static::$sut, [ + 'scripts/vortex', + ]); + } } } diff --git a/.vortex/installer/tests/Functional/InstallTest.php b/.vortex/installer/tests/Functional/InstallTest.php index 37897e59a..b772d075c 100644 --- a/.vortex/installer/tests/Functional/InstallTest.php +++ b/.vortex/installer/tests/Functional/InstallTest.php @@ -29,6 +29,7 @@ use DrevOps\VortexInstaller\Prompts\Handlers\Services; use DrevOps\VortexInstaller\Prompts\Handlers\Theme; use DrevOps\VortexInstaller\Prompts\Handlers\Timezone; +use DrevOps\VortexInstaller\Prompts\Handlers\Tools; use DrevOps\VortexInstaller\Prompts\Handlers\Webroot; use DrevOps\VortexInstaller\Prompts\PromptManager; use DrevOps\VortexInstaller\Utils\Config; @@ -261,6 +262,276 @@ public static function dataProviderInstall(): array { }), ], + 'tools, none' => [ + static::cw(function (): void { + Env::put(PromptManager::makeEnvName(Tools::id()), Converter::toList([])); + }), + static::cw(function (FunctionalTestCase $test): void { + $test->assertSutNotContains([ + 'phpcs', + 'phpcbf', + 'phpstan', + 'rector', + 'phpunit', + 'behat', + 'gherkinlint', + 'bdd', + 'lint-be:', + 'lint-be-fix:', + 'lint-tests:', + 'test:', + 'test-unit:', + 'test-kernel:', + 'test-functional:', + 'test-bdd:', + ]); + + $test->assertSutContains([ + '/\blint:/', + '/\blint-fe:/', + '/\blint-fix:/', + '/\blint-fe-fix:/', + ]); + }), + ], + 'tools, no phpcs' => [ + static::cw(function (): void { + $tools = array_keys(Tools::getToolDefinitions('tools')); + Env::put(PromptManager::makeEnvName(Tools::id()), Converter::toList(array_diff($tools, [Tools::PHPCS]))); + Env::put(PromptManager::makeEnvName(CiProvider::id()), CiProvider::GITHUB_ACTIONS); + }), + static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + 'phpcs', + 'phpcbf', + 'dealerdirect/phpcodesniffer-composer-installer', + 'drupal/coder', + 'squizlabs/php_codesniffer', + ])), + ], + 'tools, no phpcs, circleci' => [ + static::cw(function (): void { + $tools = array_keys(Tools::getToolDefinitions('tools')); + Env::put(PromptManager::makeEnvName(Tools::id()), Converter::toList(array_diff($tools, [Tools::PHPCS]))); + Env::put(PromptManager::makeEnvName(CiProvider::id()), CiProvider::CIRCLECI); + }), + static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + 'phpcs', + 'phpcbf', + 'dealerdirect/phpcodesniffer-composer-installer', + 'drupal/coder', + 'squizlabs/php_codesniffer', + ])), + ], + 'tools, no phpstan' => [ + static::cw(function (): void { + $tools = array_keys(Tools::getToolDefinitions('tools')); + Env::put(PromptManager::makeEnvName(Tools::id()), Converter::toList(array_diff($tools, [Tools::PHPSTAN]))); + Env::put(PromptManager::makeEnvName(CiProvider::id()), CiProvider::GITHUB_ACTIONS); + }), + static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + 'phpstan', + 'phpstan/phpstan', + 'mglaman/phpstan-drupal', + ])), + ], + 'tools, no phpstan, circleci' => [ + static::cw(function (): void { + $tools = array_keys(Tools::getToolDefinitions('tools')); + Env::put(PromptManager::makeEnvName(Tools::id()), Converter::toList(array_diff($tools, [Tools::PHPSTAN]))); + Env::put(PromptManager::makeEnvName(CiProvider::id()), CiProvider::CIRCLECI); + }), + static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + 'phpstan', + 'phpstan/phpstan', + 'mglaman/phpstan-drupal', + ])), + ], + 'tools, no rector' => [ + static::cw(function (): void { + $tools = array_keys(Tools::getToolDefinitions('tools')); + Env::put(PromptManager::makeEnvName(Tools::id()), Converter::toList(array_diff($tools, [Tools::RECTOR]))); + Env::put(PromptManager::makeEnvName(CiProvider::id()), CiProvider::GITHUB_ACTIONS); + }), + static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + 'rector', + 'rector/rector', + ])), + ], + 'tools, no rector, circleci' => [ + static::cw(function (): void { + $tools = array_keys(Tools::getToolDefinitions('tools')); + Env::put(PromptManager::makeEnvName(Tools::id()), Converter::toList(array_diff($tools, [Tools::RECTOR]))); + Env::put(PromptManager::makeEnvName(CiProvider::id()), CiProvider::CIRCLECI); + }), + static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + 'rector', + 'rector/rector', + ])), + ], + 'tools, no phpmd' => [ + static::cw(function (): void { + $tools = array_keys(Tools::getToolDefinitions('tools')); + Env::put(PromptManager::makeEnvName(Tools::id()), Converter::toList(array_diff($tools, [Tools::PHPMD]))); + Env::put(PromptManager::makeEnvName(CiProvider::id()), CiProvider::GITHUB_ACTIONS); + }), + static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + 'phpmd', + 'phpmd/phpmd', + ])), + ], + 'tools, no phpmd, circleci' => [ + static::cw(function (): void { + $tools = array_keys(Tools::getToolDefinitions('tools')); + Env::put(PromptManager::makeEnvName(Tools::id()), Converter::toList(array_diff($tools, [Tools::PHPMD]))); + Env::put(PromptManager::makeEnvName(CiProvider::id()), CiProvider::CIRCLECI); + }), + static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + 'phpmd', + 'phpmd/phpmd', + ])), + ], + 'tools, no phpunit' => [ + static::cw(function (): void { + $tools = array_keys(Tools::getToolDefinitions('tools')); + Env::put(PromptManager::makeEnvName(Tools::id()), Converter::toList(array_diff($tools, [Tools::PHPUNIT]))); + Env::put(PromptManager::makeEnvName(CiProvider::id()), CiProvider::GITHUB_ACTIONS); + }), + static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + 'phpunit', + 'ahoy test-unit', + 'ahoy test-kernel', + 'ahoy test-functional', + ])), + ], + 'tools, no phpunit, circleci' => [ + static::cw(function (): void { + $tools = array_keys(Tools::getToolDefinitions('tools')); + Env::put(PromptManager::makeEnvName(Tools::id()), Converter::toList(array_diff($tools, [Tools::PHPUNIT]))); + Env::put(PromptManager::makeEnvName(CiProvider::id()), CiProvider::CIRCLECI); + }), + static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + 'phpunit', + 'ahoy test-unit', + 'ahoy test-kernel', + 'ahoy test-functional', + ])), + ], + 'tools, no behat' => [ + static::cw(function (): void { + $tools = array_keys(Tools::getToolDefinitions('tools')); + Env::put(PromptManager::makeEnvName(Tools::id()), Converter::toList(array_diff($tools, [Tools::BEHAT]))); + Env::put(PromptManager::makeEnvName(CiProvider::id()), CiProvider::GITHUB_ACTIONS); + }), + static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + 'behat', + 'behat/behat', + 'drupal/drupal-extension', + 'ahoy test-bdd', + 'gherkinlint', + 'gherkin-lint', + 'gherkin', + 'bdd', + ])), + ], + 'tools, no behat, circleci' => [ + static::cw(function (): void { + $tools = array_keys(Tools::getToolDefinitions('tools')); + Env::put(PromptManager::makeEnvName(Tools::id()), Converter::toList(array_diff($tools, [Tools::BEHAT]))); + Env::put(PromptManager::makeEnvName(CiProvider::id()), CiProvider::CIRCLECI); + }), + static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + 'behat', + 'behat/behat', + 'drupal/drupal-extension', + 'ahoy test-bdd', + 'gherkinlint', + 'gherkin-lint', + 'gherkin', + ])), + ], + 'tools, groups, no be lint' => [ + static::cw(function (): void { + $tools = array_keys(Tools::getToolDefinitions('tools')); + Env::put(PromptManager::makeEnvName(Tools::id()), Converter::toList(array_diff($tools, [Tools::PHPCS, Tools::PHPMD, Tools::PHPSTAN, Tools::RECTOR]))); + Env::put(PromptManager::makeEnvName(CiProvider::id()), CiProvider::GITHUB_ACTIONS); + }), + static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + 'phpcs', + 'phpcbf', + 'dealerdirect/phpcodesniffer-composer-installer', + 'drupal/coder', + 'squizlabs/php_codesniffer', + 'phpmd', + 'phpmd/phpmd', + 'phpstan', + 'phpstan/phpstan', + 'mglaman/phpstan-drupal', + 'rector', + 'rector/rector', + ])), + ], + 'tools, groups, no be lint, circleci' => [ + static::cw(function (): void { + $tools = array_keys(Tools::getToolDefinitions('tools')); + Env::put(PromptManager::makeEnvName(Tools::id()), Converter::toList(array_diff($tools, [Tools::PHPCS, Tools::PHPMD, Tools::PHPSTAN, Tools::RECTOR]))); + Env::put(PromptManager::makeEnvName(CiProvider::id()), CiProvider::CIRCLECI); + }), + static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + 'phpcs', + 'phpcbf', + 'dealerdirect/phpcodesniffer-composer-installer', + 'drupal/coder', + 'squizlabs/php_codesniffer', + 'phpmd', + 'phpmd/phpmd', + 'phpstan', + 'phpstan/phpstan', + 'mglaman/phpstan-drupal', + 'rector', + 'rector/rector', + ])), + ], + 'tools, groups, no be tests' => [ + static::cw(function (): void { + $tools = array_keys(Tools::getToolDefinitions('tools')); + Env::put(PromptManager::makeEnvName(Tools::id()), Converter::toList(array_diff($tools, [Tools::PHPUNIT, Tools::BEHAT]))); + Env::put(PromptManager::makeEnvName(CiProvider::id()), CiProvider::GITHUB_ACTIONS); + }), + static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + 'phpunit', + 'ahoy test-unit', + 'ahoy test-kernel', + 'ahoy test-functional', + 'behat', + 'behat/behat', + 'drupal/drupal-extension', + 'ahoy test-bdd', + 'gherkinlint', + 'gherkin-lint', + 'gherkin', + ])), + ], + 'tools, groups, no be tests, circleci' => [ + static::cw(function (): void { + $tools = array_keys(Tools::getToolDefinitions('tools')); + Env::put(PromptManager::makeEnvName(Tools::id()), Converter::toList(array_diff($tools, [Tools::PHPUNIT, Tools::BEHAT]))); + Env::put(PromptManager::makeEnvName(CiProvider::id()), CiProvider::CIRCLECI); + }), + static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + 'phpunit', + 'ahoy test-unit', + 'ahoy test-kernel', + 'ahoy test-functional', + 'behat', + 'behat/behat', + 'drupal/drupal-extension', + 'ahoy test-bdd', + 'gherkinlint', + 'gherkin-lint', + 'gherkin', + ])), + ], + 'hosting, acquia' => [ static::cw(function (): void { Env::put(PromptManager::makeEnvName(HostingProvider::id()), HostingProvider::ACQUIA); @@ -411,7 +682,12 @@ public static function dataProviderInstall(): array { protected function assertCommon(): void { $this->assertDirectoryEqualsDirectory(static::$root . '/scripts/vortex', static::$sut . '/scripts/vortex', 'Vortex scripts were not modified.'); - $this->assertFileEquals(static::$root . '/tests/behat/fixtures/image.jpg', static::$sut . '/tests/behat/fixtures/image.jpg', 'Binary files were not modified.'); + if (file_exists(static::$root . '/scripts/vortex.yml')) { + $this->assertFileEquals(static::$root . '/tests/behat/fixtures/image.jpg', static::$sut . '/tests/behat/fixtures/image.jpg', 'Binary files were not modified.'); + } + + $this->assertYamlFileIsValid('.ahoy.yml'); + $this->assertJsonFileIsValid('composer.json'); } protected static function defaultAnswers(): array { diff --git a/.vortex/installer/tests/Unit/FileTest.php b/.vortex/installer/tests/Unit/FileTest.php index 5ce516977..a304f49fa 100644 --- a/.vortex/installer/tests/Unit/FileTest.php +++ b/.vortex/installer/tests/Unit/FileTest.php @@ -32,129 +32,4 @@ public static function dataProviderIsInternal(): array { ]; } - #[DataProvider('dataProviderCollapseYamlEmptyLinesInLiteralBlocks')] - public function testCollapseYamlEmptyLinesInLiteralBlocks(string $input, string $expected): void { - $actual = File::collapseYamlEmptyLinesInLiteralBlocks($input); - $this->assertSame($expected, $actual); - } - - public static function dataProviderCollapseYamlEmptyLinesInLiteralBlocks(): array { - return [ - 'empty string' => [ - '', - '', - ], - 'no literal blocks' => [ - << [ - << [ - << [ - << [ - << [ - << [ - << [ - << static::TUI_DEFAULT, Timezone::id() => static::TUI_DEFAULT, Services::id() => static::TUI_DEFAULT, + Tools::id() => static::TUI_DEFAULT, HostingProvider::id() => static::TUI_DEFAULT, Webroot::id() => static::TUI_DEFAULT, DeployType::id() => static::TUI_DEFAULT, @@ -189,6 +192,7 @@ public static function dataProviderRunPrompts(): array { Theme::id() => 'myproject', Timezone::id() => 'UTC', Services::id() => [Services::CLAMAV, Services::SOLR, Services::VALKEY], + Tools::id() => [Tools::PHPCS, Tools::PHPMD, Tools::PHPSTAN, Tools::RECTOR, Tools::PHPUNIT, Tools::BEHAT], HostingProvider::id() => HostingProvider::NONE, Webroot::id() => Webroot::WEB, DeployType::id() => [DeployType::WEBHOOK], @@ -206,6 +210,7 @@ public static function dataProviderRunPrompts(): array { // Expected values for a pre-installed project. $expected_installed = [ + Tools::id() => [], CiProvider::id() => CiProvider::NONE, DependencyUpdatesProvider::id() => DependencyUpdatesProvider::NONE, AssignAuthorPr::id() => FALSE, @@ -616,7 +621,6 @@ function (PromptManagerTest $test): void { [Services::id() => Key::ENTER], [Services::id() => [Services::CLAMAV, Services::SOLR, Services::VALKEY]] + $expected_defaults, ], - 'services - discovery - solr' => [ [], [Services::id() => [Services::SOLR]] + $expected_installed, @@ -679,6 +683,98 @@ function (PromptManagerTest $test, Config $config): void { }, ], + 'tools - prompt - defaults' => [ + [Tools::id() => Key::ENTER], + [Tools::id() => [Tools::PHPCS, Tools::PHPMD, Tools::PHPSTAN, Tools::RECTOR, Tools::PHPUNIT, Tools::BEHAT]] + $expected_defaults, + ], + 'tools - discovery - all tools' => [ + [], + [Tools::id() => [Tools::BEHAT, Tools::PHPCS, Tools::PHPMD, Tools::PHPSTAN, Tools::PHPUNIT, Tools::RECTOR]] + $expected_installed, + function (PromptManagerTest $test, Config $config): void { + $test->stubVortexProject($config); + $dependencies = [ + 'squizlabs/php_codesniffer' => '*', + 'phpmd/phpmd' => '*', + 'phpstan/phpstan' => '*', + 'rector/rector' => '*', + 'phpunit/phpunit' => '*', + 'behat/behat' => '*', + ]; + $test->stubComposerJsonDependencies($dependencies, TRUE); + }, + ], + 'tools - discovery - none' => [ + [], + [Tools::id() => []] + $expected_installed, + function (PromptManagerTest $test, Config $config): void { + $test->stubVortexProject($config); + // No tool dependencies in composer.json. + }, + ], + 'tools - discovery - non-Vortex project' => [ + [], + $expected_defaults, + function (PromptManagerTest $test, Config $config): void { + $dependencies = [ + 'squizlabs/php_codesniffer' => '*', + 'phpmd/phpmd' => '*', + 'phpstan/phpstan' => '*', + 'rector/rector' => '*', + 'phpunit/phpunit' => '*', + 'behat/behat' => '*', + ]; + $test->stubComposerJsonDependencies($dependencies, TRUE); + }, + ], + 'tools - discovery - phpcs' => [ + [], + [Tools::id() => [Tools::PHPCS]] + $expected_installed, + function (PromptManagerTest $test, Config $config): void { + $test->stubVortexProject($config); + $test->stubComposerJsonDependencies(['squizlabs/php_codesniffer' => '*'], TRUE); + }, + ], + 'tools - discovery - phpmd' => [ + [], + [Tools::id() => [Tools::PHPMD]] + $expected_installed, + function (PromptManagerTest $test, Config $config): void { + $test->stubVortexProject($config); + $test->stubComposerJsonDependencies(['phpmd/phpmd' => '*'], TRUE); + }, + ], + 'tools - discovery - phpstan' => [ + [], + [Tools::id() => [Tools::PHPSTAN]] + $expected_installed, + function (PromptManagerTest $test, Config $config): void { + $test->stubVortexProject($config); + $test->stubComposerJsonDependencies(['phpstan/phpstan' => '*'], TRUE); + }, + ], + 'tools - discovery - rector' => [ + [], + [Tools::id() => [Tools::RECTOR]] + $expected_installed, + function (PromptManagerTest $test, Config $config): void { + $test->stubVortexProject($config); + $test->stubComposerJsonDependencies(['rector/rector' => '*'], TRUE); + }, + ], + 'tools - discovery - phpunit' => [ + [], + [Tools::id() => [Tools::PHPUNIT]] + $expected_installed, + function (PromptManagerTest $test, Config $config): void { + $test->stubVortexProject($config); + $test->stubComposerJsonDependencies(['phpunit/phpunit' => '*'], TRUE); + }, + ], + 'tools - discovery - behat' => [ + [], + [Tools::id() => [Tools::BEHAT]] + $expected_installed, + function (PromptManagerTest $test, Config $config): void { + $test->stubVortexProject($config); + $test->stubComposerJsonDependencies(['behat/behat' => '*'], TRUE); + }, + ], + 'hosting provider - prompt' => [ [HostingProvider::id() => Key::ENTER], [HostingProvider::id() => HostingProvider::NONE] + $expected_defaults, @@ -765,7 +861,7 @@ function (PromptManagerTest $test, Config $config): void { [], [Webroot::id() => 'discovered_webroot'] + $expected_defaults, function (PromptManagerTest $test, Config $config): void { - $test->stubComposerJsonValue('extra', ['drupal-scaffold' => ['drupal-scaffold' => ['locations' => ['web-root' => 'discovered_webroot']]]]); + $test->stubComposerJsonValue('extra', ['drupal-scaffold' => ['locations' => ['web-root' => 'discovered_webroot']]]); }, ], 'webroot - discovery - invalid' => [ @@ -1144,6 +1240,30 @@ protected function stubComposerJsonValue(string $name, mixed $value): string { return $composer_json; } + protected function stubComposerJsonDependencies(array $dependencies, bool $is_dev = FALSE): string { + $composer_json = static::$sut . DIRECTORY_SEPARATOR . 'composer.json'; + $section = $is_dev ? 'require-dev' : 'require'; + + $data = []; + if (file_exists($composer_json)) { + $contents = file_get_contents($composer_json); + $existing = $contents !== FALSE ? json_decode($contents, TRUE) : NULL; + if ($existing) { + $data = $existing; + } + } + + if (!isset($data[$section])) { + $data[$section] = []; + } + + $data[$section] = array_merge($data[$section], $dependencies); + + file_put_contents($composer_json, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + + return $composer_json; + } + protected function stubDotenvValue(string $name, mixed $value, string $filename = '.env'): string { $dotenv = static::$sut . DIRECTORY_SEPARATOR . $filename; diff --git a/.vortex/installer/tests/Unit/StringsTest.php b/.vortex/installer/tests/Unit/StringsTest.php index edf3785ac..0f3f3904d 100644 --- a/.vortex/installer/tests/Unit/StringsTest.php +++ b/.vortex/installer/tests/Unit/StringsTest.php @@ -45,4 +45,403 @@ public static function dataProviderStrlenPlain(): array { ]; } + #[DataProvider('dataProviderCollapsePhpBlockCommentsEmptyLines')] + public function testCollapsePhpBlockCommentsEmptyLines(string $input, string $expected): void { + $actual = Strings::collapsePhpBlockCommentsEmptyLines($input); + $this->assertEquals($expected, $actual); + } + + public static function dataProviderCollapsePhpBlockCommentsEmptyLines(): array { + return [ + 'empty_string' => [ + '', + '', + ], + 'no_docblock_comments' => [ + <<<'PHP' + [ + '/** Single line comment */', + '/** Single line comment */', + ], + 'docblock_no_empty_lines' => [ + <<<'PHP' + /** + * Description here. + * @param string $param + * @return void + */ + PHP, + <<<'PHP' + /** + * Description here. + * @param string $param + * @return void + */ + PHP, + ], + 'docblock_with_two_consecutive_empty_lines' => [ + <<<'PHP' + /** + * Description here. + * + * + * @param string $param + */ + PHP, + <<<'PHP' + /** + * Description here. + * + * @param string $param + */ + PHP, + ], + 'docblock_with_multiple_consecutive_empty_lines' => [ + <<<'PHP' + /** + * Description here. + * + * + * + * + * @param string $param + */ + PHP, + <<<'PHP' + /** + * Description here. + * + * @param string $param + */ + PHP, + ], + 'multiple_docblocks_with_empty_lines' => [ + <<<'PHP' + /** + * First docblock. + * + * + * @param string $param + */ + function first() {} + + /** + * Second docblock. + * + * + * + * @return void + */ + function second() {} + PHP, + <<<'PHP' + /** + * First docblock. + * + * @param string $param + */ + function first() {} + + /** + * Second docblock. + * + * @return void + */ + function second() {} + PHP, + ], + 'docblock_with_whitespace_in_empty_lines' => [ + <<<'PHP' + /** + * Description here. + * + * + * @param string $param + */ + PHP, + <<<'PHP' + /** + * Description here. + * + * @param string $param + */ + PHP, + ], + 'mixed_docblocks_and_regular_comments' => [ + <<<'PHP' + /** + * Docblock comment. + * + * + * @param string $param + */ + function test() { + /* Regular comment + with multiple lines + should not be affected */ + return 'test'; + } + PHP, + <<<'PHP' + /** + * Docblock comment. + * + * @param string $param + */ + function test() { + /* Regular comment + with multiple lines + should not be affected */ + return 'test'; + } + PHP, + ], + 'docblock_at_beginning_and_end' => [ + <<<'PHP' + /** + * File docblock. + * + * + * @file + */ + + function test() {} + + /** + * End docblock. + * + * + * @return void + */ + PHP, + <<<'PHP' + /** + * File docblock. + * + * @file + */ + + function test() {} + + /** + * End docblock. + * + * @return void + */ + PHP, + ], + 'empty_lines_at_start_of_docblock' => [ + <<<'PHP' + /** + * + * + * Description here. + * @param string $param + */ + PHP, + <<<'PHP' + /** + * Description here. + * @param string $param + */ + PHP, + ], + 'empty_lines_at_end_of_docblock' => [ + <<<'PHP' + /** + * Description here. + * @param string $param + * + * + */ + PHP, + <<<'PHP' + /** + * Description here. + * @param string $param + */ + PHP, + ], + 'entirely_empty_docblock' => [ + <<<'PHP' + /** + * + * + * + */ + function test() {} + PHP, + <<<'PHP' + function test() {} + PHP, + ], + 'docblock_with_only_whitespace' => [ + <<<'PHP' + /** + * + * + * + */ + class Test {} + PHP, + <<<'PHP' + class Test {} + PHP, + ], + 'mixed_empty_and_content_docblocks' => [ + <<<'PHP' + /** + * + * + */ + + /** + * Real content here. + * @param string $param + */ + function test() {} + PHP, + <<<'PHP' + + /** + * Real content here. + * @param string $param + */ + function test() {} + PHP, + ], + 'docblock_with_leading_and_trailing_empty_lines' => [ + <<<'PHP' + /** + * + * + * Description here. + * + * + * @param string $param + * + * + */ + PHP, + <<<'PHP' + /** + * Description here. + * + * @param string $param + */ + PHP, + ], + 'docblock_with_custom_indentation' => [ + <<<'PHP' + /** + * Description here. + * + * + * + * @param string $param + */ + PHP, + <<<'PHP' + /** + * Description here. + * + * @param string $param + */ + PHP, + ], + 'docblock_with_deeper_indentation' => [ + <<<'PHP' + /** + * Description here. + * + * + * + * @param string $param + */ + PHP, + <<<'PHP' + /** + * Description here. + * + * @param string $param + */ + PHP, + ], + 'docblock_with_tabs_indentation' => [ + "/**\n\t * Description here.\n\t *\n\t *\n\t *\n\t * @param string \$param\n\t */", + "/**\n\t * Description here.\n\t *\n\t * @param string \$param\n\t */", + ], + 'json_string_with_docblock_pattern_should_not_be_modified' => [ + <<<'JSON' + { + "lint-css": "stylelint --allow-empty-input \"web/modules/custom/**/*.css\"", + "description": "Some /** comment */ in JSON" + } + JSON, + <<<'JSON' + { + "lint-css": "stylelint --allow-empty-input \"web/modules/custom/**/*.css\"", + "description": "Some /** comment */ in JSON" + } + JSON, + ], + 'inline_docblock_pattern_should_not_be_modified' => [ + 'const foo = "/** some comment */"; // Not a real docblock', + 'const foo = "/** some comment */"; // Not a real docblock', + ], + ]; + } + + #[DataProvider('dataProviderIsRegex')] + public function testIsRegex(string $value, mixed $expected): void { + $this->assertEquals($expected, Strings::isRegex($value)); + } + + public static function dataProviderIsRegex(): array { + return [ + ['', FALSE], + + // Valid regular expressions. + ["/^[a-z]$/", TRUE], + ["#[a-z]*#i", TRUE], + + // Invalid regular expressions (wrong delimiters or syntax). + ["{\\d+}", FALSE], + ["(\\d+)", FALSE], + ["<[A-Z]{3,6}>", FALSE], + ["^[a-z]$", FALSE], + ["/[a-z", FALSE], + ["[a-z]+/", FALSE], + ["{[a-z]*", FALSE], + ["(a-z]", FALSE], + + // Edge cases. + // Valid, but '*' as delimiter would be invalid. + ["/a*/", TRUE], + // Empty string. + ["", FALSE], + // Just delimiters, no pattern. + ["//", FALSE], + + ['web/', FALSE], + ['web\/', FALSE], + [': web', FALSE], + ['=web', FALSE], + ['!web', FALSE], + ['/web', FALSE], + ]; + } + } diff --git a/.vortex/installer/tests/Unit/UnitTestCase.php b/.vortex/installer/tests/Unit/UnitTestCase.php index 4b0beccf1..5ae958c44 100644 --- a/.vortex/installer/tests/Unit/UnitTestCase.php +++ b/.vortex/installer/tests/Unit/UnitTestCase.php @@ -10,6 +10,7 @@ use AlexSkrypnyk\PhpunitHelpers\Traits\SerializableClosureTrait; use AlexSkrypnyk\PhpunitHelpers\UnitTestCase as UpstreamUnitTestCase; use DrevOps\VortexInstaller\Utils\File; +use DrevOps\VortexInstaller\Utils\Yaml; /** * Class UnitTestCase. @@ -112,4 +113,24 @@ protected static function replaceVersionsInLine(string $content): string { return $content; } + protected function assertYamlFileIsValid(string $filename): void { + try { + Yaml::validateFile($filename); + } + catch (\Exception $exception) { + $this->fail(sprintf('YAML validation for file %s failed: %s', $filename, $exception->getMessage())); + } + } + + protected function assertJsonFileIsValid(string $filename): void { + $this->assertFileExists($filename); + + $content = file_get_contents($filename); + if ($content === FALSE) { + $this->fail(sprintf('Failed to read JSON file "%s".', $filename)); + } + + $this->assertJson($content, sprintf('JSON validation for file %s failed: %s', $filename, json_last_error_msg())); + } + } diff --git a/.vortex/installer/tests/Unit/YamlTest.php b/.vortex/installer/tests/Unit/YamlTest.php new file mode 100644 index 000000000..5c898c9a9 --- /dev/null +++ b/.vortex/installer/tests/Unit/YamlTest.php @@ -0,0 +1,491 @@ +expectException(\Exception::class); + $this->expectExceptionMessage($expected_exception_message); + } + + $temp_file = tempnam(sys_get_temp_dir(), 'yaml_test_'); + file_put_contents($temp_file, $yaml_content); + + Yaml::validateFile($temp_file); + + if ($expected_exception_message === '' || $expected_exception_message === '0') { + $this->addToAssertionCount(1); + } + } + + public function testValidateFileNonExistent(): void { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('File does not exist or is not readable'); + + $non_existent_file = sys_get_temp_dir() . '/non_existent_file.yml'; + Yaml::validateFile($non_existent_file); + } + + public static function dataProviderValidateFile(): array { + return [ + 'valid YAML file' => [ + << [ + <<expectException(\Exception::class); + $this->expectExceptionMessage($expected_exception_message); + } + + Yaml::validate($content); + + if ($expected_exception_message === '' || $expected_exception_message === '0') { + $this->addToAssertionCount(1); + } + } + + public static function dataProviderValidate(): array { + return [ + 'valid simple YAML' => [ + << [ + << [''], + 'valid YAML with arrays' => [ + << [ + << [ + << ['key: "unclosed string', 'Malformed inline YAML string'], + 'invalid YAML syntax' => [ + << [ + <<<'YAML' +key: + value1 + value2 +YAML, 'A YAML file cannot contain tabs', + ], + ]; + } + + #[DataProvider('dataProviderCollapseEmptyLinesInLiteralBlock')] + public function testCollapseEmptyLinesInLiteralBlock(string $input, string $expected): void { + $result = Yaml::collapseEmptyLinesInLiteralBlock($input); + $this->assertEquals($expected, $result); + } + + public static function dataProviderCollapseEmptyLinesInLiteralBlock(): array { + return [ + 'no literal blocks' => [ + << [ + << [ + << [ + << [ + << [ + << [ + << [ + << [ + << [ + << [ + << DRUPAL_THEME + + lint-tests: + usage: Lint tests code. + cmd: | + ahoy cli vendor/bin/gherkinlint lint tests/behat/features +YAML, + << DRUPAL_THEME + + lint-tests: + usage: Lint tests code. + cmd: | + ahoy cli vendor/bin/gherkinlint lint tests/behat/features +YAML, + ], + ]; + } + + #[DataProvider('dataProviderCollapseFirstEmptyLinesInLiteralBlock')] + public function testCollapseFirstEmptyLinesInLiteralBlock(string $input, string $expected): void { + $result = Yaml::collapseFirstEmptyLinesInLiteralBlock($input); + $this->assertEquals($expected, $result); + } + + public static function dataProviderCollapseFirstEmptyLinesInLiteralBlock(): array { + return [ + 'no empty lines' => [ + << [ + << [ + << [ + << TOOL_BEHAT) + ## What should I do to switch to a "clean" branch environment? Provided that your stack is already running: @@ -131,9 +135,13 @@ This theme will be used when Drupal is in maintenance mode. If `DRUPAL_MAINTENAN The maintenance theme should be a valid Drupal theme that is already installed and enabled on your site. +[//]: # (#;< TOOL_BEHAT) + ## Behat tests with `@javascript` tag sometimes get stuck Behat tests with `@javascript` tag sometimes get stuck for about 10min then fail. The Chrome container randomly get stuck for an unknown reason. Restart the Chrome container: `docker compose restart chrome` + +[//]: # (#;> TOOL_BEHAT) diff --git a/phpcs.xml b/phpcs.xml index 1340b0cd2..d413c6f59 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -52,8 +52,10 @@ *.test + *\/tests\/behat\/bootstrap/*\.php + diff --git a/web/sites/default/includes/providers/settings.container.php b/web/sites/default/includes/providers/settings.container.php index 96d0ac9cc..4c85649c5 100644 --- a/web/sites/default/includes/providers/settings.container.php +++ b/web/sites/default/includes/providers/settings.container.php @@ -20,6 +20,6 @@ ], $vortex_localdev_url); $settings['trusted_host_patterns'][] = '^' . $patterns . '$'; - // URL when accessed from Behat tests. + // URL for internal container access (e.g., via drush, in tests etc.). $settings['trusted_host_patterns'][] = '^nginx$'; }