Commit 983249743c for woocommerce
commit 983249743c5ec999e8b511d7be93026c599db1a3
Author: Jorge A. Torres <jorge.torres@automattic.com>
Date: Fri Jan 9 08:23:01 2026 +0000
Various release workflow enhancements (#62110)
diff --git a/.github/ISSUE_TEMPLATE/new-cfe-template.yml b/.github/ISSUE_TEMPLATE/new-cfe-template.yml
deleted file mode 100644
index 2d19a879e6..0000000000
--- a/.github/ISSUE_TEMPLATE/new-cfe-template.yml
+++ /dev/null
@@ -1,125 +0,0 @@
-name: 🚧 Code freeze exception request [RETIRED]
-description: Request to process a particular pull request as a code freeze exception.
-title: "[CFE]: "
-labels: ["code freeze exception"]
-body:
- - type: markdown
- attributes:
- value: |
- 🚧 The code freeze exception approval process is retired. If you need to apply a FIX to the frozen release do one of the following: 🚧
-
- **Make a PR against trunk (Most Common)**
- 1. Create your pull request against trunk.
- 2. Add the milestone corresponding to the release you are fixing. E.g. use milestone `10.0.0` for targeting the `release/10.0` branch.
- 3. Get your pull request reviewed and merged.
- 4. A new pull request will be generated against the release branch with your changes.
-
- **Make a PR against the release branch**
- 1. Create your pull request against the frozen release branch.
- 2. Add the label `cherry pick to trunk` to the PR.
- 3. Get your pull request reviewed and merged.
- 4. A new pull request will be generated against trunk with your changes.
-
- It is important that the newly generated pull request is reviewed, tested, and merged as soon as possible to avoid
- delaying the next planned build release.
-
- For full instructions on backporting, please see our [Backporting Guide](https://developer.woocommerce.com/docs/contribution/releases/backporting/).
-
- ---
- Your target WC version will be extracted from the base of your PR. Ex: Base branch of PR is `release/9.5`. The Target WC version will be 9.5
- Use this template to request changes to be included in a version of WooCommerce that is past its code freeze date.
- **The request will be reviewed** and accepted or denied:
-
- * If accepted, please test your changes in against the release branch and merge.
- * If rejected, please change the base against `trunk` and merge it.
-
- In either case you are responsible for managing the pull request as usual (provide a description, assign reviewers, ensure that CI jobs pass...)
- - type: textarea
- id: pr-urls
- attributes:
- label: "Which PR needs to be included? (please do not enter multiple PRs)"
- description: "Pull request URL against the release branch"
- placeholder: |
- https://github.com/woocommerce/woocommerce/pull/1234
- validations:
- required: true
- - type: textarea
- id: why-needed
- attributes:
- label: "Why do these PRs need to break the code freeze?"
- placeholder: "This is a revert of ... which introduced a bug that causes ..."
- validations:
- required: true
- - type: textarea
- attributes:
- id: consequence-if-not-included
- label: "What is the consequence if this exception does not get included?"
- placeholder: "The ... flow will be broken for ... users"
- validations:
- required: true
- - type: textarea
- id: plan-if-defects-discovered
- attributes:
- label: "What is the plan should defects to be discovered in these PRs ahead of final release?"
- placeholder: "Reverting this PR and ... would be enough"
- validations:
- required: true
- - type: textarea
- id: how-to-communicate
- attributes:
- label: "How should this change be communicated in the release post on the public developer blog:"
- description: "See the blog at [https://developer.woo.com/blog/](https://developer.woo.com/blog/) cc @woocommerce/developer-advocacy"
- placeholder: "There is no need to add new communication to the already planned one."
- validations:
- required: true
- - type: textarea
- id: who-to-ask
- attributes:
- label: "If you’re not available and we have questions about this request, is there another person(s) and/or a team that we can ping?"
- placeholder: "@person or anyone from the ... team"
- validations:
- required: true
- - type: markdown
- attributes:
- value: |
- ## Escape Analysis
- For the sake of expediting this request, the details below can be completed at a later time should you so choose.
- However, the Escape Analysis section is **required** for any accepted Code Freeze Exception.
- In this section:
- * Think about how the bug escaped your team.
- * Write down ideas on how you could prevent this bug – for example, by writing automated tests, creating a new process, or updating documentation.
- * Make a plan with your team to implement the changes proposed above in order to catch the bug earlier next time and add the related tasks to your backlog.
- * Please, add a separate comment that includes the details for the Escape Analysis. Together with this Escape Analysis, please make sure to include an actionable item that covers the gap exposed by this analysis. It could be a GitHub issue, the reference of a new item to discuss in a team meeting, etc. Anything that prevents this analysis from getting lost in P2 will be useful.
- - type: checkboxes
- id: escape-analysis-completed
- attributes:
- label: "Acknowledgement"
- options:
- - label: "I understand that I need to write an incident report (aka Escape Analysis) as a comment on this post. This is required for the request to be accepted."
- required: true
- - label: "I understand that I need to create an issue as a result of the Escape Analysis and reference it in a comment on this post. This can be done at a later time, but it is required for this request to be closed."
- required: true
- - label: "I understand that I will need to merge the PR to the target `release` branch for which the fix was intended, when my request is approved"
- required: true
- - label: "I understand that I will need to merge the followup cherry-picked PR created by the github-actions bot to `trunk` when my request is approved"
- required: true
- - type: markdown
- attributes:
- value: |
- ## Closing checklist
- Please ensure that the following checklist is completed before closing the request:
- - type: checkboxes
- id: closing-checklist
- attributes:
- label: "Closing checklist for Solaris"
- options:
- - label: "Escape analysis completed"
- required: false
- - label: "The fix is raised from intended release branch. For a fix targeting WC X.Y, the base branch should be `release/X.Y`"
- required: false
- - label: "PR merged to target `release` branch"
- required: false
- - label: "PR cherry-picked to `trunk` (you can find the links in PR comments to verify)"
- required: false
- - label: "Followup issue if any"
- required: false
diff --git a/.github/workflows/release-build-zip-file.yml b/.github/workflows/release-build-zip-file.yml
index 1608291875..2d78059466 100644
--- a/.github/workflows/release-build-zip-file.yml
+++ b/.github/workflows/release-build-zip-file.yml
@@ -143,6 +143,13 @@ jobs:
exit 1
fi
+ # Changelog section must have at least one entry.
+ changelog_section=$( awk '/^== Changelog ==$/,/^\[See changelog/' checkout-branch/plugins/woocommerce/readme.txt )
+ if ! echo "$changelog_section" | grep -qE '^\* '; then
+ echo "::error::Pre-build verification: changelog section in readme.txt has no entries. Please add at least one entry before releasing."
+ exit 1
+ fi
+
echo "release_version=$branch_plugin_version" >> $GITHUB_OUTPUT
- name: 'Pre-build verification: db updates'
diff --git a/.github/workflows/release-bump-version.yml b/.github/workflows/release-bump-version.yml
index 033db20514..00122db3a8 100644
--- a/.github/workflows/release-bump-version.yml
+++ b/.github/workflows/release-bump-version.yml
@@ -128,8 +128,9 @@ jobs:
# Update version header in woocommerce.php.
sed -i -E "s/Version: [0-9]+\.[0-9]+\.[0-9]+.*$/Version: $NEXT_VERSION/" woocommerce.php
+
# Update changelog in readme.txt.
- sed -i -E "s/^= [0-9]+\.[0-9]+\.[0-9]+(\-[a-z]+(\.[0-9]+)*)? (.*) =/= $NEXT_VERSION \3 =/" readme.txt
+ sed -i -E "s/^= [0-9]+\.[0-9]+\.[0-9]+(\-[a-z]+(\.[0-9]+)*)? (.*) =/= $NEXT_VERSION $(date +%Y)-XX-XX =/" readme.txt
if [[ "$CLEAR_CHANGELOG" == "true" ]]; then
perl -i -p0e 's/(== Changelog ==.*= \d+\.\d+\.\d+.*=\n)(.*)(\n\[See changelog for all versions\].*)/$1$3/igs' readme.txt
@@ -176,12 +177,22 @@ jobs:
milestone="${{ steps.compute-new-version.outputs.prMilestone }}"
milestone_arg=""
if [[ -n "$milestone" ]]; then
- if [ "$(gh api repos/${{ github.repository }}/milestones --jq "any(.[]; .title==\"$milestone\")")" = "true" ]; then
+ milestone_info=$(gh api repos/${{ github.repository }}/milestones --jq ".[] | select(.title==\"$milestone\")")
+
+ if [[ -n "$milestone_info" ]]; then
+ is_closed=$(echo "$milestone_info" | jq -r '.state=="closed"')
+ milestone_number=$(echo "$milestone_info" | jq -r '.number')
+
+ if [[ "$is_closed" == "true" ]]; then
+ echo "Milestone '$milestone' is closed, reopening it..."
+ gh api --method PATCH repos/${{ github.repository }}/milestones/"$milestone_number" -f state=open
+ fi
+
milestone_arg="--milestone $milestone"
fi
fi
- # Create PR
+ # Create PR.
gh pr create \
--title 'Bump WooCommerce version to `${{ steps.compute-new-version.outputs.nextVersion }}` on `${{ inputs.branch }}`' \
--body 'This PR updates the versions in ${{ inputs.branch }} to ${{ steps.compute-new-version.outputs.nextVersion }}.'$'\n\n''<!-- [x] This Pull Request does not require a changelog -->' \
diff --git a/.github/workflows/release-cfe-prr-issue-validation.yml b/.github/workflows/release-cfe-prr-issue-validation.yml
index 4da7b0161f..24fc392243 100644
--- a/.github/workflows/release-cfe-prr-issue-validation.yml
+++ b/.github/workflows/release-cfe-prr-issue-validation.yml
@@ -227,7 +227,7 @@ jobs:
const slackMessage = `*${labelName === 'code freeze exception' ? ':ice_cube: Feature Freeze Exception request' : ':release: Point Release Request'} (${releaseNumber})*: _<${issueUrl}|${issueTitle}>_`;
- const githubComment = `Your request for a ${labelName === 'code freeze exception' ? 'Feature Freeze Exception' : 'Point Release'} targeting WC version **${releaseNumber}** has been successfully created and is pending approval from @woocommerce/flux.`;
+ const githubComment = `Your request for a ${labelName === 'code freeze exception' ? 'Feature Freeze Exception' : 'Point Release'} targeting WC version **${releaseNumber}** has been successfully created and is pending approval from the release lead.`;
// Set the message as a core output
core.setOutput('SLACK_MESSAGE', slackMessage);
@@ -288,7 +288,7 @@ jobs:
gh issue comment "$ISSUE_URL" --body "This request has been approved. Please merge the PR to release branch when it is approved and ready to be merged.
You are targeting your fix for WC version: **${RELEASE_NUMBER}**."
-
+
- name: Comment on pull request that request has been approved
env:
OWNER: ${{ github.event.repository.owner.login }}
@@ -298,14 +298,14 @@ jobs:
RELEASE_NUMBER: ${{ needs.verify.outputs.release_number }}
run: |
gh pr comment $PR_NUMBER --repo "$OWNER/$REPO" --body "✅ **Your Point Release Request has been approved!**
-
+
This PR is now approved for inclusion in WC version **${RELEASE_NUMBER}**.
-
+
**Next steps:**
- Ensure your PR is ready for review and merge
- Once approved by reviewers, merge this PR to the release branch
- Cherry-pick labels have been automatically added, please remove any that should not apply prior to merging."
-
+
- name: Set Slack Message
id: set-message
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea #v7.0.1
diff --git a/.github/workflows/shared-cherry-pick.yml b/.github/workflows/shared-cherry-pick.yml
index 73d93c058f..e7f5160a0a 100644
--- a/.github/workflows/shared-cherry-pick.yml
+++ b/.github/workflows/shared-cherry-pick.yml
@@ -61,7 +61,7 @@ jobs:
with:
script: |
const prNumber = parseInt('${{ inputs.pr_number }}', 10);
-
+
if (isNaN(prNumber)) {
core.setOutput('error_message', 'PR number is not a valid integer');
core.setFailed('PR number is not a valid integer');
@@ -91,10 +91,10 @@ jobs:
core.info(`PR #${prNumber} is merged with commit SHA: ${pr.merge_commit_sha}`);
} catch (error) {
- const errorMsg = error.status === 404
+ const errorMsg = error.status === 404
? `Pull request #${prNumber} does not exist.`
: `Error fetching PR: ${error.message}`;
-
+
core.setOutput('error_message', errorMsg);
core.setFailed(errorMsg);
}
@@ -108,7 +108,7 @@ jobs:
const targetBranch = '${{ inputs.target_branch }}';
const prNumber = '${{ inputs.pr_number }}';
const cherryPickBranch = `cherry-pick-PR${prNumber}-to-${targetBranch}`;
-
+
try {
// Check target branch exists
await github.rest.repos.getBranch({
@@ -140,10 +140,10 @@ jobs:
core.info(`Cherry-pick branch '${cherryPickBranch}' is available`);
} catch (error) {
- const errorMsg = error.status === 404
+ const errorMsg = error.status === 404
? `Target branch '${targetBranch}' does not exist`
: `Error checking target branch: ${error.message}`;
-
+
core.setOutput('error_message', errorMsg);
core.setFailed(errorMsg);
}
@@ -186,16 +186,16 @@ jobs:
id: cherry-pick
run: |
set -e
-
+
CHERRY_PICK_BRANCH="${{ needs.verify.outputs.cherry_pick_branch }}"
MERGE_COMMIT_SHA="${{ needs.verify.outputs.merge_commit_sha }}"
PR_NUMBER="${{ inputs.pr_number }}"
TARGET_BRANCH="${{ inputs.target_branch }}"
-
+
# Create cherry-pick branch
git checkout -b "$CHERRY_PICK_BRANCH"
git fetch origin "$MERGE_COMMIT_SHA"
-
+
# Attempt cherry-pick with explicit conflict handling
if git cherry-pick -m1 "$MERGE_COMMIT_SHA"; then
# Success case
@@ -206,11 +206,11 @@ jobs:
if git status --porcelain | grep -E "^(DD|AU|UD|UA|DU|AA|UU)"; then
echo "has_conflicts=true" >> $GITHUB_OUTPUT
echo "Cherry-pick had conflicts - resolving automatically"
-
+
# For modify/delete conflicts, we need to handle them specifically
# Add all files (both conflicted and resolved)
git add .
-
+
# Check if we have anything to commit
if ! git diff --cached --quiet; then
git commit -m "Cherry-pick $MERGE_COMMIT_SHA with unresolved conflicts from #$PR_NUMBER"
@@ -219,7 +219,7 @@ jobs:
echo "error_message=Cherry-pick had conflicts but resulted in no changes to commit. Manual resolution required." >> $GITHUB_OUTPUT
exit 1
fi
-
+
else
# No conflicts detected, check for other failure reasons
if git log --oneline -1 2>/dev/null | grep -q "$MERGE_COMMIT_SHA"; then
@@ -232,13 +232,13 @@ jobs:
fi
fi
fi
-
+
# Validate we have commits to push
if git rev-list --count "origin/$TARGET_BRANCH..HEAD" | grep -q "^0$"; then
echo "error_message=Cherry-pick completed but created no new commits. PR #$PR_NUMBER appears to already exist in '$TARGET_BRANCH'." >> $GITHUB_OUTPUT
exit 1
fi
-
+
# Push the branch
git push origin "$CHERRY_PICK_BRANCH"
@@ -252,7 +252,7 @@ jobs:
const targetBranch = '${{ inputs.target_branch }}';
const cherryPickBranch = '${{ needs.verify.outputs.cherry_pick_branch }}';
const hasConflicts = '${{ steps.cherry-pick.outputs.has_conflicts }}' === 'true';
-
+
try {
// Get original PR details
const { data: originalPr } = await github.rest.pulls.get({
@@ -260,16 +260,16 @@ jobs:
repo: context.repo.repo,
pull_number: prNumber
});
-
+
// Create PR body
let prBody = `This PR is a cherry-pick of #${prNumber} to \`${targetBranch}\`.\n\n`;
-
+
if (hasConflicts) {
prBody += `⚠️ **WARNING**: This cherry-pick contained conflicts that have not been resolved. Please review the changes carefully before merging!\n\n`;
}
-
+
prBody += `## Original PR Description\n\n${originalPr.body || '*No description provided*'}`;
-
+
// Create new PR
const { data: newPr } = await github.rest.pulls.create({
owner: context.repo.owner,
@@ -279,15 +279,6 @@ jobs:
title: `[Backport to ${targetBranch}] ${originalPr.title}`,
body: prBody
});
-
- // Add "Release" label to the PR
- await github.rest.issues.addLabels({
- owner: context.repo.owner,
- repo: context.repo.repo,
- issue_number: newPr.number,
- labels: ['Release']
- });
-
await github.rest.pulls.requestReviewers({
owner: context.repo.owner,
repo: context.repo.repo,
@@ -316,12 +307,12 @@ jobs:
const originalAuthor = '${{ steps.create-pr.outputs.original_author }}';
const originalMerger = '${{ steps.create-pr.outputs.original_merger }}';
const hasConflicts = '${{ steps.cherry-pick.outputs.has_conflicts }}' === 'true';
-
+
// Assign PR to original author and merger
- const assignees = [originalAuthor, originalMerger].filter((user, index, arr) =>
+ const assignees = [originalAuthor, originalMerger].filter((user, index, arr) =>
user && user.trim() !== '' && arr.indexOf(user) === index
);
-
+
if (assignees.length > 0) {
try {
await github.rest.issues.addAssignees({
@@ -375,7 +366,7 @@ jobs:
run: |
verify_status="${{ needs.verify.outputs.status }}"
cherry_status="${{ needs.cherry-pick.outputs.status }}"
-
+
if [[ "$verify_status" != "success" ]]; then
echo "status=failed" >> $GITHUB_OUTPUT
echo "error_message=${{ needs.verify.outputs.error_message }}" >> $GITHUB_OUTPUT
diff --git a/tools/monorepo-utils/src/code-freeze/commands/changelog/lib/index.ts b/tools/monorepo-utils/src/code-freeze/commands/changelog/lib/index.ts
index 23bef09f4a..261369d778 100644
--- a/tools/monorepo-utils/src/code-freeze/commands/changelog/lib/index.ts
+++ b/tools/monorepo-utils/src/code-freeze/commands/changelog/lib/index.ts
@@ -274,7 +274,7 @@ export const updateReleaseBranchChangelogs = async (
}
Logger.notice( `Creating PR for ${ branch }` );
const warningMessage = noEntriesWritten
- ? '> [!WARNING]\n> No entries were written to the changelog. Consider adding a generic changelog entry before releasing.\n\n'
+ ? '> [!CAUTION]\n> No entries were written to the changelog. You will be required to manually add a changelog entry before releasing.\n\n'
: '';
const pullRequest = await createPullRequest( {
owner,