slackistrano 3.0.1 → 3.1.0.beta

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: 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