2020-01-20 04:33:14 +08:00
import { setFailed } from "@actions/core" ;
2020-01-20 02:15:19 +08:00
import {
action ,
repositoryPath ,
root ,
tokenType ,
workspace
} from "./constants" ;
2020-01-12 09:26:08 +08:00
import { execute } from "./execute" ;
import { isNullOrUndefined } from "./util" ;
2019-11-19 23:06:27 +08:00
/ * * G e n e r a t e s t h e b r a n c h i f i t d o e s n ' t e x i s t o n t h e r e m o t e .
* @returns { Promise }
* /
export async function init ( ) : Promise < any > {
try {
2020-01-12 09:26:08 +08:00
if (
isNullOrUndefined ( action . accessToken ) &&
2020-01-20 02:15:19 +08:00
isNullOrUndefined ( action . gitHubToken ) &&
isNullOrUndefined ( action . ssh )
2020-01-12 09:26:08 +08:00
) {
2020-01-20 04:33:14 +08:00
setFailed (
2020-01-20 02:15:19 +08:00
"You must provide the action with either a Personal Access Token or the GitHub Token secret in order to deploy. If you wish to use an ssh deploy token then you must set SSH to true."
2019-11-19 23:06:27 +08:00
) ;
2020-01-20 04:33:14 +08:00
throw "No deployment token/method was provided." ;
2019-11-19 23:06:27 +08:00
}
if ( action . build . startsWith ( "/" ) || action . build . startsWith ( "./" ) ) {
2020-01-20 04:33:14 +08:00
setFailed (
2019-11-19 23:06:27 +08:00
` The deployment folder cannot be prefixed with '/' or './'. Instead reference the folder name directly. `
) ;
2020-01-20 04:33:14 +08:00
throw "Incorrectly formatted build folder." ;
2019-11-19 23:06:27 +08:00
}
2020-01-20 02:15:19 +08:00
console . log ( ` Deploying using ${ tokenType } ... 🔑 ` ) ;
2019-11-19 23:06:27 +08:00
await execute ( ` git init ` , workspace ) ;
2019-11-22 23:01:06 +08:00
await execute ( ` git config user.name ${ action . name } ` , workspace ) ;
await execute ( ` git config user.email ${ action . email } ` , workspace ) ;
2020-01-15 04:14:15 +08:00
await execute ( ` git remote rm origin ` , workspace ) ;
await execute ( ` git remote add origin ${ repositoryPath } ` , workspace ) ;
2020-01-15 03:22:01 +08:00
await execute ( ` git fetch ` , workspace ) ;
2019-11-19 23:06:27 +08:00
} catch ( error ) {
2020-01-20 04:33:14 +08:00
console . log ( ` There was an error initializing the repository: ${ error } ` ) ;
2019-11-19 23:06:27 +08:00
} finally {
return Promise . resolve ( "Initialization step complete..." ) ;
}
}
2019-12-22 05:51:39 +08:00
/ * * S w i t c h e s t o t h e b a s e b r a n c h .
* @returns { Promise }
* /
2020-01-14 23:04:59 +08:00
export async function switchToBaseBranch ( ) : Promise < any > {
2019-12-22 05:51:39 +08:00
await execute (
2020-01-14 23:04:59 +08:00
` git checkout --progress --force ${
action . baseBranch ? action.baseBranch : action.defaultBranch
} ` ,
2019-12-22 05:51:39 +08:00
workspace
) ;
return Promise . resolve ( "Switched to the base branch..." ) ;
}
2019-11-19 23:06:27 +08:00
/ * * G e n e r a t e s t h e b r a n c h i f i t d o e s n ' t e x i s t o n t h e r e m o t e .
* @returns { Promise }
* /
export async function generateBranch ( ) : Promise < any > {
try {
2020-01-20 04:33:14 +08:00
if ( isNullOrUndefined ( action . branch ) ) {
throw "Branch is required." ;
}
2019-12-22 03:58:59 +08:00
console . log ( ` Creating ${ action . branch } branch... 🔧 ` ) ;
2019-12-22 05:51:39 +08:00
await switchToBaseBranch ( ) ;
2020-01-14 23:04:59 +08:00
await execute ( ` git checkout --orphan ${ action . branch } ` , workspace ) ;
2019-11-19 23:06:27 +08:00
await execute ( ` git reset --hard ` , workspace ) ;
await execute (
` git commit --allow-empty -m "Initial ${ action . branch } commit." ` ,
workspace
) ;
await execute ( ` git push ${ repositoryPath } ${ action . branch } ` , workspace ) ;
2020-01-15 03:22:01 +08:00
await execute ( ` git fetch ` , workspace ) ;
2019-11-19 23:06:27 +08:00
} catch ( error ) {
2020-01-20 04:33:14 +08:00
setFailed ( ` There was an error creating the deployment branch: ${ error } ❌ ` ) ;
2019-11-19 23:06:27 +08:00
} finally {
2019-12-22 03:58:59 +08:00
return Promise . resolve ( "Deployment branch creation step complete... ✅" ) ;
2019-11-19 23:06:27 +08:00
}
}
/ * * R u n s t h e n e c e s s a r y s t e p s t o m a k e t h e d e p l o y m e n t .
* @returns { Promise }
* /
export async function deploy ( ) : Promise < any > {
const temporaryDeploymentDirectory = "gh-action-temp-deployment-folder" ;
const temporaryDeploymentBranch = "gh-action-temp-deployment-branch" ;
/ *
Checks to see if the remote exists prior to deploying .
If the branch doesn ' t exist it gets created here as an orphan .
* /
const branchExists = await execute (
` git ls-remote --heads ${ repositoryPath } ${ action . branch } | wc -l ` ,
workspace
) ;
if ( ! branchExists ) {
console . log ( "Deployment branch does not exist. Creating...." ) ;
await generateBranch ( ) ;
}
// Checks out the base branch to begin the deployment process.
2019-12-22 05:51:39 +08:00
await switchToBaseBranch ( ) ;
2020-01-08 21:32:47 +08:00
await execute ( ` git fetch ${ repositoryPath } ` , workspace ) ;
2019-11-19 23:06:27 +08:00
await execute (
` git worktree add --checkout ${ temporaryDeploymentDirectory } origin/ ${ action . branch } ` ,
workspace
) ;
2019-12-21 06:36:35 +08:00
// Ensures that items that need to be excluded from the clean job get parsed.
let excludes = "" ;
if ( action . clean && action . cleanExclude ) {
try {
const excludedItems = JSON . parse ( action . cleanExclude ) ;
excludedItems . forEach (
( item : string ) = > ( excludes += ` --exclude ${ item } ` )
) ;
} catch {
console . log (
2019-12-22 03:58:59 +08:00
"There was an error parsing your CLEAN_EXCLUDE items. Please refer to the README for more details. ❌"
2019-12-21 06:36:35 +08:00
) ;
}
}
2019-11-19 23:06:27 +08:00
/ *
Pushes all of the build files into the deployment directory .
2019-11-22 22:58:00 +08:00
Allows the user to specify the root if '.' is provided .
rysync is used to prevent file duplication . * /
await execute (
2019-12-16 23:29:29 +08:00
` rsync -q -av --progress ${ action . build } /. ${
action . targetFolder
? ` ${ temporaryDeploymentDirectory } / ${ action . targetFolder } `
: temporaryDeploymentDirectory
} $ {
2019-12-21 06:36:30 +08:00
action . clean
? ` --delete ${ excludes } --exclude CNAME --exclude .nojekyll `
: ""
2020-01-20 02:15:19 +08:00
} -- exclude . ssh -- exclude . git -- exclude . github $ {
2019-11-22 22:58:00 +08:00
action . build === root ? ` --exclude ${ temporaryDeploymentDirectory } ` : ""
} ` ,
workspace
) ;
2019-11-19 23:06:27 +08:00
2019-11-20 07:07:27 +08:00
const hasFilesToCommit = await execute (
` git status --porcelain ` ,
temporaryDeploymentDirectory
) ;
2020-01-20 04:33:14 +08:00
if ( ! hasFilesToCommit && ! action . isTest ) {
2019-12-22 03:58:59 +08:00
console . log ( "There is nothing to commit. Exiting... ✅" ) ;
2019-11-20 07:07:27 +08:00
return Promise . resolve ( ) ;
}
2019-11-19 23:06:27 +08:00
// Commits to GitHub.
await execute ( ` git add --all . ` , temporaryDeploymentDirectory ) ;
await execute (
2020-01-14 23:04:59 +08:00
` git checkout -b ${ temporaryDeploymentBranch } ` ,
2019-11-19 23:06:27 +08:00
temporaryDeploymentDirectory
) ;
await execute (
2020-01-19 03:41:49 +08:00
` git commit -m " ${
! isNullOrUndefined ( action . commitMessage )
? action . commitMessage
: ` Deploying to ${ action . branch } from ${ action . baseBranch } `
} - $ { process . env . GITHUB_SHA } 🚀 " -- quiet ` ,
2019-11-19 23:06:27 +08:00
temporaryDeploymentDirectory
) ;
await execute (
` git push --force ${ repositoryPath } ${ temporaryDeploymentBranch } : ${ action . branch } ` ,
temporaryDeploymentDirectory
) ;
2019-12-22 03:58:59 +08:00
// Cleans up temporary files/folders and restores the git state.
2019-12-22 05:51:39 +08:00
console . log ( "Running post deployment cleanup jobs... 🔧" ) ;
await execute ( ` rm -rf ${ temporaryDeploymentDirectory } ` , workspace ) ;
2019-12-22 05:52:52 +08:00
await execute (
` git checkout --progress --force ${ action . defaultBranch } ` ,
workspace
) ;
2019-12-22 03:58:59 +08:00
2019-11-19 23:06:27 +08:00
return Promise . resolve ( "Commit step complete..." ) ;
}