Compare commits

..

No commits in common. "main" and "v2.0.4" have entirely different histories.
main ... v2.0.4

26 changed files with 859 additions and 4998 deletions

View file

@ -1,11 +0,0 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
max_line_length = 120
tab_width = 4
trim_trailing_whitespace = true

View file

@ -1,25 +0,0 @@
module.exports = {
env: {
commonjs: true,
es6: true,
node: true
},
extends: [
'airbnb-base'
],
globals: {
Atomics: 'readonly',
SharedArrayBuffer: 'readonly'
},
parserOptions: {
ecmaVersion: 2018
},
rules: {
'comma-dangle': [
'error',
'never'
],
'no-console': 'off',
'object-curly-newline': 'off'
}
};

View file

@ -1,31 +0,0 @@
name: Build
on:
push:
branches: [ '**' ]
pull_request:
branches: [ $default-branch ]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
node-version: [20.x]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm ci
- name: Build Library
run: npm run build
- name: Run Tests
run: npm test --if-present

View file

@ -1,47 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ main ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]
schedule:
- cron: '20 12 * * 0'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
steps:
- name: Checkout
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
- run: |
npm ci
npm run build --if-present
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3

View file

@ -1,104 +0,0 @@
name: e2e Manual Test
on:
workflow_dispatch:
inputs:
ARGS:
description: 'ARGS'
required: true
default: '-rlgoDzvc -i --delete --chmod=ugo+rwX --progress'
EXCLUDE:
description: 'EXCLUDE'
required: true
default: 'skip_dir/, /node_modules/'
SSH_CMD_ARGS:
description: 'SSH_CMD_ARGS'
required: true
default: '-o StrictHostKeyChecking=no, -o UserKnownHostsFile=/dev/null'
env:
TEST_HOST_DOCKER: ./test
TEST_USER: test
jobs:
e2e-manual:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
# ----------------------------------------------------------------
# START E2E Test Specific - steps
# ----------------------------------------------------------------
- name: E2E Test Specific - Clean up old test files
run: |
docker stop ssh-host-container || true && docker rm ssh-host-container || true
- name: E2E Test Specific - Create ssh keys
run: |
echo $HOME
ls -la $HOME
ssh-keygen -m PEM -t rsa -b 4096 -f "$HOME/.ssh/id_rsa" -N ""
eval `ssh-agent -s`
ssh-add "$HOME/.ssh/id_rsa"
ssh-add -l
echo "SSH_PRIVATE_KEY<<EOF" >> $GITHUB_ENV
cat $HOME/.ssh/id_rsa >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
- name: E2E Test Specific - Build Host Server Image
working-directory: ${{ env.TEST_HOST_DOCKER }}
run: |
docker build \
-t ssh-host-image . \
--build-arg SSH_PUB_KEY="$(cat $HOME/.ssh/id_rsa.pub)"
docker run -d -p 8822:22 --name=ssh-host-container ssh-host-image
docker exec ssh-host-container sh -c "hostname --ip-address" > ip.txt
echo "REMOTE_HOST<<EOF" >> $GITHUB_ENV
cat ip.txt >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
shell: bash
- name: E2E Test Specific - Create project file
run: |
mkdir test_project2 && cd "$_"
truncate -s 5MB info2.txt
cd ../
mkdir test_project && cd "$_"
touch index.html
date +"%Y-%m-%d %H:%M:%S,%3N" >> index.html
truncate -s 50MB image.svg
truncate -s 5MB info.txt
truncate -s 500MB big_file.txt
mkdir skip_dir && cd "$_"
truncate -s 5MB text_in_skip_dir.txt
cd ../
cat index.html
echo "test_project:" && ls -lR
echo "skip_dir:" && ls -lR skip_dir
# ----------------------------------------------------------------
# END E2E Test Specific - steps
# ----------------------------------------------------------------
- name: e2e Test ssh-deploy action
uses: easingthemes/ssh-deploy@main
env:
# ENV Vars created in previous steps:
# SSH_PRIVATE_KEY: $EXAMPLE_SSH_PRIVATE_KEY
# REMOTE_HOST: $EXAMPLE_REMOTE_HOST
REMOTE_USER: ${{ env.TEST_USER }}
ARGS: ${{ github.event.inputs.ARGS }}
SSH_CMD_ARGS: ${{ github.event.inputs.SSH_CMD_ARGS }}
SOURCE: test_project/ test_project2/
TARGET: /var/www/html/
EXCLUDE: ${{ github.event.inputs.EXCLUDE }}
SCRIPT_BEFORE: |
whoami
ls -lR /var/www/html/
SCRIPT_AFTER: |
ls -lR /var/www/html/
echo $RSYNC_STDOUT

View file

@ -1,179 +0,0 @@
name: e2e Test
on:
push:
branches: [ 'main' ]
env:
TEST_HOST_DOCKER: ./test
TEST_USER: test
TEST_USER2: test2
jobs:
e2e:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
# : ---------------------------------------------------------------
# : START E2E Test Specific - steps
# : ----------------------------------------------------------------
- name: E2E Test Specific - Clean up old test files
run: |
docker stop ssh-host-container || true && docker rm ssh-host-container || true
- name: E2E Test Specific - Create ssh keys
run: |
echo $HOME
ls -la $HOME
ssh-keygen -m PEM -t rsa -b 4096 -f "$HOME/.ssh/id_rsa" -N ""
eval `ssh-agent -s`
ssh-add "$HOME/.ssh/id_rsa"
ssh-add -l
echo "SSH_PRIVATE_KEY<<EOF" >> $GITHUB_ENV
cat $HOME/.ssh/id_rsa >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
- name: E2E Test Specific - Build Host Server Image
working-directory: ${{ env.TEST_HOST_DOCKER }}
run: |
docker build \
-t ssh-host-image . \
--build-arg SSH_PUB_KEY="$(cat $HOME/.ssh/id_rsa.pub)"
docker run -d -p 8822:22 --name=ssh-host-container ssh-host-image
docker exec ssh-host-container sh -c "hostname --ip-address" > ip.txt
echo "REMOTE_HOST<<EOF" >> $GITHUB_ENV
cat ip.txt >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
shell: bash
- name: E2E Test Specific - Create project file
run: |
mkdir test_project2 && cd "$_"
truncate -s 5MB info2.txt
cd ../
mkdir test_project && cd "$_"
touch index.html
date +"%Y-%m-%d %H:%M:%S,%3N" >> index.html
truncate -s 50MB image.svg
truncate -s 5MB info.txt
truncate -s 500MB big_file.txt
mkdir skip_dir && cd "$_"
truncate -s 5MB text_in_skip_dir.txt
cd ../
cat index.html
echo "test_project:" && ls -lR
echo "skip_dir:" && ls -lR skip_dir
# ----------------------------------------------------------------
# END E2E Test Specific - steps
# ----------------------------------------------------------------
- name: set shared ENV variables for multi target deployment
run: |
echo "ARGS=-rlgoDzvcR -i --delete --chmod=ugo+rwX --progress" >> $GITHUB_ENV
echo "SSH_CMD_ARGS=-o StrictHostKeyChecking=no, -o UserKnownHostsFile=/dev/null" >> $GITHUB_ENV
echo "SOURCE=test_project/ test_project2/" >> $GITHUB_ENV
echo "EXCLUDE=skip_dir/, /node_modules/" >> $GITHUB_ENV
echo "SCRIPT_BEFORE<<EOF" >> $GITHUB_ENV
echo "whoami" >> $GITHUB_ENV
echo "ls -lR /var/www/html/" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
echo "SCRIPT_AFTER<<EOF" >> $GITHUB_ENV
echo $RSYNC_STDOUT >> $GITHUB_ENV
echo "ls -lR /var/www/html/" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
- name: e2e Test ssh-deploy action - Target 1
uses: easingthemes/ssh-deploy@main
env:
# Shared ENV Vars created in previous steps
REMOTE_USER: ${{ env.TEST_USER }}
TARGET: /var/www/html/${{ env.TEST_USER }}
- name: e2e Test ssh-deploy action - Target 2
uses: easingthemes/ssh-deploy@main
env:
# Shared ENV Vars created in previous steps
REMOTE_USER: ${{ env.TEST_USER2 }}
TARGET: /var/www/html/${{ env.TEST_USER2 }}
- name: e2e Test ssh-deploy action - Default values
uses: easingthemes/ssh-deploy@main
env:
# Shared ENV Vars created in previous steps
REMOTE_USER: ${{ env.TEST_USER }}
e2e-v3:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
# : ---------------------------------------------------------------
# : START E2E Test Specific - steps
# : ----------------------------------------------------------------
- name: E2E Test Specific - Clean up old test files
run: |
docker stop ssh-host-container || true && docker rm ssh-host-container || true
- name: E2E Test Specific - Create ssh keys
run: |
echo $HOME
ls -la $HOME
ssh-keygen -m PEM -t rsa -b 4096 -f "$HOME/.ssh/id_rsa" -N ""
eval `ssh-agent -s`
ssh-add "$HOME/.ssh/id_rsa"
ssh-add -l
echo "SSH_PRIVATE_KEY<<EOF" >> $GITHUB_ENV
cat $HOME/.ssh/id_rsa >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
- name: E2E Test Specific - Build Host Server Image
working-directory: ${{ env.TEST_HOST_DOCKER }}
run: |
docker build \
-t ssh-host-image . \
--build-arg SSH_PUB_KEY="$(cat $HOME/.ssh/id_rsa.pub)"
docker run -d -p 8822:22 --name=ssh-host-container ssh-host-image
docker exec ssh-host-container sh -c "hostname --ip-address" > ip.txt
echo "REMOTE_HOST<<EOF" >> $GITHUB_ENV
cat ip.txt >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
shell: bash
- name: E2E Test Specific - Create project file
run: |
mkdir test_project2 && cd "$_"
truncate -s 5MB info2.txt
cd ../
mkdir test_project && cd "$_"
touch index.html
date +"%Y-%m-%d %H:%M:%S,%3N" >> index.html
truncate -s 50MB image.svg
truncate -s 5MB info.txt
truncate -s 500MB big_file.txt
mkdir skip_dir && cd "$_"
truncate -s 5MB text_in_skip_dir.txt
cd ../
cat index.html
echo "test_project:" && ls -lR
echo "skip_dir:" && ls -lR skip_dir
# ----------------------------------------------------------------
# END E2E Test Specific - steps
# ----------------------------------------------------------------
- name: e2e Test ssh-deploy action - Target 1
uses: easingthemes/ssh-deploy@main
env:
# Shared ENV Vars created in previous steps
REMOTE_USER: ${{ env.TEST_USER }}
TARGET: /var/www/html/${{ env.TEST_USER }}
SOURCE: test_project/
EXCLUDE: skip_dir/, /node_modules/
ARGS: -rlgoDzvcO

View file

@ -1,72 +0,0 @@
name: Manual Release
on:
workflow_dispatch:
inputs:
version:
description: 'Version'
type: choice
required: true
default: fix
options:
- fix
- feat
dryRun:
description: 'DryRun'
type: boolean
default: true
# ENV and Config
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
GIT_AUTHOR_NAME: github-actions
GIT_AUTHOR_EMAIL: github-actions@github.com
GIT_COMMITTER_NAME: github-actions
GIT_COMMITTER_EMAIL: github-actions@github.com
CI: true
CONFIG_NODE_VERSION: '["lts/*"]'
CONFIG_OS: '["ubuntu-latest"]'
# Main Job
jobs:
config:
runs-on: ubuntu-latest
outputs:
NODE_VERSION: ${{ steps.set-config.outputs.CONFIG_NODE_VERSION }}
OS: ${{ steps.set-config.outputs.CONFIG_OS }}
steps:
- id: set-config
run: |
echo "CONFIG_NODE_VERSION=${{ toJSON(env.CONFIG_NODE_VERSION) }}" >> $GITHUB_OUTPUT
echo "CONFIG_OS=${{ toJSON(env.CONFIG_OS) }}" >> $GITHUB_OUTPUT
release-manual:
name: Test, Build and force Release
needs: config
runs-on: ${{ matrix.OS }}
strategy:
matrix:
OS: ${{ fromJSON(needs.config.outputs.OS) }}
NODE_VERSION: ${{ fromJSON(needs.config.outputs.NODE_VERSION) }}
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.NODE_VERSION }}
- name: Commit trigger
run: |
git commit --allow-empty -m "${{ github.event.inputs.version }}: Trigger Manual Release"
- name: Install dependencies
run: npm ci
- name: Build Library
run: npm run build --if-present
- name: Run Tests
run: npm test --if-present
- name: Create a release - ${{ github.event.inputs.version }}
uses: cycjimmy/semantic-release-action@v4
with:
dry_run: ${{ github.event.inputs.dryRun == 'true' }}
extra_plugins: |
@semantic-release/changelog
@semantic-release/git

View file

@ -1,44 +0,0 @@
name: Release
on:
push:
branches:
- main
jobs:
release:
name: Test, Build and Release
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ ubuntu-latest ]
node-version: [ 20.x ]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix['node-version'] }}
- name: Install dependencies
run: npm ci
- name: Build Library
run: npm run build --if-present
- name: Run Tests
run: npm test --if-present
- name: Release
uses: cycjimmy/semantic-release-action@v4
with:
dry_run: false
extra_plugins: |
@semantic-release/changelog
@semantic-release/git
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
GIT_AUTHOR_NAME: github-actions
GIT_AUTHOR_EMAIL: github-actions@github.com
GIT_COMMITTER_NAME: github-actions
GIT_COMMITTER_EMAIL: github-actions@github.com
CI: true

View file

@ -1,26 +0,0 @@
# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
#
# You can adjust the behavior by modifying this file.
# For more information, see:
# https://github.com/actions/stale
name: Close stale issues
on:
schedule:
- cron: '30 1 * * *'
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- uses: actions/stale@v9
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.'
days-before-stale: 90
days-before-close: 15
stale-issue-label: 'stale'

3
.gitignore vendored
View file

@ -17,6 +17,3 @@ node_modules/
.env
.env.test
# IDE
.idea
.vscode

View file

@ -1,22 +0,0 @@
{
"branches": ["main"],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
[
"@semantic-release/changelog",
{
"changelogFile": "docs/CHANGELOG.md"
}
],
["@semantic-release/npm", {
"npmPublish": false
}],
[
"@semantic-release/git",
{
"assets": ["docs/CHANGELOG.md", "package.json"]
}
]
]
}

139
README.md
View file

@ -1,113 +1,53 @@
# ssh deployments
Deploy code with rsync over ssh.
Execute remote scripts before or after rsync
Deploy code with rsync over ssh, using NodeJS.
NodeJS version is more than a minute `faster` than simple Docker version.
This GitHub Action deploys specific directory from `GITHUB_WORKSPACE` to a folder on a server via rsync over ssh, using NodeJS.
This GitHub Action deploys specific directory from `GITHUB_WORKSPACE` to a folder on a server via rsync over ssh, using NodeJS.
This action would usually follow a build/test action which leaves deployable code in `GITHUB_WORKSPACE`, eg `dist`;
In addition to rsync, this action provides scripts execution on remote host before and/or after rsync.
# Configuration
Pass configuration with `env` vars
##### 1. `SSH_PRIVATE_KEY` [required]
1. `SSH_PRIVATE_KEY` [required]
Private key part of an SSH key pair.
The public key part should be added to the `authorized_keys` file on the server that receives the deployment.
This should be the private key part of an ssh key pair. The public key part should be added to the authorized_keys file on the server that receives the deployment.
More info for SSH keys: https://www.ssh.com/ssh/public-key-authentication
The keys should be generated using the PEM format. You can use this command
```
ssh-keygen -m PEM -t rsa -b 4096
```
**Please Note:** You should not set a Passphrase (keep it empty) for the private key you generated.
Because rsync ssh (used for deploy) does not support private key password to be entered as a command line parameter.
##### 2. `REMOTE_HOST` [required]
2. `REMOTE_HOST` [required]
eg: mydomain.com
##### 3. `REMOTE_USER` [required]
3. `REMOTE_USER` [required]
eg: myusername
##### 4. `REMOTE_PORT` (optional, default '22')
eg: '59184'
##### 5. `ARGS` (optional, default '-rlgoDzvc -i')
2. `ARGS` (optional, default '-rltgoDzvO')
For any initial/required rsync flags, eg: `-avzr --delete`
##### 6. `SOURCE` (optional, default '')
3. `SOURCE` (optional, default '')
The source directory, path relative to `$GITHUB_WORKSPACE` root, eg: `dist/`.
Multiple sources should be separated by space.
The source directory, path relative to `$GITHUB_WORKSPACE` root, eg: `dist/`
##### 7. `TARGET` (optional, default '/home/REMOTE_USER/')
4. `TARGET` (optional, default '/home/REMOTE_USER/')
The target directory
##### 8. `EXCLUDE` (optional, default '')
path to exclude separated by `,`, ie: `/dist/, /node_modules/`
##### 9. `SCRIPT_BEFORE` (optional, default '')
Script to run on host machine before rsync. Single line or multiline commands.
Execution is preformed by storing commands in `.sh` file and executing it via `.bash` over `ssh`
If you have issues with `ssh` connection, use this var, eg `SCRIPT_BEFORE: ls`.
This will force `known_hosts` update, adding your host via `ssh-keyscan`.
##### 10. `SCRIPT_BEFORE_REQUIRED` (optional, default false)
If set to `true`, Job will fail if SCRIPT_BEFORE fails.
##### 11. `SCRIPT_AFTER` (optional, default '')
Script to run on host machine after rsync.
Rsync output is stored in `$RSYNC_STDOUT` env variable.
##### 12. `SCRIPT_AFTER_REQUIRED` (optional, default false)
If set to `true`, Job will fail if SCRIPT_AFTER fails.
##### 13. `SSH_CMD_ARGS` (optional, default '-o StrictHostKeyChecking=no')
A list of ssh arguments, they must be prefixed with -o and separated by a comma, for example: -o SomeArgument=no, -o SomeOtherArgument=5
# Usage
Use the latest version from Marketplace,eg: ssh-deploy@v5.1.0
or use the latest version from a branch, eg: ssh-deploy@main
```
- name: Deploy to Staging server
uses: easingthemes/ssh-deploy@main
with:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
ARGS: "-rlgoDzvc -i"
uses: easingthemes/ssh-deploy@v2.0.2
env:
SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
ARGS: "-rltgoDzvO"
SOURCE: "dist/"
REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
REMOTE_USER: ${{ secrets.REMOTE_USER }}
TARGET: ${{ secrets.REMOTE_TARGET }}
EXCLUDE: "/dist/, /node_modules/"
SCRIPT_BEFORE: |
whoami
ls -al
SCRIPT_AFTER: |
whoami
ls -al
echo $RSYNC_STDOUT
```
# Example usage in workflow
@ -123,59 +63,28 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v1
- name: Install Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v1
with:
node-version: '16.x'
node-version: '10.x'
- name: Install npm dependencies
run: npm install
- name: Run build task
run: npm run build --if-present
- name: Deploy to Server
uses: easingthemes/ssh-deploy@main
with:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
ARGS: "-rlgoDzvc -i --delete"
uses: easingthemes/ssh-deploy@v2.0.2
env:
SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "dist/"
REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
REMOTE_USER: ${{ secrets.REMOTE_USER }}
TARGET: ${{ secrets.REMOTE_TARGET }}
EXCLUDE: "/dist/, /node_modules/"
```
## Issues
This is a GitHub Action wrapping `rsync` via `ssh`. Only issues with action functionality can be fixed here.
Almost 95% of the issues are related to wrong SSH connection or `rsync` params and permissions.
These issues are not related to the action itself.
- Check manually your ssh connection from your client before opening a bug report.
- Check `rsync` params for your use-case. Default params are not necessarily going to be enough for everyone, it highly depends on your setup.
- Check manually your rsync command from your client before opening a bug report.
- `Deployment Failed, Permission denied (publickey,password)`: This issue occures in some cases, it is related to OS and ssh. This action can only provide a workaround:
- Use `SCRIPT_BEFORE` param, eg `SCRIPT_BEFORE: ls`. This will force `known_hosts` update, adding your host via `ssh-keyscan`.
- Or manually add public key to authorized_keys and add a new line to a private key.
I've added e2e test for this action.
Real example is executed on every PR merge to `main`.
Check actions tab for example.
When opening an issue, please add example of your step with env vars. You can add dummy values.
More info for SSH keys: https://www.ssh.com/ssh/public-key-authentication
## Tips
- Optional ENV variables are created for simple requirements.
For complex use cases, use `ARGS` and `SSH_CMD_ARGS` to fully configure `rsync` with all possible options.
- If you need to use multiple steps, eg multi targets deployment, save shared ENV variables in `>> $GITHUB_ENV`.
Check .github/workflows/e2e.yml for an example
- For multi sources, use -R ARG to manipulate folders structure.
- Great post about `rsync` options specific to usage of this action: https://logansnotes.com/2020/gh-action-site-deploy/
## Disclaimer
Check your keys. Check your deployment paths. And use at your own risk.
If you're using GitHub Actions, you'll probably already know that it's still in limited public beta, and GitHub advise against using Actions in production.
So, check your keys. Check your deployment paths. And use at your own risk.

View file

@ -1,62 +1,27 @@
name: "ssh deploy"
description: "NodeJS action for FAST deployment with rsync/ssh and remote script execution before/after rsync"
author: "easingthemes"
name: 'ssh deploy'
description: 'For deploying code over ssh'
inputs:
SSH_PRIVATE_KEY:
description: "Private key part of an SSH key pair"
SSH_PRIVATE_KEY: # Private Key
description: 'Private Key'
required: true
REMOTE_HOST:
description: "Remote host"
description: 'Remote host'
required: true
REMOTE_USER:
description: "Remote user"
description: 'Remote user'
required: true
REMOTE_PORT:
description: "Remote port"
required: false
default: "22"
SOURCE:
description: "Source directory, path relative to `$GITHUB_WORKSPACE` root, eg: `dist/`"
required: false
default: ""
description: 'Source directory'
default: ''
TARGET:
description: "Target directory"
required: false
default: ""
ARGS:
description: "Arguments to pass to rsync"
required: false
default: "-rlgoDzvc -i"
SSH_CMD_ARGS:
description: "An array of ssh arguments, they must be prefixed with -o and separated by a comma, for example: -o SomeArgument=no, -o SomeOtherArgument=5 "
required: false
default: "-o StrictHostKeyChecking=no"
EXCLUDE:
description: "paths to exclude separated by `,`, ie: `/dist/, /node_modules/`"
required: false
default: ""
SCRIPT_BEFORE:
description: "Script to run on host machine before rsync"
required: false
default: ""
SCRIPT_BEFORE_REQUIRED:
description: "If not an empty string, the action will fail if the before script fails. Note: The string 'false' will be treated as true"
required: false
default: ""
SCRIPT_AFTER:
description: "Script to run on host machine after rsync"
required: false
default: ""
SCRIPT_AFTER_REQUIRED:
description: "If not an empty string, the action will fail if the after script fails. Note: The string 'false' will be treated as true"
required: false
default: ""
description: 'Target directory'
default: '/home/REMOTE_USER/'
outputs:
status:
description: "Status"
description: 'Status'
runs:
using: "node20"
main: "dist/index.js"
using: 'node12'
main: 'dist/index.js'
branding:
color: "green"
icon: "truck"
color: 'green'
icon: 'truck'

646
dist/index.js vendored

File diff suppressed because one or more lines are too long

View file

@ -1,403 +0,0 @@
## [5.1.1](https://github.com/easingthemes/ssh-deploy/compare/v5.1.0...v5.1.1) (2024-07-24)
### Bug Fixes
* Update README.md ([f007431](https://github.com/easingthemes/ssh-deploy/commit/f007431332cb2dae49153363ad22fb9f90f4aa75))
# [5.1.0](https://github.com/easingthemes/ssh-deploy/compare/v5.0.3...v5.1.0) (2024-07-24)
### Features
* Add deleteFile function to helpers module ([1befdb1](https://github.com/easingthemes/ssh-deploy/commit/1befdb1c6bf3282aa34e6caa431cb2da23d2b17d))
* apply deleteFile function to remoteCmd ([b82eced](https://github.com/easingthemes/ssh-deploy/commit/b82eced4571cb3f63369d51760a81820ffb1bc7f))
## [5.0.3](https://github.com/easingthemes/ssh-deploy/compare/v5.0.2...v5.0.3) (2024-02-27)
### Bug Fixes
* trigger automated release ([4d8bbf0](https://github.com/easingthemes/ssh-deploy/commit/4d8bbf0debaade9fb03b8dc3be3c020955557b12))
## [5.0.2](https://github.com/easingthemes/ssh-deploy/compare/v5.0.1...v5.0.2) (2024-02-18)
### Bug Fixes
* added the missing declarations [#177](https://github.com/easingthemes/ssh-deploy/issues/177) ([bb271fe](https://github.com/easingthemes/ssh-deploy/commit/bb271fe4c69eeeacb986a38cdb3347104143c61f))
* Fix default values used incorrectly. ([a1b383f](https://github.com/easingthemes/ssh-deploy/commit/a1b383f560a7f52a65da3670e61efe6e02f8639a))
## [5.0.1](https://github.com/easingthemes/ssh-deploy/compare/v5.0.0...v5.0.1) (2024-01-31)
### Bug Fixes
* Add info for Permission denied issue. ([845b578](https://github.com/easingthemes/ssh-deploy/commit/845b578606c0c5a956c70caf61e00a7d2b13ee37))
# [5.0.0](https://github.com/easingthemes/ssh-deploy/compare/v4.1.10...v5.0.0) (2023-12-12)
* Merge pull request #173 from jeromelachaud/main ([ac1908e](https://github.com/easingthemes/ssh-deploy/commit/ac1908e5d2dc749496fdbe8a918aa073e3357d85)), closes [#173](https://github.com/easingthemes/ssh-deploy/issues/173)
### BREAKING CHANGES
* update to use nodeJS v20
* update to use nodeJS v20
## [4.1.10](https://github.com/easingthemes/ssh-deploy/compare/v4.1.9...v4.1.10) (2023-09-30)
### Bug Fixes
* normalize line endings in SSH key for the underlying OS ([3f5d9aa](https://github.com/easingthemes/ssh-deploy/commit/3f5d9aab1a743bd426a4d132d07f1f5e9ed0310c))
## [4.1.9](https://github.com/easingthemes/ssh-deploy/compare/v4.1.8...v4.1.9) (2023-09-24)
### Bug Fixes
* add compiled file ([627ac29](https://github.com/easingthemes/ssh-deploy/commit/627ac29ece9dc2f1185a50d1002bc2c968fc973c))
* add uuid for ssh scripts ([66f6e4b](https://github.com/easingthemes/ssh-deploy/commit/66f6e4b367ea39479c285234797a4e86c90d9abd))
## [4.1.8](https://github.com/easingthemes/ssh-deploy/compare/v4.1.7...v4.1.8) (2023-02-21)
### Bug Fixes
* rebuild and update readme ([98025d6](https://github.com/easingthemes/ssh-deploy/commit/98025d680e96a5c6c805e377a1b81de2f626aa1e))
## [4.1.7](https://github.com/easingthemes/ssh-deploy/compare/v4.1.6...v4.1.7) (2023-02-21)
### Bug Fixes
* [#120](https://github.com/easingthemes/ssh-deploy/issues/120) check undefined data ([2fbb060](https://github.com/easingthemes/ssh-deploy/commit/2fbb06015d1a6ffd32e0100aaf1a1a46949e990e))
## [4.1.6](https://github.com/easingthemes/ssh-deploy/compare/v4.1.5...v4.1.6) (2023-02-21)
### Bug Fixes
* [#118](https://github.com/easingthemes/ssh-deploy/issues/118) check undefined default values rebuild ([54f8b6c](https://github.com/easingthemes/ssh-deploy/commit/54f8b6c60b8f2f926d5ed9538557e5521a905d87))
## [4.1.5](https://github.com/easingthemes/ssh-deploy/compare/v4.1.4...v4.1.5) (2023-02-21)
### Bug Fixes
* [#118](https://github.com/easingthemes/ssh-deploy/issues/118) check undefined default values ([f0c02fb](https://github.com/easingthemes/ssh-deploy/commit/f0c02fb2a5b3b69bb91004dd49d409eb6adfe7cd))
## [4.1.4](https://github.com/easingthemes/ssh-deploy/compare/v4.1.3...v4.1.4) (2023-02-21)
### Bug Fixes
* [#113](https://github.com/easingthemes/ssh-deploy/issues/113) limit ssh script input ([5894f5e](https://github.com/easingthemes/ssh-deploy/commit/5894f5e29008feccaf42787330ec8f49f3ad50b0))
## [4.1.3](https://github.com/easingthemes/ssh-deploy/compare/v4.1.2...v4.1.3) (2023-02-21)
### Bug Fixes
* [#113](https://github.com/easingthemes/ssh-deploy/issues/113) limit ssh script output - rebuild ([756a522](https://github.com/easingthemes/ssh-deploy/commit/756a522533d2206203b5d13b5aa11c88b3313784))
## [4.1.2](https://github.com/easingthemes/ssh-deploy/compare/v4.1.1...v4.1.2) (2023-02-21)
### Bug Fixes
* [#113](https://github.com/easingthemes/ssh-deploy/issues/113) limit ssh script output ([59827af](https://github.com/easingthemes/ssh-deploy/commit/59827af83c934996efda72f9fbd1fcd0bb9ccaac))
## [4.1.1](https://github.com/easingthemes/ssh-deploy/compare/v4.1.0...v4.1.1) (2023-02-21)
### Bug Fixes
* fix default TARGET ([4d08634](https://github.com/easingthemes/ssh-deploy/commit/4d086346af62ac5d57fa37ee6bb46f8de8ad48c3))
# [4.1.0](https://github.com/easingthemes/ssh-deploy/compare/v4.0.5...v4.1.0) (2023-02-19)
### Features
* add ssh port from VARs, fix package-lock ([808b002](https://github.com/easingthemes/ssh-deploy/commit/808b0020d44b7c25ef1c13b3979ffdab4f503236))
## [4.0.5](https://github.com/easingthemes/ssh-deploy/compare/v4.0.4...v4.0.5) (2023-01-06)
### Bug Fixes
* fix manual release action ([245b7a9](https://github.com/easingthemes/ssh-deploy/commit/245b7a9d2fe5272ee34608f86a612d643694c23b))
## [4.0.4](https://github.com/easingthemes/ssh-deploy/compare/v4.0.3...v4.0.4) (2023-01-03)
### Bug Fixes
* update sub dependencies ([2a32c8d](https://github.com/easingthemes/ssh-deploy/commit/2a32c8dcde2d64394a3577decdd9c902f37a30f8))
## [4.0.3](https://github.com/easingthemes/ssh-deploy/compare/v4.0.2...v4.0.3) (2023-01-03)
### Bug Fixes
* update v3 e2e test ([450bf42](https://github.com/easingthemes/ssh-deploy/commit/450bf423f388777bde052ad7b5bc24eca392434d))
## [4.0.2](https://github.com/easingthemes/ssh-deploy/compare/v4.0.1...v4.0.2) (2023-01-03)
### Bug Fixes
* add githubWorkspace as default source root ([3ef66d0](https://github.com/easingthemes/ssh-deploy/commit/3ef66d0be999ee45434505e8bedea4f8b5b5a909))
## [4.0.1](https://github.com/easingthemes/ssh-deploy/compare/v4.0.0...v4.0.1) (2023-01-03)
### Bug Fixes
* fix default source if empty ([9f191f4](https://github.com/easingthemes/ssh-deploy/commit/9f191f42633c4a0f66054f0fc229c9e30a08f00c))
# [4.0.0](https://github.com/easingthemes/ssh-deploy/compare/v3.4.3...v4.0.0) (2023-01-03)
### Bug Fixes
* rebuild ([2169399](https://github.com/easingthemes/ssh-deploy/commit/2169399fef9a60a2fea1ab03cce4ec8c2371e5c2))
### Performance Improvements
* update default rsync options ([2be7efb](https://github.com/easingthemes/ssh-deploy/commit/2be7efb376866327c1d1209f51eb43f34f07b354))
### BREAKING CHANGES
* Default rsync options updated to speed up default deployments.
from `rltgoDzvO` to `-rlgoDzvc -i`
## [3.4.3](https://github.com/easingthemes/ssh-deploy/compare/v3.4.2...v3.4.3) (2023-01-03)
### Bug Fixes
* move e2e tests typo branch name ([6d3554b](https://github.com/easingthemes/ssh-deploy/commit/6d3554b01e9a05de9a9d2b30274bee411a4986ed))
## [3.4.2](https://github.com/easingthemes/ssh-deploy/compare/v3.4.1...v3.4.2) (2023-01-03)
### Bug Fixes
* Update changelog ([765f5ff](https://github.com/easingthemes/ssh-deploy/commit/765f5ffd3153c76442521c61c04656cafc182731))
## [3.4.1](https://github.com/easingthemes/ssh-deploy/compare/v3.4.0...v3.4.1) (2023-01-03)
### Bug Fixes
* move e2e tests to main branch ([0069fae](https://github.com/easingthemes/ssh-deploy/commit/0069faea9ff3ea3cdd095b0f2663c9e2bcd97480))
# [3.4.0](https://github.com/easingthemes/ssh-deploy/compare/v3.3.4...v3.4.0) (2023-01-03)
**Full Changelog**: https://github.com/easingthemes/ssh-deploy/compare/v3.1.0...v3.4.1
### Features
* Add SSH remote script support - before and after rsync ([2cd8a82](https://github.com/easingthemes/ssh-deploy/commit/2cd8a820e22135b7002fbe6a47cf33f99e2d337b))
* Add multi source and multi target support ([73a65ec](https://github.com/easingthemes/ssh-deploy/commit/73a65ec97c5046a53f6b4c40823be5fd3826ede0))
* Add SSH_CMD_ARGS support ([51a0063](https://github.com/easingthemes/ssh-deploy/commit/51a00631e2b91983fb610ca2157a673b9ae03c95))
* Add manual inputs for e2e test ([8f71595](https://github.com/easingthemes/ssh-deploy/commit/8f715957ab9283ab2934dd1e5be9d78f2eaf3fae))
### Bug Fixes
* Add visible Rsync CMD for debugging ([91b6d28](https://github.com/easingthemes/ssh-deploy/commit/91b6d280aee6a7ae666a11426fb356406f4a25a5))
* log buffer from rsync stdout ([8e19e0d](https://github.com/easingthemes/ssh-deploy/commit/8e19e0d6bc9b1c332925ce0268ad64b50728fae5))
* fix e2e yaml step name ([5c13615](https://github.com/easingthemes/ssh-deploy/commit/5c13615d4b7b0bda47f622ac5f4444484d8bcdb4))
## [3.3.4](https://github.com/easingthemes/ssh-deploy/compare/v3.3.3...v3.3.4) (2023-01-02)
### Bug Fixes
* Add visible Rsync CMD for debugging ([91b6d28](https://github.com/easingthemes/ssh-deploy/commit/91b6d280aee6a7ae666a11426fb356406f4a25a5))
## [3.3.3](https://github.com/easingthemes/ssh-deploy/compare/v3.3.2...v3.3.3) (2023-01-02)
### Bug Fixes
* log buffer from rsync stdout ([8e19e0d](https://github.com/easingthemes/ssh-deploy/commit/8e19e0d6bc9b1c332925ce0268ad64b50728fae5))
## [3.3.2](https://github.com/easingthemes/ssh-deploy/compare/v3.3.1...v3.3.2) (2023-01-02)
### Bug Fixes
* Add manual inputs for e2e test ([8f71595](https://github.com/easingthemes/ssh-deploy/commit/8f715957ab9283ab2934dd1e5be9d78f2eaf3fae))
## [3.3.1](https://github.com/easingthemes/ssh-deploy/compare/v3.3.0...v3.3.1) (2023-01-02)
### Bug Fixes
* Log rsync command ([85f4a96](https://github.com/easingthemes/ssh-deploy/commit/85f4a967435d7d44edf2fea628c9f59db5e890ee))
# [3.3.0](https://github.com/easingthemes/ssh-deploy/compare/v3.2.0...v3.3.0) (2023-01-02)
### Features
* Add SSH_CMD_ARGS support ([51a0063](https://github.com/easingthemes/ssh-deploy/commit/51a00631e2b91983fb610ca2157a673b9ae03c95))
# [3.2.0](https://github.com/easingthemes/ssh-deploy/pull/94/files) (2023-01-02)
### Features
* Add SSH remote script support - before and after rsync ([2cd8a82](https://github.com/easingthemes/ssh-deploy/pull/94/files))
* feat: add sshCmdArgs option
* feat: add onStderr and onStdout logs
* feat: Add RSYNC_STDOUT env variable
* feat: Update emojis
### Bug Fixes
* fix: remove _unsafe _dirname
* fix: Add promise instead of callback
* fix: improve logs
* fix: Add simple command exists instead of a plugin
* fix: add non interactive install
* fix: Improve reject messages
* fix: update workflow actions
## [3.1.1](https://github.com/easingthemes/ssh-deploy/compare/v3.1.0...v3.1.1) (2023-01-02)
### Bug Fixes
* use main branch for e2e testes ([21451fe](https://github.com/easingthemes/ssh-deploy/commit/21451fec978a02b55672490a2c11c6c17995ff95))
# [3.1.0](https://github.com/easingthemes/ssh-deploy/compare/v3.0.1...v3.1.0) (2022-12-31)
### Features
* add e2e tests ([40f855a](https://github.com/easingthemes/ssh-deploy/commit/40f855a08b4911c3f54b7a45306af355c6d87277))
## [3.0.1](https://github.com/easingthemes/ssh-deploy/compare/v3.0.0...v3.0.1) (2022-10-28)
### Bug Fixes
* readme update ([80a7f53](https://github.com/easingthemes/ssh-deploy/commit/80a7f53ec918930fd23f0524fec0b1d4f52183bf)), closes [#67](https://github.com/easingthemes/ssh-deploy/issues/67)
# [3.0.0](https://github.com/easingthemes/ssh-deploy/compare/v2.2.11...v3.0.0) (2022-10-28)
### Bug Fixes
* plugins versions ([2f40dde](https://github.com/easingthemes/ssh-deploy/commit/2f40dde84fde36f5b9b81ededd7090c5159d9885))
* Merge pull request #83 from easingthemes/feature/#82-update-node-version ([76660a8](https://github.com/easingthemes/ssh-deploy/commit/76660a8456dbf26e6328674e25d083941ddd645e)), closes [#83](https://github.com/easingthemes/ssh-deploy/issues/83) [easingthemes/feature/#82](https://github.com/easingthemes/feature//issues/82)
### BREAKING CHANGES
* update to use nodeJS v16
## [2.2.11](https://github.com/easingthemes/ssh-deploy/compare/v2.2.10...v2.2.11) (2021-05-28)
### Bug Fixes
* remove change npm to public ([3c36af7](https://github.com/easingthemes/ssh-deploy/commit/3c36af7577e5aee231cea5a01bb6cc83717d0e74))
## [2.2.10](https://github.com/easingthemes/ssh-deploy/compare/v2.2.9...v2.2.10) (2021-05-27)
### Bug Fixes
* add assets to semantic-release git ([0867b12](https://github.com/easingthemes/ssh-deploy/commit/0867b12954dee2b8a2cccb7dfea3b8f3aa62d679))
## [2.2.9](https://github.com/easingthemes/ssh-deploy/compare/v2.2.8...v2.2.9) (2021-05-27)
### Bug Fixes
* change npm to public ([191c82d](https://github.com/easingthemes/ssh-deploy/commit/191c82d9b441e26cf43f81041376dbf5ecdc1647))
## [2.2.8](https://github.com/easingthemes/ssh-deploy/compare/v2.2.7...v2.2.8) (2021-05-27)
### Bug Fixes
* update npm package name ([9c4e411](https://github.com/easingthemes/ssh-deploy/commit/9c4e4119ad64792e5435e4dbe574c56a3e70839c))
## [2.2.7](https://github.com/easingthemes/ssh-deploy/compare/v2.2.6...v2.2.7) (2021-05-27)
### Bug Fixes
* minify dist ([87551c3](https://github.com/easingthemes/ssh-deploy/commit/87551c38936fc91c0fbe3346ca43319d8098ac09))
## [2.2.6](https://github.com/easingthemes/ssh-deploy/compare/v2.2.5...v2.2.6) (2021-05-27)
### Bug Fixes
* add NPM token ([2197bc6](https://github.com/easingthemes/ssh-deploy/commit/2197bc60ef7870d4bd494966b314eabec1615bd7))
## [2.2.5](https://github.com/easingthemes/ssh-deploy/compare/v2.2.4...v2.2.5) (2021-05-27)
### Bug Fixes
* semantic-release/npm update config ([40096bf](https://github.com/easingthemes/ssh-deploy/commit/40096bf22459d1dd82172d2bd20c0c149e70b1e1))
## [2.2.4](https://github.com/easingthemes/ssh-deploy/compare/v2.2.3...v2.2.4) (2021-05-27)
### Bug Fixes
* add branch instead of a version in a readme ([8218c8e](https://github.com/easingthemes/ssh-deploy/commit/8218c8ed9514d772933e1ab4d1c725a7c05e149f))
## [2.2.3](https://github.com/easingthemes/ssh-deploy/compare/v2.2.2...v2.2.3) (2021-05-27)
### Bug Fixes
* codeql yaml syntax update ([87ad671](https://github.com/easingthemes/ssh-deploy/commit/87ad6713b53d454bd7ad6c4576cea7b2e3e2f4f3))
## [2.2.2](https://github.com/easingthemes/ssh-deploy/compare/v2.2.1...v2.2.2) (2021-05-27)
### Bug Fixes
* codeql build ([5e456a4](https://github.com/easingthemes/ssh-deploy/commit/5e456a475a15096d08ccd2aff2734b3f1250b308))
## [2.2.1](https://github.com/easingthemes/ssh-deploy/compare/v2.2.0...v2.2.1) (2021-05-27)
### Bug Fixes
* add package json auto updates ([ce56d75](https://github.com/easingthemes/ssh-deploy/commit/ce56d75fc1b62a99d72ffba70dcb24fcc3b6b3df))
# [2.2.0](https://github.com/easingthemes/ssh-deploy/compare/v2.1.7...v2.2.0) (2021-05-27)
### Bug Fixes
* only=prod package json ([95f4dc8](https://github.com/easingthemes/ssh-deploy/commit/95f4dc8069045c1f5f726e00cb519b46e4f14267))
* replace i with ci ([50d1f57](https://github.com/easingthemes/ssh-deploy/commit/50d1f576f95c0d7e8ce99fb1b2ab68b2594973e5))
* update ncc ([20a0cae](https://github.com/easingthemes/ssh-deploy/commit/20a0cae1ae81bcc430507363e800342976307a81))
* update plugins ([b2adc00](https://github.com/easingthemes/ssh-deploy/commit/b2adc00c92f129aef41ae46441411c2bebc0dbe4))
* update plugins ([a50a999](https://github.com/easingthemes/ssh-deploy/commit/a50a999528b503846cc7fdf26210f710bd95565a))
### Features
* add semantic-release-action ([ac3c9b5](https://github.com/easingthemes/ssh-deploy/commit/ac3c9b51d5cb52f4add40e2fc2dcc5e970153afc))

3434
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,18 +1,18 @@
{
"name": "@draganfilipovic/ssh-deploy",
"version": "5.1.1",
"description": "Fast NodeJS action to deploy specific directory from `GITHUB_WORKSPACE` to a server via rsync over ssh.",
"main": "dist/index.js",
"files": [
"/dist",
"./action.yml",
"./README.md",
"LICENSE"
],
"name": "ssh-deploy",
"version": "2.0.3",
"description": "This GitHub Action deploys specific directory from `GITHUB_WORKSPACE` to a folder on a server via rsync over ssh.",
"main": "src/index.js",
"dependencies": {
"command-exists": "1.2.8",
"node-cmd": "3.0.0",
"rsyncwrapper": "3.0.1"
},
"devDependencies": {
"@zeit/ncc": "^0.20.5"
},
"scripts": {
"build": "npm run lint && ncc build ./src/index.js -o dist -m",
"lint": "eslint ./src/index.js",
"lint:fix": "eslint ./src/index.js --fix"
"build": "ncc build ./src/index.js -o dist"
},
"repository": {
"type": "git",
@ -24,22 +24,9 @@
"rsync"
],
"author": "Dragan Filipovic",
"license": "MIT",
"license": "ISC",
"bugs": {
"url": "https://github.com/easingthemes/ssh-deploy/issues"
},
"homepage": "https://github.com/easingthemes/ssh-deploy#readme",
"dependencies": {
"rsyncwrapper": "^3.0.1"
},
"devDependencies": {
"@vercel/ncc": "^0.36.0",
"eslint": "^8.30.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-plugin-import": "^2.26.0"
},
"overrides": {
"word-wrap": "npm:@aashutoshrathi/word-wrap@1.2.5",
"semver": "^7.5.2"
}
"homepage": "https://github.com/easingthemes/ssh-deploy#readme"
}

View file

@ -1,95 +0,0 @@
const { existsSync, mkdirSync, writeFileSync, unlink } = require('fs');
const { join } = require('path');
const validateDir = (dir) => {
if (!dir) {
console.warn('⚠️ [DIR] dir is not defined');
return;
}
if (existsSync(dir)) {
console.log(`✅ [DIR] ${dir} dir exist`);
return;
}
console.log(`[DIR] Creating ${dir} dir in workspace root`);
mkdirSync(dir);
console.log('✅ [DIR] dir created.');
};
const handleError = (message, isRequired) => {
if (isRequired) {
throw new Error(message);
}
console.warn(message);
};
const writeToFile = ({ dir, filename, content, isRequired, mode = '0644' }) => {
validateDir(dir);
const filePath = join(dir, filename);
if (existsSync(filePath)) {
const message = `⚠️ [FILE] ${filePath} Required file exist.`;
handleError(message, isRequired);
return;
}
try {
console.log(`[FILE] writing ${filePath} file ...`, content.length);
writeFileSync(filePath, content, {
encoding: 'utf8',
mode
});
} catch (error) {
const message = `⚠️[FILE] Writing to file error. filePath: ${filePath}, message: ${error.message}`;
handleError(message, isRequired);
}
};
const deleteFile = ({ dir, filename, isRequired }) => {
validateDir(dir);
const filePath = join(dir, filename);
if (existsSync(filePath)) {
const message = `⚠️ [FILE] ${filePath} Required file exist.`;
handleError(message, isRequired);
return;
}
try {
console.log(`[FILE] Deleting ${filePath} file ...`);
unlink(filePath, (error) => {
if (error) {
throw new Error(error);
}
});
} catch (error) {
const message = `⚠️[FILE] Deleting file error. filePath: ${filePath}, message: ${error.message}`;
handleError(message, isRequired);
}
};
const validateRequiredInputs = (inputs) => {
const inputKeys = Object.keys(inputs);
const validInputs = inputKeys.filter((inputKey) => {
const inputValue = inputs[inputKey];
if (!inputValue) {
console.error(`❌ [INPUTS] ${inputKey} is mandatory`);
}
return inputValue;
});
if (validInputs.length !== inputKeys.length) {
throw new Error('⚠️ [INPUTS] Inputs not valid, aborting ...');
}
};
const snakeToCamel = (str) => str.replace(/[^a-zA-Z0-9]+(.)/g, (m, chr) => chr.toUpperCase());
module.exports = {
writeToFile,
deleteFile,
validateRequiredInputs,
snakeToCamel
};

View file

@ -1,48 +1,151 @@
#!/usr/bin/env node
const { sshDeploy } = require('./rsyncCli');
const { remoteCmdBefore, remoteCmdAfter } = require('./remoteCmd');
const { addSshKey, getPrivateKeyPath, updateKnownHosts } = require('./sshKey');
const { validateRequiredInputs } = require('./helpers');
const inputs = require('./inputs');
const fs = require('fs');
const path = require('path');
const commandExists = require('command-exists');
const nodeCmd = require('node-cmd');
const nodeRsync = require('rsyncwrapper');
const run = async () => {
const {
source, remoteUser, remoteHost, remotePort,
deployKeyName, sshPrivateKey,
args, exclude, sshCmdArgs,
scriptBefore, scriptBeforeRequired,
scriptAfter, scriptAfterRequired,
rsyncServer
} = inputs;
// Validate required inputs
validateRequiredInputs({ sshPrivateKey, remoteHost, remoteUser });
// Add SSH key
addSshKey(sshPrivateKey, deployKeyName);
const { path: privateKeyPath } = getPrivateKeyPath(deployKeyName);
// Update known hosts if ssh command is present to avoid prompt
if (scriptBefore || scriptAfter) {
updateKnownHosts(remoteHost, remotePort);
}
// Check Script before
if (scriptBefore) {
await remoteCmdBefore(scriptBefore, privateKeyPath, scriptBeforeRequired);
}
/* eslint-disable object-property-newline */
await sshDeploy({
source, rsyncServer, exclude, remotePort,
privateKeyPath, args, sshCmdArgs
});
// Check script after
if (scriptAfter) {
await remoteCmdAfter(scriptAfter, privateKeyPath, scriptAfterRequired);
}
const { REMOTE_HOST, REMOTE_USER, SSH_PRIVATE_KEY, DEPLOY_KEY_NAME, SOURCE, TARGET, ARGS, GITHUB_WORKSPACE, HOME } = process.env;
console.log('GITHUB_WORKSPACE', GITHUB_WORKSPACE);
const sshDeploy = (() => {
const rsync = ({ privateKey, src, dest, args }) => {
console.log(`Starting Rsync Action: ${src} to ${dest}`);
try {
// RSYNC COMMAND
nodeRsync({ src, dest, args, privateKey, ssh: true, sshCmdArgs: ['-o StrictHostKeyChecking=no'], recursive: true }, (error, stdout, stderr, cmd) => {
if (error) {
console.error('⚠️ Rsync error', error.message);
process.abort();
} else {
console.log("✅ Rsync finished.", stdout);
}
});
} catch (err) {
console.error(`⚠️ An error happened:(.`, err.message, err.stack);
process.abort();
}
};
const init = ({
src,
dest,
args,
host = 'localhost',
username,
privateKeyContent
}) => {
validateRsync(() => {
const privateKey = addSshKey(privateKeyContent, DEPLOY_KEY_NAME ||'deploy_key');
const remoteDest = username + '@' + host + ':' + dest;
rsync({ privateKey, src, dest: remoteDest, args });
});
};
const validateDir = (dir) => {
if (!fs.existsSync(dir)){
console.log(`Creating ${dir} dir in `, GITHUB_WORKSPACE);
fs.mkdirSync(dir);
} else {
console.log(`${dir} dir exist`);
}
};
const validateFile = (filePath) => {
if (!fs.existsSync(filePath)){
console.log(`Creating ${filePath} file in `, GITHUB_WORKSPACE);
try {
fs.writeFileSync(filePath, '', {
encoding: 'utf8',
mode: 0o600
});
} catch (e) {
console.error('⚠️ writeFileSync error', filePath, e.message);
process.abort();
}
} else {
console.log(`${filePath} file exist`);
}
};
const addSshKey = (key, name) => {
const sshDir = path.join(HOME || __dirname, '.ssh');
const filePath = path.join(sshDir, name);
validateDir(sshDir);
validateFile(sshDir + '/known_hosts');
try {
fs.writeFileSync(filePath, key, {
encoding: 'utf8',
mode: 0o600
});
} catch (e) {
console.error('⚠️ writeFileSync error', filePath, e.message);
process.abort();
}
console.log('✅ Ssh key added to `.ssh` dir ', filePath);
return filePath;
};
const validateRsync = (callback = () => {}) => {
const rsyncCli = commandExists.sync('rsync');
if (!rsyncCli) {
nodeCmd.get(
'sudo apt-get --no-install-recommends install rsync',
function(err, data, stderr){
if (err) {
console.log('⚠️ Rsync installation failed ', err.message);
process.abort();
} else {
console.log('✅ Rsync installed. \n', data, stderr);
callback();
}
}
);
} else {
callback();
}
};
return {
init
}
})();
const validateInputs = (inputs) => {
const validInputs = inputs.filter(input => {
if (!input) {
console.error(`⚠️ ${input} is mandatory`);
}
return input;
});
if (validInputs.length !== inputs.length) {
process.abort();
}
};
run()
.then((data = '') => {
console.log('✅ [DONE]', data);
})
.catch((error) => {
console.error('❌ [ERROR]', error.message);
process.exit(1);
});
const run = () => {
validateInputs([SSH_PRIVATE_KEY, REMOTE_HOST, REMOTE_USER]);
sshDeploy.init({
src: GITHUB_WORKSPACE + '/' + SOURCE || '',
dest: TARGET || '/home/' + REMOTE_USER + '/',
args: [ARGS] || ['-rltgoDzvO'],
host: REMOTE_HOST,
username: REMOTE_USER,
privateKeyContent: SSH_PRIVATE_KEY,
});
};
run();

View file

@ -1,50 +0,0 @@
const { snakeToCamel } = require('./helpers');
const inputNames = [
'REMOTE_HOST', 'REMOTE_USER', 'REMOTE_PORT',
'SSH_PRIVATE_KEY', 'DEPLOY_KEY_NAME',
'SOURCE', 'TARGET', 'ARGS', 'SSH_CMD_ARGS', 'EXCLUDE',
'SCRIPT_BEFORE', 'SCRIPT_AFTER', 'SCRIPT_BEFORE_REQUIRED', 'SCRIPT_AFTER_REQUIRED'];
const githubWorkspace = process.env.GITHUB_WORKSPACE;
const remoteUser = process.env.REMOTE_USER || process.env.INPUT_REMOTE_USER;
const defaultInputs = {
source: '',
target: `/home/${remoteUser}/`,
exclude: '',
args: '-rlgoDzvc -i',
sshCmdArgs: '-o StrictHostKeyChecking=no',
deployKeyName: `deploy_key_${remoteUser}_${Date.now()}`
};
const inputs = {
githubWorkspace
};
inputNames.forEach((input) => {
const inputName = snakeToCamel(input.toLowerCase());
const inputVal = process.env[input] || process.env[`INPUT_${input}`] || defaultInputs[inputName];
const validVal = inputVal === undefined ? defaultInputs[inputName] : inputVal;
let extendedVal = validVal;
// eslint-disable-next-line default-case
switch (inputName) {
case 'source':
extendedVal = validVal.split(' ').map((src) => `${githubWorkspace}/${src}`);
break;
case 'args':
extendedVal = validVal.split(' ');
break;
case 'exclude':
case 'sshCmdArgs':
extendedVal = validVal.split(',').map((item) => item.trim());
break;
}
inputs[inputName] = extendedVal;
});
inputs.sshServer = `${inputs.remoteUser}@${inputs.remoteHost}`;
inputs.rsyncServer = `${inputs.remoteUser}@${inputs.remoteHost}:${inputs.target}`;
module.exports = inputs;

View file

@ -1,47 +0,0 @@
const { exec } = require('child_process');
const crypto = require('crypto');
const { sshServer, githubWorkspace, remotePort } = require('./inputs');
const { writeToFile, deleteFile } = require('./helpers');
const handleError = (message, isRequired, callback) => {
if (isRequired) {
callback(new Error(message));
} else {
console.warn(message);
}
};
// eslint-disable-next-line max-len
const remoteCmd = async (content, privateKeyPath, isRequired, label) => new Promise((resolve, reject) => {
const uuid = crypto.randomUUID();
const filename = `local_ssh_script-${label}-${uuid}.sh`;
try {
writeToFile({ dir: githubWorkspace, filename, content });
const dataLimit = 10000;
const rsyncStdout = (process.env.RSYNC_STDOUT || '').substring(0, dataLimit);
console.log(`Executing remote script: ssh -i ${privateKeyPath} ${sshServer}`);
exec(
`DEBIAN_FRONTEND=noninteractive ssh -p ${(remotePort || 22)} -i ${privateKeyPath} -o StrictHostKeyChecking=no ${sshServer} 'RSYNC_STDOUT="${rsyncStdout}" bash -s' < ${filename}`,
(err, data = '', stderr = '') => {
if (err) {
const message = `⚠️ [CMD] Remote script failed: ${err.message}`;
console.warn(`${message} \n`, data, stderr);
handleError(message, isRequired, reject);
} else {
const limited = data.substring(0, dataLimit);
console.log('✅ [CMD] Remote script executed. \n', limited, stderr);
deleteFile({ dir: githubWorkspace, filename });
console.log('✅ [FILE] Script file deleted.');
resolve(limited);
}
}
);
} catch (err) {
handleError(err.message, isRequired, reject);
}
});
module.exports = {
remoteCmdBefore: async (cmd, privateKeyPath, isRequired) => remoteCmd(cmd, privateKeyPath, isRequired, 'before'),
remoteCmdAfter: async (cmd, privateKeyPath, isRequired) => remoteCmd(cmd, privateKeyPath, isRequired, 'after')
};

View file

@ -1,86 +0,0 @@
const { execSync } = require('child_process');
const nodeRsync = require('rsyncwrapper');
const nodeRsyncPromise = async (config) => new Promise((resolve, reject) => {
const logCMD = (cmd) => {
console.warn('================================================================');
console.log(cmd);
console.warn('================================================================');
};
try {
nodeRsync(config, (error, stdout, stderr, cmd) => {
if (error) {
console.error('❌ [Rsync] error: ');
console.error(error);
console.error('❌ [Rsync] stderr: ');
console.error(stderr);
console.error('❌️ [Rsync] stdout: ');
console.error(stdout);
console.error('❌ [Rsync] command: ');
logCMD(cmd);
reject(new Error(`${error.message}\n\n${stderr}`));
} else {
console.log('⭐ [Rsync] command finished: ');
logCMD(cmd);
resolve(stdout);
}
});
} catch (error) {
console.error('❌ [Rsync] command error: ', error.message, error.stack);
reject(error);
}
});
const validateRsync = async () => {
try {
execSync('rsync --version', { stdio: 'inherit' });
console.log('✅️ [CLI] Rsync exists');
return;
} catch (error) {
console.warn('⚠️ [CLI] Rsync doesn\'t exists', error.message);
}
console.log('[CLI] Start rsync installation with "apt-get" \n');
try {
execSync('sudo DEBIAN_FRONTEND=noninteractive apt-get -y update && sudo DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends -y install rsync', { stdio: 'inherit' });
console.log('✅ [CLI] Rsync installed. \n');
} catch (error) {
throw new Error(`⚠️ [CLI] Rsync installation failed. Aborting ... error: ${error.message}`);
}
};
const rsyncCli = async ({
source, rsyncServer, exclude, remotePort,
privateKeyPath, args, sshCmdArgs
}) => {
console.log(`[Rsync] Starting Rsync Action: ${source} to ${rsyncServer}`);
if (exclude && exclude.length > 0) console.log(`[Rsync] excluding folders ${exclude}`);
const defaultOptions = {
ssh: true,
recursive: true,
onStdout: (data) => console.log(data.toString()),
onStderr: (data) => console.error(data.toString())
};
// RSYNC COMMAND
/* eslint-disable object-property-newline */
return nodeRsyncPromise({
...defaultOptions,
src: source, dest: rsyncServer, excludeFirst: exclude, port: remotePort,
privateKey: privateKeyPath, args, sshCmdArgs
});
};
const sshDeploy = async (params) => {
await validateRsync();
const stdout = await rsyncCli(params);
console.log('✅ [Rsync] finished.', stdout);
process.env.RSYNC_STDOUT = `${stdout}`;
return stdout;
};
module.exports = {
sshDeploy
};

View file

@ -1,44 +0,0 @@
const { join } = require('path');
const { execSync } = require('child_process');
const { EOL } = require('os');
const { writeToFile } = require('./helpers');
const KNOWN_HOSTS = 'known_hosts';
const getPrivateKeyPath = (filename = '') => {
const { HOME } = process.env;
const dir = join(HOME || '~', '.ssh');
const knownHostsPath = join(dir, KNOWN_HOSTS);
return {
dir,
filename,
path: join(dir, filename),
knownHostsPath
};
};
const addSshKey = (content, deployKeyName) => {
const { dir, filename } = getPrivateKeyPath(deployKeyName);
writeToFile({ dir, filename: KNOWN_HOSTS, content: '' });
console.log('✅ [SSH] known_hosts file ensured', dir);
writeToFile({ dir, filename, content: `${content}${EOL}`, isRequired: true, mode: '0400' });
console.log('✅ [SSH] key added to `.ssh` dir ', dir, filename);
};
const updateKnownHosts = (host, remotePort) => {
const { knownHostsPath } = getPrivateKeyPath();
console.log('[SSH] Adding host to `known_hosts` ....', host, knownHostsPath);
try {
execSync(`ssh-keyscan -p ${(remotePort || 22)} -H ${host} >> ${knownHostsPath}`, {
stdio: 'inherit'
});
} catch (error) {
console.error('❌ [SSH] Adding host to `known_hosts` ERROR', host, error.message);
}
console.log('✅ [SSH] Adding host to `known_hosts` DONE', host, knownHostsPath);
};
module.exports = {
getPrivateKeyPath,
updateKnownHosts,
addSshKey
};

View file

@ -1,5 +0,0 @@
/node_modules
.gitignore
.gitattributes
LICENSE
README.md

View file

@ -1,42 +0,0 @@
FROM nginx
ARG SSH_PUB_KEY
RUN apt update
RUN apt install openssh-server rsync sudo -y
RUN echo "PubkeyAuthentication yes" >> /etc/ssh/sshd_config.d/pub.conf
RUN echo "AuthorizedKeysFile .ssh/authorized_keys" >> /etc/ssh/sshd_config.d/pub.conf
RUN mkdir -p /var/www/html
RUN mkdir -p /var/www/html/test
RUN mkdir -p /var/www/html/test2
RUN chmod -R 775 /var/www/html
RUN useradd -rm -d /home/test -s /bin/bash -g root -G sudo -u 1000 test
RUN usermod -aG sudo test
RUN mkdir -p /home/test/.ssh
RUN echo "$SSH_PUB_KEY" > /home/test/.ssh/authorized_keys
RUN chmod 700 /home/test/.ssh
RUN chown -R test /home/test/.ssh
RUN useradd -rm -d /home/test2 -s /bin/bash -g root -G sudo -u 1002 test2
RUN usermod -aG sudo test2
RUN mkdir -p /home/test2/.ssh
RUN echo "$SSH_PUB_KEY" > /home/test2/.ssh/authorized_keys
RUN chmod 700 /home/test2/.ssh
RUN chown -R test2 /home/test2/.ssh
RUN service ssh start
RUN echo 'test:test' | chpasswd
RUN echo 'test2:test2' | chpasswd
EXPOSE 22
ADD entrypoint.sh /docker-entrypoint.d/entrypoint.sh
RUN chmod +x /docker-entrypoint.d/entrypoint.sh
CMD ["nginx", "-g", "daemon off;"]

View file

@ -1,3 +0,0 @@
#!/bin/bash
/usr/sbin/sshd -D