name: plugin-checks # Plugin-agnostic: nothing below names this plugin. Name, version and paths come from composer.json. # shopware-cli runs from its official Docker image, intentionally unpinned (latest); a red build # after a CLI release means fix the config. on: pull_request: branches: [main] permissions: contents: read # A new push to the PR cancels the superseded run. concurrency: group: plugin-checks-${{ github.ref }} cancel-in-progress: true jobs: # Every PR into main must bump the version and document it in CHANGELOG.md. version-changelog: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 # The version must be higher than the latest *published release* (not just higher than main), so main can # carry a version whose release failed or was withdrawn without blocking the next PR. - name: Check version is unreleased and changelog entry exists env: TOKEN: ${{ secrets.RELEASE_TOKEN }} run: | set -e PR_VERSION=$(grep -m1 '"version"' composer.json | sed -E 's/.*"version"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/') LATEST=$(curl -sf -H "Authorization: token ${TOKEN}" "${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/releases?limit=100" \ | jq -r '.[].tag_name' | sed 's/^v//' | sort -V | tail -n1) LATEST="${LATEST:-0.0.0}" echo "latest release: $LATEST" echo "PR version: $PR_VERSION" if [ "$PR_VERSION" = "$LATEST" ] || [ "$(printf '%s\n%s\n' "$LATEST" "$PR_VERSION" | sort -V | tail -n1)" != "$PR_VERSION" ]; then echo "::error::composer.json version ($PR_VERSION) is not greater than the latest release ($LATEST)" exit 1 fi if ! grep -qF "## [$PR_VERSION]" CHANGELOG.md; then echo "::error::CHANGELOG.md is missing a '## [$PR_VERSION]' heading" exit 1 fi echo "OK: $PR_VERSION is unreleased and documented in CHANGELOG.md" # Packages the extension (builds admin/storefront assets) and validates the resulting zip, # which is the artifact that ships. A separate directory validate would only repeat these checks. package: runs-on: ubuntu-latest container: image: ghcr.io/shopware/shopware-cli:latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: ./.gitea/actions/package # The release workflow reuses this tested zip when main ends up with the same tree # (it falls back to packaging again otherwise). - name: Tree hash of the packaged commit id: tree run: echo "hash=$(git rev-parse "${GITHUB_SHA}^{tree}")" >> "$GITHUB_OUTPUT" - name: Save tested zip for the release uses: actions/cache/save@v4 with: path: .build/package.zip key: plugin-zip-${{ steps.tree.outputs.hash }} phpunit: runs-on: ubuntu-latest # Same image as above: it already ships PHP, Composer and Node, so no apt install. container: image: ghcr.io/shopware/shopware-cli:latest services: database: # Shopware 6.6 supports MariaDB 10.11 / MySQL 8; 11.4 broke Shopware's own migrations. image: mariadb:10.11 env: MARIADB_ROOT_PASSWORD: root env: # Already the *_test name: Shopware's TestBootstrapper would otherwise append _test and run every migration a second time. DATABASE_URL: mysql://root:root@database:3306/shopware_test APP_ENV: test APP_URL: http://localhost APP_SECRET: ci-secret-not-for-production INSTANCE_ID: ci COMPOSER_MEMORY_LIMIT: '-1' COMPOSER_ALLOW_SUPERUSER: '1' steps: - uses: actions/checkout@v4 with: path: plugin-src # The cache key follows the Shopware version: a new release inside the plugin's supported range changes # the key, so the old project + database are never reused. Bump -v1 to force a refresh by hand. - name: Resolve the latest Shopware Version id: sw run: | set -e # The plugin's own constraint (require.shopware/core, e.g. ~6.6.0) decides which versions are acceptable; # Composer lists the matching ones newest first, the first stable one is the latest. CONSTRAINT=$(php -r 'echo json_decode(file_get_contents("plugin-src/composer.json"), true)["require"]["shopware/core"];') VERSION=$(composer show shopware/core "$CONSTRAINT" --all --format=json --no-interaction 2>/dev/null \ | php -r '$d = json_decode(stream_get_contents(STDIN), true); foreach ($d["versions"] as $v) { if (preg_match("/^v?\d+(\.\d+)+$/", $v)) { echo ltrim($v, "v"); exit; } }') test -n "$VERSION" || { echo "::error::could not resolve a Shopware version for ${CONSTRAINT}"; exit 1; } echo "Shopware ${VERSION} (constraint ${CONSTRAINT})" echo "version=${VERSION}" >> "$GITHUB_OUTPUT" echo "constraint=${CONSTRAINT}" >> "$GITHUB_OUTPUT" # Project (vendor/, phpunit, config) and the installed database as a SQL dump. Neither depends on the plugin. - name: Restore Shopware project and database id: shopware uses: actions/cache/restore@v4 with: path: | shopware !shopware/var/cache !shopware/var/log shopware-db.sql.gz key: shopware-${{ steps.sw.outputs.version }}-installed-v1 - name: Create Shopware project if: steps.shopware.outputs.cache-hit != 'true' run: | set -e # shopware/production pins one exact core version and is not tagged for every core patch # (e.g. core 6.6.10.31, template only up to 6.6.10.30): take the newest template inside the # constraint, then require the resolved core version on top of it. composer create-project "shopware/production:${{ steps.sw.outputs.constraint }}" shopware --no-interaction --no-scripts --no-install composer require "shopware/core:${{ steps.sw.outputs.version }}" --no-update -d shopware composer require --dev phpunit/phpunit:^10 --no-update -d shopware composer update -W --no-interaction --no-scripts -d shopware - name: Ensure the MariaDB client run: | command -v mariadb-dump >/dev/null || command -v mysqldump >/dev/null && exit 0 (apt-get update -q && apt-get install -y -q --no-install-recommends mariadb-client) || apk add --no-cache mariadb-client - name: Wait for the database run: | for i in $(seq 60); do (echo > /dev/tcp/database/3306) 2>/dev/null && exit 0 sleep 2 done echo "::error::database service did not come up"; exit 1 - name: Relax database durability (throwaway CI database, speeds up the install and the restore) run: | php -r '$p = new PDO("mysql:host=database;port=3306", "root", "root"); $p->exec("SET GLOBAL innodb_flush_log_at_trx_commit=0"); $p->exec("SET GLOBAL sync_binlog=0");' # One install into shopware_test; the bootstrapper then sees an installed DB, skips its own install and # only activates the plugin (tests/TestBootstrap.php forces plugin installation). - name: Install Shopware if: steps.shopware.outputs.cache-hit != 'true' working-directory: shopware run: bin/console system:install --basic-setup --create-database --force --no-interaction - name: Dump the installed database if: steps.shopware.outputs.cache-hit != 'true' run: | set -e DUMP=$(command -v mariadb-dump || command -v mysqldump) "$DUMP" -h database -P 3306 -u root -proot --skip-ssl --single-transaction --routines --triggers \ --databases shopware_test | gzip > shopware-db.sql.gz ls -l shopware-db.sql.gz - name: Save Shopware project and database if: steps.shopware.outputs.cache-hit != 'true' uses: actions/cache/save@v4 with: path: | shopware !shopware/var/cache !shopware/var/log shopware-db.sql.gz key: shopware-${{ steps.sw.outputs.version }}-installed-v1 - name: Restore the database from the cache if: steps.shopware.outputs.cache-hit == 'true' run: | set -e CLIENT=$(command -v mariadb || command -v mysql) gunzip -c shopware-db.sql.gz | "$CLIENT" -h database -P 3306 -u root -proot --skip-ssl - name: Resolve plugin name and install the plugin sources run: | # plugin directory name = short class name of extra.shopware-plugin-class CLASS=$(php -r 'echo json_decode(file_get_contents("plugin-src/composer.json"), true)["extra"]["shopware-plugin-class"];') PLUGIN_NAME="${CLASS##*\\}" echo "PLUGIN_NAME=${PLUGIN_NAME}" >> "$GITHUB_ENV" rm -rf "shopware/custom/plugins/${PLUGIN_NAME}" cp -r plugin-src "shopware/custom/plugins/${PLUGIN_NAME}" - name: Run phpunit working-directory: shopware run: vendor/bin/phpunit -c "custom/plugins/${PLUGIN_NAME}/phpunit.xml"