Feat: add support for checking if source folder exists (#463)
* Refactor: check parameters in `init`
* Test: add test for checking if folder exist
- rm unnecessary tests
* Test: fix wrong error message
* Refactor: mv initialization functions to `run`
Refactor: rm `action.root`, `action.rootPath`
Refactor: `generateFolderPath`, `hasRequiredParameters`
Test: rm some tests for `init`, add tests for `checkParameters`
diff --git a/__tests__/git.test.ts b/__tests__/git.test.ts
index 404fb9a..1ef54c6 100644
--- a/__tests__/git.test.ts
+++ b/__tests__/git.test.ts
@@ -32,171 +32,6 @@
})
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 @@
})
await init(action)
-
expect(execute).toBeCalledTimes(7)
})
})
@@ -234,27 +68,6 @@
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 @@
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 @@
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 9d9724a..0437586 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 @@
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 @@
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 @@
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 @@
it('should return ... if no token is provided', async () => {
const action = {
branch: '123',
- root: '.',
workspace: 'src/',
folder: 'build',
gitHubToken: null,
@@ -87,7 +90,6 @@
const action = {
repositoryName: 'JamesIves/github-pages-deploy-action',
branch: '123',
- root: '.',
workspace: 'src/',
folder: 'build',
gitHubToken: null,
@@ -104,7 +106,6 @@
const action = {
repositoryName: 'JamesIves/github-pages-deploy-action',
branch: '123',
- root: '.',
workspace: 'src/',
folder: 'build',
gitHubToken: null,
@@ -121,7 +122,6 @@
const action = {
repositoryName: 'JamesIves/github-pages-deploy-action',
branch: '123',
- root: '.',
workspace: 'src/',
folder: 'build',
gitHubToken: '123',
@@ -141,7 +141,6 @@
repositoryPath:
'https://x-access-token:supersecret999%%%@github.com/anothersecret123333',
branch: '123',
- root: '.',
workspace: 'src/',
folder: 'build',
accessToken: 'supersecret999%%%',
@@ -161,7 +160,6 @@
repositoryPath:
'https://x-access-token:supersecret999%%%@github.com/anothersecret123333',
branch: '123',
- root: '.',
workspace: 'src/',
folder: 'build',
accessToken: 'supersecret999%%%',
@@ -183,7 +181,6 @@
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 @@
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 @@
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 @@
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 @@
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 5e43eef..8c56f2a 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -24,6 +24,7 @@
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 @@
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 @@
: 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 @@
workspace: process.env.GITHUB_WORKSPACE || ''
}
-export type ActionFolders = NonNullable<
- Pick<ActionInterface, 'folder' | 'root'>
+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 bb7ffc2..13a7344 100644
--- a/src/git.ts
+++ b/src/git.ts
@@ -3,18 +3,11 @@
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<void | Error> {
try {
- hasRequiredParameters(action)
-
info(`Deploying using ${action.tokenType}… 🔑`)
info('Configuring git…')
@@ -73,8 +66,6 @@
action: ActionInterface
): Promise<void> {
try {
- hasRequiredParameters(action)
-
await execute(
`git checkout --progress --force ${
action.baseBranch ? action.baseBranch : action.defaultBranch
@@ -95,8 +86,6 @@
/* Generates the branch if it doesn't exist on the remote. */
export async function generateBranch(action: ActionInterface): Promise<void> {
try {
- hasRequiredParameters(action)
-
info(`Creating the ${action.branch} branch…`)
await switchToBaseBranch(action)
@@ -131,8 +120,6 @@
/* Runs the necessary steps to make the deployment. */
export async function deploy(action: ActionInterface): Promise<Status> {
- 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 @@
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 @@
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 4286d8b..6569351 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 @@
...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 d423b81..fc52f54 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 @@
}@github.com/${action.repositoryName}.git`
/* Genetate absolute folder path by the provided folder name */
-export const generateFolderPath = <K extends keyof ActionFolders>(
- 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 = <K extends keyof RequiredActionParameters>(
+ 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. */