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