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