Compare commits

...

211 Commits

Author SHA1 Message Date
Bo-Yi Wu c8ecc03ef5 Revert "chore(scripts): disable SliceFlagSeparator" (#215) 2022-12-27 08:25:11 +08:00
Bo-Yi Wu ddf9ca3e6f chore(scripts): disable SliceFlagSeparator (#212) 2022-12-25 15:48:42 +08:00
Bo-Yi Wu a568d2e551 chore(cli): upgrade urfave/cli to v2 (#211) 2022-12-25 15:44:30 +08:00
Bo-Yi Wu d17a10dd0c Revert "chore(cli): upgrade urfave/cli to v2" (#210) 2022-12-25 15:15:55 +08:00
Bo-Yi Wu d83798f25b chore(cli): upgrade urfave/cli to v2 (#152) 2022-12-25 14:56:47 +08:00
Bo-Yi.Wu 59b08e1b4c chore(CI): Add Codecov
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-12-25 14:32:58 +08:00
Bo-Yi.Wu 8d07792340 chore(CI): replace go get with go install
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-12-25 14:27:10 +08:00
Bo-Yi.Wu 55bebcef3d chore(CI): add sshd service and testing
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-12-25 14:21:18 +08:00
Bo-Yi.Wu dfa002a267 chore(CI): release binary
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-12-25 11:12:32 +08:00
Bo-Yi.Wu 60da6458b5 docs(readme): remove drone badge
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-12-25 10:02:55 +08:00
Bo-Yi.Wu fe2455ee6d chore(CI): rename
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-12-25 10:01:25 +08:00
Bo-Yi.Wu d2cea88132 chore(CI): add hadolint checker
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-12-25 09:51:48 +08:00
Bo-Yi.Wu 8da3787c97 chore(CI): add enable checker
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-12-24 21:50:42 +08:00
Bo-Yi.Wu bf419cebf8 chore(CI): check push event
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-12-24 21:41:20 +08:00
Bo-Yi.Wu 7b141b72ae chore(dockerfile): replace ADD with COPY
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-12-24 21:34:09 +08:00
Bo-Yi.Wu 058dcd40d8 chore(CI): remove drone CI config
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-12-24 21:31:52 +08:00
Bo-Yi.Wu 598a11a626 chore(CI): update setup-go version
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-12-24 21:29:23 +08:00
Bo-Yi.Wu 31c298bb19 chore(CI): update version check.
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-12-24 21:26:33 +08:00
Bo-Yi.Wu df6997a8e1 chore(CI): add build config
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-12-24 21:16:51 +08:00
Bo-Yi.Wu 8c5823d726 chore(CI): rename
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-12-24 21:15:56 +08:00
Bo-Yi.Wu c643b61b1b chore(CI): update golangci-lint to latest
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-12-24 21:09:45 +08:00
Bo-Yi Wu 7c482e315d chore(deps): upgrade goreleaser 2022-12-20 11:24:55 +08:00
dependabot[bot] 07f8a59174 chore(deps): bump golang.org/x/crypto from 0.3.0 to 0.4.0 (#206)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-17 22:37:54 +08:00
Bo-Yi Wu f1ffd39895 chore(CI): disabled release binary 2022-12-08 17:23:51 +08:00
Bo-Yi Wu 040533909f chore(CI): disable build arm and arm64 2022-12-08 16:35:43 +08:00
Bo-Yi Wu 5be4214887 chore(CI): upgrade go version. 2022-12-08 09:21:50 +08:00
Bo-Yi Wu 906f6275a4 chore(CI): remove build step 2022-12-08 08:57:49 +08:00
Bo-Yi.Wu 4a05db1fe7 chore(CI): using container
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-12-07 22:01:05 +08:00
Bo-Yi.Wu f1be0b3f99 chore(CI): disable windows
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-12-07 21:47:03 +08:00
Bo-Yi.Wu d296797a6f chore(CI): enable build
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-12-07 21:36:00 +08:00
Bo-Yi Wu 5525ed602f fix(lint): io/ioutil is deprecated 2022-12-07 13:47:02 +08:00
Bo-Yi Wu 836fd2d711 chore(dep): update go dependency 2022-12-07 13:44:49 +08:00
WangJun 307409651f remove empty string output (#198) 2022-12-07 13:35:59 +08:00
dependabot[bot] b937d64a58 chore(deps): bump github.com/stretchr/testify from 1.7.2 to 1.8.0 (#195)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-07 22:56:29 +08:00
Bo-Yi Wu 1dcc9acbe5 chore(release): support multiple platform 2022-06-16 10:50:33 +08:00
Bo-Yi Wu f11e2ed931 chore: remove testing 2022-06-15 23:16:34 +08:00
Bo-Yi Wu cf159c578b fix: missing installed version 2022-06-15 22:55:25 +08:00
Bo-Yi Wu c7df4b7f8c chore: bump golangci version 2022-06-15 22:52:24 +08:00
dependabot[bot] 4e6b85e4c7 chore(deps): bump github/codeql-action from 1 to 2 (#185)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-15 22:50:34 +08:00
dependabot[bot] 0dd29f2223 chore(deps): bump actions/checkout from 2 to 3 (#188)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-15 22:50:24 +08:00
dependabot[bot] bf697d7977 chore(deps): bump github.com/stretchr/testify from 1.6.1 to 1.7.2 (#191)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-15 22:50:06 +08:00
Bo-Yi Wu 1944689d90 chore: enable funding button 2022-06-15 22:26:03 +08:00
Bo-Yi Wu 7b035f27e3 chore: bump all dependecy 2022-06-15 22:25:29 +08:00
Bo-Yi Wu 492a5db6ae chore: update drone build flow 2022-06-15 22:24:28 +08:00
Bo-Yi Wu 2407d7a56b chore: update github plugin version 2022-06-15 22:21:51 +08:00
Bo-Yi Wu 642891be17 chore: add dependabot
fix https://github.com/appleboy/drone-ssh/pull/176
2022-06-15 22:19:33 +08:00
Bo-Yi Wu 471976f1d8 chore(go): upgrade to go1.17
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2022-02-06 13:02:24 +08:00
Bo-Yi Wu c4e24b1ab9 feat(CI): Enable GitHub Actions
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2022-02-06 13:00:22 +08:00
Bo-Yi Wu 9c88ecd501 chore: update go module
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2021-06-18 08:52:06 +08:00
Bo-Yi Wu b96b4db8cb chore: upgrade to go.16
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2021-06-18 08:48:29 +08:00
Bo-Yi Wu 6850ed1bff chore(makefile): change gofmt toll to gofumpt 2021-01-12 00:54:00 +08:00
Bo-Yi Wu 5750d46265 chore: update revive 2021-01-12 00:52:53 +08:00
Bo-Yi Wu 9edff25d7f chore(docs): fix typo 2020-11-18 06:33:33 +08:00
Bo-Yi Wu 79f4c2408b docs: add Multiple Hosts with differebt port
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2020-11-17 11:03:37 +08:00
Bo-Yi Wu 76fb630345 remove support i386
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2020-11-17 10:34:26 +08:00
Bo-Yi Wu e059b33708 chore: support multiple port (#168)
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2020-11-17 10:14:11 +08:00
Bo-Yi Wu bb733a53de chore: update docs
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2020-09-25 22:22:21 +08:00
Bo-Yi Wu f725d02d7b chore: add drone env
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2020-09-25 22:21:39 +08:00
Bo-Yi Wu ac47ca3480 chore: upgrade to go1.15
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2020-09-25 22:12:48 +08:00
Bo-Yi Wu 66bbf9b7cd chore: support Multiline SSH commands interpreted as single lines (#160)
https://github.com/appleboy/ssh-action/issues/75
2020-08-08 15:31:57 +08:00
Bo-Yi Wu 77a818a94d chore: update go module 2020-08-08 14:50:04 +08:00
Bo-Yi Wu 1d89d2d875 chore: load env from /run/drone/env path 2020-07-17 23:23:31 +08:00
Bo-Yi Wu b447da961e chore: remove auto load .env file
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2020-06-15 16:37:09 +08:00
Bo-Yi Wu 4d46a02d53 chore: bump easyssh to 1.3.7 2020-05-24 13:57:02 +08:00
Bo-Yi Wu 7994d5555e chore: update dependency 2020-05-24 12:43:43 +08:00
Bo-Yi Wu c7d8b0f2e9 chore: upgrade easy-ssh 2020-05-24 12:05:38 +08:00
Bo-Yi Wu 7755fdb39f chore: upgrade easy-ssh 2020-05-24 12:04:21 +08:00
Bo-Yi Wu 83417639b9 chore: clean up go module 2020-05-24 10:46:44 +08:00
Bo-Yi Wu 7344ac6529 chore: support UseInsecureCipher (#158) 2020-05-24 10:43:11 +08:00
Bo-Yi Wu 1288a4b20d docs: update fingerprint comment.
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2020-05-19 14:11:26 +08:00
Bo-Yi Wu 11ec0bec7e update makefile
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2020-05-19 14:02:15 +08:00
Bo-Yi Wu a2c6be3c19 support Fingerprint (#157) 2020-05-19 14:01:28 +08:00
Bo-Yi Wu 923defc397 easyssh-proxy upgrade => v1.3.5
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2020-05-19 12:55:32 +08:00
Bo-Yi Wu ec9d02ec38 missing Ciphers config
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2020-04-30 15:49:00 +08:00
Bo-Yi Wu 9c9e7914ce support custom Ciphers value
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2020-04-30 15:38:46 +08:00
techknowlogick 9e205a756f Add option to use file for host info (#156) 2020-04-29 14:21:31 +08:00
Bo-Yi Wu 99f63aaaa8 upgrade easyssh-proxy
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2020-04-16 15:27:12 +08:00
Bo-Yi Wu 6bdace5138 update to go 1.14
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2020-02-27 09:58:32 +08:00
Bo-Yi Wu 54cdb693e4 update module
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2020-02-07 13:25:48 +08:00
Bo-Yi Wu 81d0430ffd chore(makefile): remove GOPACKAGE variable 2020-02-01 00:42:50 +08:00
Bo-Yi Wu a046be092b docs: add passphrase example
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2019-12-31 09:46:38 +08:00
Bo-Yi Wu 6b488f1203 support ssh Passphrase (#149)
* support ssh Passphrase

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

* update

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2019-12-30 16:16:37 +08:00
Bo-Yi Wu 8d0b62974a chore: update default timeout to 10 minutes 2019-11-30 22:40:13 +08:00
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
36 changed files with 2166 additions and 411 deletions
-78
View File
@@ -1,78 +0,0 @@
workspace:
base: /srv/app
path: src/github.com/appleboy/drone-ssh
pipeline:
clone:
image: plugins/git
tags: true
# restore the cache from an sftp server
restore_cache:
image: appleboy/drone-sftp-cache
restore: true
mount: [ .glide, vendor ]
ignore_branch: true
test:
image: appleboy/golang-testing
pull: true
environment:
TAGS: netgo
GOPATH: /srv/app
commands:
- make dep_install
- 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:
image: appleboy/golang-testing
pull: true
environment:
TAGS: netgo
GOPATH: /srv/app
commands:
- make release
when:
event: [ tag ]
branch: [ refs/tags/* ]
publish_tag:
image: plugins/docker
repo: ${DRONE_REPO}
tags: [ '${DRONE_TAG}' ]
when:
event: [ tag ]
branch: [ refs/tags/* ]
publish_latest:
image: plugins/docker
repo: ${DRONE_REPO}
tags: [ 'latest' ]
when:
event: [ push ]
branch: [ master ]
release:
image: plugins/github-release
files:
- dist/release/*
when:
event: [ tag ]
branch: [ refs/tags/* ]
# rebuild the cache on the sftp server
rebuild_cache:
image: appleboy/drone-sftp-cache
rebuild: true
mount: [ .glide, vendor ]
ignore_branch: true
when:
branch: master
-1
View File
@@ -1 +0,0 @@
eyJhbGciOiJIUzI1NiJ9.d29ya3NwYWNlOgogIGJhc2U6IC9zcnYvYXBwCiAgcGF0aDogc3JjL2dpdGh1Yi5jb20vYXBwbGVib3kvZHJvbmUtc3NoCgpwaXBlbGluZToKICBjbG9uZToKICAgIGltYWdlOiBwbHVnaW5zL2dpdAogICAgdGFnczogdHJ1ZQoKICAjIHJlc3RvcmUgdGhlIGNhY2hlIGZyb20gYW4gc2Z0cCBzZXJ2ZXIKICByZXN0b3JlX2NhY2hlOgogICAgaW1hZ2U6IGFwcGxlYm95L2Ryb25lLXNmdHAtY2FjaGUKICAgIHJlc3RvcmU6IHRydWUKICAgIG1vdW50OiBbIC5nbGlkZSwgdmVuZG9yIF0KICAgIGlnbm9yZV9icmFuY2g6IHRydWUKCiAgdGVzdDoKICAgIGltYWdlOiBhcHBsZWJveS9nb2xhbmctdGVzdGluZwogICAgcHVsbDogdHJ1ZQogICAgZW52aXJvbm1lbnQ6CiAgICAgIFRBR1M6IG5ldGdvCiAgICAgIEdPUEFUSDogL3Nydi9hcHAKICAgIGNvbW1hbmRzOgogICAgICAtIG1ha2UgZGVwX2luc3RhbGwKICAgICAgLSBtYWtlIHZldAogICAgICAtIG1ha2UgbGludAogICAgICAtIG1ha2UgdGVzdAogICAgICAtIG1ha2UgY292ZXJhZ2UKICAgICAgLSBtYWtlIGJ1aWxkCiAgICAgICMgYnVpbGQgYmluYXJ5IGZvciBkb2NrZXIgaW1hZ2UKICAgICAgLSBtYWtlIHN0YXRpY19idWlsZAogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCwgdGFnLCBwdWxsX3JlcXVlc3QgXQoKICByZWxlYXNlOgogICAgaW1hZ2U6IGFwcGxlYm95L2dvbGFuZy10ZXN0aW5nCiAgICBwdWxsOiB0cnVlCiAgICBlbnZpcm9ubWVudDoKICAgICAgVEFHUzogbmV0Z28KICAgICAgR09QQVRIOiAvc3J2L2FwcAogICAgY29tbWFuZHM6CiAgICAgIC0gbWFrZSByZWxlYXNlCiAgICB3aGVuOgogICAgICBldmVudDogWyB0YWcgXQogICAgICBicmFuY2g6IFsgcmVmcy90YWdzLyogXQoKICBwdWJsaXNoX3RhZzoKICAgIGltYWdlOiBwbHVnaW5zL2RvY2tlcgogICAgcmVwbzogJHtEUk9ORV9SRVBPfQogICAgdGFnczogWyAnJHtEUk9ORV9UQUd9JyBdCiAgICB3aGVuOgogICAgICBldmVudDogWyB0YWcgXQogICAgICBicmFuY2g6IFsgcmVmcy90YWdzLyogXQoKICBwdWJsaXNoX2xhdGVzdDoKICAgIGltYWdlOiBwbHVnaW5zL2RvY2tlcgogICAgcmVwbzogJHtEUk9ORV9SRVBPfQogICAgdGFnczogWyAnbGF0ZXN0JyBdCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoIF0KICAgICAgYnJhbmNoOiBbIG1hc3RlciBdCgogIHJlbGVhc2U6CiAgICBpbWFnZTogcGx1Z2lucy9naXRodWItcmVsZWFzZQogICAgZmlsZXM6CiAgICAgIC0gZGlzdC9yZWxlYXNlLyoKICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHRhZyBdCiAgICAgIGJyYW5jaDogWyByZWZzL3RhZ3MvKiBdCgogICMgcmVidWlsZCB0aGUgY2FjaGUgb24gdGhlIHNmdHAgc2VydmVyCiAgcmVidWlsZF9jYWNoZToKICAgIGltYWdlOiBhcHBsZWJveS9kcm9uZS1zZnRwLWNhY2hlCiAgICByZWJ1aWxkOiB0cnVlCiAgICBtb3VudDogWyAuZ2xpZGUsIHZlbmRvciBdCiAgICBpZ25vcmVfYnJhbmNoOiB0cnVlCiAgICB3aGVuOgogICAgICBicmFuY2g6IG1hc3Rlcgo.MvA91xJBP4JoAxLObpbaumlNtzjO_Gc3znDbTRUjAGY
+13
View File
@@ -0,0 +1,13 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: ['https://www.paypal.me/appleboy46']
+10
View File
@@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
- package-ecosystem: gomod
directory: /
schedule:
interval: weekly
+24
View File
@@ -0,0 +1,24 @@
name: Release Binary
on:
push:
branches:
- master
tags:
- 'v*'
jobs:
build:
runs-on: ubuntu-latest
container: techknowlogick/xgo:go-1.19.x
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0
# - name: Release binary
# run: |
# ls -al
# make release
+54
View File
@@ -0,0 +1,54 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '41 23 * * 6'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'go' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://git.io/codeql-language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
+138
View File
@@ -0,0 +1,138 @@
name: Docker Image
on:
push:
branches:
- master
tags:
- 'v*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Setup go
uses: actions/setup-go@v3
with:
go-version: '^1'
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Build binary
run : |
make build_linux_amd64
make build_linux_arm64
make build_linux_arm
- name: Check binary
run : |
./release/linux/amd64/drone-ssh --help
-
name: Set up QEMU
uses: docker/setup-qemu-action@v2
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Login to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
-
name: Docker meta for linux amd64
id: meta-linux-amd64
uses: docker/metadata-action@v4
with:
flavor: |
latest=false
suffix=linux-amd64
images: |
appleboy/drone-ssh
ghcr.io/appleboy/drone-ssh
tags: |
type=raw,value=,enable={{is_default_branch}}
type=raw,value=latest,suffix=,enable={{is_default_branch}}
type=semver,event=tag,pattern={{version}},suffix=-linux-amd64
type=semver,event=tag,pattern={{major}}.{{minor}},suffix=-linux-amd64
type=semver,event=tag,pattern={{major}},suffix=-linux-amd64
-
name: Build and push linux amd64
uses: docker/build-push-action@v3
with:
context: .
file: docker/Dockerfile.linux.amd64
platforms: linux/amd64
push: true
tags: ${{ steps.meta-linux-amd64.outputs.tags }}
labels: ${{ steps.meta-linux-amd64.outputs.labels }}
-
name: Docker meta for linux arm64
id: meta-linux-arm64
uses: docker/metadata-action@v4
with:
flavor: |
latest=false
suffix=linux-arm64
images: |
appleboy/drone-ssh
ghcr.io/appleboy/drone-ssh
tags: |
type=raw,value=,enable={{is_default_branch}}
type=semver,event=tag,pattern={{version}},suffix=-linux-arm64
type=semver,event=tag,pattern={{major}}.{{minor}},suffix=-linux-arm64
type=semver,event=tag,pattern={{major}},suffix=-linux-arm64
-
name: Build and push linux arm64
uses: docker/build-push-action@v3
with:
context: .
file: docker/Dockerfile.linux.arm64
platforms: linux/arm64
push: true
tags: ${{ steps.meta-linux-arm64.outputs.tags }}
labels: ${{ steps.meta-linux-arm64.outputs.labels }}
-
name: Docker meta for linux arm
id: meta-linux-arm
uses: docker/metadata-action@v4
with:
flavor: |
latest=false
suffix=linux-arm
images: |
appleboy/drone-ssh
ghcr.io/appleboy/drone-ssh
tags: |
type=raw,value=,enable={{is_default_branch}}
type=semver,event=tag,pattern={{version}},suffix=-linux-arm
type=semver,event=tag,pattern={{major}}.{{minor}},suffix=-linux-arm
type=semver,event=tag,pattern={{major}},suffix=-linux-arm
-
name: Build and push linux arm
uses: docker/build-push-action@v3
with:
context: .
file: docker/Dockerfile.linux.arm
platforms: linux/arm
push: true
tags: ${{ steps.meta-linux-arm.outputs.tags }}
labels: ${{ steps.meta-linux-arm.outputs.labels }}
+35
View File
@@ -0,0 +1,35 @@
name: Goreleaser
on:
push:
tags:
- '*'
permissions:
contents: write
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
-
name: Setup go
uses: actions/setup-go@v3
with:
go-version: '^1'
-
name: Run GoReleaser
uses: goreleaser/goreleaser-action@v4
with:
# either 'goreleaser' (default) or 'goreleaser-pro'
distribution: goreleaser
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+53
View File
@@ -0,0 +1,53 @@
name: Lint and Testing
on:
push:
pull_request:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Setup go
uses: actions/setup-go@v3
with:
go-version: '^1'
- name: Checkout repository
uses: actions/checkout@v3
- name: Setup golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: latest
args: --verbose
- uses: hadolint/hadolint-action@v3.0.0
name: hadolint for Dockerfile.linux.amd64
with:
dockerfile: docker/Dockerfile.linux.amd64
- uses: hadolint/hadolint-action@v3.0.0
name: hadolint for Dockerfile.linux.arm64
with:
dockerfile: docker/Dockerfile.linux.arm64
- uses: hadolint/hadolint-action@v3.0.0
name: hadolint for Dockerfile.linux.arm
with:
dockerfile: docker/Dockerfile.linux.arm
testing:
runs-on: ubuntu-latest
container: golang:1.19-alpine
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: setup sshd server
run: |
apk add git make curl perl bash build-base zlib-dev ucl-dev
make ssh-server
- name: testing
run: |
make test
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
+3 -1
View File
@@ -25,5 +25,7 @@ _testmain.go
.env
coverage.txt
release
drone-ssh
vendor
.cover
dist
+2
View File
@@ -0,0 +1,2 @@
ignored:
- DL3018
+240 -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
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:
Use the SSH plugin to execute commands on a remote server. The below pipeline configuration demonstrates simple usage:
```yaml
pipeline:
ssh:
image: plugins/ssh
- name: ssh commands
image: appleboy/drone-ssh
settings:
host: foo.com
user: root
username: root
password: 1234
port: 22
script:
@@ -41,27 +23,235 @@ pipeline:
- echo world
```
Example configuration in your .drone.yml file for multiple hosts:
Example configuration in your `.drone.yml` file for multiple hosts:
```yaml
pipeline:
ssh:
image: plugins/ssh
host:
- foo.com
- bar.com
user: root
port: 22
sleep: 5
script:
```diff
- name: ssh commands
image: appleboy/drone-ssh
settings:
host:
+ - foo.com
+ - bar.com
username: root
password: 1234
port: 22
script:
- echo hello
- echo world
```
In the above example Drone executes the commands on multiple hosts
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.
Example configuration for multiple hosts with different port:
The above example also uses the `sleep` parameter. The sleep parameter
instructs Drone to sleep for N seconds between host executions.
```diff
- name: ssh commands
image: appleboy/drone-ssh
settings:
host:
+ - foo.com:1234
+ - bar.com:5678
username: root
password: 1234
- port: 22
script:
- echo hello
- echo world
```
Example configuration for command timeout, default value is 60 seconds:
```diff
- 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
environment:
commit: ${DRONE_BUILD_NUMBER}
settings:
host: foo.com
username: root
password: 1234
port: 22
+ envs:
- aws_access_key_id
- commit
script:
- export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
- echo $commit
```
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."
```
Example configuration for passphrase which protecting a private key:
```diff
- name: ssh commands
image: appleboy/drone-ssh
settings:
host: foo.com
username: root
+ key:
+ from_secret: ssh_key
+ passphrase: 1234
port: 22
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_passphrase
: The purpose of the passphrase is usually to encrypt the private key.
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_passphrase
: The purpose of the passphrase is usually to encrypt the private key.
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 ssh connection to establish, default is 30 seconds.
command_timeout
: Command timeout is the maximum amount of time for the execute commands, default is 10 minutes.
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"]
+124 -72
View File
@@ -1,17 +1,19 @@
.PHONY: test drone-ssh build fmt vet errcheck lint install update release-dirs release-build release-copy release-check release coverage
DIST := dist
EXECUTABLE := drone-ssh
GOFMT ?= gofumpt -l
DIST := dist
DIST_DIRS := $(DIST)/binaries $(DIST)/release
GO ?= go
SHASUM ?= shasum -a 256
GOFILES := $(shell find . -name "*.go" -type f)
HAS_GO = $(shell hash $(GO) > /dev/null 2>&1 && echo "GO" || echo "NOGO" )
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
XGO_VERSION := go-1.19.x
GXZ_PAGAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11
# for dockerhub
DEPLOY_ACCOUNT := appleboy
DEPLOY_IMAGE := $(EXECUTABLE)
TARGETS ?= linux darwin windows
PACKAGES ?= $(shell go list ./... | grep -v /vendor/)
SOURCES ?= $(shell find . -name "*.go" -type f)
TAGS ?=
LDFLAGS ?= -X 'main.Version=$(VERSION)'
LINUX_ARCHS ?= linux/amd64,linux/arm64
DARWIN_ARCHS ?= darwin-10.12/amd64,darwin-10.12/arm64
WINDOWS_ARCHS ?= windows/*
ifneq ($(shell uname), Darwin)
EXTLDFLAGS = -extldflags "-static" $(null)
@@ -19,96 +21,146 @@ else
EXTLDFLAGS =
endif
ifeq ($(HAS_GO), GO)
GOPATH ?= $(shell $(GO) env GOPATH)
export PATH := $(GOPATH)/bin:$(PATH)
CGO_EXTRA_CFLAGS := -DSQLITE_MAX_VARIABLE_NUMBER=32766
CGO_CFLAGS ?= $(shell $(GO) env CGO_CFLAGS) $(CGO_EXTRA_CFLAGS)
endif
ifeq ($(OS), Windows_NT)
GOFLAGS := -v -buildmode=exe
EXECUTABLE ?= $(EXECUTABLE).exe
else ifeq ($(OS), Windows)
GOFLAGS := -v -buildmode=exe
EXECUTABLE ?= $(EXECUTABLE).exe
else
GOFLAGS := -v
EXECUTABLE ?= $(EXECUTABLE)
endif
ifneq ($(DRONE_TAG),)
VERSION ?= $(DRONE_TAG)
else
VERSION ?= $(shell git describe --tags --always || git rev-parse --short HEAD)
endif
TAGS ?=
LDFLAGS ?= -X 'main.Version=$(VERSION)'
all: build
fmt:
find . -name "*.go" -type f -not -path "./vendor/*" | xargs gofmt -s -w
@hash gofumpt > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) install mvdan.cc/gofumpt; \
fi
$(GOFMT) -w $(GOFILES)
vet:
go vet $(PACKAGES)
$(GO) vet ./...
errcheck:
@which errcheck > /dev/null; if [ $$? -ne 0 ]; then \
go get -u github.com/kisielk/errcheck; \
.PHONY: fmt-check
fmt-check:
@hash gofumpt > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) install mvdan.cc/gofumpt; \
fi
errcheck $(PACKAGES)
lint:
@which golint > /dev/null; if [ $$? -ne 0 ]; then \
go get -u github.com/golang/lint/golint; \
fi
for PKG in $(PACKAGES); do golint -set_exit_status $$PKG || exit 1; done;
@diff=$$($(GOFMT) -d $(GOFILES)); \
if [ -n "$$diff" ]; then \
echo "Please run 'make fmt' and commit the result:"; \
echo "$${diff}"; \
exit 1; \
fi;
test:
for PKG in $(PACKAGES); do go test -v -cover -coverprofile $$GOPATH/src/$$PKG/coverage.txt $$PKG || exit 1; done;
@$(GO) test -v -cover -coverprofile coverage.txt ./... && echo "\n==>\033[32m Ok\033[m\n" || exit 1
html:
go tool cover -html=coverage.txt
dep_install:
glide install
dep_update:
glide up
install: $(SOURCES)
go install -v -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(LDFLAGS)'
install: $(GOFILES)
$(GO) install -v -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(LDFLAGS)'
build: $(EXECUTABLE)
$(EXECUTABLE): $(SOURCES)
go build -v -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(LDFLAGS)' -o $@
$(EXECUTABLE): $(GOFILES)
$(GO) build -v -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(LDFLAGS)' -o $@
release: release-dirs release-build release-copy release-check
build_linux_amd64:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GO) build -a -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(LDFLAGS)' -o release/linux/amd64/$(DEPLOY_IMAGE)
release-dirs:
mkdir -p $(DIST)/binaries $(DIST)/release
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)
release-build:
@which gox > /dev/null; if [ $$? -ne 0 ]; then \
go get -u github.com/mitchellh/gox; \
fi
gox -os="$(TARGETS)" -arch="amd64 386" -tags="$(TAGS)" -ldflags="-s -w $(LDFLAGS)" -output="$(DIST)/binaries/$(EXECUTABLE)-$(VERSION)-{{.OS}}-{{.Arch}}"
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)
release-copy:
$(foreach file,$(wildcard $(DIST)/binaries/$(EXECUTABLE)-*),cp $(file) $(DIST)/release/$(notdir $(file));)
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)
release-check:
cd $(DIST)/release; $(foreach file,$(wildcard $(DIST)/release/$(EXECUTABLE)-*),sha256sum $(notdir $(file)) > $(notdir $(file)).sha256;)
# for docker.
static_build:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(LDFLAGS)' -o $(DEPLOY_IMAGE)
docker_image:
docker build -t $(DEPLOY_ACCOUNT)/$(DEPLOY_IMAGE) .
docker: static_build docker_image
docker_deploy:
ifeq ($(tag),)
@echo "Usage: make $@ tag=<tag>"
@exit 1
endif
# deploy image
docker tag $(DEPLOY_ACCOUNT)/$(DEPLOY_IMAGE):latest $(DEPLOY_ACCOUNT)/$(DEPLOY_IMAGE):$(tag)
docker push $(DEPLOY_ACCOUNT)/$(DEPLOY_IMAGE):$(tag)
ssh-server:
adduser -h /home/drone-scp -s /bin/sh -D -S drone-scp
echo drone-scp:1234 | chpasswd
mkdir -p /home/drone-scp/.ssh
chmod 700 /home/drone-scp/.ssh
cat tests/.ssh/id_rsa.pub >> /home/drone-scp/.ssh/authorized_keys
cat tests/.ssh/test.pub >> /home/drone-scp/.ssh/authorized_keys
chmod 600 /home/drone-scp/.ssh/authorized_keys
chown -R drone-scp /home/drone-scp/.ssh
apk add --update openssh openrc
rm -rf /etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_dsa_key
sed -i 's/^#PubkeyAuthentication yes/PubkeyAuthentication yes/g' /etc/ssh/sshd_config
sed -i 's/AllowTcpForwarding no/AllowTcpForwarding yes/g' /etc/ssh/sshd_config
./tests/entrypoint.sh /usr/sbin/sshd -D &
coverage:
sed -i '/main.go/d' coverage.txt
curl -s https://codecov.io/bash > .codecov && \
chmod +x .codecov && \
./.codecov -f coverage.txt
.PHONY: deps-backend
deps-backend:
$(GO) mod download
$(GO) install $(GXZ_PAGAGE)
$(GO) install $(XGO_PACKAGE)
.PHONY: release
release: release-linux release-darwin release-windows release-copy release-compress release-check
$(DIST_DIRS):
mkdir -p $(DIST_DIRS)
.PHONY: release-windows
release-windows: | $(DIST_DIRS)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(WINDOWS_ARCHS)' -out $(EXECUTABLE)-$(VERSION) .
ifeq ($(CI),true)
cp -r /build/* $(DIST)/binaries/
endif
.PHONY: release-linux
release-linux: | $(DIST_DIRS)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out $(EXECUTABLE)-$(VERSION) .
ifeq ($(CI),true)
cp -r /build/* $(DIST)/binaries/
endif
.PHONY: release-darwin
release-darwin: | $(DIST_DIRS)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets '$(DARWIN_ARCHS)' -out $(EXECUTABLE)-$(VERSION) .
ifeq ($(CI),true)
cp -r /build/* $(DIST)/binaries/
endif
.PHONY: release-copy
release-copy: | $(DIST_DIRS)
cd $(DIST); for file in `find . -type f -name "*"`; do cp $${file} ./release/; done;
.PHONY: release-check
release-check: | $(DIST_DIRS)
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "checksumming $${file}" && $(SHASUM) `echo $${file} | sed 's/^..//'` > $${file}.sha256; done;
.PHONY: release-compress
release-compress: | $(DIST_DIRS)
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && $(GO) run $(GXZ_PAGAGE) -k -9 $${file}; done;
clean:
go clean -x -i ./...
rm -rf coverage.txt $(EXECUTABLE) $(DIST) vendor
$(GO) clean -x -i ./...
rm -rf coverage.txt $(EXECUTABLE) $(DIST)
version:
@echo $(VERSION)
+83 -21
View File
@@ -1,37 +1,76 @@
<img src="ssh.png">
# drone-ssh
[![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](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)
[![Lint and Testing](https://github.com/appleboy/drone-ssh/actions/workflows/lint.yml/badge.svg)](https://github.com/appleboy/drone-ssh/actions/workflows/lint.yml)
[![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/)
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
[the docs](DOCS.md).
information and a listing of the available options please take a look at [the docs](http://plugins.drone.io/appleboy/drone-ssh/).
## 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
Build the docker image with the following commands:
```
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -tags netgo
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..
```sh
make docker
```
## Usage
@@ -41,10 +80,33 @@ Execute from the working directory:
```sh
docker run --rm \
-e PLUGIN_HOST=foo.com \
-e PLUGIN_USER=root \
-e PLUGIN_USERNAME=root \
-e PLUGIN_KEY="$(cat ${HOME}/.ssh/id_rsa)" \
-e PLUGIN_SCRIPT=whoami \
-v $(pwd):$(pwd) \
-w $(pwd) \
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).
+13
View File
@@ -0,0 +1,13 @@
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/*
COPY release/linux/amd64/drone-ssh /bin/
ENTRYPOINT ["/bin/drone-ssh"]
+13
View File
@@ -0,0 +1,13 @@
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/*
COPY release/linux/arm/drone-ssh /bin/
ENTRYPOINT ["/bin/drone-ssh"]
+13
View File
@@ -0,0 +1,13 @@
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/*
COPY 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
+23
View File
@@ -0,0 +1,23 @@
module github.com/appleboy/drone-ssh
go 1.18
require (
github.com/appleboy/easyssh-proxy v1.3.9
github.com/joho/godotenv v1.4.0
github.com/stretchr/testify v1.8.1
github.com/urfave/cli/v2 v2.23.7
golang.org/x/crypto v0.4.0
)
require (
github.com/ScaleFT/sshkeys v1.2.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
golang.org/x/sys v0.3.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
+52
View File
@@ -0,0 +1,52 @@
github.com/ScaleFT/sshkeys v0.0.0-20200327173127-6142f742bca5/go.mod h1:gxOHeajFfvGQh/fxlC8oOKBe23xnnJTif00IFFbiT+o=
github.com/ScaleFT/sshkeys v1.2.0 h1:5BRp6rTVIhJzXT3VcUQrKgXR8zWA3sOsNeuyW15WUA8=
github.com/ScaleFT/sshkeys v1.2.0/go.mod h1:gxOHeajFfvGQh/fxlC8oOKBe23xnnJTif00IFFbiT+o=
github.com/appleboy/easyssh-proxy v1.3.9 h1:b+sVSTz+cVFvfA23HQywMMpm0s5g3gH7jYdBcQqaCQI=
github.com/appleboy/easyssh-proxy v1.3.9/go.mod h1:G1eQomBEME7NWKA3hE49s5HsT44S5fn0aBxX7k9Yjug=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a h1:saTgr5tMLFnmy/yg3qDTft4rE5DY2uJ/cCxCe3q0XTU=
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a/go.mod h1:Bw9BbhOJVNR+t0jCqx2GC6zv0TGBsShs56Y3gfSCvl0=
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
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.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/urfave/cli/v2 v2.23.7 h1:YHDQ46s3VghFHFf1DdF+Sh7H4RqhcM+t0TmZRJx4oJY=
github.com/urfave/cli/v2 v2.23.7/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
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/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
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

+204 -53
View File
@@ -1,22 +1,34 @@
package main
import (
"log"
"os"
"time"
"github.com/appleboy/easyssh-proxy"
"github.com/joho/godotenv"
_ "github.com/joho/godotenv/autoload"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
// Version set at compile-time
var Version = "v1.0.0-dev"
var Version string
func main() {
// Load env-file if it exists first
if filename, found := os.LookupEnv("PLUGIN_ENV_FILE"); found {
_ = godotenv.Load(filename)
}
if _, err := os.Stat("/run/drone/env"); err == nil {
_ = godotenv.Overload("/run/drone/env")
}
app := cli.NewApp()
app.Name = "Drone SSH"
app.Usage = "Executing remote ssh commands"
app.Copyright = "Copyright (c) 2017 Bo-Yi Wu"
app.Authors = []cli.Author{
app.Copyright = "Copyright (c) 2019 Bo-Yi Wu"
app.Authors = []*cli.Author{
{
Name: "Bo-Yi Wu",
Email: "appleboy.tw@gmail.com",
@@ -25,51 +37,165 @@ func main() {
app.Action = run
app.Version = Version
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "ssh-key",
Usage: "private ssh key",
EnvVar: "PLUGIN_SSH_KEY,PLUGIN_KEY,SSH_KEY",
&cli.StringFlag{
Name: "ssh-key",
Usage: "private ssh key",
EnvVars: []string{"PLUGIN_SSH_KEY", "PLUGIN_KEY", "SSH_KEY", "KEY", "INPUT_KEY"},
},
cli.StringFlag{
Name: "user",
Usage: "connect as user",
EnvVar: "PLUGIN_USER,SSH_USER",
Value: "root",
&cli.StringFlag{
Name: "ssh-passphrase",
Usage: "The purpose of the passphrase is usually to encrypt the private key.",
EnvVars: []string{"PLUGIN_SSH_PASSPHRASE", "PLUGIN_PASSPHRASE", "SSH_PASSPHRASE", "PASSPHRASE", "INPUT_PASSPHRASE"},
},
cli.StringFlag{
Name: "password",
Usage: "user password",
EnvVar: "PLUGIN_PASSWORD,SSH_PASSWORD",
&cli.StringFlag{
Name: "key-path",
Aliases: []string{"i"},
Usage: "ssh private key path",
EnvVars: []string{"PLUGIN_KEY_PATH", "SSH_KEY_PATH", "INPUT_KEY_PATH"},
},
cli.StringSliceFlag{
Name: "host",
Usage: "connect to host",
EnvVar: "PLUGIN_HOST,SSH_HOST",
&cli.StringFlag{
Name: "username",
Aliases: []string{"user", "u"},
Usage: "connect as user",
EnvVars: []string{"PLUGIN_USERNAME", "PLUGIN_USER", "SSH_USERNAME", "USERNAME", "INPUT_USERNAME"},
Value: "root",
},
cli.IntFlag{
Name: "port",
Usage: "connect to port",
EnvVar: "PLUGIN_PORT,SSH_PORT",
Value: 22,
&cli.StringFlag{
Name: "password",
Aliases: []string{"P"},
Usage: "user password",
EnvVars: []string{"PLUGIN_PASSWORD", "SSH_PASSWORD", "PASSWORD", "INPUT_PASSWORD"},
},
cli.IntFlag{
Name: "sleep",
Usage: "sleep between hosts",
EnvVar: "PLUGIN_SLEEP,SSH_SLEEP",
&cli.StringSliceFlag{
Name: "ciphers",
Usage: "The allowed cipher algorithms. If unspecified then a sensible",
EnvVars: []string{"PLUGIN_CIPHERS", "SSH_CIPHERS", "CIPHERS", "INPUT_CIPHERS"},
},
cli.DurationFlag{
Name: "timeout",
Usage: "connection timeout",
EnvVar: "PLUGIN_TIMEOUT,SSH_TIMEOUT",
&cli.BoolFlag{
Name: "useInsecureCipher",
Usage: "include more ciphers with use_insecure_cipher",
EnvVars: []string{"PLUGIN_USE_INSECURE_CIPHER", "SSH_USE_INSECURE_CIPHER", "USE_INSECURE_CIPHER", "INPUT_USE_INSECURE_CIPHER"},
},
cli.StringSliceFlag{
Name: "script",
Usage: "execute commands",
EnvVar: "PLUGIN_SCRIPT,SSH_SCRIPT",
&cli.StringFlag{
Name: "fingerprint",
Usage: "fingerprint SHA256 of the host public key, default is to skip verification",
EnvVars: []string{"PLUGIN_FINGERPRINT", "SSH_FINGERPRINT", "FINGERPRINT", "INPUT_FINGERPRINT"},
},
cli.StringFlag{
Name: "env-file",
Usage: "source env file",
&cli.StringSliceFlag{
Name: "host",
Aliases: []string{"H"},
Usage: "connect to host",
EnvVars: []string{"PLUGIN_HOST", "SSH_HOST", "HOST", "INPUT_HOST"},
FilePath: ".host",
},
&cli.IntFlag{
Name: "port",
Aliases: []string{"p"},
Usage: "connect to port",
EnvVars: []string{"PLUGIN_PORT", "SSH_PORT", "PORT", "INPUT_PORT"},
Value: 22,
},
&cli.BoolFlag{
Name: "sync",
Usage: "sync mode",
EnvVars: []string{"PLUGIN_SYNC", "SYNC", "INPUT_SYNC"},
},
&cli.DurationFlag{
Name: "timeout",
Aliases: []string{"t"},
Usage: "connection timeout",
EnvVars: []string{"PLUGIN_TIMEOUT", "SSH_TIMEOUT", "TIMEOUT", "INPUT_TIMEOUT"},
Value: 30 * time.Second,
},
&cli.DurationFlag{
Name: "command.timeout",
Aliases: []string{"T"},
Usage: "command timeout",
EnvVars: []string{"PLUGIN_COMMAND_TIMEOUT", "SSH_COMMAND_TIMEOUT", "COMMAND_TIMEOUT", "INPUT_COMMAND_TIMEOUT"},
Value: 10 * time.Minute,
},
&cli.StringSliceFlag{
Name: "script",
Aliases: []string{"s"},
Usage: "execute commands",
EnvVars: []string{"PLUGIN_SCRIPT", "SSH_SCRIPT", "SCRIPT"},
},
&cli.StringFlag{
Name: "script.string",
Usage: "execute single commands for github action",
EnvVars: []string{"INPUT_SCRIPT"},
},
&cli.BoolFlag{
Name: "script.stop",
Usage: "stop script after first failure",
EnvVars: []string{"PLUGIN_SCRIPT_STOP", "STOP", "INPUT_SCRIPT_STOP"},
},
&cli.StringFlag{
Name: "proxy.ssh-key",
Usage: "private ssh key of proxy",
EnvVars: []string{"PLUGIN_PROXY_SSH_KEY", "PLUGIN_PROXY_KEY", "PROXY_SSH_KEY", "INPUT_PROXY_KEY"},
},
&cli.StringFlag{
Name: "proxy.ssh-passphrase",
Usage: "The purpose of the passphrase is usually to encrypt the private key.",
EnvVars: []string{"PLUGIN_PROXY_SSH_PASSPHRASE", "PLUGIN_PROXY_PASSPHRASE", "PROXY_SSH_PASSPHRASE", "PROXY_PASSPHRASE", "INPUT_PROXY_PASSPHRASE"},
},
&cli.StringFlag{
Name: "proxy.key-path",
Usage: "ssh private key path of proxy",
EnvVars: []string{"PLUGIN_PROXY_KEY_PATH", "PROXY_SSH_KEY_PATH", "INPUT_PROXY_KEY_PATH"},
},
&cli.StringFlag{
Name: "proxy.username",
Usage: "connect as user of proxy",
EnvVars: []string{"PLUGIN_PROXY_USERNAME", "PLUGIN_PROXY_USER", "PROXY_SSH_USERNAME", "INPUT_PROXY_USERNAME"},
Value: "root",
},
&cli.StringFlag{
Name: "proxy.password",
Usage: "user password of proxy",
EnvVars: []string{"PLUGIN_PROXY_PASSWORD", "PROXY_SSH_PASSWORD", "INPUT_PROXY_PASSWORD"},
},
&cli.StringFlag{
Name: "proxy.host",
Usage: "connect to host of proxy",
EnvVars: []string{"PLUGIN_PROXY_HOST", "PROXY_SSH_HOST", "INPUT_PROXY_HOST"},
},
&cli.StringFlag{
Name: "proxy.port",
Usage: "connect to port of proxy",
EnvVars: []string{"PLUGIN_PROXY_PORT", "PROXY_SSH_PORT", "INPUT_PROXY_PORT"},
Value: "22",
},
&cli.DurationFlag{
Name: "proxy.timeout",
Usage: "proxy connection timeout",
EnvVars: []string{"PLUGIN_PROXY_TIMEOUT", "PROXY_SSH_TIMEOUT", "INPUT_PROXY_TIMEOUT"},
},
&cli.StringSliceFlag{
Name: "proxy.ciphers",
Usage: "The allowed cipher algorithms. If unspecified then a sensible",
EnvVars: []string{"PLUGIN_PROXY_CIPHERS", "SSH_PROXY_CIPHERS", "PROXY_CIPHERS", "INPUT_PROXY_CIPHERS"},
},
&cli.BoolFlag{
Name: "proxy.useInsecureCipher",
Usage: "include more ciphers with use_insecure_cipher",
EnvVars: []string{"PLUGIN_PROXY_USE_INSECURE_CIPHER", "SSH_PROXY_USE_INSECURE_CIPHER", "PROXY_USE_INSECURE_CIPHER", "INPUT_PROXY_USE_INSECURE_CIPHER"},
},
&cli.StringFlag{
Name: "proxy.fingerprint",
Usage: "fingerprint SHA256 of the host public key, default is to skip verification",
EnvVars: []string{"PLUGIN_PROXY_FINGERPRINT", "SSH_PROXY_FINGERPRINT", "PROXY_FINGERPRINT", "INPUT_PROXY_FINGERPRINT"},
},
&cli.StringSliceFlag{
Name: "envs",
Usage: "pass environment variable to shell script",
EnvVars: []string{"PLUGIN_ENVS", "INPUT_ENVS"},
},
&cli.BoolFlag{
Name: "debug",
Usage: "debug mode",
EnvVars: []string{"PLUGIN_DEBUG", "DEBUG", "INPUT_DEBUG"},
},
}
@@ -106,25 +232,50 @@ REPOSITORY:
Github: https://github.com/appleboy/drone-ssh
`
app.Run(os.Args)
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
func run(c *cli.Context) error {
if c.String("env-file") != "" {
_ = godotenv.Load(c.String("env-file"))
scripts := c.StringSlice("script")
if s := c.String("script.string"); s != "" {
scripts = append(scripts, s)
}
plugin := Plugin{
Config: Config{
Key: c.String("ssh-key"),
User: c.String("user"),
Password: c.String("password"),
Host: c.StringSlice("host"),
Port: c.Int("port"),
Sleep: c.Int("sleep"),
Timeout: c.Duration("timeout"),
Script: c.StringSlice("script"),
Key: c.String("ssh-key"),
KeyPath: c.String("key-path"),
Username: c.String("user"),
Password: c.String("password"),
Passphrase: c.String("ssh-passphrase"),
Fingerprint: c.String("fingerprint"),
Host: c.StringSlice("host"),
Port: c.Int("port"),
Timeout: c.Duration("timeout"),
CommandTimeout: c.Duration("command.timeout"),
Script: scripts,
ScriptStop: c.Bool("script.stop"),
Envs: c.StringSlice("envs"),
Debug: c.Bool("debug"),
Sync: c.Bool("sync"),
Ciphers: c.StringSlice("ciphers"),
UseInsecureCipher: c.Bool("useInsecureCipher"),
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"),
Passphrase: c.String("proxy.ssh-passphrase"),
Fingerprint: c.String("proxy.fingerprint"),
Server: c.String("proxy.host"),
Port: c.String("proxy.port"),
Timeout: c.Duration("proxy.timeout"),
Ciphers: c.StringSlice("proxy.ciphers"),
UseInsecureCipher: c.Bool("proxy.useInsecureCipher"),
},
},
Writer: os.Stdout,
}
return plugin.Exec()
+196 -67
View File
@@ -1,108 +1,237 @@
package main
import (
"errors"
"fmt"
"log"
"net"
"io"
"os"
"strconv"
"strings"
"sync"
"time"
"golang.org/x/crypto/ssh"
"github.com/appleboy/easyssh-proxy"
)
const (
missingHostOrUser = "Error: missing server host or user"
missingPasswordOrKey = "Error: can't connect without a private SSH key or password"
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 (
// Config for the plugin.
Config struct {
Key string
User string
Password string
Host []string
Port int
Sleep int
Timeout time.Duration
Script []string
Key string
Passphrase string
KeyPath string
Username string
Password string
Host []string
Port int
Fingerprint string
Timeout time.Duration
CommandTimeout time.Duration
Script []string
ScriptStop bool
Envs []string
Proxy easyssh.DefaultConfig
Debug bool
Sync bool
Ciphers []string
UseInsecureCipher bool
}
// Plugin structure
Plugin struct {
Config Config
Writer io.Writer
}
)
func escapeArg(arg string) string {
return "'" + strings.Replace(arg, "'", `'\''`, -1) + "'"
}
func (p Plugin) hostPort(host string) (string, string) {
hosts := strings.Split(host, ":")
port := strconv.Itoa(p.Config.Port)
if len(hosts) > 1 {
host = hosts[0]
port = hosts[1]
}
return host, port
}
func (p Plugin) exec(host string, wg *sync.WaitGroup, errChannel chan error) {
host, port := p.hostPort(host)
// 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: port,
Key: p.Config.Key,
KeyPath: p.Config.KeyPath,
Passphrase: p.Config.Passphrase,
Timeout: p.Config.Timeout,
Ciphers: p.Config.Ciphers,
Fingerprint: p.Config.Fingerprint,
UseInsecureCipher: p.Config.UseInsecureCipher,
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,
Passphrase: p.Config.Proxy.Passphrase,
Timeout: p.Config.Proxy.Timeout,
Ciphers: p.Config.Proxy.Ciphers,
Fingerprint: p.Config.Proxy.Fingerprint,
UseInsecureCipher: p.Config.Proxy.UseInsecureCipher,
},
}
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:
if outline != "" {
p.log(host, "out:", outline)
}
case errline := <-stderrChan:
if errline != "" {
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.
func (p Plugin) Exec() error {
if len(p.Config.Host) == 0 && p.Config.User == "" {
return fmt.Errorf(missingHostOrUser)
if len(p.Config.Host) == 0 {
return errMissingHost
}
if p.Config.Key == "" && p.Config.Password == "" {
return fmt.Errorf(missingPasswordOrKey)
if len(p.Config.Key) == 0 && len(p.Config.Password) == 0 && len(p.Config.KeyPath) == 0 {
return errMissingPasswordOrKey
}
for i, host := range p.Config.Host {
addr := net.JoinHostPort(
host,
strconv.Itoa(p.Config.Port),
)
if len(p.Config.Key) != 0 && len(p.Config.Password) != 0 {
return errSetPasswordandKey
}
// auths holds the detected ssh auth methods
auths := []ssh.AuthMethod{}
if p.Config.Key != "" {
signer, err := ssh.ParsePrivateKey([]byte(p.Config.Key))
if err != nil {
return fmt.Errorf("Error: Failed to parse private key. %s", err)
}
auths = append(auths, ssh.PublicKeys(signer))
wg := sync.WaitGroup{}
wg.Add(len(p.Config.Host))
errChannel := make(chan error)
finished := make(chan struct{})
for _, host := range p.Config.Host {
if p.Config.Sync {
p.exec(host, &wg, errChannel)
} else {
go p.exec(host, &wg, errChannel)
}
}
// figure out what auths are requested, what is supported
if p.Config.Password != "" {
auths = append(auths, ssh.Password(p.Config.Password))
}
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)
go func() {
wg.Wait()
close(finished)
}()
select {
case <-finished:
case err := <-errChannel:
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
}
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
}
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 {
cmd = strings.TrimSpace(cmd)
if strings.TrimSpace(cmd) == "" {
continue
}
commands = append(commands, cmd)
if p.Config.ScriptStop && cmd[(len(cmd)-1):] != "\\" {
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
}
+747 -4
View File
@@ -1,9 +1,17 @@
package main
import (
"bytes"
"io"
"os"
"reflect"
"strings"
"testing"
"time"
"github.com/appleboy/easyssh-proxy"
"github.com/stretchr/testify/assert"
"golang.org/x/crypto/ssh"
)
func TestMissingHostOrUser(t *testing.T) {
@@ -12,19 +20,754 @@ func TestMissingHostOrUser(t *testing.T) {
err := plugin.Exec()
assert.NotNil(t, err)
assert.Equal(t, missingHostOrUser, err.Error())
assert.Equal(t, errMissingHost, err)
}
func TestMissingKeyOrPassword(t *testing.T) {
plugin := Plugin{
Config{
Host: []string{"localhost"},
User: "ubuntu",
Host: []string{"localhost"},
Username: "ubuntu",
},
os.Stdout,
}
err := plugin.Exec()
assert.NotNil(t, err)
assert.Equal(t, missingPasswordOrKey, err.Error())
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 TestWrongFingerprint(t *testing.T) {
var buffer bytes.Buffer
plugin := Plugin{
Config: Config{
Host: []string{"localhost"},
Username: "drone-scp",
Port: 22,
KeyPath: "./tests/.ssh/id_rsa",
Script: []string{
"whoami",
},
Fingerprint: "wrong",
},
Writer: &buffer,
}
err := plugin.Exec()
assert.NotNil(t, err)
}
func getHostPublicKeyFile(keypath string) (ssh.PublicKey, error) {
var pubkey ssh.PublicKey
var err error
buf, err := os.ReadFile(keypath)
if err != nil {
return nil, err
}
pubkey, _, _, _, err = ssh.ParseAuthorizedKey(buf)
if err != nil {
return nil, err
}
return pubkey, nil
}
func TestFingerprint(t *testing.T) {
var (
buffer bytes.Buffer
expected = `
======CMD======
whoami
======END======
out: drone-scp
`
)
hostKey, err := getHostPublicKeyFile("/etc/ssh/ssh_host_rsa_key.pub")
assert.NoError(t, err)
plugin := Plugin{
Config: Config{
Host: []string{"localhost"},
Username: "drone-scp",
Port: 22,
KeyPath: "./tests/.ssh/id_rsa",
Script: []string{
"whoami",
},
Fingerprint: ssh.FingerprintSHA256(hostKey),
CommandTimeout: 10 * time.Second,
},
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/test",
Passphrase: "1234",
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;"},
},
// See: https://github.com/appleboy/ssh-action/issues/75#issuecomment-668314271
{
name: "Multiline SSH commands interpreted as single lines",
fields: fields{
Config: Config{
Script: []string{"ls \\ ", "-lah", "mkdir a"},
ScriptStop: true,
},
},
want: []string{"ls \\", "-lah", "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 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;"},
},
{
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)
}
})
}
}
func TestUseInsecureCipher(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,
UseInsecureCipher: true,
},
Writer: &buffer,
}
err := plugin.Exec()
assert.NotNil(t, err)
assert.Equal(t, unindent(expected), unindent(buffer.String()))
}
func TestPlugin_hostPort(t *testing.T) {
type fields struct {
Config Config
Writer io.Writer
}
type args struct {
h string
}
tests := []struct {
name string
fields fields
args args
wantHost string
wantPort string
}{
{
name: "default host and port",
fields: fields{
Config: Config{
Port: 22,
},
},
args: args{
h: "localhost",
},
wantHost: "localhost",
wantPort: "22",
},
{
name: "different port",
fields: fields{
Config: Config{
Port: 22,
},
},
args: args{
h: "localhost:443",
},
wantHost: "localhost",
wantPort: "443",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := Plugin{
Config: tt.fields.Config,
Writer: tt.fields.Writer,
}
gotHost, gotPort := p.hostPort(tt.args.h)
if gotHost != tt.wantHost {
t.Errorf("Plugin.hostPort() gotHost = %v, want %v", gotHost, tt.wantHost)
}
if gotPort != tt.wantPort {
t.Errorf("Plugin.hostPort() gotPort = %v, want %v", gotPort, tt.wantPort)
}
})
}
}
+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
+50
View File
@@ -0,0 +1,50 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABAZka7A7i
FscMeJBPyPteclAAAAEAAAAAEAAAIXAAAAB3NzaC1yc2EAAAADAQABAAACAQDz6aZ1jY2o
nnuj2YNHJ/HhfvIu0B973v/+pFFOavnTUOhEEKEy3TASu+s9CkHrYZAtRc+QYIkNZI31mh
HBhotdeP/7GoO2UirkFtrzyQKPNJxEcv0RBoG9ssN8jex0PyK6DHIYYFnIWadVBEEOh/H+
rK7j7u2/big3oTzYBuFrCwmYFcz5na99MzFeAUhazF44gVBma+zO+1quGeqF51UDIg1SMG
vX8I7LNEqrKEBaIUQJKFQcxlOWlRLQsjJCymrOujsXsRrXHAQWcnxDcNevv2ZMOUl0ybvv
9yH0BiGbRBd1Hy8/QPILbAQaqu0oQE7fubN8Q8lqb3Jg0loID4x/5GPhSY8WAXpuLcXTOr
b93SnCw1JsAgJDNqpuuRFy3BSZ7wBOr1jfeIoo7xk14OHiUjJ0uXDL9cLMkcw6ElWz81mr
D2VCkXUz+qFyjJ+G7aGWRtctZoOzKln4yfNfUmwW8/8ra3QnmrMZ2xW2Ylw3ZhO+tLi7jI
NHYFb54bAdLVPUU1ctIuJns2qkWnjJCxxMiynIqCif20/OU1n8CTJuOWiURmRdmvKOH4PE
3JxC2Qnk/3tV3Cf8hp1CH5VjBZ9AjGj5MDMHXyu34VY2WvYo5QyzfS3ySPoT8kCO0G0xpv
jwCMHOK+G2RP4kqb/KKZguiKdgintBXuskTlJmD7kcMQAAB1CnEMQGwAKZbd3F1DJqwfPf
KWjoUJKbTRiav6h5pQr65JaqDe/7YE2ZHYo5917AC2vPLwPxAnoHFMsbObd5mWcmpATg/0
K/qkN5Z4Ml5U3bwr51wfSPh1MiAP21Aickt09BDstIJzNNwwgcY31O3k/d6VBjqyM6Ezop
66LI4s/IIni1BI+cALyEfzE4Qu16GfzIeM+JVxildP4VImhvNBESmmbBL8rNmSzlQ+FTuF
JVmowUbcon1O0CppM1MRVPeG805XDwjxHXKwOp5O7MdTz7H8JeORoe8D6+4rNfJE0eQGY7
Nm4+Wa97HzAFbT9IS433rxoGx9Qps3LAySFONso2JWSOEfo8rxnqO04DrfVHQhY3DkkwQt
FsDnMtkthJa+ZzUYc75fnS0DBPGuF9DZUCqrev5oAUHP6C4Vc4b33JJQD4FZJ+ehk3Xsci
cwJQsmgLyc5Jdh543Dm7kZoM9ku7HDNrB4H/1p45Vo6aBZMAY50x+fTdBeTgCzzhzzTbf+
0IF8W3yW3/BYD+S2Byo3JKp6NH0Q8cgPJrGTl6GltGfpVuc6kLjMZ5zvxRbyWaqtIygM46
W1izbA+9jwbHhitCtOk42e/ff6iEB1MVC13LqPty3gPNR8Pv0rDUDjJS4KiVwXqUY+bMr0
C8l/hx93euHjLUJ49Ru6uy/2fBlHZEj6GmEAJhu/i6t2c1Rq0HBLis9X356oQT+YZnIai2
ym0MknPxjeYBAItOV3zhRd1cYnk7CDcl1XALcnh0tqP712x24IJ+Ytqg7nvB2NZV8T469I
8Fp254Nr89HOMAXaZD0UcIPm7D2rfWV+YJFI3ZcJ/8DM99H3tpXe2j4oHMdmAbBd++09sx
KBRdFLcvnBfd1lqwxpA7hbxzrxi/yehYCqzh5KQGaf2UXej6TPiVzBWVYbp34cMZtsT6mF
K8SS3l5TXoNK2DNEk30o8K3q+vngQpfC9GZ/id4B7LS/3ybellxemZHXQoU4PxDkLKt7jd
AAsd5WO13dv3n/qgyu8iBRiFU+W66NX0RJGkp+lZMnta0YzukafM2n6GDn/r/Cx/y21PAi
ah8i41ByI1QLI4m1r+bRHdUxAarS/XJw4tTSFiZu3zddMYrlzeG9O3VUX9zBvBtfQbSmeJ
omml0zlr/qD7TMsORiujy7XIn7sMW+Ls/NA8TvX8oRnACjXe/MYNEZ8WDu2rkZuY/Dfc+o
NyYWO7kZ3kcejQZ1NusJSA7MG0FFGYSIaC9T9CWqYd5IcRSJW4dZnCt9z8CIJ6TSUFqMb/
H1Y5Rmi0IIX+8qbGGXVBDIBk5y9xtS43+nz1nsdXwDmkTiXN9+ZX+GDsLxCWoHGryrWDbk
EuOAlqpvxFKzEkNsx+AC5wae6i/hBeiEce9bm4nZp+hFv1ic1Z9WS8B37YOFgJ4utGeOjB
6hnywUUJ3aH0LnCQNB3UzeFR7BmEaxmYD/phJodmjA5SD3CWpeizdXfrUjtqXGhYlr2jzq
vBAeeYEO4uaHIGxg8GqoqtaseqVcIdtouHxrVAxxXkjShV2ji7oJ/AtrLZNlkKYxMk0TpX
fFiKqL/uKfS78FfvVOhOkHZTD6ZeMgmdL/uOghEAtrf08ChyRvdp7QLjA802aio9eUVIQm
lHb1ltPEbIZNuvQ5kTIwk2eM6EAkOh0MBMoAYOxOpIb00XHNRDGJYuLewByjMQa8EoT6VM
NoiFIzJU9lLAXE6yz6JswctpTpLHK9Aq5vY7ObaOvrmpCQqsXfOuVUo2nR/FyEes97zuXG
E4aKaHK4IAW4UY/oGYk7pU/yRpudhiNRMXzmcQXfVmBEHuvDrh2chg8lDYn++07F7RWqkI
nfMAOWR8UEl4xp4zJtThDjRxNW6QLl8E1ADjndA9wVaKNSzv2i1TLXKBr5luFqY9MSJ2rm
yBR5EwairH/Qn9TUxaDD+0p6J+E9iz1l8UPTJa/cjtwiySljahY/6tHHnr9YQVnox92yfU
UXpfINGjYrpqh6EFwmyRw9fryIMvMhgZYo6ZoCRBCK2GfGAB0VTzJy2FGs4GecZK5ptXKu
sOX8BgGX/Q/nAJ7PWf9hgYlX2YyjmLjQZDMWECp05VFx9znEETNKlwF1FX5/E/37ISyz4d
I1LVSKOEccJX7jCR32LzvRW1UBX47Z+q3LVE4sa0QAV/JoISq6Qn6zAsVIV0yEPmVbd/xx
aX2uBUGHhmd99YJDh81xJIoYEMRzoGVfp0JjfYcDUc+2I6JdrOMF9/KmMA5wsZl4OKiu/F
cTRGjUkgw/cF2EFRGWknee2esYRB7tOr4y56qZ4gxqw8q9rYXhyB42jbdTvt5xcCm/ynid
sn4InokRRoIiMIPL5Ur7FZQHOP+915MWUBsrTJtkCWQuqJheYUi3mCzh/7NadAKplRpaKb
rS/DJIOOkjnGni/sDxJzPq7STDBVy4WStwQl6NI5hq+/c+JvN9GI4Vu/kz0z8qUcdShLaH
l4njcaMpg4tpQMHtCBOicGyV0=
-----END OPENSSH PRIVATE KEY-----
+1
View File
@@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDz6aZ1jY2onnuj2YNHJ/HhfvIu0B973v/+pFFOavnTUOhEEKEy3TASu+s9CkHrYZAtRc+QYIkNZI31mhHBhotdeP/7GoO2UirkFtrzyQKPNJxEcv0RBoG9ssN8jex0PyK6DHIYYFnIWadVBEEOh/H+rK7j7u2/big3oTzYBuFrCwmYFcz5na99MzFeAUhazF44gVBma+zO+1quGeqF51UDIg1SMGvX8I7LNEqrKEBaIUQJKFQcxlOWlRLQsjJCymrOujsXsRrXHAQWcnxDcNevv2ZMOUl0ybvv9yH0BiGbRBd1Hy8/QPILbAQaqu0oQE7fubN8Q8lqb3Jg0loID4x/5GPhSY8WAXpuLcXTOrb93SnCw1JsAgJDNqpuuRFy3BSZ7wBOr1jfeIoo7xk14OHiUjJ0uXDL9cLMkcw6ElWz81mrD2VCkXUz+qFyjJ+G7aGWRtctZoOzKln4yfNfUmwW8/8ra3QnmrMZ2xW2Ylw3ZhO+tLi7jINHYFb54bAdLVPUU1ctIuJns2qkWnjJCxxMiynIqCif20/OU1n8CTJuOWiURmRdmvKOH4PE3JxC2Qnk/3tV3Cf8hp1CH5VjBZ9AjGj5MDMHXyu34VY2WvYo5QyzfS3ySPoT8kCO0G0xpvjwCMHOK+G2RP4kqb/KKZguiKdgintBXuskTlJmD7kcMQ== deploy@easyssh
+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 "$@"