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' ;
const sandboxRootFolder = '/home/wpdev/public_html/' ;
const isWin = process . platform === 'win32' ;
( async function start ( ) {
let args = process . argv . slice ( 2 ) ;
let command = args ? . [ 0 ] ;
switch ( command ) {
2021-10-01 14:42:39 +00:00
case "push-button-deploy-git" : return pushButtonDeploy ( 'git' ) ;
case "push-button-deploy-svn" : return pushButtonDeploy ( 'svn' ) ;
2021-09-29 15:01:05 +00:00
case "clean-sandbox-git" : return cleanSandboxGit ( ) ;
case "clean-sandbox-svn" : return cleanSandboxSvn ( ) ;
case "clean-all-sandbox-git" : return cleanAllSandboxGit ( ) ;
case "clean-all-sandbox-svn" : return cleanAllSandboxSvn ( ) ;
case "push-to-sandbox" : return pushToSandbox ( ) ;
case "push-changes-to-sandbox" : return pushChangesToSandbox ( ) ;
case "version-bump-themes" : return versionBumpThemes ( ) ;
2021-10-01 14:42:39 +00:00
case "land-diff-git" : return landChangesGit ( args ? . [ 1 ] ) ;
case "land-diff-svn" : return landChangesSvn ( args ? . [ 1 ] ) ;
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 ] ] ) ;
2021-09-29 15:01:05 +00:00
}
return showHelp ( ) ;
} ) ( ) ;
function showHelp ( ) {
// TODO: make this helpful
console . log ( 'Help info can go here' ) ;
}
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).' ) ;
console . log ( 'npm run sandbox:clean:git OR npm run sandbox:clean:svn' )
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
2021-10-01 14:42:39 +00:00
let logs = await executeCommand ( ` git log --reverse --pretty=format:%s ${ hash } ..HEAD ` ) ;
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 .
Leverages git on the sandbox .
* 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
* /
2021-10-01 14:42:39 +00:00
async function pushButtonDeploy ( repoType ) {
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-10-01 14:42:39 +00:00
if ( repoType != 'svn' && repoType != 'git' ) {
return console . log ( 'Specify a repo type to use push-button deploy' ) ;
}
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 {
2021-10-11 20:00:20 +00:00
if ( repoType === 'git' ) {
await cleanSandboxGit ( ) ;
}
else {
await cleanSandboxSvn ( ) ;
2021-10-01 14:42:39 +00:00
}
2021-09-29 15:01:05 +00:00
2021-10-01 14:42:39 +00:00
let hash = await getLastDeployedHash ( ) ;
let diffUrl ;
2021-10-14 14:41:04 +00:00
2021-10-01 14:42:39 +00:00
await versionBumpThemes ( ) ;
2021-10-14 14:41:04 +00:00
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
//TODO: Can these be automagically uploaded?
//await buildChangedOrgZips();
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
await updateLastDeployedHash ( ) ;
if ( repoType === 'git' ) {
diffUrl = await createGitPhabricatorDiff ( hash ) ;
}
else {
diffUrl = await createSvnPhabricatorDiff ( hash ) ;
}
let diffId = diffUrl . split ( 'a8c.com/' ) [ 1 ] ;
2021-10-14 14:41:04 +00:00
2021-10-01 14:42:39 +00:00
//push changes (from version bump)
await executeCommand ( 'git push' ) ;
await tagDeployment ( {
hash : hash ,
diffId : diffId
} ) ;
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 } \n \n \n ` ) ;
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 ;
}
if ( repoType === 'git' ) {
2021-10-11 20:00:20 +00:00
await landChangesGit ( diffId ) ;
2021-10-01 14:42:39 +00:00
}
else {
2021-10-11 20:00:20 +00:00
await landChangesSvn ( diffId ) ;
2021-10-01 14:42:39 +00:00
}
2021-10-26 15:22:14 +00:00
await deployThemes ( changedThemes ) ;
await buildComZips ( changedThemes ) ;
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 ) {
console . log ( "ERROR with deply script: " , err ) ;
}
2021-09-29 15:01:05 +00:00
}
2021-10-29 07:38:36 +00:00
/ *
Build . zip file for . com
* /
async function buildComZip ( themeSlug ) {
let response ;
console . log ( ` Building ${ themeSlug } .zip ` ) ;
let themeVersion ;
let wpVersionCompat ;
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
if ( styleCss ) {
const themeVersionFromCss = styleCss . match ( / ( ? < = V e r s i o n : \ s * ) . * ? ( ? = \ s * \ r ? \ n | \ r g ) / g s ) ;
themeVersion = themeVersionFromCss [ 0 ] . trim ( ) . replace ( '-wpcom' , '' ) ;
wpVersionCompat = styleCss . match ( / ( ? < = T e s t e d u p t o : \ s * ) . * ? ( ? = \ s * \ r ? \ n | \ r g ) / g s ) ;
}
if ( themeVersion && wpVersionCompat ) {
await executeOnSandbox ( ` php ${ sandboxRootFolder } bin/themes/theme-downloads/build-theme-zip.php --stylesheet=pub/ ${ themeSlug } --themeversion= ${ themeVersion } --wpversioncompat= ${ wpVersionCompat } ` , true ) ;
} else {
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 ) {
await buildComZip ( theme ) ;
}
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" .
This is the git version of that action .
* /
async function landChangesGit ( diffId ) {
return await executeOnSandbox ( `
cd $ { sandboxPublicThemesFolder } ;
arc patch $ { diffId }
arc land
` , true);
}
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" .
This is the svn version of that action .
* /
async function landChangesSvn ( diffId ) {
return await executeOnSandbox ( `
cd $ { sandboxPublicThemesFolder } ;
2021-10-14 12:18:24 +00:00
svn ci - m $ { diffId }
2021-10-29 17:47:30 +00:00
` , true, 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 ) {
changedThemes . push ( theme . replace ( './' , '' ) ) ;
}
}
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 ;
while ( ! deploySuccess ) {
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." ) ;
}
}
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 .
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 ( ) ;
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 ) {
2021-10-14 12:18:24 +00:00
console . log ( ` ${ theme } has already been version bumped ` ) ;
2021-09-29 15:01:05 +00:00
continue ;
}
2021-10-14 14:41:04 +00:00
2021-09-29 15:01:05 +00:00
await versionBumpTheme ( theme ) ;
}
//version bump the root project if there were changes to any of the themes
let rootHasVersionBump = await checkThemeForVersionBump ( '.' , hash ) ;
if ( versionBumpCount > 0 && ! rootHasVersionBump ) {
await executeCommand ( ` npm version patch --no-git-tag-version ` ) ;
}
2021-10-01 14:42:39 +00:00
if ( versionBumpCount > 0 && ! rootHasVersionBump ) {
2021-09-29 15:01:05 +00:00
console . log ( 'commiting version-bump' ) ;
2021-10-01 14:42:39 +00:00
await executeCommand ( `
2021-09-29 15:01:05 +00:00
git commit - a - m "Version Bump" ;
2021-10-01 14:42:39 +00:00
` , true);
2021-09-29 15:01:05 +00:00
}
}
/ *
Version Bump a Theme .
Used by versionBumpThemes to do the work of version bumping .
First increment the patch version in package . json ( the source of truth for versioning )
Then update any of these files with the new version : [ style . css , style . scss , style - child - theme . scss ]
* /
async function versionBumpTheme ( theme ) {
console . log ( ` ${ theme } needs a version bump ` ) ;
await executeCommand ( ` npm --prefix ${ theme } version patch --no-git-tag-version ` ) ;
let currentPackage = JSON . parse ( fs . readFileSync ( ` ${ theme } /package.json ` ) )
let currentVersion = currentPackage . version ;
let filesToUpdate = await executeCommand ( ` find ${ theme } -name style.css -o -name style.scss -o -name style-child-theme.scss -maxdepth 2 ` ) ;
filesToUpdate = filesToUpdate . split ( '\n' ) ;
for ( let file of filesToUpdate ) {
console . log ( 'updating file' , file , currentVersion ) ;
//TODO: I'm sure we can use something other than perl for this but this was prior art...
await executeCommand ( ` perl -pi -e 's/Version: (.*) $ /"Version: ' ${ currentVersion } '"/ge' ${ file } ` ) ;
}
}
/ *
Determine if a theme 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 checkThemeForVersionBump ( theme , hash ) {
2021-10-14 12:18:24 +00:00
executeCommand ( `
2021-09-29 15:01:05 +00:00
git show $ { hash } : $ { theme } / package . json 2 > / d e v / n u l l
2021-10-14 12:18:24 +00:00
` ).catch( ( error ) => {
2021-10-14 14:41:04 +00:00
console . log ( 'This is a new theme, no need to bump versions' ) ;
2021-10-14 12:18:24 +00:00
return false ;
} ) . then ( ( previousPackageString ) => {
let previousPackage = JSON . parse ( previousPackageString ) ;
let currentPackage = JSON . parse ( fs . readFileSync ( ` ${ theme } /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 uncomittedChanges = await executeCommand ( ` git diff-index --name-only HEAD -- ${ theme } ` ) ;
let comittedChanges = await executeCommand ( ` git diff --name-only ${ hash } HEAD -- ${ theme } ` ) ;
return uncomittedChanges != '' || comittedChanges != '' ;
}
/ *
Provide a list of 'actionable' themes ( those themes that have package . json files )
* /
async function getActionableThemes ( ) {
//TODO: This could be done more effeciently. It's very slow running.
let result = await executeCommand ( ` find . -depth 2 -name package.json -print0 | xargs -0 -n1 dirname | sort --unique ` ) ;
return result . split ( '\n' ) ;
}
async function buildChangedOrgZips ( ) {
console . log ( "Building .org Zip files" ) ;
2021-10-01 14:42:39 +00:00
await executeCommand ( ` ./theme-batch-utils.sh build-org-zip-if-changed ` , true ) ;
2021-09-29 15:01:05 +00:00
}
/ *
Clean the theme sandbox .
Assumes sandbox is in 'git' mode
checkout origin / develop and ensure it ' s up - to - date .
Remove any other changes .
* /
async function cleanSandboxGit ( ) {
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 ;
git checkout develop ;
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.' ) ;
}
/ *
Clean the entire sandbox .
Assumes sandbox is in 'git' mode
checkout origin / develop and ensure it ' s up - to - date .
Remove any other changes .
* /
async function cleanAllSandboxGit ( ) {
console . log ( 'Cleaning the Entire Sandbox' ) ;
let response = await executeOnSandbox ( `
cd $ { sandboxRootFolder } ;
git reset -- hard HEAD ;
git clean - fd ;
git checkout develop ;
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.' ) ;
}
/ *
Clean the theme sandbox .
Assumes sandbox is in 'svn' mode
ensure trunk is up - to - date
Remove any other changes
* /
async function cleanSandboxSvn ( ) {
console . log ( 'Cleaning the theme sandbox' ) ;
2021-10-01 14:42:39 +00:00
await executeOnSandbox ( `
2021-09-29 15:01:05 +00:00
cd $ { sandboxPublicThemesFolder } ;
svn revert - R . ;
svn cleanup -- remove - unversioned ;
svn up ;
2021-10-01 14:42:39 +00:00
` , true);
2021-09-29 15:01:05 +00:00
console . log ( 'All done cleaning.' ) ;
}
/ *
Clean the entire sandbox .
Assumes sandbox is in 'svn' mode
ensure trunk is up - to - date
Remove any other changes
* /
async function cleanAllSandboxSvn ( ) {
console . log ( 'Cleaning the entire sandbox' ) ;
2021-10-01 14:42:39 +00:00
await executeOnSandbox ( `
2021-09-29 15:01:05 +00:00
cd $ { sandboxRootFolder } ;
svn revert - R . ;
svn cleanup -- remove - unversioned ;
svn up . ;
2021-10-01 14:42:39 +00:00
` , true);
2021-09-29 15:01:05 +00:00
console . log ( 'All done cleaning.' ) ;
}
/ *
Push exactly what is here ( all files ) up to the sandbox ( with the exclusion of files noted in . sandbox - ignore )
* /
function pushToSandbox ( ) {
executeCommand ( `
rsync - av -- no - p -- no - times -- exclude - from = '.sandbox-ignore' . / wpcom - sandbox : $ { sandboxPublicThemesFolder } /
` );
}
/ *
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 ( ) {
console . log ( "Pushing Changes to Sandbox." ) ;
let hash = await getLastDeployedHash ( ) ;
let deletedFiles = await getDeletedFilesSince ( hash ) ;
let changedFiles = await getComittedChangesSinceHash ( hash ) ;
//remove deleted files from changed files
changedFiles = changedFiles . filter ( item => {
return false === deletedFiles . includes ( item ) ;
} ) ;
if ( deletedFiles . length > 0 ) {
console . log ( 'deleting from sandbox: ' , deletedFiles ) ;
2021-10-01 14:42:39 +00:00
await executeOnSandbox ( `
2021-09-29 15:01:05 +00:00
cd $ { sandboxPublicThemesFolder } ;
rm - f $ { deletedFiles . join ( ' ' ) }
2021-10-01 14:42:39 +00:00
` , true);
2021-09-29 15:01:05 +00:00
}
if ( changedFiles . length > 0 ) {
console . log ( 'pushing changed files to sandbox:' , changedFiles ) ;
2021-10-01 14:42:39 +00:00
await executeCommand ( `
2021-09-29 15:01:05 +00:00
rsync - avR -- no - p -- no - times -- exclude - from = '.sandbox-ignore' $ { changedFiles . join ( ' ' ) } wpcom - sandbox : $ { sandboxPublicThemesFolder } /
2021-10-01 14:42:39 +00:00
` , true);
2021-09-29 15:01:05 +00:00
}
}
/ *
Provide a collection of all files that have changed since the given hash .
Used by pushChangesToSandbox
* /
async function getComittedChangesSinceHash ( hash ) {
let comittedChanges = await executeCommand ( ` git diff ${ hash } HEAD --name-only ` ) ;
2021-10-01 14:42:39 +00:00
comittedChanges = comittedChanges . replace ( /\r?\n|\r/g , " " ) . split ( " " ) ;
2021-09-29 15:01:05 +00:00
let uncomittedChanges = await executeCommand ( ` git diff HEAD --name-only ` ) ;
uncomittedChanges = uncomittedChanges . replace ( /\r?\n|\r/g , " " ) . split ( " " ) ;
return comittedChanges . concat ( uncomittedChanges ) ;
}
/ *
Provide a collection of all files that have been deleted since the given hash .
Used by pushChangesToSandbox
* /
async function getDeletedFilesSince ( hash ) {
let deletedSinceHash = await executeCommand ( `
git log -- format = format : "" -- name - only - M100 % -- diff - filter = D $ { hash } . . HEAD
` );
2021-10-01 14:42:39 +00:00
deletedSinceHash = deletedSinceHash . replace ( /\r?\n|\r/g , " " ) . trim ( ) . split ( " " ) ;
2021-09-29 15:01:05 +00:00
let deletedAndUncomitted = await executeCommand ( `
git diff HEAD -- name - only -- diff - filter = D
` );
2021-10-01 14:42:39 +00:00
deletedAndUncomitted = deletedAndUncomitted . replace ( /\r?\n|\r/g , " " ) . trim ( ) . split ( " " ) ;
2021-09-29 15:01:05 +00:00
return deletedSinceHash . concat ( deletedAndUncomitted ) . filter ( item => {
return item != '' ;
} ) ;
}
/ *
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" ` ) ;
let logs = await executeCommand ( ` git log --reverse --pretty=format:%s ${ hash } ..HEAD ` ) ;
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 :
` ;
}
/ *
Create a ( git ) Phabricator diff from a given hash .
Open the phabricator diff in your browser .
Provide the URL of the phabricator diff .
* /
async function createGitPhabricatorDiff ( hash ) {
console . log ( 'creating Phabricator Diff' ) ;
let commitMessage = await buildPhabricatorCommitMessageSince ( hash ) ;
let result = await executeOnSandbox ( `
cd $ { sandboxPublicThemesFolder } ;
git branch - D deploy
git checkout - b deploy
git add -- all
git commit - m "${commitMessage}"
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 ;
}
/ *
Create a ( svn ) Phabricator diff from a given hash .
Open the phabricator diff in your browser .
Provide the URL of the phabricator diff .
* /
async function createSvnPhabricatorDiff ( hash ) {
console . log ( 'creating Phabricator Diff' ) ;
2021-10-01 14:42:39 +00:00
const commitTempFileLocation = '/tmp/theme-deploy-comment.txt' ;
const commitMessage = await buildPhabricatorCommitMessageSince ( hash ) ;
2021-09-29 15:01:05 +00:00
2021-10-11 20:45:21 +00:00
console . log ( commitMessage ) ;
2021-10-01 14:42:39 +00:00
const result = await executeOnSandbox ( `
2021-09-29 15:01:05 +00:00
cd $ { sandboxPublicThemesFolder } ;
2021-10-11 20:45:21 +00:00
echo "${commitMessage}" > $ { commitTempFileLocation } ;
svn add -- force * -- auto - props -- parents -- depth infinity - q ;
svn status | grep "^\!" | sed 's/^\! *//g' | xargs svn rm ;
2021-10-01 14:42:39 +00:00
arc diff -- create -- message - file $ { commitTempFileLocation }
` , true);
2021-09-29 15:01:05 +00:00
2021-10-01 14:42:39 +00:00
const phabricatorUrl = getPhabricatorUrlFromResponse ( result ) ;
2021-09-29 15:01:05 +00:00
console . log ( 'Diff Created at: ' , phabricatorUrl ) ;
if ( phabricatorUrl ) {
open ( phabricatorUrl ) ;
}
return phabricatorUrl ;
}
/ *
Utility to pull the Phabricator URL from the diff creation command .
Used by createGitPhabricatorDiff
* /
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 = { } ) {
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" ` ) ;
let logs = await executeCommand ( ` git log --reverse --pretty=format:%s ${ hash } ..HEAD ` ) ;
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 }
` );
}
/ *
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 .
* /
2021-10-01 14:42:39 +00:00
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 {
child = spawn ( process . env . SHELL , [ '-c' , command ] ) ;
}
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 ( ) ) ;
} ) ;
} ) ;
}