From a551034442d8d7015a8d1d4ba5efa72cecca681b Mon Sep 17 00:00:00 2001 From: ArthurErlich Date: Sat, 3 Oct 2026 23:28:15 +0200 Subject: [PATCH] ci: plugin-agnostic PR checks and package-then-release pipeline - plugin-checks workflow: shopware-cli validate, package + built-JS assertion, phpunit - release: validate, package from the merge commit, validate the zip, create the release via the Gitea API, publish the same zip to the Composer registry - scripts/protect-main.sh sets required status checks via tea api - bump to 0.1.0 Co-Authored-By: Claude Sonnet 5.5 --- .gitea/workflows/plugin-checks.yml | 100 ++++++++++++++++ .gitea/workflows/pr-version-check.yml | 3 + .gitea/workflows/release.yml | 128 +++++++++++---------- CHANGELOG.md | 17 +++ composer.json | 2 +- docs/spec/shopware-guideline-compliance.md | 11 +- scripts/protect-main.sh | 17 +++ 7 files changed, 212 insertions(+), 66 deletions(-) create mode 100644 .gitea/workflows/plugin-checks.yml create mode 100644 scripts/protect-main.sh diff --git a/.gitea/workflows/plugin-checks.yml b/.gitea/workflows/plugin-checks.yml new file mode 100644 index 0000000..53b3ca2 --- /dev/null +++ b/.gitea/workflows/plugin-checks.yml @@ -0,0 +1,100 @@ +name: plugin-checks + +# Plugin-agnostic: nothing below names this plugin. Name, version and paths come from composer.json. +# shopware-cli is intentionally unpinned (latest); a red build after a CLI release means fix the config. + +on: + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + - uses: actions/setup-node@v4 + with: + node-version: '20' + - name: Install shopware-cli + run: | + curl -sL https://github.com/shopware/shopware-cli/releases/latest/download/shopware-cli_Linux_x86_64.tar.gz | tar -xz -C /tmp shopware-cli + sudo install /tmp/shopware-cli /usr/local/bin/shopware-cli || install /tmp/shopware-cli /usr/local/bin/shopware-cli + - name: Validate extension + run: shopware-cli extension validate . + + package: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-node@v4 + with: + node-version: '20' + - name: Install shopware-cli + run: | + curl -sL https://github.com/shopware/shopware-cli/releases/latest/download/shopware-cli_Linux_x86_64.tar.gz | tar -xz -C /tmp shopware-cli + sudo install /tmp/shopware-cli /usr/local/bin/shopware-cli || install /tmp/shopware-cli /usr/local/bin/shopware-cli + - name: Package extension + run: shopware-cli extension package . --output-directory .build --filename package.zip --git-commit "$GITHUB_SHA" + - name: Validate packaged zip + run: shopware-cli extension validate .build/package.zip + - name: Assert built JS is in the zip + run: | + set -e + LIST=$(unzip -Z1 .build/package.zip) + if [ -f src/Resources/app/administration/src/main.js ] || [ -f src/Resources/app/administration/src/main.ts ]; then + echo "$LIST" | grep -q 'Resources/public/administration/js/' || { echo "::error::admin entry exists but the zip has no built admin JS"; exit 1; } + fi + if [ -f src/Resources/app/storefront/src/main.js ] || [ -f src/Resources/app/storefront/src/main.ts ]; then + echo "$LIST" | grep -q 'Resources/app/storefront/dist/' || { echo "::error::storefront entry exists but the zip has no built storefront JS"; exit 1; } + fi + + phpunit: + runs-on: ubuntu-latest + services: + database: + image: mariadb:11.4 + env: + MARIADB_ROOT_PASSWORD: root + options: >- + --health-cmd="healthcheck.sh --connect --innodb_initialized" + --health-interval=5s --health-timeout=5s --health-retries=20 + env: + DATABASE_URL: mysql://root:root@database:3306/shopware + APP_ENV: test + APP_URL: http://localhost + steps: + - uses: actions/checkout@v4 + with: + path: plugin-src + - uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + extensions: gd, intl, pdo_mysql, zip, curl, mbstring, dom, xml, fileinfo, sodium, bcmath + tools: composer + - name: Resolve plugin name + run: | + # plugin directory name = short class name of extra.shopware-plugin-class + CLASS=$(jq -r '.extra["shopware-plugin-class"]' plugin-src/composer.json) + echo "PLUGIN_NAME=${CLASS##*\\}" >> "$GITHUB_ENV" + - name: Create Shopware 6.6 project + run: composer create-project shopware/production:6.6.* shopware --no-interaction --no-scripts + - name: Install Shopware and the plugin + working-directory: shopware + run: | + set -e + composer require --dev phpunit/phpunit:^10 --no-interaction + bin/console system:install --basic-setup --create-database --force --no-interaction + cp -r ../plugin-src "custom/plugins/${PLUGIN_NAME}" + bin/console plugin:refresh + bin/console plugin:install --activate "${PLUGIN_NAME}" + - name: Run phpunit + working-directory: shopware + run: vendor/bin/phpunit -c "custom/plugins/${PLUGIN_NAME}/phpunit.xml" diff --git a/.gitea/workflows/pr-version-check.yml b/.gitea/workflows/pr-version-check.yml index 3f5a2d8..271dbc1 100644 --- a/.gitea/workflows/pr-version-check.yml +++ b/.gitea/workflows/pr-version-check.yml @@ -4,6 +4,9 @@ on: pull_request: branches: [main] +permissions: + contents: read + jobs: version-changelog: runs-on: ubuntu-latest diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index ed1e8bb..5750cf7 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -1,11 +1,16 @@ name: release +# Plugin-agnostic: owner, repo, host, package name and version come from the `github` context +# and composer.json. shopware-cli is intentionally unpinned (latest). +# RELEASE_TOKEN is the single token used for releases, assets and the Composer registry. + on: push: branches: [main] -env: - SERVER_URL: https://git.arthurerlich.de +permissions: + contents: write + packages: write jobs: release: @@ -17,15 +22,19 @@ jobs: token: ${{ secrets.RELEASE_TOKEN }} - name: Determine version and guard against re-release - id: version + id: meta run: | set -e - VERSION=$(grep -m1 '"version"' composer.json | sed -E 's/.*"version"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/') - PACKAGE_NAME=$(grep -m1 '"name"' composer.json | sed -E 's/.*"name"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/') + VERSION=$(jq -r .version composer.json) + PACKAGE_NAME=$(jq -r .name composer.json) + REPO_NAME="${GITHUB_REPOSITORY#*/}" TAG="v${VERSION}" - echo "version=${VERSION}" >> "$GITHUB_OUTPUT" - echo "package_name=${PACKAGE_NAME}" >> "$GITHUB_OUTPUT" - echo "tag=${TAG}" >> "$GITHUB_OUTPUT" + { + echo "version=${VERSION}" + echo "package_name=${PACKAGE_NAME}" + echo "tag=${TAG}" + echo "zip=${REPO_NAME}-${TAG}.zip" + } >> "$GITHUB_OUTPUT" if git ls-remote --tags origin "refs/tags/${TAG}" | grep -q "${TAG}"; then echo "skip=true" >> "$GITHUB_OUTPUT" echo "Tag ${TAG} already exists, nothing to release." @@ -33,68 +42,66 @@ jobs: echo "skip=false" >> "$GITHUB_OUTPUT" fi - - name: Extract changelog section - if: steps.version.outputs.skip == 'false' + - uses: actions/setup-node@v4 + if: steps.meta.outputs.skip == 'false' + with: + node-version: '20' + + - name: Install shopware-cli + if: steps.meta.outputs.skip == 'false' run: | - awk -v ver="${{ steps.version.outputs.version }}" ' + curl -sL https://github.com/shopware/shopware-cli/releases/latest/download/shopware-cli_Linux_x86_64.tar.gz | tar -xz -C /tmp shopware-cli + sudo install /tmp/shopware-cli /usr/local/bin/shopware-cli || install /tmp/shopware-cli /usr/local/bin/shopware-cli + + - name: Validate extension + if: steps.meta.outputs.skip == 'false' + run: shopware-cli extension validate . + + - name: Package extension (prebuilt admin/storefront assets) + if: steps.meta.outputs.skip == 'false' + run: | + shopware-cli extension package . --output-directory .build \ + --filename "${{ steps.meta.outputs.zip }}" --git-commit "$GITHUB_SHA" + + - name: Validate packaged zip + if: steps.meta.outputs.skip == 'false' + run: shopware-cli extension validate ".build/${{ steps.meta.outputs.zip }}" + + - name: Extract changelog section + if: steps.meta.outputs.skip == 'false' + run: | + awk -v ver="${{ steps.meta.outputs.version }}" ' BEGIN { found=0 } /^## \[/ { if (found) exit - if ($0 ~ "\[" ver "\]") { found=1; next } + if ($0 ~ "\\[" ver "\\]") { found=1; next } } found { print } ' CHANGELOG.md > release-notes.md cat release-notes.md - - name: Create and push tag - if: steps.version.outputs.skip == 'false' + - name: Create Gitea release (creates the tag) and upload the zip + if: steps.meta.outputs.skip == 'false' + env: + TOKEN: ${{ secrets.RELEASE_TOKEN }} run: | - git config user.name "gitea-actions" - git config user.email "actions@git.arthurerlich.de" - git tag "${{ steps.version.outputs.tag }}" - git push origin "${{ steps.version.outputs.tag }}" + set -e + API="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}" + BODY=$(jq -n --arg tag "${{ steps.meta.outputs.tag }}" --arg sha "$GITHUB_SHA" --rawfile notes release-notes.md \ + '{tag_name:$tag, target_commitish:$sha, name:$tag, body:$notes}') + RELEASE_ID=$(curl -sf -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" -d "$BODY" "${API}/releases" | jq -r .id) + echo "release id: ${RELEASE_ID}" + curl -sf -H "Authorization: token ${TOKEN}" \ + -F "attachment=@.build/${{ steps.meta.outputs.zip }}" \ + "${API}/releases/${RELEASE_ID}/assets?name=${{ steps.meta.outputs.zip }}" > /dev/null - - name: Install shopware-cli - if: steps.version.outputs.skip == 'false' - run: | - curl -sL -o /tmp/shopware-cli.tar.gz \ - https://github.com/shopware/shopware-cli/releases/latest/download/shopware-cli_Linux_x86_64.tar.gz - mkdir -p /tmp/swcli-extract - tar -xzf /tmp/shopware-cli.tar.gz -C /tmp/swcli-extract - cp /tmp/swcli-extract/shopware-cli /usr/local/bin/shopware-cli - - - name: Validate extension (informational only, does not gate the release) - if: steps.version.outputs.skip == 'false' - run: shopware-cli extension validate . || true - - - name: Package Store-style zip for the Gitea Release asset - if: steps.version.outputs.skip == 'false' - run: | - shopware-cli extension package . --output-directory .build \ - --filename "AeonDumpManager-${{ steps.version.outputs.tag }}.zip" - - - name: Build root-level zip for the Composer registry - if: steps.version.outputs.skip == 'false' - run: git archive --format=zip -o "composer-package-${{ steps.version.outputs.tag }}.zip" "${{ steps.version.outputs.tag }}" - - - name: Create Gitea Release - if: steps.version.outputs.skip == 'false' - uses: akkuman/gitea-release-action@v1 - with: - server_url: ${{ env.SERVER_URL }} - token: ${{ secrets.RELEASE_TOKEN }} - tag_name: ${{ steps.version.outputs.tag }} - name: ${{ steps.version.outputs.tag }} - body_path: release-notes.md - files: .build/AeonDumpManager-${{ steps.version.outputs.tag }}.zip - - - name: Publish to Composer registry - if: steps.version.outputs.skip == 'false' + - name: Publish the same zip to the Composer registry + if: steps.meta.outputs.skip == 'false' run: | STATUS=$(curl -s -o /tmp/composer-publish.log -w "%{http_code}" \ - -u "haylan:${{ secrets.RELEASE_TOKEN }}" \ - -T "composer-package-${{ steps.version.outputs.tag }}.zip" \ - "https://git.arthurerlich.de/api/packages/haylan/composer") + -u "${GITHUB_REPOSITORY_OWNER}:${{ secrets.RELEASE_TOKEN }}" \ + -T ".build/${{ steps.meta.outputs.zip }}" \ + "${GITHUB_SERVER_URL}/api/packages/${GITHUB_REPOSITORY_OWNER}/composer") cat /tmp/composer-publish.log if [ "$STATUS" != "201" ] && [ "$STATUS" != "409" ]; then echo "Unexpected HTTP status $STATUS publishing to the Composer registry" @@ -102,10 +109,11 @@ jobs: fi - name: Link Composer package to this repository - if: steps.version.outputs.skip == 'false' + if: steps.meta.outputs.skip == 'false' run: | - ENCODED_NAME=$(printf '%s' "${{ steps.version.outputs.package_name }}" | sed 's#/#%2F#g') - STATUS=$(curl -s -o /dev/null -w "%{http_code}" -u "haylan:${{ secrets.RELEASE_TOKEN }}" -X POST "https://git.arthurerlich.de/api/v1/packages/haylan/composer/${ENCODED_NAME}/-/link/AeonDumpManager") + ENCODED_NAME=$(printf '%s' "${{ steps.meta.outputs.package_name }}" | sed 's#/#%2F#g') + STATUS=$(curl -s -o /dev/null -w "%{http_code}" -u "${GITHUB_REPOSITORY_OWNER}:${{ secrets.RELEASE_TOKEN }}" -X POST \ + "${GITHUB_SERVER_URL}/api/v1/packages/${GITHUB_REPOSITORY_OWNER}/composer/${ENCODED_NAME}/-/link/${GITHUB_REPOSITORY#*/}") echo "link status: $STATUS" if [ "$STATUS" != "201" ] && [ "$STATUS" != "400" ]; then echo "::warning::Unexpected status linking the Composer package to this repository (HTTP $STATUS) - not fatal, release already published" diff --git a/CHANGELOG.md b/CHANGELOG.md index d15f8cb..dde9226 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.1.0] - 2026-10-03 + +### Changed + +- Services and routes are now configured in YAML; the plugin requires PHP `^8.2`. +- Release pipeline: the packaged `shopware-cli` zip (with prebuilt assets) is validated and used for both the Gitea Release and the Composer registry; the release is created through the Gitea API after packaging, so a failed build no longer leaves an orphan tag. +- New PR checks: `shopware-cli` validate, package build and phpunit. + +### Added + +- Dedicated `aeon_dump_manager` monolog channel; dump jobs, deletions and purges are logged. +- Extension icon and `.config/shopware-extension.yml`. + +### Removed + +- Empty plugin lifecycle overrides. + ## [0.0.5] - 2026-09-21 ### Fixed diff --git a/composer.json b/composer.json index 2e2d71a..a677ca4 100755 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "description": "Export database dumps for Shopware 6 environments.", "type": "shopware-platform-plugin", "license": "MIT", - "version": "0.0.5", + "version": "0.1.0", "keywords": [ "shopware", "shopware6", diff --git a/docs/spec/shopware-guideline-compliance.md b/docs/spec/shopware-guideline-compliance.md index 23692b9..56e56aa 100644 --- a/docs/spec/shopware-guideline-compliance.md +++ b/docs/spec/shopware-guideline-compliance.md @@ -31,13 +31,14 @@ on branches `research/shopware-guideline-checklist`, `research/shopware-cli-ci-b ## 3. Pipeline -### PR workflow (`.gitea/workflows/pr.yml`, replaces `pr-version-check.yml`) +### PR workflows Jobs (all required checks on `main`): -1. `version-changelog` - unchanged logic (version bump + changelog heading). Keep this job/workflow name so the existing required context `version-changelog / version-changelog (pull_request)` keeps working, or update protection when renamed. -2. `validate` - install `shopware-cli`, `extension validate .` (hard gate, no `|| true`). -3. `package` - `extension package` with `--git-commit "$GITHUB_SHA"`, then assert the zip contains built admin JS (`Resources/public/administration/js`) when an admin entry exists. -4. `phpunit` - `composer create-project shopware/production` (6.6.x) + MariaDB service, symlink plugin, install/activate, run phpunit. PHP 8.3. +`pr-version-check.yml` (job `version-changelog`) is unchanged apart from `permissions:`; its name keeps the existing required context. New `plugin-checks.yml`: + +1. `validate` - install `shopware-cli`, `extension validate .` (hard gate, no `|| true`). +2. `package` - `extension package` with `--git-commit "$GITHUB_SHA"`, then assert the zip contains built admin JS (`Resources/public/administration/js`) when an admin entry exists. +3. `phpunit` - `composer create-project shopware/production` (6.6.x) + MariaDB service, copy the plugin into `custom/plugins`, install/activate, run phpunit. PHP 8.3. ### Release workflow (`.gitea/workflows/release.yml`) diff --git a/scripts/protect-main.sh b/scripts/protect-main.sh new file mode 100644 index 0000000..5303a53 --- /dev/null +++ b/scripts/protect-main.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# One-time (re-runnable) setup: require the PR checks on `main` using the Gitea `tea` CLI. +# `tea branches protect` cannot set status-check contexts, hence `tea api`. +# Usage: scripts/protect-main.sh [owner/repo] (default: derived from the git remote) +set -euo pipefail + +REPO="${1:-$(git config --get remote.origin.url | sed -E 's#^.*[:/]([^/]+/[^/]+)$#\1#; s#\.git$##')}" + +tea api -X PATCH "repos/${REPO}/branch_protections/main" -d '{ + "enable_status_check": true, + "status_check_contexts": [ + "version-changelog / version-changelog (pull_request)", + "plugin-checks / validate (pull_request)", + "plugin-checks / package (pull_request)", + "plugin-checks / phpunit (pull_request)" + ] +}'