mirror of
https://github.com/JamesIves/github-pages-deploy-action.git
synced 2023-12-15 20:03:39 +08:00
Native SSH Key Support (#569)
* SSH Key Support 🔑
* Update ssh.ts
* Update src/ssh.ts
Co-authored-by: Axel Hecht <axel@pike.org>
* README fixes/etc
* Unit Tests & README
* ssh key
* Update README.md
* Update ssh.test.ts
* Update ssh.test.ts
* Update ssh.test.ts
* Update ssh.test.ts
* Update ssh.test.ts
* Update ssh.test.ts
* Update integration.yml
Co-authored-by: Axel Hecht <axel@pike.org>
This commit is contained in:
parent
e00d6bfda7
commit
64eb7112e4
33
.github/workflows/integration.yml
vendored
33
.github/workflows/integration.yml
vendored
@ -90,6 +90,30 @@ jobs:
|
|||||||
|
|
||||||
# Deploys using an SSH key.
|
# Deploys using an SSH key.
|
||||||
integration-ssh:
|
integration-ssh:
|
||||||
|
needs: integration-container
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Build and Deploy
|
||||||
|
uses: JamesIves/github-pages-deploy-action@releases/v4
|
||||||
|
with:
|
||||||
|
ssh-key: ${{ secrets.DEPLOY_KEY }}
|
||||||
|
branch: gh-pages
|
||||||
|
folder: integration
|
||||||
|
target-folder: cat/montezuma3
|
||||||
|
|
||||||
|
- name: Cleanup Generated Branch
|
||||||
|
uses: dawidd6/action-delete-branch@v2.0.1
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
branches: gh-pages
|
||||||
|
|
||||||
|
# Deploys using an SSH key.
|
||||||
|
integration-ssh-third-party-client:
|
||||||
needs: integration-container
|
needs: integration-container
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
@ -109,7 +133,7 @@ jobs:
|
|||||||
ssh: true
|
ssh: true
|
||||||
branch: gh-pages
|
branch: gh-pages
|
||||||
folder: integration
|
folder: integration
|
||||||
target-folder: cat/montezuma3
|
target-folder: cat/montezuma4
|
||||||
|
|
||||||
- name: Cleanup Generated Branch
|
- name: Cleanup Generated Branch
|
||||||
uses: dawidd6/action-delete-branch@v2.0.1
|
uses: dawidd6/action-delete-branch@v2.0.1
|
||||||
@ -131,15 +155,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Install SSH Client
|
|
||||||
uses: webfactory/ssh-agent@v0.4.1
|
|
||||||
with:
|
|
||||||
ssh-private-key: ${{ secrets.DEPLOY_KEY }}
|
|
||||||
|
|
||||||
- name: Build and Deploy
|
- name: Build and Deploy
|
||||||
uses: JamesIves/github-pages-deploy-action@releases/v4
|
uses: JamesIves/github-pages-deploy-action@releases/v4
|
||||||
with:
|
with:
|
||||||
ssh: true
|
ssh-key: ${{ secrets.DEPLOY_KEY }}
|
||||||
branch: gh-pages
|
branch: gh-pages
|
||||||
folder: integration
|
folder: integration
|
||||||
target-folder: cat/montezuma4
|
target-folder: cat/montezuma4
|
||||||
|
20
README.md
20
README.md
@ -132,7 +132,7 @@ By default the action does not need any token configuration and uses the provide
|
|||||||
| Key | Value Information | Type | Required |
|
| Key | Value Information | Type | Required |
|
||||||
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | -------- |
|
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | -------- |
|
||||||
| `token` | This option defaults to the repository scoped GitHub Token. However if you need more permissions for things such as deploying to another repository, you can add a Personal Access Token (PAT) here. This should be stored in the `secrets / with` menu **as a secret**. We reccomend using a service account with the least permissions neccersary and recommend when generating a new PAT that you select the least permission scopes neccersary. [Learn more about creating and using encrypted secrets here.](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets) | **No** |
|
| `token` | This option defaults to the repository scoped GitHub Token. However if you need more permissions for things such as deploying to another repository, you can add a Personal Access Token (PAT) here. This should be stored in the `secrets / with` menu **as a secret**. We reccomend using a service account with the least permissions neccersary and recommend when generating a new PAT that you select the least permission scopes neccersary. [Learn more about creating and using encrypted secrets here.](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets) | **No** |
|
||||||
| `ssh` | You can configure the action to deploy using SSH by setting this option to `true`. For more information on how to add your ssh key pair please refer to the [Using a Deploy Key section of this README](https://github.com/JamesIves/github-pages-deploy-action/tree/dev#using-an-ssh-deploy-key-). | `with` | **No** |
|
| `ssh-key` | You can configure the action to deploy using SSH by setting this option to a private SSH key stored **as a secret**. It can also be set to `true` to use an existing SSH client configuration. For more detailed information on how to add your ssh key pair please refer to the [Using a Deploy Key section of this README](https://github.com/JamesIves/github-pages-deploy-action/tree/dev#using-an-ssh-deploy-key-). | `with` | **No** |
|
||||||
|
|
||||||
#### Optional Choices
|
#### Optional Choices
|
||||||
|
|
||||||
@ -176,20 +176,15 @@ 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`.
|
||||||
|
|
||||||
With this configured you must add the `ssh-agent` step to your workflow and set `ssh` to `true` within the deploy action. There are several SSH actions available on the [GitHub marketplace](https://github.com/marketplace?type=actions) for you to choose from.
|
With this configured you can then set the `ssh-key` part of the action to your private key stored as a secret.
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
- name: Install SSH Client 🔑
|
|
||||||
uses: webfactory/ssh-agent@v0.4.1
|
|
||||||
with:
|
|
||||||
ssh-private-key: ${{ secrets.DEPLOY_KEY }}
|
|
||||||
|
|
||||||
- name: Deploy 🚀
|
- name: Deploy 🚀
|
||||||
uses: JamesIves/github-pages-deploy-action@3.7.1
|
uses: JamesIves/github-pages-deploy-action@3.7.1
|
||||||
with:
|
with:
|
||||||
ssh: true
|
|
||||||
branch: gh-pages
|
branch: gh-pages
|
||||||
folder: site
|
folder: site
|
||||||
|
ssh-key: ${{ secrets.DEPLOY_KEY }}
|
||||||
```
|
```
|
||||||
|
|
||||||
<details><summary>You can view a full example of this here.</summary>
|
<details><summary>You can view a full example of this here.</summary>
|
||||||
@ -215,11 +210,6 @@ jobs:
|
|||||||
npm install
|
npm install
|
||||||
npm run build
|
npm run build
|
||||||
|
|
||||||
- name: Install SSH Client 🔑
|
|
||||||
uses: webfactory/ssh-agent@v0.4.1 # This step installs the ssh client into the workflow run. There's many options available for this on the action marketplace.
|
|
||||||
with:
|
|
||||||
ssh-private-key: ${{ secrets.DEPLOY_KEY }}
|
|
||||||
|
|
||||||
- name: Deploy 🚀
|
- name: Deploy 🚀
|
||||||
uses: JamesIves/github-pages-deploy-action@3.7.1
|
uses: JamesIves/github-pages-deploy-action@3.7.1
|
||||||
with:
|
with:
|
||||||
@ -229,12 +219,14 @@ jobs:
|
|||||||
clean-exclude: |
|
clean-exclude: |
|
||||||
special-file.txt
|
special-file.txt
|
||||||
some/*.txt
|
some/*.txt
|
||||||
ssh: true # SSH must be set to true so the deploy action knows which protocol to deploy with.
|
ssh-key: ${{ secrets.DEPLOY_KEY }}
|
||||||
```
|
```
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
Alternatively if you've already configured the SSH client within a previous step you can set the `ssh-key` option to `true` to allow it to deploy using an existing SSH client. Instead of adjusting the client configuration it will simply switch to using GitHub's SSH endpoints.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Operating System Support 💿
|
### Operating System Support 💿
|
||||||
|
@ -60,6 +60,7 @@ describe('main', () => {
|
|||||||
folder: 'assets',
|
folder: 'assets',
|
||||||
branch: 'branch',
|
branch: 'branch',
|
||||||
token: '123',
|
token: '123',
|
||||||
|
sshKey: true,
|
||||||
pusher: {
|
pusher: {
|
||||||
name: 'asd',
|
name: 'asd',
|
||||||
email: 'as@cat'
|
email: 'as@cat'
|
||||||
@ -77,7 +78,7 @@ describe('main', () => {
|
|||||||
folder: 'assets',
|
folder: 'assets',
|
||||||
branch: 'branch',
|
branch: 'branch',
|
||||||
token: null,
|
token: null,
|
||||||
ssh: null,
|
sshKey: null,
|
||||||
pusher: {
|
pusher: {
|
||||||
name: 'asd',
|
name: 'asd',
|
||||||
email: 'as@cat'
|
email: 'as@cat'
|
||||||
|
103
__tests__/ssh.test.ts
Normal file
103
__tests__/ssh.test.ts
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import {mkdirP} from '@actions/io'
|
||||||
|
import {appendFileSync} from 'fs'
|
||||||
|
import {action, TestFlag} from '../src/constants'
|
||||||
|
import {execute} from '../src/execute'
|
||||||
|
import {configureSSH} from '../src/ssh'
|
||||||
|
|
||||||
|
const originalAction = JSON.stringify(action)
|
||||||
|
|
||||||
|
jest.mock('fs', () => ({
|
||||||
|
appendFileSync: jest.fn(),
|
||||||
|
existsSync: jest.fn()
|
||||||
|
}))
|
||||||
|
|
||||||
|
jest.mock('@actions/io', () => ({
|
||||||
|
rmRF: jest.fn(),
|
||||||
|
mkdirP: jest.fn()
|
||||||
|
}))
|
||||||
|
|
||||||
|
jest.mock('@actions/core', () => ({
|
||||||
|
setFailed: jest.fn(),
|
||||||
|
getInput: jest.fn(),
|
||||||
|
setOutput: jest.fn(),
|
||||||
|
isDebug: jest.fn(),
|
||||||
|
info: jest.fn()
|
||||||
|
}))
|
||||||
|
|
||||||
|
jest.mock('../src/execute', () => ({
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
__esModule: true,
|
||||||
|
execute: jest.fn()
|
||||||
|
}))
|
||||||
|
|
||||||
|
describe('configureSSH', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
Object.assign(action, JSON.parse(originalAction))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should skip client configuration if sshKey is set to true', async () => {
|
||||||
|
Object.assign(action, {
|
||||||
|
silent: false,
|
||||||
|
folder: 'assets',
|
||||||
|
branch: 'branch',
|
||||||
|
sshKey: true,
|
||||||
|
pusher: {
|
||||||
|
name: 'asd',
|
||||||
|
email: 'as@cat'
|
||||||
|
},
|
||||||
|
isTest: TestFlag.HAS_CHANGED_FILES
|
||||||
|
})
|
||||||
|
|
||||||
|
await configureSSH(action)
|
||||||
|
|
||||||
|
expect(execute).toBeCalledTimes(0)
|
||||||
|
expect(mkdirP).toBeCalledTimes(0)
|
||||||
|
expect(appendFileSync).toBeCalledTimes(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should configure the ssh client if a key is defined', async () => {
|
||||||
|
Object.assign(action, {
|
||||||
|
silent: false,
|
||||||
|
folder: 'assets',
|
||||||
|
branch: 'branch',
|
||||||
|
sshKey: '?=-----BEGIN 123 456\n 789',
|
||||||
|
pusher: {
|
||||||
|
name: 'asd',
|
||||||
|
email: 'as@cat'
|
||||||
|
},
|
||||||
|
isTest: TestFlag.HAS_CHANGED_FILES
|
||||||
|
})
|
||||||
|
|
||||||
|
await configureSSH(action)
|
||||||
|
|
||||||
|
expect(execute).toBeCalledTimes(4)
|
||||||
|
expect(mkdirP).toBeCalledTimes(1)
|
||||||
|
expect(appendFileSync).toBeCalledTimes(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should throw if something errors', async () => {
|
||||||
|
;(execute as jest.Mock).mockImplementationOnce(() => {
|
||||||
|
throw new Error('Mocked throw')
|
||||||
|
})
|
||||||
|
|
||||||
|
Object.assign(action, {
|
||||||
|
silent: false,
|
||||||
|
folder: 'assets',
|
||||||
|
branch: 'branch',
|
||||||
|
sshKey: 'real_key',
|
||||||
|
pusher: {
|
||||||
|
name: 'asd',
|
||||||
|
email: 'as@cat'
|
||||||
|
},
|
||||||
|
isTest: TestFlag.HAS_CHANGED_FILES
|
||||||
|
})
|
||||||
|
|
||||||
|
try {
|
||||||
|
await configureSSH(action)
|
||||||
|
} catch (error) {
|
||||||
|
expect(error.message).toBe(
|
||||||
|
'The ssh client configuration encountered an error: Mocked throw ❌'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
@ -38,7 +38,7 @@ describe('util', () => {
|
|||||||
workspace: 'src/',
|
workspace: 'src/',
|
||||||
folder: 'build',
|
folder: 'build',
|
||||||
token: null,
|
token: null,
|
||||||
ssh: true,
|
sshKey: 'real_token',
|
||||||
silent: false,
|
silent: false,
|
||||||
isTest: TestFlag.NONE
|
isTest: TestFlag.NONE
|
||||||
}
|
}
|
||||||
@ -51,7 +51,7 @@ describe('util', () => {
|
|||||||
workspace: 'src/',
|
workspace: 'src/',
|
||||||
folder: 'build',
|
folder: 'build',
|
||||||
token: '123',
|
token: '123',
|
||||||
ssh: null,
|
sshKey: null,
|
||||||
silent: false,
|
silent: false,
|
||||||
isTest: TestFlag.NONE
|
isTest: TestFlag.NONE
|
||||||
}
|
}
|
||||||
@ -64,7 +64,7 @@ describe('util', () => {
|
|||||||
workspace: 'src/',
|
workspace: 'src/',
|
||||||
folder: 'build',
|
folder: 'build',
|
||||||
token: null,
|
token: null,
|
||||||
ssh: null,
|
sshKey: null,
|
||||||
silent: false,
|
silent: false,
|
||||||
isTest: TestFlag.NONE
|
isTest: TestFlag.NONE
|
||||||
}
|
}
|
||||||
@ -80,7 +80,7 @@ describe('util', () => {
|
|||||||
workspace: 'src/',
|
workspace: 'src/',
|
||||||
folder: 'build',
|
folder: 'build',
|
||||||
token: null,
|
token: null,
|
||||||
ssh: true,
|
sshKey: 'real_token',
|
||||||
silent: false,
|
silent: false,
|
||||||
isTest: TestFlag.NONE
|
isTest: TestFlag.NONE
|
||||||
}
|
}
|
||||||
@ -96,7 +96,7 @@ describe('util', () => {
|
|||||||
workspace: 'src/',
|
workspace: 'src/',
|
||||||
folder: 'build',
|
folder: 'build',
|
||||||
token: '123',
|
token: '123',
|
||||||
ssh: null,
|
sshKey: null,
|
||||||
silent: false,
|
silent: false,
|
||||||
isTest: TestFlag.NONE
|
isTest: TestFlag.NONE
|
||||||
}
|
}
|
||||||
@ -155,7 +155,7 @@ describe('util', () => {
|
|||||||
workspace: 'src/',
|
workspace: 'src/',
|
||||||
folder: 'build',
|
folder: 'build',
|
||||||
token: null,
|
token: null,
|
||||||
ssh: null,
|
sshKey: null,
|
||||||
silent: false,
|
silent: false,
|
||||||
isTest: TestFlag.NONE
|
isTest: TestFlag.NONE
|
||||||
}
|
}
|
||||||
@ -168,7 +168,7 @@ describe('util', () => {
|
|||||||
workspace: 'src/',
|
workspace: 'src/',
|
||||||
folder: '/home/user/repo/build',
|
folder: '/home/user/repo/build',
|
||||||
token: null,
|
token: null,
|
||||||
ssh: null,
|
sshKey: null,
|
||||||
silent: false,
|
silent: false,
|
||||||
isTest: TestFlag.NONE
|
isTest: TestFlag.NONE
|
||||||
}
|
}
|
||||||
@ -181,7 +181,7 @@ describe('util', () => {
|
|||||||
workspace: 'src/',
|
workspace: 'src/',
|
||||||
folder: './build',
|
folder: './build',
|
||||||
token: null,
|
token: null,
|
||||||
ssh: null,
|
sshKey: null,
|
||||||
silent: false,
|
silent: false,
|
||||||
isTest: TestFlag.NONE
|
isTest: TestFlag.NONE
|
||||||
}
|
}
|
||||||
@ -194,7 +194,7 @@ describe('util', () => {
|
|||||||
workspace: 'src/',
|
workspace: 'src/',
|
||||||
folder: '~/repo/build',
|
folder: '~/repo/build',
|
||||||
token: null,
|
token: null,
|
||||||
ssh: null,
|
sshKey: null,
|
||||||
silent: false,
|
silent: false,
|
||||||
isTest: TestFlag.NONE
|
isTest: TestFlag.NONE
|
||||||
}
|
}
|
||||||
|
10
action.yml
10
action.yml
@ -8,8 +8,14 @@ branding:
|
|||||||
icon: 'git-commit'
|
icon: 'git-commit'
|
||||||
color: 'orange'
|
color: 'orange'
|
||||||
inputs:
|
inputs:
|
||||||
ssh:
|
ssh-key:
|
||||||
description: 'You can configure the action to deploy using SSH by setting this option to true. More more information on how to add your ssh key pair please refer to the Using a Deploy Key section of this README.'
|
description: >
|
||||||
|
This option allows you to define a private SSH key to be used in conjunction with a repository deployment key to deploy using SSH.
|
||||||
|
The private key should be stored in the `secrets / with` menu **as a secret**. The public should be stored in the repositories deployment
|
||||||
|
keys menu and be given write access.
|
||||||
|
|
||||||
|
Alternatively you can set this field to `true` to enable SSH endpoints for deployment without configuring the ssh client. This can be useful if you've
|
||||||
|
already setup the SSH client using another package or action in a previous step.
|
||||||
required: false
|
required: false
|
||||||
|
|
||||||
token:
|
token:
|
||||||
|
@ -41,8 +41,8 @@ export interface ActionInterface {
|
|||||||
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. */
|
||||||
silent: boolean
|
silent: boolean
|
||||||
/** Set to true if you're using an ssh client in your build step. */
|
/** Defines an SSH private key that can be used during deployment. This can also be set to true to use SSH deployment endpoints if you've already configured the SSH client outside of this package. */
|
||||||
ssh?: boolean | null
|
sshKey?: string | boolean | null
|
||||||
/** If you'd like to push the contents of the deployment folder into a specific directory on the deployment branch you can specify it here. */
|
/** If you'd like to push the contents of the deployment folder into a specific directory on the deployment branch you can specify it here. */
|
||||||
targetFolder?: string
|
targetFolder?: string
|
||||||
/** Deployment token. */
|
/** Deployment token. */
|
||||||
@ -65,10 +65,11 @@ export interface NodeActionInterface {
|
|||||||
token?: string | null
|
token?: string | null
|
||||||
/** Determines if the action should run in silent mode or not. */
|
/** Determines if the action should run in silent mode or not. */
|
||||||
silent: boolean
|
silent: boolean
|
||||||
/** Set to true if you're using an ssh client in your build step. */
|
/** Defines an SSH private key that can be used during deployment. This can also be set to true to use SSH deployment endpoints if you've already configured the SSH client outside of this package. */
|
||||||
ssh?: boolean | null
|
sshKey?: string | boolean | null
|
||||||
/** The folder where your deployment project lives. */
|
/** The folder where your deployment project lives. */
|
||||||
workspace: string
|
workspace: string
|
||||||
|
/** Determines test scenarios the action is running in. */
|
||||||
isTest: TestFlag
|
isTest: TestFlag
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,9 +114,12 @@ export const action: ActionInterface = {
|
|||||||
silent: !isNullOrUndefined(getInput('silent'))
|
silent: !isNullOrUndefined(getInput('silent'))
|
||||||
? getInput('silent').toLowerCase() === 'true'
|
? getInput('silent').toLowerCase() === 'true'
|
||||||
: false,
|
: false,
|
||||||
ssh: !isNullOrUndefined(getInput('ssh'))
|
sshKey: isNullOrUndefined(getInput('ssh-key'))
|
||||||
? getInput('ssh').toLowerCase() === 'true'
|
? false
|
||||||
: false,
|
: !isNullOrUndefined(getInput('ssh-key')) &&
|
||||||
|
getInput('ssh-key').toLowerCase() === 'true'
|
||||||
|
? true
|
||||||
|
: getInput('ssh-key'),
|
||||||
targetFolder: getInput('target-folder'),
|
targetFolder: getInput('target-folder'),
|
||||||
workspace: process.env.GITHUB_WORKSPACE || ''
|
workspace: process.env.GITHUB_WORKSPACE || ''
|
||||||
}
|
}
|
||||||
@ -123,7 +127,7 @@ export const action: ActionInterface = {
|
|||||||
/** Types for the required action parameters. */
|
/** Types for the required action parameters. */
|
||||||
export type RequiredActionParameters = Pick<
|
export type RequiredActionParameters = Pick<
|
||||||
ActionInterface,
|
ActionInterface,
|
||||||
'token' | 'ssh' | 'branch' | 'folder' | 'isTest'
|
'token' | 'sshKey' | 'branch' | 'folder' | 'isTest'
|
||||||
>
|
>
|
||||||
|
|
||||||
/** Status codes for the action. */
|
/** Status codes for the action. */
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import {exportVariable, info, setFailed, setOutput} from '@actions/core'
|
import {exportVariable, info, setFailed, setOutput} from '@actions/core'
|
||||||
import {ActionInterface, Status, NodeActionInterface} from './constants'
|
import {ActionInterface, NodeActionInterface, Status} from './constants'
|
||||||
import {deploy, init} from './git'
|
import {deploy, init} from './git'
|
||||||
|
import {configureSSH} from './ssh'
|
||||||
import {
|
import {
|
||||||
generateFolderPath,
|
|
||||||
checkParameters,
|
checkParameters,
|
||||||
|
generateFolderPath,
|
||||||
generateRepositoryPath,
|
generateRepositoryPath,
|
||||||
generateTokenType
|
generateTokenType
|
||||||
} from './util'
|
} from './util'
|
||||||
@ -43,6 +44,10 @@ export default async function run(
|
|||||||
settings.repositoryPath = generateRepositoryPath(settings)
|
settings.repositoryPath = generateRepositoryPath(settings)
|
||||||
settings.tokenType = generateTokenType(settings)
|
settings.tokenType = generateTokenType(settings)
|
||||||
|
|
||||||
|
if (settings.sshKey) {
|
||||||
|
await configureSSH(settings)
|
||||||
|
}
|
||||||
|
|
||||||
await init(settings)
|
await init(settings)
|
||||||
status = await deploy(settings)
|
status = await deploy(settings)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
47
src/ssh.ts
Normal file
47
src/ssh.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import {info} from '@actions/core'
|
||||||
|
import {mkdirP} from '@actions/io'
|
||||||
|
import {appendFileSync} from 'fs'
|
||||||
|
import {ActionInterface} from './constants'
|
||||||
|
import {execute} from './execute'
|
||||||
|
import {suppressSensitiveInformation} from './util'
|
||||||
|
|
||||||
|
export async function configureSSH(action: ActionInterface): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (typeof action.sshKey === 'string') {
|
||||||
|
const sshDirectory = `${process.env['HOME']}/.ssh`
|
||||||
|
const sshKnownHostsDirectory = `${sshDirectory}/known_hosts`
|
||||||
|
|
||||||
|
// SSH fingerprints provided by GitHub: https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/githubs-ssh-key-fingerprints
|
||||||
|
const sshGitHubKnownHostRsa =
|
||||||
|
'\ngithub.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==\n'
|
||||||
|
const sshGitHubKnownHostDss =
|
||||||
|
'\ngithub.com ssh-dss AAAAB3NzaC1kc3MAAACBANGFW2P9xlGU3zWrymJgI/lKo//ZW2WfVtmbsUZJ5uyKArtlQOT2+WRhcg4979aFxgKdcsqAYW3/LS1T2km3jYW/vr4Uzn+dXWODVk5VlUiZ1HFOHf6s6ITcZvjvdbp6ZbpM+DuJT7Bw+h5Fx8Qt8I16oCZYmAPJRtu46o9C2zk1AAAAFQC4gdFGcSbp5Gr0Wd5Ay/jtcldMewAAAIATTgn4sY4Nem/FQE+XJlyUQptPWMem5fwOcWtSXiTKaaN0lkk2p2snz+EJvAGXGq9dTSWHyLJSM2W6ZdQDqWJ1k+cL8CARAqL+UMwF84CR0m3hj+wtVGD/J4G5kW2DBAf4/bqzP4469lT+dF2FRQ2L9JKXrCWcnhMtJUvua8dvnwAAAIB6C4nQfAA7x8oLta6tT+oCk2WQcydNsyugE8vLrHlogoWEicla6cWPk7oXSspbzUcfkjN3Qa6e74PhRkc7JdSdAlFzU3m7LMkXo1MHgkqNX8glxWNVqBSc0YRdbFdTkL0C6gtpklilhvuHQCdbgB3LBAikcRkDp+FCVkUgPC/7Rw==\n'
|
||||||
|
|
||||||
|
info(`Configuring SSH client… 🔑`)
|
||||||
|
|
||||||
|
await mkdirP(sshDirectory)
|
||||||
|
|
||||||
|
appendFileSync(sshKnownHostsDirectory, sshGitHubKnownHostRsa)
|
||||||
|
appendFileSync(sshKnownHostsDirectory, sshGitHubKnownHostDss)
|
||||||
|
|
||||||
|
// Initializes SSH agent.
|
||||||
|
await execute(`ssh-agent`, sshDirectory, action.silent)
|
||||||
|
|
||||||
|
// Adds the SSH key to the agent.
|
||||||
|
action.sshKey.split(/(?=-----BEGIN)/).map(async line => {
|
||||||
|
await execute(`ssh-add - ${line.trim()}\n`, sshDirectory, action.silent)
|
||||||
|
})
|
||||||
|
|
||||||
|
await execute(`ssh-add -l`, sshDirectory, action.silent)
|
||||||
|
} else {
|
||||||
|
info(`Skipping SSH client configuration… ⌚`)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(
|
||||||
|
`The ssh client configuration encountered an error: ${suppressSensitiveInformation(
|
||||||
|
error.message,
|
||||||
|
action
|
||||||
|
)} ❌`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
|
import {isDebug} from '@actions/core'
|
||||||
import {existsSync} from 'fs'
|
import {existsSync} from 'fs'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import {isDebug} from '@actions/core'
|
|
||||||
import {ActionInterface, RequiredActionParameters} from './constants'
|
import {ActionInterface, RequiredActionParameters} from './constants'
|
||||||
|
|
||||||
/* Replaces all instances of a match in a string. */
|
/* Replaces all instances of a match in a string. */
|
||||||
@ -13,11 +13,11 @@ export const isNullOrUndefined = (value: any): boolean =>
|
|||||||
|
|
||||||
/* Generates a token type used for the action. */
|
/* Generates a token type used for the action. */
|
||||||
export const generateTokenType = (action: ActionInterface): string =>
|
export const generateTokenType = (action: ActionInterface): string =>
|
||||||
action.ssh ? 'SSH Deploy Key' : action.token ? 'Deploy Token' : '…'
|
action.sshKey ? 'SSH Deploy Key' : action.token ? 'Deploy Token' : '…'
|
||||||
|
|
||||||
/* Generates a the repository path used to make the commits. */
|
/* Generates a the repository path used to make the commits. */
|
||||||
export const generateRepositoryPath = (action: ActionInterface): string =>
|
export const generateRepositoryPath = (action: ActionInterface): string =>
|
||||||
action.ssh
|
action.sshKey
|
||||||
? `git@github.com:${action.repositoryName}`
|
? `git@github.com:${action.repositoryName}`
|
||||||
: `https://${`x-access-token:${action.token}`}@github.com/${
|
: `https://${`x-access-token:${action.token}`}@github.com/${
|
||||||
action.repositoryName
|
action.repositoryName
|
||||||
@ -46,7 +46,7 @@ const hasRequiredParameters = <K extends keyof RequiredActionParameters>(
|
|||||||
|
|
||||||
/* Verifies the action has the required parameters to run, otherwise throw an error. */
|
/* Verifies the action has the required parameters to run, otherwise throw an error. */
|
||||||
export const checkParameters = (action: ActionInterface): void => {
|
export const checkParameters = (action: ActionInterface): void => {
|
||||||
if (!hasRequiredParameters(action, ['token', 'ssh'])) {
|
if (!hasRequiredParameters(action, ['token', 'sshKey'])) {
|
||||||
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.'
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user