Commit 12d19f5313 for woocommerce
commit 12d19f531317b2dba3cce48e0aa958bae890e0cc
Author: Jorge A. Torres <jorge.torres@automattic.com>
Date: Wed Jan 28 12:26:43 2026 +0000
Update release tracking templates for clarity and various improvements to kickoff workflow (#62837)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index feba0bae22..4af43dcee7 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -545,7 +545,7 @@ jobs:
list-files: shell
filters: |
needs-markdown-linting:
- - added|modified: '!(.github/**)/**/*.md'
+ - added|modified: '!(.github/**|.linear/**)/**/*.md'
- added|modified: '*.md'
needs-docs-build-validation:
- 'docs/**/*.md'
diff --git a/.github/workflows/release-assignment.yml b/.github/workflows/release-assignment.yml
index 01a0c27cf0..24d3a6f8dc 100644
--- a/.github/workflows/release-assignment.yml
+++ b/.github/workflows/release-assignment.yml
@@ -5,7 +5,8 @@ on:
workflow_dispatch:
permissions:
- contents: write
+ contents: read
+ issues: read
jobs:
check-upcoming-release-events:
@@ -28,108 +29,114 @@ jobs:
uses: actions/github-script@v7
with:
script: |
- const ical = require('node-ical');
+ const ical = require( 'node-ical' );
const isManualTrigger = context.eventName === 'workflow_dispatch';
- console.log(`Workflow triggered by: ${context.eventName}`);
+ console.log( `Workflow triggered by: ${ context.eventName }` );
try {
const today = new Date();
- let targetDate = new Date(today);
- targetDate.setDate(today.getDate() + (8 * 7)); // 8 weeks = 56 days
-
- const events = await ical.async.fromURL(`https://calendar.google.com/calendar/ical/${{ secrets.RELEASE_CALENDAR_ID }}/public/basic.ics`);
- const codeFreezeEvents = Object.values(events)
- .filter(event => event.type === 'VEVENT')
- .filter(event => {
- if (!event.start) return false;
+ let targetDate = new Date( today );
+ targetDate.setDate( today.getDate() + ( 8 * 7 ) ); // 8 weeks = 56 days.
+
+ const events = await ical.async.fromURL( `https://calendar.google.com/calendar/ical/${{ secrets.RELEASE_CALENDAR_ID }}/public/basic.ics` );
+ const codeFreezeEvents = Object.values( events )
+ .filter( ( event ) => event.type === 'VEVENT' )
+ .filter( ( event ) => {
+ if ( ! event.start ) return false;
const pattern = /^WooCommerce \d+\.\d+ Feature Freeze$/i;
- return pattern.test(event.summary || '');
- });
+ return pattern.test( event.summary || '' );
+ } );
- const eventsInRange = codeFreezeEvents.filter(event => {
- const eventDate = new Date(event.start);
- const diffTime = Math.abs(eventDate - targetDate);
- const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
+ const eventsInRange = codeFreezeEvents.filter( ( event ) => {
+ const eventDate = new Date( event.start );
+ const diffTime = Math.abs( eventDate - targetDate );
+ const diffDays = Math.ceil( diffTime / ( 1000 * 60 * 60 * 24 ) );
- if (isManualTrigger) {
- // For manual triggers, include events within 2 weeks of the target date
+ if ( isManualTrigger ) {
+ // For manual triggers, include events within 2 weeks of the target date.
return diffDays <= 14;
} else {
- // For scheduled triggers, include events within 1 day of 8 weeks
+ // For scheduled triggers, include events within 1 day of 8 weeks.
return diffDays <= 1;
}
- });
-
- if (eventsInRange.length > 0) {
- const date = eventsInRange[0].start.toISOString();
- const version = eventsInRange[0].summary.match(/\d+\.\d+/)[0];
- console.log(`Found Feature Freeze event: ${eventsInRange[0].summary}`);
- console.log(`Date: ${date}`);
- console.log(`Version: ${version}`);
- core.setOutput('should-trigger-webhook', 'true');
- core.setOutput('version', version);
- core.setOutput('feature-freeze-date', date);
+ } );
+
+ if ( eventsInRange.length > 0 ) {
+ const date = eventsInRange[ 0 ].start.toISOString();
+ const version = eventsInRange[ 0 ].summary.match( /\d+\.\d+/ )[ 0 ];
+ console.log( `Found Feature Freeze event: ${ eventsInRange[ 0 ].summary }` );
+ console.log( `Date: ${ date }` );
+ console.log( `Version: ${ version }` );
+ core.setOutput( 'should-trigger-webhook', 'true' );
+ core.setOutput( 'version', version );
+ core.setOutput( 'feature-freeze-date', date );
} else {
- console.log(`No Feature Freeze events found in the specified range.`);
- core.setOutput('should-trigger-webhook', 'false');
+ console.log( 'No Feature Freeze events found in the specified range.' );
+ core.setOutput( 'should-trigger-webhook', 'false' );
}
- } catch (error) {
- core.setFailed(`Failed to fetch calendar events: ${error.message}`);
+ } catch ( error ) {
+ core.setFailed( `Failed to fetch calendar events: ${ error.message }` );
}
- name: Get all events for version
+ if: ${{ steps.check-code-freeze-8-weeks.outputs.version != '' }}
id: get-all-events-for-version
uses: actions/github-script@v7
with:
script: |
- const ical = require('node-ical');
+ const ical = require( 'node-ical' );
const version = '${{ steps.check-code-freeze-8-weeks.outputs.version }}';
const releaseEvents = {
- 'beta-1': `WooCommerce ${version} Beta 1`,
- 'beta-2': `WooCommerce ${version} Beta 2`,
- 'rc-1': `WooCommerce ${version} RC 1`,
- final: `WooCommerce ${version} Release`,
+ 'beta-1': `WooCommerce ${ version } Beta 1`,
+ 'beta-2': `WooCommerce ${ version } Beta 2`,
+ 'rc-1': `WooCommerce ${ version } RC 1`,
+ final: `WooCommerce ${ version } Release`
};
const events = await ical.async.fromURL(
`https://calendar.google.com/calendar/ical/${{ secrets.RELEASE_CALENDAR_ID }}/public/basic.ics`
);
- const calendarEvents = Object.values(events)
- .filter(event => event.type === 'VEVENT')
- .filter(event => event.summary && event.start);
+ const calendarEvents = Object.values( events )
+ .filter( ( event ) => event.type === 'VEVENT' )
+ .filter( ( event ) => event.summary && event.start );
let teamName = null;
- for (const [eventKey, eventTitle] of Object.entries(releaseEvents)) {
- const matchingEvent = calendarEvents.find(event =>
+ for ( const [ eventKey, eventTitle ] of Object.entries( releaseEvents ) ) {
+ const matchingEvent = calendarEvents.find( ( event ) =>
event.summary.trim() === eventTitle
);
- const date = matchingEvent ? new Date(matchingEvent.start).toISOString().split('T')[0] : null;
- core.setOutput(`event-${eventKey}-date`, date);
- console.log(`Event: ${eventTitle}, Date: ${date}`);
+ const date = matchingEvent ? new Date( matchingEvent.start ).toISOString().split( 'T' )[ 0 ] : null;
+ core.setOutput( `event-${ eventKey }-date`, date );
+ console.log( `Event: ${ eventTitle }, Date: ${ date }` );
- // Extract team name from final release event description
- if (eventKey === 'final' && matchingEvent && matchingEvent.description) {
- const teamMatch = matchingEvent.description.match(/Team:\s*(.+)/i);
- if (teamMatch && teamMatch[1]) {
- teamName = teamMatch[1].trim().toLowerCase().replace(/\s+/g, '-');
- console.log(`Team name from the event description: ${teamName}`);
+ // Extract team name from final release event description.
+ if ( eventKey === 'final' && matchingEvent && matchingEvent.description ) {
+ const teamMatch = matchingEvent.description.match( /Team:\s*(.+)/i );
+ if ( teamMatch && teamMatch[ 1 ] ) {
+ teamName = teamMatch[ 1 ].trim().toLowerCase().replace( /\s+/g, '-' );
+ console.log( `Team name from the event description: ${ teamName }` );
}
}
}
-
- core.setOutput('team-name', teamName);
-
+
+ if ( ! teamName ) {
+ core.setFailed( 'Team name not found in final release event description. Expected "Team: <name>" in event description.' );
+ return;
+ }
+ core.setOutput( 'team-name', teamName );
+
trigger-upcoming-code-freeze-events:
name: Assign a release lead
- needs: check-upcoming-release-events
+ needs: [check-upcoming-release-events]
if: ${{ needs.check-upcoming-release-events.outputs.should-trigger-webhook == 'true' }}
runs-on: ${{ ( github.repository == 'woocommerce/woocommerce' && 'blacksmith-2vcpu-ubuntu-2404' ) || 'ubuntu-latest' }}
outputs:
+ create-tracking-issues: ${{ steps.trigger-upcoming-code-freeze-events.outputs.create-tracking-issues }}
post: ${{ steps.trigger-upcoming-code-freeze-events.outputs.post }}
release-team: ${{ steps.trigger-upcoming-code-freeze-events.outputs.release-team }}
release-lead: ${{ steps.trigger-upcoming-code-freeze-events.outputs.release-lead }}
@@ -140,7 +147,7 @@ jobs:
id: trigger-upcoming-code-freeze-events
with:
script: |
- const crypto = require('crypto');
+ const crypto = require( 'crypto' );
const payload = {
action: 'release-assignment',
@@ -150,50 +157,51 @@ jobs:
status: process.env.ENVIRONMENT === 'production' ? 'publish' : 'draft'
};
- const requestBody = JSON.stringify({
+ const requestBody = JSON.stringify( {
payload
- });
+ } );
- const hmac = crypto.createHmac('sha256', process.env.WPCOM_WEBHOOK_SECRET);
- hmac.update(requestBody);
- const signature = hmac.digest('hex');
+ const hmac = crypto.createHmac( 'sha256', process.env.WPCOM_WEBHOOK_SECRET );
+ hmac.update( requestBody );
+ const signature = hmac.digest( 'hex' );
- const response = await fetch(process.env.WPCOM_RELEASE_WEBHOOK_URL, {
+ const response = await fetch( process.env.WPCOM_RELEASE_WEBHOOK_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
- 'X-Hub-Signature-256': `sha256=${signature}`
+ 'X-Hub-Signature-256': `sha256=${ signature }`
},
body: requestBody
- });
+ } );
- if (!response.ok) {
+ if ( ! response.ok ) {
const status = response.status;
- let errorMessage = `Request failed with status ${status}`;
+ let errorMessage = `Request failed with status ${ status }`;
- switch (status) {
+ switch ( status ) {
case 409:
- errorMessage = `Post already exists for this release (${status})`;
- console.log(errorMessage);
+ errorMessage = `Post already exists for this release (${ status })`;
+ console.log( errorMessage );
break;
case 403:
- errorMessage = `Invalid webhook secret or unauthorized access (${status})`;
- core.setFailed(errorMessage);
+ errorMessage = `Invalid webhook secret or unauthorized access (${ status })`;
+ core.setFailed( errorMessage );
break;
default:
- core.setFailed(errorMessage);
+ core.setFailed( errorMessage );
break;
}
} else {
- console.log('Successfully triggered upcoming release notification webhook');
+ console.log( 'Successfully triggered upcoming release notification webhook' );
const responseBody = await response.json();
+ core.setOutput( 'create-tracking-issues', 'true' );
- core.setOutput('release-team', responseBody.data.release_team);
- core.setOutput('release-lead', responseBody.data.release_lead);
- core.setOutput('release-lead-google-login', responseBody.data.release_lead_google_login);
- core.setOutput('release-lead-slack-member-id', responseBody.data.release_lead_slack_member_id);
- core.setOutput('post', responseBody.data.post.view_url);
+ core.setOutput( 'release-team', responseBody.data.release_team );
+ core.setOutput( 'release-lead', responseBody.data.release_lead );
+ core.setOutput( 'release-lead-google-login', responseBody.data.release_lead_google_login );
+ core.setOutput( 'release-lead-slack-member-id', responseBody.data.release_lead_slack_member_id );
+ core.setOutput( 'post', responseBody.data.post.view_url );
}
env:
ENVIRONMENT: ${{ secrets.ENVIRONMENT }}
@@ -202,10 +210,8 @@ jobs:
send-slack-notification:
name: Send Slack notification
- needs:
- - trigger-upcoming-code-freeze-events
- - check-upcoming-release-events
- if: ${{ needs.check-upcoming-release-events.outputs.should-trigger-webhook == 'true' && needs.trigger-upcoming-code-freeze-events.outputs.post != '' }}
+ needs: [trigger-upcoming-code-freeze-events, check-upcoming-release-events, create-parent-tracking-issue, create-sub-tracking-issues]
+ if: ${{ needs.trigger-upcoming-code-freeze-events.outputs.create-tracking-issues == 'true' }}
runs-on: ${{ ( github.repository == 'woocommerce/woocommerce' && 'blacksmith-2vcpu-ubuntu-2404' ) || 'ubuntu-latest' }}
steps:
- name: Send Slack notification
@@ -220,27 +226,16 @@ jobs:
The team responsible for this release is *Team ${{ needs.check-upcoming-release-events.outputs.team-name }}*.
<@${{ needs.trigger-upcoming-code-freeze-events.outputs.release-lead-slack-member-id }}>, make sure you choose a release DRI and update the release P2 accordingly.
- :p2: <${{ needs.trigger-upcoming-code-freeze-events.outputs.post }}|Release P2 post>
+ :p2: <${{ needs.trigger-upcoming-code-freeze-events.outputs.post }}|Release P2 post> | :linear-logo: <${{ needs.create-parent-tracking-issue.outputs.issue-url }}|Linear tracking issue>
- create-release-kickoff:
- name: Create Release Kickoff in Linear
- needs:
- - trigger-upcoming-code-freeze-events
- - check-upcoming-release-events
- if: ${{ needs.check-upcoming-release-events.outputs.should-trigger-webhook == 'true' }}
+ create-parent-tracking-issue:
+ name: Create Parent Tracking Issue in Linear
+ needs: [trigger-upcoming-code-freeze-events, check-upcoming-release-events]
+ if: ${{ needs.trigger-upcoming-code-freeze-events.outputs.create-tracking-issues == 'true' }}
runs-on: ${{ ( github.repository == 'woocommerce/woocommerce' && 'blacksmith-2vcpu-ubuntu-2404' ) || 'ubuntu-latest' }}
- env:
- RELEASE_LEAD_GOOGLE_LOGIN: ${{ needs.trigger-upcoming-code-freeze-events.outputs.release-lead-google-login }}
- RELEASE_LEAD: ${{ needs.trigger-upcoming-code-freeze-events.outputs.release-lead }}
- RELEASE_TEAM: ${{ needs.trigger-upcoming-code-freeze-events.outputs.release-team }}
- POST: ${{ needs.trigger-upcoming-code-freeze-events.outputs.post }}
- RELEASE_VERSION: ${{ needs.check-upcoming-release-events.outputs.version }}
- FEATURE_FREEZE_DATE: ${{ needs.check-upcoming-release-events.outputs.feature-freeze-date }}
- BETA_1_RELEASE_DATE: ${{ needs.check-upcoming-release-events.outputs.beta-1-release-date }}
- BETA_2_RELEASE_DATE: ${{ needs.check-upcoming-release-events.outputs.beta-2-release-date }}
- RC1_RELEASE_DATE: ${{ needs.check-upcoming-release-events.outputs.rc-1-release-date }}
- RELEASE_DATE: ${{ needs.check-upcoming-release-events.outputs.final-release-date }}
- MILESTONE_NUMBER: ${{ needs.check-upcoming-release-events.outputs.version }}.0
+ outputs:
+ sub-issues-matrix: ${{ steps.build-matrix.outputs.matrix }}
+ issue-url: ${{ steps.check-existing-issue.outputs.issue-url || steps.create-tracking-issue.outputs.issue-url }}
steps:
- name: Checkout repository (sparse)
uses: actions/checkout@v4
@@ -250,179 +245,235 @@ jobs:
sparse-checkout-cone-mode: false
- name: Install linear-sdk
- run: npm install @linear/sdk
+ run: npm install @linear/sdk@71.0.0
+
+ - name: Check for existing release tracking issue
+ id: check-existing-issue
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const { LinearClient } = require( '@linear/sdk' );
+ const linearClient = new LinearClient( {
+ apiKey: '${{ secrets.LINEAR_OAUTH_TOKEN }}'
+ } );
+
+ const releaseMainVersion = '${{ needs.check-upcoming-release-events.outputs.version }}';
+ const expectedTitle = `[${ releaseMainVersion }] Release tracking`;
+
+ console.log( `Checking for existing issue with title: "${ expectedTitle }"` );
+
+ const issues = await linearClient.issues( {
+ filter: {
+ team: { id: { eq: '${{ secrets.LINEAR_TEAM_ID }}' } },
+ title: { eq: expectedTitle }
+ }
+ } );
+
+ if ( issues.nodes.length > 0 ) {
+ console.log( 'Found existing release tracking issue' );
+ core.setOutput( 'exists', 'true' );
+ core.setOutput( 'issue-url', issues.nodes[ 0 ].url );
+ } else {
+ console.log( 'No existing release tracking issue found' );
+ core.setOutput( 'exists', 'false' );
+ }
- name: Get Linear data
+ if: ${{ steps.check-existing-issue.outputs.exists != 'true' }}
id: get-linear-data
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea #v7.0.1
+ uses: actions/github-script@v7
with:
script: |
- const { LinearClient } = require("@linear/sdk");
- const linearClient = new LinearClient({
- apiKey: '${{ secrets.LINEAR_OAUTH_TOKEN }}',
- });
+ const { LinearClient } = require( '@linear/sdk' );
+ const linearClient = new LinearClient( {
+ apiKey: '${{ secrets.LINEAR_OAUTH_TOKEN }}'
+ } );
const organization = await linearClient.organization;
- const labels = await linearClient.issueLabels({
+ const labels = await linearClient.issueLabels( {
filter: {
name: { eq: 'Release' }
}
- });
+ } );
- const states = await linearClient.workflowStates({
+ const states = await linearClient.workflowStates( {
filter: {
team: { id: { eq: '${{ secrets.LINEAR_TEAM_ID }}' } },
name: { eq: 'In Progress' }
}
- });
+ } );
- const releaseLabelId = labels.nodes.length > 0 ? labels.nodes[0].id : null;
- const inProgressStateId = states.nodes.length > 0 ? states.nodes[0].id : null;
+ const releaseLabelId = labels.nodes.length > 0 ? labels.nodes[ 0 ].id : null;
+ const inProgressStateId = states.nodes.length > 0 ? states.nodes[ 0 ].id : null;
- core.setOutput('releaseLabelId', releaseLabelId);
- core.setOutput('inProgressStateId', inProgressStateId);
- core.setOutput('organizationUrlKey', organization.urlKey);
+ core.setOutput( 'releaseLabelId', releaseLabelId );
+ core.setOutput( 'inProgressStateId', inProgressStateId );
+ core.setOutput( 'organizationUrlKey', organization.urlKey );
- - name: Process release kickoff templates
- id: process-kickoff-templates
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea #v7.0.1
+ - name: Get Linear user from email
+ if: ${{ steps.check-existing-issue.outputs.exists != 'true' }}
+ id: get-linear-user
+ uses: actions/github-script@v7
with:
script: |
- const { LinearClient } = require("@linear/sdk");
- const fs = require('fs');
- const path = require('path');
-
- const templates = {
- 'parent': '.linear/release-kickoff.md',
- 'beta-1': '.linear/release-kickoff-beta.md',
- 'beta-2': '.linear/release-kickoff-beta.md',
- 'rc-1': '.linear/release-kickoff-rc.md',
- 'final': '.linear/release-kickoff-patch.md'
- };
+ const { LinearClient } = require( '@linear/sdk' );
+ const linearClient = new LinearClient( {
+ apiKey: '${{ secrets.LINEAR_OAUTH_TOKEN }}'
+ } );
- let templateContents = {};
- for (const [key, templatePath] of Object.entries(templates)) {
- let templateContent = '';
-
- try {
- templateContent = fs.readFileSync(templatePath, 'utf8');
- } catch (error) {
- core.setFailed(`Failed to read template file: ${error.message}`);
- return;
- }
-
- templateContents[key] = templateContent;
- }
-
- const linearClient = new LinearClient({
- apiKey: '${{ secrets.LINEAR_OAUTH_TOKEN }}',
- });
-
- const email = process.env.RELEASE_LEAD_GOOGLE_LOGIN || '';
- const users = await linearClient.users({
+ const email = '${{ needs.trigger-upcoming-code-freeze-events.outputs.release-lead-google-login }}';
+ const users = await linearClient.users( {
filter: {
email: { eq: email }
}
- });
+ } );
- const userId = users.nodes.length ? users.nodes[0].id : null;
- const displayName = users.nodes.length ? users.nodes[0].displayName : null;
- const mention = displayName ? `https://linear.app/${{ steps.get-linear-data.outputs.organizationUrlKey }}/profiles/${displayName}` : null;
+ const userId = users.nodes.length ? users.nodes[ 0 ].id : null;
+ const displayName = users.nodes.length ? users.nodes[ 0 ].displayName : null;
+ const username = users.nodes.length ? users.nodes[ 0 ].username : null;
- const replacements = {
- '{RELEASE_VERSION}': process.env.RELEASE_VERSION || '',
- '{MILESTONE_NUMBER}': process.env.MILESTONE_NUMBER || '',
- '{RELEASE_LEAD}': mention || process.env.RELEASE_LEAD || '',
- '{RELEASE_TEAM}': process.env.RELEASE_TEAM || '',
- '{BETA_1_RELEASE_DATE}': process.env.BETA_1_RELEASE_DATE || '',
- '{BETA_2_RELEASE_DATE}': process.env.BETA_2_RELEASE_DATE || '',
- '{RC1_RELEASE_DATE}': process.env.RC1_RELEASE_DATE || '',
- '{RELEASE_DATE}': process.env.RELEASE_DATE || '',
- '{FEATURE_FREEZE_DATE}': process.env.FEATURE_FREEZE_DATE || '',
- '{PATCH_VERSION}': 0,
- };
+ console.log( userId ? 'Found Linear user for assignee' : 'No Linear user found for assignee' );
- let processedTemplates = {};
- for (const [key, content] of Object.entries(templateContents)) {
- const lines = content.split('\n');
- const title = lines[0].slice(2).trim();
- const body = lines.slice(1).join('\n').trim();
+ core.setOutput( 'assignee-id', userId || '' );
+ core.setOutput( 'assignee-display-name', displayName || '' );
+ core.setOutput( 'assignee-username', username || '' );
- let processedTitle = title;
- let processedBody = body;
+ - name: Process template and create release tracking issue
+ if: ${{ steps.check-existing-issue.outputs.exists != 'true' }}
+ id: create-tracking-issue
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const { LinearClient } = require( '@linear/sdk' );
+ const fs = require( 'fs' );
- if (key.includes('beta')) {
- replacements['{BETA_PATCH_VERSION}'] = key.split('-')[1] || '';
- } else if (key.includes('rc')) {
- replacements['{RC_PATCH_VERSION}'] = key.split('-')[1] || '';
- }
+ const linearClient = new LinearClient( {
+ apiKey: '${{ secrets.LINEAR_OAUTH_TOKEN }}'
+ } );
- for (const [placeholder, value] of Object.entries(replacements)) {
- processedTitle = processedTitle.replaceAll(placeholder, value);
- processedBody = processedBody.replaceAll(placeholder, value);
- }
+ // Read template file.
+ const templatePath = '.linear/release-kickoff.md';
+ let templateContent;
- processedTemplates[key] = {
- title: processedTitle,
- body: processedBody
- };
+ try {
+ templateContent = fs.readFileSync( templatePath, 'utf8' );
+ } catch ( error ) {
+ core.setFailed( `Failed to read template file: ${ error.message }` );
+ return;
}
- core.setOutput('processed-templates', JSON.stringify(processedTemplates));
- core.setOutput('userId', userId || '');
+ // Parse template (first line is title with # prefix).
+ const lines = templateContent.split( '\n' );
+ const titleTemplate = lines[ 0 ].slice( 2 ).trim();
+ const bodyTemplate = lines.slice( 1 ).join( '\n' ).trim();
- - name: Create Release Kickoff Linear Issue
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea #v7.0.1
- with:
- script: |
- const { LinearClient } = require("@linear/sdk");
+ // Prepare placeholder values.
+ const username = '${{ steps.get-linear-user.outputs.assignee-username }}';
+ const organizationUrlKey = '${{ steps.get-linear-data.outputs.organizationUrlKey }}';
+ const mention = username ? `https://linear.app/${ organizationUrlKey }/profiles/${ username }` : null;
- const linearClient = new LinearClient({
- apiKey: '${{ secrets.LINEAR_OAUTH_TOKEN }}',
- });
+ const releaseMainVersion = '${{ needs.check-upcoming-release-events.outputs.version }}';
+ const releaseMilestone = `${ releaseMainVersion }.0`;
+ const releaseBranch = `release/${ releaseMainVersion }`;
- const { parent, ...subIssues } = ${{ steps.process-kickoff-templates.outputs.processed-templates }};
+ const featureFreezeDate = '${{ needs.check-upcoming-release-events.outputs.feature-freeze-date }}';
- let parentIssue;
+ const repositoryUrl = `https://github.com/${ context.repo.owner }/${ context.repo.repo }`;
+ const replacements = {
+ '{repository_url}': repositoryUrl,
+ '{release_main_version}': releaseMainVersion,
+ '{release_milestone}': releaseMilestone,
+ '{release_branch}': releaseBranch,
+ '{release_version}': releaseMilestone,
+ '{release_date}': '',
+ '{release_type}': '',
+ '{release_monitoring_time}': '',
+ '{lead_user}': mention || '${{ needs.trigger-upcoming-code-freeze-events.outputs.release-lead }}',
+ '{lead_team}': '${{ needs.trigger-upcoming-code-freeze-events.outputs.release-team }}',
+ '{date_feature_freeze}': featureFreezeDate ? featureFreezeDate.split( 'T' )[ 0 ] : '',
+ '{date_beta1}': '${{ needs.check-upcoming-release-events.outputs.beta-1-release-date }}',
+ '{date_beta2}': '${{ needs.check-upcoming-release-events.outputs.beta-2-release-date }}',
+ '{date_rc1}': '${{ needs.check-upcoming-release-events.outputs.rc-1-release-date }}',
+ '{date_stable}': '${{ needs.check-upcoming-release-events.outputs.final-release-date }}'
+ };
+
+ // Apply replacements.
+ let title = titleTemplate;
+ let body = bodyTemplate;
+
+ for ( const [ placeholder, value ] of Object.entries( replacements ) ) {
+ title = title.replaceAll( placeholder, value );
+ body = body.replaceAll( placeholder, value );
+ }
+
+ // Create the Linear issue.
const labelId = '${{ steps.get-linear-data.outputs.releaseLabelId }}' || null;
- const assigneeId = '${{ steps.process-kickoff-templates.outputs.userId }}' || null;
+ const stateId = '${{ steps.get-linear-data.outputs.inProgressStateId }}' || null;
+ const assigneeId = '${{ steps.get-linear-user.outputs.assignee-id }}' || null;
try {
- const stateId = '${{ steps.get-linear-data.outputs.inProgressStateId }}' || null;
-
- const parentResult = await linearClient.createIssue({
+ const parentResult = await linearClient.createIssue( {
teamId: '${{ secrets.LINEAR_TEAM_ID }}',
- title: `${parent.title}`,
- description: `${parent.body}`,
+ title: title,
+ description: body,
priority: 2,
- ...(labelId && { labelIds: [labelId] }),
- ...(stateId && { stateId }),
- ...(assigneeId && { assigneeId })
- });
-
- parentIssue = await parentResult.issue;
- } catch (error) {
- core.setFailed(`Failed to create Linear issue: ${error.message}`);
- return;
+ ...( labelId && { labelIds: [ labelId ] } ),
+ ...( stateId && { stateId } ),
+ ...( assigneeId && { assigneeId } )
+ } );
+
+ const parentIssue = await parentResult.issue;
+ console.log( 'Parent tracking issue created successfully' );
+ core.setOutput( 'issue-url', parentIssue.url );
+ } catch ( error ) {
+ core.setFailed( `Failed to create parent Linear issue: ${ error.message }` );
}
- if (parentIssue) {
- for (const subIssue of Object.values(subIssues)) {
- try {
- const subIssueResult = await linearClient.createIssue({
- teamId: `${{ secrets.LINEAR_TEAM_ID }}`,
- title: subIssue.title,
- description: subIssue.body,
- parentId: parentIssue.id,
- priority: 2,
- ...(labelId && { labelIds: [labelId] }),
- ...(assigneeId && { assigneeId })
- });
- } catch (error) {
- console.log(`Failed to create sub-issue in Linear: ${error.message}`);
- }
- }
- }
+ - name: Build sub-issues matrix
+ id: build-matrix
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const version = '${{ needs.check-upcoming-release-events.outputs.version }}';
+
+ const matrix = {
+ include: [
+ { version: `${ version }.0-beta.1`, releaseDate: '${{ needs.check-upcoming-release-events.outputs.beta-1-release-date }}' },
+ { version: `${ version }.0-beta.2`, releaseDate: '${{ needs.check-upcoming-release-events.outputs.beta-2-release-date }}' },
+ { version: `${ version }.0-rc.1`, releaseDate: '${{ needs.check-upcoming-release-events.outputs.rc-1-release-date }}' },
+ { version: `${ version }.0`, releaseDate: '${{ needs.check-upcoming-release-events.outputs.final-release-date }}' }
+ ]
+ };
- console.log(`Release kickoff issue created: ${parentIssue?.url}`);
+ core.setOutput( 'matrix', JSON.stringify( matrix ) );
+
+ create-sub-tracking-issues:
+ name: Create sub-issue for tracking ${{ matrix.version }} release
+ needs: [create-parent-tracking-issue]
+ strategy:
+ matrix: ${{ fromJSON(needs.create-parent-tracking-issue.outputs.sub-issues-matrix) }}
+ uses: ./.github/workflows/release-create-tracking-issue.yml
+ with:
+ version: ${{ matrix.version }}
+ release-date: ${{ matrix.releaseDate }}
+ skip-branch-validation: true
+ secrets:
+ LINEAR_OAUTH_TOKEN: ${{ secrets.LINEAR_OAUTH_TOKEN }}
+ LINEAR_TEAM_ID: ${{ secrets.LINEAR_TEAM_ID }}
+
+ notify-on-failure:
+ name: Notify on failure
+ needs: [check-upcoming-release-events, trigger-upcoming-code-freeze-events, send-slack-notification, create-parent-tracking-issue, create-sub-tracking-issues]
+ if: ${{ failure() }}
+ runs-on: ${{ ( github.repository == 'woocommerce/woocommerce' && 'blacksmith-2vcpu-ubuntu-2404' ) || 'ubuntu-latest' }}
+ steps:
+ - name: Send failure notification to Slack
+ uses: archive/github-actions-slack@a62d71a4ea93e68cbdc37581166b0298bea512e9 # v2.10.0
+ with:
+ slack-bot-user-oauth-access-token: ${{ secrets.CODE_FREEZE_BOT_TOKEN }}
+ slack-channel: ${{ secrets.WOO_RELEASE_SLACK_CHANNEL }}
+ slack-optional-unfurl_links: false
+ slack-text: |
+ :x: Some steps in the Release Assignment workflow failed. Please check the logs for details and re-run if needed.
+ <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View workflow run>
diff --git a/.github/workflows/release-create-tracking-issue.yml b/.github/workflows/release-create-tracking-issue.yml
new file mode 100644
index 0000000000..c8c3f2b527
--- /dev/null
+++ b/.github/workflows/release-create-tracking-issue.yml
@@ -0,0 +1,311 @@
+name: 'Release: Create Tracking Issue'
+
+on:
+ workflow_dispatch:
+ inputs:
+ version:
+ description: 'Version (e.g., 9.5.0-beta.1, 9.5.0-rc.1, 9.5.0)'
+ required: true
+ type: string
+ release-date:
+ description: 'Release date (YYYY-MM-DD), defaults to today'
+ required: false
+ type: string
+
+ workflow_call:
+ inputs:
+ version:
+ description: 'Version (e.g., 9.5.0-beta.1, 9.5.0-rc.1, 9.5.0)'
+ required: true
+ type: string
+ release-date:
+ description: 'Release date (YYYY-MM-DD), defaults to today'
+ required: false
+ type: string
+ skip-branch-validation:
+ description: 'Skip release branch validation'
+ required: false
+ type: boolean
+ default: false
+ secrets:
+ LINEAR_OAUTH_TOKEN:
+ required: true
+ LINEAR_TEAM_ID:
+ required: true
+
+permissions:
+ contents: read
+ issues: read
+
+jobs:
+ create-tracking-issue:
+ name: Create Release Tracking Issue
+ runs-on: ${{ ( github.repository == 'woocommerce/woocommerce' && 'blacksmith-2vcpu-ubuntu-2404' ) || 'ubuntu-latest' }}
+ steps:
+ - name: Parse version and determine template
+ id: parse-version
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const version = '${{ inputs.version }}';
+
+ // Match X.Y.Z with optional pre-release suffix (beta.N or rc.N).
+ const match = version.match( /^(\d+\.\d+)\.(\d+)(?:-(beta|rc)\.(\d+))?$/ );
+
+ // Pre-release versions must have patch version 0.
+ if ( ! match || ( match[ 3 ] && match[ 2 ] !== '0' ) ) {
+ core.setFailed( `Invalid version format: ${ version }. Expected: X.Y.Z (stable) or X.Y.0-beta.N / X.Y.0-rc.N (pre-release)` );
+ return;
+ }
+
+ const mainVersion = match[ 1 ];
+ const releaseType = match[ 3 ] || 'patch';
+ const templateFile = `.linear/release-kickoff-${ releaseType }.md`;
+ const monitoringTime = releaseType === 'rc' ? '4' : ( releaseType === 'patch' ? '2' : '' );
+
+ const milestone = `${ mainVersion }.0`;
+ const branch = `release/${ mainVersion }`;
+
+ console.log( `Version: ${ version }` );
+ console.log( `Main version: ${ mainVersion }` );
+ console.log( `Release type: ${ releaseType }` );
+ console.log( `Milestone: ${ milestone }` );
+ console.log( `Branch: ${ branch }` );
+ console.log( `Template: ${ templateFile }` );
+
+ core.setOutput( 'main-version', mainVersion );
+ core.setOutput( 'release-type', releaseType );
+ core.setOutput( 'template-file', templateFile );
+ core.setOutput( 'milestone', milestone );
+ core.setOutput( 'branch', branch );
+ core.setOutput( 'monitoring-time', monitoringTime );
+
+ - name: Validate release branch exists
+ if: ${{ !inputs.skip-branch-validation }}
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const branchName = '${{ steps.parse-version.outputs.branch }}';
+
+ try {
+ await github.rest.repos.getBranch( {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ branch: branchName
+ } );
+ console.log( `Branch '${ branchName }' exists` );
+ } catch ( error ) {
+ if ( error.status === 404 ) {
+ core.setFailed( `Release branch '${ branchName }' does not exist` );
+ } else {
+ throw error;
+ }
+ }
+
+ - name: Install linear-sdk
+ run: npm install @linear/sdk@71.0.0
+
+ - name: Check if tracking issue already exists
+ id: check-existing
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const { LinearClient } = require( '@linear/sdk' );
+ const linearClient = new LinearClient( {
+ apiKey: '${{ secrets.LINEAR_OAUTH_TOKEN }}'
+ } );
+
+ const mainVersion = '${{ steps.parse-version.outputs.main-version }}';
+ const version = '${{ inputs.version }}';
+ const specificTitle = `[${ mainVersion }] Release \`${ version }\``;
+
+ console.log( `Checking for existing issue: "${ specificTitle }"` );
+
+ const existingIssues = await linearClient.issues( {
+ filter: {
+ team: { id: { eq: '${{ secrets.LINEAR_TEAM_ID }}' } },
+ title: { eq: specificTitle }
+ }
+ } );
+
+ if ( existingIssues.nodes.length > 0 ) {
+ const existingUrl = existingIssues.nodes[ 0 ].url;
+ console.log( 'Tracking issue already exists' );
+ core.setOutput( 'issue-exists', 'true' );
+ core.setOutput( 'existing-url', existingUrl );
+
+ // Write to GitHub job summary.
+ await core.summary
+ .addRaw( `Linear tracking issue already exists: [${ specificTitle }](${ existingUrl })` )
+ .write();
+ return;
+ }
+
+ console.log( 'No existing tracking issue found, proceeding with creation' );
+ core.setOutput( 'issue-exists', 'false' );
+
+ - name: Checkout repository (sparse)
+ if: ${{ steps.check-existing.outputs.issue-exists != 'true' }}
+ uses: actions/checkout@v4
+ with:
+ path: repo
+ sparse-checkout: |
+ .linear
+ sparse-checkout-cone-mode: false
+
+ - name: Get Linear data
+ if: ${{ steps.check-existing.outputs.issue-exists != 'true' }}
+ id: get-linear-data
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const { LinearClient } = require( '@linear/sdk' );
+ const linearClient = new LinearClient( {
+ apiKey: '${{ secrets.LINEAR_OAUTH_TOKEN }}'
+ } );
+
+ const labels = await linearClient.issueLabels( {
+ filter: {
+ name: { eq: 'Release' }
+ }
+ } );
+
+ const releaseLabelId = labels.nodes.length > 0 ? labels.nodes[ 0 ].id : null;
+
+ console.log( releaseLabelId ? 'Found Release label' : 'Release label not found' );
+
+ core.setOutput( 'release-label-id', releaseLabelId || '' );
+
+ - name: Get parent tracking issue and extract assignee
+ if: ${{ steps.check-existing.outputs.issue-exists != 'true' }}
+ id: get-parent-issue
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const { LinearClient } = require( '@linear/sdk' );
+ const linearClient = new LinearClient( {
+ apiKey: '${{ secrets.LINEAR_OAUTH_TOKEN }}'
+ } );
+
+ const mainVersion = '${{ steps.parse-version.outputs.main-version }}';
+ const parentTitle = `[${ mainVersion }] Release tracking`;
+
+ console.log( `Looking for parent issue: "${ parentTitle }"` );
+
+ const parentIssues = await linearClient.issues( {
+ filter: {
+ team: { id: { eq: '${{ secrets.LINEAR_TEAM_ID }}' } },
+ title: { eq: parentTitle }
+ }
+ } );
+
+ if ( parentIssues.nodes.length === 0 ) {
+ core.setFailed( `Parent tracking issue '${ parentTitle }' does not exist in Linear` );
+ return;
+ }
+
+ const parentIssue = parentIssues.nodes[ 0 ];
+ console.log( 'Found parent issue' );
+
+ core.setOutput( 'parent-id', parentIssue.id );
+
+ const assignee = await parentIssue.assignee;
+ if ( assignee ) {
+ console.log( 'Parent issue has an assignee' );
+ core.setOutput( 'assignee-id', assignee.id );
+ } else {
+ console.log( 'Parent issue has no assignee' );
+ core.setOutput( 'assignee-id', '' );
+ }
+
+ - name: Process template and create Linear issue
+ if: ${{ steps.check-existing.outputs.issue-exists != 'true' }}
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const { LinearClient } = require( '@linear/sdk' );
+ const fs = require( 'fs' );
+
+ const linearClient = new LinearClient( {
+ apiKey: '${{ secrets.LINEAR_OAUTH_TOKEN }}'
+ } );
+
+ // Read template file.
+ const templateFile = 'repo/${{ steps.parse-version.outputs.template-file }}';
+ let templateContent;
+ try {
+ templateContent = fs.readFileSync( templateFile, 'utf8' );
+ } catch ( error ) {
+ core.setFailed( `Failed to read template file: ${ error.message }` );
+ return;
+ }
+
+ // Parse template (first line is title with # prefix).
+ const lines = templateContent.split( '\n' );
+ const title = lines[ 0 ].slice( 2 ).trim();
+ const body = lines.slice( 1 ).join( '\n' ).trim();
+
+ // Prepare placeholder values.
+ const version = '${{ inputs.version }}';
+ const mainVersion = '${{ steps.parse-version.outputs.main-version }}';
+ const milestone = '${{ steps.parse-version.outputs.milestone }}';
+ const branch = '${{ steps.parse-version.outputs.branch }}';
+ const releaseType = '${{ steps.parse-version.outputs.release-type }}';
+ const monitoringTime = '${{ steps.parse-version.outputs.monitoring-time }}';
+ const repositoryUrl = `https://github.com/${ context.repo.owner }/${ context.repo.repo }`;
+
+ // Release date: use input or default to today.
+ let releaseDate = '${{ inputs.release-date }}';
+ if ( ! releaseDate ) {
+ releaseDate = new Date().toISOString().split( 'T' )[ 0 ];
+ }
+
+ const replacements = {
+ '{repository_url}': repositoryUrl,
+ '{release_main_version}': mainVersion,
+ '{release_milestone}': milestone,
+ '{release_branch}': branch,
+ '{release_version}': version,
+ '{release_date}': releaseDate,
+ '{release_type}': releaseType,
+ '{release_monitoring_time}': monitoringTime
+ };
+
+ // Apply replacements.
+ let processedTitle = title;
+ let processedBody = body;
+
+ for ( const [ placeholder, value ] of Object.entries( replacements ) ) {
+ processedTitle = processedTitle.replaceAll( placeholder, value );
+ processedBody = processedBody.replaceAll( placeholder, value );
+ }
+
+ console.log( `Creating issue with title: "${ processedTitle }"` );
+
+ // Create the Linear issue.
+ const parentId = '${{ steps.get-parent-issue.outputs.parent-id }}' || '';
+ const labelId = '${{ steps.get-linear-data.outputs.release-label-id }}';
+ const assigneeId = '${{ steps.get-parent-issue.outputs.assignee-id }}' || '';
+
+ try {
+ const result = await linearClient.createIssue( {
+ teamId: '${{ secrets.LINEAR_TEAM_ID }}',
+ title: processedTitle,
+ description: processedBody,
+ priority: 2,
+ ...( parentId && { parentId } ),
+ ...( labelId && { labelIds: [ labelId ] } ),
+ ...( assigneeId && { assigneeId } )
+ } );
+
+ const issue = await result.issue;
+ console.log( 'Successfully created tracking issue' );
+
+ // Write to GitHub job summary.
+ await core.summary
+ .addRaw( `Linear tracking issue created: [${ processedTitle }](${ issue.url })` )
+ .write();
+ } catch ( error ) {
+ core.setFailed( `Failed to create Linear issue: ${ error.message }` );
+ return;
+ }
diff --git a/.linear/release-kickoff-beta.md b/.linear/release-kickoff-beta.md
index 6fe51fb027..6bb62112f5 100644
--- a/.linear/release-kickoff-beta.md
+++ b/.linear/release-kickoff-beta.md
@@ -1,63 +1,41 @@
-# WooCommerce {RELEASE_VERSION}-beta.{BETA_PATCH_VERSION}
+# [{release_main_version}] Release `{release_version}`
-This issue tracks the progress of a single WooCommerce core plugin release iteration that covers the publishing of a single patch version. Patch version in this context refers to `z` in a version following `x.y.z` where `z` can be any of `0-99`, `0-rc.[0-99]`, or `0-beta.[0-99]`.
+These are the instructions for releasing `{release_version}`, scheduled for `{release_date}`.
-## Version being released {RELEASE_VERSION}-beta.{BETA_PATCH_VERSION}
+----
-The following details are copied from the official [Building and Publishing guide](https://developer.woocommerce.com/docs/contribution/releases/building-and-publishing/). Please check it to make sure these instructions haven't become out of date.
-Check the [Release Troubleshooting & Recovery](https://developer.woocommerce.com/docs/contribution/releases/troubleshooting/) page for any issues you may encounter.
+Perform all the steps below in order. When running _any_ GitHub workflow, ensure you do it from the `trunk` branch (the default) and input the release version or branch as indicated.
-## Pre-Checks
+Keep the _[Release Troubleshooting & Recovery](https://developer.woocommerce.com/docs/contribution/releases/troubleshooting/)_ guide handy, in case you encounter any issues.
-- [ ] **Verify no open [Pull Requests](https://github.com/woocommerce/woocommerce/pulls?q=is%3Aopen+is%3Apr) or [Issues](https://github.com/woocommerce/woocommerce/issues)** for the milestone matching the release being published.
- - All pull requests tied to the release milestone must be closed, including [backported pull requests](https://github.com/woocommerce/woocommerce/pulls?q=is%3Apr+label%3A%22type%3A+cherry-pick%22) that may need to be merged into other release branches or trunk.
-- [ ] **Check for unresolved ["cherry pick failed" Pull Requests](https://github.com/woocommerce/woocommerce/pulls?q=is:pr+label:%22cherry+pick+failed%22).**
- - Ensure any such PRs are either expected or manually resolved via another PR.
-- [ ] **Confirm the Stable tag in** `readme.txt` **matches [trunk on WordPress.org](https://plugins.trac.wordpress.org/browser/woocommerce/trunk/readme.txt#L7).**
- - The value should match the current stable version, not the version being built.
-- [ ] **Ensure [GitHub services](https://www.githubstatus.com/) are fully operational**
+----
-## Build WooCommerce
+### 1. Pre-build checks
-1. **Run the ["Release: Bump version number" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-bump-version.yml).**
- - Run from `trunk`.
- - Choose the type of version you're releasing (`beta`, `rc`, or `stable`).
- - Enter as *Release branch* the branch you are about to release from (e.g. `release/10.0`).
- - Review and merge the PR created.
+- [ ] Confirm [GitHub services](https://www.githubstatus.com/) are operational.
+- [ ] Verify no open issues or pull requests exist against the [{release_milestone} milestone]({repository_url}/issues?q=is:open+milestone:{release_milestone}). Ping authors as needed to merge or close.
+- [ ] Ensure that there aren't any pull requests [with label "cherry pick failed"]({repository_url}/pulls?q=is:pr+label:%22cherry+pick+failed%22) that apply to this release that haven't been actioned.
+- [ ] Confirm the `Stable tag` value [in the readme.txt on the release branch]({repository_url}/blob/{release_branch}/plugins/woocommerce/readme.txt#L7) matches the one [on WordPress.org's `trunk`](https://plugins.trac.wordpress.org/browser/woocommerce/trunk/readme.txt#L7).
-2. **Run the ["Release: Compile changelog" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-compile-changelog.yml).**
- - Run from `trunk` and enter the major version number and the intended release date.
- - **Review and merge the two PRs created** (one for trunk, one for the release branch).
- - **Ensure the changelog date is correct.**
-3. **Build the release ZIP file.**
- - Build the release ZIP file using the ["Release: Build ZIP file" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-build-zip-file.yml).
- - Run from `trunk` and enter the release branch as argument.
- - Set "Create GitHub release" to `true`.
- - The workflow will create a [draft release tag](https://github.com/woocommerce/woocommerce/releases) with an attached `woocommerce.zip` file.
+### 2. Build the release package
-## Publish the Release
+- [ ] Run workflow **[Release: Bump version number]({repository_url}/actions/workflows/release-bump-version.yml)**: enter `{release_branch}` as _Release branch_ and `{release_type}` as _Type of version bump to perform_.
+- [ ] Review and merge the PR that was generated against the release branch. Check the [{release_milestone} milestone]({repository_url}/issues?q=is:open+milestone:{release_milestone}).
+- [ ] Run workflow **[Release: Compile changelog]({repository_url}/actions/workflows/release-compile-changelog.yml)**: enter `{release_main_version}` as _Version_ and leave _Release date_ empty, except when building the package ahead of schedule.
+- [ ] Review and merge the PRs that were generated: one against `trunk` and another one against the release branch. Both should be under the [{release_milestone} milestone]({repository_url}/issues?q=is:open+milestone:{release_milestone}).
+- [ ] Run workflow **[Release: Build ZIP file]({repository_url}/actions/workflows/release-build-zip-file.yml)** to build the asset and create the GitHub release: enter `{release_branch}` as _Release branch_ and check _Create GitHub release_.
+- [ ] Confirm that a draft `{release_version}` release [was created in the repository]({repository_url}/releases) with an attached `woocommerce.zip` asset.
-### Step 1: Upload Release to WordPress.org
-- [ ] **Run the ["Release: Upload release to WordPress.org" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-upload-to-wporg.yml)** from `trunk` using the release tag.
-- [ ] **This creates a new [SVN tag](https://plugins.svn.wordpress.org/woocommerce/tags/) and, if the release is newer than trunk, overwrites trunk.**
+### 3. Upload the release to WordPress.org
-### Step 2: Approve the Release
+- [ ] Run workflow **[Release: Upload release to WordPress.org]({repository_url}/actions/workflows/release-upload-to-wporg.yml)**: enter `{release_version}` as _Release tag to upload_ and make sure to check off the confirmation box. This can take up to 40 mins.
+- [ ] Confirm that SVN tag `{release_version}` [exists on WordPress.org SVN](https://plugins.svn.wordpress.org/woocommerce/tags/{release_version}).
+- [ ] [Log into WordPress.org](https://wordpress.org/plugins/developers/releases/) using the credentials from the `WordPress.org "WooCommerce" user account` secret in the secret store and approve the release.
+- [ ] After a few minutes, confirm that [`{release_version}` is available for download](https://downloads.wordpress.org/plugin/woocommerce.{release_version}.zip).
-- [ ] **Visit [WordPress.org plugin releases](https://wordpress.org/plugins/developers/releases/) and approve the release.**
-- [ ] **Wait a few minutes for WordPress.org to build the new version.**
-### Step 3: Verify Release Availability
+### 4. Publish the release
-- [ ] **Confirm the new release appears at:**
- - <https://plugins.svn.wordpress.org/woocommerce/tags/>
- - The "Previous versions" dropdown on the [Advanced Options screen](https://wordpress.org/plugins/woocommerce/advanced/).
-
-## Update the Release Tags
-
-### Step 1: Publish GitHub Release Tag
-
-- [ ] **Action:** [Publish the previously created GitHub draft release tag](https://github.com/woocommerce/woocommerce/releases).
-- [ ] **When setting release status:**
- - check "Set as a pre-release."
+- [ ] Publish the `{release_version}` [release draft]({repository_url}/releases) that was previously created.
diff --git a/.linear/release-kickoff-patch.md b/.linear/release-kickoff-patch.md
index 171e995954..4172e4e57b 100644
--- a/.linear/release-kickoff-patch.md
+++ b/.linear/release-kickoff-patch.md
@@ -1,88 +1,58 @@
-# WooCommerce {RELEASE_VERSION}.{PATCH_VERSION}
+# [{release_main_version}] Release `{release_version}`
-This issue tracks the progress of a single WooCommerce core plugin release iteration that covers the publishing of a single patch version. "Patch version" in this context refers to `z` in a version following `x.y.z` where `z` can be any of `0-99`, `0-rc.[0-99]`, or `0-beta.[0-99]`.
+These are the instructions for releasing `{release_version}`, scheduled for `{release_date}`.
-## Version being released {RELEASE_VERSION}.{PATCH_VERSION}
+----
-The following details are copied from the official [Building and Publishing guide](https://developer.woocommerce.com/docs/contribution/releases/building-and-publishing/). Please check it to make sure these instructions haven't become out of date.
-Check the [Release Troubleshooting & Recovery](https://developer.woocommerce.com/docs/contribution/releases/troubleshooting/) page for any issues you may encounter.
+Perform all the steps below in order. When running _any_ GitHub workflow, ensure you do it from the `trunk` branch (the default) and input the release version or branch as indicated.
-## Pre-Checks
+Keep the _[Release Troubleshooting & Recovery](https://developer.woocommerce.com/docs/contribution/releases/troubleshooting/)_ guide handy, in case you encounter any issues.
-- [ ] **Verify no open [Pull Requests](https://github.com/woocommerce/woocommerce/pulls?q=is%3Aopen+is%3Apr) or [Issues](https://github.com/woocommerce/woocommerce/issues)** for the milestone matching the release being published.
- - All pull requests tied to the release milestone must be closed, including [backported pull requests](https://github.com/woocommerce/woocommerce/pulls?q=is%3Apr+label%3A%22type%3A+cherry-pick%22) that may need to be merged into other release branches or trunk.
-- [ ] **Check for unresolved ["cherry pick failed" Pull Requests](https://github.com/woocommerce/woocommerce/pulls?q=is:pr+label:%22cherry+pick+failed%22).**
- - Ensure any such PRs are either expected or manually resolved via another PR.
-- [ ] **Confirm the Stable tag in** `readme.txt` **matches [trunk on WordPress.org](https://plugins.trac.wordpress.org/browser/woocommerce/trunk/readme.txt#L7).**
- - The value should match the current stable version, not the version being built.
-- [ ] **Ensure [GitHub services](https://www.githubstatus.com/) are fully operational**
+----
-## Build WooCommerce
+### 1. Pre-build checks
-1. **Run the ["Release: Bump version number" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-bump-version.yml).**
- - Run from `trunk`.
- - Choose the type of version you're releasing (`beta`, `rc`, or `stable`).
- - Enter as *Release branch* the branch you are about to release from (e.g. `release/10.0`).
- - Review and merge the PR created.
+- [ ] Confirm [GitHub services](https://www.githubstatus.com/) are operational.
+- [ ] Verify no open issues or pull requests exist against the [{release_milestone} milestone]({repository_url}/issues?q=is:open+milestone:{release_milestone}). Ping authors as needed to merge or close.
+- [ ] Ensure that there aren't any pull requests [with label "cherry pick failed"]({repository_url}/pulls?q=is:pr+label:%22cherry+pick+failed%22) that apply to this release that haven't been actioned.
+- [ ] Confirm the `Stable tag` value [in the readme.txt on the release branch]({repository_url}/blob/{release_branch}/plugins/woocommerce/readme.txt#L7) matches the one [on WordPress.org's `trunk`](https://plugins.trac.wordpress.org/browser/woocommerce/trunk/readme.txt#L7).
-2. **Run the ["Release: Compile changelog" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-compile-changelog.yml).**
- - Run from `trunk` and enter the major version number and the intended release date.
- - **Review and merge the two PRs created** (one for trunk, one for the release branch).
- - **Ensure the changelog date is correct.**
-3. **Build the release ZIP file.**
- - Build the release ZIP file using the ["Release: Build ZIP file" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-build-zip-file.yml).
- - Run from `trunk` and enter the release branch as argument.
- - The workflow will create a [draft release tag](https://github.com/woocommerce/woocommerce/releases) with an attached `woocommerce.zip` file.
+### 2. Build the release package
-## Publish the Release
+- [ ] Run workflow **[Release: Bump version number]({repository_url}/actions/workflows/release-bump-version.yml)**: enter `{release_branch}` as _Release branch_ and `{release_type}` as _Type of version bump to perform_.
+- [ ] Review and merge the PR that was generated against the release branch. Check the [{release_milestone} milestone]({repository_url}/issues?q=is:open+milestone:{release_milestone}).
+- [ ] Run workflow **[Release: Compile changelog]({repository_url}/actions/workflows/release-compile-changelog.yml)**: enter `{release_main_version}` as _Version_ and leave _Release date_ empty, except when building the package ahead of schedule.
+- [ ] Review and merge the PRs that were generated: one against `trunk` and another one against the release branch. Both should be under the [{release_milestone} milestone]({repository_url}/issues?q=is:open+milestone:{release_milestone}).
+- [ ] Run workflow **[Release: Build ZIP file]({repository_url}/actions/workflows/release-build-zip-file.yml)** to build the asset and create the GitHub release: enter `{release_branch}` as _Release branch_ and check _Create GitHub release_.
+- [ ] Confirm that a draft `{release_version}` release [was created in the repository]({repository_url}/releases) with an attached `woocommerce.zip` asset.
-### Step 1: Upload Release to WordPress.org
-- [ ] **Run the ["Release: Upload release to WordPress.org" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-upload-to-wporg.yml)** from `trunk` using the release tag.
-- [ ] **This creates a new [SVN tag](https://plugins.svn.wordpress.org/woocommerce/tags/) and, if the release is newer than trunk, overwrites trunk.**
+### 3. Upload the release to WordPress.org
-### Step 2: Approve the Release
+- [ ] Run workflow **[Release: Upload release to WordPress.org]({repository_url}/actions/workflows/release-upload-to-wporg.yml)**: enter `{release_version}` as _Release tag to upload_ and make sure to check off the confirmation box.
+- [ ] Confirm that SVN tag `{release_version}` [exists on WordPress.org SVN](https://plugins.svn.wordpress.org/woocommerce/tags/{release_version}).
+- [ ] [Log into WordPress.org](https://wordpress.org/plugins/developers/releases/) using the credentials from the `WordPress.org "WooCommerce" user account` secret in the secret store and approve the release.
+- [ ] After a few minutes, confirm that [`{release_version}` is available for download](https://downloads.wordpress.org/plugin/woocommerce.{release_version}.zip).
-- [ ] **Visit [WordPress.org plugin releases](https://wordpress.org/plugins/developers/releases/) and approve the release.**
-- [ ] **Wait a few minutes for WordPress.org to build the new version.**
-### Step 3: Verify Release Availability
+### 4. Deploy to the staging environment
-- [ ] **Confirm the new release appears at:**
- - <https://plugins.svn.wordpress.org/woocommerce/tags/>
- - The "Previous versions" dropdown on the [Advanced Options screen](https://wordpress.org/plugins/woocommerce/advanced/).
+- [ ] Follow the [guide to deploy to the staging environment](https://wp.me/PCYsg-18BQ) and monitor for at least {release_monitoring_time} hours after deploy.
-## Release to the Staging Environment (Stable and RC releases)
+**If a critical issue was detected while monitoring...**
-- [ ] **Condition:** Only perform this step for stable and RC releases (`-rc.x` or `.x`).
-- [ ] **Action:** Follow the [guide to deploy to the staging environment](https://developer.woocommerce.com/docs/contribution/releases/building-and-publishing/).
+- [ ] Request a revert in the staging environment.
+- [ ] Pause the release process and **do not continue with any steps on this issue**. Follow the procedure in the [troubleshooting guide](https://developer.woocommerce.com/docs/contribution/releases/troubleshooting/#deploy-serious-bug) instead.
-## Update the Release Tags
-### Step 1: Update Stable Tag
+### 5. Publish the release
-- [ ] **Condition:** Only perform this step if:
- - The release is a stable release, and
- - No major issues were found during the release to staging.
- - You have monitored the release following the ["Monitoring Deploys to WPCOM Atomic"](https://fieldguide.automattic.com/woocommerce-core-releases/woocommerce-core-releases-deploying-to-atomic-staging/#monitoring-deploys-atomic) guide for at least 4 hours for `.0` releases and at least 2 hours for other patch releases.
-- [ ] **Action:** Run the ["Release: Update stable tag" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-update-stable-tag.yml) from `trunk`, set the version, and select the option to update the stable tag as part of the workflow input.
- - Review and merge the pull requests created (one for the release branch and trunk).
+- [ ] Run workflow **[Release: Update stable tag]({repository_url}/actions/workflows/release-update-stable-tag.yml)**: enter `{release_version}` as _Version_ and make sure to check off the confirmation box.
+- [ ] Publish the `{release_version}` [release draft]({repository_url}/releases) that was previously created, as well as any other `{release_main_version}` drafts that might exist from previous attempts. **Ensure** that "Set as the latest release" is checked for `{release_version}`.
-### Step 2: Publish GitHub Release Tag
-- [ ] **Action:** Publish the [previously created GitHub draft release tag](https://github.com/woocommerce/woocommerce/releases).
-- [ ] **When setting release status:**
- - If releasing a dev, beta, or RC, check "Set as a pre-release."
- - If the version was marked as stable in Step 1 above, check "Set as the latest release."
- - If the version was not marked as stable in Step 1 above, do not set as the latest release.
+### 6. Post-release tasks
-### Step 3: Merge follow-up PRs
-
-- [ ] **Action:** Merge the "Update changelog.txt after {RELEASE_VERSION}.{PATCH_VERSION}" PR that is automatically created once the release tag is published on GitHub.
-
-## Post Release Monitoring
-
-For all RC and stable releases, the release lead should continue to monitor for any bugs directly related to the latest version. Monitoring should continue for 3 days after a major release and 1 day for a point release.
-
-See the [WooCommerce Release Monitoring Guide](https://developer.woocommerce.com/docs/contribution/releases/monitoring/) for more details.
+- [ ] Wait at least 1 hour for all automations to complete and make sure to merge any follow-up PRs under the [{release_milestone} milestone]({repository_url}/issues?q=is:open+milestone:{release_milestone}).
+- [ ] Continue monitoring for bugs related to the release for at least 3 days. See the [release monitoring guide](https://developer.woocommerce.com/docs/contribution/releases/monitoring/) for more details.
diff --git a/.linear/release-kickoff-rc.md b/.linear/release-kickoff-rc.md
index e2728360a4..b01afd6ef0 100644
--- a/.linear/release-kickoff-rc.md
+++ b/.linear/release-kickoff-rc.md
@@ -1,73 +1,51 @@
-# WooCommerce {RELEASE_VERSION}-rc.{RC_PATCH_VERSION}
+# [{release_main_version}] Release `{release_version}`
-This issue tracks the progress of a single WooCommerce core plugin release iteration that covers the publishing of a single patch version. Patch version in this context refers to `z` in a version following `x.y.z` where `z` can be any of `0-99`, `0-rc.[0-99]`, or `0-beta.[0-99]`.
+These are the instructions for releasing `{release_version}`, scheduled for `{release_date}`.
-## Version being released {RELEASE_VERSION}-RC.{RC_PATCH_VERSION}
+----
-The following details are copied from the official [Building and Publishing guide](https://developer.woocommerce.com/docs/contribution/releases/building-and-publishing/). Please check it to make sure these instructions haven't become out of date.
-Check the [Release Troubleshooting & Recovery](https://developer.woocommerce.com/docs/contribution/releases/troubleshooting/) page for any issues you may encounter.
+Perform all the steps below in order. When running _any_ GitHub workflow, ensure you do it from the `trunk` branch (the default) and input the release version or branch as indicated.
-## Pre-Checks
+Keep the _[Release Troubleshooting & Recovery](https://developer.woocommerce.com/docs/contribution/releases/troubleshooting/)_ guide handy, in case you encounter any issues.
-- [ ] **Verify no open [Pull Requests](https://github.com/woocommerce/woocommerce/pulls?q=is%3Aopen+is%3Apr) or [Issues](https://github.com/woocommerce/woocommerce/issues)** for the milestone matching the release being published.
- - All pull requests tied to the release milestone must be closed, including [backported pull requests](https://github.com/woocommerce/woocommerce/pulls?q=is%3Apr+label%3A%22type%3A+cherry-pick%22) that may need to be merged into other release branches or trunk.
-- [ ] **Check for unresolved ["cherry pick failed" Pull Requests](https://github.com/woocommerce/woocommerce/pulls?q=is:pr+label:%22cherry+pick+failed%22).**
- - Ensure any such PRs are either expected or manually resolved via another PR.
-- [ ] **Confirm the Stable tag in** `readme.txt` **matches [trunk on WordPress.org](https://plugins.trac.wordpress.org/browser/woocommerce/trunk/readme.txt#L7).**
- - The value should match the current stable version, not the version being built.
-- [ ] **Ensure [GitHub services](https://www.githubstatus.com/) are fully operational**
+----
-## Build WooCommerce
+### 1. Pre-build checks
-1. **Run the ["Release: Bump version number" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-bump-version.yml).**
- - Run from `trunk`.
- - Choose the type of version you're releasing (`beta`, `rc`, or `stable`).
- - Enter as *Release branch* the branch you are about to release from (e.g. `release/10.0`).
- - Review and merge the PR created.
+- [ ] Confirm [GitHub services](https://www.githubstatus.com/) are operational.
+- [ ] Verify no open issues or pull requests exist against the [{release_milestone} milestone]({repository_url}/issues?q=is:open+milestone:{release_milestone}). Ping authors as needed to merge or close.
+- [ ] Ensure that there aren't any pull requests [with label "cherry pick failed"]({repository_url}/pulls?q=is:pr+label:%22cherry+pick+failed%22) that apply to this release that haven't been actioned.
+- [ ] Confirm the `Stable tag` value [in the readme.txt on the release branch]({repository_url}/blob/{release_branch}/plugins/woocommerce/readme.txt#L7) matches the one [on WordPress.org's `trunk`](https://plugins.trac.wordpress.org/browser/woocommerce/trunk/readme.txt#L7).
-2. **Run the ["Release: Compile changelog" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-compile-changelog.yml).**
- - Run from `trunk` and enter the major version number and the intended release date.
- - **Review and merge the two PRs created** (one for trunk, one for the release branch).
- - **Ensure the changelog date is correct.**
-3. **Build the release ZIP file.**
- - Build the release ZIP file using the ["Release: Build ZIP file" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-build-zip-file.yml).
- - Run from `trunk` and enter the release branch as argument.
- - The workflow will create a [draft release tag](https://github.com/woocommerce/woocommerce/releases) with an attached `woocommerce.zip` file.
+### 2. Build the release package
-## Publish the Release
+- [ ] Run workflow **[Release: Bump version number]({repository_url}/actions/workflows/release-bump-version.yml)**: enter `{release_branch}` as _Release branch_ and `{release_type}` as _Type of version bump to perform_.
+- [ ] Review and merge the PR that was generated against the release branch. Check the [{release_milestone} milestone]({repository_url}/issues?q=is:open+milestone:{release_milestone}).
+- [ ] Run workflow **[Release: Compile changelog]({repository_url}/actions/workflows/release-compile-changelog.yml)**: enter `{release_main_version}` as _Version_ and leave _Release date_ empty, except when building the package ahead of schedule.
+- [ ] Review and merge the PRs that were generated: one against `trunk` and another one against the release branch. Both should be under the [{release_milestone} milestone]({repository_url}/issues?q=is:open+milestone:{release_milestone}).
+- [ ] Run workflow **[Release: Build ZIP file]({repository_url}/actions/workflows/release-build-zip-file.yml)** to build the asset and create the GitHub release: enter `{release_branch}` as _Release branch_ and check _Create GitHub release_.
+- [ ] Confirm that a draft `{release_version}` release [was created in the repository]({repository_url}/releases) with an attached `woocommerce.zip` asset.
-### Step 1: Upload Release to WordPress.org
-- [ ] **Run the ["Release: Upload release to WordPress.org" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-upload-to-wporg.yml)** from `trunk` using the release tag.
-- [ ] **This creates a new [SVN tag](https://plugins.svn.wordpress.org/woocommerce/tags/) and, if the release is newer than trunk, overwrites trunk.**
+### 3. Upload the release to WordPress.org
-### Step 2: Approve the Release
+- [ ] Run workflow **[Release: Upload release to WordPress.org]({repository_url}/actions/workflows/release-upload-to-wporg.yml)**: enter `{release_version}` as _Release tag to upload_ and make sure to check off the confirmation box.
+- [ ] Confirm that SVN tag `{release_version}` [exists on WordPress.org SVN](https://plugins.svn.wordpress.org/woocommerce/tags/{release_version}).
+- [ ] [Log into WordPress.org](https://wordpress.org/plugins/developers/releases/) using the credentials from the `WordPress.org "WooCommerce" user account` secret in the secret store and approve the release.
+- [ ] After a few minutes, confirm that [`{release_version}` is available for download](https://downloads.wordpress.org/plugin/woocommerce.{release_version}.zip).
-- [ ] **Visit [WordPress.org plugin releases](https://wordpress.org/plugins/developers/releases/) and approve the release.**
-- [ ] **Wait a few minutes for WordPress.org to build the new version.**
-### Step 3: Verify Release Availability
+### 4. Deploy to the staging environment
-- [ ] **Confirm the new release appears at:**
- - <https://plugins.svn.wordpress.org/woocommerce/tags/>
- - The "Previous versions" dropdown on the [Advanced Options screen](https://wordpress.org/plugins/woocommerce/advanced/).
+- [ ] Follow the [guide to deploy to the staging environment](https://wp.me/PCYsg-18BQ) and monitor for at least {release_monitoring_time} hours after deploy.
-## Release to the Staging Environment (Stable and RC releases)
+**If a critical issue was detected while monitoring...**
-- [ ] **Condition:** Only perform this step for stable and RC releases (`-rc.x` or `.x`).
-- [ ] **Action:** Follow the [guide to deploy to the staging environment](https://wp.me/PCYsg-18BQ).
+- [ ] Request a revert in the staging environment.
+- [ ] Pause the release process and **do not continue with any steps on this issue**. Follow the procedure in the [troubleshooting guide](https://developer.woocommerce.com/docs/contribution/releases/troubleshooting/#deploy-serious-bug) instead.
-## Update the Release Tags
-### Step 1: Publish GitHub Release Tag
+### 5. Publish the release
-- [ ] **Action:** [Publish the previously created GitHub draft release tag](https://github.com/woocommerce/woocommerce/releases).
-- [ ] **When setting release status:**
- - check "Set as a pre-release."
-
-## Post Release Monitoring
-
-For all RC and stable releases, the release lead should continue to monitor for any bugs directly related to the latest version. Monitoring should continue for 3 days after a major release and 1 day for a point release.
-
-See the [WooCommerce Release Monitoring Guide](https://developer.woocommerce.com/docs/contribution/releases/monitoring/) for more details.
+- [ ] Publish the `{release_version}` [release draft]({repository_url}/releases) that was previously created, as well as any other `{release_main_version}` drafts that might exist from previous attempts. **Do not** check "Set as the latest release".
diff --git a/.linear/release-kickoff.md b/.linear/release-kickoff.md
index 2b04b4df5c..2b01fdc301 100644
--- a/.linear/release-kickoff.md
+++ b/.linear/release-kickoff.md
@@ -1,18 +1,31 @@
-# WooCommerce Release {RELEASE_VERSION}: Tracking
+# [{release_main_version}] Release tracking
-This issue provides visibility on the progress of the release process of WooCommerce core **{RELEASE_VERSION}**, and it centralizes all related discussions and documentation. The ultimate goal of this issue is to keep a reference to the steps, resources, work, and conversations associated with this release so it can be helpful for future contributors handling the release of a new WooCommerce core version.
+This issue provides visibility on the progress of the release process of WooCommerce core **{release_main_version}**.
-• **WooCommerce core version to release:** {RELEASE_VERSION} ([milestone](https://github.com/woocommerce/woocommerce/milestones/{MILESTONE_NUMBER}))
-• **Release lead:** {RELEASE_LEAD} ({RELEASE_TEAM})
-• **Beta 1 Release Date:** {BETA_1_RELEASE_DATE}
-• **Beta 2 Release Date:** {BETA_2_RELEASE_DATE}
-• **RC 1 Release Date:** {RC1_RELEASE_DATE}
-• **Final Release Date:** {RELEASE_DATE} ([release calendar](https://developer.woocommerce.com/release-calendar/))
+- **Main version being released:** `{release_main_version}`
+- **Milestone:** [{release_milestone}]({repository_url}/issues?q=is:open+milestone:{release_milestone})
+- **Release branch:** [`{release_branch}`]({repository_url}/tree/{release_branch})
+- **Release lead:** {lead_user} ({lead_team})
+- **Relevant dates:** ([release calendar](https://developer.woocommerce.com/release-calendar/))
+ - **Feature Freeze:** {date_feature_freeze}
+ - **Beta 1:** {date_beta1}
+ - **Beta 2:** {date_beta2}
+ - **RC 1:** {date_rc1}
+ - **Stable:** {date_stable}
-## Resources
+---
-• **[WooCommerce core changelog](https://github.com/woocommerce/woocommerce/blob/trunk/changelog.txt)**
-• **[Previous releases](https://github.com/woocommerce/woocommerce/releases)**
-• **[Release process documentation](https://developer.woocommerce.com/docs/contribution/releases/)**
+⚠ Dear release lead:
+
+- Please read this issue carefully and familiarize yourself with the [release process documentation](https://developer.woocommerce.com/docs/contribution/releases/).
+- Join the release channels in Slack (`#woo-core-releases`, `#woo-core-releases-notifications`), where discussions happen and notifications are sent.
+- For every release in the cycle, there's a corresponding sub-issue. On the date of each release (see schedule above), open the relevant issue and follow the instructions in it.
+- Any additional point/patch releases after the first stable must be tracked as well. Run the **[Release: Create Tracking Issue]({repository_url}/actions/workflows/release-create-tracking-issue.yml)** workflow with the version (e.g., `{release_main_version}.1`) to create the sub-issue.
+
+##### Resources
+
+- [Release process documentation](https://developer.woocommerce.com/docs/contribution/releases/).
+- [Troubleshooting Guide](https://developer.woocommerce.com/docs/contribution/releases/troubleshooting/).
+- [Previous releases]({repository_url}/releases).
+- [WooCommerce core changelog]({repository_url}/blob/trunk/changelog.txt).
-Add any new Sub-issues as needed for any point releases (patch) needed after the `x.y.0` stable release using the [`.linear/release-kickoff-patch.md`](https://github.com/woocommerce/woocommerce/blob/trunk/.linear/release-kickoff-patch.md) template.