slackistrano 3.0.1 → 3.1.0.beta

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d61845e7cdfae5f62970368fb7151e5c2e01a525
4
- data.tar.gz: 8b29ea88f33d79f8430f185bcf3d88a6bf9f79eb
3
+ metadata.gz: 896df176f220863aab32aa8e816f4b466dc9a105
4
+ data.tar.gz: 2f80f45cf424c0490be715670ccea02117a33376
5
5
  SHA512:
6
- metadata.gz: 26f4396d6d1ab40bebc9e66d26a43ad31b7d178c9444354031d5f20206b3ee34795dd9f06f331adcfa070c0e61da591888458e909d501e82a1054da0529f29da
7
- data.tar.gz: 05fe08acd7d3a21495bbdabe64001a6d79ecf5e1f12360b23b564af01bb324a7a0b56ace54981e6379663cc90fd40d7b5ee3ea2b922f4d5eed1030d44615d636
6
+ metadata.gz: 17ca0ce1996dc7ec7b54f18d3804b949f4ff73d58a0ccae28a4124aba7d764971108296941b6d37a37eec61585a2746ae2677ea06c3867f8ee9da376cc0569a2
7
+ data.tar.gz: d407a73ccec54c17c2ae891480976a24ef747b93e1953417009e3fabaa8bdf28e0259b143438349b5775a77cdf172ba058eef755b1eaeb8426bf3a4ff33f69e4
data/README.md CHANGED
@@ -6,181 +6,178 @@
6
6
 
7
7
  Send notifications to [Slack](https://slack.com) about [Capistrano](http://www.capistranorb.com) deployments.
8
8
 
9
- If you need Capistrano v2 support, check out [capistrano-slack](https://github.com/j-mcnally/capistrano-slack).
10
-
11
- ## NOTE: Upgrading from 1.x? Not Getting Notifications?
12
-
13
- Version 2.0 has changed how Slackistrano must be loaded in your Gemfile and Capfile. See the *Installation* section
14
- below current install.
9
+ **NOTE:** This README documents version 3.1.0.beta. You can read about 3.0.1 [here](https://github.com/phallstrom/slackistrano/tree/v3.0.1).
15
10
 
16
11
  ## Requirements
17
12
 
18
- - Capistrano >= 3.1.0
13
+ - Capistrano >= 3.5.0
19
14
  - Ruby >= 2.0
20
15
  - A Slack account
21
16
 
22
17
  ## Installation
23
18
 
24
- Add this line to your application's Gemfile:
19
+ 1. Add this line to your application's Gemfile:
25
20
 
26
- ```ruby
27
- gem 'slackistrano'
28
- ```
21
+ ```ruby
22
+ gem 'slackistrano'
23
+ ```
29
24
 
30
- And then execute:
25
+ 2. Execute:
31
26
 
32
- ```bash
33
- $ bundle
34
- ```
27
+ ```
28
+ $ bundle
29
+ ```
30
+
31
+ 3. Require the library in your application's Capfile:
32
+
33
+ ```ruby
34
+ require 'slackistrano/capistrano'
35
+ ```
35
36
 
36
37
  ## Configuration
37
38
 
38
39
  You have two options to notify a channel in Slack when you deploy:
39
40
 
40
- 1. Using *Incoming WebHooks* integration, offering more options but requires one of the five free integrations. This is the default option.
41
- 2. Using *Slackbot*, which will not use one of the five free integrations. Enable via the `:slack_via_slackbot` option.
41
+ 1. Using *Incoming WebHooks* integration, offering more options but requires
42
+ one of the five free integrations. This option provides more messaging
43
+ flexibility.
44
+ 2. Using *Slackbot*, which will not use one of the five free integrations.
42
45
 
43
- In both case, you need to enable the integration inside Slack and get the token and/or webhook url that will be needed later.
46
+ ### Incoming Webhook
44
47
 
45
- Require the library in your application's Capfile:
48
+ 1. Configure your Slack's Incoming Webhook.
49
+ 2. Add the following to `config/deploy.rb`:
46
50
 
47
- ```ruby
48
- require 'slackistrano/capistrano'
49
- ```
51
+ ```ruby
52
+ set :slackistrano, {
53
+ channel: '#your-channel',
54
+ webhook: 'your-incoming-webhook-url'
55
+ }
56
+ ```
50
57
 
51
- If you post using *Incoming Webhooks* you need to set your webhook url in your application's config/deploy.rb:
58
+ ### Slackbot
52
59
 
53
- ```ruby
54
- set :slack_webhook, "https://hooks.slack.com/services/XXX/XXX/XXX"
55
- ```
60
+ 1. Configure your Slack's Slackbot (not Bot).
61
+ 2. Add the following to `config/deploy.rb`:
56
62
 
57
- If you choose to post using *Slackbot* you **must** set your team, token, and channel in your application's config/deploy.rb:
63
+ ```ruby
64
+ set :slackistrano, {
65
+ channel: '#your-channel',
66
+ team: 'your-team-name',
67
+ token: 'your-token'
68
+ }
69
+ ```
58
70
 
59
- ```ruby
60
- set :slack_via_slackbot, true
61
- set :slack_team, "teamname"
62
- set :slack_token, "xxxxxxxxxxxxxxxxxxxxxxxx"
63
- set :slack_channel, '#general'
64
- ```
71
+ ### Test your Configuration
65
72
 
66
- You can set `:slack_channel` (or any of the `:slack_channel_xxxx` settings) to an array and Slackistrano
67
- will post to each channel. For example:
73
+ Test your setup by running the following command. This will post each stage's
74
+ message to Slack in turn.
68
75
 
69
- ```ruby
70
- set :slack_channel, ['#general', '#ops']
71
76
  ```
72
-
73
- Optionally, override the other slack settings.
74
-
75
- ```ruby
76
- set :slack_channel_updating, -> { nil } # Channel to post to. Defaults to :slack_channel.
77
- set :slack_channel_reverting, -> { nil } # Channel to post to. Defaults to :slack_channel.
78
- set :slack_channel_updated, -> { nil } # Channel to post to. Defaults to :slack_channel.
79
- set :slack_channel_reverted, -> { nil } # Channel to post to. Defaults to :slack_channel.
80
- set :slack_channel_failed, -> { nil } # Channel to post to. Defaults to :slack_channel.
81
-
82
- set :slack_icon_url, -> { 'http://gravatar.com/avatar/885e1c523b7975c4003de162d8ee8fee?r=g&s=40' }
83
- set :slack_icon_emoji, -> { nil } # Emoji to use. Overrides icon_url. Must be a string (ex: ':shipit:')
84
- set :slack_username, -> { 'Slackistrano' }
85
-
86
- set :slack_run, -> { true } # Set to false to disable all messages.
87
- set :slack_run_updating, -> { true } # Set to false to disable deploy starting message.
88
- set :slack_run_reverting, -> { true } # Set to false to disable rollback starting message.
89
- set :slack_run_updated, -> { true } # Set to false to disable deploy finished message.
90
- set :slack_run_reverted, -> { true } # Set to false to disable rollback finished message.
91
- set :slack_run_failed, -> { true } # Set to false to disable failure message.
92
-
93
- set :slack_deploy_user, -> { ENV['USER'] || ENV['USERNAME'] }
94
-
95
- set :slack_msg_updating, -> { "#{fetch :slack_deploy_user} has started deploying branch #{fetch :branch} of #{fetch :application} to #{fetch :stage, 'an unknown stage'}" }
96
- set :slack_msg_reverting, -> { "#{fetch :slack_deploy_user} has started rolling back branch #{fetch :branch} of #{fetch :application} to #{fetch :stage, 'an unknown stage'}" }
97
- set :slack_msg_updated, -> { "#{fetch :slack_deploy_user} has finished deploying branch #{fetch :branch} of #{fetch :application} to #{fetch :stage, 'an unknown stage'}" }
98
- set :slack_msg_reverted, -> { "#{fetch :slack_deploy_user} has finished rolling back branch of #{fetch :application} to #{fetch :stage, 'an unknown stage'}" }
99
- set :slack_msg_failed, -> { "#{fetch :slack_deploy_user} has failed to #{fetch :slack_deploy_or_rollback} branch #{fetch :branch} of #{fetch :application} to #{fetch :stage, 'an unknown stage'}" }
100
-
101
- set :slack_fields_updating, -> { [] }
102
- set :slack_fields_reverting, -> { [] }
103
- set :slack_fields_updated, -> { [] }
104
- set :slack_fields_reverted, -> { [] }
105
- set :slack_fields_failed, -> { [] }
106
-
107
- set :slack_fallback_updating, -> { nil }
108
- set :slack_fallback_reverting, -> { nil }
109
- set :slack_fallback_updated, -> { nil }
110
- set :slack_fallback_reverted, -> { nil }
111
- set :slack_fallback_failed, -> { nil }
112
-
113
- set :slack_title_updating, -> { nil }
114
- set :slack_title_reverting, -> { nil }
115
- set :slack_title_updated, -> { nil }
116
- set :slack_title_reverted, -> { nil }
117
- set :slack_title_failed, -> { nil }
118
-
119
- set :slack_pretext_updating, -> { nil }
120
- set :slack_pretext_reverting, -> { nil }
121
- set :slack_pretext_updated, -> { nil }
122
- set :slack_pretext_reverted, -> { nil }
123
- set :slack_pretext_failed, -> { nil }
77
+ $ cap production slack:deploy:test
124
78
  ```
125
79
 
126
- **Note**: You may wish to disable one of the notifications if another service (ex:
127
- Honeybadger) also displays a deploy notification.
128
-
129
- Test your setup by running:
80
+ ## Usage
130
81
 
131
- ```bash
132
- $ cap production slack:deploy:updating
133
- $ cap production slack:deploy:reverting
134
- $ cap production slack:deploy:updated
135
- $ cap production slack:deploy:reverted
136
- $ cap production slack:deploy:failed
137
- ```
82
+ Deploy your application like normal and you should see messages in the channel
83
+ you specified.
138
84
 
139
- ## Formatted messages
85
+ ## Customizing the Messaging
140
86
 
141
- Slack allows you to send complex content, composed by fields. You can use the `fields` and `fallback` variables in order to have a well formatted message as follows:
87
+ You can customize the messaging posted to Slack by providing your own messaging
88
+ class and overriding several methods. Here is one example:
142
89
 
143
90
  ```ruby
144
- set :slack_revision, `git rev-parse origin/#{fetch(:branch)}`.strip!
145
- set :slack_msg_updated, nil
146
- set :slack_fallback_updated, "#{fetch(:slack_deploy_user)} deployed #{fetch(:application)} on #{fetch(:stage)}"
147
- set :slack_fields_updated, [
148
- {
149
- title: "Project",
150
- value: "<https://github.com/XXXXX/#{fetch(:application)}|#{fetch(:application)}>",
151
- short: true
152
- },
153
- {
154
- title: "Environment",
155
- value: fetch(:stage),
156
- short: true
157
- },
158
- {
159
- title: "Deployer",
160
- value: fetch(:slack_deploy_user),
161
- short: true
162
- },
163
- {
164
- title: "Revision",
165
- value: "<https://github.com/XXXXX/#{fetch(:application)}/commit/#{fetch(:slack_revision)}|#{fetch(:slack_revision)[0..6]}>",
166
- short: true
167
- }
168
- ]
91
+ module Slackistrano
92
+ class CustomMessaging < Messaging::Base
93
+
94
+ # Send failed message to #ops. Send all other messages to default channels.
95
+ def channels_for(action)
96
+ if action == :failed
97
+ "#ops"
98
+ else
99
+ super
100
+ end
101
+ end
102
+
103
+ # Supress updating message.
104
+ def payload_for_updating
105
+ nil
106
+ end
107
+
108
+ # Supress reverting message.
109
+ def payload_for_reverting
110
+ nil
111
+ end
112
+
113
+ # Fancy updated message.
114
+ # See https://api.slack.com/docs/message-attachments
115
+ def payload_for_updated
116
+ {
117
+ attachments: [{
118
+ color: 'good',
119
+ title: "Application Deployed",
120
+ fields: [
121
+ {title: "Project", value: application, short: true},
122
+ {title: "Environment", value: stage, short: true},
123
+ {title: "Deployer", value: deployer, short: true},
124
+ {title: "Time", value: elapsed_time, short: true},
125
+ ],
126
+ fallback: super[:text],
127
+ }]}
128
+ end
129
+
130
+ # Default reverted message. Alternatively we could have simply not
131
+ # redefined this method at all.
132
+ def payload_for_reverted
133
+ super
134
+ end
135
+
136
+ # Slightly tweaked failed message.
137
+ # See https://api.slack.com/docs/message-formatting
138
+ def payload_for_failed
139
+ payload = super
140
+ payload[:text] = ":fire: #{payload[:text]}"
141
+ payload
142
+ end
143
+
144
+ # Override the deployer helper to pull the full name from the password
145
+ # file.
146
+ # See https://github.com/phallstrom/slackistrano/blob/master/lib/slackistrano/messaging/helpers.rb
147
+ def deployer
148
+ Etc.getpwnam(ENV['USER']).gecos
149
+ end
150
+
151
+ end
152
+ end
169
153
  ```
170
154
 
171
- It will produce the following format:
172
- ![Formatted message](https://raw.githubusercontent.com/phallstrom/slackistrano/master/examples/fomatting_with_fields.png)
155
+ The output would look like this:
156
+ ![Custom Messaging](https://raw.githubusercontent.com/phallstrom/slackistrano/overhaul/images/custom_messaging.jpg)
173
157
 
174
- **Note 1:** *The `fallback` field is required in order to display notifications when using `fields`.*
158
+ To set this up:
175
159
 
176
- **Note 2:** *The `fields` configuration requires you to use webhooks.*
160
+ 1. Add the above class to your app, for example `lib/custom_messaging.rb`.
177
161
 
178
- More information: [https://api.slack.com/docs/attachments](https://api.slack.com/docs/attachments)
162
+ 2. Require the library after the requiring of Slackistrano in your application's Capfile.
179
163
 
180
- ## Usage
164
+ ```ruby
165
+ require_relative 'lib/custom_messaging'
166
+ ```
181
167
 
182
- Deploy your application like normal and you should see messages in the channel
183
- you specified.
168
+ 3. Update the `slackistrano` configuration in `config/deploy.rb` and add the `klass` option.
169
+
170
+ ```ruby
171
+ set :slackistrano, {
172
+ klass: Slackistrano::CustomMessaging,
173
+ channel: '#your-channel',
174
+ webhook: 'your-incoming-webhook-url'
175
+ }
176
+ ```
177
+
178
+ 4. If you come up with something that you think others would enjoy submit it as
179
+ an issue along with a screenshot of the output from `cap production
180
+ slack:deploy:test` and I'll add it to the Wiki.
184
181
 
185
182
  ## TODO
186
183
 
Binary file
Binary file
data/lib/slackistrano.rb CHANGED
@@ -1,7 +1,4 @@
1
1
  require 'slackistrano/version'
2
- require 'net/http'
3
- require 'json'
4
2
 
5
3
  module Slackistrano
6
-
7
4
  end
@@ -1,3 +1,4 @@
1
+ require_relative 'messaging/base'
1
2
  require 'net/http'
2
3
  require 'json'
3
4
  require 'forwardable'
@@ -7,163 +8,121 @@ load File.expand_path("../tasks/slack.rake", __FILE__)
7
8
  module Slackistrano
8
9
  class Capistrano
9
10
 
11
+ attr_reader :backend
12
+ private :backend
13
+
10
14
  extend Forwardable
11
15
  def_delegators :env, :fetch, :run_locally
12
16
 
13
- #
14
- #
15
- #
16
17
  def initialize(env)
17
18
  @env = env
19
+ opts = fetch(:slackistrano, {}).dup
20
+ @messaging = if opts.empty?
21
+ klass = Messaging::Deprecated.new(
22
+ env: @env,
23
+ team: fetch(:slack_team),
24
+ channel: fetch(:slack_channel),
25
+ token: fetch(:slack_token),
26
+ webhook: fetch(:slack_webhook)
27
+ )
28
+ else
29
+ klass = opts.delete(:klass) || Messaging::Default
30
+ klass.new(opts.merge(env: @env))
31
+ end
18
32
  end
19
33
 
20
- #
21
- #
22
- #
23
34
  def run(action)
24
- should_run = fetch("slack_run".to_sym)
25
- should_run &&= fetch("slack_run_#{action}".to_sym)
26
- return unless should_run
27
-
28
35
  _self = self
29
- run_locally { _self.post(action, self) }
30
-
36
+ run_locally { _self.process(action, self) }
31
37
  end
32
38
 
33
- #
34
- #
35
- #
36
- def post(action, backend)
37
- team = fetch(:slack_team)
38
- token = fetch(:slack_token)
39
- webhook = fetch(:slack_webhook)
40
- via_slackbot = fetch(:slack_via_slackbot)
41
-
42
- channels = fetch(:slack_channel)
43
- channel_action = "slack_channel_#{action}".to_sym
44
- channels = fetch(channel_action) if fetch(channel_action)
45
- channels = Array(channels)
46
- if via_slackbot == false && channels.empty?
47
- channels = [nil] # default webhook channel
48
- end
39
+ def process(action, backend)
40
+ @backend = backend
41
+
42
+ payload = @messaging.payload_for(action)
43
+ return if payload.nil?
49
44
 
50
45
  payload = {
51
- username: fetch(:slack_username),
52
- icon_url: fetch(:slack_icon_url),
53
- icon_emoji: fetch(:slack_icon_emoji),
54
- }
55
-
56
- payload[:attachments] = case action
57
- when :updated
58
- make_attachments(action, color: 'good')
59
- when :reverted
60
- make_attachments(action, color: '#4CBDEC')
61
- when :failed
62
- make_attachments(action, color: 'danger')
63
- else
64
- make_attachments(action)
65
- end
46
+ username: @messaging.username,
47
+ icon_url: @messaging.icon_url,
48
+ icon_emoji: @messaging.icon_emoji,
49
+ }.merge(payload)
66
50
 
67
- channels.each do |channel|
68
- payload[:channel] = channel
69
-
70
- dry_run = if ::Capistrano::Configuration.respond_to?(:dry_run?)
71
- ::Capistrano::Configuration.dry_run?
72
- else
73
- ::Capistrano::Configuration.env.send(:config)[:sshkit_backend] == SSHKit::Backend::Printer
74
- end
75
-
76
- if dry_run
77
- backend.info("[slackistrano] Slackistrano Dry Run:")
78
- backend.info("[slackistrano] Team: #{team}")
79
- backend.info("[slackistrano] Webhook: #{webhook}")
80
- backend.info("[slackistrano] Via Slackbot: #{via_slackbot}")
81
- backend.info("[slackistrano] Payload: #{payload.to_json}")
82
-
83
- # Post to the channel.
84
- else
85
-
86
- begin
87
- response = post_to_slack(team: team,
88
- token: token,
89
- webhook: webhook,
90
- via_slackbot: via_slackbot,
91
- payload: payload)
92
-
93
- rescue => e
94
- backend.warn("[slackistrano] Error notifying Slack!")
95
- backend.warn("[slackistrano] Error: #{e.inspect}")
96
- end
97
-
98
- if response.code !~ /^2/
99
- warn("[slackistrano] Slack API Failure!")
100
- warn("[slackistrano] URI: #{response.uri}")
101
- warn("[slackistrano] Code: #{response.code}")
102
- warn("[slackistrano] Message: #{response.message}")
103
- warn("[slackistrano] Body: #{response.body}") if response.message != response.body && response.body !~ /<html/
104
- end
105
- end
51
+ channels = Array(@messaging.channels_for(action))
52
+ if !@messaging.via_slackbot? == false && channels.empty?
53
+ channels = [nil] # default webhook channel
106
54
  end
107
55
 
56
+ channels.each do |channel|
57
+ post(payload.merge(channel: channel))
58
+ end
108
59
  end
109
60
 
110
- #
111
- #
112
- #
113
- def make_attachments(action, options={})
114
- attachments = options.merge({
115
- title: fetch(:"slack_title_#{action}"),
116
- pretext: fetch(:"slack_pretext_#{action}"),
117
- text: fetch(:"slack_msg_#{action}"),
118
- fields: fetch(:"slack_fields_#{action}"),
119
- fallback: fetch(:"slack_fallback_#{action}"),
120
- mrkdwn_in: [:text, :pretext]
121
- }).reject{|k, v| v.nil? }
122
- [attachments]
61
+ private ##################################################
62
+
63
+ def post(payload)
64
+
65
+ if dry_run?
66
+ post_dry_run(payload)
67
+ return
68
+ end
69
+
70
+ begin
71
+ response = post_to_slack(payload)
72
+ rescue => e
73
+ backend.warn("[slackistrano] Error notifying Slack!")
74
+ backend.warn("[slackistrano] Error: #{e.inspect}")
75
+ end
76
+
77
+ if response && response.code !~ /^2/
78
+ warn("[slackistrano] Slack API Failure!")
79
+ warn("[slackistrano] URI: #{response.uri}")
80
+ warn("[slackistrano] Code: #{response.code}")
81
+ warn("[slackistrano] Message: #{response.message}")
82
+ warn("[slackistrano] Body: #{response.body}") if response.message != response.body && response.body !~ /<html/
83
+ end
123
84
  end
124
- private :make_attachments
125
-
126
- #
127
- #
128
- #
129
- def post_to_slack(via_slackbot: nil, team: nil, token: nil, webhook: nil, payload: {})
130
- if via_slackbot
131
- post_as_slackbot(team: team, token: token, webhook: webhook, payload: payload)
85
+
86
+ def post_to_slack(payload = {})
87
+ if @messaging.via_slackbot?
88
+ post_to_slack_as_slackbot(payload)
132
89
  else
133
- post_as_webhook(team: team, token: token, webhook: webhook, payload: payload)
90
+ post_to_slack_as_webhook(payload)
134
91
  end
135
92
  end
136
- private :post_to_slack
137
-
138
- #
139
- #
140
- #
141
- def post_as_slackbot(team: nil, token: nil, webhook: nil, payload: {})
142
- uri = URI(URI.encode("https://#{team}.slack.com/services/hooks/slackbot?token=#{token}&channel=#{payload[:channel]}"))
143
-
144
- text = payload[:attachments].collect { |a| a[:text] }.join("\n")
145
93
 
94
+ def post_to_slack_as_slackbot(payload = {})
95
+ uri = URI(URI.encode("https://#{@messaging.team}.slack.com/services/hooks/slackbot?token=#{@messaging.token}&channel=#{payload[:channel]}"))
96
+ text = (payload[:attachments] || [payload]).collect { |a| a[:text] }.join("\n")
146
97
  Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
147
98
  http.request_post uri, text
148
99
  end
149
100
  end
150
- private :post_as_slackbot
151
101
 
152
- #
153
- #
154
- #
155
- def post_as_webhook(team: nil, token: nil, webhook: nil, payload: {})
102
+ def post_to_slack_as_webhook(payload = {})
156
103
  params = {'payload' => payload.to_json}
104
+ uri = URI(@messaging.webhook)
105
+ Net::HTTP.post_form(uri, params)
106
+ end
157
107
 
158
- if webhook.nil?
159
- webhook = "https://#{team}.slack.com/services/hooks/incoming-webhook"
160
- params.merge!('token' => token)
108
+ def dry_run?
109
+ if ::Capistrano::Configuration.respond_to?(:dry_run?)
110
+ ::Capistrano::Configuration.dry_run?
111
+ else
112
+ ::Capistrano::Configuration.env.send(:config)[:sshkit_backend] == SSHKit::Backend::Printer
161
113
  end
114
+ end
162
115
 
163
- uri = URI(webhook)
164
- Net::HTTP.post_form(uri, params)
116
+ def post_dry_run(payload)
117
+ backend.info("[slackistrano] Slackistrano Dry Run:")
118
+ if @messaging.via_slackbot?
119
+ backend.info("[slackistrano] Team: #{@messaging.team}")
120
+ backend.info("[slackistrano] Token: #{@messaging.token}")
121
+ else
122
+ backend.info("[slackistrano] Webhook: #{@messaging.webhook}")
123
+ end
124
+ backend.info("[slackistrano] Payload: #{payload.to_json}")
165
125
  end
166
- private :post_as_webhook
167
126
 
168
127
  end
169
128
  end