diff --git a/.gitea/workflows/pr-version-check.yml b/.gitea/workflows/pr-version-check.yml new file mode 100644 index 0000000..3f5a2d8 --- /dev/null +++ b/.gitea/workflows/pr-version-check.yml @@ -0,0 +1,40 @@ +name: version-changelog + +on: + pull_request: + branches: [main] + +jobs: + version-changelog: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Check version bump and changelog entry + run: | + set -e + PR_VERSION=$(grep -m1 '"version"' composer.json | sed -E 's/.*"version"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/') + MAIN_VERSION=$(git show origin/main:composer.json | grep -m1 '"version"' | sed -E 's/.*"version"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/') + + echo "main version: $MAIN_VERSION" + echo "PR version: $PR_VERSION" + + if [ "$MAIN_VERSION" = "$PR_VERSION" ]; then + echo "::error::composer.json version ($PR_VERSION) was not bumped from main ($MAIN_VERSION)" + exit 1 + fi + + HIGHEST=$(printf '%s\n%s\n' "$MAIN_VERSION" "$PR_VERSION" | sort -V | tail -n1) + if [ "$HIGHEST" != "$PR_VERSION" ]; then + echo "::error::composer.json version ($PR_VERSION) is not greater than main's ($MAIN_VERSION)" + 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: version bumped to $PR_VERSION and documented in CHANGELOG.md" diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..d941600 --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,100 @@ +name: release + +on: + push: + branches: [main] + +env: + SERVER_URL: https://git.arthurerlich.de + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.RELEASE_TOKEN }} + + - name: Determine version and guard against re-release + id: version + run: | + set -e + VERSION=$(grep -m1 '"version"' composer.json | sed -E 's/.*"version"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/') + TAG="v${VERSION}" + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" + echo "tag=${TAG}" >> "$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." + else + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + + - name: Extract changelog section + if: steps.version.outputs.skip == 'false' + run: | + awk -v ver="${{ steps.version.outputs.version }}" ' + BEGIN { found=0 } + /^## \[/ { + if (found) exit + 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' + 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 }}" + + - 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' + 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") + cat /tmp/composer-publish.log + if [ "$STATUS" != "201" ] && [ "$STATUS" != "409" ]; then + echo "Unexpected HTTP status $STATUS publishing to the Composer registry" + exit 1 + fi diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f678bd..783a80c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Filter the dump list by date range and sort by date or size; dates use Shopware's locale-aware formatting. +## [0.0.2] - 2026-09-21 + +### Added + +- Automated release pipeline: a pre-merge check enforcing a semver version bump and a matching + CHANGELOG entry on every PR into `main`, and a post-merge workflow that tags the release, + packages the plugin with `shopware-cli`, publishes a Gitea Release with the packaged zip, and + publishes the package to this Gitea instance's Composer registry. +- `main` is now a protected branch: no direct pushes, merges require the version/changelog check + to pass. + ## [0.0.1] - 2026-09-15 ### Added