Commit e85fa9538a for woocommerce
commit e85fa9538a9dcc189d0de1569e6d5d969c5dcb60
Author: Jorge A. Torres <jorge.torres@automattic.com>
Date: Thu Nov 27 17:21:31 2025 +0000
Automate cleanup of old milestones at feature freeze time (#62135)
diff --git a/.github/workflows/release-bump-version.yml b/.github/workflows/release-bump-version.yml
index ff831134e7..2ace76d284 100644
--- a/.github/workflows/release-bump-version.yml
+++ b/.github/workflows/release-bump-version.yml
@@ -173,23 +173,3 @@ jobs:
--head ${branch_name} \
--label Release
- - name: Ensure milestone for dev version exists
- if: ${{ inputs.bump-type == 'dev' }}
- uses: actions/github-script@v7
- with:
- script: |
- const milestone = '${{ steps.compute-new-version.outputs.nextStable }}';
-
- try {
- await github.rest.issues.createMilestone({
- owner: context.repo.owner,
- repo: context.repo.repo,
- title: milestone,
- });
- } catch ( e ) {
- if ( e.response.data.errors?.some( (err) => 'already_exists' === err.code ) ) {
- console.log( `Milestone ${ milestone } already exists.` );
- } else {
- throw e;
- }
- }
diff --git a/.github/workflows/release-code-freeze.yml b/.github/workflows/release-code-freeze.yml
index a169514c81..27840280a8 100644
--- a/.github/workflows/release-code-freeze.yml
+++ b/.github/workflows/release-code-freeze.yml
@@ -76,6 +76,7 @@ jobs:
outputs:
nextReleaseBranch: ${{ steps.calculate-versions.outputs.nextReleaseBranch }}
nextReleaseVersion: ${{ steps.calculate-versions.outputs.nextReleaseVersion }}
+ nextTrunkMainVersion: ${{ steps.calculate-versions.outputs.nextTrunkMainVersion }}
steps:
# - name: Verify no PRs open by the github-actions bot are open
# uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea #v7.0.1
@@ -114,14 +115,14 @@ jobs:
return;
}
- const trunkMainVersion = m[1];
- const nextReleaseVersion = trunkVersion.replace( '-dev', '' );
- const nextReleaseBranch = `release/${ nextReleaseVersion.slice( 0, -2 ) }`;
- const nextTrunkVersion = `${ ( Number( trunkMainVersion ) + 0.1 ).toFixed( 1 ) }.0-dev`;
+ const trunkMainVersion = m[1]; // e.g. '10.4'
+ const nextReleaseVersion = `${ trunkMainVersion }.0`; // e.g. '10.4.0'
+ const nextReleaseBranch = `release/${ trunkMainVersion }`; // e.g. 'release/10.4'
+ const nextTrunkMainVersion = `${ ( Number( trunkMainVersion ) + 0.1 ).toFixed( 1 ) }`; // e.g. '10.5'
core.setOutput( 'nextReleaseVersion', nextReleaseVersion );
core.setOutput( 'nextReleaseBranch', nextReleaseBranch );
- core.setOutput( 'nextTrunkVersion', nextTrunkVersion );
+ core.setOutput( 'nextTrunkMainVersion', nextTrunkMainVersion );
run-feature-freeze:
name: Perform feature freeze
@@ -143,6 +144,63 @@ jobs:
git checkout trunk
git checkout -b ${{ needs.prepare-for-feature-freeze.outputs.nextReleaseBranch }}
git push origin ${{ needs.prepare-for-feature-freeze.outputs.nextReleaseBranch }}
+ - name: Create milestone for next dev cycle
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const trunkMilestone = '${{ needs.prepare-for-feature-freeze.outputs.nextTrunkMainVersion }}.0';
+
+ try {
+ await github.rest.issues.createMilestone( {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ title: trunkMilestone,
+ } );
+ } catch ( e ) {
+ if ( e.response?.data?.errors?.some( ( err ) => 'already_exists' === err.code ) ) {
+ console.log( `Milestone ${ trunkMilestone } already exists.` );
+ } else {
+ throw e;
+ }
+ }
+
+ cleanup-milestones:
+ name: Clean up old and completed milestones
+ runs-on: ${{ ( github.repository == 'woocommerce/woocommerce' && 'blacksmith-2vcpu-ubuntu-2404' ) || 'ubuntu-latest' }}
+ needs: [prepare-for-feature-freeze, run-feature-freeze]
+ steps:
+ - name: Close completed milestones
+ uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea #v7.0.1
+ with:
+ script: |
+ const nextTrunkMainVersion = '${{ needs.prepare-for-feature-freeze.outputs.nextTrunkMainVersion }}'; // e.g. '10.5'
+ const minVersionToKeep = Number( nextTrunkMainVersion ) - 0.2; // e.g. 10.3
+
+ const { data: latestMilestones } = await github.rest.issues.listMilestones( {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ state: 'open',
+ per_page: 10,
+ sort: 'completeness',
+ direction: 'desc',
+ } );
+
+ // Filter out milestones that do not have any open issues and are older than the minimum version to keep.
+ // We want to keep milestone for at least the last 3 main versions (dev, frozen, stable).
+ const milestonesToClose = latestMilestones.filter(
+ milestone => 0 === milestone.open_issues && /^\d+\.\d+\.0$/.test( milestone.title ) && Number( milestone.title.slice( 0, -2 ) ) < minVersionToKeep
+ );
+
+ for ( const milestone of milestonesToClose ) {
+ await github.rest.issues.updateMilestone( {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ milestone_number: milestone.number,
+ state: 'closed',
+ } );
+
+ console.log( `Closed milestone ${milestone.title}` );
+ }
bump-version-in-trunk:
name: Bump version in trunk for next development cycle
@@ -260,7 +318,7 @@ jobs:
notify-slack-on-failure:
name: Notify Slack on Failure
runs-on: ${{ ( github.repository == 'woocommerce/woocommerce' && 'blacksmith-2vcpu-ubuntu-2404' ) || 'ubuntu-latest' }}
- needs: [ check-feature-freeze-event, prepare-for-feature-freeze, run-feature-freeze, bump-version-in-trunk, build-dev-release, publish-dev-release, notify-slack, trigger-webhook ]
+ needs: [ check-feature-freeze-event, prepare-for-feature-freeze, run-feature-freeze, cleanup-milestones, bump-version-in-trunk, build-dev-release, publish-dev-release, notify-slack, trigger-webhook ]
if: always() && ((contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')))
steps:
- name: Notify Slack on failure