From 1f5891992b5b353ca33b33d736b9fb5f36fcb1f4 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 8 Mar 2021 22:46:34 +0100 Subject: [PATCH 01/42] Release Workflow: Allow triggering manually --- .github/workflows/build-plugin-zip.yml | 122 ++++++++++++++++-- .../upload-release-to-plugin-repo.yml | 64 ++++++++- 2 files changed, 171 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml index 7b4c161ff3b5e5..f8260511ffe256 100644 --- a/.github/workflows/build-plugin-zip.yml +++ b/.github/workflows/build-plugin-zip.yml @@ -6,18 +6,98 @@ on: - '**.md' push: branches: [trunk] - tags: - - 'v*' paths-ignore: - '**.md' + workflow_dispatch: + inputs: + version: + description: 'rc or stable?' + required: true jobs: + bump-version: + name: Bump version + runs-on: ubuntu-latest + if: github.event_name == 'workflow_dispatch' + outputs: + old_version: ${{ steps.get_version.outputs.old_version }} + new_version: ${{ steps.get_version.outputs.new_version }} + release_branch: ${{ steps.get_version.outputs.release_branch }} + steps: + - name: Checkout code + uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4 + + - name: Configure git user name and email + run: | + git config user.name "Gutenberg Repository Automation" + git config user.email gutenberg@wordpress.org + + - name: Compute old and new version + id: get_version + run: | + OLD_VERSION=$(jq --raw-output '.version' package.json) + echo "::set-output name=old_version::$(echo $OLD_VERSION)" + if [[ ${{ github.event.inputs.version }} == 'stable' ]]; then + NEW_VERSION=$(npx semver $OLD_VERSION -i patch) + else + if [[ $OLD_VERSION == *"rc"* ]]; then + NEW_VERSION=$(npx semver $OLD_VERSION -i prerelease) + else + # WordPress version guidelines: If minor is 9, bump major instead. + IFS='.' read -r -a OLD_VERSION_ARRAY <<< "$OLD_VERSION" + if [[ ${OLD_VERSION_ARRAY[1]} == "9" ]]; then + NEW_VERSION="$(npx semver $OLD_VERSION -i major)-rc.1" + else + NEW_VERSION="$(npx semver $OLD_VERSION -i minor)-rc.1" + fi + fi + fi + echo "::set-output name=new_version::$(echo $NEW_VERSION)" + IFS='.' read -r -a NEW_VERSION_ARRAY <<< "$NEW_VERSION" + RELEASE_BRANCH="release/${NEW_VERSION_ARRAY[0]}.${NEW_VERSION_ARRAY[1]}" + echo "::set-output name=release_branch::$(echo $RELEASE_BRANCH)" + + - name: Create release branch + if: ${{ github.event.inputs.version == 'rc' && ! contains( steps.get_version.outputs.old_version, 'rc' ) }} + run: git checkout -b "${{ steps.get_version.outputs.release_branch }}" + + - name: Update plugin version + env: + VERSION: ${{ steps.get_version.outputs.new_version }} + run: | + cat <<< $(jq --tab --arg version "${VERSION}" '.version = $version' package.json) > package.json + cat <<< $(jq --tab --arg version "${VERSION}" '.version = $version' package-lock.json) > package-lock.json + sed -i "s/${{ steps.get_version.outputs.old_version }}/${VERSION}/g" gutenberg.php + sed -i "s/${{ steps.get_version.outputs.old_version }}/${VERSION}/g" readme.txt + + - name: Commit the version bump + run: | + git add gutenberg.php package.json package-lock.json readme.txt + git commit -m "Bump plugin version to ${{ steps.get_version.outputs.new_version }}" + git push --set-upstream origin "${{ steps.get_version.outputs.release_branch }}" + + - name: Cherry-pick to trunk + if: github.ref != 'trunk' + run: | + git fetch --depth=1 origin trunk + git checkout trunk + TRUNK_VERSION=$(jq --raw-output '.version' package.json) + if [[ ${{ steps.get_version.outputs.old_version }} == "$TRUNK_VERSION" ]]; then + git cherry-pick "${{ steps.get_version.outputs.release_branch }}" + git push + fi + + build: name: Build Release Artifact runs-on: ubuntu-latest + needs: bump-version + if: always() steps: - name: Checkout code uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4 + with: + ref: ${{ needs.bump-version.outputs.release_branch || github.ref }} - name: Cache node modules uses: actions/cache@26968a09c0ea4f3e233fdddbafd1166051a095f6 # v2.1.4 @@ -48,26 +128,42 @@ jobs: name: gutenberg-plugin path: ./gutenberg.zip + - name: Build release notes draft + if: github.event_name == 'workflow_dispatch' + run: | + IFS='.' read -r -a VERSION_ARRAY <<< "${{ needs.bump-version.outputs.new_version }}" + MILESTONE="Gutenberg ${VERSION_ARRAY[0]}.${VERSION_ARRAY[1]}" + npm run changelog -- --milestone="$MILESTONE" --unreleased > release-notes.txt + sed -ie '1,6d' release-notes.txt + + - name: Upload release notes artifact + if: github.event_name == 'workflow_dispatch' + uses: actions/upload-artifact@e448a9b857ee2131e752b06002bf0e093c65e571 # v2.2.2 + with: + name: release-notes + path: ./release-notes.txt + create-release: name: Create Release Draft and Attach Asset - needs: build + needs: [bump-version, build] runs-on: ubuntu-latest - if: startsWith(github.ref, 'refs/tags/v') + if: github.event_name == 'workflow_dispatch' steps: - name: Set Release Version id: get_release_version - run: echo ::set-output name=version::$(echo $GITHUB_REF | cut -d / -f 3 | sed s/^v// | sed 's/-rc./ RC/' ) + env: + VERSION: ${{ needs.bump-version.outputs.new_version }} + run: echo ::set-output name=version::$(echo $VERSION | cut -d / -f 3 | sed 's/-rc./ RC/' ) - name: Download Plugin Zip Artifact uses: actions/download-artifact@4a7a711286f30c025902c28b541c10e147a9b843 # v2.0.8 with: name: gutenberg-plugin - - name: Extract Changelog for Release - run: | - unzip gutenberg.zip changelog.txt - CHANGELOG_REGEX="/=\s[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?\s=/" - awk -i inplace "$CHANGELOG_REGEX"'{p++;next} p==2{exit} p>=1' changelog.txt + - name: Download Release Notes Artifact + uses: actions/download-artifact@4a7a711286f30c025902c28b541c10e147a9b843 # v2.0.8 + with: + name: release-notes - name: Create Release Draft id: create_release @@ -75,11 +171,11 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - tag_name: ${{ github.ref }} + tag_name: "v${{ needs.bump-version.outputs.new_version }}" release_name: ${{ steps.get_release_version.outputs.version }} draft: true - prerelease: ${{ contains(github.ref, 'rc') }} - body_path: changelog.txt + prerelease: ${{ contains(needs.bump-version.outputs.new_version, 'rc') }} + body_path: release-notes.txt - name: Upload Release Asset id: upload-release-asset diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index 57df6897e9553c..c3e3a7ac01a545 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -1,15 +1,69 @@ on: release: - types: [released] + types: [published] name: Upload Gutenberg plugin to WordPress.org plugin repo jobs: + update-changelog: + name: Update Changelog + runs-on: ubuntu-latest + if: github.event.release.assets[0] + env: + VERSION: ${{ github.event.release.name }} + steps: + - name: Configure git user name and email + run: | + git config user.name "Gutenberg Repository Automation" + git config user.email gutenberg@wordpress.org + + - name: Compute release branch name + id: get_release_branch + run: | + IFS='.' read -r -a VERSION_ARRAY <<< "$VERSION" + RELEASE_BRANCH="release/${VERSION_ARRAY[0]}.${VERSION_ARRAY[1]}" + echo "::set-output name=release_branch::$(echo $RELEASE_BRANCH)" + + - name: Checkout code + uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4 + with: + ref: ${{ steps.get_release_branch.outputs.release_branch }} + + - name: Update the Changelog to include the release notes + run: | + printf '== Changelog ==\n\n' > new_changelog.txt + printf '= %s =\n\n' "$VERSION" >> new_changelog.txt + echo "${{ github.event.release.body }}" >> new_changelog.txt + printf '\n\n' >> new_changelog.txt + CHANGELOG_REGEX="=\s[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?\s=" + RC_REGEX="=\s${VERSION}(-rc\.[0-9]+)?\s=" + if [[ ${{ github.event.release.prerelease }} == 'true' ]]; then + awk "/${CHANGELOG_REGEX}/,EOF" changelog.txt >> new_changelog.txt + else + awk "/${RC_REGEX}/ {next}; /${CHANGELOG_REGEX}/ {found = 1}; found" changelog.txt >> new_changelog.txt + fi + mv new_changelog.txt changelog.txt + + - name: Commit the Changelog update + run: | + git add changelog.txt + git commit -m "Update Changelog for ${VERSION}" + git push --set-upstream origin "${{ steps.get_release_branch.outputs.release_branch }}" + + # TODO: Do the same for trunk + + - name: Upload Changelog artifact + uses: actions/upload-artifact@e448a9b857ee2131e752b06002bf0e093c65e571 # v2.2.2 + with: + name: changelog + path: ./changelog.txt + upload: name: Upload Gutenberg Plugin runs-on: ubuntu-latest environment: wp.org plugin - if: github.event.release.assets[0] + needs: update-changelog + if: ${{ !github.event.release.prerelease && github.event.release.assets[0] }} env: PLUGIN_REPO_URL: 'https://plugins.svn.wordpress.org/gutenberg' STABLE_TAG_REGEX: 'Stable tag: [0-9]\+\.[0-9]\+\.[0-9]\+\s*' @@ -42,6 +96,12 @@ jobs: STABLE_TAG: ${{ steps.get_previous_stable_tag.outputs.stable_tag }} run: sed -i "s/${STABLE_TAG_PLACEHOLDER}/${STABLE_TAG}/g" ./trunk/readme.txt + - name: Download Changelog Artifact + uses: actions/download-artifact@4a7a711286f30c025902c28b541c10e147a9b843 # v2.0.8 + with: + name: changelog + path: trunk/changelog.txt + - name: Commit the content changes working-directory: ./trunk run: | From 355a42a578623daac644176d41f25ffd557567bc Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 8 Mar 2021 23:14:06 +0100 Subject: [PATCH 02/42] Gotta check out the code first --- .github/workflows/upload-release-to-plugin-repo.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index c3e3a7ac01a545..f7436d33de8015 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -12,11 +12,6 @@ jobs: env: VERSION: ${{ github.event.release.name }} steps: - - name: Configure git user name and email - run: | - git config user.name "Gutenberg Repository Automation" - git config user.email gutenberg@wordpress.org - - name: Compute release branch name id: get_release_branch run: | @@ -44,6 +39,11 @@ jobs: fi mv new_changelog.txt changelog.txt + - name: Configure git user name and email + run: | + git config user.name "Gutenberg Repository Automation" + git config user.email gutenberg@wordpress.org + - name: Commit the Changelog update run: | git add changelog.txt From 68831b6df2e96264a0f8c1046861ed7613bc53f1 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 8 Mar 2021 23:31:54 +0100 Subject: [PATCH 03/42] Need commitish arg for release creation --- .github/workflows/build-plugin-zip.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml index f8260511ffe256..80e2311ddb31bf 100644 --- a/.github/workflows/build-plugin-zip.yml +++ b/.github/workflows/build-plugin-zip.yml @@ -173,6 +173,7 @@ jobs: with: tag_name: "v${{ needs.bump-version.outputs.new_version }}" release_name: ${{ steps.get_release_version.outputs.version }} + commitish: ${{ needs.bump-version.outputs.release_branch || github.ref }} draft: true prerelease: ${{ contains(needs.bump-version.outputs.new_version, 'rc') }} body_path: release-notes.txt From 774e15723fca96512c8d6e457ec78036c27f9975 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Tue, 9 Mar 2021 15:07:48 +0100 Subject: [PATCH 04/42] Update Changelog in trunk --- .github/workflows/upload-release-to-plugin-repo.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index f7436d33de8015..761c22a19bc41b 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -50,14 +50,22 @@ jobs: git commit -m "Update Changelog for ${VERSION}" git push --set-upstream origin "${{ steps.get_release_branch.outputs.release_branch }}" - # TODO: Do the same for trunk - - name: Upload Changelog artifact uses: actions/upload-artifact@e448a9b857ee2131e752b06002bf0e093c65e571 # v2.2.2 with: name: changelog path: ./changelog.txt + - name: Cherry-pick Changelog update to trunk + run: | + git fetch --depth=1 origin trunk + git checkout trunk + TRUNK_VERSION=$(jq --raw-output '.version' package.json) + if [[ "$VERSION" == "$TRUNK_VERSION" ]]; then + git cherry-pick "${{ steps.get_release_branch.outputs.release_branch }}" + git push + fi + upload: name: Upload Gutenberg Plugin runs-on: ubuntu-latest From 18b8fe5f74d8f6c1f1929f99567f673cb959ced1 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Tue, 9 Mar 2021 15:21:12 +0100 Subject: [PATCH 05/42] Use correct version var --- .github/workflows/upload-release-to-plugin-repo.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index 761c22a19bc41b..6b10d0d01e324b 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -10,12 +10,12 @@ jobs: runs-on: ubuntu-latest if: github.event.release.assets[0] env: - VERSION: ${{ github.event.release.name }} + VERSION: ${{ github.event.release.tag_name }} steps: - name: Compute release branch name id: get_release_branch run: | - IFS='.' read -r -a VERSION_ARRAY <<< "$VERSION" + IFS='.' read -r -a VERSION_ARRAY <<< "${VERSION#v}" RELEASE_BRANCH="release/${VERSION_ARRAY[0]}.${VERSION_ARRAY[1]}" echo "::set-output name=release_branch::$(echo $RELEASE_BRANCH)" @@ -27,11 +27,11 @@ jobs: - name: Update the Changelog to include the release notes run: | printf '== Changelog ==\n\n' > new_changelog.txt - printf '= %s =\n\n' "$VERSION" >> new_changelog.txt + printf '= %s =\n\n' "${VERSION#v}" >> new_changelog.txt echo "${{ github.event.release.body }}" >> new_changelog.txt printf '\n\n' >> new_changelog.txt CHANGELOG_REGEX="=\s[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?\s=" - RC_REGEX="=\s${VERSION}(-rc\.[0-9]+)?\s=" + RC_REGEX="=\s${VERSION#v}(-rc\.[0-9]+)?\s=" if [[ ${{ github.event.release.prerelease }} == 'true' ]]; then awk "/${CHANGELOG_REGEX}/,EOF" changelog.txt >> new_changelog.txt else @@ -47,7 +47,7 @@ jobs: - name: Commit the Changelog update run: | git add changelog.txt - git commit -m "Update Changelog for ${VERSION}" + git commit -m "Update Changelog for ${VERSION#v}" git push --set-upstream origin "${{ steps.get_release_branch.outputs.release_branch }}" - name: Upload Changelog artifact @@ -61,7 +61,7 @@ jobs: git fetch --depth=1 origin trunk git checkout trunk TRUNK_VERSION=$(jq --raw-output '.version' package.json) - if [[ "$VERSION" == "$TRUNK_VERSION" ]]; then + if [[ "${VERSION#v}" == "$TRUNK_VERSION" ]]; then git cherry-pick "${{ steps.get_release_branch.outputs.release_branch }}" git push fi From 67705734bf1bea62baa7e7ad28338120ef823770 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Tue, 9 Mar 2021 15:22:05 +0100 Subject: [PATCH 06/42] Rename to TAG --- .github/workflows/upload-release-to-plugin-repo.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index 6b10d0d01e324b..51b00d8121b85f 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -10,12 +10,12 @@ jobs: runs-on: ubuntu-latest if: github.event.release.assets[0] env: - VERSION: ${{ github.event.release.tag_name }} + TAG: ${{ github.event.release.tag_name }} steps: - name: Compute release branch name id: get_release_branch run: | - IFS='.' read -r -a VERSION_ARRAY <<< "${VERSION#v}" + IFS='.' read -r -a VERSION_ARRAY <<< "${TAG#v}" RELEASE_BRANCH="release/${VERSION_ARRAY[0]}.${VERSION_ARRAY[1]}" echo "::set-output name=release_branch::$(echo $RELEASE_BRANCH)" @@ -27,11 +27,11 @@ jobs: - name: Update the Changelog to include the release notes run: | printf '== Changelog ==\n\n' > new_changelog.txt - printf '= %s =\n\n' "${VERSION#v}" >> new_changelog.txt + printf '= %s =\n\n' "${TAG#v}" >> new_changelog.txt echo "${{ github.event.release.body }}" >> new_changelog.txt printf '\n\n' >> new_changelog.txt CHANGELOG_REGEX="=\s[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?\s=" - RC_REGEX="=\s${VERSION#v}(-rc\.[0-9]+)?\s=" + RC_REGEX="=\s${TAG#v}(-rc\.[0-9]+)?\s=" if [[ ${{ github.event.release.prerelease }} == 'true' ]]; then awk "/${CHANGELOG_REGEX}/,EOF" changelog.txt >> new_changelog.txt else @@ -47,7 +47,7 @@ jobs: - name: Commit the Changelog update run: | git add changelog.txt - git commit -m "Update Changelog for ${VERSION#v}" + git commit -m "Update Changelog for ${TAG#v}" git push --set-upstream origin "${{ steps.get_release_branch.outputs.release_branch }}" - name: Upload Changelog artifact @@ -61,7 +61,7 @@ jobs: git fetch --depth=1 origin trunk git checkout trunk TRUNK_VERSION=$(jq --raw-output '.version' package.json) - if [[ "${VERSION#v}" == "$TRUNK_VERSION" ]]; then + if [[ "${TAG#v}" == "$TRUNK_VERSION" ]]; then git cherry-pick "${{ steps.get_release_branch.outputs.release_branch }}" git push fi From a24bd7291a465e50e77ff3ee567b026d76901611 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Tue, 9 Mar 2021 16:49:51 +0100 Subject: [PATCH 07/42] Moar surgical --- .../upload-release-to-plugin-repo.yml | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index 51b00d8121b85f..37c97b2692e42e 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -26,17 +26,26 @@ jobs: - name: Update the Changelog to include the release notes run: | - printf '== Changelog ==\n\n' > new_changelog.txt + # First, determine where to insert the new Changelog entry. + RC_REGEX="=\s${TAG#v}(-rc\.[0-9]+)?\s=" + CUT_MARKS=$( grep -nP ${RC_REGEX} changelog.txt | cut -d: -f1 'rc') + if [[ -z "${CUT_MARKS}" ]]; then + SERIES="${RELEASE_BRANCH#release/}" + SERIES_REGEX="=\s${SERIES}\.[0-9]+\s=" + CUT_MARKS=$( grep -nP -m 1 "${SERIES_REGEX}" changelog.txt | cut -d: -f1 ) + if [[ -z "${CUT_MARKS}" ]]; then + CHANGELOG_REGEX="=\s[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?\s=" + CUT_MARKS=$( grep -nP -m 1 "${CHANGELOG_REGEX}" changelog.txt | cut -d: -f1 ) + fi + fi + BEFORE=$( echo "$CUT_MARKS" | head -n 1 ) + AFTER=$( echo "$CUT_MARKS" | tail -n 1 ) + # Okay, we have all we need to build the new Changelog. + head -n $(( "${BEFORE}" - 1 )) changelog.txt > new_changelog.txt printf '= %s =\n\n' "${TAG#v}" >> new_changelog.txt echo "${{ github.event.release.body }}" >> new_changelog.txt printf '\n\n' >> new_changelog.txt - CHANGELOG_REGEX="=\s[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?\s=" - RC_REGEX="=\s${TAG#v}(-rc\.[0-9]+)?\s=" - if [[ ${{ github.event.release.prerelease }} == 'true' ]]; then - awk "/${CHANGELOG_REGEX}/,EOF" changelog.txt >> new_changelog.txt - else - awk "/${RC_REGEX}/ {next}; /${CHANGELOG_REGEX}/ {found = 1}; found" changelog.txt >> new_changelog.txt - fi + tail -n +"${AFTER}" changelog.txt >> new_changelog.txt mv new_changelog.txt changelog.txt - name: Configure git user name and email From e90aaeb195ccba936085a8a77e8b874430613a5f Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Tue, 9 Mar 2021 17:26:01 +0100 Subject: [PATCH 08/42] Remove stray string literal --- .github/workflows/upload-release-to-plugin-repo.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index 37c97b2692e42e..bda97d32e4b929 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -28,7 +28,7 @@ jobs: run: | # First, determine where to insert the new Changelog entry. RC_REGEX="=\s${TAG#v}(-rc\.[0-9]+)?\s=" - CUT_MARKS=$( grep -nP ${RC_REGEX} changelog.txt | cut -d: -f1 'rc') + CUT_MARKS=$( grep -nP ${RC_REGEX} changelog.txt | cut -d: -f1 ) if [[ -z "${CUT_MARKS}" ]]; then SERIES="${RELEASE_BRANCH#release/}" SERIES_REGEX="=\s${SERIES}\.[0-9]+\s=" From c4b316689869c7867b9028d34b17be0f61d18d2b Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Tue, 9 Mar 2021 20:10:43 +0100 Subject: [PATCH 09/42] Fix Changelog splicing --- .../workflows/upload-release-to-plugin-repo.yml | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index bda97d32e4b929..572773bca62464 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -27,16 +27,13 @@ jobs: - name: Update the Changelog to include the release notes run: | # First, determine where to insert the new Changelog entry. - RC_REGEX="=\s${TAG#v}(-rc\.[0-9]+)?\s=" - CUT_MARKS=$( grep -nP ${RC_REGEX} changelog.txt | cut -d: -f1 ) + SERIES="${RELEASE_BRANCH#release/}" + SERIES_REGEX="=\s${SERIES}\.[0-9]+\s=" + CUT_MARKS=$( grep -nP -m 1 "${SERIES_REGEX}" changelog.txt | cut -d: -f1 ) if [[ -z "${CUT_MARKS}" ]]; then - SERIES="${RELEASE_BRANCH#release/}" - SERIES_REGEX="=\s${SERIES}\.[0-9]+\s=" - CUT_MARKS=$( grep -nP -m 1 "${SERIES_REGEX}" changelog.txt | cut -d: -f1 ) - if [[ -z "${CUT_MARKS}" ]]; then - CHANGELOG_REGEX="=\s[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?\s=" - CUT_MARKS=$( grep -nP -m 1 "${CHANGELOG_REGEX}" changelog.txt | cut -d: -f1 ) - fi + CHANGELOG_REGEX="=\s[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?\s=" + RC_REGEX="=\s${TAG#v}(-rc\.[0-9]+)?\s=" + CUT_MARKS=$( awk "/${RC_REGEX}/ {print NR; next}; /${CHANGELOG_REGEX}/ {print NR; exit}" changelog.txt ) fi BEFORE=$( echo "$CUT_MARKS" | head -n 1 ) AFTER=$( echo "$CUT_MARKS" | tail -n 1 ) From 1bcc5db5fe8068722ad624858be5374ea8d6289b Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Wed, 10 Mar 2021 22:34:41 +0100 Subject: [PATCH 10/42] Tighten criteria --- .github/workflows/build-plugin-zip.yml | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml index 80e2311ddb31bf..7b933e109daee9 100644 --- a/.github/workflows/build-plugin-zip.yml +++ b/.github/workflows/build-plugin-zip.yml @@ -18,7 +18,11 @@ jobs: bump-version: name: Bump version runs-on: ubuntu-latest - if: github.event_name == 'workflow_dispatch' + if: | + github.event_name == 'workflow_dispatch' && ( + github.ref == 'refs/heads/trunk' || + startsWith( github.ref, 'refs/heads/release/' ) + ) outputs: old_version: ${{ steps.get_version.outputs.old_version }} new_version: ${{ steps.get_version.outputs.new_version }} @@ -129,7 +133,11 @@ jobs: path: ./gutenberg.zip - name: Build release notes draft - if: github.event_name == 'workflow_dispatch' + if: | + github.event_name == 'workflow_dispatch' && ( + github.ref == 'refs/heads/trunk' || + startsWith( github.ref, 'refs/heads/release/' ) + ) run: | IFS='.' read -r -a VERSION_ARRAY <<< "${{ needs.bump-version.outputs.new_version }}" MILESTONE="Gutenberg ${VERSION_ARRAY[0]}.${VERSION_ARRAY[1]}" @@ -137,7 +145,11 @@ jobs: sed -ie '1,6d' release-notes.txt - name: Upload release notes artifact - if: github.event_name == 'workflow_dispatch' + if: | + github.event_name == 'workflow_dispatch' && ( + github.ref == 'refs/heads/trunk' || + startsWith( github.ref, 'refs/heads/release/' ) + ) uses: actions/upload-artifact@e448a9b857ee2131e752b06002bf0e093c65e571 # v2.2.2 with: name: release-notes @@ -147,7 +159,11 @@ jobs: name: Create Release Draft and Attach Asset needs: [bump-version, build] runs-on: ubuntu-latest - if: github.event_name == 'workflow_dispatch' + if: | + github.event_name == 'workflow_dispatch' && ( + github.ref == 'refs/heads/trunk' || + startsWith( github.ref, 'refs/heads/release/' ) + ) steps: - name: Set Release Version id: get_release_version From c4c00283aab73ef668f3b3daed03c67512d93131 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Wed, 10 Mar 2021 22:44:14 +0100 Subject: [PATCH 11/42] Tighten even more --- .github/workflows/build-plugin-zip.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml index 7b933e109daee9..875fef4bc701bb 100644 --- a/.github/workflows/build-plugin-zip.yml +++ b/.github/workflows/build-plugin-zip.yml @@ -19,7 +19,8 @@ jobs: name: Bump version runs-on: ubuntu-latest if: | - github.event_name == 'workflow_dispatch' && ( + github.event_name == 'workflow_dispatch' && + contains( [ "rc", "stable" ], github.event.inputs.version ) && ( github.ref == 'refs/heads/trunk' || startsWith( github.ref, 'refs/heads/release/' ) ) From bc567dda5e4ac0fe0e4479ef2ed2cbcc39ab1f67 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Wed, 10 Mar 2021 22:45:27 +0100 Subject: [PATCH 12/42] Simplify --- .github/workflows/build-plugin-zip.yml | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml index 875fef4bc701bb..8e85d63e507134 100644 --- a/.github/workflows/build-plugin-zip.yml +++ b/.github/workflows/build-plugin-zip.yml @@ -134,11 +134,7 @@ jobs: path: ./gutenberg.zip - name: Build release notes draft - if: | - github.event_name == 'workflow_dispatch' && ( - github.ref == 'refs/heads/trunk' || - startsWith( github.ref, 'refs/heads/release/' ) - ) + if: ${{ needs.bump-version.outputs.new_version }} run: | IFS='.' read -r -a VERSION_ARRAY <<< "${{ needs.bump-version.outputs.new_version }}" MILESTONE="Gutenberg ${VERSION_ARRAY[0]}.${VERSION_ARRAY[1]}" @@ -146,11 +142,7 @@ jobs: sed -ie '1,6d' release-notes.txt - name: Upload release notes artifact - if: | - github.event_name == 'workflow_dispatch' && ( - github.ref == 'refs/heads/trunk' || - startsWith( github.ref, 'refs/heads/release/' ) - ) + if: ${{ needs.bump-version.outputs.new_version }} uses: actions/upload-artifact@e448a9b857ee2131e752b06002bf0e093c65e571 # v2.2.2 with: name: release-notes @@ -160,11 +152,7 @@ jobs: name: Create Release Draft and Attach Asset needs: [bump-version, build] runs-on: ubuntu-latest - if: | - github.event_name == 'workflow_dispatch' && ( - github.ref == 'refs/heads/trunk' || - startsWith( github.ref, 'refs/heads/release/' ) - ) + if: ${{ needs.bump-version.outputs.new_version }} steps: - name: Set Release Version id: get_release_version From d66719c7311bb446095aff074ce87ba85074807d Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 12 Mar 2021 15:34:11 +0100 Subject: [PATCH 13/42] Make version bump work if run from trunk --- .github/workflows/build-plugin-zip.yml | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml index 8e85d63e507134..099c5223492c3c 100644 --- a/.github/workflows/build-plugin-zip.yml +++ b/.github/workflows/build-plugin-zip.yml @@ -62,10 +62,24 @@ jobs: RELEASE_BRANCH="release/${NEW_VERSION_ARRAY[0]}.${NEW_VERSION_ARRAY[1]}" echo "::set-output name=release_branch::$(echo $RELEASE_BRANCH)" - - name: Create release branch - if: ${{ github.event.inputs.version == 'rc' && ! contains( steps.get_version.outputs.old_version, 'rc' ) }} + - name: Create and switch to release branch + if: | + github.ref == 'trunk' && ( + github.event.inputs.version == 'rc' && + ! contains( steps.get_version.outputs.old_version, 'rc' ) + ) run: git checkout -b "${{ steps.get_version.outputs.release_branch }}" + - name: Switch to release branch + if: | + github.ref == 'trunk' && ! ( + github.event.inputs.version == 'rc' && + ! contains( steps.get_version.outputs.old_version, 'rc' ) + ) + run: | + git fetch --depth=1 origin "${{ steps.get_version.outputs.release_branch }}" + git checkout "${{ steps.get_version.outputs.release_branch }}" + - name: Update plugin version env: VERSION: ${{ steps.get_version.outputs.new_version }} From 35974272ece6ef3beb0304db552266c67ff3e1ab Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 12 Mar 2021 16:25:03 +0100 Subject: [PATCH 14/42] Include previous RCs' release notes in release notes draft --- .github/workflows/build-plugin-zip.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml index 099c5223492c3c..3c75f6e7c26ad3 100644 --- a/.github/workflows/build-plugin-zip.yml +++ b/.github/workflows/build-plugin-zip.yml @@ -154,6 +154,10 @@ jobs: MILESTONE="Gutenberg ${VERSION_ARRAY[0]}.${VERSION_ARRAY[1]}" npm run changelog -- --milestone="$MILESTONE" --unreleased > release-notes.txt sed -ie '1,6d' release-notes.txt + if [[ ${{ needs.bump-version.outputs.new_version }} != *"rc"* ]]; then + # Include previous RCs' release notes, if any + awk "/${RC_REGEX}/ {found=1;print;next} /${CHANGELOG_REGEX}/ {found=0} found" changelog.txt >> release-notes.txt + fi - name: Upload release notes artifact if: ${{ needs.bump-version.outputs.new_version }} From 2ce38ddcfdd2070408c2a780afb721db39d4806e Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 12 Mar 2021 19:23:17 +0100 Subject: [PATCH 15/42] Move release branch computation to separate job --- .../upload-release-to-plugin-repo.yml | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index 572773bca62464..59cd1dd545b797 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -5,24 +5,34 @@ on: name: Upload Gutenberg plugin to WordPress.org plugin repo jobs: + get-release-branch: + name: Get release branch name + runs-on: ubuntu-latest + if: github.event.release.assets[0] + outputs: + release_branch: ${{ steps.get_release_branch.outputs.release_branch }} + steps: + - name: Compute release branch name + id: get_release_branch + env: + TAG: ${{ github.event.release.tag_name }} + run: | + IFS='.' read -r -a VERSION_ARRAY <<< "${TAG#v}" + RELEASE_BRANCH="release/${VERSION_ARRAY[0]}.${VERSION_ARRAY[1]}" + echo "::set-output name=release_branch::$(echo $RELEASE_BRANCH)" + update-changelog: name: Update Changelog runs-on: ubuntu-latest if: github.event.release.assets[0] + needs: get-release-branch env: TAG: ${{ github.event.release.tag_name }} steps: - - name: Compute release branch name - id: get_release_branch - run: | - IFS='.' read -r -a VERSION_ARRAY <<< "${TAG#v}" - RELEASE_BRANCH="release/${VERSION_ARRAY[0]}.${VERSION_ARRAY[1]}" - echo "::set-output name=release_branch::$(echo $RELEASE_BRANCH)" - - name: Checkout code uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4 with: - ref: ${{ steps.get_release_branch.outputs.release_branch }} + ref: ${{ needs.get-release-branch.outputs.release_branch }} - name: Update the Changelog to include the release notes run: | @@ -54,7 +64,7 @@ jobs: run: | git add changelog.txt git commit -m "Update Changelog for ${TAG#v}" - git push --set-upstream origin "${{ steps.get_release_branch.outputs.release_branch }}" + git push --set-upstream origin "${{ needs.get-release-branch.outputs.release_branch }}" - name: Upload Changelog artifact uses: actions/upload-artifact@e448a9b857ee2131e752b06002bf0e093c65e571 # v2.2.2 @@ -68,7 +78,7 @@ jobs: git checkout trunk TRUNK_VERSION=$(jq --raw-output '.version' package.json) if [[ "${TAG#v}" == "$TRUNK_VERSION" ]]; then - git cherry-pick "${{ steps.get_release_branch.outputs.release_branch }}" + git cherry-pick "${{ needs.get-release-branch.outputs.release_branch }}" git push fi From 7e08860523bec265e2a6732608a8d7de8eb55015 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 12 Mar 2021 19:23:45 +0100 Subject: [PATCH 16/42] Update workflow description --- .github/workflows/upload-release-to-plugin-repo.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index 59cd1dd545b797..fdc13dc49497de 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -2,7 +2,7 @@ on: release: types: [published] -name: Upload Gutenberg plugin to WordPress.org plugin repo +name: Update Changelog and upload Gutenberg plugin to WordPress.org plugin repo jobs: get-release-branch: From 010904f328e3f90e11cdaa4f85f742a511971dc7 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 12 Mar 2021 19:44:46 +0100 Subject: [PATCH 17/42] Update changelog on both trunk and release branch --- .../upload-release-to-plugin-repo.yml | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index fdc13dc49497de..70d3e88e4f5579 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -22,17 +22,24 @@ jobs: echo "::set-output name=release_branch::$(echo $RELEASE_BRANCH)" update-changelog: - name: Update Changelog + name: Update Changelog on ${{ matrix.branch }} branch runs-on: ubuntu-latest if: github.event.release.assets[0] needs: get-release-branch env: TAG: ${{ github.event.release.tag_name }} + strategy: + matrix: + include: + - branch: trunk + label: trunk + - branch: ${{ needs.get-release-branch.outputs.release_branch }} + label: release steps: - name: Checkout code uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4 with: - ref: ${{ needs.get-release-branch.outputs.release_branch }} + ref: ${{ matrix.branch }} - name: Update the Changelog to include the release notes run: | @@ -64,24 +71,14 @@ jobs: run: | git add changelog.txt git commit -m "Update Changelog for ${TAG#v}" - git push --set-upstream origin "${{ needs.get-release-branch.outputs.release_branch }}" + git push --set-upstream origin "${{ matrix.branch }}" - name: Upload Changelog artifact uses: actions/upload-artifact@e448a9b857ee2131e752b06002bf0e093c65e571 # v2.2.2 with: - name: changelog + name: changelog ${{ matrix.label }} path: ./changelog.txt - - name: Cherry-pick Changelog update to trunk - run: | - git fetch --depth=1 origin trunk - git checkout trunk - TRUNK_VERSION=$(jq --raw-output '.version' package.json) - if [[ "${TAG#v}" == "$TRUNK_VERSION" ]]; then - git cherry-pick "${{ needs.get-release-branch.outputs.release_branch }}" - git push - fi - upload: name: Upload Gutenberg Plugin runs-on: ubuntu-latest @@ -123,7 +120,7 @@ jobs: - name: Download Changelog Artifact uses: actions/download-artifact@4a7a711286f30c025902c28b541c10e147a9b843 # v2.0.8 with: - name: changelog + name: changelog trunk path: trunk/changelog.txt - name: Commit the content changes From ed9ac68f6968798c9596f103ffc352ac4e75f885 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 12 Mar 2021 19:54:56 +0100 Subject: [PATCH 18/42] Avoid array --- .github/workflows/build-plugin-zip.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml index 3c75f6e7c26ad3..3f4e5568af1835 100644 --- a/.github/workflows/build-plugin-zip.yml +++ b/.github/workflows/build-plugin-zip.yml @@ -19,8 +19,10 @@ jobs: name: Bump version runs-on: ubuntu-latest if: | - github.event_name == 'workflow_dispatch' && - contains( [ "rc", "stable" ], github.event.inputs.version ) && ( + github.event_name == 'workflow_dispatch' && ( + github.event.inputs.version == 'rc' || + github.event.inputs.version == 'stable' + ) && ( github.ref == 'refs/heads/trunk' || startsWith( github.ref, 'refs/heads/release/' ) ) From d3d5ee050b616b098f4bffeb520e17b941a9a779 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 12 Mar 2021 20:11:44 +0100 Subject: [PATCH 19/42] Try rephrasing criteria --- .github/workflows/build-plugin-zip.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml index 3f4e5568af1835..246e30ec881841 100644 --- a/.github/workflows/build-plugin-zip.yml +++ b/.github/workflows/build-plugin-zip.yml @@ -74,9 +74,9 @@ jobs: - name: Switch to release branch if: | - github.ref == 'trunk' && ! ( - github.event.inputs.version == 'rc' && - ! contains( steps.get_version.outputs.old_version, 'rc' ) + github.ref == 'trunk' && ( + github.event.inputs.version == 'stable' || + contains( steps.get_version.outputs.old_version, 'rc' ) ) run: | git fetch --depth=1 origin "${{ steps.get_version.outputs.release_branch }}" From b6ab9c83f598f0562d88274b0a31d390fcd3129d Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 12 Mar 2021 20:21:30 +0100 Subject: [PATCH 20/42] Forget refs/heads/ prefix --- .github/workflows/build-plugin-zip.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml index 246e30ec881841..3cb0f787000f2a 100644 --- a/.github/workflows/build-plugin-zip.yml +++ b/.github/workflows/build-plugin-zip.yml @@ -66,7 +66,7 @@ jobs: - name: Create and switch to release branch if: | - github.ref == 'trunk' && ( + github.ref == 'refs/heads/trunk' && ( github.event.inputs.version == 'rc' && ! contains( steps.get_version.outputs.old_version, 'rc' ) ) @@ -74,7 +74,7 @@ jobs: - name: Switch to release branch if: | - github.ref == 'trunk' && ( + github.ref == 'refs/heads/trunk' && ( github.event.inputs.version == 'stable' || contains( steps.get_version.outputs.old_version, 'rc' ) ) @@ -98,7 +98,7 @@ jobs: git push --set-upstream origin "${{ steps.get_version.outputs.release_branch }}" - name: Cherry-pick to trunk - if: github.ref != 'trunk' + if: github.ref != 'refs/heads/trunk' run: | git fetch --depth=1 origin trunk git checkout trunk From c5de43717a07af5ed46f95fe7afb84235fb0e8ff Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 12 Mar 2021 20:24:45 +0100 Subject: [PATCH 21/42] Move name to the top --- .github/workflows/upload-release-to-plugin-repo.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index 70d3e88e4f5579..41395905761afd 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -1,9 +1,9 @@ +name: Update Changelog and upload Gutenberg plugin to WordPress.org plugin repo + on: release: types: [published] -name: Update Changelog and upload Gutenberg plugin to WordPress.org plugin repo - jobs: get-release-branch: name: Get release branch name From 81743c2a87ddbf6ac8bdf5a48909cea5bee0459c Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 12 Mar 2021 20:31:44 +0100 Subject: [PATCH 22/42] Also need to cherry-pick to trunk --- .github/workflows/build-plugin-zip.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml index 3cb0f787000f2a..687b418acdea45 100644 --- a/.github/workflows/build-plugin-zip.yml +++ b/.github/workflows/build-plugin-zip.yml @@ -98,7 +98,6 @@ jobs: git push --set-upstream origin "${{ steps.get_version.outputs.release_branch }}" - name: Cherry-pick to trunk - if: github.ref != 'refs/heads/trunk' run: | git fetch --depth=1 origin trunk git checkout trunk From 33f62ea984afd61f40f01c1d5d035a9988d3ac89 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 12 Mar 2021 20:37:32 +0100 Subject: [PATCH 23/42] Remove unnecessary criterion --- .github/workflows/build-plugin-zip.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml index 687b418acdea45..7c3157f7b4e497 100644 --- a/.github/workflows/build-plugin-zip.yml +++ b/.github/workflows/build-plugin-zip.yml @@ -171,7 +171,6 @@ jobs: name: Create Release Draft and Attach Asset needs: [bump-version, build] runs-on: ubuntu-latest - if: ${{ needs.bump-version.outputs.new_version }} steps: - name: Set Release Version id: get_release_version From e52aa39a675034f02f248842ce09edc3116bde32 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 12 Mar 2021 21:20:16 +0100 Subject: [PATCH 24/42] Forgot two regexes --- .github/workflows/build-plugin-zip.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml index 7c3157f7b4e497..96857fdf545243 100644 --- a/.github/workflows/build-plugin-zip.yml +++ b/.github/workflows/build-plugin-zip.yml @@ -150,13 +150,17 @@ jobs: - name: Build release notes draft if: ${{ needs.bump-version.outputs.new_version }} + env: + VERSION: ${{ needs.bump-version.outputs.new_version }} run: | - IFS='.' read -r -a VERSION_ARRAY <<< "${{ needs.bump-version.outputs.new_version }}" + IFS='.' read -r -a VERSION_ARRAY <<< "${VERSION}" MILESTONE="Gutenberg ${VERSION_ARRAY[0]}.${VERSION_ARRAY[1]}" npm run changelog -- --milestone="$MILESTONE" --unreleased > release-notes.txt sed -ie '1,6d' release-notes.txt if [[ ${{ needs.bump-version.outputs.new_version }} != *"rc"* ]]; then # Include previous RCs' release notes, if any + CHANGELOG_REGEX="=\s[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?\s=" + RC_REGEX="=\s${VERSION}(-rc\.[0-9]+)?\s=" awk "/${RC_REGEX}/ {found=1;print;next} /${CHANGELOG_REGEX}/ {found=0} found" changelog.txt >> release-notes.txt fi From ec4e451a1a4d2d1892ebe1dd5ea5705a0b99e1be Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 12 Mar 2021 21:49:57 +0100 Subject: [PATCH 25/42] Extract stable version for later use --- .../workflows/upload-release-to-plugin-repo.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index 41395905761afd..94129aeec18a6b 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -87,7 +87,7 @@ jobs: if: ${{ !github.event.release.prerelease && github.event.release.assets[0] }} env: PLUGIN_REPO_URL: 'https://plugins.svn.wordpress.org/gutenberg' - STABLE_TAG_REGEX: 'Stable tag: [0-9]\+\.[0-9]\+\.[0-9]\+\s*' + STABLE_VERSION_REGEX: '[0-9]\+\.[0-9]\+\.[0-9]\+\s*' SVN_USERNAME: ${{ secrets.svn_username }} SVN_PASSWORD: ${{ secrets.svn_password }} VERSION: ${{ github.event.release.name }} @@ -95,9 +95,11 @@ jobs: - name: Check out Gutenberg trunk from WP.org plugin repo run: svn checkout "$PLUGIN_REPO_URL/trunk" - - name: Get previous stable tag - id: get_previous_stable_tag - run: echo ::set-output name=stable_tag::$(grep "$STABLE_TAG_REGEX" ./trunk/readme.txt) + - name: Get previous stable version + id: get_previous_stable_version + env: + STABLE_TAG_REGEX: 'Stable tag: \K${{ env.STABLE_VERSION_REGEX }}' + run: echo ::set-output name=stable_version::$(grep -oP "${STABLE_TAG_REGEX}" ./trunk/readme.txt) - name: Delete everything working-directory: ./trunk @@ -114,7 +116,7 @@ jobs: - name: Replace the stable tag placeholder with the existing stable tag on the SVN repository env: STABLE_TAG_PLACEHOLDER: 'Stable tag: V\.V\.V' - STABLE_TAG: ${{ steps.get_previous_stable_tag.outputs.stable_tag }} + STABLE_TAG: 'Stable tag: ${{ steps.get_previous_stable_tag.outputs.stable_version }}' run: sed -i "s/${STABLE_TAG_PLACEHOLDER}/${STABLE_TAG}/g" ./trunk/readme.txt - name: Download Changelog Artifact @@ -140,6 +142,6 @@ jobs: - name: Update the plugin's stable version working-directory: ./trunk run: | - sed -i "s/${STABLE_TAG_REGEX}/Stable tag: ${VERSION}/g" ./readme.txt + sed -i "s/Stable tag: ${STABLE_VERSION_REGEX}/Stable tag: ${VERSION}/g" ./readme.txt svn commit -m "Releasing version $VERSION" \ --no-auth-cache --non-interactive --username "$SVN_USERNAME" --password "$SVN_PASSWORD" From b4926360b817104d59f41056fecb88c8fa4c8955 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Fri, 12 Mar 2021 21:50:27 +0100 Subject: [PATCH 26/42] oops --- .github/workflows/upload-release-to-plugin-repo.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index 94129aeec18a6b..58be8b68988384 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -116,7 +116,7 @@ jobs: - name: Replace the stable tag placeholder with the existing stable tag on the SVN repository env: STABLE_TAG_PLACEHOLDER: 'Stable tag: V\.V\.V' - STABLE_TAG: 'Stable tag: ${{ steps.get_previous_stable_tag.outputs.stable_version }}' + STABLE_TAG: 'Stable tag: ${{ steps.get_previous_stable_version.outputs.stable_version }}' run: sed -i "s/${STABLE_TAG_PLACEHOLDER}/${STABLE_TAG}/g" ./trunk/readme.txt - name: Download Changelog Artifact From f44ce2a877a73a23a5ce6401547e952691123003 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Sat, 13 Mar 2021 01:16:24 +0100 Subject: [PATCH 27/42] Limit to trunk --- .github/workflows/build-plugin-zip.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml index 96857fdf545243..3c9a7621592117 100644 --- a/.github/workflows/build-plugin-zip.yml +++ b/.github/workflows/build-plugin-zip.yml @@ -19,12 +19,10 @@ jobs: name: Bump version runs-on: ubuntu-latest if: | - github.event_name == 'workflow_dispatch' && ( + github.event_name == 'workflow_dispatch' && + github.ref == 'refs/heads/trunk' && ( github.event.inputs.version == 'rc' || github.event.inputs.version == 'stable' - ) && ( - github.ref == 'refs/heads/trunk' || - startsWith( github.ref, 'refs/heads/release/' ) ) outputs: old_version: ${{ steps.get_version.outputs.old_version }} From e022fd2fe69f3009ea3869c431450f3142e1505a Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Sat, 13 Mar 2021 01:17:45 +0100 Subject: [PATCH 28/42] Simplify a bit --- .github/workflows/upload-release-to-plugin-repo.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index 58be8b68988384..cf33578587f2f0 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -32,9 +32,7 @@ jobs: matrix: include: - branch: trunk - label: trunk - branch: ${{ needs.get-release-branch.outputs.release_branch }} - label: release steps: - name: Checkout code uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4 @@ -76,7 +74,7 @@ jobs: - name: Upload Changelog artifact uses: actions/upload-artifact@e448a9b857ee2131e752b06002bf0e093c65e571 # v2.2.2 with: - name: changelog ${{ matrix.label }} + name: ${{ matrix.branch }} changelog path: ./changelog.txt upload: @@ -122,7 +120,7 @@ jobs: - name: Download Changelog Artifact uses: actions/download-artifact@4a7a711286f30c025902c28b541c10e147a9b843 # v2.0.8 with: - name: changelog trunk + name: trunk changelog path: trunk/changelog.txt - name: Commit the content changes From 426633e872a918c450b69fad797ec75a4c50b492 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Sat, 13 Mar 2021 01:22:23 +0100 Subject: [PATCH 29/42] Simplify more --- .github/workflows/build-plugin-zip.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml index 3c9a7621592117..a35ae23a97f75e 100644 --- a/.github/workflows/build-plugin-zip.yml +++ b/.github/workflows/build-plugin-zip.yml @@ -64,18 +64,14 @@ jobs: - name: Create and switch to release branch if: | - github.ref == 'refs/heads/trunk' && ( - github.event.inputs.version == 'rc' && - ! contains( steps.get_version.outputs.old_version, 'rc' ) - ) + github.event.inputs.version == 'rc' && + ! contains( steps.get_version.outputs.old_version, 'rc' ) run: git checkout -b "${{ steps.get_version.outputs.release_branch }}" - name: Switch to release branch if: | - github.ref == 'refs/heads/trunk' && ( - github.event.inputs.version == 'stable' || - contains( steps.get_version.outputs.old_version, 'rc' ) - ) + github.event.inputs.version == 'stable' || + contains( steps.get_version.outputs.old_version, 'rc' ) run: | git fetch --depth=1 origin "${{ steps.get_version.outputs.release_branch }}" git checkout "${{ steps.get_version.outputs.release_branch }}" From 85c3affe5ebdf0062b2d0b9f6676223f385369ea Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Sat, 13 Mar 2021 01:23:07 +0100 Subject: [PATCH 30/42] No need to fetch trunk again --- .github/workflows/build-plugin-zip.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml index a35ae23a97f75e..1a95fc8ffba582 100644 --- a/.github/workflows/build-plugin-zip.yml +++ b/.github/workflows/build-plugin-zip.yml @@ -93,7 +93,6 @@ jobs: - name: Cherry-pick to trunk run: | - git fetch --depth=1 origin trunk git checkout trunk TRUNK_VERSION=$(jq --raw-output '.version' package.json) if [[ ${{ steps.get_version.outputs.old_version }} == "$TRUNK_VERSION" ]]; then From 8720d6b7f41dda68a73d73e173f3ab24d0067232 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Sat, 13 Mar 2021 01:25:07 +0100 Subject: [PATCH 31/42] Move git config step to a place where it makes more sense --- .github/workflows/build-plugin-zip.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml index 1a95fc8ffba582..2bce11a3c0f932 100644 --- a/.github/workflows/build-plugin-zip.yml +++ b/.github/workflows/build-plugin-zip.yml @@ -32,11 +32,6 @@ jobs: - name: Checkout code uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4 - - name: Configure git user name and email - run: | - git config user.name "Gutenberg Repository Automation" - git config user.email gutenberg@wordpress.org - - name: Compute old and new version id: get_version run: | @@ -62,6 +57,11 @@ jobs: RELEASE_BRANCH="release/${NEW_VERSION_ARRAY[0]}.${NEW_VERSION_ARRAY[1]}" echo "::set-output name=release_branch::$(echo $RELEASE_BRANCH)" + - name: Configure git user name and email + run: | + git config user.name "Gutenberg Repository Automation" + git config user.email gutenberg@wordpress.org + - name: Create and switch to release branch if: | github.event.inputs.version == 'rc' && From a32c0f5919a4bc7f215a178f8653d3106e9d8ef0 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 15 Mar 2021 14:43:59 +0100 Subject: [PATCH 32/42] Revert "Simplify a bit" This reverts commit e022fd2fe69f3009ea3869c431450f3142e1505a. --- .github/workflows/upload-release-to-plugin-repo.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index cf33578587f2f0..58be8b68988384 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -32,7 +32,9 @@ jobs: matrix: include: - branch: trunk + label: trunk - branch: ${{ needs.get-release-branch.outputs.release_branch }} + label: release steps: - name: Checkout code uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4 @@ -74,7 +76,7 @@ jobs: - name: Upload Changelog artifact uses: actions/upload-artifact@e448a9b857ee2131e752b06002bf0e093c65e571 # v2.2.2 with: - name: ${{ matrix.branch }} changelog + name: changelog ${{ matrix.label }} path: ./changelog.txt upload: @@ -120,7 +122,7 @@ jobs: - name: Download Changelog Artifact uses: actions/download-artifact@4a7a711286f30c025902c28b541c10e147a9b843 # v2.0.8 with: - name: trunk changelog + name: changelog trunk path: trunk/changelog.txt - name: Commit the content changes From 45bfea9eeaaff064f6300f0a4d7e4b75950a8e41 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 15 Mar 2021 18:12:35 +0100 Subject: [PATCH 33/42] Try without escape characters --- .github/workflows/upload-release-to-plugin-repo.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index 58be8b68988384..5759e38819b7cb 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -57,7 +57,9 @@ jobs: # Okay, we have all we need to build the new Changelog. head -n $(( "${BEFORE}" - 1 )) changelog.txt > new_changelog.txt printf '= %s =\n\n' "${TAG#v}" >> new_changelog.txt - echo "${{ github.event.release.body }}" >> new_changelog.txt + cat <<- "EOF" >> new_changelog.txt + ${{ github.event.release.body }} + EOF printf '\n\n' >> new_changelog.txt tail -n +"${AFTER}" changelog.txt >> new_changelog.txt mv new_changelog.txt changelog.txt From 748fac769bc6ba2f5862cae35279317d8032c81f Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 15 Mar 2021 18:26:26 +0100 Subject: [PATCH 34/42] Fix download-artifact action path arg --- .github/workflows/upload-release-to-plugin-repo.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index 5759e38819b7cb..854983ebe4e938 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -125,7 +125,7 @@ jobs: uses: actions/download-artifact@4a7a711286f30c025902c28b541c10e147a9b843 # v2.0.8 with: name: changelog trunk - path: trunk/changelog.txt + path: trunk - name: Commit the content changes working-directory: ./trunk From ff03948e0fc83a0310fd8670993febf43c17485d Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Thu, 18 Mar 2021 14:11:44 +0100 Subject: [PATCH 35/42] Add one explanatory comment --- .github/workflows/upload-release-to-plugin-repo.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index 854983ebe4e938..1f8cc5192af96e 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -57,6 +57,7 @@ jobs: # Okay, we have all we need to build the new Changelog. head -n $(( "${BEFORE}" - 1 )) changelog.txt > new_changelog.txt printf '= %s =\n\n' "${TAG#v}" >> new_changelog.txt + # Need to use a heredoc in order to preserve special characters. cat <<- "EOF" >> new_changelog.txt ${{ github.event.release.body }} EOF From fc79221bd4f4f6fbf9c14210ffa5e386fff140bd Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Thu, 18 Mar 2021 14:14:35 +0100 Subject: [PATCH 36/42] Trim empty lines at beginning and end of file --- .github/workflows/upload-release-to-plugin-repo.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index 1f8cc5192af96e..7451016bd6c11c 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -61,6 +61,9 @@ jobs: cat <<- "EOF" >> new_changelog.txt ${{ github.event.release.body }} EOF + # Normalize empty lines: Trim them from beginning and end of file... + awk 'NF {p=1} p' <<< "$(< new_changelog.txt)" > new_changelog.txt + # ...then add two empty lines at the end. printf '\n\n' >> new_changelog.txt tail -n +"${AFTER}" changelog.txt >> new_changelog.txt mv new_changelog.txt changelog.txt From f9b925eb48f1f763a3fcddb8fe3236de3838931a Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Thu, 18 Mar 2021 14:30:30 +0100 Subject: [PATCH 37/42] Add workflow dispatch banner screenshot --- .../code/workflow-dispatch-banner.png | Bin 0 -> 68372 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/contributors/code/workflow-dispatch-banner.png diff --git a/docs/contributors/code/workflow-dispatch-banner.png b/docs/contributors/code/workflow-dispatch-banner.png new file mode 100644 index 0000000000000000000000000000000000000000..d05c88d753f2f8b36ac4764c0e84b617dfdcd909 GIT binary patch literal 68372 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

eL^vA0oc!NgE0+ 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

I2aN`RCs-g~a{E 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 literal 0 HcmV?d00001 From 3c03fd5b0a370bb01d54d75708132e6a27837c34 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Thu, 18 Mar 2021 18:50:56 +0100 Subject: [PATCH 38/42] Update release docs --- docs/contributors/code/release.md | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/docs/contributors/code/release.md b/docs/contributors/code/release.md index c08bf5cdde81c0..85b88b4137a340 100644 --- a/docs/contributors/code/release.md +++ b/docs/contributors/code/release.md @@ -22,27 +22,26 @@ If critical bugs are discovered on stable versions of the plugin, patch versions > Note that at the time of writing, the tool doesn't support releasing consecutive RC releases. However, it is possible to use the tool for patch releases following the first stable release. -The plugin release process is entirely automated. To release the RC version of the plugin, run the following command and follow the instructions: +The plugin release process is entirely automated and happens solely on GitHub -- i.e. it doesn't require any steps to be run locally on your machine. -```bash -./bin/plugin/cli.js rc -``` +For your convenience, here's an [11-minute video walkthrough](https://youtu.be/TnSgJd3zpJY) that demonstrates the release process. It's recommended to watch this if you're unfamiliar with it. The process is also documented in the following paragraphs. -To release a stable version, run: +In order to start the release process, go to Gutenberg's GitHub repository's Actions tab, and locate the ["Build Gutenberg Plugin Zip" action](https://github.com/WordPress/gutenberg/actions/workflows/build-plugin-zip.yml). Note the blue banner that says "This workflow has a `workflow_dispatch` event trigger.", and expand the "Run workflow" dropdown on its right hand side. -```bash -./bin/plugin/cli.js stable -``` +![Run workflow dropdown](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/contributors/code/workflow-dispatch-banner.png) + +To release a release candidate (RC) version of the plugin, enter `rc`. To release a stable version, enter `stable`. In each case, press the green "Run workflow" button. + +This will trigger a GitHub Actions (GHA) workflow that bumps the plugin version, builds the Gutenberg plugin .zip file, creates a release draft, and attaches the plugin .zip file to it. This part of the process typically takes a little under six minutes. You'll see that workflow appear at the top of the list, right under the blue banner. Once it's finished, it'll change its status icon from a yellow dot to a green checkmark. You can follow along in a more detailed view by clicking on the workflow. -During the release process, you'll be asked to provide: +As soon as the workflow has finished, you'll find the release draft under https://github.com/WordPress/gutenberg/releases. The draft is pre-populated with changelog entries based on previous release candidates for this version, and any changes that have since been cherry-picked to the release branch. Thus, when releasing the first stable version of a series, make sure to delete any RC version headers (that are only there for your information), and to move the more recent changes to the corresponding sections below. Furthermore, take some time to edit the release notes into a more legible format, by grouping related entries under common bullet points. Don't rush this part -- it's important to bring the release notes into a nice shape. You don't have to do it all in one go -- you can save the draft and come back to it later. -- A changelog: prepare one beforehand by following the instructions below. -- A [personal access token](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line): have one ready beforehand by visiting [this page](https://github.com/settings/tokens/new?scopes=repo,admin:org,write:packages), if you haven't got one yet. -- User and password for your GitHub account: if 2FA is enabled for your account (it should), you need to provide a personal access token instead of password (you can use the one necessary for the release). +Only once you're happy with the shape of the release notes should you press the green "Publish release" button. This will create a `git` tag for the version, publish the release, and trigger [another GHA workflow](https://github.com/WordPress/gutenberg/actions/workflows/upload-release-to-plugin-repo.yml) that has a twofold purpose: -The release script will create a `git` tag for the release and push it to GitHub. This triggers a GitHub workflow that builds the plugin, creates a release draft (based on the Changelog), and attaches the plugin zip. This will take a couple of minutes. You will then find the release draft at https://github.com/WordPress/gutenberg/releases. You can edit it further (but note that the changes won't be propagated to `changelog.txt`). Once you're happy with it, press the 'Publish' button. +1. Use the release notes that you just edited to update `changelog.txt`, and +2. upload the new plugin version to the WordPress.org plugin repository (SVN) (only if you're releasing a stable version). -If you're releasing a stable version (rather than an RC), this will trigger a GitHub action that will upload the plugin to the WordPress.org plugin repository (SVN). This action needs approval by a member of the [`gutenberg-core` team](https://github.com/orgs/WordPress/teams/gutenberg-core). Locate the ["Upload Gutenberg plugin to WordPress.org plugin repo" workflow](https://github.com/WordPress/gutenberg/actions/workflows/upload-release-to-plugin-repo.yml) for the new version, and have it [approved](https://docs.github.com/en/actions/managing-workflow-runs/reviewing-deployments#approving-or-rejecting-a-job). +The latter step needs approval by a member of the [`gutenberg-core` team](https://github.com/orgs/WordPress/teams/gutenberg-core). Locate the ["Upload Gutenberg plugin to WordPress.org plugin repo" workflow](https://github.com/WordPress/gutenberg/actions/workflows/upload-release-to-plugin-repo.yml) for the new version, and have it [approved](https://docs.github.com/en/actions/managing-workflow-runs/reviewing-deployments#approving-or-rejecting-a-job). ### Manual Release Process From 3f3fabc8a6d14022b45bbe77e149c03519ed0023 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Thu, 18 Mar 2021 19:09:04 +0100 Subject: [PATCH 39/42] Remove now-obsolete parts of Manual release process section --- docs/contributors/code/release.md | 191 ++---------------------------- 1 file changed, 12 insertions(+), 179 deletions(-) diff --git a/docs/contributors/code/release.md b/docs/contributors/code/release.md index 85b88b4137a340..52b7dfdb152510 100644 --- a/docs/contributors/code/release.md +++ b/docs/contributors/code/release.md @@ -34,7 +34,7 @@ To release a release candidate (RC) version of the plugin, enter `rc`. To releas This will trigger a GitHub Actions (GHA) workflow that bumps the plugin version, builds the Gutenberg plugin .zip file, creates a release draft, and attaches the plugin .zip file to it. This part of the process typically takes a little under six minutes. You'll see that workflow appear at the top of the list, right under the blue banner. Once it's finished, it'll change its status icon from a yellow dot to a green checkmark. You can follow along in a more detailed view by clicking on the workflow. -As soon as the workflow has finished, you'll find the release draft under https://github.com/WordPress/gutenberg/releases. The draft is pre-populated with changelog entries based on previous release candidates for this version, and any changes that have since been cherry-picked to the release branch. Thus, when releasing the first stable version of a series, make sure to delete any RC version headers (that are only there for your information), and to move the more recent changes to the corresponding sections below. Furthermore, take some time to edit the release notes into a more legible format, by grouping related entries under common bullet points. Don't rush this part -- it's important to bring the release notes into a nice shape. You don't have to do it all in one go -- you can save the draft and come back to it later. +As soon as the workflow has finished, you'll find the release draft under https://github.com/WordPress/gutenberg/releases. The draft is pre-populated with changelog entries based on previous release candidates for this version, and any changes that have since been cherry-picked to the release branch. Thus, when releasing the first stable version of a series, make sure to delete any RC version headers (that are only there for your information), and to move the more recent changes to the corresponding sections below. Furthermore, take some time to edit the release notes into a more legible format, by grouping related entries under common bullet points. Don't rush this part -- it's important to bring the release notes into a nice shape. You don't have to do it all in one go -- you can save the draft and come back to it later. You can find some more tips on writing the release notes and post in the section below. Only once you're happy with the shape of the release notes should you press the green "Publish release" button. This will create a `git` tag for the version, publish the release, and trigger [another GHA workflow](https://github.com/WordPress/gutenberg/actions/workflows/upload-release-to-plugin-repo.yml) that has a twofold purpose: @@ -43,39 +43,14 @@ Only once you're happy with the shape of the release notes should you press the The latter step needs approval by a member of the [`gutenberg-core` team](https://github.com/orgs/WordPress/teams/gutenberg-core). Locate the ["Upload Gutenberg plugin to WordPress.org plugin repo" workflow](https://github.com/WordPress/gutenberg/actions/workflows/upload-release-to-plugin-repo.yml) for the new version, and have it [approved](https://docs.github.com/en/actions/managing-workflow-runs/reviewing-deployments#approving-or-rejecting-a-job). -### Manual Release Process - -#### Creating the first Release Candidate - -Releasing the first release candidate for this milestone (`x.x`) involves: - -1. writing a release blog post and changelog -2. creating the release branch -3. bumping the version and tagging the release -4. building the plugin -5. publishing the release to GitHub -6. publishing the call for testing - -##### Writing the Release Post and Changelog - -To generate a changelog for a release, use the changelog generator tool: - -``` -npm run changelog -``` - -By default, this will search for and organize all pull requests associated with the milestone for the next version of the project. - -To override the default behavior, you can pass one or both of the following options. Remember to use `--` to let NPM pass the options to the script. +This will cause the new version to be available to users of WordPress all over the globe! πŸ’ƒ +You should check that folks are able to install the new version from their Dashboard. -- `--milestone `: Provide the title of the milestone for which the changelog should be generated. This should exactly match the title as shown on [the milestones page](https://github.com/WordPress/gutenberg/milestones). - - Example: `npm run changelog -- --milestone="Gutenberg 8.1"` -- `--token `: Provide a [GitHub personal access token](https://github.com/settings/tokens) for authenticating requests. This should only be necessary if you run the script frequently enough to been blocked by [rate limiting](https://developer.github.com/v3/#rate-limiting). - - Example: `npm run changelog -- --token="..."` -- `--unreleased`: Only list PRs that have been closed after the latest release in the milestone's series has been published. In other words, only list PRs that haven't been part of a release yet. - - Example: `npm run changelog -- --milestone="Gutenberg 9.8" --unreleased`. If the latest version in the 9.8 series is 9.8.3, only show PRs for the in the 9.8 series that were closed (merged) after 9.8.3 was published. +Once released, all that's left to do is writing a release post on [make.wordpress.org/core](https://make.wordpress.org/core/). You can find some tips on that below. +### Writing the Release Notes and Post -The script will output a generated changelog, grouped by pull request label. _Note that this is intended to be a starting point for release notes_. You will still want to manually review and curate the changelog entries. +The release notes draft is auto-generated by a script that looks for pull requests for the current milestone, and groups them by pull request label. +This is intended to be a starting point for release notes; you will still want to manually review and curate the changelog entries. Guidelines for proof-reading include: @@ -91,159 +66,17 @@ You should also include a performance audit at the end of the release post. You Compile this to a draft post on [make.wordpress.org/core](https://make.wordpress.org/core/); this post should be published after the actual release. -##### Creating the Release Branch - -For each milestone (let's assume it's `x.x` here), a release branch is used to release all RCs and minor releases. For the first RC of the milestone, a release branch is created from trunk. - -``` -git checkout trunk -git checkout -b release/x.x -git push origin release/x.x -``` - -##### Bumping the Version and Tagging the Release - -1. Checkout the `release/x.x` branch. -2. Create [a commit like this](https://github.com/WordPress/gutenberg/pull/13125/commits/13fa651dadc2472abb9b95f80db9d5f23e63ae9c), bumping the version number in `gutenberg.php`, `package.json`, and `package-lock.json` to `x.x.0-rc.1`. -3. Create a Pull Request from the release branch into `trunk` using the changelog as a description and ensure the tests pass properly. -4. Tag the RC version. `git tag vx.x.0-rc.1` from the release branch. -5. Push the tag `git push --tags`. -6. Merge the version bump pull request and avoid removing the release branch. - -##### Build the Plugin - -1. Run `git fetch --tags`. -2. Check out the tag for this release, you should run `git checkout vx.x.0-rc.1`. -3. Run `npm run build:plugin-zip` from the root of project. This packages a zip file with a release build of `gutenberg.zip`. - -##### Publish the Release on GitHub - -1. [Create a new release on GitHub](https://github.com/WordPress/gutenberg/releases/new). -2. If you were releasing the `x.x.0-rc.1` release candidate, label it `x.x.0-rc.1` and use the `vx.x.x-rc.1` as a tag. -3. Upload the `gutenberg.zip` file into the release. -4. Use the changelog as a description of the release. -5. Publish the release. - -Here's an example [release candidate page](https://github.com/WordPress/gutenberg/releases/tag/v4.6.0-rc.1); yours should look like that when you're finished. +If you don't have access to [make.wordpress.org/core](https://make.wordpress.org/core/), ping [someone on the Gutenberg Core team](https://github.com/orgs/WordPress/teams/gutenberg-core) in the [WordPress #core-editor Slack channel](https://wordpress.slack.com/messages/C02QB2JS7) to publish the post. -#### Creating Release Candidate Patches (done via `git cherry-pick`) +### Creating Release Candidate Patches (done via `git cherry-pick`) -If a bug is found in a release candidate and a fix is committed to `trunk`, we should include that fix in a new release candidate. To do this you'll need to use `git cherry-pick` to add these changes to the milestone's release branch. This way only fixes are added to the release candidate and not all the new code that has landed on `trunk` since tagging: +If a bug is found in a release candidate and a fix is committed to `trunk`, we should include that fix in the stable version (or optionally in another release candidate before that). To do this you'll need to use `git cherry-pick` to add these changes to the milestone's release branch. This way only the desired fixes are added rather than all the new code that has landed on `trunk` since tagging: 1. Checkout the corresponding release branch with: `git checkout release/x.x`. 2. Cherry-pick fix commits (in chronological order) with `git cherry-pick [SHA]`. -3. Create [a commit like this](https://github.com/WordPress/gutenberg/pull/13125/commits/13fa651dadc2472abb9b95f80db9d5f23e63ae9c), bumping the version number in `gutenberg.php`, `package.json`, and `package-lock.json` to `x.x.0-rc.2`. -4. Create a Pull Request from the release branch into `trunk` using the changelog as a description and ensure the tests pass properly. Note that if there there are merge conflicts, Travis CI will not run on the PR. Run tests locally using `npm run test` and `npm run test-e2e` if this happens. -5. Tag the RC version. `git tag vx.x.0-rc.2` from the release branch. -6. Push the tag `git push --tags`. -7. Create a branch for bumping the version number. `git checkout -b bump/x.x`. -8. Create a Pull Request from the `bump/x.x` branch into `trunk` using the - changelog as a description. -9. Merge the version bump pull request. -10. Follow the steps in [build the plugin](#build-the-plugin) and [publish the release on GitHub](#publish-the-release-on-github). - -You can copy the existing changelog from the previous release candidate. Let other contributors know that a new release candidate has been released in the [`#core-editor` channel](https://wordpress.slack.com/messages/C02QB2JS7) and the call for testing post. - -### Official Gutenberg Releasesβ„’ - -The process of releasing Gutenberg is similar to creating a release candidate, except we don't use the `-rc.X` in the `git` tag and we publish a new branch in the subversion repository. This updates the version available in the WordPress plugin repository and will cause WordPress sites around the world to prompt users to update to this new version. - -#### Creating a Release - -Creating a release involves: - -1. verifying the release blog post and changelog -2. bumping the version -3. building the plugin -4. publishing the new release to GitHub -5. committing to the [plugin repository] -6. publishing the release blog post - -##### Verifying the Release Post and Changelog - -1. Check the draft post on [make.wordpress.org/core](https://make.wordpress.org/core/); make sure the changelog reflects what's shipping in the release. - -##### Bumping the Version - -1. Checkout the release branch `git checkout release/x.x`. - -**Note:** This branch should never be removed or rebased. When we want to merge something from it to trunk and conflicts exist/may exist we use a temporary branch `bump/x.x`. - -2. Create [a commit like this](https://github.com/WordPress/gutenberg/commit/00d01049685f11f9bb721ad3437cb928814ab2a2#diff-b9cfc7f2cdf78a7f4b91a753d10865a2), removing the `-rc.X` from the version number in `gutenberg.php`, `package.json`, and `package-lock.json`. -3. Create a new branch called `bump/x.x` from `release/x.x` and switch to it: `git checkout -b bump/x.x`. -4. Create a pull request from `bump/x.x` to `trunk`. Verify the continuous integrations tests pass, before continuing to the next step even if conflicts exist. -5. Rebase `bump/x.x` against `origin/trunk` using `git fetch origin && git rebase origin/trunk`. -6. Force push the branch `bump/x.x` using `git push --force-with-lease`. -7. Switch to the `release/x.x` branch. Tag the version from the release branch `git tag vx.x.0`. -8. Push the tag `git push --tags`. -9. Merge the version bump pull request. - -##### Build the Plugin - -1. Run `git fetch --tags`. -2. Check out the tag for this release, you should run `git checkout vx.x.0`. -3. Run `npm run build:plugin-zip` from the root of project. This packages a zip file with a release build of `gutenberg.zip`. - -##### Publish the Release on GitHub - -1. [Create a new release on GitHub](https://github.com/WordPress/gutenberg/releases/new). -2. If you were releasing the `x.x.0` release candidate, label it `x.x.0` and use the `vx.x.x` as a tag. -3. Upload the a `gutenberg.zip` file into the release. -4. Use the changelog as a description of the release. -5. Publish the release. - -##### Commit to the Plugin Repository - -You'll need to use Subversion to publish the plugin to WordPress.org. - -1. Do an SVN checkout of `https://wordpress.org/plugins/gutenberg/trunk`: - -- If this is your first checkout, run: `svn checkout https://plugins.svn.wordpress.org/gutenberg/trunk` -- If you already have a copy, run: `svn up` - -2. Delete the contents except for the `readme.txt` and `changelog.txt` files (these files don’t exist in the `git` repo, only in Subversion). -3. Extract the contents of the zip file. -4. Edit `readme.txt`, replacing the changelog for the previous version with the current release's changelog. -5. Add the changelog for the current release to `changelog.txt`. -6. Add new files/remove deleted files from the repository: - -```bash -# Add new files: -svn st | grep '^\?' | awk '{print $2}' | xargs svn add # add the -r option to xargs if you use a linux-based OS -# Delete old files: -svn st | grep '^!' | awk '{print $2}' | xargs svn rm # add the -r option to xargs if you use a linux-based OS -``` - -7. Commit the new version: - -```bash -# Replace X.X.X with your version: -svn ci -m "Committing Gutenberg version X.X.X" -``` - -8. Tag the new version: - -```bash -svn cp https://plugins.svn.wordpress.org/gutenberg/trunk https://plugins.svn.wordpress.org/gutenberg/tags/X.X.X -m "Tagging Gutenberg version X.X.X" -``` - -9. Edit `readme.txt` to point to the new tag. The **Stable version** header in `readme.txt` should be updated to match the new release version number. After updating and committing that, the new version should be released: - -```bash -svn ci -m "Releasing Gutenberg version X.X.X" -``` - -This will cause the new version to be available to users of WordPress all over the globe! πŸ’ƒ - -You should check that folks are able to install the new version from their Dashboard. - -### Publish the Release Blog Post - -1. Publish the [make/core](https://make.wordpress.org/core/) release blog post drafted earlier. -2. Pat yourself on the back! πŸ‘ - -If you don't have access to [make.wordpress.org/core](https://make.wordpress.org/core/), ping [someone on the Gutenberg Core team](https://github.com/orgs/WordPress/teams/gutenberg-core) in the [WordPress #core-editor Slack channel](https://wordpress.slack.com/messages/C02QB2JS7) to publish the post. +3. When done, push the changes to GitHub: `git push`. +If you decide that the fixes deserve another release candidate before the stable version is published, create one through by following the instructions above. Let other contributors know that a new release candidate has been released in the [`#core-editor` channel](https://wordpress.slack.com/messages/C02QB2JS7). ## Packages Releases to npm and WordPress Core Updates The Gutenberg repository mirrors the [WordPress SVN repository](https://make.wordpress.org/core/handbook/about/release-cycle/) in terms of branching for each SVN branch, a corresponding Gutenberg `wp/*` branch is created: From be25920339bc8bdf3c159bd5adf58eb9587fa0aa Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Thu, 18 Mar 2021 19:13:24 +0100 Subject: [PATCH 40/42] Remove release script and commands --- bin/plugin/cli.js | 13 - bin/plugin/commands/release.js | 542 --------------------------------- 2 files changed, 555 deletions(-) delete mode 100644 bin/plugin/commands/release.js diff --git a/bin/plugin/cli.js b/bin/plugin/cli.js index c1a978c33b131d..e3a22ebd3d603c 100755 --- a/bin/plugin/cli.js +++ b/bin/plugin/cli.js @@ -19,7 +19,6 @@ const catchException = ( command ) => { /** * Internal dependencies */ -const { releaseRC, releaseStable } = require( './commands/release' ); const { publishNpmLatestDistTag, publishNpmNextDistTag, @@ -27,18 +26,6 @@ const { const { getReleaseChangelog } = require( './commands/changelog' ); const { runPerformanceTests } = require( './commands/performance' ); -program - .command( 'release-plugin-rc' ) - .alias( 'rc' ) - .description( 'Release an RC version of the plugin' ) - .action( catchException( releaseRC ) ); - -program - .command( 'release-plugin-stable' ) - .alias( 'stable' ) - .description( 'Release a stable version of the plugin' ) - .action( catchException( releaseStable ) ); - program .command( 'publish-npm-packages-latest' ) .alias( 'npm-latest' ) diff --git a/bin/plugin/commands/release.js b/bin/plugin/commands/release.js deleted file mode 100644 index a554ac42f1288d..00000000000000 --- a/bin/plugin/commands/release.js +++ /dev/null @@ -1,542 +0,0 @@ -/** - * External dependencies - */ -const inquirer = require( 'inquirer' ); -const fs = require( 'fs' ); -const semver = require( 'semver' ); -const Octokit = require( '@octokit/rest' ); -const { sprintf } = require( 'sprintf-js' ); - -/** - * Internal dependencies - */ -const { log, formats } = require( '../lib/logger' ); -const { askForConfirmation, runStep, readJSONFile } = require( '../lib/utils' ); -const git = require( '../lib/git' ); -const { getNextMajorVersion } = require( '../lib/version' ); -const { - getIssuesByMilestone, - getMilestoneByTitle, -} = require( '../lib/milestone' ); -const config = require( '../config' ); -const { - runGitRepositoryCloneStep, - runCleanLocalFoldersStep, - findReleaseBranchName, -} = require( './common' ); - -/** - * Creates a new release branch based on the last package.json version - * and chooses the next version number. - * - * @param {string} gitWorkingDirectoryPath Git Working Directory Path. - * @param {string} abortMessage Abort Message. - * @param {string} version the new version. - * @param {string} releaseBranch The release branch to push to. - */ -async function runReleaseBranchCreationStep( - gitWorkingDirectoryPath, - abortMessage, - version, - releaseBranch -) { - await runStep( 'Creating the release branch', abortMessage, async () => { - await askForConfirmation( - 'The Plugin version to be used is ' + - formats.success( version ) + - '. Proceed with the creation of the release branch?', - true, - abortMessage - ); - - // Creates the release branch - await git.createLocalBranch( gitWorkingDirectoryPath, releaseBranch ); - - log( - '>> The local release branch ' + - formats.success( releaseBranch ) + - ' has been successfully created.' - ); - } ); -} - -/** - * Checkouts out the release branch. - * - * @param {string} gitWorkingDirectoryPath Git Working Directory Path. - * @param {string} abortMessage Abort Message. - * @param {string} version The new version. - * @param {string} releaseBranch The release branch to checkout. - */ -async function runReleaseBranchCheckoutStep( - gitWorkingDirectoryPath, - abortMessage, - version, - releaseBranch -) { - await runStep( - 'Getting into the release branch', - abortMessage, - async () => { - await git.checkoutRemoteBranch( - gitWorkingDirectoryPath, - releaseBranch - ); - - log( - '>> The local release branch ' + - formats.success( releaseBranch ) + - ' has been successfully checked out.' - ); - - await askForConfirmation( - 'The Version to release is ' + - formats.success( version ) + - '. Proceed?', - true, - abortMessage - ); - } - ); -} - -/** - * Bump the version in the different files (package.json, package-lock.json...) - * and commit the changes. - * - * @param {string} gitWorkingDirectoryPath Git Working Directory Path. - * @param {string} version Version to use. - * @param {string} changelog Changelog to use. - * @param {string} abortMessage Abort message. - * - * @return {Promise} hash of the version bump commit. - */ -async function runBumpPluginVersionUpdateChangelogAndCommitStep( - gitWorkingDirectoryPath, - version, - changelog, - abortMessage -) { - let commitHash; - await runStep( 'Updating the plugin version', abortMessage, async () => { - const packageJsonPath = gitWorkingDirectoryPath + '/package.json'; - const packageLockPath = gitWorkingDirectoryPath + '/package-lock.json'; - const pluginFilePath = - gitWorkingDirectoryPath + '/' + config.pluginEntryPoint; - const packageJson = readJSONFile( packageJsonPath ); - const packageLock = readJSONFile( packageLockPath ); - const newPackageJson = { - ...packageJson, - version, - }; - fs.writeFileSync( - packageJsonPath, - JSON.stringify( newPackageJson, null, '\t' ) + '\n' - ); - const newPackageLock = { - ...packageLock, - version, - }; - fs.writeFileSync( - packageLockPath, - JSON.stringify( newPackageLock, null, '\t' ) + '\n' - ); - const content = fs.readFileSync( pluginFilePath, 'utf8' ); - fs.writeFileSync( - pluginFilePath, - content.replace( - ' * Version: ' + packageJson.version, - ' * Version: ' + version - ) - ); - - // Update the content of the readme.txt file - const readmePath = gitWorkingDirectoryPath + '/readme.txt'; - const readmeFileContent = fs.readFileSync( readmePath, 'utf8' ); - const newReadmeContent = - readmeFileContent.substr( - 0, - readmeFileContent.indexOf( '== Changelog ==' ) - ) + - '== Changelog ==\n\n' + - `To read the changelog for ${ config.name } ${ version }, please navigate to the release page.` + - '\n'; - fs.writeFileSync( readmePath, newReadmeContent ); - - // Update the content of the changelog.txt file - const stableVersion = version.match( /[0-9]+\.[0-9]+\.[0-9]+/ )[ 0 ]; - const changelogPath = gitWorkingDirectoryPath + '/changelog.txt'; - const changelogFileContent = fs.readFileSync( changelogPath, 'utf8' ); - const versionHeader = '= ' + version + ' =\n\n'; - const regexToSearch = /=\s([0-9]+\.[0-9]+\.[0-9]+)(-rc\.[0-9]+)?\s=\n\n/g; - let lastDifferentVersionMatch = regexToSearch.exec( - changelogFileContent - ); - if ( lastDifferentVersionMatch[ 1 ] === stableVersion ) { - lastDifferentVersionMatch = regexToSearch.exec( - changelogFileContent - ); - } - const newChangelogContent = - '== Changelog ==\n\n' + - versionHeader + - changelog + - '\n\n' + - changelogFileContent.substr( lastDifferentVersionMatch.index ); - fs.writeFileSync( changelogPath, newChangelogContent ); - - log( - '>> The plugin version and changelog have been updated successfully.' - ); - - // Commit the version bump - await askForConfirmation( - 'Please check the diff. Proceed with the version bump commit?', - true, - abortMessage - ); - commitHash = await git.commit( - gitWorkingDirectoryPath, - 'Bump plugin version to ' + version, - [ - packageJsonPath, - packageLockPath, - pluginFilePath, - readmePath, - changelogPath, - ] - ); - log( - '>> The plugin version bump and changelog update have been committed successfully.' - ); - } ); - - return commitHash; -} - -/** - * Create a local Git Tag. - * - * @param {string} gitWorkingDirectoryPath Git Working Directory Path. - * @param {string} version Version to use. - * @param {string} abortMessage Abort message. - */ -async function runCreateGitTagStep( - gitWorkingDirectoryPath, - version, - abortMessage -) { - await runStep( 'Creating the git tag', abortMessage, async () => { - await askForConfirmation( - 'Proceed with the creation of the git tag?', - true, - abortMessage - ); - - await git.createLocalTag( gitWorkingDirectoryPath, 'v' + version ); - - log( - '>> The ' + - formats.success( 'v' + version ) + - ' tag has been created successfully.' - ); - } ); -} - -/** - * Push the local Git Changes and Tags to the remote repository. - * - * @param {string} gitWorkingDirectoryPath Git Working Directory Path. - * @param {string} releaseBranch Release branch name. - * @param {string} abortMessage Abort message. - */ -async function runPushGitChangesStep( - gitWorkingDirectoryPath, - releaseBranch, - abortMessage -) { - await runStep( - 'Pushing the release branch and the tag', - abortMessage, - async () => { - await askForConfirmation( - 'The release branch and the tag are going to be pushed to the remote repository. Continue?', - true, - abortMessage - ); - - await git.pushBranchToOrigin( - gitWorkingDirectoryPath, - releaseBranch - ); - - await git.pushTagsToOrigin( gitWorkingDirectoryPath ); - } - ); -} - -/** - * Cherry-picks the version bump commit into trunk. - * - * @param {string} gitWorkingDirectoryPath Git Working Directory Path. - * @param {string} commitHash Commit to cherry-pick. - * @param {string} abortMessage Abort message. - */ -async function runCherrypickBumpCommitIntoTrunkStep( - gitWorkingDirectoryPath, - commitHash, - abortMessage -) { - await runStep( - 'Cherry-picking the bump commit into trunk', - abortMessage, - async () => { - await askForConfirmation( - 'The plugin is now released. Proceed with the version bump in the trunk branch?', - true, - abortMessage - ); - await git.discardLocalChanges( gitWorkingDirectoryPath ); - await git.resetLocalBranchAgainstOrigin( - gitWorkingDirectoryPath, - 'trunk' - ); - await git.cherrypickCommitIntoBranch( - gitWorkingDirectoryPath, - commitHash, - 'trunk' - ); - await git.pushBranchToOrigin( gitWorkingDirectoryPath, 'trunk' ); - } - ); -} - -/** - * Checks whether the milestone associated with the release has open issues or - * pull requests. Returns a promise resolving to true if there are no open issue - * or pull requests, or false otherwise. - * - * @param {string} version Release version. - * - * @return {Promise} Promise resolving to boolean indicating whether - * the milestone is clear of open issues. - */ -async function isMilestoneClear( version ) { - const { githubRepositoryOwner: owner, githubRepositoryName: name } = config; - const octokit = new Octokit(); - const milestone = await getMilestoneByTitle( - octokit, - owner, - name, - // Disable reason: valid-sprintf applies to `@wordpress/i18n` where - // strings are expected to need to be extracted, and thus variables are - // not allowed. This string will not need to be extracted. - // eslint-disable-next-line @wordpress/valid-sprintf - sprintf( config.versionMilestoneFormat, { - ...config, - ...semver.parse( version ), - } ) - ); - - if ( ! milestone ) { - // If milestone can't be found, it's not especially actionable to warn - // that the milestone isn't clear. `true` is the sensible fallback. - return true; - } - - const issues = await getIssuesByMilestone( - new Octokit(), - owner, - name, - milestone.number, - 'open' - ); - - return issues.length === 0; -} - -/** - * Release a new version. - * - * @param {boolean} isRC Whether it's an RC release or not. - * - * @return {string} The new version. - */ -async function releasePlugin( isRC = true ) { - // This is a variable that contains the abort message shown when the script is aborted. - let abortMessage = 'Aborting!'; - let version, releaseBranch; - - const temporaryFolders = []; - - await askForConfirmation( 'Ready to go? ' ); - - const { changelog } = await inquirer.prompt( [ - { - type: 'editor', - name: 'changelog', - message: 'Please provide the CHANGELOG of the release (markdown)', - }, - ] ); - - // Cloning the Git repository - const gitWorkingDirectory = await runGitRepositoryCloneStep( abortMessage ); - temporaryFolders.push( gitWorkingDirectory ); - - const packageJsonPath = gitWorkingDirectory + '/package.json'; - const packageJson = readJSONFile( packageJsonPath ); - const packageVersion = packageJson.version; - const parsedPackagedVersion = semver.parse( packageJson.version ); - const isPackageVersionRC = parsedPackagedVersion.prerelease.length > 0; - - // Are we going to release an RC? - if ( isRC ) { - // We are releasing an RC. - // If packageVersion is stable, then generate new branch and RC1. - // If packageVersion is RC, then checkout branch and inc RC. - if ( ! isPackageVersionRC ) { - version = getNextMajorVersion( packageVersion ) + '-rc.1'; - const parsedVersion = semver.parse( version ); - - releaseBranch = - 'release/' + parsedVersion.major + '.' + parsedVersion.minor; - - await runReleaseBranchCreationStep( - gitWorkingDirectory, - abortMessage, - version, - releaseBranch - ); - } else { - version = semver.inc( packageVersion, 'prerelease', 'rc' ); - releaseBranch = findReleaseBranchName( packageJsonPath ); - - await runReleaseBranchCheckoutStep( - gitWorkingDirectory, - abortMessage, - version, - releaseBranch - ); - } - } else { - // We are releasing a stable version. - // If packageVersion is stable, then checkout the branch and inc patch. - // If packageVersion is RC, then checkout the branch and inc patch, effectively removing the RC. - version = semver.inc( packageVersion, 'patch' ); - releaseBranch = findReleaseBranchName( packageJsonPath ); - - await runReleaseBranchCheckoutStep( - gitWorkingDirectory, - abortMessage, - version, - findReleaseBranchName( packageJsonPath ) - ); - } - - if ( ! ( await isMilestoneClear( version ) ) ) { - await askForConfirmation( - 'There are still open issues in the milestone. Are you sure you want to proceed?', - false, - abortMessage - ); - } - - // Bumping the version and commit. - const commitHash = await runBumpPluginVersionUpdateChangelogAndCommitStep( - gitWorkingDirectory, - version, - changelog, - abortMessage - ); - - // Creating the git tag - await runCreateGitTagStep( gitWorkingDirectory, version, abortMessage ); - - // Push the local changes - await runPushGitChangesStep( - gitWorkingDirectory, - releaseBranch, - abortMessage - ); - - abortMessage = - 'Aborting! Make sure to manually cherry-pick the ' + - formats.success( commitHash ) + - ' commit to the trunk branch.'; - - // Cherry-picking the bump commit into trunk - await runCherrypickBumpCommitIntoTrunkStep( - gitWorkingDirectory, - commitHash, - abortMessage - ); - - abortMessage = 'Aborting! The release is finished though.'; - await runCleanLocalFoldersStep( temporaryFolders, abortMessage ); - - return version; -} - -async function releaseRC() { - log( - formats.title( '\nπŸ’ƒ Time to release ' + config.name + ' πŸ•Ί\n\n' ), - 'Welcome! This tool is going to help you release a new RC version of the Plugin.\n', - 'It goes through different steps: creating the release branch, bumping the plugin version, tagging the release, and pushing the tag to GitHub.\n', - 'Once the tag is pushed to GitHub, GitHub will build the plugin ZIP, and attach it to a release draft.\n' - ); - - const version = await releasePlugin( true ); - - log( - '\n>> πŸŽ‰ The ' + - config.name + - ' version ' + - formats.success( version ) + - ' has been successfully tagged.\n', - "In a few minutes, you'll be able to find the GitHub release draft here: " + - formats.success( config.wpRepositoryReleasesURL ) + - '\n', - "Don't forget to publish the release once the draft is available!\n", - 'Thanks for performing the release!\n' - ); -} - -async function releaseStable() { - log( - formats.title( '\nπŸ’ƒ Time to release ' + config.name + ' πŸ•Ί\n\n' ), - 'Welcome! This tool is going to help you release a new stable version of the Plugin.\n', - 'It goes through different steps: bumping the plugin version, tagging the release, and pushing the tag to GitHub.\n', - 'Once the tag is pushed to GitHub, GitHub will build the plugin ZIP, and attach it to a release draft.\n', - 'To have the release uploaded to the WP.org plugin repository SVN, you need approval from a member of the ' + - config.team + - ' Team.\n' - ); - - const version = await releasePlugin( false ); - - log( - '\n>> πŸŽ‰ ' + - config.name + - ' ' + - formats.success( version ) + - ' has been successfully tagged.\n', - "In a few minutes, you'll be able to find the GitHub release draft here: " + - formats.success( config.wpRepositoryReleasesURL ) + - '\n', - "Don't forget to publish the release once the draft is available!\n", - 'Once published, the upload to the WP.org plugin repository needs approval from a member of the ' + - config.team + - ' Team at ' + - formats.success( - config.githubRepositoryURL + - 'actions/workflows/upload-release-to-plugin-repo.yml ' - ) + - '.\n', - "Thanks for performing the release! and don't forget to publish the release post.\n" - ); -} - -module.exports = { - releaseRC, - releaseStable, -}; From 4309c6a97149d36b25bd06fe9c060ddacbc73b93 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Thu, 18 Mar 2021 19:35:17 +0100 Subject: [PATCH 41/42] Typo --- docs/contributors/code/release.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributors/code/release.md b/docs/contributors/code/release.md index 52b7dfdb152510..aba102136232c8 100644 --- a/docs/contributors/code/release.md +++ b/docs/contributors/code/release.md @@ -76,7 +76,7 @@ If a bug is found in a release candidate and a fix is committed to `trunk`, we s 2. Cherry-pick fix commits (in chronological order) with `git cherry-pick [SHA]`. 3. When done, push the changes to GitHub: `git push`. -If you decide that the fixes deserve another release candidate before the stable version is published, create one through by following the instructions above. Let other contributors know that a new release candidate has been released in the [`#core-editor` channel](https://wordpress.slack.com/messages/C02QB2JS7). +If you decide that the fixes deserve another release candidate before the stable version is published, create one by following the instructions above. Let other contributors know that a new release candidate has been released in the [`#core-editor` channel](https://wordpress.slack.com/messages/C02QB2JS7). ## Packages Releases to npm and WordPress Core Updates The Gutenberg repository mirrors the [WordPress SVN repository](https://make.wordpress.org/core/handbook/about/release-cycle/) in terms of branching for each SVN branch, a corresponding Gutenberg `wp/*` branch is created: From 248108b438175e2f2aaef1ab6f6ce842ef2cf67d Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Thu, 18 Mar 2021 20:10:51 +0100 Subject: [PATCH 42/42] Trim properly --- .github/workflows/upload-release-to-plugin-repo.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index 7451016bd6c11c..06b02347e72378 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -58,11 +58,11 @@ jobs: head -n $(( "${BEFORE}" - 1 )) changelog.txt > new_changelog.txt printf '= %s =\n\n' "${TAG#v}" >> new_changelog.txt # Need to use a heredoc in order to preserve special characters. - cat <<- "EOF" >> new_changelog.txt + cat <<- "EOF" > release_notes.txt ${{ github.event.release.body }} EOF # Normalize empty lines: Trim them from beginning and end of file... - awk 'NF {p=1} p' <<< "$(< new_changelog.txt)" > new_changelog.txt + awk 'NF {p=1} p' <<< "$(< release_notes.txt)" >> new_changelog.txt # ...then add two empty lines at the end. printf '\n\n' >> new_changelog.txt tail -n +"${AFTER}" changelog.txt >> new_changelog.txt