diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..d2bfd43 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,62 @@ +name: scp files +on: [push] + +jobs: + build: + name: Build + runs-on: ubuntu-latest + + steps: + - name: checkout + uses: actions/checkout@v1 + + - name: ssh scp ssh pipelines + uses: cross-the-world/ssh-scp-ssh-pipelines@v1.0.0 + env: + WELCOME: "ssh scp ssh pipelines" + LASTSSH: "Doing something after copying" + with: + host: {{ secrets.DC_HOST }} + user: {{ secrets.DC_USER }} + pass: {{ secrets.DC_PASS }} + port: {{ secrets.DC_PORT }} + connect_timeout: 10s + first_ssh: | + rm -rf /home/github/test + ls -la \necho $WELCOME + mkdir -p /home/github/test/test1 && + mkdir -p /home/github/test/test2 && + scp: | + './test/*' => /home/github/test/ + ./test/test1* => /home/github/test/test1/ + ./test/test*.csv => "/home/github/test/test2/" + last_ssh: | + echo $LASTSSH && + (mkdir test1/test || true) + || ls -la + + - name: scp ssh pipelines + uses: cross-the-world/ssh-scp-ssh-pipelines@v1.0.0 + env: + WELCOME: "scp ssh pipelines" + with: + host: {{ secrets.DC_HOST }} + user: {{ secrets.DC_USER }} + pass: {{ secrets.DC_PASS }} + scp: | + ./test/test1* => /home/github/test/test1/ + ./test/test*.csv => "/home/github/test/test2/" + last_ssh: | + echo $LASTSSH + ls -la + + - name: scp pipelines + uses: cross-the-world/ssh-scp-ssh-pipelines@v1.0.0 + env: + WELCOME: "scp pipelines" + with: + host: {{ secrets.DC_HOST }} + user: {{ secrets.DC_USER }} + pass: {{ secrets.DC_PASS }} + scp: | + './test/*' => /home/github/test/ \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..043359d --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea + +test.sh +.env \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..eb9de63 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +FROM alpine:latest + +LABEL "maintainer"="Scott Ng " +LABEL "repository"="https://github.com/cross-the-world/ssh-scp-ssh-pipelines" +LABEL "version"="1.0.0" + +LABEL "com.github.actions.name"="ssh-scp-ssh-pipelines" +LABEL "com.github.actions.description"="Pipelines: ssh -> scp -> ssh" +LABEL "com.github.actions.icon"="terminal" +LABEL "com.github.actions.color"="gray-dark" + +RUN apk update && \ + apk add ca-certificates && \ + apk add --no-cache openssh-client openssl openssh sshpass && \ + apk add --no-cache --upgrade bash openssh sshpass && \ + rm -rf /var/cache/apk/* + +COPY entrypoint.sh /entrypoint.sh +COPY test /opt/test +RUN chmod +x /entrypoint.sh + +ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..5e82238 --- /dev/null +++ b/README.md @@ -0,0 +1,115 @@ +# SSH SCP SSH Pipelines + +Credit to [SSH SCP Action of Ali Najafizadeh](https://github.com/alinz/ssh-scp-action) + +This action allows doing +1. ssh +2. scp +3. ssh + +## Inputs +see the [action.yml](./action.yml) file for more detail imformation. + +### `host` + +**Required** ssh remote host. + +### `port` + +**NOT Required** ssh remote port. Default 22 + +### `user` + +**Required** ssh remote user. + +### `pass` + +**NOT Required** ssh remote pass. + +### `key` + +**NOT Required** ssh remote key as string. + +### `connect_timeout` + +**NOT Required** connection timeout to remote host. Default 30s + +### `first_ssh` + +**NOT Required** execute pre-commands before scp. + +### `scp` + +**NOT Required** scp from local to remote. + +**Syntax** +local_path => remote_path +e.g. +/opt/test/* => /home/github/test + +### `last_ssh` + +**NOT Required** execute pre-commands after scp. + + +## Usages + +#### ssh scp ssh pipelines +```yaml +- name: ssh scp ssh pipelines + uses: cross-the-world/ssh-scp-ssh-pipelines@v1.0.0 + env: + WELCOME: "ssh scp ssh pipelines" + LASTSSH: "Doing something after copying" + with: + host: {{ secrets.DC_HOST }} + user: {{ secrets.DC_USER }} + pass: {{ secrets.DC_PASS }} + port: {{ secrets.DC_PORT }} + connect_timeout: 10s + first_ssh: | + rm -rf /home/github/test + ls -la \necho $WELCOME + mkdir -p /home/github/test/test1 && + mkdir -p /home/github/test/test2 && + scp: | + './test/*' => /home/github/test/ + ./test/test1* => /home/github/test/test1/ + ./test/test*.csv => "/home/github/test/test2/" + last_ssh: | + echo $LASTSSH && + (mkdir test1/test || true) + || ls -la +``` + +#### scp ssh pipelines +```yaml +- name: scp ssh pipelines + uses: cross-the-world/ssh-scp-ssh-pipelines@v1.0.0 + env: + LASTSSH: "Doing something after copying" + with: + host: {{ secrets.DC_HOST }} + user: {{ secrets.DC_USER }} + pass: {{ secrets.DC_PASS }} + scp: | + ./test/test1* => /home/github/test/test1/ + ./test/test*.csv => "/home/github/test/test2/" + last_ssh: | + echo $LASTSSH + ls -la +``` + +#### scp pipelines +```yaml +- name: scp pipelines + uses: cross-the-world/ssh-scp-ssh-pipelines@v1.0.0 + with: + host: {{ secrets.DC_HOST }} + user: {{ secrets.DC_USER }} + pass: {{ secrets.DC_PASS }} + scp: | + './test/*' => /home/github/test/ +``` + + diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..9f67835 --- /dev/null +++ b/action.yml @@ -0,0 +1,38 @@ +name: 'ssh-scp-ssh-pipelines' +description: 'Pipelines: ssh -> scp -> ssh' +author: 'Scott Ng' +inputs: + host: + description: 'ssh remote host' + required: true + port: + description: 'ssh remote port' + default: 22 + user: + description: 'ssh remote user' + required: true + key: + description: 'content of ssh private key. ex raw content of ~/.ssh/id_rsa' + required: false + pass: + description: 'ssh remote password' + required: false + connect_timeout: + description: 'connection timeout to remote host' + default: "30s" + required: false + first_ssh: + description: 'execute pre-commands before scp' + required: false + scp: + description: 'scp from local to remote' + required: false + last_ssh: + description: 'execute post-commands after scp' + required: false +runs: + using: 'docker' + image: 'Dockerfile' +branding: + icon: 'terminal' + color: 'gray-dark' \ No newline at end of file diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..3c64795 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,140 @@ +#!/bin/bash + +set -e + +createKeyFile() { + local SSH_PATH="$HOME/.ssh" + + mkdir -p "$SSH_PATH" + touch "$SSH_PATH/known_hosts" + + echo "$INPUT_KEY" > "$SSH_PATH/id_rsa" + + chmod 700 "$SSH_PATH" + chmod 600 "$SSH_PATH/known_hosts" + chmod 600 "$SSH_PATH/id_rsa" + + eval $(ssh-agent) + ssh-add "$SSH_PATH/id_rsa" + + ssh-keyscan -t rsa "$INPUT_HOST" >> "$SSH_PATH/known_hosts" +} + +executeSSH() { + local USEPASS=$1 + local LINES=$(echo -e $2) + local COMMAND="" + + # holds all commands separated by semi-colon or keep "&&" + local COMMANDS="" + + # this while read each commands in line and + # evaluate each line against all environment variables + while IFS=$'\n' read -r LINE; do + LINE=$(echo $LINE) + COMBINE="&&" + LASTCOMBINE="&&" + if [[ $LINE =~ ^.*\&\&$ ]]; then + LINE="$LINE true" + elif [[ $LINE =~ ^\&\&.*$ ]]; then + LINE="true $LINE" + elif [[ $LINE =~ ^.*\|\|$ ]]; then + LINE="$LINE false" + LASTCOMBINE="||" + elif [[ $LINE =~ ^\|\|.*$ ]]; then + LINE="false $LINE" + COMBINE="||" + fi + LINE=$(eval 'echo "$LINE"') + LINE=$(eval echo "$LINE") + LINE="$LINE $LASTCOMBINE" + + if [ -z "$COMMANDS" ]; then + COMMANDS="$LINE" + else + # ref. https://unix.stackexchange.com/questions/459923/multiple-commands-in-sshpass + if [[ $COMMANDS =~ ^.*\&\&$ ]] || [[ $COMMANDS =~ ^.*\|\|$ ]]; then + COMMANDS="$COMMANDS $LINE" + else + COMMANDS="$COMMANDS $COMBINE $LINE" + fi + fi + done <<< "$LINES" + + if [[ $COMMANDS =~ ^.*\&\&$ ]]; then + COMMANDS="$COMMANDS true" + elif [[ $COMMANDS =~ ^.*\|\|$ ]]; then + COMMANDS="$COMMANDS false" + fi + echo "$COMMANDS" + + CMD="ssh" + if $USEPASS; then + CMD="sshpass -p $INPUT_PASS ssh" + fi + $CMD -o StrictHostKeyChecking=no -o ConnectTimeout=${INPUT_CONNECT_TIMEOUT:-30s} -p "${INPUT_PORT:-22}" "$INPUT_USER"@"$INPUT_HOST" "$COMMANDS" > /dev/stdout +} + +executeSCP() { + local USEPASS=$1 + local LINES=$(echo -e $2) + local COMMAND= + + CMD="scp" + if $USEPASS; then + CMD="sshpass -p $INPUT_PASS scp" + fi + + while IFS=$'\n' read -r LINE; do + delimiter="=>" + LINE=`echo $LINE` + s=$LINE$delimiter + arr=() + while [[ $s ]]; do + arr+=( "${s%%"$delimiter"*}" ); + s=${s#*"$delimiter"}; + done; + LOCAL=$(eval 'echo "${arr[0]}"') + LOCAL=$(eval echo "$LOCAL") + REMOTE=$(eval 'echo "${arr[1]}"') + REMOTE=$(eval echo "$REMOTE") + + if [[ -z "${LOCAL}" ]] || [[ -z "${REMOTE}" ]]; then + echo "LOCAL/REMOTE can not be parsed $LINE" + else + echo "Copying $LOCAL ---> $REMOTE" + $CMD -r -o StrictHostKeyChecking=no -o ConnectTimeout=${INPUT_CONNECT_TIMEOUT:-30s} -P "${INPUT_PORT:-22}" $LOCAL "$INPUT_USER"@"$INPUT_HOST":$REMOTE > /dev/stdout + fi + done <<< "$LINES" +} + + +###################################################################################### + +echo "+++++++++++++++++++STARTING PIPELINES+++++++++++++++++++" + +USEPASS=true +if [[ -z "${INPUT_KEY}" ]]; then + echo "+++++++++++++++++++Use password+++++++++++++++++++" +else + echo "+++++++++++++++++++Create Key File+++++++++++++++++++" + USEPASS=false + createKeyFile || false +fi + +if ! [[ -z "${INPUT_FIRST_SSH}" ]]; then + echo "+++++++++++++++++++Step 1: RUNNING SSH+++++++++++++++++++" + executeSSH "$USEPASS" "$INPUT_FIRST_SSH" || false +fi + +if ! [[ -z "${INPUT_SCP}" ]]; then + echo "+++++++++++++++++++Step 2: RUNNING SCP+++++++++++++++++++" + executeSCP "$USEPASS" "$INPUT_SCP" || false +fi + +if ! [[ -z "${INPUT_LAST_SSH}" ]]; then + echo "+++++++++++++++++++Step 3: RUNNING SSH+++++++++++++++++++" + executeSSH "$USEPASS" "$INPUT_LAST_SSH" || false +fi + +echo "+++++++++++++++++++END PIPELINES+++++++++++++++++++" diff --git a/test/test1.csv b/test/test1.csv new file mode 100644 index 0000000..e69de29 diff --git a/test/test1.txt b/test/test1.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/test2.csv b/test/test2.csv new file mode 100644 index 0000000..e69de29