2022-08-02 15:14:58 +00:00
name : Cherry Pick Tool
on :
2022-12-15 22:41:03 +00:00
issues :
types : [ milestoned, labeled]
pull_request :
types : [ closed]
workflow_dispatch :
inputs :
release_branch :
description : Provide the release branch you want to cherry pick into. Example release/6.9
default : ''
required : true
pull_requests :
description : The pull request number.
default : ''
required : true
skipSlackPing :
description: 'Skip Slack Ping : If true, the Slack ping will be skipped (useful for testing)'
type : boolean
required : false
default : false
slackChannelOverride :
description: 'Slack Channel Override : The channel ID to send the Slack ping about the code freeze violation'
required : false
default : ''
2022-08-02 15:14:58 +00:00
2022-08-10 23:36:59 +00:00
env :
GIT_COMMITTER_NAME : 'WooCommerce Bot'
2024-04-19 21:11:01 +00:00
GIT_COMMITTER_EMAIL : 'no-reply@woocommerce.com'
2022-08-10 23:36:59 +00:00
GIT_AUTHOR_NAME : 'WooCommerce Bot'
2024-04-19 21:11:01 +00:00
GIT_AUTHOR_EMAIL : 'no-reply@woocommerce.com'
2022-08-10 23:36:59 +00:00
2023-01-02 17:28:11 +00:00
permissions : {}
2022-08-02 15:14:58 +00:00
jobs :
2022-12-15 22:41:03 +00:00
verify :
name : Verify
runs-on : ubuntu-20.04
outputs :
run : ${{ steps.check.outputs.run }}
steps :
2024-05-11 21:15:53 +00:00
- name : Fetch Pull Request Details
if : github.event.issue.pull_request
id : fetch_pr_details
uses : actions/github-script@v6
with :
script : |
const issue = context.payload.issue;
const pullRequestUrl = issue.pull_request.url;
const prDetails = await github.request(pullRequestUrl);
core.setOutput('base_ref', prDetails.data.base.ref);
env :
GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
2022-12-15 22:41:03 +00:00
- name : check
id : check
uses : actions/github-script@v6
with :
script : |
2024-05-11 21:15:53 +00:00
const baseRef = process.env.BASE_REF;
console.log("baseRef:", baseRef);
2022-12-15 22:41:03 +00:00
let run = false;
const isManualTrigger = context.payload.inputs && context.payload.inputs.release_branch && context.payload.inputs.release_branch != null;
2024-05-11 21:15:53 +00:00
const isMergedMilestonedIssue = context.payload.issue && context.payload.issue.pull_request != null && context.payload.issue.pull_request.merged_at != null && context.payload.issue.milestone != null && baseRef == 'trunk';
2022-12-15 22:41:03 +00:00
2024-05-11 21:15:53 +00:00
const isMergedMilestonedPR = context.payload.pull_request && context.payload.pull_request != null && context.payload.pull_request.merged == true && context.payload.pull_request.milestone != null && baseRef == 'trunk';
2022-12-15 22:41:03 +00:00
const isBot = context.payload.pull_request && ( context.payload.pull_request.user.login == 'github-actions[bot]' || context.payload.pull_request.user.type == 'Bot' );
if ( !isBot && ( isManualTrigger || isMergedMilestonedIssue || isMergedMilestonedPR ) ) {
core.setOutput( 'run', 'true' );
} else {
core.setOutput( 'run', 'false' );
2022-11-04 16:11:26 +00:00
}
2024-05-11 21:15:53 +00:00
env :
BASE_REF : ${{ steps.fetch_pr_details.outputs.base_ref }}
2022-12-15 22:41:03 +00:00
prep :
name : Prep inputs
runs-on : ubuntu-20.04
needs : verify
if : needs.verify.outputs.run == 'true'
outputs :
release : ${{ steps.prep-inputs.outputs.release }}
pr : ${{ steps.prep-inputs.outputs.pr }}
version : ${{ steps.prep-inputs.outputs.version }}
steps :
- name : Prep inputs
id : prep-inputs
uses : actions/github-script@v6
with :
script : |
const event = ${{ toJSON( github.event ) }}
// Means this workflow was triggered manually.
if ( event.inputs && event.inputs.release_branch ) {
const releaseBranch = '${{ inputs.release_branch }}'
const version = releaseBranch.replace( 'release/', '' )
core.setOutput( 'version', version )
core.setOutput( 'release', releaseBranch )
} else if ( event.action === 'milestoned' ) {
const version = '${{ github.event.issue.milestone.title }}'
const release = version.substring( 0, 3 )
core.setOutput( 'version', version )
core.setOutput( 'release', `release/${release}` )
} else {
const version = '${{ github.event.pull_request.milestone.title }}'
const release = version.substring( 0, 3 )
core.setOutput( 'version', version )
core.setOutput( 'release', `release/${release}` )
2022-11-04 16:11:26 +00:00
}
2022-08-10 23:36:59 +00:00
2022-12-15 22:41:03 +00:00
// Means this workflow was triggered manually.
if ( event.inputs && event.inputs.pull_requests ) {
core.setOutput( 'pr', '${{ inputs.pull_requests }}' )
} else if ( event.action === 'milestoned' ) {
core.setOutput( 'pr', '${{ github.event.issue.number }}' )
} else {
core.setOutput( 'pr', '${{ github.event.pull_request.number }}' )
2022-11-04 16:11:26 +00:00
}
2022-12-15 22:41:03 +00:00
check-release-branch-exists :
name : Check for existence of release branch
runs-on : ubuntu-20.04
needs : prep
steps :
- name : Check for release branch
id : release-breanch-check
uses : actions/github-script@v6
with :
script : |
// This will throw an error for non-200 responses, which prevents subsequent jobs from completing, as desired.
await github.request( 'GET /repos/{owner}/{repo}/branches/{branch}', {
owner : context.repo.owner,
repo : context.repo.repo,
branch : '${{ needs.prep.outputs.release }}' ,
} );
cherry-pick-run :
name : Run cherry pick tool
runs-on : ubuntu-20.04
2023-01-02 17:28:11 +00:00
permissions :
actions : write
contents : write
pull-requests : write
2022-12-15 22:41:03 +00:00
needs : [ prep, check-release-branch-exists]
if : success()
steps :
- name : Checkout release branch
uses : actions/checkout@v3
with :
fetch-depth : 0
- name : Git fetch the release branch
run : git fetch origin ${{ needs.prep.outputs.release }}
- name : Checkout release branch
run : git checkout ${{ needs.prep.outputs.release }}
- name : Create a cherry pick branch based on release branch
run : git checkout -b cherry-pick-${{ needs.prep.outputs.version }}/${{ needs.prep.outputs.pr }}
- name : Get commit sha from PR
id : commit-sha
uses : actions/github-script@v6
with :
script : |
const pr = await github.rest.pulls.get({
owner : context.repo.owner,
repo : context.repo.repo,
pull_number : '${{ needs.prep.outputs.pr }}'
})
core.setOutput( 'sha', pr.data.merge_commit_sha )
- name : Cherry pick
run : |
2022-12-22 03:56:14 +00:00
git cherry-pick ${{ steps.commit-sha.outputs.sha }} -m1
2022-12-15 22:41:03 +00:00
- name : Generate changelog
id : changelog
uses : actions/github-script@v6
with :
script : |
const fs = require( 'node:fs' );
const changelogsToBeDeleted = []
let changelogTxt = '';
const commit = await github.rest.repos.getCommit({
owner : context.repo.owner,
repo : context.repo.repo,
ref : '${{ steps.commit-sha.outputs.sha }}'
})
for ( const file of commit.data.files ) {
if ( file.filename.match( 'plugins/woocommerce/changelog/' ) ) {
if ( changelogsToBeDeleted.indexOf( file.filename ) === -1 ) {
changelogsToBeDeleted.push( file.filename );
}
let changelogEntry = '';
let changelogEntryType = '';
fs.readFile( './' + file.filename, 'utf-8', function( err, data ) {
if ( err ) {
console.error( err );
}
const changelogEntryArr = data.split( "\n" );
changelogEntryType = data.match( /Type : (.+)/i );
changelogEntryType = changelogEntryType[ 1 ].charAt( 0 ).toUpperCase() + changelogEntryType[ 1 ].slice( 1 );
changelogEntry = changelogEntryArr.filter( el => {
return el !== null && typeof el !== 'undefined' && el !== '';
} );
changelogEntry = changelogEntry[ changelogEntry.length - 1 ];
// Check if changelogEntry is what we want.
if ( changelogEntry.length < 1 ) {
changelogEntry = false;
}
if ( changelogEntry.match( /significance:/i ) ) {
changelogEntry = false;
}
if ( changelogEntry.match( /type:/i ) ) {
changelogEntry = false;
}
if ( changelogEntry.match( /comment:/i ) ) {
changelogEntry = false;
}
if ( ! changelogEntry ) {
return;
}
fs.readFile( './plugins/woocommerce/readme.txt', 'utf-8', function( err, data ) {
if ( err ) {
console.error( err );
}
changelogTxt = data.split( "\n" );
let isInRange = false;
let newChangelogTxt = [];
for ( const line of changelogTxt ) {
if ( isInRange === false && line === '== Changelog ==' ) {
isInRange = true;
}
if ( isInRange === true && line.match( /\*\*WooCommerce Blocks/ ) ) {
isInRange = false;
}
// Find the first match of the entry "Type".
if ( isInRange && line.match( `\\* ${changelogEntryType} -` ) ) {
newChangelogTxt.push( '* ' + changelogEntryType + ' - ' + changelogEntry + ` [#${{ needs.prep.outputs.pr }}](https://github.com/woocommerce/woocommerce/pull/${{ needs.prep.outputs.pr }})` );
newChangelogTxt.push( line );
isInRange = false;
continue;
}
newChangelogTxt.push( line );
}
fs.writeFile( './plugins/woocommerce/readme.txt', newChangelogTxt.join( "\n" ), err => {
if ( err ) {
console.error( `Unable to generate the changelog entry for PR ${{ needs.prep.outputs.pr }}` );
}
} );
} );
} );
}
2022-11-04 16:11:26 +00:00
}
2022-12-15 22:41:03 +00:00
core.setOutput( 'changelogsToBeDeleted', changelogsToBeDeleted.join( ' ' ) )
- name : Delete changelog files from cherry pick branch
if : steps.changelog.outputs.changelogsToBeDeleted != '' && steps.changelog.outputs.changelogsToBeDeleted != null
run : git rm ${{ steps.changelog.outputs.changelogsToBeDeleted }}
- name : Commit changes for cherry pick
if : steps.changelog.outputs.changelogsToBeDeleted != '' && steps.changelog.outputs.changelogsToBeDeleted != null
run : git commit --no-verify -am "Prep for cherry pick ${{ needs.prep.outputs.pr }}"
- name : Push cherry pick branch up
run : git push origin cherry-pick-${{ needs.prep.outputs.version }}/${{ needs.prep.outputs.pr }}
- name : Create the PR for cherry pick branch
id : cherry-pick-pr
uses : actions/github-script@v6
with :
script : |
let cherryPickPRBody = "This PR cherry-picks the following PRs into the release branch:\n";
cherryPickPRBody = `${cherryPickPRBody}` + `* #${{ needs.prep.outputs.pr }}` + "\n";
const pr = await github.rest.pulls.create({
owner : context.repo.owner,
repo : context.repo.repo,
title : "Cherry pick ${{ needs.prep.outputs.pr }} into ${{ needs.prep.outputs.release }}" ,
head : "cherry-pick-${{ needs.prep.outputs.version }}/${{ needs.prep.outputs.pr }}" ,
base : "${{ needs.prep.outputs.release }}" ,
body : cherryPickPRBody
})
core.setOutput( 'cherry-pick-pr', pr.data.html_url )
2023-10-03 21:00:17 +00:00
// label PR
const label = await github.rest.issues.addLabels({
owner : context.repo.owner,
repo : context.repo.repo,
issue_number : pr.data.number,
labels : [ "metric: code freeze exception" ] ,
});
2022-12-15 22:41:03 +00:00
- name : Checkout trunk branch
if : steps.changelog.outputs.changelogsToBeDeleted != '' && steps.changelog.outputs.changelogsToBeDeleted != null
run : git checkout trunk
- name : Create a branch based on trunk branch
if : steps.changelog.outputs.changelogsToBeDeleted != '' && steps.changelog.outputs.changelogsToBeDeleted != null
run : git checkout -b delete-changelogs/${{ needs.prep.outputs.pr }}
- name : Delete changelogs from trunk
if : steps.changelog.outputs.changelogsToBeDeleted != '' && steps.changelog.outputs.changelogsToBeDeleted != null
run : git rm ${{ steps.changelog.outputs.changelogsToBeDeleted }}
- name : Commit changes for deletion
if : steps.changelog.outputs.changelogsToBeDeleted != '' && steps.changelog.outputs.changelogsToBeDeleted != null
run : git commit --no-verify -am "Delete changelog files for ${{ needs.prep.outputs.pr }}"
- name : Push deletion branch up
if : steps.changelog.outputs.changelogsToBeDeleted != '' && steps.changelog.outputs.changelogsToBeDeleted != null
run : git push origin delete-changelogs/${{ needs.prep.outputs.pr }}
- name : Create the PR for deletion branch
id : deletion-pr
if : steps.changelog.outputs.changelogsToBeDeleted != '' && steps.changelog.outputs.changelogsToBeDeleted != null
uses : actions/github-script@v6
with :
script : |
const pr = await github.rest.pulls.create({
owner : context.repo.owner,
repo : context.repo.repo,
title : "Delete changelog files based on PR ${{ needs.prep.outputs.pr }}" ,
head : "delete-changelogs/${{ needs.prep.outputs.pr }}" ,
base : "trunk" ,
body : "Delete changelog files based on PR #${{ needs.prep.outputs.pr }}"
})
core.setOutput( 'deletion-pr', pr.data.html_url )
- name : Notify Slack on failure
if : ${{ failure() && inputs.skipSlackPing != true }}
uses : archive/github-actions-slack@v2.0.0
with :
slack-bot-user-oauth-access-token : ${{ secrets.CODE_FREEZE_BOT_TOKEN }}
2023-06-26 20:24:33 +00:00
slack-channel : ${{ inputs.slackChannelOverride || secrets.WOO_RELEASE_SLACK_NOTIFICATION_CHANNEL }}
2022-12-15 22:41:03 +00:00
slack-text : |
:warning-8c : Code freeze violation. PR(s) created that breaks the Code Freeze for '${{ needs.prep.outputs.release }}' :ice_cube:
An attempt to cherry pick PR(s) into outgoing release '${{ needs.prep.outputs.release }}' has failed. This could be due to a merge conflict or something else that requires manual attention. Please check: https://github.com/woocommerce/woocommerce/pull/${{ needs.prep.outputs.pr }}
- name : Notify Slack on success
if : ${{ success() && inputs.skipSlackPing != true }}
uses : archive/github-actions-slack@v2.0.0
with :
slack-bot-user-oauth-access-token : ${{ secrets.CODE_FREEZE_BOT_TOKEN }}
2023-06-26 20:24:33 +00:00
slack-channel : ${{ inputs.slackChannelOverride || secrets.WOO_RELEASE_SLACK_NOTIFICATION_CHANNEL }}
2022-12-15 22:41:03 +00:00
slack-text : |
:warning-8c : Code freeze violation. PR(s) created that breaks the Code Freeze for '${{ needs.prep.outputs.release }}' :ice_cube:
Release lead please review :
${{ steps.cherry-pick-pr.outputs.cherry-pick-pr }}
2024-05-11 21:15:53 +00:00
${{ steps.deletion-pr.outputs.deletion-pr }}