name: Release # Fires when a v* tag is pushed — including tags created through Gitea's # "New Release" UI. Builds the distributable plugin zip and attaches it to # the release for that tag (creating the release if only a bare tag was # pushed). The attached zip is what UpdateChecker serves to WordPress # sites as the update package. on: push: tags: - 'v*' jobs: release: name: Build and Publish Release runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: '8.3' tools: composer:v2 # A tag that disagrees with the plugin header would make sites see a # phantom update forever (or never see a real one), so fail fast. - name: Verify tag matches plugin version id: meta run: | tag_version="${GITHUB_REF_NAME#v}" header_version="$(sed -nE 's/^[[:space:]]*\*?[[:space:]]*Version:[[:space:]]*([^[:space:]]+).*/\1/p' unsupervised-schedular.php | head -1)" if [ "$tag_version" != "$header_version" ]; then echo "Tag ${GITHUB_REF_NAME} does not match plugin header Version: ${header_version}" >&2 exit 1 fi echo "version=${header_version}" >> "$GITHUB_OUTPUT" - name: Install dependencies run: composer install --prefer-dist --no-progress --no-interaction - name: Run tests run: composer test - name: Build plugin zip run: composer build # Pull the section for this version out of CHANGELOG.md so it can become # the release body. Matches "## [x.y.z]" and prints every line up to the # next "## " heading. A missing section is a warning, not a failure — the # release still publishes with empty notes. - name: Extract changelog notes run: | version="${{ steps.meta.outputs.version }}" awk -v ver="$version" ' $0 ~ "^## \\[" ver "\\]" { found = 1; next } found && /^## / { exit } found { print } ' CHANGELOG.md | sed -e '/./,$!d' | tac | sed -e '/./,$!d' | tac > release-notes.md if [ ! -s release-notes.md ]; then echo "::warning::No CHANGELOG.md section found for version ${version}" fi - name: Publish release with zip asset env: TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | api="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}" version="${{ steps.meta.outputs.version }}" zip="dist/unsupervised-schedular-${version}.zip" # Pre-release versions (1.2.3-rc.1) are flagged so Gitea's # /releases/latest endpoint — and therefore the update checker — # skips them. prerelease=false case "$version" in *-*) prerelease=true ;; esac # Reuse the release if the tag was created via Gitea's release UI. release_id="$(curl -sS -H "Authorization: token ${TOKEN}" \ "${api}/releases/tags/${GITHUB_REF_NAME}" | jq -r '.id // empty' || true)" if [ -z "$release_id" ]; then body="$(jq -Rs --arg tag "${GITHUB_REF_NAME}" --argjson pre "${prerelease}" \ '{tag_name:$tag, name:$tag, prerelease:$pre, body:.}' release-notes.md)" release_id="$(curl -fsS -X POST "${api}/releases" \ -H "Authorization: token ${TOKEN}" \ -H 'Content-Type: application/json' \ -d "${body}" | jq -r '.id')" else # Release pre-created via the UI: fill in the notes from the changelog. body="$(jq -Rs '{body:.}' release-notes.md)" curl -fsS -X PATCH "${api}/releases/${release_id}" \ -H "Authorization: token ${TOKEN}" \ -H 'Content-Type: application/json' \ -d "${body}" > /dev/null fi echo "Attaching ${zip} to release ${release_id}" curl -fsS -X POST \ "${api}/releases/${release_id}/assets?name=unsupervised-schedular-${version}.zip" \ -H "Authorization: token ${TOKEN}" \ -F "attachment=@${zip}" > /dev/null # After a stable release, move main forward: bump the plugin to the next patch # version and open a fresh changelog section for it, via a PR. Skipped for # pre-releases (tags containing a hyphen, e.g. v1.2.0-rc.1) — those don't # advance the mainline version. bump-version: name: Open next-version bump PR needs: release if: ${{ !contains(github.ref_name, '-') }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: ref: main fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} - name: Compute next patch version id: next run: | current="$(sed -nE 's/^[[:space:]]*\*?[[:space:]]*Version:[[:space:]]*([^[:space:]]+).*/\1/p' unsupervised-schedular.php | head -1)" base="${current%%-*}" # drop any pre-release suffix major="${base%%.*}" rest="${base#*.}" minor="${rest%%.*}" patch="${rest#*.}" next="${major}.${minor}.$((patch + 1))" echo "next=${next}" >> "$GITHUB_OUTPUT" - name: Apply version bump and open changelog section run: | next="${{ steps.next.outputs.next }}" # Plugin header + USC_VERSION constant must stay in lockstep. sed -i -E "s/^([[:space:]]*\*?[[:space:]]*Version:[[:space:]]*).*/\1${next}/" unsupervised-schedular.php sed -i -E "s/(define\('USC_VERSION', ')[^']+('\);)/\1${next}\2/" unsupervised-schedular.php # Insert an empty section for the new version above the current top one # (the first "## [" heading in the file). awk -v ver="$next" ' !done && /^## \[/ { print "## [" ver "]"; print ""; done = 1 } { print } ' CHANGELOG.md > CHANGELOG.md.tmp && mv CHANGELOG.md.tmp CHANGELOG.md - name: Open pull request env: TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | next="${{ steps.next.outputs.next }}" branch="release/bump-${next}" api="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}" git config user.name 'Release Bot' git config user.email 'release-bot@unsupervised.ca' git checkout -b "${branch}" git commit -am "Bump version to ${next} and open changelog section" git push origin "${branch}" curl -fsS -X POST "${api}/pulls" \ -H "Authorization: token ${TOKEN}" \ -H 'Content-Type: application/json' \ -d "$(jq -n --arg head "${branch}" --arg title "Bump version to ${next}" \ '{head:$head, base:"main", title:$title, body:"Automated post-release bump to the next patch version. Record changes for this version under its changelog heading."}')" \ > /dev/null