Plugin Release #111
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Plugin release automation | |
# | |
# Builds the plugin for release candidates and stable releases. | |
# | |
# Creates the release branch, the actual release on GitHub, and the correct tag. | |
# | |
# For new major releases, the action should be run from the `main` branch. | |
# For patch releases, the action should be run from the corresponding release branch (e.g. `release/1.2.0`) | |
name: Plugin Release | |
on: | |
workflow_dispatch: | |
inputs: | |
version: | |
description: 'Plugin version (e.g. 1.2.3 or 7.2.0-rc.1)' | |
required: true | |
permissions: | |
contents: read | |
env: | |
PLUGIN_VERSION: ${{ github.event.inputs.version }} | |
TAG_NAME: 'v${{ github.event.inputs.version }}' | |
IS_RC: ${{ contains(github.event.inputs.version, 'rc') }} | |
IS_PATCH_RELEASE: ${{ startsWith(github.ref, 'refs/heads/release/') }} | |
GIT_AUTHOR_EMAIL: 94923726+googleforcreators-bot@users.noreply.github.com | |
GIT_AUTHOR_NAME: googleforcreators-bot | |
GIT_COMMITTER_EMAIL: 94923726+googleforcreators-bot@users.noreply.github.com | |
GIT_COMMITTER_NAME: googleforcreators-bot | |
GITHUB_REPO_ID: 235435637 | |
jobs: | |
# Perform some sanity checks at the beginning to avoid surprises. | |
checks: | |
name: Checks | |
runs-on: ubuntu-latest | |
timeout-minutes: 5 | |
# This step requires additional review | |
# See https://docs.github.com/en/actions/reference/environments | |
environment: Production | |
steps: | |
- name: Harden Runner | |
uses: step-security/harden-runner@cba0d00b1fc9a034e1e642ea0f1103c282990604 | |
with: | |
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs | |
- name: Checkout | |
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 | |
- name: Verify semver compatibility | |
run: | | |
if [[ $PLUGIN_VERSION =~ $SEMVER_VERSION_REGEX ]]; then | |
echo "Given plugin version string is a valid semver version" | |
else | |
echo "Given plugin version string is not a valid semver version" | |
exit 1 | |
fi | |
env: | |
SEMVER_VERSION_REGEX: ^([0-9]+)\.([0-9]+)\.([0-9]+)(-([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?(\+[0-9A-Za-z-]+)?$ | |
- name: Verify release does not exist yet | |
run: | | |
if git describe --abbrev=0 --tags --match "$TAG_NAME" &>/dev/null; then | |
echo "The planned plugin version already exists!" | |
exit 1 | |
fi | |
# - name: Ensure RC exists for stable release | |
# if: ${{ ! contains(github.event.inputs.version, 'rc') }} | |
# run: | | |
# VERSION_WITHOUT_SUFFIX=${PLUGIN_VERSION/-rc.*/} | |
# BRANCH=release/$VERSION_WITHOUT_SUFFIX | |
# | |
# if [[ -z $(git ls-remote origin $BRANCH) ]]; then | |
# echo "No release branch exists for this planned stable release" | |
# exit 1 | |
# fi | |
# | |
# git checkout --track origin/$BRANCH | |
# | |
# if ! git describe --abbrev=0 --tags --match "$TAG_NAME-rc.*" &>/dev/null; then | |
# echo "No RC exists for this planned stable release" | |
# exit 1 | |
# fi | |
- name: Ensure readme.txt contains changelog | |
run: | | |
VERSION_WITHOUT_SUFFIX=${PLUGIN_VERSION/-rc.*/} | |
CHANGELOG_REGEX="= $VERSION_WITHOUT_SUFFIX =" | |
if ! grep -q -P "$CHANGELOG_REGEX" readme.txt; then | |
echo "No changelog found in readme.txt" | |
exit 1 | |
fi | |
# Get the current CDN assets version. | |
# If the static assets on the CDN have changed since the last release, | |
# bump the assets version accordingly in the GoogleForCreators/wp.stories.google repo. | |
assets-version: | |
name: Prepare static assets | |
runs-on: ubuntu-latest | |
timeout-minutes: 10 | |
needs: [checks] | |
steps: | |
- name: Harden Runner | |
uses: step-security/harden-runner@cba0d00b1fc9a034e1e642ea0f1103c282990604 | |
with: | |
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs | |
# TODO: Define behavior for patch releases. | |
# | |
# A patch release is done from a specific release branch instead of `main` | |
# | |
# Patch releases must not necessarily copy assets from `main`. | |
# Given the following assets versions: | |
# main 1 2 3 4 <- next major release | |
# ^ | |
# | | |
# current branch | |
# | |
# The patch release should probably get version 3.1 or similar, | |
# since version 4 is already used by the next major release. | |
# | |
# Right now, this is needs to be done manually for patch releases, | |
# otherwise the assets version is left unchanged here. | |
# Grab current assets version from `web-stories.php` and pass on to next steps. | |
# - name: Checkout | |
# uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 | |
# with: | |
# ref: | |
# - name: Get current assets version | |
# id: base_assets_version | |
# run: | | |
# BASE_ASSETS_VERSION=main | |
# if [[ $(cat web-stories.php) =~ $ASSETS_VERSION_REGEX ]]; then | |
# BASE_ASSETS_VERSION=${BASH_REMATCH[1]} | |
# fi | |
# echo "BASE_ASSETS_VERSION=$BASE_ASSETS_VERSION" >> $GITHUB_OUTPUT | |
# env: | |
# ASSETS_VERSION_REGEX: "https://wp.stories.google/static/([^']+)" | |
- name: Checkout wp.stories.google | |
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 | |
with: | |
repository: GoogleForCreators/wp.stories.google | |
lfs: true | |
# Needed so the below commits will trigger a website deployment. | |
token: ${{ secrets.GOOGLEFORCREATORS_BOT_TOKEN }} | |
- name: Authenticate | |
uses: google-github-actions/auth@35b0e87d162680511bf346c299f71c9c5c379033 | |
with: | |
credentials_json: ${{ secrets.GCP_SA_KEY }} | |
- name: Setup Cloud SDK | |
uses: google-github-actions/setup-gcloud@e30db14379863a8c79331b04a9969f4c1e225e0b | |
with: | |
project_id: ${{ secrets.GCP_PROJECT_ID }} | |
# For release candidates of new major releases: | |
# | |
# 1. Get highest assets version | |
# 2. Compare with `main` | |
# 3. If they differ: | |
# 3.1 Set new_version = version+1 | |
# 3.2 Copy `main` to new_version | |
# 3.2 Push new directory | |
# 4. Else, keep currently highest version | |
- name: Prepare assets for RC | |
if: ${{ contains(github.event.inputs.version, 'rc') && ! startsWith(github.ref, 'refs/heads/release/') }} | |
run: | | |
LATEST_ASSETS_VERSION=$(gsutil ls gs://web-stories-wp-cdn-assets/ | sed 's/gs:\/\/web-stories-wp-cdn-assets\///' | sed 's/\///' | sort -n | tail -1) | |
NEW_ASSETS_VERSION=$LATEST_ASSETS_VERSION | |
NUMBER_OF_NEW_ASSETS=$(ls main | wc -l) | |
if [[ "0" -eq NUMBER_OF_NEW_ASSETS ]]; then | |
echo "No new assets found. Not uploading anything." | |
else | |
echo "New assets found." | |
NEW_ASSETS_VERSION=$((LATEST_ASSETS_VERSION+1)) | |
echo "Copying existing assets over to new version." | |
gsutil -m rsync -r gs://web-stories-wp-cdn-assets/$LATEST_ASSETS_VERSION gs://web-stories-wp-cdn-assets/$NEW_ASSETS_VERSION | |
echo "Uploading new assets to new version." | |
gsutil -m rsync -r -x '(^|/)\.' main gs://web-stories-wp-cdn-assets/$NEW_ASSETS_VERSION | |
rm -rf main/* | |
echo "Updating LATEST_ASSETS_VERSION Firebase env variable." | |
echo "LATEST_ASSETS_VERSION=$NEW_ASSETS_VERSION" > ../../packages/functions/.env | |
git add ../../packages/functions/.env | |
git add . | |
git status | |
git commit -m "Preparing assets for plugin release $PLUGIN_VERSION" | |
git pull --rebase | |
git push origin main | |
fi | |
echo "Assets version for this release: $NEW_ASSETS_VERSION" | |
mkdir -p assets_version | |
echo $NEW_ASSETS_VERSION > assets_version/assets_version.txt | |
echo "ASSETS_VERSION=${NEW_ASSETS_VERSION}" >> $GITHUB_ENV | |
working-directory: public/static | |
env: | |
BASE_ASSETS_VERSION: main | |
# Uploads an empty file just so we have something to download in the next step | |
# Essentially a no-op. | |
- name: Prepare assets for stable release | |
if: ${{ ! contains(github.event.inputs.version, 'rc') || startsWith(github.ref, 'refs/heads/release/') }} | |
run: | | |
mkdir -p assets_version | |
echo "" > assets_version/assets_version.txt | |
- name: Upload assets version | |
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce | |
with: | |
name: assets-version | |
path: public/static/assets_version | |
- name: Write summary | |
run: | | |
echo "Preparing assets for release" >> $GITHUB_STEP_SUMMARY | |
echo "" >> $GITHUB_STEP_SUMMARY | |
echo "Assets version: $ASSETS_VERSION" >> $GITHUB_STEP_SUMMARY | |
env: | |
ASSETS_VERSION: ${{ env.ASSES_VERSION }} | |
build: | |
name: Build new version | |
runs-on: ubuntu-latest | |
timeout-minutes: 20 | |
needs: [assets-version] | |
outputs: | |
release_branch: ${{ steps.release_branch.outputs.release_branch }} | |
release_name: ${{ steps.release_branch.outputs.release_name }} | |
steps: | |
- name: Harden Runner | |
uses: step-security/harden-runner@cba0d00b1fc9a034e1e642ea0f1103c282990604 | |
with: | |
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs | |
- name: Checkout | |
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 | |
with: | |
fetch-depth: 0 # 0 indicates all history for all branches and tags. | |
token: ${{ secrets.GOOGLEFORCREATORS_BOT_TOKEN }} | |
- name: Download assets version | |
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a | |
with: | |
name: assets-version | |
continue-on-error: true | |
- name: Retrieve assets version | |
id: assets_version | |
run: | | |
echo "ASSETS_VERSION=$(cat assets_version.txt)" >> $GITHUB_ENV | |
rm -rf assets_version.txt | |
continue-on-error: true | |
- name: Setup Node | |
uses: actions/setup-node@e33196f7422957bea03ed53f6fbb155025ffc7b8 | |
with: | |
node-version-file: '.nvmrc' | |
cache: npm | |
- name: Setup PHP | |
uses: shivammathur/setup-php@72ae4ccbe57f82bbe08411e84e2130bd4ba1c10f | |
with: | |
php-version: '8.0' | |
coverage: none | |
tools: composer | |
- name: Install dependencies | |
run: | | |
npm ci | |
env: | |
PUPPETEER_SKIP_DOWNLOAD: true | |
- name: Install PHP dependencies | |
uses: ramsey/composer-install@83af392bf5f031813d25e6fe4cd626cdba9a2df6 | |
with: | |
composer-options: '--prefer-dist --no-progress --no-interaction' | |
- name: Setup Bun | |
uses: oven-sh/setup-bun@4573031972107e0af692492ee967d9022deafd7b | |
with: | |
bun-version: '0.5.9' | |
- name: Create release branch | |
id: release_branch | |
run: | | |
VERSION_WITHOUT_SUFFIX=${PLUGIN_VERSION/-rc.*/} | |
BRANCH=release/$VERSION_WITHOUT_SUFFIX | |
# Patch releases are already on the correct branch. | |
if $IS_PATCH_RELEASE; then | |
BRANCH=${GITHUB_REF#refs/heads/} | |
echo "release_branch=${BRANCH}" >> $GITHUB_OUTPUT | |
exit 0 | |
fi | |
if [[ -z $(git ls-remote origin $BRANCH) ]]; then | |
git checkout -b $BRANCH | |
else | |
git checkout --track origin/$BRANCH | |
fi | |
echo "release_branch=${BRANCH/-rc./ RC}" >> $GITHUB_OUTPUT | |
RELEASE_NAME=${PLUGIN_VERSION/-rc./ RC} | |
echo "release_name=${RELEASE_NAME}" >> $GITHUB_OUTPUT | |
- name: Update assets version | |
run: bun run workflow:assets-version $ASSETS_VERSION | |
if: ${{ env.ASSETS_VERSION }} | |
env: | |
ASSETS_VERSION: ${{ env.ASSETS_VERSION }} | |
- name: Commit assets version bump | |
run: | | |
git add web-stories.php | |
git status | |
git diff --staged --quiet && echo 'No changes to commit; exiting!' && exit 0 | |
git commit -m "Update assets version to $ASSETS_VERSION" | |
git push -u origin HEAD | |
if: ${{ env.ASSETS_VERSION }} | |
env: | |
ASSETS_VERSION: ${{ env.ASSETS_VERSION }} | |
- name: Update plugin version | |
run: bun run workflow:version $PLUGIN_VERSION | |
# Commit the plugin version bump if it was successful. | |
# It's also possible that there were no changes, for example if the | |
# workflow was run a second time and the commit has already happened, | |
# but the process later failed. | |
# This allows re-running the workflow again without aborting in this case. | |
- name: Commit plugin version bump | |
id: plugin_version_bump | |
run: | | |
git add web-stories.php | |
git status | |
if git diff --cached --exit-code > /dev/null; then | |
git commit -m "Prepare release $PLUGIN_VERSION" | |
git push -u origin HEAD | |
echo "changes=yes" >> $GITHUB_ENV | |
else | |
echo 'No changes to commit; exiting!' | |
echo "changes=no" >> $GITHUB_ENV | |
fi | |
# Only non-patch release version bumps should be cherry picked to main | |
# This will cherry-pick the last commit from the release branch, as | |
# we only want the plugin version bump, not the assets version bump. | |
# | |
# Cherry-picking is only done if there actually was | |
# a version bump in the previous step. | |
- name: Cherry-pick to main | |
run: | | |
git checkout main | |
git cherry-pick $BRANCH | |
git pull --rebase | |
git push | |
git checkout $BRANCH | |
if: ${{ ! startsWith(github.ref, 'refs/heads/release/') && env.changes == 'yes' }} | |
env: | |
BRANCH: ${{ steps.release_branch.outputs.release_branch }} | |
- name: Build plugin | |
run: bun run build:js | |
- name: Bundle regular version | |
run: bun run workflow:build-plugin -- --zip web-stories.zip | |
- name: Bundle development version | |
run: | | |
rm -rf assets/css/* assets/js/* | |
npx webpack --env=development | |
bun run workflow:build-plugin -- --zip web-stories-dev.zip | |
- name: Prepare release artifacts | |
run: | | |
mkdir -p build/release-assets | |
mv build/*.zip build/release-assets/ | |
- name: Upload artifacts | |
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce | |
with: | |
name: release-assets | |
path: build/release-assets | |
create-release: | |
name: Create Release | |
runs-on: ubuntu-latest | |
timeout-minutes: 5 | |
needs: [build] | |
steps: | |
- name: Harden Runner | |
uses: step-security/harden-runner@cba0d00b1fc9a034e1e642ea0f1103c282990604 | |
with: | |
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs | |
- name: Checkout | |
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 | |
- name: Download release artifacts | |
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a | |
with: | |
name: release-assets | |
path: build | |
- name: Publish Release | |
id: create_release | |
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 | |
with: | |
tag_name: ${{ env.TAG_NAME }} | |
name: ${{ env.release_name }} | |
target_commitish: ${{ steps.release_branch.outputs.release_branch || github.ref }} | |
prerelease: ${{ env.IS_RC }} | |
generate_release_notes: true | |
files: | | |
build/web-stories.zip | |
build/web-stories-dev.zip | |
fail_on_unmatched_files: true | |
token: ${{ secrets.GOOGLEFORCREATORS_BOT_TOKEN }} | |
# Post-release version bumps for non-patch releases. | |
post-release: | |
name: Post-release version bump | |
needs: [create-release] | |
runs-on: ubuntu-latest | |
if: ${{ ! startsWith(github.ref, 'refs/heads/release/') && ! contains(github.event.inputs.version, 'rc') }} | |
steps: | |
- name: Harden Runner | |
uses: step-security/harden-runner@cba0d00b1fc9a034e1e642ea0f1103c282990604 | |
with: | |
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs | |
- name: Checkout | |
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 | |
with: | |
ref: main | |
token: ${{ secrets.GOOGLEFORCREATORS_BOT_TOKEN }} | |
- name: Setup Node | |
uses: actions/setup-node@e33196f7422957bea03ed53f6fbb155025ffc7b8 | |
with: | |
node-version-file: '.nvmrc' | |
cache: npm | |
- name: Install dependencies | |
run: npm ci | |
env: | |
PUPPETEER_SKIP_DOWNLOAD: true | |
- name: Setup Bun | |
uses: oven-sh/setup-bun@4573031972107e0af692492ee967d9022deafd7b | |
with: | |
bun-version: '0.5.9' | |
# If we're releasing 1.6.0, bump version on main to 1.7.0-alpha.0. | |
- name: Update plugin version | |
run: npm run workflow:version -- --increment preminor --preid alpha | |
- name: Commit changes | |
run: | | |
git add web-stories.php | |
git status | |
git diff --staged --quiet && echo 'No changes to commit; exiting!' && exit 1 | |
git commit -m "Post-release version bump" | |
git pull --rebase | |
git push -u origin HEAD | |
# Stable releases are automatically deployed to WordPress.org. | |
# TODO: Consider also deploying other types of releases (RC, beta), but without bumping the stable tag. | |
# This way we could offer users a way to beta test the plugin. | |
# See http://plugins.svn.wordpress.org/buddypress/tags/ and https://wordpress.org/plugins/bp-beta-tester/ for inspiration. | |
deploy: | |
name: Deploy plugin to WordPress.org | |
runs-on: ubuntu-latest | |
timeout-minutes: 10 | |
needs: [create-release] | |
if: ${{ ! contains(github.event.inputs.version, 'rc') }} | |
env: | |
PLUGIN_REPO_URL: 'https://plugins.svn.wordpress.org/web-stories' | |
STABLE_TAG_REGEX: 'Stable tag:\s*(.+)' | |
SVN_USERNAME: ${{ secrets.SVN_USERNAME }} | |
SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }} | |
steps: | |
- name: Harden Runner | |
uses: step-security/harden-runner@cba0d00b1fc9a034e1e642ea0f1103c282990604 | |
with: | |
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs | |
- name: Download release artifacts | |
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a | |
with: | |
name: release-assets | |
path: release-assets | |
- name: Check out trunk folder | |
run: svn checkout $PLUGIN_REPO_URL svn --username "$SVN_USERNAME" | |
- name: Get previous stable tag | |
id: get_previous_stable_tag | |
# Returns the whole matching line. | |
run: echo stable_tag=$(grep -P "$STABLE_TAG_REGEX" ./svn/trunk/readme.txt) >> $GITHUB_ENV | |
- name: Delete everything in trunk | |
run: find . -maxdepth 1 -not -name ".svn" -not -name "." -not -name ".." -exec rm -rf {} + | |
working-directory: ./svn/trunk | |
- name: Unzip release asset into trunk | |
run: | | |
unzip release-assets/web-stories.zip | |
mv web-stories/* svn/trunk | |
env: | |
PLUGIN_URL: ${{ github.event.release.assets[0].browser_download_url }} | |
- name: Replace stable tag placeholder with pre-existing stable tag | |
run: | | |
sed -r -i "s/${STABLE_TAG_REGEX}/${STABLE_TAG}/g" ./readme.txt | |
working-directory: ./svn/trunk | |
env: | |
STABLE_TAG: ${{ env.stable_tag }} | |
# Note: Creating the tag trigger an email confirmation that needs to be confirmed by someone with commit access. | |
- name: Commit changes to trunk | |
run: | | |
svn st | grep '^?' | awk '{print $2}' | xargs -r svn add | |
svn st | grep '^!' | awk '{print $2}' | xargs -r svn rm | |
svn commit -m "Committing version $PLUGIN_VERSION" \ | |
--no-auth-cache --non-interactive --username "$SVN_USERNAME" --password "$SVN_PASSWORD" | |
working-directory: ./svn | |
# Copy trunk to the new tag directly on the server. | |
# Not done in the same commit as the changes to trunk in order to reduce number of file operations | |
# and to prevent potential timeouts. | |
# See https://developer.wordpress.org/plugins/wordpress-org/how-to-use-subversion/#create-tags-from-trunk | |
- name: Create the SVN tag | |
run: | | |
svn cp "$PLUGIN_REPO_URL/trunk" "$PLUGIN_REPO_URL/tags/$PLUGIN_VERSION" \ | |
-m "Tagging version $PLUGIN_VERSION" \ | |
--no-auth-cache --non-interactive --username "$SVN_USERNAME" --password "$SVN_PASSWORD" | |
working-directory: ./svn | |
# It's recommended to run this only after the tag was successfully created. | |
# Otherwise, if there were any errors, we risk changing this to a tag that doesn't exist. | |
# The actual release still needs to be confirmed via email. | |
- name: Update stable tag | |
working-directory: ./svn | |
run: | | |
sed -r -i "s/${STABLE_TAG_REGEX}/Stable tag: ${PLUGIN_VERSION}/g" ./trunk/readme.txt | |
svn commit -m "Releasing version $PLUGIN_VERSION" \ | |
--no-auth-cache --non-interactive --username "$SVN_USERNAME" --password "$SVN_PASSWORD" |