2022-08-02 15:14:58 +00:00
name : Cherry Pick Tool
on :
2022-08-10 23:36:59 +00:00
issues :
types : [ milestoned, labeled]
2022-08-02 15:14:58 +00:00
pull_request :
2022-08-10 23:36:59 +00:00
types : [ closed]
2022-08-02 15:14:58 +00:00
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 :
2022-08-10 23:36:59 +00:00
description : The pull request number.
default : ''
2022-08-02 15:14:58 +00:00
required : true
2022-08-10 23:36:59 +00:00
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'
GIT_COMMITTER_EMAIL : 'no-reply@woocommerce.com'
GIT_AUTHOR_NAME : 'WooCommerce Bot'
GIT_AUTHOR_EMAIL : 'no-reply@woocommerce.com'
2022-08-02 15:14:58 +00:00
jobs :
2022-08-10 23:36:59 +00:00
verify :
name : Verify
runs-on : ubuntu-20.04
outputs :
run : ${{ steps.check.outputs.run }}
steps :
- name : check
id : check
uses : actions/github-script@v6
with :
script : |
let run = false;
const isManualTrigger = context.payload.inputs && context.payload.inputs.release_branch && context.payload.inputs.release_branch != null;
const isMergedMilestonedIssue = context.payload.issue && context.payload.issue.pull_request != null && context.payload.issue.pull_request.merged_at != null && context.payload.issue.milestone != null;
const isMergedMilestonedPR = context.payload.pull_request && context.payload.pull_request != null && context.payload.pull_request.merged == true && context.payload.pull_request.milestone != null;
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 ) ) {
console.log( "::set-output name=run::true" );
} else {
console.log( "::set-output name=run::false" );
}
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/', '' );
console.log( "::set-output name=version::" + version )
console.log( "::set-output name=release::${{ inputs.release_branch }}" )
2022-09-09 12:25:53 +00:00
} else if ( event.action === 'milestoned' ) {
const version = '${{ github.event.issue.milestone.title }}'
const release = version.substring( 0, 3 )
console.log( "::set-output name=version::" + version )
console.log( "::set-output name=release::release/" + release )
2022-08-10 23:36:59 +00:00
} else {
const version = '${{ github.event.pull_request.milestone.title }}'
2022-09-09 12:25:53 +00:00
const release = version.substring( 0, 3 )
2022-08-10 23:36:59 +00:00
console.log( "::set-output name=version::" + version )
2022-09-09 12:25:53 +00:00
console.log( "::set-output name=release::release/" + release )
2022-08-10 23:36:59 +00:00
}
// Means this workflow was triggered manually.
if ( event.inputs && event.inputs.pull_requests ) {
console.log( "::set-output name=pr::${{ inputs.pull_requests }}" )
2022-09-09 12:25:53 +00:00
} else if ( event.action === 'milestoned' ) {
console.log( "::set-output name=pr::${{ github.event.issue.number }}" )
2022-08-10 23:36:59 +00:00
} else {
console.log( "::set-output name=pr::${{ github.event.pull_request.number }}" )
}
2022-08-15 21:00:14 +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 }}' ,
} );
2022-08-02 15:14:58 +00:00
cherry-pick-run :
name : Run cherry pick tool
runs-on : ubuntu-20.04
2022-08-15 21:00:14 +00:00
needs : [ prep, check-release-branch-exists ]
if : success()
2022-08-02 15:14:58 +00:00
steps :
2022-08-10 23:36:59 +00:00
- name : Checkout release branch
uses : actions/checkout@v3
with :
fetch-depth : 100
- 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 }}'
})
console.log( `::set-output name=sha::${ pr.data.merge_commit_sha }` )
- name : Cherry pick
run : |
git cherry-pick ${{ steps.commit-sha.outputs.sha }}
- 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 === false ) {
continue;
}
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 }}` );
}
} );
} );
}
}
console.log( `::set-output name=changelogsToBeDeleted::${ changelogsToBeDeleted }` )
- name : Delete changelog files from cherry pick branch
run : git rm ${{ steps.changelog.outputs.changelogsToBeDeleted }}
- name : Commit changes for cherry pick
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
})
console.log( `::set-output name=cherry-pick-pr::${ pr.data.html_url }` )
- name : Checkout trunk branch
run : git checkout trunk
- name : Create a branch based on trunk branch
run : git checkout -b delete-changelogs/${{ needs.prep.outputs.pr }}
- name : Delete changelogs from trunk
run : git rm ${{ steps.changelog.outputs.changelogsToBeDeleted }}
- name : Commit changes for deletion
run : git commit --no-verify -am "Delete changelog files for ${{ needs.prep.outputs.pr }}"
- name : Push deletion branch up
run : git push origin delete-changelogs/${{ needs.prep.outputs.pr }}
- name : Create the PR for deletion branch
id : deletion-pr
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 }}"
})
console.log( `::set-output name=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 }}
slack-channel : ${{ inputs.slackChannelOverride || secrets.WOO_RELEASE_SLACK_CHANNEL }}
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 }}
slack-channel : ${{ inputs.slackChannelOverride || secrets.WOO_RELEASE_SLACK_CHANNEL }}
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 :
2022-08-02 15:14:58 +00:00
2022-08-10 23:36:59 +00:00
${{ steps.cherry-pick-pr.outputs.cherry-pick-pr }}
${{ steps.deletion-pr.outputs.deletion-pr }}