lita-capistrano 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 90855c98bec9889e6de9f42443c9351a427298cf
4
- data.tar.gz: 5ebf58de07d8aa52d4d6c5a28053a288b9700f17
3
+ metadata.gz: ea6a52089cbc1f81dbd9af144053a8beaaa2cf11
4
+ data.tar.gz: 95e3b661916ae02f93542874fd8a89411d699099
5
5
  SHA512:
6
- metadata.gz: 83481fc4506bc698e5f258005f6a27a62e0cf053ba88156ccabcfe8f5e320040509455388e306d86ee3acac2f9d724acff06d41c5e0bcdd5346a56430fa3417c
7
- data.tar.gz: 63aa9564e6e6fdcb632bff8c26df562bda80b6909346685ac6d55e0710c88b4797627d3e0a856097b92304f69450a6da61fb00d24ee2e77744e15943450dd9f8
6
+ metadata.gz: 5485817a04268dfc8657585a61a7a1ba14f27c705765c68ab3e5e9dd1986b51f515d7e2529cdb31301787623ef3e9c842a6d12dee46cf922d83313b561d734a9
7
+ data.tar.gz: df0b4b1c15a12347ad8e48ac79b66bfbb8013d063ffb7ed89b6221b3660ed56831f03519101a1aac670c3951ef4d6e2dda1b5aab6a2fdea29ff7dcd1a29dd0fe
data/.gitignore CHANGED
@@ -1,17 +1,17 @@
1
- *.gem
2
- *.rbc
3
- .bundle
4
- .config
5
- .yardoc
6
- Gemfile.lock
7
- InstalledFiles
8
- _yardoc
9
- coverage
10
- doc/
11
- lib/bundler/man
12
- pkg
13
- rdoc
14
- spec/reports
15
- test/tmp
16
- test/version_tmp
17
- tmp
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
- source "https://rubygems.org"
2
-
3
- gemspec
1
+ source "https://rubygems.org"
2
+ gem "net-ssh"
3
+ gem 'slack-api'
4
+
5
+ gemspec
data/README.md CHANGED
@@ -1,108 +1,127 @@
1
- # lita-capistrano
2
-
3
- [![Gem Version](https://badge.fury.io/rb/lita-capistrano.png)](http://badge.fury.io/rb/lita-capistrano)
4
-
5
- **lita-capistrano** is a hanlder for [Lita](https://www.lita.io/) that allows you to use make deploys through your robot.
6
-
7
- ## Requirements
8
-
9
- In order to **lita-capistrano** to identify a good deploy from a failed one, you capistrano script should always end with a message.
10
-
11
- ## Installation
12
-
13
- Add lita-capistrano to your Lita instance's Gemfile:
14
-
15
- ``` ruby
16
- gem "lita-capistrano"
17
- ```
18
-
19
- ## Configuration
20
-
21
- ### Required attributes
22
-
23
- * `server` (String) – The deploy server host.
24
-
25
- * `server_user` (String) – The deploy server host ssh user.
26
-
27
- * `server_password` (String) – The deploy server host ssh password.
28
-
29
- * `deploy_tree` (String) – A json configuration of how deploys work.
30
-
31
- ### Example
32
-
33
- ``` ruby
34
- Lita.configure do |config|
35
- config.handlers.capistrano.server = "capistrano-deploy.com"
36
- config.handlers.capistrano.server_user = "lita"
37
- config.handlers.capistrano.server_password = "secret"
38
- end
39
-
40
- config.handlers.capistrano.deploy_tree = {
41
- first_app: {
42
- qa: {
43
- dir: "/capistrano/first_app/qa",
44
- auth_group: "first_app_qa", # auth_group required to be able to deploy
45
- channel: "first_app_channel", # not required, if configured limits deploys to this channel
46
- envs: [
47
- "qa1",
48
- "qa2"
49
- ]
50
- },
51
- staging: {
52
- dir: "/capistrano/fist_app/staging",
53
- auth_group: "first_app_staging",
54
- envs: [
55
- "stagin1",
56
- "staging2",
57
- "staging3"
58
- ]
59
- }
60
- },
61
- second_app: {
62
- prod: {
63
- dir: "/capistrano/second_app/production",
64
- auth_group: "second_app_staging",
65
- channel: "second_app_prod_channel",
66
- envs: [
67
- "dc1",
68
- "dc2"
69
- ]
70
- }
71
- }
72
- ```
73
-
74
- ## Usage
75
-
76
- List available apps for deploy:
77
-
78
- ```
79
- Lita: deploy list
80
- ```
81
-
82
- List available app areas for deploy:
83
-
84
- ```
85
- Lita: deploy list [APP]
86
- ```
87
-
88
- List required auth groups to deploy:
89
-
90
- ```
91
- Lita: deploy auth list [APP]
92
- ```
93
-
94
- Deploy a tag or branch:
95
-
96
- ```
97
- Lita: deploy [APP] [AREA] [ENV] [TAG]
98
- ```
99
-
100
- Rollback last tag or branch:
101
-
102
- ```
103
- Lita: deploy [APP] [AREA] [ENV] rollback
104
- ```
105
-
106
- ## License
107
-
108
- [MIT](http://opensource.org/licenses/MIT)
1
+ # lita-capistrano
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/lita-capistrano.png)](http://badge.fury.io/rb/lita-capistrano)
4
+
5
+ **lita-capistrano** is a handler for [Lita](https://www.lita.io/) that allows you to make deploys through your robot.
6
+
7
+ ## Features
8
+
9
+ * Deploy and rollback.
10
+
11
+ * Permission groups and allowed rooms for deploy based on apps.
12
+
13
+ * Set reminders to deploy to environments after a sucessful deploy.
14
+
15
+ ## Requirements
16
+
17
+ In order to **lita-capistrano** to identify a good deploy from a failed one, your capistrano script should always end with a message.
18
+
19
+ ## Installation
20
+
21
+ Add lita-capistrano to your Lita instance's Gemfile:
22
+
23
+ ``` ruby
24
+ gem "lita-capistrano"
25
+ ```
26
+
27
+ ## Configuration
28
+
29
+ ### Required attributes
30
+
31
+ * `server` (String) – The deploy server host.
32
+
33
+ * `server_user` (String) – The deploy server host ssh user.
34
+
35
+ * `server_password` (String) – The deploy server host ssh password.
36
+
37
+ * `deploy_tree` (String) – A json configuration of how deploys work.
38
+
39
+ ### Optional attributes
40
+
41
+ * `slack_api_token` (String) – The slack api token. Only necessary if using Slack reminders
42
+
43
+ ### Example
44
+
45
+ ``` ruby
46
+ Lita.configure do |config|
47
+ config.handlers.capistrano.server = "capistrano-deploy.com"
48
+ config.handlers.capistrano.server_user = "lita"
49
+ config.handlers.capistrano.server_password = "secret"
50
+ config.handlers.capistrano.slack_api_token = "super-long-token-for-slack-api" # not required, if not using Slack reminders
51
+
52
+ end
53
+
54
+ config.handlers.capistrano.deploy_tree = {
55
+ first_app: {
56
+ qa: {
57
+ dir: "/capistrano/first_app/qa",
58
+ auth_group: "first_app_qa", # auth_group required to be able to deploy
59
+ channel: "first_app_channel", # not required, if configured limits deploys to this channel
60
+ envs: [
61
+ "qa1",
62
+ "qa2"
63
+ ]
64
+ },
65
+ staging: {
66
+ dir: "/capistrano/fist_app/staging",
67
+ auth_group: "first_app_staging",
68
+ envs: [
69
+ "stagin1",
70
+ "staging2",
71
+ "staging3"
72
+ ]
73
+ }
74
+ },
75
+ second_app: {
76
+ prod: {
77
+ dir: "/capistrano/second_app/production",
78
+ auth_group: "second_app_staging",
79
+ channel: "second_app_prod_channel",
80
+ envs: [
81
+ "dc1",
82
+ "dc2"
83
+ ],
84
+ reminders: {
85
+ dc1: {
86
+ dc2: "2 hours" # Set a reminder to deploy to dc2 after 2 hours from the dc1 deploy success.
87
+ }
88
+ }
89
+ }
90
+ }
91
+ ```
92
+
93
+ ## Usage
94
+
95
+ List available apps for deploy:
96
+
97
+ ```
98
+ Lita: deploy list
99
+ ```
100
+
101
+ List available app areas for deploy:
102
+
103
+ ```
104
+ Lita: deploy list [APP]
105
+ ```
106
+
107
+ List required auth groups to deploy:
108
+
109
+ ```
110
+ Lita: deploy auth list [APP]
111
+ ```
112
+
113
+ Deploy a tag or branch:
114
+
115
+ ```
116
+ Lita: deploy [APP] [AREA] [ENV] [TAG]
117
+ ```
118
+
119
+ Rollback last tag or branch:
120
+
121
+ ```
122
+ Lita: deploy [APP] [AREA] [ENV] rollback
123
+ ```
124
+
125
+ ## License
126
+
127
+ [MIT](http://opensource.org/licenses/MIT)
data/Rakefile CHANGED
@@ -1,6 +1,6 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
-
4
- RSpec::Core::RakeTask.new(:spec)
5
-
6
- task default: :spec
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -1,12 +1,12 @@
1
- require "lita"
2
-
3
- Lita.load_locales Dir[File.expand_path(
4
- File.join("..", "..", "locales", "*.yml"), __FILE__
5
- )]
6
-
7
- require "lita/handlers/capistrano"
8
-
9
- Lita::Handlers::Capistrano.template_root File.expand_path(
10
- File.join("..", "..", "templates"),
11
- __FILE__
12
- )
1
+ require "lita"
2
+
3
+ Lita.load_locales Dir[File.expand_path(
4
+ File.join("..", "..", "locales", "*.yml"), __FILE__
5
+ )]
6
+
7
+ require "lita/handlers/capistrano"
8
+
9
+ Lita::Handlers::Capistrano.template_root File.expand_path(
10
+ File.join("..", "..", "templates"),
11
+ __FILE__
12
+ )
@@ -1,324 +1,363 @@
1
- require 'net/ssh'
2
-
3
- module Lita
4
- module Handlers
5
- class Capistrano < Handler
6
-
7
- config :server, type: String, required: true
8
- config :server_user, type: String, required: true
9
- config :server_password, type: String, required: true
10
- config :deploy_tree, type: Hash, required: true
11
-
12
- on :loaded, :define_routes
13
-
14
- on :deploy_checked, :deploy_exec
15
- on :deploy_aborted, :deploy_abort
16
-
17
- def define_routes(payload)
18
- define_static_routes
19
- define_dinamic_routes(config.deploy_tree)
20
- end
21
-
22
- def deploy_list(response)
23
- requested_app = response.args[1]
24
- if requested_app.nil?
25
- apps = config.deploy_tree.keys.join("\n")
26
- response.reply_privately("Available apps:\n#{apps}")
27
- else
28
- app_tree = get_app_tree(config.deploy_tree[requested_app.to_sym])
29
- response.reply_privately("Available tree for #{requested_app}: \n #{app_tree}")
30
- end
31
- end
32
-
33
- def deploy_auth_list(response)
34
- requested_app = response.args[2]
35
- if requested_app.nil?
36
- apps_auth_tree = get_apps_auth_groups(config.deploy_tree)
37
- response.reply_privately("Auth groups for apps:\n#{apps_auth_tree}")
38
- else
39
- app_tree = get_app_auth_group(config.deploy_tree[requested_app.to_sym])
40
- response.reply_privately("Auth group needed to deploy #{requested_app}: \n #{app_tree}")
41
- end
42
- end
43
-
44
- def deploy_request(response)
45
- app = response.matches[0][0]
46
- area = response.matches[0][1]
47
- env = response.matches[0][2]
48
- tag = response.matches[0][3]
49
- allowed_channel = config.deploy_tree[app.to_sym][area.to_sym][:channel]
50
- room_id = response.message.source.room_object.id
51
-
52
- # Do not deploy if not in the right channel, only if channel is
53
- # set in config.
54
- if !allowed_channel.nil? && !allowed_room?(room_id, allowed_channel)
55
- return response.reply("Deploy da app #{app} #{area} permitido somente no canal ##{allowed_channel}")
56
- end
57
-
58
- unless area_exists?(app, area)
59
- return response.reply("A área informada é inválida.")
60
- end
61
- unless env_exists?(app, area, env)
62
- return response.reply("O ambiente informado é inválido.")
63
- end
64
-
65
- # Pre deploy check, if no deploy in progress, deploy-tracker will
66
- # trigger :deploy_checked to continue the deploy.
67
- deploy_in_progress?(app, area, env, tag, response)
68
- end
69
-
70
- def area_exists?(app, area)
71
- config.deploy_tree[app.to_sym].include?(area.to_sym)
72
- end
73
-
74
- def env_exists?(app, area, env)
75
- config.deploy_tree[app.to_sym][area.to_sym][:envs].include?(env)
76
- end
77
-
78
- def get_app_tree(config_tree)
79
- app_tree = {}
80
- config_tree.each do |key, value|
81
- app_tree.store(key.to_s, value[:envs].map { |e| ">#{e}\n" }.join)
82
- end
83
- app_tree.flatten.map { |e| "#{e}\n" }.join
84
- end
85
-
86
- def get_apps_auth_groups(config_tree)
87
- app_tree = {}
88
- config_tree.each do |key, value|
89
- app_tree.store(key.to_s, value.map { |e| ">#{e[0]}: #{e[1][:auth_group]}\n" }.join)
90
- end
91
- app_tree.flatten.map { |e| "#{e}\n" }.join
92
- end
93
-
94
- def get_app_auth_group(config_tree)
95
- app_tree = []
96
- config_tree.each do |key, value|
97
- app_tree << "#{key.to_s}: #{value[:auth_group]}"
98
- end
99
- app_tree.flatten.map { |e| "#{e}\n" }.join
100
- end
101
-
102
- # If a deploy is in progress the deploy_tracker handler will return a
103
- # reponse to chat and will interrupt further using the interrupt_deploy
104
- # method
105
- def deploy_in_progress?(app, area, env, tag, response)
106
- robot.trigger(:deploy_in_progress?, app: app, area: area, env: env, tag: tag, response: response)
107
- end
108
-
109
- def deploy_abort(payload)
110
- return payload[:response].reply(payload[:msg])
111
- end
112
-
113
- def deploy_exec(payload)
114
- app = payload[:app]
115
- area = payload[:area]
116
- env = payload[:env]
117
- tag = payload[:tag]
118
- response = payload[:response]
119
- dir = config.deploy_tree[app.to_sym][area.to_sym][:dir]
120
- responsible_user = response.user.mention_name
121
- target = response.message.source.room_object
122
-
123
- # Default initial message
124
- initial_message = "#{responsible_user}: Deploy da tag #{tag} iniciado no ambiente #{env}."
125
-
126
- if (tag == "rollback")
127
- # Change the initial message in case of rollback
128
- initial_message = "#{responsible_user}: Rollback iniciado no ambiente #{env}."
129
- end
130
-
131
- # Deploy/Rollback start
132
- response.reply(initial_message)
133
- start_time = Time.now
134
- robot.trigger(:deploy_started,
135
- app: app,
136
- area: area,
137
- env: env,
138
- tag: tag,
139
- responsible: responsible_user,
140
- start_time: start_time)
141
-
142
- # Deploy/Rollback execution
143
- output = ""
144
- if (tag == "rollback")
145
- output = rollback(dir, env)
146
- else
147
- output = deploy(dir, env, tag)
148
- end
149
-
150
- # After deploy stopped
151
- finish_time =Time.now
152
-
153
- msg_components = {}
154
-
155
- # Send back a message indicating the deploy status
156
- if !output[:error]
157
- robot.trigger(:deploy_finished,
158
- app: app,
159
- area: area,
160
- env: env,
161
- tag: tag,
162
- responsible: responsible_user,
163
- start_time: start_time,
164
- finish_time: finish_time,
165
- status: 'success')
166
- msg_components = {title: "Finalizado com sucesso!",
167
- color: "good",
168
- text: ""}
169
- elsif output[:data].lines.last.include? "status code 32768"
170
- robot.trigger(:deploy_finished,
171
- app: app,
172
- area: area,
173
- env: env,
174
- tag: tag,
175
- responsible: responsible_user,
176
- start_time: start_time,
177
- finish_time: finish_time,
178
- status: 'invalid tag')
179
- msg_components = {title: "A tag informada não existe.",
180
- color: "warning",
181
- text: ""}
182
- else
183
- robot.trigger(:deploy_finished,
184
- app: app,
185
- area: area,
186
- env: env,
187
- tag: tag,
188
- responsible: responsible_user,
189
- start_time: start_time,
190
- finish_time: finish_time,
191
- status: 'error')
192
- msg_components = {title: "Error!",
193
- color: "danger",
194
- text: output[:data]}
195
- end
196
-
197
- # generate the Attachment for slack
198
- attachment = gen_deploy_msg(msg_components[:title],
199
- msg_components[:color],
200
- msg_components[:text],
201
- responsible_user,
202
- app,
203
- area,
204
- env,
205
- tag)
206
-
207
- # Default message for other adapters
208
- message = "Deploy - #{msg_components[:title]}. #{msg_components[:text]}"
209
- if (tag == "rollback")
210
- message = "Rollback - #{msg_components[:title]}. #{msg_components[:text]}"
211
- end
212
-
213
- case robot.config.robot.adapter
214
- when :slack
215
- return robot.chat_service.send_attachments(target, attachment)
216
- else
217
- robot.send_message(target, message)
218
- end
219
- end
220
-
221
- private
222
-
223
- def define_static_routes
224
- self.class.route(
225
- %r{^deploy\s+list},
226
- :deploy_list,
227
- command: false,
228
- help: { "deploy list [APP] " => "List available apps for deploy"}
229
- )
230
- self.class.route(
231
- %r{^deploy\s+auth\s+list},
232
- :deploy_auth_list,
233
- command: false,
234
- help: { "deploy auth list [APP] " => "List required auth groups to deploy"}
235
- )
236
- end
237
-
238
- def define_dinamic_routes(deploy_tree)
239
- deploy_tree.each do |app, areas|
240
- areas.each do |area, value|
241
- self.class.route(
242
- %r{^deploy\s+(#{app})\s+(#{area})\s+(.+)\s+(.+)},
243
- :deploy_request,
244
- command: true,
245
- restrict_to: [:admins, value[:auth_group].to_sym],
246
- help: { "deploy #{app} #{area} ENV TAG " => "Executa deploy da app #{app} na area #{area}"}
247
- )
248
-
1
+ require 'net/ssh'
2
+ require 'slack'
3
+
4
+ module Lita
5
+ module Handlers
6
+ class Capistrano < Handler
7
+
8
+ config :server, type: String, required: true
9
+ config :server_user, type: String, required: true
10
+ config :server_password, type: String, required: true
11
+ config :deploy_tree, type: Hash, required: true
12
+ config :slack_api_token, type: String, required: false
13
+
14
+ on :loaded, :define_routes
15
+
16
+ on :deploy_checked, :deploy_exec
17
+ on :deploy_aborted, :deploy_abort
18
+ on :deploy_finished, :remind_next_deploy
19
+
20
+ def define_routes(payload)
21
+ define_static_routes
22
+ define_dinamic_routes(config.deploy_tree)
23
+ end
24
+
25
+ def deploy_list(response)
26
+ requested_app = response.args[1]
27
+ if requested_app.nil?
28
+ apps = config.deploy_tree.keys.join("\n")
29
+ response.reply_privately("Available apps:\n#{apps}")
30
+ else
31
+ app_tree = get_app_tree(config.deploy_tree[requested_app.to_sym])
32
+ response.reply_privately("Available tree for #{requested_app}: \n #{app_tree}")
33
+ end
34
+ end
35
+
36
+ def deploy_auth_list(response)
37
+ requested_app = response.args[2]
38
+ if requested_app.nil?
39
+ apps_auth_tree = get_apps_auth_groups(config.deploy_tree)
40
+ response.reply_privately("Auth groups for apps:\n#{apps_auth_tree}")
41
+ else
42
+ app_tree = get_app_auth_group(config.deploy_tree[requested_app.to_sym])
43
+ response.reply_privately("Auth group needed to deploy #{requested_app}: \n #{app_tree}")
44
+ end
45
+ end
46
+
47
+ def deploy_request(response)
48
+ app = response.matches[0][0]
49
+ area = response.matches[0][1]
50
+ env = response.matches[0][2]
51
+ tag = response.matches[0][3]
52
+ allowed_channel = config.deploy_tree[app.to_sym][area.to_sym][:channel]
53
+ room_id = response.message.source.room_object.id
54
+
55
+ # Do not deploy if not in the right channel, only if channel is
56
+ # set in config.
57
+ if !allowed_channel.nil? && !allowed_room?(room_id, allowed_channel)
58
+ return response.reply("Deploy da app #{app} #{area} permitido somente no canal ##{allowed_channel}")
59
+ end
60
+
61
+ unless area_exists?(app, area)
62
+ return response.reply("A área informada é inválida.")
63
+ end
64
+ unless env_exists?(app, area, env)
65
+ return response.reply("O ambiente informado é inválido.")
66
+ end
67
+
68
+ # Pre deploy check, if no deploy in progress, deploy-tracker will
69
+ # trigger :deploy_checked to continue the deploy.
70
+ deploy_in_progress?(app, area, env, tag, response)
71
+ end
72
+
73
+ def area_exists?(app, area)
74
+ config.deploy_tree[app.to_sym].include?(area.to_sym)
75
+ end
76
+
77
+ def env_exists?(app, area, env)
78
+ config.deploy_tree[app.to_sym][area.to_sym][:envs].include?(env)
79
+ end
80
+
81
+ def get_app_tree(config_tree)
82
+ app_tree = {}
83
+ config_tree.each do |key, value|
84
+ app_tree.store(key.to_s, value[:envs].map { |e| ">#{e}\n" }.join)
85
+ end
86
+ app_tree.flatten.map { |e| "#{e}\n" }.join
87
+ end
88
+
89
+ def get_apps_auth_groups(config_tree)
90
+ app_tree = {}
91
+ config_tree.each do |key, value|
92
+ app_tree.store(key.to_s, value.map { |e| ">#{e[0]}: #{e[1][:auth_group]}\n" }.join)
93
+ end
94
+ app_tree.flatten.map { |e| "#{e}\n" }.join
95
+ end
96
+
97
+ def get_app_auth_group(config_tree)
98
+ app_tree = []
99
+ config_tree.each do |key, value|
100
+ app_tree << "#{key.to_s}: #{value[:auth_group]}"
101
+ end
102
+ app_tree.flatten.map { |e| "#{e}\n" }.join
103
+ end
104
+
105
+ # If a deploy is in progress the deploy_tracker handler will return a
106
+ # reponse to chat and will interrupt further using the interrupt_deploy
107
+ # method
108
+ def deploy_in_progress?(app, area, env, tag, response)
109
+ robot.trigger(:deploy_in_progress?, app: app, area: area, env: env, tag: tag, response: response)
110
+ end
111
+
112
+ def deploy_abort(payload)
113
+ return payload[:response].reply(payload[:msg])
114
+ end
115
+
116
+ def deploy_exec(payload)
117
+ app = payload[:app]
118
+ area = payload[:area]
119
+ env = payload[:env]
120
+ tag = payload[:tag]
121
+ response = payload[:response]
122
+ dir = config.deploy_tree[app.to_sym][area.to_sym][:dir]
123
+ responsible_user = response.user.mention_name
124
+ target = response.message.source.room_object
125
+
126
+ # Default initial message
127
+ initial_message = "#{responsible_user}: Deploy da tag #{tag} iniciado no ambiente #{env}."
128
+
129
+ if (tag == "rollback")
130
+ # Change the initial message in case of rollback
131
+ initial_message = "#{responsible_user}: Rollback iniciado no ambiente #{env}."
132
+ end
133
+
134
+ # Deploy/Rollback start
135
+ response.reply(initial_message)
136
+ start_time = Time.now
137
+ robot.trigger(:deploy_started,
138
+ app: app,
139
+ area: area,
140
+ env: env,
141
+ tag: tag,
142
+ responsible: responsible_user,
143
+ start_time: start_time)
144
+
145
+ # Deploy/Rollback execution
146
+ output = ""
147
+ if (tag == "rollback")
148
+ output = rollback(dir, env)
149
+ else
150
+ output = deploy(dir, env, tag)
151
+ end
152
+
153
+ # After deploy stopped
154
+ finish_time =Time.now
155
+
156
+ msg_components = {}
157
+
158
+ # Send back a message indicating the deploy status
159
+ if !output[:error]
160
+ robot.trigger(:deploy_finished,
161
+ app: app,
162
+ area: area,
163
+ env: env,
164
+ tag: tag,
165
+ responsible: responsible_user,
166
+ start_time: start_time,
167
+ finish_time: finish_time,
168
+ status: 'success')
169
+ msg_components = {title: "Finalizado com sucesso!",
170
+ color: "good",
171
+ text: ""}
172
+ elsif output[:data].lines.last.include? "status code 32768"
173
+ robot.trigger(:deploy_finished,
174
+ app: app,
175
+ area: area,
176
+ env: env,
177
+ tag: tag,
178
+ responsible: responsible_user,
179
+ start_time: start_time,
180
+ finish_time: finish_time,
181
+ status: 'invalid tag')
182
+ msg_components = {title: "A tag informada não existe.",
183
+ color: "warning",
184
+ text: ""}
185
+ else
186
+ robot.trigger(:deploy_finished,
187
+ app: app,
188
+ area: area,
189
+ env: env,
190
+ tag: tag,
191
+ responsible: responsible_user,
192
+ start_time: start_time,
193
+ finish_time: finish_time,
194
+ status: 'error')
195
+ msg_components = {title: "Error!",
196
+ color: "danger",
197
+ text: output[:data]}
198
+ end
199
+
200
+ # generate the Attachment for slack
201
+ attachment = gen_deploy_msg(msg_components[:title],
202
+ msg_components[:color],
203
+ msg_components[:text],
204
+ responsible_user,
205
+ app,
206
+ area,
207
+ env,
208
+ tag)
209
+
210
+ # Default message for other adapters
211
+ message = "Deploy - #{msg_components[:title]}. #{msg_components[:text]}"
212
+ if (tag == "rollback")
213
+ message = "Rollback - #{msg_components[:title]}. #{msg_components[:text]}"
214
+ end
215
+
216
+ case robot.config.robot.adapter
217
+ when :slack
218
+ return robot.chat_service.send_attachments(target, attachment)
219
+ else
220
+ robot.send_message(target, message)
221
+ end
222
+ end
223
+
224
+ def remind_next_deploy(payload)
225
+ deploy_status = payload[:status]
226
+ if slack_api_configured? && deploy_status == "success"
227
+ app = payload[:app]
228
+ area = payload[:area]
229
+ env = payload[:env]
230
+ tag = payload[:tag]
231
+ responsible = payload[:responsible]
232
+ reminders = get_reminders(app, area, env)
233
+ if reminders
234
+ reminders.each do |env, time|
235
+ set_deploy_reminder(responsible, app, area, env, tag, time)
236
+ end
249
237
  end
250
238
  end
251
239
  end
252
240
 
253
- def deploy(dir, env, tag)
254
- output = ssh_exec("cd #{dir}; cap #{env} deploy tag=#{tag}")
255
- end
256
-
257
- def rollback(dir, env)
258
- output = ssh_exec("cd #{dir}; cap #{env} deploy:rollback")
259
- end
260
-
261
- def ssh_exec(cmd)
262
- Net::SSH.start(config.server, config.server_user, password: config.server_password) do |ssh|
263
- exec_ssh(ssh, cmd)
264
- end
265
- end
266
-
267
- def exec_ssh(ssh, cmd)
268
- ssh.exec! cmd do |ch, stream, data|
269
- @output = get_deploy_output(stream, data)
270
- end
271
- @output
272
- end
273
-
274
- def get_deploy_output(stream, data)
275
- if stream == :stderr
276
- { data: "#{data}", error: true }
277
- else
278
- { data: "#{data}", error: false }
279
- end
280
- end
281
-
282
-
283
- def gen_deploy_msg (title, color, body, user, app, area, env, tag)
284
- msg = Adapters::Slack::Attachment.new(
285
- body,
286
- title: "Deploy - #{title}",
287
- color: "#{color}",
288
- pretext: "@#{user}:",
289
- fields: [
290
- {
291
- title: "App",
292
- value: app,
293
- short: true
241
+ private
242
+
243
+ def define_static_routes
244
+ self.class.route(
245
+ %r{^deploy\s+list},
246
+ :deploy_list,
247
+ command: false,
248
+ help: { "deploy list [APP] " => "List available apps for deploy"}
249
+ )
250
+ self.class.route(
251
+ %r{^deploy\s+auth\s+list},
252
+ :deploy_auth_list,
253
+ command: false,
254
+ help: { "deploy auth list [APP] " => "List required auth groups to deploy"}
255
+ )
256
+ end
257
+
258
+ def define_dinamic_routes(deploy_tree)
259
+ deploy_tree.each do |app, areas|
260
+ areas.each do |area, value|
261
+ self.class.route(
262
+ %r{^deploy\s+(#{app})\s+(#{area})\s+(.+)\s+(.+)},
263
+ :deploy_request,
264
+ command: true,
265
+ restrict_to: [:admins, value[:auth_group].to_sym],
266
+ help: { "deploy #{app} #{area} ENV TAG " => "Executa deploy da app #{app} na area #{area}"}
267
+ )
268
+
269
+ end
270
+ end
271
+ end
272
+
273
+ def deploy(dir, env, tag)
274
+ output = ssh_exec("cd #{dir}; cap #{env} deploy tag=#{tag}")
275
+ end
276
+
277
+ def rollback(dir, env)
278
+ output = ssh_exec("cd #{dir}; cap #{env} deploy:rollback")
279
+ end
280
+
281
+ def ssh_exec(cmd)
282
+ Net::SSH.start(config.server, config.server_user, password: config.server_password) do |ssh|
283
+ exec_ssh(ssh, cmd)
284
+ end
285
+ end
286
+
287
+ def exec_ssh(ssh, cmd)
288
+ ssh.exec! cmd do |ch, stream, data|
289
+ @output = get_deploy_output(stream, data)
290
+ end
291
+ @output
292
+ end
293
+
294
+ def get_deploy_output(stream, data)
295
+ if stream == :stderr
296
+ { data: "#{data}", error: true }
297
+ else
298
+ { data: "#{data}", error: false }
299
+ end
300
+ end
301
+
302
+
303
+ def gen_deploy_msg (title, color, body, user, app, area, env, tag)
304
+ msg = Adapters::Slack::Attachment.new(
305
+ body,
306
+ title: "Deploy - #{title}",
307
+ color: "#{color}",
308
+ pretext: "@#{user}:",
309
+ fields: [
310
+ {
311
+ title: "App",
312
+ value: app,
313
+ short: true
314
+ },
315
+ {
316
+ title: "Área",
317
+ value: area,
318
+ short: true
319
+ },
320
+ {
321
+ title: "Ambiente",
322
+ value: env,
323
+ short: true
324
+ },
325
+ {
326
+ title: "tag",
327
+ value: tag,
328
+ short: true
294
329
  },
295
- {
296
- title: "Área",
297
- value: area,
298
- short: true
299
- },
300
- {
301
- title: "Ambiente",
302
- value: env,
303
- short: true
304
- },
305
- {
306
- title: "tag",
307
- value: tag,
308
- short: true
309
- },
310
- ]
311
- )
330
+ ]
331
+ )
332
+ end
333
+
334
+ def allowed_room?(room_id, allowed_channel)
335
+ room = Lita::Room.find_by_id(room_id)
336
+ return false if room.nil?
337
+ return true if room.metadata["name"] == allowed_channel
338
+ end
339
+
340
+ def get_reminders(app, area, env)
341
+ config.deploy_tree[app.to_sym][area.to_sym][:reminders][env.to_sym]
342
+ rescue
343
+ false
312
344
  end
313
345
 
314
- def allowed_room?(room_id, allowed_channel)
315
- room = Lita::Room.find_by_id(room_id)
316
- return false if room.nil?
317
- return true if room.metadata["name"] == allowed_channel
346
+ def set_deploy_reminder(target_user, app, area, env, tag, time)
347
+ slack = Slack::API.new(token: config.slack_api_token)
348
+ target = Lita::User.find_by_mention_name(target_user).id
349
+ todo_action = "Fazer deploy da app #{app} #{env} tag:#{tag}"
350
+ slack.reminders_add(text: todo_action, time: time, user: target)
318
351
  end
319
352
 
320
- end
321
-
322
- Lita.register_handler(Capistrano)
323
- end
324
- end
353
+ def slack_api_configured?
354
+ config.slack_api_token
355
+ rescue
356
+ config.slack_api_token ||= false
357
+ end
358
+
359
+ end
360
+
361
+ Lita.register_handler(Capistrano)
362
+ end
363
+ end
@@ -1,25 +1,26 @@
1
- Gem::Specification.new do |spec|
2
- spec.name = "lita-capistrano"
3
- spec.version = "0.2.2"
4
- spec.authors = ["Alexandre Gomes"]
5
- spec.email = ["alejdg@outlook.com.br"]
6
- spec.summary = "A Lita handler to integrate with Capistrano.rb"
7
- spec.description = "A Lita handler to integrate with Capistrano.rb"
8
- spec.homepage = "https://github.com/alejdg/lita-capistrano.git"
9
- spec.metadata = { "lita_plugin_type" => "handler" }
10
-
11
- spec.files = `git ls-files`.split($/)
12
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
13
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
14
- spec.require_paths = ["lib"]
15
-
16
- spec.add_runtime_dependency "lita", " ~> 4.6"
17
- spec.add_runtime_dependency 'lita-deploy-tracker'
18
- spec.add_runtime_dependency 'net-ssh'
19
-
20
- spec.add_development_dependency "bundler", "~> 1.3"
21
- spec.add_development_dependency "pry-byebug"
22
- spec.add_development_dependency "rake"
23
- spec.add_development_dependency "rack-test"
24
- spec.add_development_dependency "rspec", "~> 3.0"
25
- end
1
+ Gem::Specification.new do |spec|
2
+ spec.name = "lita-capistrano"
3
+ spec.version = "0.2.3"
4
+ spec.authors = ["Alexandre Gomes"]
5
+ spec.email = ["alejdg@outlook.com.br"]
6
+ spec.summary = "A Lita handler to integrate with Capistrano.rb"
7
+ spec.description = "A Lita handler to integrate with Capistrano.rb"
8
+ spec.homepage = "https://github.com/alejdg/lita-capistrano.git"
9
+ spec.metadata = { "lita_plugin_type" => "handler" }
10
+
11
+ spec.files = `git ls-files`.split($/)
12
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
13
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
14
+ spec.require_paths = ["lib"]
15
+
16
+ spec.add_runtime_dependency "lita", " ~> 4.6"
17
+ spec.add_runtime_dependency 'lita-deploy-tracker'
18
+ spec.add_runtime_dependency 'net-ssh'
19
+ spec.add_runtime_dependency 'slack-api'
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "pry-byebug"
23
+ spec.add_development_dependency "rake"
24
+ spec.add_development_dependency "rack-test"
25
+ spec.add_development_dependency "rspec", "~> 3.0"
26
+ end
@@ -1,4 +1,4 @@
1
- en:
2
- lita:
3
- handlers:
4
- capistrano:
1
+ en:
2
+ lita:
3
+ handlers:
4
+ capistrano:
@@ -1,5 +1,5 @@
1
- require "spec_helper"
2
-
3
- describe Lita::Handlers::Capistrano, lita_handler: true do
4
- it { is_expected.to route ("cap commerce list").to(:cap_list)}
5
- end
1
+ require "spec_helper"
2
+
3
+ describe Lita::Handlers::Capistrano, lita_handler: true do
4
+ it { is_expected.to route ("cap commerce list").to(:cap_list)}
5
+ end
@@ -1,6 +1,6 @@
1
- require "lita-capistrano"
2
- require "lita/rspec"
3
-
4
- # A compatibility mode is provided for older plugins upgrading from Lita 3. Since this plugin
5
- # was generated with Lita 4, the compatibility mode should be left disabled.
6
- Lita.version_3_compatibility_mode = false
1
+ require "lita-capistrano"
2
+ require "lita/rspec"
3
+
4
+ # A compatibility mode is provided for older plugins upgrading from Lita 3. Since this plugin
5
+ # was generated with Lita 4, the compatibility mode should be left disabled.
6
+ Lita.version_3_compatibility_mode = false
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lita-capistrano
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexandre Gomes
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-04 00:00:00.000000000 Z
11
+ date: 2016-08-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lita
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: slack-api
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: bundler
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -160,7 +174,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
160
174
  version: '0'
161
175
  requirements: []
162
176
  rubyforge_project:
163
- rubygems_version: 2.5.1
177
+ rubygems_version: 2.5.2
164
178
  signing_key:
165
179
  specification_version: 4
166
180
  summary: A Lita handler to integrate with Capistrano.rb