Skip to content

Add Release and Release RC workflows#505

Open
enricobattocchi wants to merge 4 commits into
trunkfrom
add-release-workflows
Open

Add Release and Release RC workflows#505
enricobattocchi wants to merge 4 commits into
trunkfrom
add-release-workflows

Conversation

@enricobattocchi
Copy link
Copy Markdown
Member

@enricobattocchi enricobattocchi commented May 26, 2026

Context

Ports the release and release-rc workflows from yoast-test-helper so duplicate-post stops cutting releases by hand.

Summary

This PR can be summarized in the following changelog entry:

  • Adds Release and Release RC workflows (dispatch-triggered, dry-run capable) that handle version bump, tag, wp.org SVN deploy, GitHub release, milestone management, and merge-back.

Relevant technical choices:

  • trunk plays y-t-h's develop role; main is the stable branch.
  • Release notes pulled from changelog.md (## X.Y block), not readme.txt.
  • RC version bump uses grunt update-version-trunk to preserve readme.txt Stable tag.
  • RC pre-release body is a separate QA changelog: incremental vs previous RC/release, includes non-user-facing entries, appends PR links.
  • SVN deploy uses 10up/action-wordpress-plugin-deploy (not grunt-wp-deploy).
  • Artifacts are duplicate-post-${VERSION}.zip with a single duplicate-post/ top-level folder. Shape is sanity-checked with unzip -l.
  • deploy.yml unchanged: RC tags will keep mirroring to Yoast-dist/duplicate-post.
  • Requires new repo secrets WPORG_SVN_USERNAME and WPORG_SVN_PASSWORD before the first non-dry-run release.

Test instructions

Test instructions for the acceptance test before the PR gets merged

  • Workflows can only be exercised after merge. Pre-merge, confirm both files render in the GitHub UI without YAML errors and expose their workflow_dispatch inputs.

Relevant test scenarios

  • Changes should be tested with the browser console open
  • Changes should be tested on different posts/pages/taxonomies/custom post types/custom taxonomies
  • Changes should be tested on different editors (Default Block/Gutenberg/Classic/Elementor/other)
  • Changes should be tested on different browsers
  • Changes should be tested on multisite

Test instructions for QA when the code is in the RC

  • QA should use the same steps as above.

QA can test this PR by following these steps:

  • Dispatch Release RC with version=99.0, dry-run=true, deploy-to-svn-trunk=true. Confirm nothing is pushed, the downloaded duplicate-post-99.0-RC1.zip has a single duplicate-post/ top-level folder, its readme.txt Stable tag is still 4.6, and the "Build QA changelog" step output has PR links plus (if applicable) a ### Non user facing: section.
  • Dispatch again to simulate RC2 and confirm the QA changelog compares against 99.0-RC1.
  • Dispatch Release with version=99.0, dry-run=true. Confirm same artifact shape and that the release-notes preview matches the ## 4.6-equivalent block from changelog.md.

Impact check

  • No plugin code changes. Only two new workflow files.

UI changes

  • This PR changes the UI in the plugin. I have added the 'UI change' label to this PR.

Documentation

  • I have written documentation for this change.

Each workflow has a header comment explaining triggers, inputs, dry-run semantics, and pipeline order.

Quality assurance

  • I have tested this code to the best of my abilities
  • I have added unittests to verify the code works as intended

Innovation

  • No innovation project is applicable for this PR.

Fixes #

Adds two workflow_dispatch-driven release pipelines ported from
yoast-test-helper:

* `release.yml`: end-to-end stable release. Verifies the changelog.md
  section, bumps the version via `grunt set-version` + `grunt
  update-version`, commits to trunk, merges into main, tags, deploys
  to wp.org SVN via the 10up action, creates a GitHub release with
  `duplicate-post-${VERSION}.zip` attached and changelog.md extract as
  notes, manages milestones, merges main back into trunk.
* `release-rc.yml`: release-candidate flow. Auto-detects the next RC
  number, regenerates changelog.md via the existing
  generate-changelog.sh, bumps the version with `grunt
  update-version-trunk` (preserves the readme.txt Stable tag), tags
  `${VERSION}-RC${N}`, optionally pushes a translation-strings build
  to wp.org SVN trunk, and creates a GitHub pre-release with a
  separate QA changelog (incremental since previous RC/release,
  includes non-user-facing entries and PR links).

Both workflows support a `dry-run` flag that skips every remote
side-effect while still uploading the built artifact as a workflow
artifact. Artifacts are produced as `duplicate-post-${VERSION}.zip`
containing a single top-level `duplicate-post/` folder, with an
`unzip -l` shape check guarding against drift.
@coveralls
Copy link
Copy Markdown

coveralls commented May 26, 2026

Coverage Report for CI Build 26539072857

Warning

No base build found for commit 1e67e24 on trunk.
Coverage changes can't be calculated without a base build.
If a base build is processing, this comment will update automatically when it completes.

Coverage: 59.881%

Details

  • Patch coverage: No coverable lines changed in this PR.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

Requires a base build to compare against. How to fix this →


Coverage Stats

Coverage Status
Relevant Lines: 2687
Covered Lines: 1609
Line Coverage: 59.88%
Coverage Strength: 7.36 hits per line

💛 - Coveralls

Resolves shellcheck SC2001 from actionlint's Docker image (bundles a
newer shellcheck than the standalone actionlint binary). Verified
locally with `docker run rhysd/actionlint:latest`.
@enricobattocchi enricobattocchi requested a review from Copilot May 26, 2026 14:30
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

Comment thread .github/workflows/release.yml Outdated
Comment on lines +234 to +236
BAD=$(unzip -l "${ZIP_NAME}" \
| awk 'NR>3 && $NF != "" && $NF !~ /^duplicate-post\// { print $NF }' \
| grep -vE '^[0-9-]+ files?$' || true)
Comment thread .github/workflows/release-rc.yml Outdated
Comment on lines +233 to +235
BAD=$(unzip -l "${ZIP_NAME}" \
| awk 'NR>3 && $NF != "" && $NF !~ /^duplicate-post\// { print $NF }' \
| grep -vE '^[0-9-]+ files?$' || true)
Comment thread .github/workflows/release.yml Outdated
Comment on lines +111 to +113
if ! grep -qE "^## ${VERSION}\b" changelog.md; then
echo "::error::No '## ${VERSION}' section found in changelog.md. Run the 'Generate Changelog' workflow first."
exit 1
Comment thread .github/workflows/release-rc.yml Outdated
Comment on lines +150 to +152
if ! grep -qE "^## ${VERSION}\b" changelog.md; then
echo "::error::No '## ${VERSION}' section found in changelog.md. Either label the relevant PRs with a 'changelog:' label so they can be picked up, or run 'Generate Changelog' first."
exit 1
Both flagged by Copilot review on PR #505:

* `unzip -l` emits footer lines (`-------` and `N files`) that the
  awk-based filter doesn't strip, so the shape check would flag valid
  artifacts. Switched to `unzip -Z1` which lists only entry paths.
* `^## ${VERSION}\b` would falsely match `## 4.6.1` when looking for
  `4.6` because `\b` treats the dot as a word boundary. Switched to
  `grep -Fxq` for an exact full-line match.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

Comment on lines +225 to +240
set -euo pipefail
if [ ! -f artifact.zip ]; then
echo "::error::Expected artifact.zip from 'grunt artifact', but it is missing."
exit 1
fi
ZIP_NAME="duplicate-post-${VERSION}.zip"
cp artifact.zip "${ZIP_NAME}"
cp -a artifact duplicate-post
# Sanity-check the zip shape: every entry must be under `duplicate-post/`.
BAD=$(unzip -Z1 "${ZIP_NAME}" | grep -v '^duplicate-post/' || true)
if [ -n "$BAD" ]; then
echo "::error::Artifact zip contains entries outside duplicate-post/:"
echo "$BAD"
exit 1
fi
echo "ZIP_NAME=${ZIP_NAME}" >> "$GITHUB_ENV"
Comment on lines +225 to +240
set -euo pipefail
if [ ! -f artifact.zip ]; then
echo "::error::Expected artifact.zip from 'grunt artifact', but it is missing."
exit 1
fi
ZIP_NAME="duplicate-post-${RC_VERSION}.zip"
cp artifact.zip "${ZIP_NAME}"
cp -a artifact duplicate-post
BAD=$(unzip -Z1 "${ZIP_NAME}" | grep -v '^duplicate-post/' || true)
if [ -n "$BAD" ]; then
echo "::error::Artifact zip contains entries outside duplicate-post/:"
echo "$BAD"
exit 1
fi
echo "name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"

Comment on lines +198 to +203
- name: "Copy composer.json into artifact"
# Matches deploy.yml behaviour: the dist artifact includes composer.json so
# downstream tooling that requires it via Packagist installers keeps working.
run: cp composer.json ./artifact

- name: Extract release notes from changelog.md
Ports three post-snapshot fixes from yoast-test-helper that we had
inherited the bugs of:

* release.yml released trunk's state and ignored release/x.y entirely.
  Now it selects a source branch (release/x.y when an RC was cut, else
  trunk), bumps + pushes there, merges the source into main, and
  tags/builds/deploys/releases from main before back-merging into trunk.
* release-rc.yml merged trunk into the release branch on every re-run,
  pulling next-version work into the frozen RC line and (with the
  back-merge) collapsing the two branches. Now it re-uses the release
  branch as-is; fixes reach it via PRs targeting release/x.y directly.
* Plugin-folder staging used `cp -a artifact duplicate-post`, which only
  yields the right shape when the wrapper doesn't already exist. Switched
  to `mkdir duplicate-post && cp -a artifact/.`. Also added a
  `git reset --hard HEAD` after the artifact build in release.yml so the
  merge-back checkout can't be blocked by a dirty tree.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants