diff --git a/.github/workflows/release_from_version.yml b/.github/workflows/release_from_version.yml index be555c5..c882db4 100644 --- a/.github/workflows/release_from_version.yml +++ b/.github/workflows/release_from_version.yml @@ -2,277 +2,64 @@ name: Release from Version Branch Pipeline on: workflow_dispatch: + inputs: + source_branch: + description: "Source dev branch. Example dev/03.06.00" + required: true permissions: contents: write - pull-requests: write - issues: write jobs: - meta: - name: Derive version metadata from branch + rename_branch: + name: 01 Promote dev to version runs-on: ubuntu-latest outputs: - branch: ${{ steps.meta.outputs.branch }} - version: ${{ steps.meta.outputs.version }} - is_prerelease: ${{ steps.meta.outputs.is_prerelease }} + version: ${{ steps.extract.outputs.version }} + version_branch: ${{ steps.extract.outputs.version_branch }} steps: - - name: Determine branch and version - id: meta + - name: Extract Version from Branch Name + id: extract run: | - BRANCH="${GITHUB_REF_NAME}" + set -euo pipefail - echo "Running on branch: ${BRANCH}" + SRC="${{ inputs.source_branch }}" - if [[ ! "${BRANCH}" =~ ^version\/[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9._]+)?$ ]]; then - echo "This workflow must be run on a branch named version/X.Y.Z or version/X.Y.Z-suffix" - exit 1 - fi + echo "$SRC" | grep -E '^dev/[0-9]+\.[0-9]+\.[0-9]+$' - VERSION="${BRANCH#version/}" + VERSION="${SRC#dev/}" + VERSION_BRANCH="version/$VERSION" - echo "Detected version: ${VERSION}" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "version_branch=$VERSION_BRANCH" >> "$GITHUB_OUTPUT" - if [[ "${VERSION}" =~ -(alpha|beta|rc|pre|preview|dev|test) ]]; then - echo "Version is prerelease: ${VERSION}" - IS_PRERELEASE="true" - else - echo "Version is stable: ${VERSION}" - IS_PRERELEASE="false" - fi - - echo "branch=${BRANCH}" >> "$GITHUB_OUTPUT" - echo "version=${VERSION}" >> "$GITHUB_OUTPUT" - echo "is_prerelease=${IS_PRERELEASE}" >> "$GITHUB_OUTPUT" - - build-and-test: - name: Build and test (sanity check) - runs-on: ubuntu-latest - needs: meta - - steps: - - name: Check out version branch + - name: Checkout Source Branch uses: actions/checkout@v4 with: - ref: ${{ needs.meta.outputs.branch }} - - - name: Set up PHP - uses: shivammathur/setup-php@v2 - with: - php-version: "8.2" - coverage: none - - - name: PHP lint under src (if present) - run: | - if [ -d "src" ]; then - echo "Running php -l against PHP files in src/" - find src -type f -name "*.php" -print0 | xargs -0 -n 1 -P 4 php -l - else - echo "No src directory found. Skipping PHP lint." - fi - - - name: Install dependencies if composer.json exists - run: | - if [ -f "composer.json" ]; then - composer install --no-interaction --no-progress --prefer-dist - else - echo "No composer.json found. Skipping composer install." - fi - - - name: Run Composer tests when defined - run: | - if [ ! -f "composer.json" ]; then - echo "No composer.json. Nothing to test." - exit 0 - fi - - if composer run -q | grep -q "^ test"; then - echo "Detected composer script 'test'. Running composer test." - composer test - else - echo "No 'test' script defined in composer.json. Skipping tests." - fi - - changelog: - name: Update CHANGELOG.md on version branch - runs-on: ubuntu-latest - needs: [meta, build-and-test] - - steps: - - name: Check out version branch with history - uses: actions/checkout@v4 - with: - ref: ${{ needs.meta.outputs.branch }} + ref: ${{ inputs.source_branch }} fetch-depth: 0 - - name: Fetch main for comparison - run: | - git fetch origin main - - - name: Update CHANGELOG using script - env: - VERSION: ${{ needs.meta.outputs.version }} - run: | - if [ ! -f "scripts/update_changelog.sh" ]; then - echo "ERROR: scripts/update_changelog.sh not found" - exit 1 - fi - - chmod +x scripts/update_changelog.sh - ./scripts/update_changelog.sh "${VERSION}" - - - name: Commit CHANGELOG.md if changed - env: - VERSION: ${{ needs.meta.outputs.version }} + - name: Configure Git Identity run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - if git diff --quiet; then - echo "No changelog changes to commit." - exit 0 - fi - - git add CHANGELOG.md - git commit -m "chore: update changelog for ${VERSION}" - git push origin HEAD - - pr-merge-release: - name: PR, conditional squash, and GitHub release - runs-on: ubuntu-latest - needs: [meta, changelog] - - steps: - - name: Check out version branch - uses: actions/checkout@v4 - with: - ref: ${{ needs.meta.outputs.branch }} - fetch-depth: 0 - - - name: Verify branch has commits ahead of main + - name: Promote dev branch to version branch run: | - git fetch origin main + set -euo pipefail - AHEAD_COUNT=$(git rev-list --count origin/main..HEAD) - echo "Commits ahead of main: ${AHEAD_COUNT}" + SRC="${{ inputs.source_branch }}" + DST="${{ steps.extract.outputs.version_branch }}" - if [ "${AHEAD_COUNT}" -eq 0 ]; then - echo "ERROR: No commits between main and ${GITHUB_REF_NAME}." - echo "Action required: commit changes to the version branch before running this workflow." + git fetch origin + + if git show-ref --verify --quiet "refs/remotes/origin/$DST"; then + echo "ERROR: $DST already exists." exit 1 fi - - name: Ensure standard PR labels exist - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - echo "Ensuring standard labels exist" - gh label create "release" --color "0E8A16" --description "Release related PR" || echo "Label 'release' already exists" - gh label create "version-update" --color "5319E7" --description "Version bump and release PR" || echo "Label 'version-update' already exists" - - - name: Create or reuse PR from version branch to main - id: pr - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BRANCH: ${{ needs.meta.outputs.branch }} - VERSION: ${{ needs.meta.outputs.version }} - run: | - echo "Ensuring PR exists for ${BRANCH} -> main" - - PR_NUMBER=$(gh pr list --head "${BRANCH}" --base "main" --state open --json number -q '.[0].number' || true) - - if [ -z "${PR_NUMBER}" ]; then - echo "No existing open PR found. Creating PR." - PR_URL=$(gh pr create --base "main" --head "${BRANCH}" --title "Merge version ${VERSION} into main" --body "Automated PR to merge version ${VERSION} into main.") - PR_NUMBER=$(gh pr view "${PR_URL}" --json number -q '.number') - - echo "Applying standard labels (non-blocking)" - gh pr edit "${PR_NUMBER}" --add-label "release" || echo "Label 'release' not found or cannot be applied" - gh pr edit "${PR_NUMBER}" --add-label "version-update" || echo "Label 'version-update' not found or cannot be applied" - else - echo "Found existing PR #${PR_NUMBER}" - fi - - echo "pr_number=${PR_NUMBER}" >> "$GITHUB_OUTPUT" - - - name: Squash merge PR into main (stable only) - if: needs.meta.outputs.is_prerelease == 'false' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - REPO: ${{ github.repository }} - VERSION: ${{ needs.meta.outputs.version }} - PR_NUMBER: ${{ steps.pr.outputs.pr_number }} - run: | - if [ -z "${PR_NUMBER}" ]; then - echo "No pull request number returned. Cannot squash merge." - exit 1 - fi - - echo "Performing squash merge PR #${PR_NUMBER} into main" - - MERGE_PAYLOAD=$(jq -n --arg method "squash" --arg title "Squash merge version ${VERSION} into main" '{"merge_method": $method, "commit_title": $title}') - - curl -sS -X PUT -H "Authorization: Bearer ${GITHUB_TOKEN}" -H "Accept: application/vnd.github+json" "https://api.github.com/repos/${REPO}/pulls/${PR_NUMBER}/merge" -d "${MERGE_PAYLOAD}" - - - name: Skip squash (prerelease detected) - if: needs.meta.outputs.is_prerelease == 'true' - run: | - echo "Prerelease version detected. PR created but squash merge intentionally skipped." - - - name: Create GitHub Release (stable and prerelease) and attach ZIP from src - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - VERSION: ${{ needs.meta.outputs.version }} - IS_PRERELEASE: ${{ needs.meta.outputs.is_prerelease }} - run: | - PRERELEASE_FLAG="false" - if [ "${IS_PRERELEASE}" = "true" ]; then - PRERELEASE_FLAG="true" - fi - - echo "Building ZIP from src for version ${VERSION}" - - REPO_NAME="${GITHUB_REPOSITORY##*/}" - ASSET_NAME="${REPO_NAME}-${VERSION}.zip" - - if [ ! -d "src" ]; then - echo "ERROR: src directory does not exist. Cannot build release ZIP." - exit 1 - fi - - mkdir -p dist - cd src - zip -r "../dist/${ASSET_NAME}" . - cd .. - - echo "Preparing GitHub release for ${VERSION} (prerelease=${PRERELEASE_FLAG}) with asset dist/${ASSET_NAME}" - - if gh release view "${VERSION}" >/dev/null 2>&1; then - echo "Release ${VERSION} already exists. Uploading asset." - gh release upload "${VERSION}" "dist/${ASSET_NAME}" --clobber - else - ARGS=( - "${VERSION}" - "dist/${ASSET_NAME}" - --title - "Version ${VERSION}" - --notes - "Release generated from branch version/${VERSION}." - ) - - if [ "${PRERELEASE_FLAG}" = "true" ]; then - ARGS+=(--prerelease) - fi - - gh release create "${ARGS[@]}" - fi - - - name: Optional delete version branch after merge (stable only) - if: needs.meta.outputs.is_prerelease == 'false' - env: - BRANCH: ${{ needs.meta.outputs.branch }} - run: | - echo "Deleting branch ${BRANCH} after squash merge and release" - git push origin --delete "${BRANCH}" || echo "Branch already deleted or cannot delete" + git checkout -B "$DST" "origin/$SRC" + git push origin "$DST" + git push origin --delete "$SRC"