Compare commits

...

131 Commits

Author SHA1 Message Date
Bo-Yi Wu 9b57f85e47 chore(cmd): trim space in command
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2019-11-21 09:13:04 +08:00
Bo-Yi Wu 648e3c1321 chore: support split from string text (#148) 2019-11-21 09:05:32 +08:00
Bo-Yi Wu 9ef0a47bc0 chore: add single command for github actions. 2019-11-21 00:44:00 +08:00
Bo-Yi Wu 530cb1df6e docs: update diff format 2019-10-18 22:37:47 +08:00
Bo-Yi Wu 414ebba911 chore: udpate success message 2019-09-29 12:47:36 +08:00
Bo-Yi Wu ff6e63366c docs: add example for stoping script
stoping script after first failure

fix: https://github.com/appleboy/drone-ssh/issues/143
2019-09-29 12:32:38 +08:00
Bo-Yi Wu 553894abe6 docs: update drone document
fix: https://github.com/appleboy/drone-ssh/issues/144
fix: https://github.com/appleboy/drone-ssh/issues/146
2019-09-29 12:29:29 +08:00
Bo-Yi Wu 79584d6cfa docs: update envs usage, 2019-09-29 11:21:15 +08:00
Bo-Yi Wu cacb93d984 chore: support github actions input variable. 2019-09-29 10:26:21 +08:00
Bo-Yi Wu 160519dca6 chore: upgrade go module 2019-09-29 10:18:15 +08:00
Bo-Yi Wu b8149fe18c chore: remove microbadger 2019-09-29 10:17:23 +08:00
Bo-Yi Wu 5e47c61788 chore: upgrade to go1.13 2019-09-29 10:12:57 +08:00
Bo-Yi Wu bd8532afdc docs: update markdown format 2019-08-04 07:04:52 +08:00
Bo-Yi Wu 44cc1f409f remove path variable
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2019-05-16 10:04:25 +08:00
Bo-Yi Wu b83dae891d add arch
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2019-05-15 07:08:08 +08:00
Bo-Yi Wu 27b9f04a04 remove secrets flag
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2019-05-15 06:45:04 +08:00
Bo-Yi Wu f6128860ab chore: Add short variable 2019-05-14 22:15:29 +08:00
Bo-Yi Wu 3505455bb7 Update README.md 2019-05-09 14:16:02 +08:00
Bo-Yi Wu 29018dba3a chore: switch finished to struct{} for zero allocate (#140) 2019-03-30 08:03:16 +08:00
Bo-Yi Wu e7fd9e9b89 docs: update to drone 1.0
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2019-03-11 14:47:55 +08:00
Bo-Yi Wu 56b67e1314 chore: change command timeout flag (#138)
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2019-03-11 14:36:17 +08:00
Bo-Yi Wu e79f8934dc chore: remove build number 2019-03-10 23:44:59 +08:00
Bo-Yi Wu 2fd7649e83 fix: trigger build fail if error 2019-03-10 23:40:29 +08:00
Bo-Yi Wu 89fce5b1d3 chore(variable): change UserName to Username
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2019-03-06 11:00:09 +08:00
Bo-Yi Wu 0b241dd368 chore: add revive config
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2019-03-04 16:17:27 +08:00
Bo-Yi Wu a92fa054fe chore: add lint check
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2019-03-04 16:10:45 +08:00
Bo-Yi Wu 3342cdf59a refactor: error var missingXXX should have name of the form errFoo
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2019-03-04 14:47:28 +08:00
Bo-Yi Wu aa95c51e7f refactor(error): update error
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2019-03-04 14:42:28 +08:00
Bo-Yi Wu 86a962988d docs: update logo path
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2019-03-04 14:28:19 +08:00
Bo-Yi Wu 2584920545 chore: update go module
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2019-03-04 14:27:51 +08:00
Bo-Yi Wu d128f2fecf update image path
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2019-03-04 12:05:16 +08:00
Bo-Yi Wu 83f8486e15 docs: update badge
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2019-03-04 12:01:14 +08:00
Bo-Yi Wu d32a2675d5 feat(vendor): switch to go module (#137)
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2019-03-04 10:55:52 +08:00
Bo-Yi Wu 7ac526845d Revert "feat: upgrade easyssh-proxy and update flag of timeout field (#127)" (#128)
This reverts commit eb33537e3f.
2018-10-23 15:54:40 +08:00
Bo-Yi Wu eb33537e3f feat: upgrade easyssh-proxy and update flag of timeout field (#127) 2018-10-23 15:14:48 +08:00
Bo-Yi Wu 3a227c8854 fix: Fix golint to new URL 2018-10-14 15:24:55 +08:00
Bo-Yi Wu d3999bf6cc add microbadger webhook
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2018-10-09 09:02:46 +08:00
Bo-Yi Wu f9cc37282c update golang to 1.11
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2018-09-26 15:42:19 +08:00
Bo-Yi Wu 6e431b0c53 test: add TestCommandScriptStop (#124) 2018-09-26 15:35:57 +08:00
Marco Vito Moscaritolo 3499506089 exit after first error (#123)
Closes #121 

WIP
2018-09-26 15:23:31 +08:00
Josh Komoroske 6c0b475c15 Customization of logger output destination (#117)
* Customization of logger output destination

* Tests to verify output correctness
2018-02-28 14:52:15 +08:00
Josh Komoroske 60993a71e2 Preservation of forwarded environment (#113)
* Tests for omitting unset variables

* Preservation of forwarded environment
2018-02-28 13:44:44 +08:00
Bo-Yi Wu 8bfc58f9d0 test: Add escapeArg testing (#116) 2018-02-27 14:58:43 +08:00
Damian Kaczmarek 7f4cb1c1d0 improve: shell escaping, allow for whitespace and single quotes (#108) 2018-02-27 14:48:38 +08:00
Josh Komoroske f92f762c9d Load PLUGIN_ENV_FILE before app is run (#112) 2018-02-24 19:47:07 +08:00
Bo-Yi Wu 84cb184039 refactor(drone): replace facebook with discord. 2017-12-18 09:34:36 +08:00
Bo-Yi Wu 31c084fd3e remove unused doc.
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-12-18 09:30:13 +08:00
Bo-Yi Wu 69b3a40978 update drone-docker plugin. (#107)
* update drone-docker plugin.

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>

* add testing

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>

* add testing

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>

* add testing

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-11-09 23:32:01 -06:00
Damian Kaczmarek 4d443c40f2 fix: ssh process error not resulting in pipeline error (#105)
* fix: ssh process error not resulting in pipeline error

* Update main.go
2017-11-09 20:47:15 -06:00
Bo-Yi Wu 9dd4b8db8d add arm arm64 and amd64 build. (#106)
* add arm arm64 and amd64 build.

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>

* add release folder to ignore list.

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-11-09 20:12:30 -06:00
Damian Kaczmarek 45f43d7ffd fix: escaping special characters when passing env to ssh (#104) 2017-11-09 19:01:28 -06:00
Bo-Yi Wu 7220c94832 Add sync mode. (#101)
* Add sync mode.

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>

* close channel in sync mode.

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>

* close channel in sync mode.

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-10-29 21:31:51 -05:00
Bo-Yi Wu 2d5668ff17 Update document for mount key path. (#100)
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-10-29 20:46:16 -05:00
Bo-Yi Wu 6f1ace35bf add build number for drone.
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-09-04 17:22:52 +08:00
Bo-Yi Wu 05ebe5b663 update docs
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-09-04 14:40:20 +08:00
Bo-Yi Wu e331f975ad update docs.
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-09-04 14:38:04 +08:00
Bo-Yi Wu f943ff7179 update dockerfile. (#99)
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-08-19 21:48:30 +08:00
Bo-Yi Wu 65e15c4aab update gopath (#98)
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-08-19 21:45:07 +08:00
Bo-Yi Wu 83273b5669 add cloc command
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-08-19 21:35:53 +08:00
Bo-Yi Wu a8392b5f22 fix typo
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-08-15 15:01:51 +08:00
Bo-Yi Wu e057a699a4 refactor: add group build for drone. (#97)
* refactor: add group build for drone.

* remove codecov_token

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>

* fix format

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>

* fix path

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-08-15 14:52:35 +08:00
Bo-Yi Wu 14fddbbba5 feat: add multiple stage build for docker. (#96)
* feat: add multiple stage build for docker.

* remove unused target

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-08-15 14:23:25 +08:00
Bo-Yi Wu 5fbd22f265 refactor(Makefile): allow overriding default go program (#95)
* refactor(Makefile): allow overriding default go program

* fix typo

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-08-15 14:05:11 +08:00
Bo-Yi Wu bf269615ce update docs (#94)
* update docs

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>

* [ci skip] add username secret.

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-08-13 23:06:31 +08:00
Bo-Yi Wu 538a5a6ce5 remove unsed space.
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-08-09 22:25:26 +08:00
DINESH S 78f4f15754 Update doc with custom secrets example (#93) fix #19 2017-08-09 22:24:08 +08:00
Bo-Yi Wu 40323f23e5 update testing
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-08-01 17:24:38 +08:00
Bo-Yi Wu ed83305de8 add debug mode. (#92)
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-08-01 16:54:34 +08:00
Bo-Yi Wu 4e625fa760 feat: add editor config. 2017-08-01 16:04:55 +08:00
Bo-Yi Wu c79b44dca2 update env key.
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-08-01 12:51:36 +08:00
Bo-Yi Wu c86c472904 fix: env to ToUpper
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-08-01 12:40:35 +08:00
Bo-Yi Wu ecfaecd46d feat(env): pass secret to remote server. (#91)
* feat(env): pass secret to remote server.

* add testing

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-08-01 12:01:37 +08:00
Bo-Yi Wu e6d4fa77d1 add notify
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-06-24 22:58:13 +08:00
Bo-Yi Wu 9651a4eb6c feat: add check unused package. (#85) 2017-06-03 01:13:19 -05:00
Bo-Yi Wu b5b13e8b72 upgrade easyssh to 1.1.6 (#81)
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-05-16 15:45:00 +08:00
Bo-Yi Wu 26b3d47ee2 add exit code example.
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-05-13 13:47:00 +08:00
Bo-Yi Wu 0a78278313 add return error code command example.
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-05-12 14:21:33 +08:00
Bo-Yi Wu a7c37e0936 fix panic from easyssh-proxy
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-05-11 15:14:28 +08:00
Bo-Yi Wu 699d9148d8 update readme.
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-05-10 21:55:56 +08:00
Bo-Yi Wu ceec42efdd bump easyssh package.
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-05-10 21:37:48 +08:00
Bo-Yi Wu 88b5394dac update output
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-05-10 21:26:34 +08:00
Bo-Yi Wu 1637772e0b hide domain if only single host in config. (#80)
* hide domain if only single host in config.

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>

* fix host

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>

* refactor

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-05-10 21:17:31 +08:00
Bo-Yi Wu efdac217bd update log format.
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-05-10 16:42:37 +08:00
Bo-Yi Wu f81056261d add demo gif
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-05-10 16:21:22 +08:00
Bo-Yi Wu 3fffe80a14 remove timestamp in log
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-05-10 15:36:20 +08:00
Bo-Yi Wu 2d568d1fde Support stream output logs. (#79)
* Support stream output logs.

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>

* fix block channel.

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-05-10 11:34:00 +08:00
Bo-Yi Wu f26bd7f7f7 update makefile
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-05-09 15:13:50 +08:00
Bo-Yi Wu 95427edbba update makefile
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-05-09 15:11:17 +08:00
Bo-Yi Wu 7f168bd1cb update makefile
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-05-09 15:07:57 +08:00
Bo-Yi Wu b6c973ef1e fix: get exit code from ssh run command. (#78) 2017-05-09 09:38:31 +08:00
Bo-Yi Wu 356b2ae6cc feat: support group build (#76) 2017-05-08 09:06:35 +08:00
Bo-Yi Wu b698d56d60 add some secrets.
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-05-02 22:03:26 +08:00
Bo-Yi Wu 06f4f77ebc update drone to 0.6
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-05-02 21:51:15 +08:00
Bo-Yi Wu b63f275e9e refactor: show errors if set password and key at the same time (#72) 2017-04-23 11:49:40 +08:00
Bo-Yi Wu 4d8adbffca fix: missing github release key 2017-04-21 17:16:55 +08:00
Bo-Yi Wu c73e22e279 refactor: HostKeyCallback is the function type used for verifying server (#68) 2017-04-15 20:49:33 +08:00
Bo-Yi Wu 6c2d8f278d refactor: remove drone sig. (#65) 2017-04-14 09:09:46 +08:00
Bo-Yi Wu a4dc098318 refactor: check gofmt error. (#63) 2017-04-01 22:22:26 +08:00
Bo-Yi Wu c2776cbaed feat: add when.local=false to disable plugin steps locally (#62) 2017-04-01 14:24:31 +08:00
Bo-Yi Wu 05b1a61165 refactor: upgrade easyssh to support DefaultConfig (#61) 2017-04-01 13:57:53 +08:00
Bo-Yi Wu d447bbd595 [ci skip] add proxy config docs
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-03-23 13:45:04 +08:00
Bo-Yi Wu 6921b0b786 fix: command line variable. (#58) 2017-03-04 23:13:55 +08:00
Bo-Yi Wu 20a4793249 fix: typo 2017-03-04 22:11:45 +08:00
Bo-Yi Wu b6ec7c2347 fix: remove check error string. (#57) 2017-03-04 22:01:48 +08:00
Bo-Yi Wu e5dc646e5d fix: redefined: user (#56) 2017-03-04 21:26:18 +08:00
Bo-Yi Wu 530df8d98b feat: Support proxy command. (#55) 2017-03-04 18:31:49 +08:00
Bo-Yi Wu 7e4e0224ee feat: Add time out flag. (#53)
* feat: Add time out flag.

* fix testing

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-03-04 17:50:05 +08:00
Bo-Yi Wu 28ffc3a790 release v1.1.0
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2017-03-04 17:37:25 +08:00
Bo-Yi Wu 6f6e20312f docs: [ci skip] update docs. 2017-03-03 16:15:29 +08:00
Bo-Yi Wu 4507e5e4f0 docs: improve docs (#50) 2017-03-03 16:11:21 +08:00
Bo-Yi Wu bb0d89368f refactor: hash is a much faster shell-builtin alternative to which. (#47) 2017-02-27 21:55:05 +08:00
Kittipun Khantitrirat fdc7a43f73 Fixed logging output typo when finished (#46) 2017-02-26 09:36:19 +08:00
Bo-Yi Wu 833d26f473 feat: support username flag. (#45)
* feat: support username flag.
2017-02-21 11:44:37 +08:00
Bo-Yi Wu 7871a8e9f5 feat: switch glide to govendor. (#43)
* feat: switch glide to govendor.
2017-02-18 23:45:53 +08:00
Bo-Yi Wu 3a5b4aa23b docs: [ci skip] add drone plugin document link. 2017-02-08 10:26:46 +08:00
Bo-Yi Wu 03be9e5be0 refactor: remove unnecessary func. (#40) 2017-02-04 16:37:26 +08:00
Bo-Yi Wu 3943bd28ff docs: [ci skip] add drone document. (#39) 2017-01-31 21:19:00 +08:00
Bo-Yi Wu a8cc62009f refactor: update ssh host using JoinHostPort 2017-01-30 00:45:46 +08:00
Bo-Yi Wu b704ba55cb docs: [ci skip] remove sleep flag. 2017-01-29 12:58:36 +08:00
Bo-Yi Wu 6e733c0a03 feat: add easyssh and go routine. (#38) 2017-01-29 12:57:00 +08:00
Bo-Yi Wu c086c6a226 fix: update image path. 2017-01-28 11:52:56 +08:00
Bo-Yi Wu d87def7ef9 feat: Add unconvert command 2017-01-28 11:42:54 +08:00
Bo-Yi Wu 7314547924 docs: Add change docker image note. 2017-01-25 10:22:01 +08:00
Bo-Yi Wu d9476acbfa docs: remove duplicated of port config. (#36) 2017-01-25 09:21:53 +08:00
Bo-Yi Wu da4a550afd feat: add command line flag. 2017-01-24 15:31:06 +08:00
Bo-Yi Wu 95f777f22e test: improve testing. 2017-01-24 15:06:46 +08:00
Bo-Yi Wu 876c4c1cee feat: Support import private key from file path. (#34) 2017-01-24 11:28:48 +08:00
Bo-Yi Wu 79530c6026 feat: add some testing. (#32) 2017-01-23 16:00:48 +08:00
Bo-Yi Wu ded90faebd feat: update command line. 2017-01-23 14:33:49 +08:00
Bo-Yi Wu c46dd8eadf feat: check missing host or user. 2017-01-23 14:24:27 +08:00
Bo-Yi Wu 18d0ff3a20 fix: [ci skip] tag version 2017-01-23 12:12:07 +08:00
30 changed files with 2102 additions and 341 deletions
+16
View File
@@ -0,0 +1,16 @@
local pipeline = import 'pipeline.libsonnet';
local name = 'drone-ssh';
[
pipeline.test,
pipeline.build(name, 'linux', 'amd64'),
pipeline.build(name, 'linux', 'arm64'),
pipeline.build(name, 'linux', 'arm'),
pipeline.release,
pipeline.notifications(depends_on=[
'linux-amd64',
'linux-arm64',
'linux-arm',
'release-binary',
]),
]
+356 -69
View File
@@ -1,78 +1,365 @@
workspace: ---
base: /srv/app kind: pipeline
path: src/github.com/appleboy/drone-ssh name: testing
pipeline: platform:
clone: os: linux
image: plugins/git arch: amd64
tags: true
# restore the cache from an sftp server steps:
restore_cache: - name: vet
image: appleboy/drone-sftp-cache pull: always
restore: true image: golang:1.13
mount: [ .glide, vendor ] commands:
ignore_branch: true - make vet
volumes:
- name: gopath
path: /go
test: - name: lint
image: appleboy/golang-testing pull: always
pull: true image: golang:1.13
environment: commands:
TAGS: netgo - make lint
GOPATH: /srv/app volumes:
commands: - name: gopath
- make dep_install path: /go
- make vet
- make lint
- make test
- make coverage
- make build
# build binary for docker image
- make static_build
when:
event: [ push, tag, pull_request ]
release: - name: misspell
image: appleboy/golang-testing pull: always
pull: true image: golang:1.13
environment: commands:
TAGS: netgo - make misspell-check
GOPATH: /srv/app volumes:
commands: - name: gopath
- make release path: /go
when:
event: [ tag ]
branch: [ refs/tags/* ]
publish_tag: - name: test
image: plugins/docker pull: always
repo: ${DRONE_REPO} image: golang:1.13-alpine
tags: [ '${DRONE_TAG}' ] commands:
when: - apk add git make curl perl bash build-base zlib-dev ucl-dev
event: [ tag ] - make ssh-server
branch: [ refs/tags/* ] - make test
- make coverage
volumes:
- name: gopath
path: /go
publish_latest: - name: codecov
image: plugins/docker pull: always
repo: ${DRONE_REPO} image: robertstettner/drone-codecov
tags: [ 'latest' ] settings:
when: token:
event: [ push ] from_secret: codecov_token
branch: [ master ]
release: volumes:
image: plugins/github-release - name: gopath
temp: {}
---
kind: pipeline
name: linux-amd64
platform:
os: linux
arch: amd64
steps:
- name: build-push
pull: always
image: golang:1.13
commands:
- go build -v -ldflags '-X main.build=${DRONE_BUILD_NUMBER}' -a -o release/linux/amd64/drone-ssh
environment:
CGO_ENABLED: 0
when:
event:
exclude:
- tag
- name: build-tag
pull: always
image: golang:1.13
commands:
- go build -v -ldflags '-X main.version=${DRONE_TAG##v} -X main.build=${DRONE_BUILD_NUMBER}' -a -o release/linux/amd64/drone-ssh
environment:
CGO_ENABLED: 0
when:
event:
- tag
- name: executable
pull: always
image: golang:1.13
commands:
- ./release/linux/amd64/drone-ssh --help
- name: dryrun
pull: always
image: plugins/docker:linux-amd64
settings:
cache_from: appleboy/drone-ssh
dockerfile: docker/Dockerfile.linux.amd64
dry_run: true
repo: appleboy/drone-ssh
tags: linux-amd64
when:
event:
- pull_request
- name: publish
pull: always
image: plugins/docker:linux-amd64
settings:
auto_tag: true
auto_tag_suffix: linux-amd64
cache_from: appleboy/drone-ssh
daemon_off: false
dockerfile: docker/Dockerfile.linux.amd64
password:
from_secret: docker_password
repo: appleboy/drone-ssh
username:
from_secret: docker_username
when:
event:
exclude:
- pull_request
trigger:
ref:
- refs/heads/master
- refs/pull/**
- refs/tags/**
depends_on:
- testing
---
kind: pipeline
name: linux-arm64
platform:
os: linux
arch: arm64
steps:
- name: build-push
pull: always
image: golang:1.13
commands:
- go build -v -ldflags '-X main.build=${DRONE_BUILD_NUMBER}' -a -o release/linux/arm64/drone-ssh
environment:
CGO_ENABLED: 0
when:
event:
exclude:
- tag
- name: build-tag
pull: always
image: golang:1.13
commands:
- go build -v -ldflags '-X main.version=${DRONE_TAG##v} -X main.build=${DRONE_BUILD_NUMBER}' -a -o release/linux/arm64/drone-ssh
environment:
CGO_ENABLED: 0
when:
event:
- tag
- name: executable
pull: always
image: golang:1.13
commands:
- ./release/linux/arm64/drone-ssh --help
- name: dryrun
pull: always
image: plugins/docker:linux-arm64
settings:
cache_from: appleboy/drone-ssh
dockerfile: docker/Dockerfile.linux.arm64
dry_run: true
repo: appleboy/drone-ssh
tags: linux-arm64
when:
event:
- pull_request
- name: publish
pull: always
image: plugins/docker:linux-arm64
settings:
auto_tag: true
auto_tag_suffix: linux-arm64
cache_from: appleboy/drone-ssh
daemon_off: false
dockerfile: docker/Dockerfile.linux.arm64
password:
from_secret: docker_password
repo: appleboy/drone-ssh
username:
from_secret: docker_username
when:
event:
exclude:
- pull_request
trigger:
ref:
- refs/heads/master
- refs/pull/**
- refs/tags/**
depends_on:
- testing
---
kind: pipeline
name: linux-arm
platform:
os: linux
arch: arm
steps:
- name: build-push
pull: always
image: golang:1.13
commands:
- go build -v -ldflags '-X main.build=${DRONE_BUILD_NUMBER}' -a -o release/linux/arm/drone-ssh
environment:
CGO_ENABLED: 0
when:
event:
exclude:
- tag
- name: build-tag
pull: always
image: golang:1.13
commands:
- go build -v -ldflags '-X main.version=${DRONE_TAG##v} -X main.build=${DRONE_BUILD_NUMBER}' -a -o release/linux/arm/drone-ssh
environment:
CGO_ENABLED: 0
when:
event:
- tag
- name: executable
pull: always
image: golang:1.13
commands:
- ./release/linux/arm/drone-ssh --help
- name: dryrun
pull: always
image: plugins/docker:linux-arm
settings:
cache_from: appleboy/drone-ssh
dockerfile: docker/Dockerfile.linux.arm
dry_run: true
repo: appleboy/drone-ssh
tags: linux-arm
when:
event:
- pull_request
- name: publish
pull: always
image: plugins/docker:linux-arm
settings:
auto_tag: true
auto_tag_suffix: linux-arm
cache_from: appleboy/drone-ssh
daemon_off: false
dockerfile: docker/Dockerfile.linux.arm
password:
from_secret: docker_password
repo: appleboy/drone-ssh
username:
from_secret: docker_username
when:
event:
exclude:
- pull_request
trigger:
ref:
- refs/heads/master
- refs/pull/**
- refs/tags/**
depends_on:
- testing
---
kind: pipeline
name: release-binary
platform:
os: linux
arch: amd64
steps:
- name: build-all-binary
pull: always
image: golang:1.13
commands:
- make release
when:
event:
- tag
- name: deploy-all-binary
pull: always
image: plugins/github-release
settings:
api_key:
from_secret: github_release_api_key
files: files:
- dist/release/* - dist/release/*
when: when:
event: [ tag ] event:
branch: [ refs/tags/* ] - tag
# rebuild the cache on the sftp server trigger:
rebuild_cache: ref:
image: appleboy/drone-sftp-cache - refs/tags/**
rebuild: true
mount: [ .glide, vendor ] depends_on:
ignore_branch: true - testing
when:
branch: master ---
kind: pipeline
name: notifications
platform:
os: linux
arch: amd64
steps:
- name: manifest
pull: always
image: plugins/manifest
settings:
ignore_missing: true
password:
from_secret: docker_password
spec: docker/manifest.tmpl
username:
from_secret: docker_username
trigger:
ref:
- refs/heads/master
- refs/tags/**
depends_on:
- linux-amd64
- linux-arm64
- linux-arm
- release-binary
...
-1
View File
@@ -1 +0,0 @@
eyJhbGciOiJIUzI1NiJ9.d29ya3NwYWNlOgogIGJhc2U6IC9zcnYvYXBwCiAgcGF0aDogc3JjL2dpdGh1Yi5jb20vYXBwbGVib3kvZHJvbmUtc3NoCgpwaXBlbGluZToKICBjbG9uZToKICAgIGltYWdlOiBwbHVnaW5zL2dpdAogICAgdGFnczogdHJ1ZQoKICAjIHJlc3RvcmUgdGhlIGNhY2hlIGZyb20gYW4gc2Z0cCBzZXJ2ZXIKICByZXN0b3JlX2NhY2hlOgogICAgaW1hZ2U6IGFwcGxlYm95L2Ryb25lLXNmdHAtY2FjaGUKICAgIHJlc3RvcmU6IHRydWUKICAgIG1vdW50OiBbIC5nbGlkZSwgdmVuZG9yIF0KICAgIGlnbm9yZV9icmFuY2g6IHRydWUKCiAgdGVzdDoKICAgIGltYWdlOiBhcHBsZWJveS9nb2xhbmctdGVzdGluZwogICAgcHVsbDogdHJ1ZQogICAgZW52aXJvbm1lbnQ6CiAgICAgIFRBR1M6IG5ldGdvCiAgICAgIEdPUEFUSDogL3Nydi9hcHAKICAgIGNvbW1hbmRzOgogICAgICAtIG1ha2UgZGVwX2luc3RhbGwKICAgICAgLSBtYWtlIHZldAogICAgICAtIG1ha2UgbGludAogICAgICAtIG1ha2UgdGVzdAogICAgICAtIG1ha2UgY292ZXJhZ2UKICAgICAgLSBtYWtlIGJ1aWxkCiAgICAgICMgYnVpbGQgYmluYXJ5IGZvciBkb2NrZXIgaW1hZ2UKICAgICAgLSBtYWtlIHN0YXRpY19idWlsZAogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCwgdGFnLCBwdWxsX3JlcXVlc3QgXQoKICByZWxlYXNlOgogICAgaW1hZ2U6IGFwcGxlYm95L2dvbGFuZy10ZXN0aW5nCiAgICBwdWxsOiB0cnVlCiAgICBlbnZpcm9ubWVudDoKICAgICAgVEFHUzogbmV0Z28KICAgICAgR09QQVRIOiAvc3J2L2FwcAogICAgY29tbWFuZHM6CiAgICAgIC0gbWFrZSByZWxlYXNlCiAgICB3aGVuOgogICAgICBldmVudDogWyB0YWcgXQogICAgICBicmFuY2g6IFsgcmVmcy90YWdzLyogXQoKICBwdWJsaXNoX3RhZzoKICAgIGltYWdlOiBwbHVnaW5zL2RvY2tlcgogICAgcmVwbzogJHtEUk9ORV9SRVBPfQogICAgdGFnczogWyAnJHtEUk9ORV9UQUd9JyBdCiAgICB3aGVuOgogICAgICBldmVudDogWyB0YWcgXQogICAgICBicmFuY2g6IFsgcmVmcy90YWdzLyogXQoKICBwdWJsaXNoX2xhdGVzdDoKICAgIGltYWdlOiBwbHVnaW5zL2RvY2tlcgogICAgcmVwbzogJHtEUk9ORV9SRVBPfQogICAgdGFnczogWyAnbGF0ZXN0JyBdCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoIF0KICAgICAgYnJhbmNoOiBbIG1hc3RlciBdCgogIHJlbGVhc2U6CiAgICBpbWFnZTogcGx1Z2lucy9naXRodWItcmVsZWFzZQogICAgZmlsZXM6CiAgICAgIC0gZGlzdC9yZWxlYXNlLyoKICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHRhZyBdCiAgICAgIGJyYW5jaDogWyByZWZzL3RhZ3MvKiBdCgogICMgcmVidWlsZCB0aGUgY2FjaGUgb24gdGhlIHNmdHAgc2VydmVyCiAgcmVidWlsZF9jYWNoZToKICAgIGltYWdlOiBhcHBsZWJveS9kcm9uZS1zZnRwLWNhY2hlCiAgICByZWJ1aWxkOiB0cnVlCiAgICBtb3VudDogWyAuZ2xpZGUsIHZlbmRvciBdCiAgICBpZ25vcmVfYnJhbmNoOiB0cnVlCiAgICB3aGVuOgogICAgICBicmFuY2g6IG1hc3Rlcgo.MvA91xJBP4JoAxLObpbaumlNtzjO_Gc3znDbTRUjAGY
+42
View File
@@ -0,0 +1,42 @@
# unifying the coding style for different editors and IDEs => editorconfig.org
; indicate this is the root of the project
root = true
###########################################################
; common
###########################################################
[*]
charset = utf-8
end_of_line = LF
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 2
###########################################################
; make
###########################################################
[Makefile]
indent_style = tab
[makefile]
indent_style = tab
###########################################################
; markdown
###########################################################
[*.md]
trim_trailing_whitespace = false
###########################################################
; golang
###########################################################
[*.go]
indent_style = tab
+2 -1
View File
@@ -25,5 +25,6 @@ _testmain.go
.env .env
coverage.txt coverage.txt
release
drone-ssh drone-ssh
vendor .cover
+25
View File
@@ -0,0 +1,25 @@
ignoreGeneratedHeader = false
severity = "warning"
confidence = 0.8
errorCode = 1
warningCode = 1
[rule.blank-imports]
[rule.context-as-argument]
[rule.context-keys-type]
[rule.dot-imports]
[rule.error-return]
[rule.error-strings]
[rule.error-naming]
[rule.exported]
[rule.if-return]
[rule.increment-decrement]
[rule.var-naming]
[rule.var-declaration]
[rule.package-comments]
[rule.range]
[rule.receiver-naming]
[rule.time-naming]
[rule.unexported-return]
[rule.indent-error-flow]
[rule.errorf]
+196 -50
View File
@@ -1,39 +1,21 @@
Use the SSH plugin to execute commands on a remote server. You will need to ---
supply Drone with a private SSH key to being able to connect to a host. date: 2019-08-04T00:00:00+00:00
title: SSH
author: appleboy
tags: [ deploy, publish, ssh ]
repo: appleboy/drone-ssh
logo: term.svg
image: appleboy/drone-ssh
---
## Config Use the SSH plugin to execute commands on a remote server. The below pipeline configuration demonstrates simple usage:
The following parameters are used to configure the plugin:
* **host** - address or IP of the remote machine
* **port** - port to connect to on the remote machine
* **user** - user to log in as on the remote machine
* **passsword** - password to log in as on the remote machine
* **key** - private SSH key for the remote machine
* **sleep** - sleep for seconds between host connections
* **timeout** - timeout for the tcp connection attempt
* **script** - list of commands to execute
The following secret values can be set to configure the plugin.
* **SSH_HOST** - corresponds to **host**
* **SSH_PORT** - corresponds to **port**
* **SSH_USER** - corresponds to **user**
* **SSH_PASSWORD** - corresponds to **password**
* **SSH_KEY** - corresponds to **key**
* **SSH_SLEEP** - corresponds to **sleep**
* **SSH_TIMEOUT** - corresponds to **timeout**
## Examples
Example configuration in your .drone.yml file for a single host:
```yaml ```yaml
pipeline: - name: ssh commands
ssh: image: appleboy/drone-ssh
image: plugins/ssh settings:
host: foo.com host: foo.com
user: root username: root
password: 1234 password: 1234
port: 22 port: 22
script: script:
@@ -41,27 +23,191 @@ pipeline:
- echo world - echo world
``` ```
Example configuration in your .drone.yml file for multiple hosts: Example configuration in your `.drone.yml` file for multiple hosts:
```yaml ```diff
pipeline: - name: ssh commands
ssh: image: appleboy/drone-ssh
image: plugins/ssh settings:
host: host:
- foo.com + - foo.com
- bar.com + - bar.com
user: root username: root
port: 22 password: 1234
sleep: 5 port: 22
script: script:
- echo hello - echo hello
- echo world - echo world
``` ```
In the above example Drone executes the commands on multiple hosts Example configuration for command timeout, default value is 60 seconds:
sequentially. If the commands fail on a single host this plugin exits
immediatly, and will not run your commands on the remaining hosts in the
list.
The above example also uses the `sleep` parameter. The sleep parameter ```diff
instructs Drone to sleep for N seconds between host executions. - name: ssh commands
image: appleboy/drone-ssh
settings:
host: foo.com
username: root
password: 1234
port: 22
+ command_timeout: 2m
script:
- echo hello
- echo world
```
Example configuration for execute commands on a remote server using SSHProxyCommand:
```diff
- name: ssh commands
image: appleboy/drone-ssh
settings:
host: foo.com
username: root
password: 1234
port: 22
script:
- echo hello
- echo world
+ proxy_host: 10.130.33.145
+ proxy_user: ubuntu
+ proxy_port: 22
+ proxy_password: 1234
```
Example configuration using password from secrets:
```diff
- name: ssh commands
image: appleboy/drone-ssh
settings:
host: foo.com
username: root
+ password:
+ from_secret: ssh_password
port: 22
script:
- echo hello
- echo world
```
Example configuration using ssh key from secrets:
```diff
- name: ssh commands
image: appleboy/drone-ssh
settings:
host: foo.com
username: root
port: 22
+ key:
+ from_secret: ssh_key
script:
- echo hello
- echo world
```
Example configuration for exporting custom secrets:
```diff
- name: ssh commands
image: appleboy/drone-ssh
settings:
host: foo.com
username: root
password: 1234
port: 22
+ envs:
- aws_access_key_id
script:
- export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
```
Example configuration for stoping script after first failure:
```diff
- name: ssh commands
image: appleboy/drone-ssh
settings:
host: foo.com
username: root
password: 1234
port: 22
+ script_stop: true
script:
- mkdir abc/def/efg
- echo "you can't see the steps."
```
## Secret Reference
ssh_username
: account for target host user
ssh_password
: password for target host user
ssh_key
: plain text of user private key
proxy_ssh_username
: account for user of proxy server
proxy_ssh_password
: password for user of proxy server
proxy_ssh_key
: plain text of user private key for proxy server
## Parameter Reference
host
: target hostname or IP
port
: ssh port of target host
username
: account for target host user
password
: password for target host user
key
: plain text of user private key
key_path
: key path of user private key
envs
: custom secrets which are made available in the script section
script
: execute commands on a remote server
script_stop
: stop script after first failure
timeout
: Timeout is the maximum amount of time for the TCP connection to establish.
command_timeout
: Command timeout is the maximum amount of time for the execute commands, default is 60 secs.
proxy_host
: proxy hostname or IP
proxy_port
: ssh port of proxy host
proxy_username
: account for proxy host user
proxy_password
: password for proxy host user
proxy_key
: plain text of proxy private key
proxy_key_path
: key path of proxy private key
-10
View File
@@ -1,10 +0,0 @@
FROM alpine:3.4
RUN apk update && \
apk add \
ca-certificates \
openssh-client && \
rm -rf /var/cache/apk/*
ADD drone-ssh /bin/
ENTRYPOINT ["/bin/drone-ssh"]
-10
View File
@@ -1,10 +0,0 @@
FROM armhfbuild/alpine:3.4
RUN apk update && \
apk add \
ca-certificates \
openssh-client && \
rm -rf /var/cache/apk/*
ADD drone-ssh /bin/
ENTRYPOINT ["/bin/drone-ssh"]
+62 -35
View File
@@ -1,14 +1,15 @@
.PHONY: test drone-ssh build fmt vet errcheck lint install update release-dirs release-build release-copy release-check release coverage
DIST := dist DIST := dist
EXECUTABLE := drone-ssh EXECUTABLE := drone-ssh
GOFMT ?= gofmt "-s"
GO ?= go
# for dockerhub # for dockerhub
DEPLOY_ACCOUNT := appleboy DEPLOY_ACCOUNT := appleboy
DEPLOY_IMAGE := $(EXECUTABLE) DEPLOY_IMAGE := $(EXECUTABLE)
TARGETS ?= linux darwin windows TARGETS ?= linux darwin windows
PACKAGES ?= $(shell go list ./... | grep -v /vendor/) ARCHS ?= amd64 386
PACKAGES ?= $(shell $(GO) list ./...)
SOURCES ?= $(shell find . -name "*.go" -type f) SOURCES ?= $(shell find . -name "*.go" -type f)
TAGS ?= TAGS ?=
LDFLAGS ?= -X 'main.Version=$(VERSION)' LDFLAGS ?= -X 'main.Version=$(VERSION)'
@@ -28,42 +29,50 @@ endif
all: build all: build
fmt: fmt:
find . -name "*.go" -type f -not -path "./vendor/*" | xargs gofmt -s -w $(GOFMT) -w $(SOURCES)
vet: vet:
go vet $(PACKAGES) $(GO) vet $(PACKAGES)
errcheck:
@which errcheck > /dev/null; if [ $$? -ne 0 ]; then \
go get -u github.com/kisielk/errcheck; \
fi
errcheck $(PACKAGES)
lint: lint:
@which golint > /dev/null; if [ $$? -ne 0 ]; then \ @hash revive > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go get -u github.com/golang/lint/golint; \ $(GO) get -u github.com/mgechev/revive; \
fi fi
for PKG in $(PACKAGES); do golint -set_exit_status $$PKG || exit 1; done; revive -config .revive.toml ./... || exit 1
test: .PHONY: misspell-check
for PKG in $(PACKAGES); do go test -v -cover -coverprofile $$GOPATH/src/$$PKG/coverage.txt $$PKG || exit 1; done; misspell-check:
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u github.com/client9/misspell/cmd/misspell; \
fi
misspell -error $(SOURCES)
html: .PHONY: misspell
go tool cover -html=coverage.txt misspell:
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u github.com/client9/misspell/cmd/misspell; \
fi
misspell -w $(SOURCES)
dep_install: .PHONY: fmt-check
glide install fmt-check:
@diff=$$($(GOFMT) -d $(SOURCES)); \
if [ -n "$$diff" ]; then \
echo "Please run 'make fmt' and commit the result:"; \
echo "$${diff}"; \
exit 1; \
fi;
dep_update: test: fmt-check
glide up @$(GO) test -v -cover -coverprofile coverage.txt $(PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1
install: $(SOURCES) install: $(SOURCES)
go install -v -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(LDFLAGS)' $(GO) install -v -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(LDFLAGS)'
build: $(EXECUTABLE) build: $(EXECUTABLE)
$(EXECUTABLE): $(SOURCES) $(EXECUTABLE): $(SOURCES)
go build -v -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(LDFLAGS)' -o $@ $(GO) build -v -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(LDFLAGS)' -o $@
release: release-dirs release-build release-copy release-check release: release-dirs release-build release-copy release-check
@@ -72,9 +81,9 @@ release-dirs:
release-build: release-build:
@which gox > /dev/null; if [ $$? -ne 0 ]; then \ @which gox > /dev/null; if [ $$? -ne 0 ]; then \
go get -u github.com/mitchellh/gox; \ $(GO) get -u github.com/mitchellh/gox; \
fi fi
gox -os="$(TARGETS)" -arch="amd64 386" -tags="$(TAGS)" -ldflags="-s -w $(LDFLAGS)" -output="$(DIST)/binaries/$(EXECUTABLE)-$(VERSION)-{{.OS}}-{{.Arch}}" gox -os="$(TARGETS)" -arch="$(ARCHS)" -tags="$(TAGS)" -ldflags="-s -w $(LDFLAGS)" -output="$(DIST)/binaries/$(EXECUTABLE)-$(VERSION)-{{.OS}}-{{.Arch}}"
release-copy: release-copy:
$(foreach file,$(wildcard $(DIST)/binaries/$(EXECUTABLE)-*),cp $(file) $(DIST)/release/$(notdir $(file));) $(foreach file,$(wildcard $(DIST)/binaries/$(EXECUTABLE)-*),cp $(file) $(DIST)/release/$(notdir $(file));)
@@ -82,14 +91,22 @@ release-copy:
release-check: release-check:
cd $(DIST)/release; $(foreach file,$(wildcard $(DIST)/release/$(EXECUTABLE)-*),sha256sum $(notdir $(file)) > $(notdir $(file)).sha256;) cd $(DIST)/release; $(foreach file,$(wildcard $(DIST)/release/$(EXECUTABLE)-*),sha256sum $(notdir $(file)) > $(notdir $(file)).sha256;)
# for docker. build_linux_amd64:
static_build: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GO) build -a -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(LDFLAGS)' -o release/linux/amd64/$(DEPLOY_IMAGE)
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(LDFLAGS)' -o $(DEPLOY_IMAGE)
build_linux_i386:
CGO_ENABLED=0 GOOS=linux GOARCH=386 $(GO) build -a -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(LDFLAGS)' -o release/linux/i386/$(DEPLOY_IMAGE)
build_linux_arm64:
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 $(GO) build -a -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(LDFLAGS)' -o release/linux/arm64/$(DEPLOY_IMAGE)
build_linux_arm:
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 $(GO) build -a -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(LDFLAGS)' -o release/linux/arm/$(DEPLOY_IMAGE)
docker_image: docker_image:
docker build -t $(DEPLOY_ACCOUNT)/$(DEPLOY_IMAGE) . docker build -t $(DEPLOY_ACCOUNT)/$(DEPLOY_IMAGE) .
docker: static_build docker_image docker: docker_image
docker_deploy: docker_deploy:
ifeq ($(tag),) ifeq ($(tag),)
@@ -100,15 +117,25 @@ endif
docker tag $(DEPLOY_ACCOUNT)/$(DEPLOY_IMAGE):latest $(DEPLOY_ACCOUNT)/$(DEPLOY_IMAGE):$(tag) docker tag $(DEPLOY_ACCOUNT)/$(DEPLOY_IMAGE):latest $(DEPLOY_ACCOUNT)/$(DEPLOY_IMAGE):$(tag)
docker push $(DEPLOY_ACCOUNT)/$(DEPLOY_IMAGE):$(tag) docker push $(DEPLOY_ACCOUNT)/$(DEPLOY_IMAGE):$(tag)
ssh-server:
adduser -h /home/drone-scp -s /bin/bash -D -S drone-scp
echo drone-scp:1234 | chpasswd
mkdir -p /home/drone-scp/.ssh
chmod 700 /home/drone-scp/.ssh
cp tests/.ssh/id_rsa.pub /home/drone-scp/.ssh/authorized_keys
chown -R drone-scp /home/drone-scp/.ssh
# install ssh and start server
apk add --update openssh openrc
rm -rf /etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_dsa_key
sed -i 's/AllowTcpForwarding no/AllowTcpForwarding yes/g' /etc/ssh/sshd_config
./tests/entrypoint.sh /usr/sbin/sshd -D &
coverage: coverage:
sed -i '/main.go/d' coverage.txt sed -i '/main.go/d' coverage.txt
curl -s https://codecov.io/bash > .codecov && \
chmod +x .codecov && \
./.codecov -f coverage.txt
clean: clean:
go clean -x -i ./... $(GO) clean -x -i ./...
rm -rf coverage.txt $(EXECUTABLE) $(DIST) vendor rm -rf coverage.txt $(EXECUTABLE) $(DIST)
version: version:
@echo $(VERSION) @echo $(VERSION)
+84 -21
View File
@@ -1,37 +1,77 @@
<img src="ssh.png">
# drone-ssh # drone-ssh
[![GitHub tag](https://img.shields.io/github/tag/strongloop/express.svg)]() [![GoDoc](https://godoc.org/github.com/appleboy/drone-ssh?status.svg)](https://godoc.org/github.com/appleboy/drone-ssh) [![Build Status](http://drone.wu-boy.com/api/badges/appleboy/drone-ssh/status.svg)](http://drone.wu-boy.com/appleboy/drone-ssh) [![codecov](https://codecov.io/gh/appleboy/drone-ssh/branch/master/graph/badge.svg)](https://codecov.io/gh/appleboy/drone-ssh) [![Go Report Card](https://goreportcard.com/badge/github.com/appleboy/drone-ssh)](https://goreportcard.com/report/github.com/appleboy/drone-ssh) [![Docker Pulls](https://img.shields.io/docker/pulls/appleboy/drone-ssh.svg)](https://hub.docker.com/r/appleboy/drone-ssh/) [![](https://images.microbadger.com/badges/image/appleboy/drone-ssh.svg)](https://microbadger.com/images/appleboy/drone-ssh "Get your own image badge on microbadger.com") ![sshlog](images/ssh.png)
[![GitHub tag](https://img.shields.io/github/tag/appleboy/drone-ssh.svg)](https://github.com/appleboy/drone-ssh/releases)
[![GoDoc](https://godoc.org/github.com/appleboy/drone-ssh?status.svg)](https://godoc.org/github.com/appleboy/drone-ssh)
[![Build Status](https://cloud.drone.io/api/badges/appleboy/drone-ssh/status.svg)](https://cloud.drone.io/appleboy/drone-ssh)
[![codecov](https://codecov.io/gh/appleboy/drone-ssh/branch/master/graph/badge.svg)](https://codecov.io/gh/appleboy/drone-ssh)
[![Go Report Card](https://goreportcard.com/badge/github.com/appleboy/drone-ssh)](https://goreportcard.com/report/github.com/appleboy/drone-ssh)
[![Docker Pulls](https://img.shields.io/docker/pulls/appleboy/drone-ssh.svg)](https://hub.docker.com/r/appleboy/drone-ssh/)
[![micro badger](https://images.microbadger.com/badges/image/appleboy/drone-ssh.svg)](https://microbadger.com/images/appleboy/drone-ssh "Get your own image badge on microbadger.com")
Drone plugin to execute commands on a remote host through SSH. For the usage Drone plugin to execute commands on a remote host through SSH. For the usage
information and a listing of the available options please take a look at information and a listing of the available options please take a look at [the docs](http://plugins.drone.io/appleboy/drone-ssh/).
[the docs](DOCS.md).
## Build **Note: Please update your image config path to `appleboy/drone-ssh` for drone. `plugins/ssh` is no longer maintained.**
Build the binary with the following commands: ![demo](./images/demo2017.05.10.gif)
## Breaking changes
`v1.5.0`: change command timeout flag to `Duration`. See the following setting:
```diff
pipeline:
scp:
image: appleboy/drone-scp
settings:
host:
- example1.com
- example2.com
username: ubuntu
password:
from_secret: ssh_password
port: 22
- command_timeout: 120
+ command_timeout: 2m
script:
- echo "Hello World"
``` ```
go build
go test ## Build or Download a binary
The pre-compiled binaries can be downloaded from [release page](https://github.com/appleboy/drone-ssh/releases). Support the following OS type.
* Windows amd64/386
* Linux arm/amd64/386
* Darwin amd64/386
With `Go` installed
```sh
go get -u -v github.com/appleboy/drone-ssh
```
or build the binary with the following command:
```sh
export GOOS=linux
export GOARCH=amd64
export CGO_ENABLED=0
export GO111MODULE=on
go test -cover ./...
go build -v -a -tags netgo -o release/linux/amd64/drone-ssh .
``` ```
## Docker ## Docker
Build the docker image with the following commands: Build the docker image with the following commands:
``` ```sh
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -tags netgo make docker
docker build -t appleboy/drone-ssh .
```
Please note incorrectly building the image for the correct x64 linux and with
GCO disabled will result in an error when running the Docker image:
```
docker: Error response from daemon: Container command
'/bin/drone-ssh' not found or does not exist..
``` ```
## Usage ## Usage
@@ -41,10 +81,33 @@ Execute from the working directory:
```sh ```sh
docker run --rm \ docker run --rm \
-e PLUGIN_HOST=foo.com \ -e PLUGIN_HOST=foo.com \
-e PLUGIN_USER=root \ -e PLUGIN_USERNAME=root \
-e PLUGIN_KEY="$(cat ${HOME}/.ssh/id_rsa)" \ -e PLUGIN_KEY="$(cat ${HOME}/.ssh/id_rsa)" \
-e PLUGIN_SCRIPT=whoami \ -e PLUGIN_SCRIPT=whoami \
-v $(pwd):$(pwd) \ -v $(pwd):$(pwd) \
-w $(pwd) \ -w $(pwd) \
appleboy/drone-ssh appleboy/drone-ssh
``` ```
## Mount key from file path
Please make sure that enable the `trusted` mode in project setting for [drone 0.8 version](https://0-8-0.docs.drone.io/).
![trusted mode](./images/trust.png)
Mount private key in `volumes` setting of `.drone.yml` config
```diff
pipeline:
ssh:
image: appleboy/drone-ssh
host: xxxxx.com
username: deploy
+ volumes:
+ - /root/drone_rsa:/root/ssh/drone_rsa
key_path: /root/ssh/drone_rsa
script:
- echo "test ssh"
```
See the detail of [issue comment](https://github.com/appleboy/drone-ssh/issues/51#issuecomment-336732928).
+12
View File
@@ -0,0 +1,12 @@
FROM plugins/base:linux-amd64
LABEL maintainer="Bo-Yi Wu <appleboy.tw@gmail.com>" \
org.label-schema.name="Drone SSH" \
org.label-schema.vendor="Bo-Yi Wu" \
org.label-schema.schema-version="1.0"
RUN apk add --no-cache ca-certificates && \
rm -rf /var/cache/apk/*
ADD release/linux/amd64/drone-ssh /bin/
ENTRYPOINT ["/bin/drone-ssh"]
+12
View File
@@ -0,0 +1,12 @@
FROM plugins/base:linux-arm
LABEL maintainer="Bo-Yi Wu <appleboy.tw@gmail.com>" \
org.label-schema.name="Drone SSH" \
org.label-schema.vendor="Bo-Yi Wu" \
org.label-schema.schema-version="1.0"
RUN apk add --no-cache ca-certificates && \
rm -rf /var/cache/apk/*
ADD release/linux/arm/drone-ssh /bin/
ENTRYPOINT ["/bin/drone-ssh"]
+12
View File
@@ -0,0 +1,12 @@
FROM plugins/base:linux-arm64
LABEL maintainer="Bo-Yi Wu <appleboy.tw@gmail.com>" \
org.label-schema.name="Drone SSH" \
org.label-schema.vendor="Bo-Yi Wu" \
org.label-schema.schema-version="1.0"
RUN apk add --no-cache ca-certificates && \
rm -rf /var/cache/apk/*
ADD release/linux/arm64/drone-ssh /bin/
ENTRYPOINT ["/bin/drone-ssh"]
+9
View File
@@ -0,0 +1,9 @@
FROM microsoft/nanoserver:10.0.14393.1884
LABEL maintainer="Bo-Yi Wu <appleboy.tw@gmail.com>" \
org.label-schema.name="Drone SSH" \
org.label-schema.vendor="Bo-Yi Wu" \
org.label-schema.schema-version="1.0"
ADD drone-ssh.exe /drone-ssh.exe
ENTRYPOINT [ "\\drone-ssh.exe" ]
+25
View File
@@ -0,0 +1,25 @@
image: appleboy/drone-ssh:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
{{#if build.tags}}
tags:
{{#each build.tags}}
- {{this}}
{{/each}}
{{/if}}
manifests:
-
image: appleboy/drone-ssh:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
platform:
architecture: amd64
os: linux
-
image: appleboy/drone-ssh:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
platform:
architecture: arm64
os: linux
variant: v8
-
image: appleboy/drone-ssh:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
platform:
architecture: arm
os: linux
variant: v7
Generated
-29
View File
@@ -1,29 +0,0 @@
hash: 0c9184ed3a3b510feedfeb4fa4a119bdfa1a88b5dcfe9977944817a512958d44
updated: 2017-01-23T10:04:51.354467848+08:00
imports:
- name: github.com/joho/godotenv
version: a01a834e1654b4c9ca5b3ad05159445cc9c7ad08
subpackages:
- autoload
- name: github.com/stretchr/testify
version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0
subpackages:
- assert
- name: github.com/urfave/cli
version: 0bdeddeeb0f650497d603c4ad7b20cfe685682f6
- name: golang.org/x/crypto
version: c3b1d0d6d8690eaebe3064711b026770cc37efa3
subpackages:
- curve25519
- ed25519
- ed25519/internal/edwards25519
- ssh
testImports:
- name: github.com/davecgh/go-spew
version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9
subpackages:
- spew
- name: github.com/pmezard/go-difflib
version: d8ed2627bdf02c080bf22230dbb337003b7aba2d
subpackages:
- difflib
-15
View File
@@ -1,15 +0,0 @@
package: github.com/appleboy/drone-ssh
import:
- package: github.com/joho/godotenv
version: ^1.0.0
subpackages:
- autoload
- package: github.com/urfave/cli
version: ^1.19.1
- package: golang.org/x/crypto
subpackages:
- ssh
- package: github.com/stretchr/testify
version: ^1.1.4
subpackages:
- assert
+10
View File
@@ -0,0 +1,10 @@
module github.com/appleboy/drone-ssh
go 1.13
require (
github.com/appleboy/easyssh-proxy v1.2.0
github.com/joho/godotenv v1.3.0
github.com/stretchr/testify v1.4.0
github.com/urfave/cli v1.22.1
)
+29
View File
@@ -0,0 +1,29 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/appleboy/easyssh-proxy v1.2.0 h1:KvaUGC18WkBFet+N1oofQy03jkC5HaKFn2XGxFxCTtg=
github.com/appleboy/easyssh-proxy v1.2.0/go.mod h1:vHskChUNhxwW4dXMe2MNE/k+UBCkBagrQDm70UWZrS0=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25 h1:jsG6UpNLt9iAsb0S2AGW28DveNzzgmbXR+ENoPjUeIU=
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

+166 -36
View File
@@ -1,90 +1,220 @@
package main package main
import ( import (
"fmt" "log"
"os" "os"
"time"
"github.com/appleboy/easyssh-proxy"
"github.com/joho/godotenv" "github.com/joho/godotenv"
_ "github.com/joho/godotenv/autoload" _ "github.com/joho/godotenv/autoload"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
var build = "0" // build number set at compile-time // Version set at compile-time
var Version string
func main() { func main() {
// Load env-file if it exists first
if filename, found := os.LookupEnv("PLUGIN_ENV_FILE"); found {
_ = godotenv.Load(filename)
}
app := cli.NewApp() app := cli.NewApp()
app.Name = "ssh plugin" app.Name = "Drone SSH"
app.Usage = "ssh plugin" app.Usage = "Executing remote ssh commands"
app.Copyright = "Copyright (c) 2019 Bo-Yi Wu"
app.Authors = []cli.Author{
{
Name: "Bo-Yi Wu",
Email: "appleboy.tw@gmail.com",
},
}
app.Action = run app.Action = run
app.Version = fmt.Sprintf("1.0.%s", build) app.Version = Version
app.Flags = []cli.Flag{ app.Flags = []cli.Flag{
cli.StringFlag{ cli.StringFlag{
Name: "ssh-key", Name: "ssh-key",
Usage: "private ssh key", Usage: "private ssh key",
EnvVar: "PLUGIN_SSH_KEY,PLUGIN_KEY,SSH_KEY", EnvVar: "PLUGIN_SSH_KEY,PLUGIN_KEY,SSH_KEY,KEY,INPUT_KEY",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "user", Name: "key-path,i",
Usage: "ssh private key path",
EnvVar: "PLUGIN_KEY_PATH,SSH_KEY_PATH,INPUT_KEY_PATH",
},
cli.StringFlag{
Name: "username,user,u",
Usage: "connect as user", Usage: "connect as user",
EnvVar: "PLUGIN_USER,SSH_USER", EnvVar: "PLUGIN_USERNAME,PLUGIN_USER,SSH_USERNAME,USERNAME,INPUT_USERNAME",
Value: "root", Value: "root",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "password", Name: "password,P",
Usage: "user password", Usage: "user password",
EnvVar: "PLUGIN_PASSWORD,SSH_PASSWORD", EnvVar: "PLUGIN_PASSWORD,SSH_PASSWORD,PASSWORD,INPUT_PASSWORD",
}, },
cli.StringSliceFlag{ cli.StringSliceFlag{
Name: "host", Name: "host,H",
Usage: "connect to host", Usage: "connect to host",
EnvVar: "PLUGIN_HOST,SSH_HOST", EnvVar: "PLUGIN_HOST,SSH_HOST,HOST,INPUT_HOST",
}, },
cli.IntFlag{ cli.IntFlag{
Name: "port", Name: "port,p",
Usage: "connect to port", Usage: "connect to port",
EnvVar: "PLUGIN_PORT,SSH_PORT", EnvVar: "PLUGIN_PORT,SSH_PORT,PORT,INPUT_PORT",
Value: 22, Value: 22,
}, },
cli.IntFlag{ cli.BoolFlag{
Name: "sleep", Name: "sync",
Usage: "sleep between hosts", Usage: "sync mode",
EnvVar: "PLUGIN_SLEEP,SSH_SLEEP", EnvVar: "PLUGIN_SYNC,SYNC,INPUT_SYNC",
}, },
cli.DurationFlag{ cli.DurationFlag{
Name: "timeout", Name: "timeout,t",
Usage: "connection timeout", Usage: "connection timeout",
EnvVar: "PLUGIN_TIMEOUT,SSH_TIMEOUT", EnvVar: "PLUGIN_TIMEOUT,SSH_TIMEOUT,TIMEOUT,INPUT_TIMEOUT",
Value: 30 * time.Second,
},
cli.DurationFlag{
Name: "command.timeout,T",
Usage: "command timeout",
EnvVar: "PLUGIN_COMMAND_TIMEOUT,SSH_COMMAND_TIMEOUT,COMMAND_TIMEOUT,INPUT_COMMAND_TIMEOUT",
Value: 60 * time.Second,
}, },
cli.StringSliceFlag{ cli.StringSliceFlag{
Name: "script", Name: "script,s",
Usage: "execute commands", Usage: "execute commands",
EnvVar: "PLUGIN_SCRIPT,SSH_SCRIPT", EnvVar: "PLUGIN_SCRIPT,SSH_SCRIPT,SCRIPT",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "env-file", Name: "script.string",
Usage: "source env file", Usage: "execute single commands for github action",
EnvVar: "INPUT_SCRIPT",
},
cli.BoolFlag{
Name: "script.stop",
Usage: "stop script after first failure",
EnvVar: "PLUGIN_SCRIPT_STOP,STOP,INPUT_SCRIPT_STOP",
},
cli.StringFlag{
Name: "proxy.ssh-key",
Usage: "private ssh key of proxy",
EnvVar: "PLUGIN_PROXY_SSH_KEY,PLUGIN_PROXY_KEY,PROXY_SSH_KEY,INPUT_PROXY_KEY",
},
cli.StringFlag{
Name: "proxy.key-path",
Usage: "ssh private key path of proxy",
EnvVar: "PLUGIN_PROXY_KEY_PATH,PROXY_SSH_KEY_PATH,INPUT_PROXY_KEY_PATH",
},
cli.StringFlag{
Name: "proxy.username",
Usage: "connect as user of proxy",
EnvVar: "PLUGIN_PROXY_USERNAME,PLUGIN_PROXY_USER,PROXY_SSH_USERNAME,INPUT_PROXY_USERNAME",
Value: "root",
},
cli.StringFlag{
Name: "proxy.password",
Usage: "user password of proxy",
EnvVar: "PLUGIN_PROXY_PASSWORD,PROXY_SSH_PASSWORD,INPUT_PROXY_PASSWORD",
},
cli.StringFlag{
Name: "proxy.host",
Usage: "connect to host of proxy",
EnvVar: "PLUGIN_PROXY_HOST,PROXY_SSH_HOST,INPUT_PROXY_HOST",
},
cli.StringFlag{
Name: "proxy.port",
Usage: "connect to port of proxy",
EnvVar: "PLUGIN_PROXY_PORT,PROXY_SSH_PORT,INPUT_PROXY_PORT",
Value: "22",
},
cli.DurationFlag{
Name: "proxy.timeout",
Usage: "proxy connection timeout",
EnvVar: "PLUGIN_PROXY_TIMEOUT,PROXY_SSH_TIMEOUT,INPUT_PROXY_TIMEOUT",
},
cli.StringSliceFlag{
Name: "envs",
Usage: "pass environment variable to shell script",
EnvVar: "PLUGIN_ENVS,INPUT_ENVS",
},
cli.BoolFlag{
Name: "debug",
Usage: "debug mode",
EnvVar: "PLUGIN_DEBUG,DEBUG,INPUT_DEBUG",
}, },
} }
app.Run(os.Args) // Override a template
cli.AppHelpTemplate = `
________ _________ _________ ___ ___
\______ \_______ ____ ____ ____ / _____// _____// | \
| | \_ __ \/ _ \ / \_/ __ \ ______ \_____ \ \_____ \/ ~ \
| | \ | \( <_> ) | \ ___/ /_____/ / \/ \ Y /
/_______ /__| \____/|___| /\___ > /_______ /_______ /\___|_ /
\/ \/ \/ \/ \/ \/
version: {{.Version}}
NAME:
{{.Name}} - {{.Usage}}
USAGE:
{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
{{if len .Authors}}
AUTHOR:
{{range .Authors}}{{ . }}{{end}}
{{end}}{{if .Commands}}
COMMANDS:
{{range .Commands}}{{if not .HideHelp}} {{join .Names ", "}}{{ "\t"}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
GLOBAL OPTIONS:
{{range .VisibleFlags}}{{.}}
{{end}}{{end}}{{if .Copyright }}
COPYRIGHT:
{{.Copyright}}
{{end}}{{if .Version}}
VERSION:
{{.Version}}
{{end}}
REPOSITORY:
Github: https://github.com/appleboy/drone-ssh
`
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
} }
func run(c *cli.Context) error { func run(c *cli.Context) error {
if c.String("env-file") != "" { scripts := c.StringSlice("script")
_ = godotenv.Load(c.String("env-file")) if s := c.String("script.string"); s != "" {
scripts = append(scripts, s)
} }
plugin := Plugin{ plugin := Plugin{
Config: Config{ Config: Config{
Key: c.String("ssh-key"), Key: c.String("ssh-key"),
User: c.String("user"), KeyPath: c.String("key-path"),
Password: c.String("password"), Username: c.String("user"),
Host: c.StringSlice("host"), Password: c.String("password"),
Port: c.Int("port"), Host: c.StringSlice("host"),
Sleep: c.Int("sleep"), Port: c.Int("port"),
Timeout: c.Duration("timeout"), Timeout: c.Duration("timeout"),
Script: c.StringSlice("script"), CommandTimeout: c.Duration("command.timeout"),
Script: scripts,
ScriptStop: c.Bool("script.stop"),
Envs: c.StringSlice("envs"),
Debug: c.Bool("debug"),
Sync: c.Bool("sync"),
Proxy: easyssh.DefaultConfig{
Key: c.String("proxy.ssh-key"),
KeyPath: c.String("proxy.key-path"),
User: c.String("proxy.username"),
Password: c.String("proxy.password"),
Server: c.String("proxy.host"),
Port: c.String("proxy.port"),
Timeout: c.Duration("proxy.timeout"),
},
}, },
Writer: os.Stdout,
} }
return plugin.Exec() return plugin.Exec()
+256
View File
@@ -0,0 +1,256 @@
{
test:: {
kind: 'pipeline',
name: 'testing',
platform: {
os: 'linux',
arch: 'amd64',
},
steps: [
{
name: 'vet',
image: 'golang:1.13',
pull: 'always',
commands: [
'make vet',
],
volumes: [
{
name: 'gopath',
path: '/go',
},
],
},
{
name: 'lint',
image: 'golang:1.13',
pull: 'always',
commands: [
'make lint',
],
volumes: [
{
name: 'gopath',
path: '/go',
},
],
},
{
name: 'misspell',
image: 'golang:1.13',
pull: 'always',
commands: [
'make misspell-check',
],
volumes: [
{
name: 'gopath',
path: '/go',
},
],
},
{
name: 'test',
image: 'golang:1.13-alpine',
pull: 'always',
commands: [
'apk add git make curl perl bash build-base zlib-dev ucl-dev',
'make ssh-server',
'make test',
'make coverage',
],
volumes: [
{
name: 'gopath',
path: '/go',
},
],
},
{
name: 'codecov',
image: 'robertstettner/drone-codecov',
pull: 'always',
settings: {
token: { 'from_secret': 'codecov_token' },
},
},
],
volumes: [
{
name: 'gopath',
temp: {},
},
],
},
build(name, os='linux', arch='amd64'):: {
kind: 'pipeline',
name: os + '-' + arch,
platform: {
os: os,
arch: arch,
},
steps: [
{
name: 'build-push',
image: 'golang:1.13',
pull: 'always',
environment: {
CGO_ENABLED: '0',
},
commands: [
'go build -v -ldflags \'-X main.build=${DRONE_BUILD_NUMBER}\' -a -o release/' + os + '/' + arch + '/' + name,
],
when: {
event: {
exclude: [ 'tag' ],
},
},
},
{
name: 'build-tag',
image: 'golang:1.13',
pull: 'always',
environment: {
CGO_ENABLED: '0',
},
commands: [
'go build -v -ldflags \'-X main.version=${DRONE_TAG##v} -X main.build=${DRONE_BUILD_NUMBER}\' -a -o release/' + os + '/' + arch + '/' + name,
],
when: {
event: [ 'tag' ],
},
},
{
name: 'executable',
image: 'golang:1.13',
pull: 'always',
commands: [
'./release/' + os + '/' + arch + '/' + name + ' --help',
],
},
{
name: 'dryrun',
image: 'plugins/docker:' + os + '-' + arch,
pull: 'always',
settings: {
daemon_off: false,
dry_run: true,
tags: os + '-' + arch,
dockerfile: 'docker/Dockerfile.' + os + '.' + arch,
repo: 'appleboy/' + name,
cache_from: 'appleboy/' + name,
},
when: {
event: [ 'pull_request' ],
},
},
{
name: 'publish',
image: 'plugins/docker:' + os + '-' + arch,
pull: 'always',
settings: {
daemon_off: 'false',
auto_tag: true,
auto_tag_suffix: os + '-' + arch,
dockerfile: 'docker/Dockerfile.' + os + '.' + arch,
repo: 'appleboy/' + name,
cache_from: 'appleboy/' + name,
username: { 'from_secret': 'docker_username' },
password: { 'from_secret': 'docker_password' },
},
when: {
event: {
exclude: [ 'pull_request' ],
},
},
},
],
depends_on: [
'testing',
],
trigger: {
ref: [
'refs/heads/master',
'refs/pull/**',
'refs/tags/**',
],
},
},
release:: {
kind: 'pipeline',
name: 'release-binary',
platform: {
os: 'linux',
arch: 'amd64',
},
steps: [
{
name: 'build-all-binary',
image: 'golang:1.13',
pull: 'always',
commands: [
'make release'
],
when: {
event: [ 'tag' ],
},
},
{
name: 'deploy-all-binary',
image: 'plugins/github-release',
pull: 'always',
settings: {
files: [ 'dist/release/*' ],
api_key: { 'from_secret': 'github_release_api_key' },
},
when: {
event: [ 'tag' ],
},
},
],
depends_on: [
'testing',
],
trigger: {
ref: [
'refs/tags/**',
],
},
},
notifications(os='linux', arch='amd64', depends_on=[]):: {
kind: 'pipeline',
name: 'notifications',
platform: {
os: os,
arch: arch,
},
steps: [
{
name: 'manifest',
image: 'plugins/manifest',
pull: 'always',
settings: {
username: { from_secret: 'docker_username' },
password: { from_secret: 'docker_password' },
spec: 'docker/manifest.tmpl',
ignore_missing: true,
},
},
],
depends_on: depends_on,
trigger: {
ref: [
'refs/heads/master',
'refs/tags/**',
],
},
},
signature(key):: {
kind: 'signature',
hmac: key,
}
}
+171 -62
View File
@@ -1,99 +1,208 @@
package main package main
import ( import (
"log" "errors"
"net" "fmt"
"io"
"os" "os"
"strconv" "strconv"
"strings" "strings"
"sync"
"time" "time"
"fmt" "github.com/appleboy/easyssh-proxy"
"golang.org/x/crypto/ssh" )
var (
errMissingHost = errors.New("Error: missing server host")
errMissingPasswordOrKey = errors.New("Error: can't connect without a private SSH key or password")
errCommandTimeOut = errors.New("Error: command timeout")
errSetPasswordandKey = errors.New("can't set password and key at the same time")
) )
type ( type (
// Config for the plugin. // Config for the plugin.
Config struct { Config struct {
Key string Key string
User string KeyPath string
Password string Username string
Host []string Password string
Port int Host []string
Sleep int Port int
Timeout time.Duration Timeout time.Duration
Script []string CommandTimeout time.Duration
Script []string
ScriptStop bool
Envs []string
Proxy easyssh.DefaultConfig
Debug bool
Sync bool
} }
// Plugin structure // Plugin structure
Plugin struct { Plugin struct {
Config Config Config Config
Writer io.Writer
} }
) )
func escapeArg(arg string) string {
return "'" + strings.Replace(arg, "'", `'\''`, -1) + "'"
}
func (p Plugin) exec(host string, wg *sync.WaitGroup, errChannel chan error) {
// Create MakeConfig instance with remote username, server address and path to private key.
ssh := &easyssh.MakeConfig{
Server: host,
User: p.Config.Username,
Password: p.Config.Password,
Port: strconv.Itoa(p.Config.Port),
Key: p.Config.Key,
KeyPath: p.Config.KeyPath,
Timeout: p.Config.Timeout,
Proxy: easyssh.DefaultConfig{
Server: p.Config.Proxy.Server,
User: p.Config.Proxy.User,
Password: p.Config.Proxy.Password,
Port: p.Config.Proxy.Port,
Key: p.Config.Proxy.Key,
KeyPath: p.Config.Proxy.KeyPath,
Timeout: p.Config.Proxy.Timeout,
},
}
p.log(host, "======CMD======")
p.log(host, strings.Join(p.Config.Script, "\n"))
p.log(host, "======END======")
env := []string{}
for _, key := range p.Config.Envs {
key = strings.ToUpper(key)
if val, found := os.LookupEnv(key); found {
env = append(env, key+"="+escapeArg(val))
}
}
p.Config.Script = append(env, p.scriptCommands()...)
if p.Config.Debug {
p.log(host, "======ENV======")
p.log(host, strings.Join(env, "\n"))
p.log(host, "======END======")
}
stdoutChan, stderrChan, doneChan, errChan, err := ssh.Stream(strings.Join(p.Config.Script, "\n"), p.Config.CommandTimeout)
if err != nil {
errChannel <- err
} else {
// read from the output channel until the done signal is passed
isTimeout := true
loop:
for {
select {
case isTimeout = <-doneChan:
break loop
case outline := <-stdoutChan:
p.log(host, "out:", outline)
case errline := <-stderrChan:
p.log(host, "err:", errline)
case err = <-errChan:
}
}
// get exit code or command error.
if err != nil {
errChannel <- err
}
// command time out
if !isTimeout {
errChannel <- errCommandTimeOut
}
}
wg.Done()
}
func (p Plugin) log(host string, message ...interface{}) {
if p.Writer == nil {
p.Writer = os.Stdout
}
if count := len(p.Config.Host); count == 1 {
fmt.Fprintf(p.Writer, "%s", fmt.Sprintln(message...))
} else {
fmt.Fprintf(p.Writer, "%s: %s", host, fmt.Sprintln(message...))
}
}
// Exec executes the plugin. // Exec executes the plugin.
func (p Plugin) Exec() error { func (p Plugin) Exec() error {
if p.Config.Key == "" && p.Config.Password == "" { if len(p.Config.Host) == 0 {
return fmt.Errorf("Error: can't connect without a private SSH key or password") return errMissingHost
} }
for i, host := range p.Config.Host { if len(p.Config.Key) == 0 && len(p.Config.Password) == 0 && len(p.Config.KeyPath) == 0 {
addr := net.JoinHostPort( return errMissingPasswordOrKey
host, }
strconv.Itoa(p.Config.Port),
)
// auths holds the detected ssh auth methods if len(p.Config.Key) != 0 && len(p.Config.Password) != 0 {
auths := []ssh.AuthMethod{} return errSetPasswordandKey
}
if p.Config.Key != "" { wg := sync.WaitGroup{}
signer, err := ssh.ParsePrivateKey([]byte(p.Config.Key)) wg.Add(len(p.Config.Host))
errChannel := make(chan error)
if err != nil { finished := make(chan struct{})
return fmt.Errorf("Error: Failed to parse private key. %s", err) for _, host := range p.Config.Host {
} if p.Config.Sync {
p.exec(host, &wg, errChannel)
auths = append(auths, ssh.PublicKeys(signer)) } else {
go p.exec(host, &wg, errChannel)
} }
}
// figure out what auths are requested, what is supported go func() {
if p.Config.Password != "" { wg.Wait()
auths = append(auths, ssh.Password(p.Config.Password)) close(finished)
} }()
config := &ssh.ClientConfig{
Timeout: p.Config.Timeout,
User: p.Config.User,
Auth: auths,
}
log.Printf("+ ssh %s@%s -p %d\n", p.Config.User, addr, p.Config.Port)
client, err := ssh.Dial("tcp", addr, config)
select {
case <-finished:
case err := <-errChannel:
if err != nil { if err != nil {
return fmt.Errorf("Error: Failed to dial to server. %s", err)
}
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("Error: Failed to start a SSH session. %s", err)
}
defer session.Close()
session.Stdout = os.Stdout
session.Stderr = os.Stderr
if err := session.Run(strings.Join(p.Config.Script, "\n")); err != nil {
return err return err
} }
if p.Config.Sleep != 0 && i != len(p.Config.Host)-1 {
log.Printf("+ sleep %d\n", p.Config.Sleep)
time.Sleep(time.Duration(p.Config.Sleep) * time.Second)
}
} }
fmt.Println("==============================================")
fmt.Println("✅ Successfully executed commands to all host.")
fmt.Println("==============================================")
return nil return nil
} }
func (p Plugin) scriptCommands() []string {
scripts := []string{}
for _, cmd := range p.Config.Script {
if p.Config.ScriptStop {
scripts = append(scripts, strings.Split(cmd, "\n")...)
} else {
scripts = append(scripts, cmd)
}
}
commands := make([]string, 0)
for _, cmd := range scripts {
if strings.TrimSpace(cmd) == "" {
continue
}
commands = append(commands, cmd)
if p.Config.ScriptStop {
commands = append(commands, "DRONE_SSH_PREV_COMMAND_EXIT_CODE=$? ; if [ $DRONE_SSH_PREV_COMMAND_EXIT_CODE -ne 0 ]; then exit $DRONE_SSH_PREV_COMMAND_EXIT_CODE; fi;")
}
}
return commands
}
+576 -2
View File
@@ -1,20 +1,594 @@
package main package main
import ( import (
"bytes"
"io"
"os"
"reflect"
"strings"
"testing" "testing"
"time"
"github.com/appleboy/easyssh-proxy"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestMissingHostOrUser(t *testing.T) {
plugin := Plugin{}
err := plugin.Exec()
assert.NotNil(t, err)
assert.Equal(t, errMissingHost, err)
}
func TestMissingKeyOrPassword(t *testing.T) { func TestMissingKeyOrPassword(t *testing.T) {
plugin := Plugin{ plugin := Plugin{
Config{ Config{
Host: []string{"localhost"}, Host: []string{"localhost"},
User: "ubuntu", Username: "ubuntu",
}, },
os.Stdout,
} }
err := plugin.Exec() err := plugin.Exec()
assert.NotNil(t, err) assert.NotNil(t, err)
assert.Equal(t, errMissingPasswordOrKey, err)
}
func TestSetPasswordAndKey(t *testing.T) {
plugin := Plugin{
Config{
Host: []string{"localhost"},
Username: "ubuntu",
Password: "1234",
Key: "1234",
},
os.Stdout,
}
err := plugin.Exec()
assert.NotNil(t, err)
assert.Equal(t, errSetPasswordandKey, err)
}
func TestIncorrectPassword(t *testing.T) {
plugin := Plugin{
Config: Config{
Host: []string{"localhost"},
Username: "drone-scp",
Port: 22,
Password: "123456",
Script: []string{"whoami"},
CommandTimeout: 60 * time.Second,
},
}
err := plugin.Exec()
assert.NotNil(t, err)
}
func TestSSHScriptFromRawKey(t *testing.T) {
plugin := Plugin{
Config: Config{
Host: []string{"localhost"},
Username: "drone-scp",
Port: 22,
CommandTimeout: 60 * time.Second,
Key: `-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA4e2D/qPN08pzTac+a8ZmlP1ziJOXk45CynMPtva0rtK/RB26
VbfAF0hIJji7ltvnYnqCU9oFfvEM33cTn7T96+od8ib/Vz25YU8ZbstqtIskPuwC
bv3K0mAHgsviJyRD7yM+QKTbBQEgbGuW6gtbMKhiYfiIB4Dyj7AdS/fk3v26wDgz
7SHI5OBqu9bv1KhxQYdFEnU3PAtAqeccgzNpbH3eYLyGzuUxEIJlhpZ/uU2G9ppj
/cSrONVPiI8Ahi4RrlZjmP5l57/sq1ClGulyLpFcMw68kP5FikyqHpHJHRBNgU57
1y0Ph33SjBbs0haCIAcmreWEhGe+/OXnJe6VUQIDAQABAoIBAH97emORIm9DaVSD
7mD6DqA7c5m5Tmpgd6eszU08YC/Vkz9oVuBPUwDQNIX8tT0m0KVs42VVPIyoj874
bgZMJoucC1G8V5Bur9AMxhkShx9g9A7dNXJTmsKilRpk2TOk7wBdLp9jZoKoZBdJ
jlp6FfaazQjjKD6zsCsMATwAoRCBpBNsmT6QDN0n0bIgY0tE6YGQaDdka0dAv68G
R0VZrcJ9voT6+f+rgJLoojn2DAu6iXaM99Gv8FK91YCymbQlXXgrk6CyS0IHexN7
V7a3k767KnRbrkqd3o6JyNun/CrUjQwHs1IQH34tvkWScbseRaFehcAm6mLT93RP
muauvMECgYEA9AXGtfDMse0FhvDPZx4mx8x+vcfsLvDHcDLkf/lbyPpu97C27b/z
ia07bu5TAXesUZrWZtKA5KeRE5doQSdTOv1N28BEr8ZwzDJwfn0DPUYUOxsN2iIy
MheO5A45Ko7bjKJVkZ61Mb1UxtqCTF9mqu9R3PBdJGthWOd+HUvF460CgYEA7QRf
Z8+vpGA+eSuu29e0xgRKnRzed5zXYpcI4aERc3JzBgO4Z0er9G8l66OWVGdMfpe6
CBajC5ToIiT8zqoYxXwqJgN+glir4gJe3mm8J703QfArZiQrdk0NTi5bY7+vLLG/
knTrtpdsKih6r3kjhuPPaAsIwmMxIydFvATKjLUCgYEAh/y4EihRSk5WKC8GxeZt
oiZ58vT4z+fqnMIfyJmD5up48JuQNcokw/LADj/ODiFM7GUnWkGxBrvDA3H67WQm
49bJjs8E+BfUQFdTjYnJRlpJZ+7Zt1gbNQMf5ENw5CCchTDqEq6pN0DVf8PBnSIF
KvkXW9KvdV5J76uCAn15mDkCgYA1y8dHzbjlCz9Cy2pt1aDfTPwOew33gi7U3skS
RTerx29aDyAcuQTLfyrROBkX4TZYiWGdEl5Bc7PYhCKpWawzrsH2TNa7CRtCOh2E
R+V/84+GNNf04ALJYCXD9/ugQVKmR1XfDRCvKeFQFE38Y/dvV2etCswbKt5tRy2p
xkCe/QKBgQCkLqafD4S20YHf6WTp3jp/4H/qEy2X2a8gdVVBi1uKkGDXr0n+AoVU
ib4KbP5ovZlrjL++akMQ7V2fHzuQIFWnCkDA5c2ZAqzlM+ZN+HRG7gWur7Bt4XH1
7XC9wlRna4b3Ln8ew3q1ZcBjXwD4ppbTlmwAfQIaZTGJUgQbdsO9YA==
-----END RSA PRIVATE KEY-----
`,
Script: []string{"whoami"},
},
}
err := plugin.Exec()
assert.Nil(t, err)
}
func TestSSHScriptFromKeyFile(t *testing.T) {
plugin := Plugin{
Config: Config{
Host: []string{"localhost", "127.0.0.1"},
Username: "drone-scp",
Port: 22,
KeyPath: "./tests/.ssh/id_rsa",
Script: []string{"whoami", "ls -al"},
CommandTimeout: 60 * time.Second,
},
}
err := plugin.Exec()
assert.Nil(t, err)
}
func TestStreamFromSSHCommand(t *testing.T) {
plugin := Plugin{
Config: Config{
Host: []string{"localhost", "127.0.0.1"},
Username: "drone-scp",
Port: 22,
KeyPath: "./tests/.ssh/id_rsa",
Script: []string{"whoami", "for i in {1..5}; do echo ${i}; sleep 1; done", "echo 'done'"},
CommandTimeout: 60 * time.Second,
},
}
err := plugin.Exec()
assert.Nil(t, err)
}
func TestSSHScriptWithError(t *testing.T) {
plugin := Plugin{
Config: Config{
Host: []string{"localhost", "127.0.0.1"},
Username: "drone-scp",
Port: 22,
KeyPath: "./tests/.ssh/id_rsa",
Script: []string{"exit 1"},
CommandTimeout: 60 * time.Second,
},
}
err := plugin.Exec()
// Process exited with status 1
assert.NotNil(t, err)
}
func TestSSHCommandTimeOut(t *testing.T) {
plugin := Plugin{
Config: Config{
Host: []string{"localhost"},
Username: "drone-scp",
Port: 22,
KeyPath: "./tests/.ssh/id_rsa",
Script: []string{"sleep 5"},
CommandTimeout: 1 * time.Second,
},
}
err := plugin.Exec()
assert.NotNil(t, err)
}
func TestProxyCommand(t *testing.T) {
plugin := Plugin{
Config: Config{
Host: []string{"localhost"},
Username: "drone-scp",
Port: 22,
KeyPath: "./tests/.ssh/id_rsa",
Script: []string{"whoami"},
CommandTimeout: 1 * time.Second,
Proxy: easyssh.DefaultConfig{
Server: "localhost",
User: "drone-scp",
Port: "22",
KeyPath: "./tests/.ssh/id_rsa",
},
},
}
err := plugin.Exec()
assert.Nil(t, err)
}
func TestSSHCommandError(t *testing.T) {
plugin := Plugin{
Config: Config{
Host: []string{"localhost"},
Username: "drone-scp",
Port: 22,
KeyPath: "./tests/.ssh/id_rsa",
Script: []string{"mkdir a", "mkdir a"},
CommandTimeout: 60 * time.Second,
},
}
err := plugin.Exec()
assert.NotNil(t, err)
}
func TestSSHCommandExitCodeError(t *testing.T) {
plugin := Plugin{
Config: Config{
Host: []string{"localhost"},
Username: "drone-scp",
Port: 22,
KeyPath: "./tests/.ssh/id_rsa",
Script: []string{
"set -e",
"echo 1",
"mkdir a",
"mkdir a",
"echo 2",
},
CommandTimeout: 60 * time.Second,
},
}
err := plugin.Exec()
assert.NotNil(t, err)
}
func TestSetENV(t *testing.T) {
os.Setenv("FOO", `' 1) '`)
plugin := Plugin{
Config: Config{
Host: []string{"localhost"},
Username: "drone-scp",
Port: 22,
KeyPath: "./tests/.ssh/id_rsa",
Envs: []string{"foo"},
Debug: true,
Script: []string{"whoami; echo $FOO"},
CommandTimeout: 1 * time.Second,
Proxy: easyssh.DefaultConfig{
Server: "localhost",
User: "drone-scp",
Port: "22",
KeyPath: "./tests/.ssh/id_rsa",
},
},
}
err := plugin.Exec()
assert.Nil(t, err)
}
func TestSetExistingENV(t *testing.T) {
os.Setenv("FOO", "Value for foo")
os.Setenv("BAR", "")
plugin := Plugin{
Config: Config{
Host: []string{"localhost"},
Username: "drone-scp",
Port: 22,
KeyPath: "./tests/.ssh/id_rsa",
Envs: []string{"foo", "bar", "baz"},
Debug: true,
Script: []string{"export FOO", "export BAR", "export BAZ", "env | grep -q '^FOO=Value for foo$'", "env | grep -q '^BAR=$'", "if env | grep -q BAZ; then false; else true; fi"},
CommandTimeout: 1 * time.Second,
Proxy: easyssh.DefaultConfig{
Server: "localhost",
User: "drone-scp",
Port: "22",
KeyPath: "./tests/.ssh/id_rsa",
},
},
}
err := plugin.Exec()
assert.Nil(t, err)
}
func TestSyncMode(t *testing.T) {
plugin := Plugin{
Config: Config{
Host: []string{"localhost", "127.0.0.1"},
Username: "drone-scp",
Port: 22,
KeyPath: "./tests/.ssh/id_rsa",
Script: []string{"whoami", "for i in {1..3}; do echo ${i}; sleep 1; done", "echo 'done'"},
CommandTimeout: 60 * time.Second,
Sync: true,
},
}
err := plugin.Exec()
assert.Nil(t, err)
}
func Test_escapeArg(t *testing.T) {
type args struct {
arg string
}
tests := []struct {
name string
args args
want string
}{
{
name: "escape nothing",
args: args{
arg: "Hi I am appleboy",
},
want: `'Hi I am appleboy'`,
},
{
name: "escape single quote",
args: args{
arg: "Hi I am 'appleboy'",
},
want: `'Hi I am '\''appleboy'\'''`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := escapeArg(tt.args.arg)
assert.Equal(t, tt.want, got)
})
}
}
func TestCommandOutput(t *testing.T) {
var (
buffer bytes.Buffer
expected = `
localhost: ======CMD======
localhost: pwd
whoami
uname
localhost: ======END======
localhost: out: /home/drone-scp
localhost: out: drone-scp
localhost: out: Linux
127.0.0.1: ======CMD======
127.0.0.1: pwd
whoami
uname
127.0.0.1: ======END======
127.0.0.1: out: /home/drone-scp
127.0.0.1: out: drone-scp
127.0.0.1: out: Linux
`
)
plugin := Plugin{
Config: Config{
Host: []string{"localhost", "127.0.0.1"},
Username: "drone-scp",
Port: 22,
KeyPath: "./tests/.ssh/id_rsa",
Script: []string{
"pwd",
"whoami",
"uname",
},
CommandTimeout: 60 * time.Second,
Sync: true,
},
Writer: &buffer,
}
err := plugin.Exec()
assert.Nil(t, err)
assert.Equal(t, unindent(expected), unindent(buffer.String()))
}
func TestScriptStop(t *testing.T) {
var (
buffer bytes.Buffer
expected = `
======CMD======
mkdir a/b/c
mkdir d/e/f
======END======
err: mkdir: can't create directory 'a/b/c': No such file or directory
`
)
plugin := Plugin{
Config: Config{
Host: []string{"localhost"},
Username: "drone-scp",
Port: 22,
KeyPath: "./tests/.ssh/id_rsa",
Script: []string{
"mkdir a/b/c",
"mkdir d/e/f",
},
CommandTimeout: 10 * time.Second,
ScriptStop: true,
},
Writer: &buffer,
}
err := plugin.Exec()
assert.NotNil(t, err)
assert.Equal(t, unindent(expected), unindent(buffer.String()))
}
func TestNoneScriptStop(t *testing.T) {
var (
buffer bytes.Buffer
expected = `
======CMD======
mkdir a/b/c
mkdir d/e/f
======END======
err: mkdir: can't create directory 'a/b/c': No such file or directory
err: mkdir: can't create directory 'd/e/f': No such file or directory
`
)
plugin := Plugin{
Config: Config{
Host: []string{"localhost"},
Username: "drone-scp",
Port: 22,
KeyPath: "./tests/.ssh/id_rsa",
Script: []string{
"mkdir a/b/c",
"mkdir d/e/f",
},
CommandTimeout: 10 * time.Second,
},
Writer: &buffer,
}
err := plugin.Exec()
assert.NotNil(t, err)
assert.Equal(t, unindent(expected), unindent(buffer.String()))
}
func TestEnvOutput(t *testing.T) {
var (
buffer bytes.Buffer
expected = `
======CMD======
echo "[${ENV_1}]"
echo "[${ENV_2}]"
echo "[${ENV_3}]"
echo "[${ENV_4}]"
echo "[${ENV_5}]"
echo "[${ENV_6}]"
echo "[${ENV_7}]"
======END======
======ENV======
ENV_1='test'
ENV_2='test test'
ENV_3='test '
ENV_4=' test test '
ENV_5='test'\'''
ENV_6='test"'
ENV_7='test,!#;?.@$~'\''"'
======END======
out: [test]
out: [test test]
out: [test ]
out: [ test test ]
out: [test']
out: [test"]
out: [test,!#;?.@$~'"]
`
)
os.Setenv("ENV_1", `test`)
os.Setenv("ENV_2", `test test`)
os.Setenv("ENV_3", `test `)
os.Setenv("ENV_4", ` test test `)
os.Setenv("ENV_5", `test'`)
os.Setenv("ENV_6", `test"`)
os.Setenv("ENV_7", `test,!#;?.@$~'"`)
plugin := Plugin{
Config: Config{
Host: []string{"localhost"},
Username: "drone-scp",
Port: 22,
KeyPath: "./tests/.ssh/id_rsa",
Envs: []string{"env_1", "env_2", "env_3", "env_4", "env_5", "env_6", "env_7"},
Debug: true,
Script: []string{
`echo "[${ENV_1}]"`,
`echo "[${ENV_2}]"`,
`echo "[${ENV_3}]"`,
`echo "[${ENV_4}]"`,
`echo "[${ENV_5}]"`,
`echo "[${ENV_6}]"`,
`echo "[${ENV_7}]"`,
},
CommandTimeout: 10 * time.Second,
Proxy: easyssh.DefaultConfig{
Server: "localhost",
User: "drone-scp",
Port: "22",
KeyPath: "./tests/.ssh/id_rsa",
},
},
Writer: &buffer,
}
err := plugin.Exec()
assert.Nil(t, err)
assert.Equal(t, unindent(expected), unindent(buffer.String()))
}
func unindent(text string) string {
return strings.TrimSpace(strings.Replace(text, "\t", "", -1))
}
func TestPlugin_scriptCommands(t *testing.T) {
type fields struct {
Config Config
Writer io.Writer
}
tests := []struct {
name string
fields fields
want []string
}{
{
name: "normal testing",
fields: fields{
Config: Config{
Script: []string{"mkdir a", "mkdir b"},
},
},
want: []string{"mkdir a", "mkdir b"},
},
{
name: "script stop",
fields: fields{
Config: Config{
Script: []string{"mkdir a", "mkdir b"},
ScriptStop: true,
},
},
want: []string{"mkdir a", "DRONE_SSH_PREV_COMMAND_EXIT_CODE=$? ; if [ $DRONE_SSH_PREV_COMMAND_EXIT_CODE -ne 0 ]; then exit $DRONE_SSH_PREV_COMMAND_EXIT_CODE; fi;", "mkdir b", "DRONE_SSH_PREV_COMMAND_EXIT_CODE=$? ; if [ $DRONE_SSH_PREV_COMMAND_EXIT_CODE -ne 0 ]; then exit $DRONE_SSH_PREV_COMMAND_EXIT_CODE; fi;"},
},
{
name: "normal testing 2",
fields: fields{
Config: Config{
Script: []string{"mkdir a\nmkdir c", "mkdir b"},
ScriptStop: true,
},
},
want: []string{"mkdir a", "DRONE_SSH_PREV_COMMAND_EXIT_CODE=$? ; if [ $DRONE_SSH_PREV_COMMAND_EXIT_CODE -ne 0 ]; then exit $DRONE_SSH_PREV_COMMAND_EXIT_CODE; fi;", "mkdir c", "DRONE_SSH_PREV_COMMAND_EXIT_CODE=$? ; if [ $DRONE_SSH_PREV_COMMAND_EXIT_CODE -ne 0 ]; then exit $DRONE_SSH_PREV_COMMAND_EXIT_CODE; fi;", "mkdir b", "DRONE_SSH_PREV_COMMAND_EXIT_CODE=$? ; if [ $DRONE_SSH_PREV_COMMAND_EXIT_CODE -ne 0 ]; then exit $DRONE_SSH_PREV_COMMAND_EXIT_CODE; fi;"},
},
{
name: "trim space",
fields: fields{
Config: Config{
Script: []string{"mkdir a", "mkdir b", "\t", " "},
ScriptStop: false,
},
},
want: []string{"mkdir a", "mkdir b"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := Plugin{
Config: tt.fields.Config,
Writer: tt.fields.Writer,
}
if got := p.scriptCommands(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("Plugin.scriptCommands() = %#v, want %#v", got, tt.want)
}
})
}
} }
+27
View File
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA4e2D/qPN08pzTac+a8ZmlP1ziJOXk45CynMPtva0rtK/RB26
VbfAF0hIJji7ltvnYnqCU9oFfvEM33cTn7T96+od8ib/Vz25YU8ZbstqtIskPuwC
bv3K0mAHgsviJyRD7yM+QKTbBQEgbGuW6gtbMKhiYfiIB4Dyj7AdS/fk3v26wDgz
7SHI5OBqu9bv1KhxQYdFEnU3PAtAqeccgzNpbH3eYLyGzuUxEIJlhpZ/uU2G9ppj
/cSrONVPiI8Ahi4RrlZjmP5l57/sq1ClGulyLpFcMw68kP5FikyqHpHJHRBNgU57
1y0Ph33SjBbs0haCIAcmreWEhGe+/OXnJe6VUQIDAQABAoIBAH97emORIm9DaVSD
7mD6DqA7c5m5Tmpgd6eszU08YC/Vkz9oVuBPUwDQNIX8tT0m0KVs42VVPIyoj874
bgZMJoucC1G8V5Bur9AMxhkShx9g9A7dNXJTmsKilRpk2TOk7wBdLp9jZoKoZBdJ
jlp6FfaazQjjKD6zsCsMATwAoRCBpBNsmT6QDN0n0bIgY0tE6YGQaDdka0dAv68G
R0VZrcJ9voT6+f+rgJLoojn2DAu6iXaM99Gv8FK91YCymbQlXXgrk6CyS0IHexN7
V7a3k767KnRbrkqd3o6JyNun/CrUjQwHs1IQH34tvkWScbseRaFehcAm6mLT93RP
muauvMECgYEA9AXGtfDMse0FhvDPZx4mx8x+vcfsLvDHcDLkf/lbyPpu97C27b/z
ia07bu5TAXesUZrWZtKA5KeRE5doQSdTOv1N28BEr8ZwzDJwfn0DPUYUOxsN2iIy
MheO5A45Ko7bjKJVkZ61Mb1UxtqCTF9mqu9R3PBdJGthWOd+HUvF460CgYEA7QRf
Z8+vpGA+eSuu29e0xgRKnRzed5zXYpcI4aERc3JzBgO4Z0er9G8l66OWVGdMfpe6
CBajC5ToIiT8zqoYxXwqJgN+glir4gJe3mm8J703QfArZiQrdk0NTi5bY7+vLLG/
knTrtpdsKih6r3kjhuPPaAsIwmMxIydFvATKjLUCgYEAh/y4EihRSk5WKC8GxeZt
oiZ58vT4z+fqnMIfyJmD5up48JuQNcokw/LADj/ODiFM7GUnWkGxBrvDA3H67WQm
49bJjs8E+BfUQFdTjYnJRlpJZ+7Zt1gbNQMf5ENw5CCchTDqEq6pN0DVf8PBnSIF
KvkXW9KvdV5J76uCAn15mDkCgYA1y8dHzbjlCz9Cy2pt1aDfTPwOew33gi7U3skS
RTerx29aDyAcuQTLfyrROBkX4TZYiWGdEl5Bc7PYhCKpWawzrsH2TNa7CRtCOh2E
R+V/84+GNNf04ALJYCXD9/ugQVKmR1XfDRCvKeFQFE38Y/dvV2etCswbKt5tRy2p
xkCe/QKBgQCkLqafD4S20YHf6WTp3jp/4H/qEy2X2a8gdVVBi1uKkGDXr0n+AoVU
ib4KbP5ovZlrjL++akMQ7V2fHzuQIFWnCkDA5c2ZAqzlM+ZN+HRG7gWur7Bt4XH1
7XC9wlRna4b3Ln8ew3q1ZcBjXwD4ppbTlmwAfQIaZTGJUgQbdsO9YA==
-----END RSA PRIVATE KEY-----
+1
View File
@@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDh7YP+o83TynNNpz5rxmaU/XOIk5eTjkLKcw+29rSu0r9EHbpVt8AXSEgmOLuW2+dieoJT2gV+8QzfdxOftP3r6h3yJv9XPblhTxluy2q0iyQ+7AJu/crSYAeCy+InJEPvIz5ApNsFASBsa5bqC1swqGJh+IgHgPKPsB1L9+Te/brAODPtIcjk4Gq71u/UqHFBh0USdTc8C0Cp5xyDM2lsfd5gvIbO5TEQgmWGln+5TYb2mmP9xKs41U+IjwCGLhGuVmOY/mXnv+yrUKUa6XIukVwzDryQ/kWKTKoekckdEE2BTnvXLQ+HfdKMFuzSFoIgByat5YSEZ7785ecl7pVR drone-scp@localhost
+13
View File
@@ -0,0 +1,13 @@
#!/bin/sh
if [ ! -f "/etc/ssh/ssh_host_rsa_key" ]; then
# generate fresh rsa key
ssh-keygen -f /etc/ssh/ssh_host_rsa_key -N '' -t rsa
fi
if [ ! -f "/etc/ssh/ssh_host_dsa_key" ]; then
# generate fresh dsa key
ssh-keygen -f /etc/ssh/ssh_host_dsa_key -N '' -t dsa
fi
exec "$@"