2021-09-29 15:01:05 +00:00
import { spawn } from 'child_process' ;
import fs from 'fs' ;
import open from 'open' ;
2021-10-01 14:42:39 +00:00
import inquirer from 'inquirer' ;
2021-09-29 15:01:05 +00:00
const remoteSSH = 'wpcom-sandbox' ;
const sandboxPublicThemesFolder = '/home/wpdev/public_html/wp-content/themes/pub' ;
2022-01-26 16:59:51 +00:00
const sandboxPremiumThemesFolder = '/home/wpdev/public_html/wp-content/themes/premium' ;
2021-09-29 15:01:05 +00:00
const sandboxRootFolder = '/home/wpdev/public_html/' ;
const isWin = process . platform === 'win32' ;
2022-01-26 16:59:51 +00:00
const premiumThemes = [ 'videomaker' , 'videomaker-white' ] ;
2022-02-21 20:52:07 +00:00
const coreThemes = [ 'twentyten' , 'twentyeleven' , 'twentytwelve' , 'twentythirteen' , 'twentyfourteen' , 'twentyfifteen' , 'twentysixteen' , 'twentyseventeen' , 'twentynineteen' , 'twentytwenty' , 'twentytwentyone' , 'twentytwentytwo' ] ;
2021-09-29 15:01:05 +00:00
( async function start ( ) {
let args = process . argv . slice ( 2 ) ;
let command = args ? . [ 0 ] ;
switch ( command ) {
2022-02-02 09:53:54 +00:00
case "push-button-deploy" : return pushButtonDeploy ( ) ;
case "clean-sandbox" : return cleanSandbox ( ) ;
case "clean-premium-sandbox" : return cleanPremiumSandbox ( ) ;
case "clean-all-sandbox" : return cleanAllSandbox ( ) ;
2021-09-29 15:01:05 +00:00
case "push-to-sandbox" : return pushToSandbox ( ) ;
case "push-changes-to-sandbox" : return pushChangesToSandbox ( ) ;
2021-12-16 10:28:46 +00:00
case "push-premium-to-sandbox" : return pushPremiumToSandbox ( ) ;
2021-09-29 15:01:05 +00:00
case "version-bump-themes" : return versionBumpThemes ( ) ;
2022-02-02 09:53:54 +00:00
case "land-diff" : return landChanges ( args ? . [ 1 ] ) ;
2021-10-01 14:42:39 +00:00
case "deploy-preview" : return deployPreview ( ) ;
2021-10-26 15:22:14 +00:00
case "deploy-theme" : return deployThemes ( [ args ? . [ 1 ] ] ) ;
2021-10-29 07:38:36 +00:00
case "build-com-zip" : return buildComZip ( [ args ? . [ 1 ] ] ) ;
2022-02-21 20:52:07 +00:00
case "pull-core-themes" : return pullCoreThemes ( ) ;
case "push-core-themes" : return pushCoreThemes ( ) ;
case "sync-core-theme" : return syncCoreTheme ( args ? . [ 1 ] , args ? . [ 2 ] ) ;
case "deploy-sync-core-theme" : return deploySyncCoreTheme ( args ? . [ 1 ] , args ? . [ 2 ] ) ;
2021-09-29 15:01:05 +00:00
}
return showHelp ( ) ;
} ) ( ) ;
function showHelp ( ) {
// TODO: make this helpful
console . log ( 'Help info can go here' ) ;
}
2022-04-20 18:28:17 +00:00
/ *
Create list of changes from git logs
Optionally pass in a deployed hash or default to calling getLastDeployedHash ( )
Optionally pass in boolean bulletPoints to add bullet points to each commit log
* /
async function getCommitLogs ( hash , bulletPoints ) {
if ( ! hash ) {
hash = await getLastDeployedHash ( ) ;
}
let logs = await executeCommand ( ` git log --reverse --pretty=format:%s ${ hash } ..HEAD ` ) ;
if ( bulletPoints ) {
// Add a '*' to the start of each log (used in changelogs)
logs = await executeCommand ( ` git log --reverse --pretty=format:"* %s" ${ hash } ..HEAD ` ) ;
}
// Remove any double quotes from commit messages
logs = logs . replace ( /"/g , '' ) ;
return logs ;
}
2021-10-14 12:18:24 +00:00
/ *
2021-10-01 14:42:39 +00:00
Determine what changes would be deployed
* /
async function deployPreview ( ) {
console . clear ( ) ;
console . log ( 'To ensure accuracy clean your sandbox before previewing. (It is not automatically done).' ) ;
let message = await checkForDeployability ( ) ;
if ( message ) {
console . log ( ` \n ${ message } \n \n ` ) ;
}
let hash = await getLastDeployedHash ( ) ;
console . log ( ` Last deployed hash: ${ hash } ` ) ;
let changedThemes = await getChangedThemes ( hash ) ;
console . log ( ` The following themes have changes: \n ${ changedThemes } ` ) ;
2021-10-14 12:18:24 +00:00
2022-04-20 18:28:17 +00:00
let logs = await getCommitLogs ( hash ) ;
2021-10-01 14:42:39 +00:00
console . log ( ` \n \n Commit log of changes to be deployed: \n \n ${ logs } \n \n ` ) ;
}
2021-09-29 15:01:05 +00:00
/ *
Execute the first phase of a deployment .
* Gets the last deployed hash from the sandbox
* Version bump all themes have have changes since the last deployment
* Commit the version bump change to github
* Clean the sandbox and ensure it is up - to - date
* Push all changed files ( including removal of deleted files ) since the last deployment
* Update the 'last deployed' hash on the sandbox
* Create a phabricator diff based on the changes since the last deployment . The description including the commit messages since the last deployment .
* Open the Phabricator Diff in your browser
* Create a tag in the github repository at this point of change which includes the phabricator link in the description
* /
2022-02-02 09:53:54 +00:00
async function pushButtonDeploy ( ) {
2021-09-29 15:01:05 +00:00
2021-10-11 20:00:20 +00:00
console . clear ( ) ;
let prompt = await inquirer . prompt ( [ {
type : 'confirm' ,
message : 'You are about to deploy /trunk. Are you ready to continue?' ,
name : "continue" ,
default : false
} ] ) ;
if ( ! prompt . continue ) {
return ;
}
2021-09-29 15:01:05 +00:00
2021-10-01 14:42:39 +00:00
let message = await checkForDeployability ( ) ;
if ( message ) {
return console . log ( ` \n \n ${ message } \n \n ` ) ;
}
2021-09-29 15:01:05 +00:00
2021-10-01 14:42:39 +00:00
try {
2022-02-02 09:53:54 +00:00
await cleanSandbox ( ) ;
2021-09-29 15:01:05 +00:00
2022-01-31 16:37:12 +00:00
//build variations
console . log ( 'Building Variations' ) ;
await executeCommand ( ` node ./variations/build-variations.mjs git-add-changes ` )
prompt = await inquirer . prompt ( [ {
type : 'confirm' ,
message : 'Are you good with any staged theme variations changes? Make any manual adjustments now if necessary.' ,
name : "continue" ,
default : false
} ] ) ;
if ( ! prompt . continue ) {
console . log ( ` Aborted Automated Deploy Process at variations building. ` ) ;
return ;
}
try {
await executeCommand ( `
git commit - m "Building Variations"
` );
} catch ( err ) {
// Most likely the error is that there are no variation changes to commit.
// Just swallowing that error for now
}
2021-10-01 14:42:39 +00:00
let hash = await getLastDeployedHash ( ) ;
2021-12-15 16:41:24 +00:00
let thingsWentBump = await versionBumpThemes ( ) ;
2021-10-14 14:41:04 +00:00
2022-01-17 17:45:15 +00:00
if ( thingsWentBump ) {
prompt = await inquirer . prompt ( [ {
type : 'confirm' ,
2022-04-20 18:28:17 +00:00
message : 'Are you good with the version bump and changelog updates? Make any manual adjustments now if necessary.' ,
2022-01-17 17:45:15 +00:00
name : "continue" ,
default : false
} ] ) ;
if ( ! prompt . continue ) {
console . log ( ` Aborted Automated Deploy Process at version bump changes. ` ) ;
return ;
}
}
2021-10-01 14:42:39 +00:00
let changedThemes = await getChangedThemes ( hash ) ;
2021-10-14 14:41:04 +00:00
2021-10-01 14:42:39 +00:00
await pushChangesToSandbox ( ) ;
2021-10-14 14:41:04 +00:00
2021-10-01 14:42:39 +00:00
2021-12-15 18:04:22 +00:00
//push changes (from version bump)
if ( thingsWentBump ) {
prompt = await inquirer . prompt ( [ {
type : 'confirm' ,
message : 'Are you ready to push this version bump change to the source repository (Github)?' ,
name : "continue" ,
default : false
} ] ) ;
if ( ! prompt . continue ) {
console . log ( ` Aborted Automated Deploy Process at version bump push change. ` ) ;
return ;
}
await executeCommand ( `
2021-12-15 18:18:32 +00:00
git commit - m "Version Bump" ;
2021-12-15 18:04:22 +00:00
git push
` , true);
}
2022-01-17 17:45:15 +00:00
await updateLastDeployedHash ( ) ;
2022-02-21 20:52:07 +00:00
let commitMessage = await buildPhabricatorCommitMessageSince ( hash ) ;
let diffUrl = await createPhabricatorDiff ( commitMessage ) ;
2021-10-01 14:42:39 +00:00
let diffId = diffUrl . split ( 'a8c.com/' ) [ 1 ] ;
2021-10-14 14:41:04 +00:00
2021-10-01 14:42:39 +00:00
await tagDeployment ( {
hash : hash ,
diffId : diffId
} ) ;
2021-11-12 13:50:34 +00:00
console . log ( ` \n \n Phase One Complete \n \n Your sandbox has been updated and the diff is available for review. \n Please give your sandbox a smoke test to determine that the changes work as expected. \n The following themes have had changes: \n \n ${ changedThemes . join ( ' ' ) } \n \n \n ` ) ;
2021-10-01 14:42:39 +00:00
prompt = await inquirer . prompt ( [ {
type : 'confirm' ,
message : 'Are you ready to land these changes?' ,
name : "continue" ,
default : false
} ] ) ;
if ( ! prompt . continue ) {
console . log ( ` Aborted Automated Deploy Process Landing Phase \n \n You will have to land these changes manually. The ID of the diff to land: ${ diffId } ` ) ;
return ;
}
2022-02-02 09:53:54 +00:00
await landChanges ( diffId ) ;
2021-10-01 14:42:39 +00:00
2022-02-01 14:26:32 +00:00
let changedPublicThemes = changedThemes . filter ( item => ! premiumThemes . includes ( item ) ) ;
try {
await deployThemes ( changedPublicThemes ) ;
}
catch ( err ) {
prompt = await inquirer . prompt ( [ {
type : 'confirm' ,
message : ` There was an error deploying themes. ${ err } Do you wish to continue to the next step? ` ,
name : "continue" ,
default : false
} ] ) ;
if ( ! prompt . continue ) {
console . log ( ` Aborted Automated Deploy during deploy phase. ` ) ;
return ;
}
}
await buildComZips ( changedPublicThemes ) ;
2021-10-11 20:45:21 +00:00
console . log ( ` The following themes have changed: \n ${ changedThemes . join ( '\n' ) } ` )
2021-10-01 14:42:39 +00:00
console . log ( '\n\nAll Done!!\n\n' ) ;
}
catch ( err ) {
2022-02-01 14:26:32 +00:00
console . log ( "ERROR with deploy script: " , err ) ;
2021-10-01 14:42:39 +00:00
}
2021-09-29 15:01:05 +00:00
}
2021-10-29 07:38:36 +00:00
2022-02-21 20:52:07 +00:00
async function deploySyncCoreTheme ( theme , sinceRevision ) {
await cleanSandbox ( ) ;
let latestRevision = await syncCoreTheme ( theme , sinceRevision ) ;
let prompt = await inquirer . prompt ( [ {
type : 'confirm' ,
message : ` Changes have been synced to your sandbox. Please resolve any conflicts (noted in .rej files). Are you ready to continue? ` ,
name : "continue" ,
default : false
} ] ) ;
if ( ! prompt . continue ) {
console . log ( ` Aborted Core Sync Deploy. ` ) ;
return ;
}
let logs = await executeCommand ( ` svn log https://core.svn.wordpress.org/trunk/wp-content/themes/ ${ theme } -r ${ sinceRevision } :HEAD ` )
let commitMessage = ` ${ theme } : Merge latest core changes up to [wp ${ latestRevision } ]
Summary :
$ { logs }
Test Plan : Activate $ { theme } and ensure nothing is broken
Reviewers :
# themes _team
Subscribers :
` ;
let diffUrl = await createPhabricatorDiff ( commitMessage ) ;
let diffId = diffUrl . split ( 'a8c.com/' ) [ 1 ] ;
prompt = await inquirer . prompt ( [ {
type : 'confirm' ,
message : 'Are you ready to land these changes?' ,
name : "continue" ,
default : false
} ] ) ;
if ( ! prompt . continue ) {
console . log ( ` Aborted Automated Deploy Sync Process Landing Phase \n \n You will have to land these changes manually. The ID of the diff to land: ${ diffId } ` ) ;
return ;
}
return ;
// await landChanges(diffId);
// await deployThemes([theme]);
// await buildComZips([theme]);
}
2021-10-29 07:38:36 +00:00
/ *
Build . zip file for . com
* /
async function buildComZip ( themeSlug ) {
console . log ( ` Building ${ themeSlug } .zip ` ) ;
let styleCss = fs . readFileSync ( ` ${ themeSlug } /style.css ` , 'utf8' ) ;
// Gets the theme version (Version:) and minimum WP version (Tested up to:) from the theme's style.css
2021-11-12 13:50:34 +00:00
let themeVersion = getThemeMetadata ( styleCss , 'Version' ) ;
2021-12-20 15:18:46 +00:00
let wpVersionCompat = getThemeMetadata ( styleCss , 'Requires at least' ) ;
2021-10-29 07:38:36 +00:00
if ( themeVersion && wpVersionCompat ) {
2022-02-01 14:26:32 +00:00
await executeOnSandbox ( ` php ${ sandboxRootFolder } bin/themes/theme-downloads/build-theme-zip.php --stylesheet=pub/ ${ themeSlug } --themeversion= ${ themeVersion } --wpversioncompat= ${ wpVersionCompat } ; ` , true ) ;
2021-12-14 16:45:07 +00:00
}
2021-11-12 13:50:34 +00:00
else {
2021-10-29 07:38:36 +00:00
console . log ( 'Unable to build theme .zip.' ) ;
if ( ! themeVersion ) {
console . log ( 'Could not find theme version (Version:) in the theme style.css.' ) ;
}
if ( ! wpVersionCompat ) {
console . log ( 'Could not find WP compat version (Tested up to:) in the theme style.css.' ) ;
}
console . log ( 'Please build the .zip file for the theme manually.' , themeSlug ) ;
open ( 'https://mc.a8c.com/themes/downloads/' ) ;
}
}
2021-10-26 15:22:14 +00:00
async function buildComZips ( themes ) {
2021-10-29 07:38:36 +00:00
for ( let theme of themes ) {
2022-02-01 14:26:32 +00:00
try {
await buildComZip ( theme ) ;
} catch ( err ) {
console . log ( ` There was an error building dotcom zip for ${ theme } . ${ err } ` ) ;
}
2021-10-29 07:38:36 +00:00
}
2021-10-26 15:22:14 +00:00
}
2021-09-29 15:01:05 +00:00
/ *
2021-10-01 14:42:39 +00:00
Check to ensure that :
* The current branch is / trunk
* That trunk is up - to - date with origin / trunk
2021-09-29 15:01:05 +00:00
* /
2021-10-01 14:42:39 +00:00
async function checkForDeployability ( ) {
let branchName = await executeCommand ( 'git symbolic-ref --short HEAD' ) ;
if ( branchName !== 'trunk' ) {
return 'Only the /trunk branch can be deployed.' ;
}
2021-09-29 15:01:05 +00:00
2021-10-01 14:42:39 +00:00
await executeCommand ( 'git remote update' , true ) ;
let localMasterHash = await executeCommand ( 'git rev-parse trunk' )
let remoteMasterHash = await executeCommand ( 'git rev-parse origin/trunk' )
if ( localMasterHash !== remoteMasterHash ) {
return 'Local /trunk is out-of-date. Pull changes to continue.'
}
return null ;
}
2021-09-29 15:01:05 +00:00
2021-10-01 14:42:39 +00:00
/ *
Land the changes from the given diff ID . This is the "production merge" .
* /
2022-02-02 09:53:54 +00:00
async function landChanges ( diffId ) {
2022-02-01 14:47:28 +00:00
return executeCommand ( ` ssh -tt -A ${ remoteSSH } "cd ${ sandboxPublicThemesFolder } ; /usr/local/bin/arc patch ${ diffId } ; /usr/local/bin/arc land; exit;" ` , true ) ;
2021-10-01 14:42:39 +00:00
}
2021-09-29 15:01:05 +00:00
2021-10-01 14:42:39 +00:00
async function getChangedThemes ( hash ) {
2021-10-11 20:00:20 +00:00
console . log ( 'Determining all changed themes' ) ;
2021-10-01 14:42:39 +00:00
let themes = await getActionableThemes ( ) ;
let changedThemes = [ ] ;
for ( let theme of themes ) {
let hasChanges = await checkThemeForChanges ( theme , hash ) ;
if ( hasChanges ) {
2021-11-12 13:50:34 +00:00
changedThemes . push ( theme ) ;
2021-10-01 14:42:39 +00:00
}
}
return changedThemes ;
}
2021-09-29 15:01:05 +00:00
2021-10-11 20:00:20 +00:00
/ *
2021-10-26 15:22:14 +00:00
Deploy a collection of themes .
Part of the push - button - deploy process .
Can also be triggered to deploy a single theme with the command :
node . / theme - utils . mjs deploy - theme THEMENAME
2021-10-11 20:00:20 +00:00
* /
2021-10-26 15:22:14 +00:00
async function deployThemes ( themes ) {
2021-10-01 14:42:39 +00:00
let response ;
2021-10-11 20:00:20 +00:00
2021-10-26 15:22:14 +00:00
for ( let theme of themes ) {
console . log ( ` Deploying ${ theme } ` ) ;
let deploySuccess = false ;
let attempt = 0 ;
2022-02-01 14:26:32 +00:00
while ( ! deploySuccess && attempt <= 2 ) {
2021-10-26 15:22:14 +00:00
attempt ++ ;
console . log ( ` \n attempt # ${ attempt } \n \n ` ) ;
response = await executeOnSandbox ( ` deploy pub ${ theme } ;exit; ` , true , true ) ;
deploySuccess = response . includes ( 'successfully deployed to' ) ;
if ( ! deploySuccess ) {
console . log ( 'Deploy was not successful. Trying again in 10 seconds...' ) ;
await new Promise ( resolve => setTimeout ( resolve , 10000 ) ) ;
2021-10-29 07:38:36 +00:00
}
2021-10-26 15:22:14 +00:00
else {
console . log ( "Deploy successful." ) ;
}
}
2022-02-01 14:26:32 +00:00
if ( ! deploySuccess ) {
await inquirer . prompt ( [ {
type : 'confirm' ,
message : ` ${ theme } was not sucessfully deployed and should be deployed manually. ` ,
name : "continue" ,
default : false
} ] ) ;
}
2021-10-01 14:42:39 +00:00
}
2021-09-29 15:01:05 +00:00
}
/ *
Provide the hash of the last managed deployment .
This hash is used to determine all the changes that have happened between that point and the current point .
* /
async function getLastDeployedHash ( ) {
let result = await executeOnSandbox ( `
cat $ { sandboxPublicThemesFolder } / . pub - git - hash
` );
return result ;
}
/ *
Update the 'last deployed hash' on the server with the current hash .
* /
async function updateLastDeployedHash ( ) {
let hash = await executeCommand ( ` git rev-parse HEAD ` ) ;
await executeOnSandbox ( `
echo '${hash}' > $ { sandboxPublicThemesFolder } / . pub - git - hash
` );
}
/ *
Version bump ( increment version patch ) any theme project that has had changes since the last deployment .
2021-10-01 14:42:39 +00:00
If a theme ' s version has already been changed since that last deployment then do not version bump it .
2021-09-29 15:01:05 +00:00
If any theme projects have had a version bump also version bump the parent project .
2022-04-20 18:28:17 +00:00
If a theme has changes also update its changelog .
2021-10-01 14:42:39 +00:00
Commit the change .
2021-09-29 15:01:05 +00:00
* /
2021-10-01 14:42:39 +00:00
async function versionBumpThemes ( ) {
2021-09-29 15:01:05 +00:00
console . log ( "Version Bumping" ) ;
let themes = await getActionableThemes ( ) ;
let hash = await getLastDeployedHash ( ) ;
2021-12-15 18:04:22 +00:00
let changesWereMade = false ;
2021-09-29 15:01:05 +00:00
let versionBumpCount = 0 ;
for ( let theme of themes ) {
let hasChanges = await checkThemeForChanges ( theme , hash ) ;
if ( ! hasChanges ) {
// console.log(`${theme} has no changes`);
continue ;
}
versionBumpCount ++ ;
let hasVersionBump = await checkThemeForVersionBump ( theme , hash ) ;
if ( hasVersionBump ) {
continue ;
}
2021-10-14 14:41:04 +00:00
2021-12-15 18:04:22 +00:00
await versionBumpTheme ( theme , true ) ;
2022-04-20 18:28:17 +00:00
await updateThemeChangelog ( theme , true ) ;
2021-12-15 18:04:22 +00:00
changesWereMade = true ;
2021-09-29 15:01:05 +00:00
}
//version bump the root project if there were changes to any of the themes
2021-12-15 18:04:22 +00:00
let rootHasVersionBump = await checkProjectForVersionBump ( hash ) ;
2021-09-29 15:01:05 +00:00
if ( versionBumpCount > 0 && ! rootHasVersionBump ) {
2021-12-15 18:04:22 +00:00
await executeCommand ( ` npm version patch --no-git-tag-version && git add package.json package-lock.json ` ) ;
changesWereMade = true ;
2021-09-29 15:01:05 +00:00
}
2021-12-15 18:04:22 +00:00
return changesWereMade ;
2021-09-29 15:01:05 +00:00
}
2022-01-31 16:37:12 +00:00
export function getThemeMetadata ( styleCss , attribute ) {
2021-11-12 13:50:34 +00:00
if ( ! styleCss || ! attribute ) {
return null ;
}
switch ( attribute ) {
case 'Version' :
return styleCss
. match ( / ( ? < = V e r s i o n : \ s * ) . * ? ( ? = \ s * \ r ? \ n | \ r g ) / g s ) [ 0 ]
. trim ( )
. replace ( '-wpcom' , '' ) ;
2021-12-20 15:18:46 +00:00
case 'Requires at least' :
2021-11-12 13:50:34 +00:00
return styleCss
2021-12-20 15:18:46 +00:00
. match ( / ( ? < = R e q u i r e s a t l e a s t : \ s * ) . * ? ( ? = \ s * \ r ? \ n | \ r g ) / g s ) ;
2021-11-12 13:50:34 +00:00
}
}
2022-04-20 18:28:17 +00:00
/ *
Update theme changelog using current commit logs .
Used by versionBumpThemes to update each theme changelog .
* /
async function updateThemeChangelog ( theme , addChanges ) {
console . log ( ` Updating ${ theme } changelog ` ) ;
// Get theme version
let styleCss = fs . readFileSync ( ` ${ theme } /style.css ` , 'utf8' ) ;
let version = getThemeMetadata ( styleCss , 'Version' ) ;
// Get list of updates with bullet points
let logs = await getCommitLogs ( '' , true ) ;
// Get theme readme.txt
let readmeFile = ` ${ theme } /readme.txt ` ;
// Build changelog entry
let newChangelogEntry = ` == Changelog ==
= $ { version } =
$ { logs } ` ;
if ( ! readmeFile ) {
console . log ( ` Unable to find a readme.txt for ${ theme } . Aborted Automated Deploy Process at changelog step. ` ) ;
return ;
}
// Update readme.txt
fs . readFile ( readmeFile , 'utf8' , function ( err , data ) {
let changelogSection = '== Changelog ==' ;
let regex = new RegExp ( '^.*' + changelogSection + '.*$' , 'gm' ) ;
let formattedChangelog = data . replace ( regex , newChangelogEntry ) ;
fs . writeFile ( readmeFile , formattedChangelog , 'utf8' , function ( err ) {
if ( err ) return console . log ( err ) ;
} ) ;
} ) ;
// Stage readme.txt
if ( addChanges ) {
await executeCommand ( ` git add ${ readmeFile } ` ) ;
}
}
2021-11-12 13:50:34 +00:00
2021-09-29 15:01:05 +00:00
/ *
Version Bump a Theme .
Used by versionBumpThemes to do the work of version bumping .
2021-11-12 13:50:34 +00:00
First increment the patch version in style . css
Then update any of these files with the new version : [ package . json , style . scss , style - child - theme . scss ]
2021-09-29 15:01:05 +00:00
* /
2021-12-15 18:04:22 +00:00
async function versionBumpTheme ( theme , addChanges ) {
2021-11-12 13:50:34 +00:00
2021-09-29 15:01:05 +00:00
console . log ( ` ${ theme } needs a version bump ` ) ;
2021-11-12 13:50:34 +00:00
await executeCommand ( ` perl -pi -e 's/Version: (( \\ d+ \\ .)*)( \\ d+)(.*) $ /"Version: ". $ 1.( $ 3+1). $ 4/ge' ${ theme } /style.css ` , true ) ;
2021-12-16 19:06:38 +00:00
await executeCommand ( ` git add ${ theme } /style.css ` ) ;
2021-11-12 13:50:34 +00:00
let styleCss = fs . readFileSync ( ` ${ theme } /style.css ` , 'utf8' ) ;
let currentVersion = getThemeMetadata ( styleCss , 'Version' ) ;
let filesToUpdate = await executeCommand ( ` find ${ theme } -name package.json -o -name style.scss -o -name style-child-theme.scss -maxdepth 2 ` ) ;
filesToUpdate = filesToUpdate . split ( '\n' ) . filter ( item => item != '' ) ;
2021-09-29 15:01:05 +00:00
for ( let file of filesToUpdate ) {
await executeCommand ( ` perl -pi -e 's/Version: (.*) $ /"Version: ' ${ currentVersion } '"/ge' ${ file } ` ) ;
2021-11-12 13:50:34 +00:00
await executeCommand ( ` perl -pi -e 's/ \\ "version \\ ": (.*) $ /" \\ "version \\ ": \\ "' ${ currentVersion } ' \\ ","/ge' ${ file } ` ) ;
2021-12-15 18:04:22 +00:00
if ( addChanges ) {
await executeCommand ( ` git add ${ file } ` ) ;
}
2021-09-29 15:01:05 +00:00
}
}
/ *
Determine if a theme has had a version bump since a given hash .
Used by versionBumpThemes
2021-11-12 13:50:34 +00:00
Compares the value of 'version' in style . css between the hash and current value
2021-09-29 15:01:05 +00:00
* /
async function checkThemeForVersionBump ( theme , hash ) {
2021-11-12 13:50:34 +00:00
return executeCommand ( `
git show $ { hash } : $ { theme } / style . css 2 > / d e v / n u l l
` )
. catch ( ( error ) => {
//This is a new theme, no need to bump versions so we'll just say we've already done it
return true ;
} )
. then ( ( previousStyleString ) => {
if ( previousStyleString === true ) {
return previousStyleString ;
}
let previousVersion = getThemeMetadata ( previousStyleString , 'Version' ) ;
let styleCss = fs . readFileSync ( ` ${ theme } /style.css ` , 'utf8' ) ;
let currentVersion = getThemeMetadata ( styleCss , 'Version' ) ;
return previousVersion != currentVersion ;
2021-10-14 12:18:24 +00:00
} ) ;
2021-09-29 15:01:05 +00:00
}
2021-12-15 18:04:22 +00:00
/ *
Determine if the project has had a version bump since a given hash .
Used by versionBumpThemes
Compares the value of 'version' in package . json between the hash and current value
* /
async function checkProjectForVersionBump ( hash ) {
let previousPackageString = await executeCommand ( `
git show $ { hash } : . / package . json 2 > / d e v / n u l l
` );
let previousPackage = JSON . parse ( previousPackageString ) ;
let currentPackage = JSON . parse ( fs . readFileSync ( ` ./package.json ` ) )
return previousPackage . version != currentPackage . version ;
}
2021-09-29 15:01:05 +00:00
/ *
Determine if a theme has had changes since a given hash .
Used by versionBumpThemes
2021-10-01 14:42:39 +00:00
* /
2021-09-29 15:01:05 +00:00
async function checkThemeForChanges ( theme , hash ) {
let comittedChanges = await executeCommand ( ` git diff --name-only ${ hash } HEAD -- ${ theme } ` ) ;
2022-02-01 16:36:16 +00:00
return comittedChanges != '' ;
2021-09-29 15:01:05 +00:00
}
/ *
2021-11-12 13:50:34 +00:00
Provide a list of 'actionable' themes ( those themes that have style . css files )
2021-09-29 15:01:05 +00:00
* /
async function getActionableThemes ( ) {
2021-12-14 16:45:07 +00:00
let result = await executeCommand ( ` for d in */; do
2021-11-12 13:50:34 +00:00
if test - f "./$d/style.css" ; then
2021-12-14 16:45:07 +00:00
echo $d ;
2021-11-12 13:50:34 +00:00
fi
done ` );
return result
. split ( '\n' )
. map ( item => item . replace ( '/' , '' ) ) ;
2021-09-29 15:01:05 +00:00
}
/ *
Clean the theme sandbox .
2022-02-01 14:26:32 +00:00
checkout origin / trunk and ensure it ' s up - to - date .
2021-09-29 15:01:05 +00:00
Remove any other changes .
* /
2022-02-02 09:53:54 +00:00
async function cleanSandbox ( ) {
2021-09-29 15:01:05 +00:00
console . log ( 'Cleaning the Themes Sandbox' ) ;
2021-10-01 14:42:39 +00:00
await executeOnSandbox ( `
2021-09-29 15:01:05 +00:00
cd $ { sandboxPublicThemesFolder } ;
git reset -- hard HEAD ;
git clean - fd ;
2022-02-01 14:26:32 +00:00
git checkout trunk ;
2021-09-29 15:01:05 +00:00
git pull ;
echo ;
git status
2021-10-01 14:42:39 +00:00
` , true);
2021-09-29 15:01:05 +00:00
console . log ( 'All done cleaning.' ) ;
}
2022-01-26 16:59:51 +00:00
/ *
Clean the premium theme sandbox .
2022-02-01 14:26:32 +00:00
checkout origin / trunk and ensure it ' s up - to - date .
2022-01-26 16:59:51 +00:00
Remove any other changes .
* /
2022-02-02 09:53:54 +00:00
async function cleanPremiumSandbox ( ) {
2022-01-26 16:59:51 +00:00
console . log ( 'Cleaning the Themes Sandbox' ) ;
await executeOnSandbox ( `
cd $ { sandboxPremiumThemesFolder } ;
git reset -- hard HEAD ;
git clean - fd ;
2022-02-01 14:26:32 +00:00
git checkout trunk ;
2022-01-26 16:59:51 +00:00
git pull ;
echo ;
git status
` , true);
console . log ( 'All done cleaning.' ) ;
}
2021-09-29 15:01:05 +00:00
/ *
Clean the entire sandbox .
2022-02-01 14:26:32 +00:00
checkout origin / trunk and ensure it ' s up - to - date .
2021-09-29 15:01:05 +00:00
Remove any other changes .
* /
2022-02-02 09:53:54 +00:00
async function cleanAllSandbox ( ) {
2021-09-29 15:01:05 +00:00
console . log ( 'Cleaning the Entire Sandbox' ) ;
let response = await executeOnSandbox ( `
cd $ { sandboxRootFolder } ;
git reset -- hard HEAD ;
git clean - fd ;
2022-02-01 14:26:32 +00:00
git checkout trunk ;
2021-09-29 15:01:05 +00:00
git pull ;
echo ;
git status
2021-10-01 14:42:39 +00:00
` , true);
2021-09-29 15:01:05 +00:00
console . log ( 'All done cleaning.' ) ;
}
/ *
2022-02-02 09:53:54 +00:00
Push exactly what is here ( all files ) up to the sandbox ( with the exclusion of files noted in . sandbox - ignore )
2021-09-29 15:01:05 +00:00
* /
2022-02-02 09:53:54 +00:00
async function pushToSandbox ( ) {
console . log ( "Pushing All Themes to Sandbox." ) ;
let allThemes = await getActionableThemes ( ) ;
allThemes = allThemes . filter ( item => ! premiumThemes . includes ( item ) ) ;
console . log ( ` Syncing ${ allThemes . length } themes ` ) ;
for ( let theme of allThemes ) {
await pushThemeToSandbox ( theme ) ;
}
2021-09-29 15:01:05 +00:00
}
2022-02-02 09:53:54 +00:00
async function pushThemeToSandbox ( theme ) {
console . log ( ` Syncing ${ theme } ` ) ;
return executeCommand ( `
rsync - avR -- no - p -- no - times -- delete - m -- exclude - from = '.sandbox-ignore' . / $ { theme } / wpcom - sandbox : $ { sandboxPublicThemesFolder } /
2021-10-01 14:42:39 +00:00
` , true);
2021-09-29 15:01:05 +00:00
}
2021-12-16 10:28:46 +00:00
/ *
Push exactly what is here ( all files ) up to the sandbox ( with the exclusion of files noted in . sandbox - ignore )
This pushes only the folders noted as "premiumThemes" into the premium themes directory .
This is the only part of the deploy process that is automated ; the rest must be done manually including :
* Creating a Phabricator Diff
* Landing ( comitting ) the change
* Deploying the theme
* Triggering the . zip builds
* /
2022-01-26 16:59:51 +00:00
async function pushPremiumToSandbox ( ) {
//TODO: It would be nice to determine this list programatically
const filesToModify = [
'style.css' ,
'block-templates/404.html' ,
'block-template-parts/header.html' ,
'block-template-parts/footer.html'
] ;
// Change 'blockbase' to 'blockbase-premium' in the files noted
for ( let theme of premiumThemes ) {
for ( let file of filesToModify ) {
await executeCommand ( ` perl -pi -e 's/blockbase/blockbase-premium/' ${ theme } / ${ file } ` , true ) ;
}
}
// Push the changes in the premium themes to the sandbox
await executeCommand ( `
rsync - avR -- no - p -- no - times -- delete - m -- exclude - from = '.sandbox-ignore' -- exclude = 'sass' . / $ { premiumThemes . join ( ' ./' ) } wpcom - sandbox : $ { sandboxPremiumThemesFolder } /
2021-12-16 10:28:46 +00:00
` , true);
2022-01-26 16:59:51 +00:00
// revert the local blockbase-premium changes
for ( let theme of premiumThemes ) {
for ( let file of filesToModify ) {
await executeCommand ( `
git restore -- source = HEAD -- staged -- worktree . / $ { theme } / $ { file }
` );
}
}
2021-12-16 10:28:46 +00:00
}
2021-09-29 15:01:05 +00:00
/ *
Push only ( and every ) change since the point - of - diversion from / trunk
Remove files from the sandbox that have been removed since the last deployed hash
* /
async function pushChangesToSandbox ( ) {
2022-02-02 09:53:54 +00:00
console . log ( "Pushing Changed Themes to Sandbox." ) ;
2021-09-29 15:01:05 +00:00
let hash = await getLastDeployedHash ( ) ;
2022-01-14 17:26:13 +00:00
let changedThemes = await getChangedThemes ( hash ) ;
2022-01-26 16:59:51 +00:00
changedThemes = changedThemes . filter ( item => ! premiumThemes . includes ( item ) ) ;
2022-01-14 17:26:13 +00:00
console . log ( ` Syncing ${ changedThemes . length } themes ` ) ;
2021-09-29 15:01:05 +00:00
2022-01-14 17:26:13 +00:00
for ( let theme of changedThemes ) {
2022-02-02 21:37:17 +00:00
await pushThemeToSandbox ( theme ) ;
2022-01-13 20:38:55 +00:00
}
2021-09-29 15:01:05 +00:00
}
2022-02-21 20:52:07 +00:00
async function pullCoreThemes ( ) {
console . log ( "Pulling CORE themes from sandbox." ) ;
for ( let theme of coreThemes ) {
await executeCommand ( `
rsync - avr -- no - p -- no - times -- delete - m -- exclude - from = '.sandbox-ignore' wpcom - sandbox : $ { sandboxPublicThemesFolder } / $ { theme } / . / $ { theme } /
` , true);
}
}
async function pushCoreThemes ( ) {
console . log ( "Pushing CORE themes to sandbox." ) ;
for ( let theme of coreThemes ) {
await executeCommand ( `
rsync - avr -- no - p -- no - times -- delete - m -- exclude - from = '.sandbox-ignore' . / $ { theme } / wpcom - sandbox : $ { sandboxPublicThemesFolder } / $ { theme } /
` , true);
}
}
async function syncCoreTheme ( theme , sinceRevision ) {
if ( ! theme ) {
console . log ( 'Must supply theme to sync and revision to start from' ) ;
return ;
}
if ( ! sinceRevision ) {
sinceRevision = await executeOnSandbox ( ` cat ${ sandboxPublicThemesFolder } / ${ theme } /.pub-svn-revision ` ) ;
}
let latestRevision = await executeCommand ( ` svn info -r HEAD https://core.svn.wordpress.org/trunk | grep Revision | egrep -o "[0-9]+" ` ) ;
console . log ( ` syncing core theme ${ theme } from ${ sinceRevision } to ${ latestRevision } on your sandbox ` ) ;
try {
await executeOnSandbox ( `
cd $ { sandboxPublicThemesFolder } ;
/ u s r / b i n / s v n d i f f - - g i t - r $ { s i n c e R e v i s i o n } : H E A D h t t p s : / / c o r e . s v n . w o r d p r e s s . o r g / t r u n k / w p - c o n t e n t / t h e m e s / $ { t h e m e } | g i t a p p l y - - r e j e c t - - i g n o r e - s p a c e - c h a n g e - - i g n o r e - w h i t e s p a c e - p 4 - - d i r e c t o r y = $ { t h e m e } -
` , true);
}
catch ( err ) {
console . log ( 'Error merging:' , err ) ;
}
await executeOnSandbox ( `
echo '${latestRevision}' > $ { sandboxPublicThemesFolder } / $ { theme } / . pub - svn - revision
` );
return latestRevision ;
}
2021-09-29 15:01:05 +00:00
/ *
Build the Phabricator commit message .
This message contains the logs from all of the commits since the given hash .
Used by create * PhabricatorDiff
* /
async function buildPhabricatorCommitMessageSince ( hash ) {
let projectVersion = await executeCommand ( ` node -p "require('./package.json').version" ` ) ;
2022-04-20 18:28:17 +00:00
let logs = await getCommitLogs ( hash ) ;
2021-09-29 15:01:05 +00:00
return ` Deploy Themes ${ projectVersion } to wpcom
2021-10-01 14:42:39 +00:00
2021-09-29 15:01:05 +00:00
Summary :
$ { logs }
Test Plan : Execute Smoke Test
2021-10-01 14:42:39 +00:00
2021-09-29 15:01:05 +00:00
Reviewers :
2021-10-01 14:42:39 +00:00
2021-09-29 15:01:05 +00:00
Subscribers :
` ;
}
/ *
2022-02-21 20:52:07 +00:00
Create a Phabricator diff with the given message based on the contents currently in the sandbox .
2021-09-29 15:01:05 +00:00
Open the phabricator diff in your browser .
Provide the URL of the phabricator diff .
* /
2022-02-21 20:52:07 +00:00
async function createPhabricatorDiff ( commitMessage ) {
2021-09-29 15:01:05 +00:00
console . log ( 'creating Phabricator Diff' ) ;
let result = await executeOnSandbox ( `
cd $ { sandboxPublicThemesFolder } ;
git branch - D deploy
git checkout - b deploy
git add -- all
2021-11-12 16:23:31 +00:00
git commit - m "${commitMessage}"
2021-09-29 15:01:05 +00:00
arc diff -- create -- verbatim
2021-10-01 14:42:39 +00:00
` , true);
2021-09-29 15:01:05 +00:00
let phabricatorUrl = getPhabricatorUrlFromResponse ( result ) ;
console . log ( 'Diff Created at: ' , phabricatorUrl ) ;
if ( phabricatorUrl ) {
open ( phabricatorUrl ) ;
}
return phabricatorUrl ;
}
/ *
Utility to pull the Phabricator URL from the diff creation command .
2022-02-02 09:53:54 +00:00
Used by createPhabricatorDiff
2021-09-29 15:01:05 +00:00
* /
function getPhabricatorUrlFromResponse ( response ) {
2021-10-01 14:42:39 +00:00
return response
2021-09-29 15:01:05 +00:00
? . split ( '\n' )
? . find ( item => {
return item . includes ( 'Revision URI: ' ) ;
} )
? . split ( "Revision URI: " ) [ 1 ] ;
}
/ *
Create a git tag at the current hash .
In the description include the commit logs since the given hash .
Include the ( cleansed ) Phabricator link .
* /
async function tagDeployment ( options = { } ) {
2021-12-15 18:04:22 +00:00
console . log ( 'tagging deployment' ) ;
2021-09-29 15:01:05 +00:00
let hash = options . hash || await getLastDeployedHash ( ) ;
let workInTheOpenPhabricatorUrl = '' ;
2021-10-01 14:42:39 +00:00
if ( options . diffId ) {
2021-10-06 12:48:12 +00:00
workInTheOpenPhabricatorUrl = ` Phabricator: ${ options . diffId } -code ` ;
2021-09-29 15:01:05 +00:00
}
let projectVersion = await executeCommand ( ` node -p "require('./package.json').version" ` ) ;
2022-04-20 18:28:17 +00:00
let logs = await getCommitLogs ( hash ) ;
2021-09-29 15:01:05 +00:00
let tag = ` v ${ projectVersion } ` ;
let message = ` Deploy Themes ${ tag } to wpcom. \n \n ${ logs } \n \n ${ workInTheOpenPhabricatorUrl } ` ;
await executeCommand ( `
git tag - a $ { tag } - m "${message}"
git push origin $ { tag }
2021-12-15 18:04:22 +00:00
` , true);
2021-09-29 15:01:05 +00:00
}
/ *
Execute a command on the sandbox .
Expects the following to be configured in your ~ / . s s h / c o n f i g f i l e :
Host wpcom - sandbox
User wpdev
HostName SANDBOXURL . wordpress . com
ForwardAgent yes
* /
2021-10-26 15:22:14 +00:00
function executeOnSandbox ( command , logResponse , enablePsudoterminal ) {
if ( enablePsudoterminal ) {
return executeCommand ( ` ssh -tt -A ${ remoteSSH } << EOF
$ { command }
EOF ` , logResponse);
}
2021-10-01 14:42:39 +00:00
return executeCommand ( ` ssh -TA ${ remoteSSH } << EOF
$ { command }
EOF ` , logResponse);
2021-09-29 15:01:05 +00:00
}
/ *
Execute a command locally .
* /
2022-01-31 16:37:12 +00:00
export async function executeCommand ( command , logResponse ) {
2021-09-29 15:01:05 +00:00
return new Promise ( ( resolove , reject ) => {
let child ;
let response = '' ;
let errResponse = '' ;
if ( isWin ) {
child = spawn ( 'cmd.exe' , [ '/s' , '/c' , '"' + command + '"' ] , {
windowsVerbatimArguments : true ,
stdio : [ process . stdin , 'pipe' , 'pipe' ] ,
} )
} else {
2022-02-01 14:26:32 +00:00
child = spawn ( process . env . SHELL , [ '-c' , command ] , {
stdio : [ process . stdin , 'pipe' , 'pipe' ] ,
} ) ;
2021-09-29 15:01:05 +00:00
}
child . stdout . on ( 'data' , ( data ) => {
response += data ;
2021-10-01 14:42:39 +00:00
if ( logResponse ) {
console . log ( data . toString ( ) ) ;
}
2021-09-29 15:01:05 +00:00
} ) ;
child . stderr . on ( 'data' , ( data ) => {
errResponse += data ;
2021-10-01 14:42:39 +00:00
if ( logResponse ) {
console . log ( data . toString ( ) ) ;
}
2021-09-29 15:01:05 +00:00
} ) ;
child . on ( 'exit' , ( code ) => {
if ( code !== 0 ) {
reject ( errResponse . trim ( ) ) ;
}
resolove ( response . trim ( ) ) ;
} ) ;
} ) ;
}