Merge branch 'dev' into releases/v3

This commit is contained in:
James Ives 2020-10-17 15:19:50 -04:00
commit e45a9df141
18 changed files with 906 additions and 1127 deletions

View File

@ -1,6 +1,6 @@
{ {
"plugins": ["jest", "@typescript-eslint"], "plugins": ["jest", "@typescript-eslint", "github"],
"extends": ["plugin:github/es6"], "extends": ["plugin:github/recommended"],
"parser": "@typescript-eslint/parser", "parser": "@typescript-eslint/parser",
"parserOptions": { "parserOptions": {
"ecmaVersion": 9, "ecmaVersion": 9,
@ -19,13 +19,17 @@
"@typescript-eslint/no-require-imports": "error", "@typescript-eslint/no-require-imports": "error",
"@typescript-eslint/array-type": "error", "@typescript-eslint/array-type": "error",
"@typescript-eslint/await-thenable": "error", "@typescript-eslint/await-thenable": "error",
"@typescript-eslint/ban-ts-ignore": "error", "@typescript-eslint/ban-ts-comment": "error",
"camelcase": "off", "camelcase": "off",
"@typescript-eslint/camelcase": "error", "@typescript-eslint/naming-convention": [
"@typescript-eslint/class-name-casing": "error", "error",
{
"selector": "default",
"format": ["camelCase", "UPPER_CASE", "StrictPascalCase"]
}
],
"@typescript-eslint/explicit-function-return-type": ["error", {"allowExpressions": true}], "@typescript-eslint/explicit-function-return-type": ["error", {"allowExpressions": true}],
"@typescript-eslint/func-call-spacing": ["error", "never"], "@typescript-eslint/func-call-spacing": ["error", "never"],
"@typescript-eslint/generic-type-naming": ["error", "^[A-Z][A-Za-z]*$"],
"@typescript-eslint/no-array-constructor": "error", "@typescript-eslint/no-array-constructor": "error",
"@typescript-eslint/no-empty-interface": "error", "@typescript-eslint/no-empty-interface": "error",
"@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-explicit-any": "off",
@ -50,7 +54,8 @@
"@typescript-eslint/semi": ["error", "never"], "@typescript-eslint/semi": ["error", "never"],
"@typescript-eslint/type-annotation-spacing": "error", "@typescript-eslint/type-annotation-spacing": "error",
"@typescript-eslint/unbound-method": "error", "@typescript-eslint/unbound-method": "error",
"no-console": "off" "no-console": "off",
"no-shadow": ["error", { "builtinGlobals": false, "hoist": "all", "allow": ["Status"] }]
}, },
"env": { "env": {
"node": true, "node": true,

3
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,3 @@
# These are supported funding model platforms
github: [JamesIves]

View File

@ -6,7 +6,7 @@ labels:
--- ---
<!-- Please check the FAQ before posting an issue: https://github.com/JamesIves/github-pages-deploy-action/wiki --> <!-- Please check the Q&A before posting an issue: https://github.com/JamesIves/github-pages-deploy-action/discussions?discussions_q=category%3AQ%26A -->
**Describe the bug** **Describe the bug**
<!-- Please provide a clear and concise description of what the bug is. --> <!-- Please provide a clear and concise description of what the bug is. -->

View File

@ -1,18 +0,0 @@
---
name: Feature Request
about: If you'd like to make a suggestion please fill out the form below.
labels:
- feature request
---
<!-- Please check the FAQ before posting an issue: https://github.com/JamesIves/github-pages-deploy-action/wiki -->
**Is your feature request related to a problem? Please describe.**
<!-- Please provide a clear and concise description of what the problem is. Please be sure to read the README first! -->
**Describe the solution you'd like**
<!-- Please provide a clear and concise description of what you want to happen. -->
**Additional Comments**
<!-- Add any other context about the feature request here. -->

View File

@ -1,18 +0,0 @@
---
name: Support
about: If you're having problems setting up the action you can make a request for support here.
labels:
- support
---
<!-- Please check the FAQ before posting an issue: https://github.com/JamesIves/github-pages-deploy-action/wiki -->
**Describe the Issue**
<!-- Please provide a clear and concise description of what the problem is. Please be sure to read the README first! -->
**Logs**
<!-- Please provide your deployment logs and a link or sample to/of your workflow. If the error message isn't revealing the problem please set ACTIONS_STEP_DEBUG to true in your repository's secrets menu and run the workflow again. -->
**Additional Comments**
<!-- Add any other context about the issue here. -->

10
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,10 @@
blank_issues_enabled: false
contact_links:
- name: Feature Request and Ideas
url: https://github.com/JamesIves/github-pages-deploy-action/discussions?discussions_q=category%3AIdeas
about: If you have an idea or would like to make a feature request please open a discussion thread.
- name: Support and Questions
url: https://github.com/JamesIves/github-pages-deploy-action/discussions?discussions_q=category%3AQ%26A
about: Need help or just have a general question? Please open a discussion thread.

8
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,8 @@
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: daily
time: "10:00"
open-pull-requests-limit: 10

2
.nvmrc
View File

@ -1 +1 @@
v10.19.0 v12.18.4

View File

@ -1,7 +1,7 @@
# Contributing ✏️ # Contributing ✏️
When contributing to this repository, please first discuss the change you wish to make via issue, When contributing to this repository, please first discuss the change you wish to make via issue,
[email, or any other method with the owners of this repository](https://jamesiv.es) before making a change. [email, or any other method with the owners of this repository](https://jamesiv.es) before making a change. If you are planning to work on an issue that already exists please let us know before writing any code incase it's already in flight!
## Before Making a Pull Request 🎒 ## Before Making a Pull Request 🎒

View File

@ -17,16 +17,16 @@
<img src="https://github.com/JamesIves/github-pages-deploy-action/workflows/integration-tests/badge.svg"> <img src="https://github.com/JamesIves/github-pages-deploy-action/workflows/integration-tests/badge.svg">
</a> </a>
<a href="https://github.com/marketplace/actions/deploy-to-github-pages"> <a href="https://codecov.io/gh/JamesIves/github-pages-deploy-action/branch/dev">
<img src="https://img.shields.io/badge/action-marketplace-blue.svg?logo=github&color=orange"> <img src="https://codecov.io/gh/JamesIves/github-pages-deploy-action/branch/dev/graph/badge.svg">
</a> </a>
<a href="https://github.com/JamesIves/github-pages-deploy-action/releases"> <a href="https://github.com/JamesIves/github-pages-deploy-action/releases">
<img src="https://img.shields.io/github/v/release/JamesIves/github-pages-deploy-action.svg?logo=github"> <img src="https://img.shields.io/github/v/release/JamesIves/github-pages-deploy-action.svg?logo=github">
</a> </a>
<a href="https://codecov.io/gh/JamesIves/github-pages-deploy-action/branch/dev"> <a href="https://github.com/marketplace/actions/deploy-to-github-pages">
<img src="https://codecov.io/gh/JamesIves/github-pages-deploy-action/branch/dev/graph/badge.svg"> <img src="https://img.shields.io/badge/action-marketplace-blue.svg?logo=github&color=orange">
</a> </a>
</p> </p>
@ -62,7 +62,7 @@ jobs:
npm run build npm run build
- name: Deploy 🚀 - name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@3.6.1 uses: JamesIves/github-pages-deploy-action@3.6.2
with: with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH: gh-pages # The branch the action should deploy to. BRANCH: gh-pages # The branch the action should deploy to.
@ -83,21 +83,20 @@ It's recommended that you use [Dependabot](https://dependabot.com/github-actions
#### Install as a Node Module 📦 #### Install as a Node Module 📦
If you'd like to use the functionality provided by this action in your own action you can install it using [yarn](https://yarnpkg.com/) by running the following command. If you'd like to use the functionality provided by this action in your own action you can install it using [yarn](https://yarnpkg.com/) or [npm](https://www.npmjs.com/get-npm) by running the following commands. It's available on both the [npm](https://www.npmjs.com/package/@jamesives/github-pages-deploy-action) and [GitHub registry](https://github.com/JamesIves/github-pages-deploy-action/packages/229985).
``` ```
yarn add @jamesives/github-pages-deploy-action yarn add @jamesives/github-pages-deploy-action
``` ```
```
npm install @jamesives/github-pages-deploy-action
```
It can then be imported into your project like so. It can then be imported into your project like so.
```javascript ```javascript
import run, { import run from "github-pages-deploy-action";
init,
deploy,
generateBranch,
ActionInterface
} from "github-pages-deploy-action";
``` ```
Calling the functions directly will require you to pass in an object containing the variables found in the configuration section, you'll also need to provide a `workspace` with a path to your project. Calling the functions directly will require you to pass in an object containing the variables found in the configuration section, you'll also need to provide a `workspace` with a path to your project.
@ -110,11 +109,12 @@ run({
branch: "gh-pages", branch: "gh-pages",
folder: "build", folder: "build",
repositoryName: "JamesIves/github-pages-deploy-action", repositoryName: "JamesIves/github-pages-deploy-action",
silent: true,
workspace: "src/project/location", workspace: "src/project/location",
}); });
``` ```
For more information regarding the [action interface please click here](https://github.com/JamesIves/github-pages-deploy-action/blob/dev/src/constants.ts#L7). You can find the npm registry listing for the module [here](https://www.npmjs.com/package/@jamesives/github-pages-deploy-action), and the GitHub registry listing [here](https://github.com/JamesIves/github-pages-deploy-action/packages/229985). For more information regarding the [action interface please click here](https://github.com/JamesIves/github-pages-deploy-action/blob/dev/src/constants.ts#L7).
## Configuration 📁 ## Configuration 📁
@ -135,7 +135,7 @@ In addition to the deployment options you must also configure the following.
| Key | Value Information | Type | Required | | Key | Value Information | Type | Required |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | -------- | | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | -------- |
| `BRANCH` | This is the branch you wish to deploy to, for example `gh-pages` or `docs`. | `with` | **Yes** | | `BRANCH` | This is the branch you wish to deploy to, for example `gh-pages` or `docs`. | `with` | **Yes** |
| `FOLDER` | The folder in your repository that you want to deploy. If your build script compiles into a directory named `build` you'd put it here. **Folder paths cannot have a leading `/` or `./`**. If you wish to deploy the root directory you can place a `.` here. | `with` | **Yes** | | `FOLDER` | The folder in your repository that you want to deploy. If your build script compiles into a directory named `build` you'd put it here. If you wish to deploy the root directory you can place a `.` here. You can also utilize absolute file paths by appending `~` to your folder path. | `with` | **Yes** |
#### Optional Choices #### Optional Choices
@ -174,7 +174,7 @@ The action will export an environment variable called `DEPLOYMENT_STATUS` that y
If you'd prefer to use an SSH deploy key as opposed to a token you must first generate a new SSH key by running the following terminal command, replacing the email with one connected to your GitHub account. If you'd prefer to use an SSH deploy key as opposed to a token you must first generate a new SSH key by running the following terminal command, replacing the email with one connected to your GitHub account.
```bash ```bash
ssh-keygen -t rsa -b 4096 -C "youremailhere@example.com" -N "" ssh-keygen -t rsa -m pem -b 4096 -C "youremailhere@example.com" -N ""
``` ```
Once you've generated the key pair you must add the contents of the public key within your repository's [deploy keys menu](https://developer.github.com/v3/guides/managing-deploy-keys/). You can find this option by going to `Settings > Deploy Keys`, you can name the public key whatever you want, but you **do** need to give it write access. Afterwards add the contents of the private key to the `Settings > Secrets` menu as `DEPLOY_KEY`. Once you've generated the key pair you must add the contents of the public key within your repository's [deploy keys menu](https://developer.github.com/v3/guides/managing-deploy-keys/). You can find this option by going to `Settings > Deploy Keys`, you can name the public key whatever you want, but you **do** need to give it write access. Afterwards add the contents of the private key to the `Settings > Secrets` menu as `DEPLOY_KEY`.
@ -183,12 +183,12 @@ With this configured you must add the `ssh-agent` step to your workflow and set
```yml ```yml
- name: Install SSH Client 🔑 - name: Install SSH Client 🔑
uses: webfactory/ssh-agent@v0.2.0 uses: webfactory/ssh-agent@v0.4.1
with: with:
ssh-private-key: ${{ secrets.DEPLOY_KEY }} ssh-private-key: ${{ secrets.DEPLOY_KEY }}
- name: Deploy 🚀 - name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@3.6.1 uses: JamesIves/github-pages-deploy-action@3.6.2
with: with:
SSH: true SSH: true
BRANCH: gh-pages BRANCH: gh-pages
@ -224,7 +224,7 @@ jobs:
ssh-private-key: ${{ secrets.DEPLOY_KEY }} ssh-private-key: ${{ secrets.DEPLOY_KEY }}
- name: Deploy 🚀 - name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@3.6.1 uses: JamesIves/github-pages-deploy-action@3.6.2
with: with:
BASE_BRANCH: master BASE_BRANCH: master
BRANCH: gh-pages BRANCH: gh-pages
@ -291,7 +291,7 @@ jobs:
name: site name: site
- name: Deploy 🚀 - name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@3.6.1 uses: JamesIves/github-pages-deploy-action@3.6.2
with: with:
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
BRANCH: gh-pages BRANCH: gh-pages
@ -313,7 +313,7 @@ If you use a [container](https://help.github.com/en/actions/automating-your-work
apt-get update && apt-get install -y rsync apt-get update && apt-get install -y rsync
- name: Deploy 🚀 - name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@3.6.1 uses: JamesIves/github-pages-deploy-action@3.6.2
``` ```
--- ---
@ -329,3 +329,11 @@ If you wish to remove these files you must go into the deployment branch directl
### Debugging 🐝 ### Debugging 🐝
If you'd like to enable action debugging you can set the `ACTIONS_STEP_DEBUG` environment variable to true within the [Settings/Secrets](https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets#creating-encrypted-secrets) menu. If you're using this action in your own project as a node module via yarn or npm **you may expose your secrets if you toggle this on in a production environment**. You can learn more about debugging GitHub actions [here](https://github.com/actions/toolkit/blob/master/docs/action-debugging.md). If you'd like to enable action debugging you can set the `ACTIONS_STEP_DEBUG` environment variable to true within the [Settings/Secrets](https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets#creating-encrypted-secrets) menu. If you're using this action in your own project as a node module via yarn or npm **you may expose your secrets if you toggle this on in a production environment**. You can learn more about debugging GitHub actions [here](https://github.com/actions/toolkit/blob/master/docs/action-debugging.md).
---
## Support 💖
This project would not be possible without all of our fantastic [contributors](https://github.com/JamesIves/github-pages-deploy-action/graphs/contributors).
If you'd like to support the maintenance and upkeep of this project you can [donate via GitHub Sponsors](https://github.com/sponsors/JamesIves). This project is distributed under the [MIT](https://github.com/JamesIves/github-pages-deploy-action/blob/dev/LICENSE) license.

View File

@ -23,6 +23,7 @@ jest.mock('@actions/io', () => ({
})) }))
jest.mock('../src/execute', () => ({ jest.mock('../src/execute', () => ({
__esModule: true,
execute: jest.fn() execute: jest.fn()
})) }))
@ -32,216 +33,6 @@ describe('git', () => {
}) })
describe('init', () => { 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 fail if the build folder begins with a /', async () => {
Object.assign(action, {
silent: false,
accessToken: '123',
repositoryPath: 'JamesIves/github-pages-deploy-action',
branch: 'branch',
folder: '/',
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: Incorrectly formatted build folder. The deployment folder cannot be prefixed with '/' or './'. Instead reference the folder name directly. ❌"
)
}
})
it('should fail if the build folder begins with a ./', async () => {
Object.assign(action, {
silent: false,
accessToken: '123',
branch: 'branch',
folder: './',
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: Incorrectly formatted build folder. The deployment folder cannot be prefixed with '/' or './'. Instead reference the folder name directly. ❌"
)
}
})
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 () => { it('should stash changes if preserve is true', async () => {
Object.assign(action, { Object.assign(action, {
silent: false, silent: false,
@ -258,9 +49,36 @@ describe('git', () => {
}) })
await init(action) await init(action)
expect(execute).toBeCalledTimes(7) expect(execute).toBeCalledTimes(7)
}) })
it('should catch when a function throws an error', async () => {
;(execute as jest.Mock).mockImplementationOnce(() => {
throw new Error('Mocked throw')
})
Object.assign(action, {
silent: false,
repositoryPath: 'JamesIves/github-pages-deploy-action',
accessToken: '123',
branch: 'branch',
folder: '.',
preserve: true,
isTest: true,
pusher: {
name: 'asd',
email: 'as@cat'
}
})
try {
await init(action)
} catch (error) {
expect(error.message).toBe(
'There was an error initializing the repository: Mocked throw ❌'
)
}
})
}) })
describe('generateBranch', () => { describe('generateBranch', () => {
@ -280,11 +98,15 @@ describe('git', () => {
expect(execute).toBeCalledTimes(6) expect(execute).toBeCalledTimes(6)
}) })
it('should fail if there is no branch', async () => { it('should catch when a function throws an error', async () => {
;(execute as jest.Mock).mockImplementationOnce(() => {
throw new Error('Mocked throw')
})
Object.assign(action, { Object.assign(action, {
silent: false, silent: false,
accessToken: '123', accessToken: '123',
branch: null, branch: 'branch',
folder: '.', folder: '.',
pusher: { pusher: {
name: 'asd', name: 'asd',
@ -294,9 +116,9 @@ describe('git', () => {
try { try {
await generateBranch(action) await generateBranch(action)
} catch (e) { } catch (error) {
expect(e.message).toMatch( expect(error.message).toBe(
'There was an error creating the deployment branch: Branch is required. ❌' 'There was an error creating the deployment branch: There was an error switching to the base branch: Mocked throw ❌ ❌'
) )
} }
}) })
@ -336,15 +158,17 @@ describe('git', () => {
expect(execute).toBeCalledTimes(1) expect(execute).toBeCalledTimes(1)
}) })
it('should fail if one of the required parameters is not available', async () => { it('should catch when a function throws an error', async () => {
;(execute as jest.Mock).mockImplementationOnce(() => {
throw new Error('Mocked throw')
})
Object.assign(action, { Object.assign(action, {
silent: false, silent: false,
baseBranch: '123', baseBranch: '123',
accessToken: null, accessToken: '123',
gitHubToken: null,
ssh: null,
branch: 'branch', branch: 'branch',
folder: null, folder: '.',
pusher: { pusher: {
name: 'asd', name: 'asd',
email: 'as@cat' email: 'as@cat'
@ -353,10 +177,9 @@ describe('git', () => {
try { try {
await switchToBaseBranch(action) await switchToBaseBranch(action)
} catch (e) { } catch (error) {
expect(execute).toBeCalledTimes(0) expect(error.message).toBe(
expect(e.message).toMatch( 'There was an error switching to the base branch: Mocked throw ❌'
'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. ❌'
) )
} }
}) })
@ -388,6 +211,7 @@ describe('git', () => {
Object.assign(action, { Object.assign(action, {
silent: false, silent: false,
folder: 'assets', folder: 'assets',
folderPath: 'assets',
branch: 'branch', branch: 'branch',
gitHubToken: '123', gitHubToken: '123',
lfs: true, lfs: true,
@ -407,26 +231,33 @@ describe('git', () => {
expect(response).toBe(Status.SUCCESS) expect(response).toBe(Status.SUCCESS)
}) })
it('should not ignore CNAME or nojekyll if they exist in the deployment folder', async () => { it('should appropriately move along if git stash errors', async () => {
;(execute as jest.Mock).mockImplementation(cmd => {
if (cmd === 'git stash apply') {
// Mocks the case where git stash apply errors.
throw new Error()
}
})
Object.assign(action, { Object.assign(action, {
silent: false, silent: false,
folder: 'assets', folder: 'assets',
folderPath: 'assets',
branch: 'branch', branch: 'branch',
gitHubToken: '123', gitHubToken: '123',
lfs: true,
preserve: true,
isTest: true,
pusher: { pusher: {
name: 'asd', name: 'asd',
email: 'as@cat' email: 'as@cat'
}, }
clean: true
}) })
const response = await deploy(action) const response = await deploy(action)
fs.createWriteStream('assets/.nojekyll')
fs.createWriteStream('assets/CNAME')
// Includes the call to generateBranch // Includes the call to generateBranch
expect(execute).toBeCalledTimes(12) expect(execute).toBeCalledTimes(14)
expect(rmRF).toBeCalledTimes(1) expect(rmRF).toBeCalledTimes(1)
expect(response).toBe(Status.SUCCESS) expect(response).toBe(Status.SUCCESS)
}) })
@ -434,14 +265,16 @@ describe('git', () => {
it('should execute commands with single commit toggled', async () => { it('should execute commands with single commit toggled', async () => {
Object.assign(action, { Object.assign(action, {
silent: false, silent: false,
folder: 'assets', folder: 'other',
folderPath: 'other',
branch: 'branch', branch: 'branch',
gitHubToken: '123', gitHubToken: '123',
singleCommit: true, singleCommit: true,
pusher: { pusher: {
name: 'asd', name: 'asd',
email: 'as@cat' email: 'as@cat'
} },
clean: true
}) })
await deploy(action) await deploy(action)
@ -451,11 +284,37 @@ describe('git', () => {
expect(rmRF).toBeCalledTimes(1) expect(rmRF).toBeCalledTimes(1)
}) })
it('should not ignore CNAME or nojekyll if they exist in the deployment folder', async () => {
Object.assign(action, {
silent: false,
folder: 'assets',
folderPath: 'assets',
branch: 'branch',
gitHubToken: '123',
pusher: {
name: 'asd',
email: 'as@cat'
},
clean: true
})
fs.createWriteStream('assets/.nojekyll')
fs.createWriteStream('assets/CNAME')
const response = await deploy(action)
// Includes the call to generateBranch
expect(execute).toBeCalledTimes(12)
expect(rmRF).toBeCalledTimes(1)
expect(response).toBe(Status.SUCCESS)
})
it('should execute commands with clean options, ommits sha commit message', async () => { it('should execute commands with clean options, ommits sha commit message', async () => {
process.env.GITHUB_SHA = '' process.env.GITHUB_SHA = ''
Object.assign(action, { Object.assign(action, {
silent: false, silent: false,
folder: 'assets', folder: 'other',
folderPath: 'other',
branch: 'branch', branch: 'branch',
gitHubToken: '123', gitHubToken: '123',
pusher: { pusher: {
@ -463,7 +322,8 @@ describe('git', () => {
email: 'as@cat' email: 'as@cat'
}, },
clean: true, clean: true,
cleanExclude: '["cat", "montezuma"]' cleanExclude: '["cat", "montezuma"]',
workspace: 'other'
}) })
await deploy(action) await deploy(action)
@ -477,6 +337,7 @@ describe('git', () => {
Object.assign(action, { Object.assign(action, {
silent: false, silent: false,
folder: 'assets', folder: 'assets',
folderPath: 'assets',
branch: 'branch', branch: 'branch',
gitHubToken: '123', gitHubToken: '123',
pusher: { pusher: {
@ -534,28 +395,28 @@ describe('git', () => {
expect(response).toBe(Status.SKIPPED) expect(response).toBe(Status.SKIPPED)
}) })
it('should throw an error if one of the required parameters is not available', async () => { it('should catch when a function throws an error', async () => {
;(execute as jest.Mock).mockImplementationOnce(() => {
throw new Error('Mocked throw')
})
Object.assign(action, { Object.assign(action, {
silent: false, silent: false,
folder: 'assets', folder: 'assets',
branch: 'branch', branch: 'branch',
ssh: null, gitHubToken: '123',
accessToken: null, lfs: true,
gitHubToken: null,
pusher: { pusher: {
name: 'asd', name: 'asd',
email: 'as@cat' 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 { try {
await deploy(action) await deploy(action)
} catch (e) { } catch (error) {
expect(execute).toBeCalledTimes(1) expect(error.message).toBe(
expect(rmRF).toBeCalledTimes(1) 'The deploy step encountered an error: Mocked throw ❌'
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. ❌'
) )
} }
}) })

View File

@ -1,8 +1,11 @@
import {ActionInterface} from '../src/constants'
import { import {
isNullOrUndefined, isNullOrUndefined,
generateTokenType, generateTokenType,
generateRepositoryPath, generateRepositoryPath,
suppressSensitiveInformation generateFolderPath,
suppressSensitiveInformation,
checkParameters
} from '../src/util' } from '../src/util'
describe('util', () => { describe('util', () => {
@ -21,13 +24,17 @@ describe('util', () => {
const value = 'montezuma' const value = 'montezuma'
expect(isNullOrUndefined(value)).toBeFalsy() expect(isNullOrUndefined(value)).toBeFalsy()
}) })
it('should return false if the value is empty string', async () => {
const value = ''
expect(isNullOrUndefined(value)).toBeTruthy()
})
}) })
describe('generateTokenType', () => { describe('generateTokenType', () => {
it('should return ssh if ssh is provided', async () => { it('should return ssh if ssh is provided', async () => {
const action = { const action = {
branch: '123', branch: '123',
root: '.',
workspace: 'src/', workspace: 'src/',
folder: 'build', folder: 'build',
gitHubToken: null, gitHubToken: null,
@ -41,7 +48,6 @@ describe('util', () => {
it('should return access token if access token is provided', async () => { it('should return access token if access token is provided', async () => {
const action = { const action = {
branch: '123', branch: '123',
root: '.',
workspace: 'src/', workspace: 'src/',
folder: 'build', folder: 'build',
gitHubToken: null, gitHubToken: null,
@ -55,7 +61,6 @@ describe('util', () => {
it('should return github token if github token is provided', async () => { it('should return github token if github token is provided', async () => {
const action = { const action = {
branch: '123', branch: '123',
root: '.',
workspace: 'src/', workspace: 'src/',
folder: 'build', folder: 'build',
gitHubToken: '123', gitHubToken: '123',
@ -69,7 +74,6 @@ describe('util', () => {
it('should return ... if no token is provided', async () => { it('should return ... if no token is provided', async () => {
const action = { const action = {
branch: '123', branch: '123',
root: '.',
workspace: 'src/', workspace: 'src/',
folder: 'build', folder: 'build',
gitHubToken: null, gitHubToken: null,
@ -86,7 +90,6 @@ describe('util', () => {
const action = { const action = {
repositoryName: 'JamesIves/github-pages-deploy-action', repositoryName: 'JamesIves/github-pages-deploy-action',
branch: '123', branch: '123',
root: '.',
workspace: 'src/', workspace: 'src/',
folder: 'build', folder: 'build',
gitHubToken: null, gitHubToken: null,
@ -103,7 +106,6 @@ describe('util', () => {
const action = { const action = {
repositoryName: 'JamesIves/github-pages-deploy-action', repositoryName: 'JamesIves/github-pages-deploy-action',
branch: '123', branch: '123',
root: '.',
workspace: 'src/', workspace: 'src/',
folder: 'build', folder: 'build',
gitHubToken: null, gitHubToken: null,
@ -120,7 +122,6 @@ describe('util', () => {
const action = { const action = {
repositoryName: 'JamesIves/github-pages-deploy-action', repositoryName: 'JamesIves/github-pages-deploy-action',
branch: '123', branch: '123',
root: '.',
workspace: 'src/', workspace: 'src/',
folder: 'build', folder: 'build',
gitHubToken: '123', gitHubToken: '123',
@ -140,7 +141,6 @@ describe('util', () => {
repositoryPath: repositoryPath:
'https://x-access-token:supersecret999%%%@github.com/anothersecret123333', 'https://x-access-token:supersecret999%%%@github.com/anothersecret123333',
branch: '123', branch: '123',
root: '.',
workspace: 'src/', workspace: 'src/',
folder: 'build', folder: 'build',
accessToken: 'supersecret999%%%', accessToken: 'supersecret999%%%',
@ -160,7 +160,6 @@ describe('util', () => {
repositoryPath: repositoryPath:
'https://x-access-token:supersecret999%%%@github.com/anothersecret123333', 'https://x-access-token:supersecret999%%%@github.com/anothersecret123333',
branch: '123', branch: '123',
root: '.',
workspace: 'src/', workspace: 'src/',
folder: 'build', folder: 'build',
accessToken: 'supersecret999%%%', accessToken: 'supersecret999%%%',
@ -177,4 +176,154 @@ describe('util', () => {
}) })
}) })
}) })
describe('generateFolderPath', () => {
it('should return absolute path if folder name is provided', () => {
const action = {
branch: '123',
workspace: 'src/',
folder: 'build',
gitHubToken: null,
accessToken: null,
ssh: null,
silent: false
}
expect(generateFolderPath(action)).toEqual('src/build')
})
it('should return original path if folder name begins with /', () => {
const action = {
branch: '123',
workspace: 'src/',
folder: '/home/user/repo/build',
gitHubToken: null,
accessToken: null,
ssh: null,
silent: false
}
expect(generateFolderPath(action)).toEqual('/home/user/repo/build')
})
it('should process as relative path if folder name begins with ./', () => {
const action = {
branch: '123',
workspace: 'src/',
folder: './build',
gitHubToken: null,
accessToken: null,
ssh: null,
silent: false
}
expect(generateFolderPath(action)).toEqual('src/build')
})
it('should return absolute path if folder name begins with ~', () => {
const action = {
branch: '123',
workspace: 'src/',
folder: '~/repo/build',
gitHubToken: null,
accessToken: null,
ssh: null,
silent: false
}
process.env.HOME = '/home/user'
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 directory you're trying to deploy named notARealFolder doesn't exist. Please double check the path and any prerequisite build scripts and try again. ❗`
)
}
})
})
}) })

View File

@ -2,12 +2,12 @@
"name": "@jamesives/github-pages-deploy-action", "name": "@jamesives/github-pages-deploy-action",
"description": "GitHub action for building a project and deploying it to GitHub pages.", "description": "GitHub action for building a project and deploying it to GitHub pages.",
"author": "James Ives <iam@jamesiv.es> (https://jamesiv.es)", "author": "James Ives <iam@jamesiv.es> (https://jamesiv.es)",
"version": "3.6.1", "version": "3.6.2",
"license": "MIT", "license": "MIT",
"main": "lib/lib.js", "main": "lib/lib.js",
"types": "lib/lib.d.ts", "types": "lib/lib.d.ts",
"scripts": { "scripts": {
"build": "rm -rf lib && tsc --declaration", "build": "rimraf lib && tsc --declaration",
"test": "jest", "test": "jest",
"lint": "eslint src/**/*.ts", "lint": "eslint src/**/*.ts",
"format": "prettier --write './**/*.ts'" "format": "prettier --write './**/*.ts'"
@ -40,14 +40,15 @@
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "26.0.14", "@types/jest": "26.0.14",
"@types/node": "14.11.2", "@types/node": "14.11.8",
"eslint": "7.9.0", "eslint": "7.11.0",
"eslint-plugin-github": "3.4.1", "eslint-plugin-github": "4.1.1",
"eslint-plugin-jest": "24.0.2", "eslint-plugin-jest": "24.1.0",
"eslint-plugin-prettier": "^3.1.2", "eslint-plugin-prettier": "^3.1.2",
"jest": "25.5.4", "jest": "25.5.4",
"jest-circus": "26.4.2", "jest-circus": "26.5.3",
"prettier": "2.1.2", "prettier": "2.1.2",
"rimraf": "^3.0.2",
"ts-jest": "25.5.1", "ts-jest": "25.5.1",
"typescript": "3.9.7" "typescript": "3.9.7"
} }

View File

@ -24,6 +24,8 @@ export interface ActionInterface {
email?: string email?: string
/** The folder to deploy. */ /** The folder to deploy. */
folder: string folder: string
/** The auto generated folder path. */
folderPath?: string
/** GitHub deployment token. */ /** GitHub deployment token. */
gitHubToken?: string | null gitHubToken?: string | null
/** Determines if the action is running in test mode or not. */ /** Determines if the action is running in test mode or not. */
@ -38,8 +40,6 @@ export interface ActionInterface {
repositoryName?: string repositoryName?: string
/** The fully qualified repositpory path, this gets auto generated if repositoryName is provided. */ /** The fully qualified repositpory path, this gets auto generated if repositoryName is provided. */
repositoryPath?: string 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. */ /** Wipes the commit history from the deployment branch in favor of a single commit. */
singleCommit?: boolean | null singleCommit?: boolean | null
/** Determines if the action should run in silent mode or not. */ /** Determines if the action should run in silent mode or not. */
@ -54,6 +54,26 @@ export interface ActionInterface {
workspace: string workspace: string
} }
/** The minimum required values to run the action as a node module. */
export interface NodeActionInterface {
/** Deployment access token. */
accessToken?: string | null
/** The branch that the action should deploy to. */
branch: string
/** The folder to deploy. */
folder: string
/** GitHub deployment token. */
gitHubToken?: string | null
/** The repository path, for example JamesIves/github-pages-deploy-action. */
repositoryName: string
/** Determines if the action should run in silent mode or not. */
silent: boolean
/** Set to true if you're using an ssh client in your build step. */
ssh?: boolean | null
/** The folder where your deployment project lives. */
workspace: string
}
/* Required action data that gets initialized when running within the GitHub Actions environment. */ /* Required action data that gets initialized when running within the GitHub Actions environment. */
export const action: ActionInterface = { export const action: ActionInterface = {
accessToken: getInput('ACCESS_TOKEN'), accessToken: getInput('ACCESS_TOKEN'),
@ -95,7 +115,6 @@ export const action: ActionInterface = {
: repository && repository.full_name : repository && repository.full_name
? repository.full_name ? repository.full_name
: process.env.GITHUB_REPOSITORY, : process.env.GITHUB_REPOSITORY,
root: '.',
singleCommit: !isNullOrUndefined(getInput('SINGLE_COMMIT')) singleCommit: !isNullOrUndefined(getInput('SINGLE_COMMIT'))
? getInput('SINGLE_COMMIT').toLowerCase() === 'true' ? getInput('SINGLE_COMMIT').toLowerCase() === 'true'
: false, : false,
@ -109,6 +128,12 @@ export const action: ActionInterface = {
workspace: process.env.GITHUB_WORKSPACE || '' workspace: process.env.GITHUB_WORKSPACE || ''
} }
/** Types for the required action parameters. */
export type RequiredActionParameters = Pick<
ActionInterface,
'accessToken' | 'gitHubToken' | 'ssh' | 'branch' | 'folder'
>
/** Status codes for the action. */ /** Status codes for the action. */
export enum Status { export enum Status {
SUCCESS = 'success', SUCCESS = 'success',

View File

@ -3,17 +3,11 @@ import {mkdirP, rmRF} from '@actions/io'
import fs from 'fs' import fs from 'fs'
import {ActionInterface, Status} from './constants' import {ActionInterface, Status} from './constants'
import {execute} from './execute' import {execute} from './execute'
import { import {isNullOrUndefined, suppressSensitiveInformation} from './util'
hasRequiredParameters,
isNullOrUndefined,
suppressSensitiveInformation
} from './util'
/* Initializes git in the workspace. */ /* Initializes git in the workspace. */
export async function init(action: ActionInterface): Promise<void | Error> { export async function init(action: ActionInterface): Promise<void | Error> {
try { try {
hasRequiredParameters(action)
info(`Deploying using ${action.tokenType}… 🔑`) info(`Deploying using ${action.tokenType}… 🔑`)
info('Configuring git…') info('Configuring git…')
@ -72,8 +66,6 @@ export async function switchToBaseBranch(
action: ActionInterface action: ActionInterface
): Promise<void> { ): Promise<void> {
try { try {
hasRequiredParameters(action)
await execute( await execute(
`git checkout --progress --force ${ `git checkout --progress --force ${
action.baseBranch ? action.baseBranch : action.defaultBranch action.baseBranch ? action.baseBranch : action.defaultBranch
@ -94,8 +86,6 @@ export async function switchToBaseBranch(
/* Generates the branch if it doesn't exist on the remote. */ /* Generates the branch if it doesn't exist on the remote. */
export async function generateBranch(action: ActionInterface): Promise<void> { export async function generateBranch(action: ActionInterface): Promise<void> {
try { try {
hasRequiredParameters(action)
info(`Creating the ${action.branch} branch…`) info(`Creating the ${action.branch} branch…`)
await switchToBaseBranch(action) await switchToBaseBranch(action)
@ -139,8 +129,6 @@ export async function deploy(action: ActionInterface): Promise<Status> {
info('Starting to commit changes…') info('Starting to commit changes…')
try { try {
hasRequiredParameters(action)
const commitMessage = !isNullOrUndefined(action.commitMessage) const commitMessage = !isNullOrUndefined(action.commitMessage)
? (action.commitMessage as string) ? (action.commitMessage as string)
: `Deploying to ${action.branch} from ${action.baseBranch} ${ : `Deploying to ${action.branch} from ${action.baseBranch} ${
@ -185,10 +173,6 @@ export async function deploy(action: ActionInterface): Promise<Status> {
try { try {
await execute(`git stash apply`, action.workspace, action.silent) await execute(`git stash apply`, action.workspace, action.silent)
if (action.isTest) {
throw new Error()
}
} catch { } catch {
info('Unable to apply from stash, continuing…') info('Unable to apply from stash, continuing…')
} }
@ -229,22 +213,24 @@ export async function deploy(action: ActionInterface): Promise<Status> {
Allows the user to specify the root if '.' is provided. Allows the user to specify the root if '.' is provided.
rsync is used to prevent file duplication. */ rsync is used to prevent file duplication. */
await execute( await execute(
`rsync -q -av --checksum --progress ${action.folder}/. ${ `rsync -q -av --checksum --progress ${action.folderPath}/. ${
action.targetFolder action.targetFolder
? `${temporaryDeploymentDirectory}/${action.targetFolder}` ? `${temporaryDeploymentDirectory}/${action.targetFolder}`
: temporaryDeploymentDirectory : temporaryDeploymentDirectory
} ${ } ${
action.clean action.clean
? `--delete ${excludes} ${ ? `--delete ${excludes} ${
!fs.existsSync(`${action.folder}/CNAME`) ? '--exclude CNAME' : '' !fs.existsSync(`${action.folderPath}/CNAME`)
? '--exclude CNAME'
: ''
} ${ } ${
!fs.existsSync(`${action.folder}/.nojekyll`) !fs.existsSync(`${action.folderPath}/.nojekyll`)
? '--exclude .nojekyll' ? '--exclude .nojekyll'
: '' : ''
}` }`
: '' : ''
} --exclude .ssh --exclude .git --exclude .github ${ } --exclude .ssh --exclude .git --exclude .github ${
action.folder === action.root action.folderPath === action.workspace
? `--exclude ${temporaryDeploymentDirectory}` ? `--exclude ${temporaryDeploymentDirectory}`
: '' : ''
}`, }`,

View File

@ -1,14 +1,19 @@
import {exportVariable, info, setFailed} from '@actions/core' import {exportVariable, info, setFailed} from '@actions/core'
import {action, ActionInterface, Status} from './constants' import {ActionInterface, Status, NodeActionInterface} from './constants'
import {deploy, generateBranch, init} from './git' import {deploy, init} from './git'
import {generateRepositoryPath, generateTokenType} from './util' import {
generateFolderPath,
checkParameters,
generateRepositoryPath,
generateTokenType
} from './util'
/** Initializes and runs the action. /** Initializes and runs the action.
* *
* @param {object} configuration - The action configuration. * @param {object} configuration - The action configuration.
*/ */
export default async function run( export default async function run(
configuration: ActionInterface configuration: ActionInterface | NodeActionInterface
): Promise<void> { ): Promise<void> {
let status: Status = Status.RUNNING let status: Status = Status.RUNNING
@ -17,20 +22,24 @@ export default async function run(
GitHub Pages Deploy Action 🚀 GitHub Pages Deploy Action 🚀
🚀 Getting Started Guide: https://github.com/marketplace/actions/deploy-to-github-pages 🚀 Getting Started Guide: https://github.com/marketplace/actions/deploy-to-github-pages
FAQ/Wiki: https://github.com/JamesIves/github-pages-deploy-action/wiki Discussions / Q&A: https://github.com/JamesIves/github-pages-deploy-action/discussions
🔧 Support: https://github.com/JamesIves/github-pages-deploy-action/issues 🔧 Report a Bug: https://github.com/JamesIves/github-pages-deploy-action/issues
Contribute: https://github.com/JamesIves/github-pages-deploy-action/blob/dev/CONTRIBUTING.md
📣 Maintained by James Ives (https://jamesiv.es)`) 📣 Maintained by James Ives: https://jamesiv.es
💖 Support: https://github.com/sponsors/JamesIves`)
info('Checking configuration and starting deployment… 🚦') info('Checking configuration and starting deployment… 🚦')
const settings = { const settings: ActionInterface = {
...action,
...configuration ...configuration
} }
// Defines the repository paths and token types. // Defines the repository/folder paths and token types.
// Also verifies that the action has all of the required parameters.
settings.folderPath = generateFolderPath(settings)
checkParameters(settings)
settings.repositoryPath = generateRepositoryPath(settings) settings.repositoryPath = generateRepositoryPath(settings)
settings.tokenType = generateTokenType(settings) settings.tokenType = generateTokenType(settings)
@ -53,5 +62,3 @@ export default async function run(
exportVariable('DEPLOYMENT_STATUS', status) exportVariable('DEPLOYMENT_STATUS', status)
} }
} }
export {init, deploy, generateBranch, ActionInterface}

View File

@ -1,6 +1,9 @@
import {existsSync} from 'fs'
import path from 'path'
import {isDebug} from '@actions/core' import {isDebug} from '@actions/core'
import {ActionInterface} from './constants' import {ActionInterface, RequiredActionParameters} from './constants'
/* Replaces all instances of a match in a string. */
const replaceAll = (input: string, find: string, replace: string): string => const replaceAll = (input: string, find: string, replace: string): string =>
input.split(find).join(replace) input.split(find).join(replace)
@ -26,31 +29,46 @@ export const generateRepositoryPath = (action: ActionInterface): string =>
action.accessToken || `x-access-token:${action.gitHubToken}` action.accessToken || `x-access-token:${action.gitHubToken}`
}@github.com/${action.repositoryName}.git` }@github.com/${action.repositoryName}.git`
/* Genetate absolute folder path by the provided folder name */
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)
}
/* Checks for the required tokens and formatting. Throws an error if any case is matched. */ /* Checks for the required tokens and formatting. Throws an error if any case is matched. */
export const hasRequiredParameters = (action: ActionInterface): void => { const hasRequiredParameters = <K extends keyof RequiredActionParameters>(
if ( action: ActionInterface,
(isNullOrUndefined(action.accessToken) && params: K[]
isNullOrUndefined(action.gitHubToken) && ): boolean => {
isNullOrUndefined(action.ssh)) || const nonNullParams = params.filter(
isNullOrUndefined(action.repositoryPath) || param => !isNullOrUndefined(action[param])
(action.accessToken && action.accessToken === '') )
) { return Boolean(nonNullParams.length)
}
/* Verifies the action has the required parameters to run, otherwise throw an error. */
export const checkParameters = (action: ActionInterface): void => {
if (!hasRequiredParameters(action, ['accessToken', 'gitHubToken', 'ssh'])) {
throw new Error( 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.' '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.') 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.') throw new Error('You must provide the action with a folder to deploy.')
} }
if (action.folder.startsWith('/') || action.folder.startsWith('./')) { if (!existsSync(action.folderPath as string)) {
throw new Error( throw new Error(
"Incorrectly formatted build folder. The deployment folder cannot be prefixed with '/' or './'. Instead reference the folder name directly." `The directory you're trying to deploy named ${action.folderPath} doesn't exist. Please double check the path and any prerequisite build scripts and try again. ❗`
) )
} }
} }

1226
yarn.lock

File diff suppressed because it is too large Load Diff