diff --git a/__tests__/git.test.ts b/__tests__/git.test.ts index 404fb9a1..1ef54c6d 100644 --- a/__tests__/git.test.ts +++ b/__tests__/git.test.ts @@ -32,171 +32,6 @@ describe('git', () => { }) describe('init', () => { - it('should execute commands if a GitHub token is provided', async () => { - Object.assign(action, { - silent: false, - repositoryPath: 'JamesIves/github-pages-deploy-action', - folder: 'assets', - branch: 'branch', - gitHubToken: '123', - pusher: { - name: 'asd', - email: 'as@cat' - } - }) - - await init(action) - expect(execute).toBeCalledTimes(6) - }) - - it('should execute commands if an Access Token is provided', async () => { - Object.assign(action, { - silent: false, - repositoryPath: 'JamesIves/github-pages-deploy-action', - folder: 'assets', - branch: 'branch', - accessToken: '123', - pusher: { - name: 'asd', - email: 'as@cat' - } - }) - - await init(action) - expect(execute).toBeCalledTimes(6) - }) - - it('should execute commands if SSH is true', async () => { - Object.assign(action, { - silent: false, - repositoryPath: 'JamesIves/github-pages-deploy-action', - folder: 'assets', - branch: 'branch', - ssh: true, - pusher: { - name: 'asd', - email: 'as@cat' - } - }) - - await init(action) - - expect(execute).toBeCalledTimes(6) - }) - - it('should fail if there is no provided GitHub Token, Access Token or SSH bool', async () => { - Object.assign(action, { - silent: false, - repositoryPath: null, - folder: 'assets', - branch: 'branch', - pusher: { - name: 'asd', - email: 'as@cat' - } - }) - - try { - await init(action) - } catch (e) { - expect(execute).toBeCalledTimes(0) - expect(e.message).toMatch( - 'There was an error initializing the repository: No deployment token/method was provided. 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. ❌' - ) - } - }) - - it('should fail if access token is defined but it is an empty string', async () => { - Object.assign(action, { - silent: false, - repositoryPath: null, - folder: 'assets', - branch: 'branch', - pusher: { - name: 'asd', - email: 'as@cat' - }, - accessToken: '' - }) - - try { - await init(action) - } catch (e) { - expect(execute).toBeCalledTimes(0) - expect(e.message).toMatch( - 'There was an error initializing the repository: No deployment token/method was provided. 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. ❌' - ) - } - }) - - it('should fail if there is no folder', async () => { - Object.assign(action, { - silent: false, - repositoryPath: 'JamesIves/github-pages-deploy-action', - gitHubToken: '123', - branch: 'branch', - pusher: { - name: 'asd', - email: 'as@cat' - }, - folder: null, - ssh: true - }) - - try { - await init(action) - } catch (e) { - expect(execute).toBeCalledTimes(0) - expect(e.message).toMatch( - 'There was an error initializing the repository: You must provide the action with a folder to deploy. ❌' - ) - } - }) - - it('should fail if there is no provided repository path', async () => { - Object.assign(action, { - silent: true, - repositoryPath: null, - folder: 'assets', - branch: 'branch', - pusher: { - name: 'asd', - email: 'as@cat' - }, - gitHubToken: '123', - accessToken: null, - ssh: null - }) - - try { - await init(action) - } catch (e) { - expect(execute).toBeCalledTimes(0) - expect(e.message).toMatch( - 'There was an error initializing the repository: No deployment token/method was provided. 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. ' - ) - } - }) - - it('should not fail if root is used', async () => { - Object.assign(action, { - silent: false, - repositoryPath: 'JamesIves/github-pages-deploy-action', - accessToken: '123', - branch: 'branch', - folder: '.', - root: '.', - pusher: { - name: 'asd', - email: 'as@cat' - } - }) - - await init(action) - - expect(execute).toBeCalledTimes(6) - }) - it('should stash changes if preserve is true', async () => { Object.assign(action, { silent: false, @@ -213,7 +48,6 @@ describe('git', () => { }) await init(action) - expect(execute).toBeCalledTimes(7) }) }) @@ -234,27 +68,6 @@ describe('git', () => { await generateBranch(action) expect(execute).toBeCalledTimes(6) }) - - it('should fail if there is no branch', async () => { - Object.assign(action, { - silent: false, - accessToken: '123', - branch: null, - folder: '.', - pusher: { - name: 'asd', - email: 'as@cat' - } - }) - - try { - await generateBranch(action) - } catch (e) { - expect(e.message).toMatch( - 'There was an error creating the deployment branch: Branch is required. ❌' - ) - } - }) }) describe('switchToBaseBranch', () => { @@ -290,31 +103,6 @@ describe('git', () => { await switchToBaseBranch(action) expect(execute).toBeCalledTimes(1) }) - - it('should fail if one of the required parameters is not available', async () => { - Object.assign(action, { - silent: false, - baseBranch: '123', - accessToken: null, - gitHubToken: null, - ssh: null, - branch: 'branch', - folder: null, - pusher: { - name: 'asd', - email: 'as@cat' - } - }) - - try { - await switchToBaseBranch(action) - } catch (e) { - expect(execute).toBeCalledTimes(0) - expect(e.message).toMatch( - 'There was an error switching to the base branch: No deployment token/method was provided. 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. ❌' - ) - } - }) }) describe('deploy', () => { @@ -488,31 +276,5 @@ describe('git', () => { expect(rmRF).toBeCalledTimes(1) expect(response).toBe(Status.SKIPPED) }) - - it('should throw an error if one of the required parameters is not available', async () => { - Object.assign(action, { - silent: false, - folder: 'assets', - branch: 'branch', - ssh: null, - accessToken: null, - gitHubToken: null, - pusher: { - name: 'asd', - email: 'as@cat' - }, - isTest: false // Setting this env variable to false means there will never be anything to commit and the action will exit early. - }) - - try { - await deploy(action) - } catch (e) { - expect(execute).toBeCalledTimes(1) - expect(rmRF).toBeCalledTimes(1) - expect(e.message).toMatch( - 'The deploy step encountered an error: No deployment token/method was provided. 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. ❌' - ) - } - }) }) }) diff --git a/__tests__/util.test.ts b/__tests__/util.test.ts index 9d9724a1..0437586b 100644 --- a/__tests__/util.test.ts +++ b/__tests__/util.test.ts @@ -1,9 +1,11 @@ +import {ActionInterface} from '../src/constants' import { isNullOrUndefined, generateTokenType, generateRepositoryPath, generateFolderPath, - suppressSensitiveInformation + suppressSensitiveInformation, + checkParameters } from '../src/util' describe('util', () => { @@ -22,13 +24,17 @@ describe('util', () => { const value = 'montezuma' expect(isNullOrUndefined(value)).toBeFalsy() }) + + it('should return false if the value is empty string', async () => { + const value = '' + expect(isNullOrUndefined(value)).toBeTruthy() + }) }) describe('generateTokenType', () => { it('should return ssh if ssh is provided', async () => { const action = { branch: '123', - root: '.', workspace: 'src/', folder: 'build', gitHubToken: null, @@ -42,7 +48,6 @@ describe('util', () => { it('should return access token if access token is provided', async () => { const action = { branch: '123', - root: '.', workspace: 'src/', folder: 'build', gitHubToken: null, @@ -56,7 +61,6 @@ describe('util', () => { it('should return github token if github token is provided', async () => { const action = { branch: '123', - root: '.', workspace: 'src/', folder: 'build', gitHubToken: '123', @@ -70,7 +74,6 @@ describe('util', () => { it('should return ... if no token is provided', async () => { const action = { branch: '123', - root: '.', workspace: 'src/', folder: 'build', gitHubToken: null, @@ -87,7 +90,6 @@ describe('util', () => { const action = { repositoryName: 'JamesIves/github-pages-deploy-action', branch: '123', - root: '.', workspace: 'src/', folder: 'build', gitHubToken: null, @@ -104,7 +106,6 @@ describe('util', () => { const action = { repositoryName: 'JamesIves/github-pages-deploy-action', branch: '123', - root: '.', workspace: 'src/', folder: 'build', gitHubToken: null, @@ -121,7 +122,6 @@ describe('util', () => { const action = { repositoryName: 'JamesIves/github-pages-deploy-action', branch: '123', - root: '.', workspace: 'src/', folder: 'build', gitHubToken: '123', @@ -141,7 +141,6 @@ describe('util', () => { repositoryPath: 'https://x-access-token:supersecret999%%%@github.com/anothersecret123333', branch: '123', - root: '.', workspace: 'src/', folder: 'build', accessToken: 'supersecret999%%%', @@ -161,7 +160,6 @@ describe('util', () => { repositoryPath: 'https://x-access-token:supersecret999%%%@github.com/anothersecret123333', branch: '123', - root: '.', workspace: 'src/', folder: 'build', accessToken: 'supersecret999%%%', @@ -183,7 +181,6 @@ describe('util', () => { it('should return absolute path if folder name is provided', () => { const action = { branch: '123', - root: '.', workspace: 'src/', folder: 'build', gitHubToken: null, @@ -191,13 +188,12 @@ describe('util', () => { ssh: null, silent: false } - expect(generateFolderPath(action, 'folder')).toEqual('src/build') + expect(generateFolderPath(action)).toEqual('src/build') }) it('should return original path if folder name begins with /', () => { const action = { branch: '123', - root: '.', workspace: 'src/', folder: '/home/user/repo/build', gitHubToken: null, @@ -205,15 +201,12 @@ describe('util', () => { ssh: null, silent: false } - expect(generateFolderPath(action, 'folder')).toEqual( - '/home/user/repo/build' - ) + expect(generateFolderPath(action)).toEqual('/home/user/repo/build') }) it('should process as relative path if folder name begins with ./', () => { const action = { branch: '123', - root: '.', workspace: 'src/', folder: './build', gitHubToken: null, @@ -221,13 +214,12 @@ describe('util', () => { ssh: null, silent: false } - expect(generateFolderPath(action, 'folder')).toEqual('src/build') + expect(generateFolderPath(action)).toEqual('src/build') }) it('should return absolute path if folder name begins with ~', () => { const action = { branch: '123', - root: '.', workspace: 'src/', folder: '~/repo/build', gitHubToken: null, @@ -236,9 +228,102 @@ describe('util', () => { silent: false } process.env.HOME = '/home/user' - expect(generateFolderPath(action, 'folder')).toEqual( - '/home/user/repo/build' - ) + expect(generateFolderPath(action)).toEqual('/home/user/repo/build') + }) + }) + + describe('hasRequiredParameters', () => { + it('should fail if there is no provided GitHub Token, Access Token or SSH bool', () => { + const action = { + silent: false, + repositoryPath: undefined, + branch: 'branch', + folder: 'build', + workspace: 'src/' + } + + try { + checkParameters(action) + } catch (e) { + expect(e.message).toMatch( + 'No deployment token/method was provided. 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.' + ) + } + }) + + it('should fail if access token is defined but it is an empty string', () => { + const action = { + silent: false, + repositoryPath: undefined, + accessToken: '', + branch: 'branch', + folder: 'build', + workspace: 'src/' + } + + try { + checkParameters(action) + } catch (e) { + expect(e.message).toMatch( + 'No deployment token/method was provided. 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.' + ) + } + }) + + it('should fail if there is no branch', () => { + const action = { + silent: false, + repositoryPath: undefined, + accessToken: '123', + branch: '', + folder: 'build', + workspace: 'src/' + } + + try { + checkParameters(action) + } catch (e) { + expect(e.message).toMatch('Branch is required.') + } + }) + + it('should fail if there is no folder', () => { + const action = { + silent: false, + repositoryPath: undefined, + gitHubToken: '123', + branch: 'branch', + folder: '', + workspace: 'src/' + } + + try { + checkParameters(action) + } catch (e) { + expect(e.message).toMatch( + 'You must provide the action with a folder to deploy.' + ) + } + }) + + it('should fail if the folder does not exist in the tree', () => { + const action: ActionInterface = { + silent: false, + repositoryPath: undefined, + gitHubToken: '123', + branch: 'branch', + folder: 'notARealFolder', + workspace: '.' + } + + try { + action.folderPath = generateFolderPath(action) + checkParameters(action) + } catch (e) { + expect(e.message).toMatch( + `The ${action.folderPath} directory you're trying to deploy doesn't exist.` + ) + } }) }) }) diff --git a/src/constants.ts b/src/constants.ts index 5e43eef1..8c56f2a9 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -24,6 +24,7 @@ export interface ActionInterface { email?: string /** The folder to deploy. */ folder: string + folderPath?: string /** GitHub deployment token. */ gitHubToken?: string | null /** Determines if the action is running in test mode or not. */ @@ -38,8 +39,6 @@ export interface ActionInterface { repositoryName?: string /** The fully qualified repositpory path, this gets auto generated if repositoryName is provided. */ repositoryPath?: string - /** The root directory where your project lives. */ - root: string /** Wipes the commit history from the deployment branch in favor of a single commit. */ singleCommit?: boolean | null /** Determines if the action should run in silent mode or not. */ @@ -95,7 +94,6 @@ export const action: ActionInterface = { : repository && repository.full_name ? repository.full_name : process.env.GITHUB_REPOSITORY, - root: '.', singleCommit: !isNullOrUndefined(getInput('SINGLE_COMMIT')) ? getInput('SINGLE_COMMIT').toLowerCase() === 'true' : false, @@ -109,8 +107,9 @@ export const action: ActionInterface = { workspace: process.env.GITHUB_WORKSPACE || '' } -export type ActionFolders = NonNullable< - Pick +export type RequiredActionParameters = Pick< + ActionInterface, + 'accessToken' | 'gitHubToken' | 'ssh' | 'branch' | 'folder' > /** Status codes for the action. */ diff --git a/src/git.ts b/src/git.ts index bb7ffc22..13a7344a 100644 --- a/src/git.ts +++ b/src/git.ts @@ -3,18 +3,11 @@ import {mkdirP, rmRF} from '@actions/io' import fs from 'fs' import {ActionInterface, Status} from './constants' import {execute} from './execute' -import { - generateFolderPath, - hasRequiredParameters, - isNullOrUndefined, - suppressSensitiveInformation -} from './util' +import {isNullOrUndefined, suppressSensitiveInformation} from './util' /* Initializes git in the workspace. */ export async function init(action: ActionInterface): Promise { try { - hasRequiredParameters(action) - info(`Deploying using ${action.tokenType}… 🔑`) info('Configuring git…') @@ -73,8 +66,6 @@ export async function switchToBaseBranch( action: ActionInterface ): Promise { try { - hasRequiredParameters(action) - await execute( `git checkout --progress --force ${ action.baseBranch ? action.baseBranch : action.defaultBranch @@ -95,8 +86,6 @@ export async function switchToBaseBranch( /* Generates the branch if it doesn't exist on the remote. */ export async function generateBranch(action: ActionInterface): Promise { try { - hasRequiredParameters(action) - info(`Creating the ${action.branch} branch…`) await switchToBaseBranch(action) @@ -131,8 +120,6 @@ export async function generateBranch(action: ActionInterface): Promise { /* Runs the necessary steps to make the deployment. */ export async function deploy(action: ActionInterface): Promise { - const folderPath = generateFolderPath(action, 'folder') - const rootPath = generateFolderPath(action, 'root') const temporaryDeploymentDirectory = 'github-pages-deploy-action-temp-deployment-folder' const temporaryDeploymentBranch = `github-pages-deploy-action/${Math.random() @@ -142,8 +129,6 @@ export async function deploy(action: ActionInterface): Promise { info('Starting to commit changes…') try { - hasRequiredParameters(action) - const commitMessage = !isNullOrUndefined(action.commitMessage) ? (action.commitMessage as string) : `Deploying to ${action.branch} from ${action.baseBranch} ${ @@ -232,22 +217,24 @@ export async function deploy(action: ActionInterface): Promise { Allows the user to specify the root if '.' is provided. rsync is used to prevent file duplication. */ await execute( - `rsync -q -av --checksum --progress ${folderPath}/. ${ + `rsync -q -av --checksum --progress ${action.folderPath}/. ${ action.targetFolder ? `${temporaryDeploymentDirectory}/${action.targetFolder}` : temporaryDeploymentDirectory } ${ action.clean ? `--delete ${excludes} ${ - !fs.existsSync(`${folderPath}/CNAME`) ? '--exclude CNAME' : '' + !fs.existsSync(`${action.folderPath}/CNAME`) + ? '--exclude CNAME' + : '' } ${ - !fs.existsSync(`${folderPath}/.nojekyll`) + !fs.existsSync(`${action.folderPath}/.nojekyll`) ? '--exclude .nojekyll' : '' }` : '' } --exclude .ssh --exclude .git --exclude .github ${ - folderPath === rootPath + action.folderPath === action.workspace ? `--exclude ${temporaryDeploymentDirectory}` : '' }`, diff --git a/src/lib.ts b/src/lib.ts index 4286d8bf..6569351f 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -1,7 +1,12 @@ import {exportVariable, info, setFailed} from '@actions/core' import {action, ActionInterface, Status} from './constants' import {deploy, init} from './git' -import {generateRepositoryPath, generateTokenType} from './util' +import { + generateFolderPath, + checkParameters, + generateRepositoryPath, + generateTokenType +} from './util' /** Initializes and runs the action. * @@ -30,6 +35,11 @@ export default async function run( ...configuration } + // Defines the folder paths + settings.folderPath = generateFolderPath(settings) + + checkParameters(settings) + // Defines the repository paths and token types. settings.repositoryPath = generateRepositoryPath(settings) settings.tokenType = generateTokenType(settings) diff --git a/src/util.ts b/src/util.ts index d423b814..fc52f541 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,6 +1,7 @@ +import {existsSync} from 'fs' import path from 'path' import {isDebug} from '@actions/core' -import {ActionInterface, ActionFolders} from './constants' +import {ActionInterface, RequiredActionParameters} from './constants' const replaceAll = (input: string, find: string, replace: string): string => input.split(find).join(replace) @@ -28,40 +29,46 @@ export const generateRepositoryPath = (action: ActionInterface): string => }@github.com/${action.repositoryName}.git` /* Genetate absolute folder path by the provided folder name */ -export const generateFolderPath = ( - action: ActionInterface, - key: K -): string => { - const folderName = action[key] - const folderPath = path.isAbsolute(folderName) +export const generateFolderPath = (action: ActionInterface): string => { + const folderName = action['folder'] + return path.isAbsolute(folderName) ? folderName : folderName.startsWith('~') ? folderName.replace('~', process.env.HOME as string) : path.join(action.workspace, folderName) - return folderPath } /* Checks for the required tokens and formatting. Throws an error if any case is matched. */ -export const hasRequiredParameters = (action: ActionInterface): void => { - if ( - (isNullOrUndefined(action.accessToken) && - isNullOrUndefined(action.gitHubToken) && - isNullOrUndefined(action.ssh)) || - isNullOrUndefined(action.repositoryPath) || - (action.accessToken && action.accessToken === '') - ) { +const hasRequiredParameters = ( + action: ActionInterface, + params: K[] +): boolean => { + const nonNullParams = params.filter( + param => !isNullOrUndefined(action[param]) + ) + return Boolean(nonNullParams.length) +} + +export const checkParameters = (action: ActionInterface): void => { + if (!hasRequiredParameters(action, ['accessToken', 'gitHubToken', 'ssh'])) { throw new Error( 'No deployment token/method was provided. 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.' ) } - if (isNullOrUndefined(action.branch)) { + if (!hasRequiredParameters(action, ['branch'])) { throw new Error('Branch is required.') } - if (!action.folder || isNullOrUndefined(action.folder)) { + if (!hasRequiredParameters(action, ['folder'])) { throw new Error('You must provide the action with a folder to deploy.') } + + if (!existsSync(action.folderPath as string)) { + throw new Error( + `The ${action.folderPath} directory you're trying to deploy doesn't exist. ❗` + ) + } } /* Suppresses sensitive information from being exposed in error messages. */