[Issue-130] SSH Support (#140)

* First pass at deploy key support

* Needed

* More ssh

* Adds keyscan

* Another look at this

* More things....

* w

* Path to home

* w

* k

* Update git.ts

* ff

* dd

* test

* File Sync maybe

* Adds SSH Examples

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* x

* c

* Update README.md

* Integration tests

* Update integration-beta.yml

* More Changes
This commit is contained in:
James Ives 2020-01-19 13:15:19 -05:00 committed by GitHub
parent 5e22417cf8
commit 7cea1d5967
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 258 additions and 26 deletions

View File

@ -72,9 +72,61 @@ jobs:
BASE_BRANCH: dev
TARGET_FOLDER: montezuma2
# Deploys using an SSH key
integration-ssh:
needs: integration-container
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
persist-credentials: false
- name: Install SSH Client
uses: webfactory/ssh-agent@v0.2.0
with:
ssh-private-key: ${{ secrets.DEPLOY_KEY }}
- name: Build and Deploy
uses: JamesIves/github-pages-deploy-action@releases/v3-test
with:
SSH: true
BRANCH: gh-pages-beta
FOLDER: integration
BASE_BRANCH: dev
TARGET_FOLDER: montezuma3
# Deploys using a custom env.
integration-env:
needs: integration-ssh
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v1
with:
node-version: '10.x'
- name: Checkout
uses: actions/checkout@v2
with:
persist-credentials: false
- name: Install SSH Client
uses: webfactory/ssh-agent@v0.2.0
with:
ssh-private-key: ${{ secrets.DEPLOY_KEY }}
- name: Build and Deploy
uses: JamesIves/github-pages-deploy-action@releases/v3-test
with:
SSH: true
BRANCH: gh-pages-beta
FOLDER: integration
BASE_BRANCH: dev
TARGET_FOLDER: montezuma4
# Deploys using the CLEAN option.
integration-clean:
needs: [integration-checkout-v1, integration-checkout-v2, integration-container]
needs: [integration-checkout-v1, integration-checkout-v2, integration-container, integration-ssh, integration-env]
runs-on: ubuntu-latest
steps:
- name: Checkout
@ -93,7 +145,6 @@ jobs:
# Deploys to a branch that doesn't exist.
integration-branch-creation:
needs: [integration-checkout-v1, integration-checkout-v2]
runs-on: ubuntu-latest
steps:
- name: Checkout

View File

@ -69,9 +69,61 @@ jobs:
BASE_BRANCH: dev
TARGET_FOLDER: montezuma2
# Deploys using an SSH key.
integration-ssh:
needs: integration-container
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
persist-credentials: false
- name: Install SSH Client
uses: webfactory/ssh-agent@v0.2.0
with:
ssh-private-key: ${{ secrets.DEPLOY_KEY }}
- name: Build and Deploy
uses: JamesIves/github-pages-deploy-action@releases/v3
with:
SSH: true
BRANCH: gh-pages-beta
FOLDER: integration
BASE_BRANCH: dev
TARGET_FOLDER: montezuma3
# Deploys using a custom env.
integration-env:
needs: integration-ssh
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v1
with:
node-version: '10.x'
- name: Checkout
uses: actions/checkout@v2
with:
persist-credentials: false
- name: Install SSH Client
uses: webfactory/ssh-agent@v0.2.0
with:
ssh-private-key: ${{ secrets.DEPLOY_KEY }}
- name: Build and Deploy
uses: JamesIves/github-pages-deploy-action@releases/v3
with:
SSH: true
BRANCH: gh-pages-beta
FOLDER: integration
BASE_BRANCH: dev
TARGET_FOLDER: montezuma4
# Deploys using the CLEAN option.
integration-clean:
needs: [integration-checkout-v1, integration-checkout-v2, integration-container]
needs: [integration-checkout-v1, integration-checkout-v2, integration-container, integration-ssh, integration-env]
runs-on: ubuntu-latest
steps:
- name: Checkout
@ -90,7 +142,6 @@ jobs:
# Deploys to a branch that doesn't exist.
integration-branch-creation:
needs: [integration-checkout-v1, integration-checkout-v2]
runs-on: ubuntu-latest
steps:
- name: Checkout

6
.gitignore vendored
View File

@ -9,4 +9,8 @@ node_modules
## Registry
package-lock.json
yarn-error.log
yarn-error.log
## SSH
.ssh
*.pub

View File

@ -40,9 +40,80 @@ on:
- master
```
#### Operating System Support 💿
---
This action is primarily developed using [Ubuntu](https://ubuntu.com/). [In your workflow job configuration](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idruns-on) it's reccomended to set the `runs-on` property to `ubuntu-latest`.
### Using an SSH Deploy Key 🔑
If you'd prefer to use an SSH deploy key you must first generate a new SSH key by running the following terminal command, replacing the email with one connected to your GitHub account.
```
ssh-keygen -t rsa -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 repositories [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. 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.
```yml
- name: Install SSH Client
uses: webfactory/ssh-agent@v0.2.0
with:
ssh-private-key: ${{ secrets.DEPLOY_KEY }}
- name: Build and Deploy
uses: JamesIves/github-pages-deploy-action@releases/v3
with:
SSH: true
BRANCH: gh-pages
FOLDER: 'site'
```
<details><summary>You can view a full example of this here.</summary>
<p>
```yml
name: Build and Deploy
on:
push:
branches:
- master
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
persist-credentials: false
- name: Install
run: |
npm install
npm run-script build
- name: Install SSH Client
- uses: webfactory/ssh-agent@v0.2.0 # 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: Build and Deploy Repo
uses: JamesIves/github-pages-deploy-action@releases/v3-test
with:
BASE_BRANCH: master
BRANCH: gh-pages
FOLDER: 'build'
CLEAN: true
SSH: true # SSH must be set to true so the deploy action knows which protocol to deploy with.
```
</p>
</details>
------
### Operating System Support 💿
This action is primarily developed using [Ubuntu](https://ubuntu.com/). [In your workflow job configuration](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idruns-on) it's recommended to set the `runs-on` property to `ubuntu-latest`.
```yml
jobs:
@ -50,7 +121,7 @@ jobs:
runs-on: ubuntu-latest
```
Operating systems such as [Windows](https://www.microsoft.com/en-us/windows/) are not currently supported, however you can workaround this using [artifacts](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/persisting-workflow-data-using-artifacts). In your workflow configuration you can utilize the `actions/upload-artifact` and `actions/download-artifact` actions to move your project built on a Windows job to a secondary job that will handle the deployment.
If you're using an operating system such as [Windows](https://www.microsoft.com/en-us/windows/) you can workaround this using [artifacts](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/persisting-workflow-data-using-artifacts). In your workflow configuration you can utilize the `actions/upload-artifact` and `actions/download-artifact` actions to move your project built on a Windows job to a secondary job that will handle the deployment.
<details><summary>You can view an example of this pattern here.</summary>
<p>
@ -102,7 +173,9 @@ jobs:
</p>
</details>
#### Using a Container 📦
---
### Using a Container 📦
If you use a [container](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idcontainer) in your workflow you may need to run an additional step to install `rsync` as this action depends on it. You can view an example of this below.
@ -119,14 +192,27 @@ If you use a [container](https://help.github.com/en/actions/automating-your-work
The `with` portion of the workflow **must** be configured before the action will work. You can add these in the `with` section found in the examples above. Any `secrets` must be referenced using the bracket syntax and stored in the GitHub repositories `Settings/Secrets` menu. You can learn more about setting environment variables with GitHub actions [here](https://help.github.com/en/articles/workflow-syntax-for-github-actions#jobsjob_idstepsenv).
Below you'll find a description of what each option does.
#### Required Setup
One of the following deployment options must be configured.
| Key | Value Information | Type | Required |
| ------------- | ------------- | ------------- | ------------- |
| `ACCESS_TOKEN` | Depending on the repository permissions you may need to provide the action with a GitHub personal access token instead of the provided GitHub token in order to deploy. You can [learn more about how to generate one here](https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line). **This should be stored as a secret**. | `secrets / with` | **Yes** |
| `GITHUB_TOKEN` | In order for GitHub to trigger the rebuild of your page you must provide the action with the repositories provided GitHub token. This can be referenced in the workflow `yml` file by using `${{ secrets.GITHUB_TOKEN }}`. Only required if an access token is **not** provided. **Please note there is currently an issue affecting the use of this token, [you can learn more here](https://github.com/JamesIves/github-pages-deploy-action/issues/5)**. | `secrets / with` | **Yes** |
| `GITHUB_TOKEN` | In order for GitHub to trigger the rebuild of your page you must provide the action with the repositories provided GitHub token. This can be referenced in the workflow `yml` file by using `${{ secrets.GITHUB_TOKEN }}`. **Please note there is currently an issue affecting the use of this token which makes it so it only works with private repositories, [you can learn more here](https://github.com/JamesIves/github-pages-deploy-action/issues/5)**. | `secrets / with` | **Yes** |
| `SSH` | 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](). | `with` | **Yes** |
In addition to the deployment options you must also configure the following.
| Key | Value Information | Type | Required |
| ------------- | ------------- | ------------- | ------------- |
| `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** |
#### Optional Choices
| Key | Value Information | Type | Required |
| ------------- | ------------- | ------------- | ------------- |
| `TARGET_FOLDER` | 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. | `with` | **No** |
| `BASE_BRANCH` | The base branch of your repository which you'd like to checkout prior to deploying. This defaults to the current commit [SHA](http://en.wikipedia.org/wiki/SHA-1) that triggered the build followed by `master` if it doesn't exist. This is useful for making deployments from another branch, and also may be necessary when using a scheduled job. | `with` | **No** |
| `COMMIT_MESSAGE` | If you need to customize the commit message for an integration you can do so. | `with` | **No** |

View File

@ -12,8 +12,8 @@ const github = __importStar(require("@actions/github"));
const { pusher, repository } = github.context.payload;
exports.workspace = process.env.GITHUB_WORKSPACE;
exports.folder = core.getInput("FOLDER", { required: true });
exports.root = ".";
exports.isTest = process.env.UNIT_TEST;
exports.root = ".";
// Required action data.
exports.action = {
accessToken: core.getInput("ACCESS_TOKEN"),
@ -24,6 +24,7 @@ exports.action = {
clean: core.getInput("CLEAN"),
cleanExclude: core.getInput("CLEAN_EXCLUDE"),
defaultBranch: process.env.GITHUB_SHA ? process.env.GITHUB_SHA : "master",
ssh: core.getInput("SSH"),
email: pusher && pusher.email
? pusher.email
: `${process.env.GITHUB_ACTOR ||
@ -39,6 +40,16 @@ exports.action = {
: "GitHub Pages Deploy Action",
targetFolder: core.getInput("TARGET_FOLDER")
};
// Token Types
exports.tokenType = exports.action.ssh
? "SSH"
: exports.action.accessToken
? "Access Token"
: exports.action.gitHubToken
? "GitHub Token"
: "...";
// Repository path used for commits/pushes.
exports.repositoryPath = `https://${exports.action.accessToken ||
`x-access-token:${exports.action.gitHubToken}`}@github.com/${exports.action.gitHubRepository}.git`;
exports.repositoryPath = exports.action.ssh
? `git@github.com:${exports.action.gitHubRepository}`
: `https://${exports.action.accessToken ||
`x-access-token:${exports.action.gitHubToken}`}@github.com/${exports.action.gitHubRepository}.git`;

View File

@ -27,8 +27,16 @@ function init() {
return __awaiter(this, void 0, void 0, function* () {
try {
if (util_1.isNullOrUndefined(constants_1.action.accessToken) &&
util_1.isNullOrUndefined(constants_1.action.gitHubToken)) {
return core.setFailed("You must provide the action with either a Personal Access Token or the GitHub Token secret in order to deploy.");
util_1.isNullOrUndefined(constants_1.action.gitHubToken) &&
util_1.isNullOrUndefined(constants_1.action.ssh)) {
return core.setFailed("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.");
}
const sshAuthenticated = yield execute_1.execute(`ssh -T git@github.com | wc -l`, constants_1.workspace);
if (constants_1.action.ssh && !sshAuthenticated) {
return core.setFailed(`You're not authenticated with SSH. Please double check your deploy token configuration and try again.`);
}
else if (constants_1.action.ssh && sshAuthenticated) {
console.log("SSH is correctly authenticated, proceeding... 🔑");
}
if (constants_1.action.build.startsWith("/") || constants_1.action.build.startsWith("./")) {
return core.setFailed(`The deployment folder cannot be prefixed with '/' or './'. Instead reference the folder name directly.`);
@ -121,7 +129,7 @@ function deploy() {
? `${temporaryDeploymentDirectory}/${constants_1.action.targetFolder}`
: temporaryDeploymentDirectory} ${constants_1.action.clean
? `--delete ${excludes} --exclude CNAME --exclude .nojekyll`
: ""} --exclude .git --exclude .github ${constants_1.action.build === constants_1.root ? `--exclude ${temporaryDeploymentDirectory}` : ""}`, constants_1.workspace);
: ""} --exclude .ssh --exclude .git --exclude .github ${constants_1.action.build === constants_1.root ? `--exclude ${temporaryDeploymentDirectory}` : ""}`, constants_1.workspace);
const hasFilesToCommit = yield execute_1.execute(`git status --porcelain`, temporaryDeploymentDirectory);
if (!hasFilesToCommit && !constants_1.isTest) {
console.log("There is nothing to commit. Exiting... ✅");

View File

@ -5,8 +5,8 @@ const { pusher, repository } = github.context.payload;
export const workspace: any = process.env.GITHUB_WORKSPACE;
export const folder = core.getInput("FOLDER", { required: true });
export const root = ".";
export const isTest = process.env.UNIT_TEST;
export const root = ".";
// Required action data.
export const action = {
@ -18,6 +18,7 @@ export const action = {
clean: core.getInput("CLEAN"),
cleanExclude: core.getInput("CLEAN_EXCLUDE"),
defaultBranch: process.env.GITHUB_SHA ? process.env.GITHUB_SHA : "master",
ssh: core.getInput("SSH"),
email:
pusher && pusher.email
? pusher.email
@ -37,8 +38,19 @@ export const action = {
targetFolder: core.getInput("TARGET_FOLDER")
};
// Token Types
export const tokenType = action.ssh
? "SSH"
: action.accessToken
? "Access Token"
: action.gitHubToken
? "GitHub Token"
: "...";
// Repository path used for commits/pushes.
export const repositoryPath = `https://${action.accessToken ||
`x-access-token:${action.gitHubToken}`}@github.com/${
action.gitHubRepository
}.git`;
export const repositoryPath = action.ssh
? `git@github.com:${action.gitHubRepository}`
: `https://${action.accessToken ||
`x-access-token:${action.gitHubToken}`}@github.com/${
action.gitHubRepository
}.git`;

View File

@ -1,7 +1,14 @@
import * as core from "@actions/core";
import {
action,
isTest,
repositoryPath,
root,
tokenType,
workspace
} from "./constants";
import { execute } from "./execute";
import { isNullOrUndefined } from "./util";
import { workspace, action, root, repositoryPath, isTest } from "./constants";
/** Generates the branch if it doesn't exist on the remote.
* @returns {Promise}
@ -10,10 +17,11 @@ export async function init(): Promise<any> {
try {
if (
isNullOrUndefined(action.accessToken) &&
isNullOrUndefined(action.gitHubToken)
isNullOrUndefined(action.gitHubToken) &&
isNullOrUndefined(action.ssh)
) {
return core.setFailed(
"You must provide the action with either a Personal Access Token or the GitHub Token secret in order to deploy."
"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."
);
}
@ -23,6 +31,7 @@ export async function init(): Promise<any> {
);
}
console.log(`Deploying using ${tokenType}... 🔑`);
await execute(`git init`, workspace);
await execute(`git config user.name ${action.name}`, workspace);
await execute(`git config user.email ${action.email}`, workspace);
@ -129,7 +138,7 @@ export async function deploy(): Promise<any> {
action.clean
? `--delete ${excludes} --exclude CNAME --exclude .nojekyll`
: ""
} --exclude .git --exclude .github ${
} --exclude .ssh --exclude .git --exclude .github ${
action.build === root ? `--exclude ${temporaryDeploymentDirectory}` : ""
}`,
workspace