├── 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  [](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 |
--------------------------------------------------------------------------------