├── .github └── workflows │ └── main.yml ├── Dockerfile ├── LICENSE ├── README.md ├── action.yml └── entrypoint.sh /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | paths: 6 | - .github/workflows/main.yml 7 | - action.yml 8 | - Dockerfile 9 | - entrypoint.sh 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | name: Testing and build 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: up9cloud/action-rsync@master 18 | name: | 19 | Mode: push, local (SOURCE) to remote (TARGET), run scripts on remote (TARGET) 20 | env: 21 | HOST: ${{secrets.DEPLOY_HOST}} 22 | KEY: ${{secrets.DEPLOY_SSH_KEY}} 23 | TARGET: /tmp/action-rsync_push/ 24 | VERBOSE: true 25 | PRE_SCRIPT: | 26 | echo I ❤️ this action! 27 | rm -fr /tmp/action-rsync_push 28 | date -u 29 | POST_SCRIPT: "ls /tmp/action-rsync_push && date -u" 30 | - uses: up9cloud/action-rsync@master 31 | name: | 32 | Test multiple hosts 33 | env: 34 | REMOTE_HOSTS: ${{secrets.DEPLOY_HOST}},${{secrets.DEPLOY_HOST}} 35 | KEY: ${{secrets.DEPLOY_SSH_KEY}} 36 | TARGET: /tmp/action-rsync_push/ 37 | VERBOSE: true 38 | PRE_SCRIPT: | 39 | echo I ❤️ this action! 40 | rm -fr /tmp/action-rsync_push 41 | date -u 42 | POST_SCRIPT: "ls /tmp/action-rsync_push && date -u" 43 | - uses: up9cloud/action-rsync@master 44 | name: | 45 | Mode: push, local (SOURCE) to remote (TARGET), ssh login by PASSWORD 46 | env: 47 | HOST: ${{secrets.DEPLOY_HOST}} 48 | USER: ${{secrets.DEPLOY_SSH_USER}} 49 | PASSWORD: ${{secrets.DEPLOY_SSH_PASSWORD}} 50 | TARGET: /tmp/action-rsync_push_via_ssh_password/ 51 | VERBOSE: true 52 | PRE_SCRIPT: | 53 | date -u 54 | POST_SCRIPT: "pwd && date -u" 55 | - uses: up9cloud/action-rsync@master 56 | name: | 57 | Mode: push, local (SOURCE) to remote (TARGET), run scripts at local (SOURCE) 58 | env: 59 | HOST: ${{secrets.DEPLOY_HOST}} 60 | KEY: ${{secrets.DEPLOY_SSH_KEY}} 61 | TARGET: /tmp/action-rsync/ 62 | VERBOSE: true 63 | RUN_SCRIPT_ON: local 64 | PRE_SCRIPT: | 65 | if ls /tmp/action-rsync; then 66 | exit 2 67 | fi 68 | date -u 69 | POST_SCRIPT: "if ls /tmp/action-rsync; then exit 2; fi && date -u" 70 | - uses: up9cloud/action-rsync@master 71 | name: | 72 | Mode: pull, remote (SOURCE) to local (TARGET), run scripts at local (TARGET) 73 | env: 74 | MODE: pull 75 | HOST: ${{secrets.DEPLOY_HOST}} 76 | KEY: ${{secrets.DEPLOY_SSH_KEY}} 77 | SOURCE: /root/.profile 78 | TARGET: /tmp/.profile 79 | VERBOSE: true 80 | PRE_SCRIPT: | 81 | if ls /tmp/.profile; then 82 | exit 2 83 | fi 84 | date -u 85 | POST_SCRIPT: "cat /tmp/.profile && date -u" 86 | - uses: up9cloud/action-rsync@master 87 | name: | 88 | Mode: pull, remote (SOURCE) to local (TARGET), run scripts on remote (SOURCE) 89 | env: 90 | MODE: pull 91 | HOST: ${{secrets.DEPLOY_HOST}} 92 | KEY: ${{secrets.DEPLOY_SSH_KEY}} 93 | SOURCE: /root/.profile 94 | TARGET: /tmp/.profile 95 | VERBOSE: true 96 | RUN_SCRIPT_ON: source 97 | PRE_SCRIPT: | 98 | rm -fr /tmp/action-rsync_pull 99 | date -u 100 | POST_SCRIPT: "if ls /tmp/action-rsync_pull; then exit 2; fi && date -u" 101 | - uses: up9cloud/action-rsync@master 102 | name: | 103 | Mode: local, local (SOURCE) to local (TARGET), run scripts at local (always) 104 | env: 105 | MODE: local 106 | TARGET: /tmp/action-rsync_local/ 107 | VERBOSE: true 108 | PRE_SCRIPT: | 109 | if ls /tmp/action-rsync_local; then 110 | exit 2 111 | fi 112 | date -u 113 | POST_SCRIPT: "ls /tmp/action-rsync_local && date -u" 114 | - uses: docker/setup-buildx-action@v3 115 | if: ${{ github.event_name == 'push' && success() }} 116 | - uses: docker/login-action@v3 117 | if: ${{ github.event_name == 'push' && success() }} 118 | with: 119 | username: ${{ secrets.DOCKER_USERNAME }} 120 | password: ${{ secrets.DOCKER_PASSWORD }} 121 | - name: Push to Docker Hub 122 | uses: docker/build-push-action@v5 123 | if: ${{ github.event_name == 'push' && success() }} 124 | with: 125 | push: true 126 | tags: sstc/action-rsync:latest 127 | - name: Notify telegram 128 | uses: up9cloud/action-notify@master 129 | if: cancelled() == false 130 | env: 131 | GITHUB_JOB_STATUS: ${{ job.status }} 132 | TELEGRAM_BOT_TOKEN: ${{secrets.TELEGRAM_BOT_TOKEN}} 133 | TELEGRAM_CHAT_ID: ${{secrets.TELEGRAM_CHAT_ID}} 134 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM sstc/rsync 2 | 3 | COPY entrypoint.sh /entrypoint.sh 4 | 5 | ENTRYPOINT ["/entrypoint.sh"] 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2024 https://github.com/up9cloud 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # action-rsync ![.github/workflows/main.yml](https://github.com/up9cloud/action-rsync/workflows/.github/workflows/main.yml/badge.svg) [![Docker Automated build](https://img.shields.io/docker/automated/sstc/action-rsync)](https://hub.docker.com/repository/docker/sstc/action-rsync) 2 | 3 | - Small: Alpine based image with pre-installed rsync, see [sstc/rsync](https://hub.docker.com/r/sstc/rsync). 4 | - Hooks: Basic pre and post scripts support. 5 | - Pure 😊: No github specific inputs, outputs, it can be used on other platform! 6 | 7 | ## Quick start 8 | 9 | > Github Action (`.github/workflows/*.yml`) 10 | 11 | ```yml 12 | on: [push] 13 | jobs: 14 | rsync: 15 | runs-on: ubuntu-latest 16 | steps: 17 | # Must checkout first, otherwise it would show empty folder, see https://github.com/actions/checkout 18 | - uses: actions/checkout@v4 19 | # Modify `master` to a valid version, see https://github.com/marketplace/actions/action-rsync 20 | - uses: up9cloud/action-rsync@master 21 | env: 22 | HOST: target.example.com 23 | KEY: ${{secrets.DEPLOY_SSH_KEY}} 24 | TARGET: /app/ 25 | ``` 26 | 27 | > Drone CI (`.drone.yml`) 28 | 29 | ```yml 30 | kind: pipeline 31 | type: docker 32 | name: default 33 | 34 | steps: 35 | - name: deploy 36 | when: 37 | branch: 38 | - master 39 | event: [push] 40 | image: sstc/action-rsync 41 | settings: 42 | # lowercase attributes, see https://readme.drone.io/plugins/overview/#plugin-inputs 43 | key: 44 | from_secret: deploy_ssh_key 45 | host: target.example.com 46 | target: /app/ 47 | ``` 48 | 49 | > Docker Container 50 | 51 | ```bash 52 | docker run -it --rm \ 53 | -v $(pwd):/app \ 54 | -w /app \ 55 | -e HOST="target.example.com" \ 56 | -e KEY="$(cat ~/.ssh/id_rsa)" 57 | -e TARGET="/app/" \ 58 | sstc/action-rsync 59 | 60 | # Or aliases with prefix PLUGIN_, based on drone ci envs 61 | docker run -it --rm \ 62 | -v $(pwd):/app \ 63 | -w /app \ 64 | -e PLUGIN_HOST="target.example.com" \ 65 | -e PLUGIN_KEY="$(cat ~/.ssh/id_rsa)" 66 | -e PLUGIN_TARGET="/app/" \ 67 | sstc/action-rsync 68 | ``` 69 | 70 | ## ENVs 71 | 72 | ||Default Value|Description| 73 | |---|---|---| 74 | |**`HOST`**||Remote server ssh hostname or ip address
**Required if** **`MODE`** is `push` or `pull`| 75 | |**`REMOTE_HOSTS`**||Multiple remote hosts
Could be comma separate items style, e.q. 1.2.3.4,8.8.4.4
or one line one item style, e.q. 8.8.8.8\n111.111.111.111
It will execute pre & post scripts on every host
**Required if** **`MODE`** is `push` or `pull`| 76 | |**`USER`**|`root`|Remote server ssh user
It's useless when **`MODE`** is `local`| 77 | |**`PORT`**|`22`|Remote server ssh port
It's useless when **`MODE`** is `local`| 78 | |**`KEY`**||The ssh private key
**Required if** **`PASSWORD`** is not provided and **`MODE`** is `push` or `pull`| 79 | |**`PASSWORD`**||The ssh password
**Required if** **`KEY`** is not provided and **`MODE`** is `push` or `pull`| 80 | |**`SOURCE`**|`./`|Source path for folder or file| 81 | |**`TARGET`**||Target path for folder or file
**Required**| 82 | |**`MODE`**|`push`|Must be one of:
`push`: local (SOURCE) to remote (TARGET)
`pull`: remote (SOURCE) to local (TARGET)
`local`: local (SOURCE) to local (TARGET)| 83 | |**`VERBOSE`**|`false`|Set it to `true` when you need some tips| 84 | |**`ARGS`**|`-avz --delete --exclude=/.git/ --exclude=/.github/`|Arguments for rsync| 85 | |**`ARGS_MORE`**||More rsync arguments. Append more args for rsync, it means the final rsync arguments will be: `$ARGS $ARGS_MORE`.

For example, if you set ARGS_MORE to be `--no-o --no-g` and keep ARGS as default, then the final args will be: `-avz --delete --exclude=/.git/ --exclude=/.github/ --no-o --no-g`| 86 | |**`SSH_ARGS`**|`-p 22 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=quiet`|Arguments for ssh. The value of `-p` is dynamic, depends on what value you set for `PORT`, but what if you set SSH_ARGS, the PORT would be ignored| 87 | |**`RUN_SCRIPT_ON`**|`target`|Must be one of:
`target`: When **`MODE`** is `push`, run pre and post scripts on remote (because the target is on remote). When **`MODE`** is others, run on local.
`source`: When **`MODE`** is `push`, run pre and post scripts on local. When **`MODE`** is others, run on remote.
`local`: Always run scripts on local.
`remote`: Always run scripts on remote.| 88 | |**`PRE_SCRIPT`**||The script runs before rsync.
The target system of RUN_SCRIPT_ON must support `mktemp` command| 89 | |**`POST_SCRIPT`**||The script runs after rsync.
The target system of RUN_SCRIPT_ON must support `mktemp` command| 90 | 91 | ### Example 92 | 93 | ```yml 94 | on: [push] 95 | jobs: 96 | rsync: 97 | runs-on: ubuntu-latest 98 | steps: 99 | - uses: actions/checkout@v4 100 | - name: Deploy to my ❤️ 101 | uses: up9cloud/action-rsync@master 102 | env: 103 | HOST: example.com 104 | KEY: ${{secrets.DEPLOY_SSH_KEY}} 105 | # PASSWORD: ${{secrets.DEPLOY_SSH_PASSWORD}} # it's less secure, using KEY instead 106 | TARGET: /app/hello-service/ 107 | 108 | VERBOSE: true 109 | USER: ubuntu 110 | # PORT: 2222 # no need to set this, because of $SSH_ARGS 111 | ARGS: -az --exclude=/.git/ 112 | SSH_ARGS: '-p 2222 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' 113 | SOURCE: ./public/ 114 | 115 | PRE_SCRIPT: | 116 | echo start at: 117 | date -u 118 | POST_SCRIPT: "echo done at: && date -u" 119 | ``` 120 | 121 | See also: [.github/workflows/main.yml](https://github.com/up9cloud/action-rsync/blob/master/.github/workflows/main.yml) 122 | 123 | ## TODO 124 | 125 | - [ ] test ssh connection before executing 126 | - [ ] benchmark, compare with other actions based on js 127 | - [ ] lock the version of docker image 128 | - [ ] let variable names more meaningful, e.q. HOST to REMOTE_HOST 129 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | # action.yml 2 | name: "Action - rsync" 3 | description: "Alpine based image with pre-installed rsync, basic pre and post scripts support. Pure docker, minimize github things." 4 | runs: 5 | using: "docker" 6 | image: "Dockerfile" 7 | branding: 8 | icon: "star" 9 | color: "yellow" 10 | -------------------------------------------------------------------------------- /entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | __err=0 5 | function log() { 6 | if [ "$VERBOSE" == "true" ]; then 7 | printf '[action-rsync] %s\n' "$@" 8 | fi 9 | } 10 | function err() { 11 | __err=$((__err + 1)) 12 | printf '[action-rsync] %s\n' "$@" 1>&2 13 | } 14 | function die() { 15 | err "$@" 16 | exit 1 17 | } 18 | 19 | function setup_keys() { 20 | local prefix="$1" 21 | K_VERBOSE="\$${prefix}VERBOSE" 22 | K_MODE="\$${prefix}MODE" 23 | K_HOST="\$${prefix}HOST" 24 | K_REMOTE_HOSTS="\$${prefix}REMOTE_HOSTS" 25 | K_TARGET="\$${prefix}TARGET" 26 | K_KEY="\$${prefix}KEY" 27 | K_PASSWORD="\$${prefix}PASSWORD" 28 | K_USER="\$${prefix}USER" 29 | K_PORT="\$${prefix}PORT" 30 | K_SOURCE="\$${prefix}SOURCE" 31 | K_ARGS="\$${prefix}ARGS" 32 | K_ARGS_MORE="\$${prefix}ARGS_MORE" 33 | K_SSH_ARGS="\$${prefix}SSH_ARGS" 34 | K_RUN_SCRIPT_ON="\$${prefix}RUN_SCRIPT_ON" 35 | K_PRE_SCRIPT="\$${prefix}PRE_SCRIPT" 36 | K_POST_SCRIPT="\$${prefix}POST_SCRIPT" 37 | } 38 | # Defaults 39 | setup_keys 40 | 41 | # Drone CI 42 | if [ -n "$PLUGIN_VERBOSE" ]; then 43 | VERBOSE="$PLUGIN_VERBOSE" 44 | fi 45 | if [ -n "$PLUGIN_MODE" ]; then 46 | MODE="$PLUGIN_MODE" 47 | fi 48 | if [ -n "$PLUGIN_HOST" ]; then 49 | HOST="$PLUGIN_HOST" 50 | fi 51 | if [ -n "$PLUGIN_REMOTE_HOSTS" ]; then 52 | REMOTE_HOSTS="$PLUGIN_REMOTE_HOSTS" 53 | fi 54 | if [ -n "$PLUGIN_TARGET" ]; then 55 | TARGET="$PLUGIN_TARGET" 56 | # Because $TARGET must be set, so we set keys here 57 | setup_keys "PLUGIN_" 58 | fi 59 | if [ -n "$PLUGIN_KEY" ]; then 60 | KEY="$PLUGIN_KEY" 61 | fi 62 | if [ -n "$PLUGIN_PASSWORD" ]; then 63 | PASSWORD="$PLUGIN_PASSWORD" 64 | fi 65 | if [ -n "$PLUGIN_USER" ]; then 66 | USER="$PLUGIN_USER" 67 | fi 68 | if [ -n "$PLUGIN_PORT" ]; then 69 | PORT="$PLUGIN_PORT" 70 | fi 71 | if [ -n "$PLUGIN_SOURCE" ]; then 72 | SOURCE="$PLUGIN_SOURCE" 73 | fi 74 | if [ -n "$PLUGIN_ARGS" ]; then 75 | ARGS="$PLUGIN_ARGS" 76 | fi 77 | if [ -n "$PLUGIN_ARGS_MORE" ]; then 78 | ARGS_MORE="$PLUGIN_ARGS_MORE" 79 | fi 80 | if [ -n "$PLUGIN_SSH_ARGS" ]; then 81 | SSH_ARGS="$PLUGIN_SSH_ARGS" 82 | fi 83 | if [ -n "$PLUGIN_RUN_SCRIPT_ON" ]; then 84 | RUN_SCRIPT_ON="$PLUGIN_RUN_SCRIPT_ON" 85 | fi 86 | if [ -n "$PLUGIN_PRE_SCRIPT" ]; then 87 | PRE_SCRIPT="$PLUGIN_PRE_SCRIPT" 88 | fi 89 | if [ -n "$PLUGIN_POST_SCRIPT" ]; then 90 | POST_SCRIPT="$PLUGIN_POST_SCRIPT" 91 | fi 92 | 93 | # Github action 94 | if [ -n "$GITHUB_WORKSPACE" ]; then 95 | cd $GITHUB_WORKSPACE 96 | fi 97 | 98 | if [ -z "$VERBOSE" ]; then 99 | VERBOSE=false 100 | fi 101 | 102 | if [ -z "$MODE" ]; then 103 | MODE=push 104 | else 105 | MODE=$(echo "$MODE" | tr '[:upper:]' '[:lower:]') 106 | 107 | case "$MODE" in 108 | push | pull | local) ;; 109 | *) 110 | die "Invalid $K_MODE. Must be one of [push, pull, local]" 111 | ;; 112 | esac 113 | fi 114 | 115 | if [ -z "$REMOTE_HOSTS" ]; then 116 | if [ -z "$HOST" ]; then 117 | case "$MODE" in 118 | push | pull) 119 | die "Must specify $K_HOST or $K_REMOTE_HOSTS! (Remote host)" 120 | ;; 121 | esac 122 | fi 123 | REMOTE_HOSTS="$HOST" 124 | else 125 | REMOTE_HOSTS="${REMOTE_HOSTS//[,]/ }" 126 | REMOTE_HOSTS="${REMOTE_HOSTS//$'\n'/ }" 127 | fi 128 | 129 | if [ -z "$TARGET" ]; then 130 | die "Must specify $K_TARGET! (Target folder or file. If you set it as a file, must set $K_SOURCE as file too.)" 131 | fi 132 | 133 | if [ -z "$KEY" ]; then 134 | if [ -z "$PASSWORD" ]; then 135 | case "$MODE" in 136 | push | pull) 137 | die "Must provide either $K_KEY or $K_PASSWORD! (ssh private key or ssh password)" 138 | ;; 139 | esac 140 | else 141 | log "Using $K_PASSWORD is less secure, please consider using $K_KEY instead." 142 | fi 143 | fi 144 | 145 | if [ -z "$USER" ]; then 146 | USER="root" 147 | case "$MODE" in 148 | push | pull) 149 | log "$K_USER not specified, using default: '$USER'." 150 | ;; 151 | esac 152 | fi 153 | 154 | if [ -z "$PORT" ]; then 155 | PORT="22" 156 | case "$MODE" in 157 | push | pull) 158 | log "$K_PORT not specified, using default: $PORT." 159 | ;; 160 | esac 161 | fi 162 | 163 | if [ -z "$SOURCE" ]; then 164 | SOURCE="./" 165 | log "$K_SOURCE not specified, using default folder: '$SOURCE'." 166 | fi 167 | 168 | if [ -z "$ARGS" ]; then 169 | ARGS="-azv --delete --exclude=/.git/ --exclude=/.github/" 170 | log "$K_ARGS not specified, using default rsync arguments: '$ARGS'." 171 | fi 172 | 173 | if [ ! -z "$ARGS_MORE" ]; then 174 | log "$K_ARGS_MORE specified, will append to $K_ARGS." 175 | fi 176 | 177 | if [ -z "$SSH_ARGS" ]; then 178 | SSH_ARGS="-p $PORT -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=quiet" 179 | case "$MODE" in 180 | push | pull) 181 | log "$K_SSH_ARGS not specified, using default: '$SSH_ARGS'." 182 | ;; 183 | esac 184 | else 185 | log "You spcified $K_SSH_ARGS, so $K_PORT will be ignored." 186 | fi 187 | 188 | if [ -z "$RUN_SCRIPT_ON" ]; then 189 | RUN_SCRIPT_ON=target 190 | log "$K_RUN_SCRIPT_ON not specified, using default: '$RUN_SCRIPT_ON'" 191 | else 192 | RUN_SCRIPT_ON=$(echo "$RUN_SCRIPT_ON" | tr '[:upper:]' '[:lower:]') 193 | fi 194 | 195 | case "$RUN_SCRIPT_ON" in 196 | local) 197 | REAL_RUN_SCRIPT_ON="$RUN_SCRIPT_ON" 198 | ;; 199 | remote) 200 | REAL_RUN_SCRIPT_ON="$RUN_SCRIPT_ON" 201 | if [ "$MODE" == "local" ]; then 202 | die "It's meaningless, you want run scripts on remote but $K_MODE is local?" 203 | fi 204 | ;; 205 | source) 206 | if [ "$MODE" == "local" ]; then 207 | REAL_RUN_SCRIPT_ON=local 208 | elif [ "$MODE" == "push" ]; then 209 | REAL_RUN_SCRIPT_ON=local 210 | else 211 | REAL_RUN_SCRIPT_ON=remote 212 | fi 213 | ;; 214 | target) 215 | if [ "$MODE" == "local" ]; then 216 | REAL_RUN_SCRIPT_ON=local 217 | elif [ "$MODE" == "push" ]; then 218 | REAL_RUN_SCRIPT_ON=remote 219 | else 220 | REAL_RUN_SCRIPT_ON=local 221 | fi 222 | ;; 223 | *) 224 | die "Invalid $K_RUN_SCRIPT_ON, must be one of [local, remote, source, target]" 225 | ;; 226 | esac 227 | 228 | # Prepare 229 | if [ -n "$KEY" ]; then 230 | case "$MODE" in 231 | push | pull) 232 | mkdir -p "$HOME/.ssh" 233 | echo "$KEY" | tr -d '\r' >"$HOME/.ssh/key" 234 | chmod 600 "$HOME/.ssh/key" 235 | ;; 236 | esac 237 | cmd_ssh=$(printf "ssh -i %s %s" "$HOME/.ssh/key" "$SSH_ARGS") 238 | elif [ -n "$PASSWORD" ]; then 239 | export SSHPASS="$PASSWORD" 240 | cmd_ssh=$(printf "sshpass -e ssh %s" "$SSH_ARGS") 241 | fi 242 | 243 | case "$MODE" in 244 | push | pull) 245 | if [ -n "$KEY" ]; then 246 | cmd_rsync=$(printf "rsync %s %s -e '%s'" "$ARGS" "$ARGS_MORE" "$cmd_ssh") 247 | elif [ -n "$PASSWORD" ]; then 248 | cmd_rsync=$(printf "sshpass -e rsync %s %s -e 'ssh %s'" "$ARGS" "$ARGS_MORE" "$SSH_ARGS") 249 | fi 250 | ;; 251 | local) 252 | cmd_rsync=$(printf "rsync %s %s" "$ARGS" "$ARGS_MORE") 253 | ;; 254 | esac 255 | case "$REAL_RUN_SCRIPT_ON" in 256 | local) 257 | cmd_rsync_script=$(printf "rsync -av") 258 | ;; 259 | remote) 260 | if [ -n "$KEY" ]; then 261 | cmd_rsync_script=$(printf "rsync -avz -e '%s'" "$cmd_ssh") 262 | elif [ -n "$PASSWORD" ]; then 263 | cmd_rsync_script=$(printf "sshpass -e rsync -avz -e 'ssh %s'" "$SSH_ARGS") 264 | fi 265 | ;; 266 | esac 267 | 268 | run_script() { 269 | local name="$1" 270 | local src="$2" 271 | 272 | log "========== $name starting ==========" 273 | local tmp_output=/tmp/target_mktemp_output 274 | if [ "$REAL_RUN_SCRIPT_ON" == "remote" ]; then 275 | eval "$cmd_ssh" "$USER@$HOST" 'mktemp' >"$tmp_output" 276 | else 277 | mktemp >"$tmp_output" 278 | fi 279 | if [ $? -ne 0 ]; then 280 | die "Run 'mktemp' command failed, make sure $REAL_RUN_SCRIPT_ON server has that command!" 281 | fi 282 | local dest=$(cat "$tmp_output") 283 | 284 | if [ "$REAL_RUN_SCRIPT_ON" == "remote" ]; then 285 | eval "$cmd_rsync_script" "$src" "$USER@$HOST:$dest" 286 | else 287 | eval "$cmd_rsync_script" "$src" "$dest" 288 | fi 289 | log "========== $name sent ==========" 290 | if [ "$REAL_RUN_SCRIPT_ON" == "remote" ]; then 291 | eval "$cmd_ssh" "$USER@$HOST" "sh $dest" 292 | else 293 | sh "$dest" 294 | fi 295 | log "========== $name executed ==========" 296 | if [ "$REAL_RUN_SCRIPT_ON" == "remote" ]; then 297 | eval "$cmd_ssh" "$USER@$HOST" "rm $dest" 298 | else 299 | rm "$dest" 300 | fi 301 | log "========== $name removed ==========" 302 | } 303 | 304 | __run_count=0 305 | run_once() { 306 | if [ -n "$PRE_SCRIPT" ]; then 307 | pre_src=$(mktemp) 308 | echo -e "$PRE_SCRIPT" >"$pre_src" 309 | run_script "Pre script" "$pre_src" 310 | fi 311 | case "$MODE" in 312 | push) 313 | eval "$cmd_rsync" "$SOURCE" "$USER@$HOST:$TARGET" 314 | ;; 315 | pull) 316 | eval "$cmd_rsync" "$USER@$HOST:$SOURCE" "$TARGET" 317 | ;; 318 | local) 319 | eval "$cmd_rsync" "$SOURCE" "$TARGET" 320 | ;; 321 | esac 322 | if [ -n "$POST_SCRIPT" ]; then 323 | post_src=$(mktemp) 324 | echo -e "$POST_SCRIPT" >"$post_src" 325 | run_script "Post script" "$post_src" 326 | fi 327 | __run_count=$((__run_count + 1)) 328 | } 329 | 330 | # Execute 331 | if [ "$MODE" == "local" ]; then 332 | run_once 333 | else 334 | for h in $REMOTE_HOSTS; do 335 | HOST="$h" 336 | run_once 337 | done 338 | fi 339 | 340 | # final handler 341 | if [[ "$__run_count" -eq 0 ]]; then 342 | err "No successful execution was detected" 343 | fi 344 | if [[ "$__err" -ne 0 ]]; then 345 | exit 1 346 | fi 347 | --------------------------------------------------------------------------------