capistrano-slacky 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +13 -0
  3. data/LICENSE.md +21 -0
  4. data/README.md +13 -0
  5. data/lib/capistrano-slacky.rb +3 -0
  6. data/lib/capistrano/slacky.rb +36 -0
  7. data/lib/capistrano/slacky/block.rb +6 -0
  8. data/lib/capistrano/slacky/block/context.rb +35 -0
  9. data/lib/capistrano/slacky/block/header.rb +24 -0
  10. data/lib/capistrano/slacky/block/root.rb +19 -0
  11. data/lib/capistrano/slacky/block/section.rb +34 -0
  12. data/lib/capistrano/slacky/command.rb +4 -0
  13. data/lib/capistrano/slacky/command/diff.rb +79 -0
  14. data/lib/capistrano/slacky/command/duration.rb +13 -0
  15. data/lib/capistrano/slacky/configuration.rb +74 -0
  16. data/lib/capistrano/slacky/facade.rb +9 -0
  17. data/lib/capistrano/slacky/facade/body.rb +35 -0
  18. data/lib/capistrano/slacky/facade/changelog.rb +45 -0
  19. data/lib/capistrano/slacky/facade/deployed_by.rb +15 -0
  20. data/lib/capistrano/slacky/facade/exception.rb +23 -0
  21. data/lib/capistrano/slacky/facade/header.rb +47 -0
  22. data/lib/capistrano/slacky/facade/revision.rb +31 -0
  23. data/lib/capistrano/slacky/facade/root.rb +9 -0
  24. data/lib/capistrano/slacky/fanout.rb +39 -0
  25. data/lib/capistrano/slacky/i18n.rb +21 -0
  26. data/lib/capistrano/slacky/messaging.rb +22 -0
  27. data/lib/capistrano/slacky/messaging/base.rb +37 -0
  28. data/lib/capistrano/slacky/messaging/default.rb +37 -0
  29. data/lib/capistrano/slacky/messaging/null.rb +21 -0
  30. data/lib/capistrano/slacky/payload.rb +25 -0
  31. data/lib/capistrano/slacky/runner.rb +18 -0
  32. data/lib/capistrano/slacky/version.rb +7 -0
  33. data/lib/tasks/slacky.rake +42 -0
  34. metadata +156 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c87234b5cfdadfb8ce089157537abe447d9836f10bb53b457a2e56dbd8a43f39
4
+ data.tar.gz: 8c80128b0c46a4b1e7882a8c13cdb68d627054d341605148eeb88964cd43dd63
5
+ SHA512:
6
+ metadata.gz: a4a123f09d2ea1285546071859f1bd2fa7d2a86b100e435f580b410a9e348f540fd2717769a8b3fff7862ade8c9aa62b8973c1b971b8f867f9793ee5b942c89f
7
+ data.tar.gz: 6918fccca963085fc524885ff4adee5cc8fa5b28723417f60a6fcdfa049728c04eb079458268d0f86dd4b7e8067030500ba11ff3be48535bbecc21ce78b34467
data/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.0] - 2021-05-26
10
+ ### Added
11
+ - Initial version. ([@chubchenko][])
12
+
13
+ [@chubchenko]: https://github.com/chubchenko
data/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Artem Chubchenko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,13 @@
1
+ [![build][1]][2]
2
+
3
+ # capistrano-slacky
4
+
5
+ Send `Capistrano` deployment status to `Slack`.
6
+
7
+ ## License
8
+
9
+ [MIT][3]
10
+
11
+ [1]: https://github.com/chubchenko/capistrano-slacky/workflows/build/badge.svg
12
+ [2]: https://github.com/chubchenko/capistrano-slacky/actions
13
+ [3]: https://choosealicense.com/licenses/mit
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "capistrano/slacky"
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ load File.expand_path("../../tasks/slacky.rake", __FILE__)
4
+
5
+ require_relative "slacky/version"
6
+ require_relative "slacky/configuration"
7
+ require_relative "slacky/i18n"
8
+ require_relative "slacky/runner"
9
+ require_relative "slacky/block"
10
+ require_relative "slacky/facade"
11
+ require_relative "slacky/command"
12
+
13
+ module Capistrano
14
+ module Slacky
15
+ module_function
16
+
17
+ # Delegate any missing method call to the configuration
18
+ def method_missing(method_name, *arguments, &block)
19
+ return super unless configuration.respond_to?(method_name)
20
+
21
+ configuration.public_send(method_name, *arguments, &block)
22
+ end
23
+
24
+ # Replace the Object.respond_to?() method
25
+ def respond_to_missing?(method_name, include_private = false)
26
+ configuration.respond_to?(method_name) || super
27
+ end
28
+
29
+ # @return [Capistrano::Slacky::Configuration]
30
+ def configuration
31
+ @configuration ||= ::Capistrano::Slacky::Configuration.new
32
+ end
33
+
34
+ private_class_method :configuration
35
+ end
36
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "block/root"
4
+ require_relative "block/header"
5
+ require_relative "block/context"
6
+ require_relative "block/section"
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capistrano
4
+ module Slacky
5
+ module Block
6
+ class Context
7
+ def initialize(*elements)
8
+ @elements = elements.map do |element|
9
+ Element.new(element)
10
+ end
11
+ end
12
+
13
+ def to_json
14
+ {
15
+ type: :context,
16
+ elements: @elements.map(&:to_json)
17
+ }
18
+ end
19
+
20
+ class Element
21
+ def initialize(text)
22
+ @text = text
23
+ end
24
+
25
+ def to_json
26
+ {
27
+ type: :mrkdwn,
28
+ text: @text
29
+ }
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capistrano
4
+ module Slacky
5
+ module Block
6
+ class Header
7
+ def initialize(text)
8
+ @text = text
9
+ end
10
+
11
+ def to_json
12
+ {
13
+ type: :header,
14
+ text: {
15
+ type: :plain_text,
16
+ text: @text,
17
+ emoji: true
18
+ }
19
+ }
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capistrano
4
+ module Slacky
5
+ module Block
6
+ class Root
7
+ def initialize(*blocks)
8
+ @blocks = blocks.flatten
9
+ end
10
+
11
+ def to_json
12
+ {
13
+ blocks: @blocks.map(&:to_json)
14
+ }
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capistrano
4
+ module Slacky
5
+ module Block
6
+ class Section
7
+ def initialize(*fields)
8
+ @fields = fields.map do |field|
9
+ Field.new(field)
10
+ end
11
+ end
12
+
13
+ def to_json
14
+ {
15
+ type: :section,
16
+ fields: @fields.map(&:to_json)
17
+ }
18
+ end
19
+
20
+ class Field
21
+ def initialize(text)
22
+ @text = text
23
+ end
24
+
25
+ def to_json
26
+ {
27
+ type: :mrkdwn, text: @text
28
+ }
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "command/duration"
4
+ require_relative "command/diff"
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capistrano
4
+ module Slacky
5
+ module Command
6
+ class Diff
7
+ def self.call(previous:, current:)
8
+ new(previous: previous, current: current).call
9
+ end
10
+
11
+ def initialize(previous:, current:)
12
+ @previous = previous
13
+ @current = current
14
+ end
15
+
16
+ def call
17
+ diff = ::IO.popen(
18
+ ["git", "log", "--oneline", "--first-parent", "#{previous}..#{current}"]
19
+ ).readlines
20
+
21
+ diff.map.with_index(1) do |line, index|
22
+ sha, commit = line.match(/^(\w+) (.*+?)/).captures
23
+
24
+ if /^Merge pull request/.match?(commit)
25
+ commit = ::IO.popen(["git", "log", "-1", sha, '--pretty=format:"%b"']).readline
26
+ end
27
+
28
+ Message.new(index: index, sha: sha, commit: commit)
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :previous, :current
35
+ end
36
+
37
+ class Message
38
+ EMOJI_MAP = {
39
+ "1" => ":one:",
40
+ "2" => ":two:",
41
+ "3" => ":three:",
42
+ "4" => ":four:",
43
+ "5" => ":five:",
44
+ "6" => ":six:",
45
+ "7" => ":seven:",
46
+ "8" => ":eight:",
47
+ "9" => ":nine:",
48
+ "0" => ":zero:"
49
+ }.freeze
50
+
51
+ def initialize(index:, sha:, commit:)
52
+ @index = index
53
+ @sha = sha
54
+ @commit = commit
55
+ end
56
+
57
+ def emoji
58
+ @index.to_s.chars.map do |value|
59
+ EMOJI_MAP[value]
60
+ end.join
61
+ end
62
+
63
+ def link
64
+ "<#{::Capistrano::Slacky.repo.url}/commit/#{@sha}|#{@sha}>"
65
+ end
66
+
67
+ def commit
68
+ @commit.delete('"').strip
69
+ end
70
+
71
+ def to_a
72
+ [emoji, link, commit]
73
+ end
74
+ end
75
+
76
+ private_constant :Message
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capistrano
4
+ module Slacky
5
+ module Command
6
+ class Duration
7
+ def self.call(pid: $PID)
8
+ `ps -p #{pid} -o etime=`.strip
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capistrano
4
+ module Slacky
5
+ class Configuration
6
+ DEFAULT_USERNAME = "ChatOps"
7
+ DEFAULT_ICON_EMOJI = ":robot_face:"
8
+ DEFAULT_CHANNEL = "#deployment"
9
+
10
+ private_constant :DEFAULT_USERNAME, :DEFAULT_ICON_EMOJI, :DEFAULT_CHANNEL
11
+
12
+ def initialize(env: ::Capistrano::Configuration.env)
13
+ @env = env
14
+ end
15
+
16
+ def username
17
+ data.fetch(:username, DEFAULT_USERNAME)
18
+ end
19
+
20
+ def icon_emoji
21
+ data.fetch(:icon_emoji, DEFAULT_ICON_EMOJI)
22
+ end
23
+
24
+ def channel
25
+ data.fetch(:channel, DEFAULT_CHANNEL)
26
+ end
27
+
28
+ def klass
29
+ data[:klass]
30
+ end
31
+
32
+ def slacky?
33
+ return false unless data
34
+
35
+ true
36
+ end
37
+
38
+ def repo
39
+ @repo ||= Repo.new(
40
+ remote: env.fetch(:repo_url)
41
+ )
42
+ end
43
+
44
+ private
45
+
46
+ def data
47
+ @data ||= env.fetch(:slacky, {})
48
+ end
49
+
50
+ class Repo
51
+ def initialize(remote:)
52
+ @remote = remote
53
+ end
54
+
55
+ def url
56
+ return @url if @url
57
+
58
+ @url =
59
+ if ssh?
60
+ "https://" + @remote[/(?<=@).*/].gsub(".git", "").tr(":", "/")
61
+ else
62
+ @remote
63
+ end
64
+ end
65
+
66
+ def ssh?
67
+ @remote.match?(/((git|ssh|http(s)?)|(git@[\w.]+))(:(\/)?)([\w.@:\/-~]+)(\.git)(\/)?/)
68
+ end
69
+ end
70
+
71
+ private_constant :Repo
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "facade/root"
4
+ require_relative "facade/header"
5
+ require_relative "facade/body"
6
+ require_relative "facade/revision"
7
+ require_relative "facade/changelog"
8
+ require_relative "facade/exception"
9
+ require_relative "facade/deployed_by"
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capistrano
4
+ module Slacky
5
+ module Facade
6
+ class Body < ::Capistrano::Slacky::Block::Section
7
+ def initialize(env:)
8
+ @env = env
9
+
10
+ super(
11
+ t("slacky.stage"), "`#{stage}`",
12
+ t("slacky.branch"), "`#{branch}`",
13
+ t("slacky.duration"), "`#{duration}`",
14
+ )
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :env
20
+
21
+ def stage
22
+ @stage ||= env.fetch(:stage)
23
+ end
24
+
25
+ def branch
26
+ @branch ||= env.fetch(:branch)
27
+ end
28
+
29
+ def duration
30
+ @duration ||= ::Capistrano::Slacky::Command::Duration.call
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capistrano
4
+ module Slacky
5
+ module Facade
6
+ class Changelog
7
+ def self.for(env:)
8
+ new(
9
+ previous: env.fetch(:previous_revision),
10
+ current: env.fetch(:current_revision)
11
+ ).call
12
+ end
13
+
14
+ attr_accessor :difference
15
+
16
+ def initialize(previous:, current:)
17
+ ref = self
18
+
19
+ on(::Capistrano::Configuration.env.primary(:app)) do
20
+ within repo_path do
21
+ ref.difference = ::Capistrano::Slacky::Command::Diff.call(
22
+ previous: previous,
23
+ current: current
24
+ )
25
+ end
26
+ end
27
+ end
28
+
29
+ def call
30
+ if difference.empty?
31
+ return ::Capistrano::Slacky::Block::Context.new(
32
+ t("slacky.nothing_has_changed_since_the_previous_release")
33
+ )
34
+ end
35
+
36
+ difference.map do |message|
37
+ ::Capistrano::Slacky::Block::Context.new(
38
+ *message.to_a
39
+ )
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capistrano
4
+ module Slacky
5
+ module Facade
6
+ class DeployedBy < ::Capistrano::Slacky::Block::Context
7
+ def initialize(env:)
8
+ super(
9
+ t("slacky.deployed_by", deployer: env.fetch(:local_user))
10
+ )
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capistrano
4
+ module Slacky
5
+ module Facade
6
+ class Exception < ::Capistrano::Slacky::Block::Context
7
+ DefaultException = ::Class.new(::StandardError) do
8
+ def initialize
9
+ super(t("slacky.something_went_wrong"))
10
+ end
11
+ end
12
+
13
+ private_constant :DefaultException
14
+
15
+ def initialize(exception: $ERROR_INFO)
16
+ super(
17
+ (exception || DefaultException.new).message
18
+ )
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capistrano
4
+ module Slacky
5
+ module Facade
6
+ class Header < ::Capistrano::Slacky::Block::Header
7
+ DEPLOYED_SUCCESSFULLY = [
8
+ ":smile:",
9
+ ":drooling_face:",
10
+ ":sunglasses:",
11
+ ":partying_face:",
12
+ ":heart_eyes:"
13
+ ].freeze
14
+
15
+ REVERTED_SUCCESSFULLY = [
16
+ ":upside_down_face:",
17
+ ":pensive:",
18
+ ":face_with_raised_eyebrow:",
19
+ ":worried:"
20
+ ].freeze
21
+
22
+ DEPLOYMENT_FAILED = [
23
+ ":grimacing:",
24
+ ":face_with_head_bandage:",
25
+ ":face_with_thermometer:",
26
+ ":woozy_face:",
27
+ ":exploding_head:",
28
+ ":sob:",
29
+ ":cry:"
30
+ ].freeze
31
+
32
+ ROLLBACK_FAILED = DEPLOYMENT_FAILED
33
+
34
+ EMOJI_MAP = {
35
+ deployed_successfully: DEPLOYED_SUCCESSFULLY,
36
+ reverted_successfully: REVERTED_SUCCESSFULLY,
37
+ deployment_failed: DEPLOYMENT_FAILED,
38
+ rollback_failed: ROLLBACK_FAILED
39
+ }.freeze
40
+
41
+ def initialize(text)
42
+ super(t("slacky.#{text}", emoji: EMOJI_MAP.fetch(text).sample))
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capistrano
4
+ module Slacky
5
+ module Facade
6
+ class Revision < ::Capistrano::Slacky::Block::Context
7
+ def initialize(env:)
8
+ @env = env
9
+
10
+ super(
11
+ t("slacky.revision", repository_url: ::Capistrano::Slacky.repo.url,
12
+ current: current,
13
+ previous: previous)
14
+ )
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :env
20
+
21
+ def current
22
+ env.fetch(:current_revision)[0, 7]
23
+ end
24
+
25
+ def previous
26
+ env.fetch(:previous_revision)[0, 7]
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capistrano
4
+ module Slacky
5
+ module Facade
6
+ Root = Class.new(::Capistrano::Slacky::Block::Root)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capistrano
4
+ module Slacky
5
+ module Hook
6
+ require "uri"
7
+
8
+ DEFAULT_HOOK_FILE = "config/slacky.yml"
9
+
10
+ module_function
11
+
12
+ def uri(role: Hook.role)
13
+ output = nil
14
+
15
+ on(role) do
16
+ output = capture(:cat, File.join(shared_path, DEFAULT_HOOK_FILE))
17
+ end
18
+
19
+ URI(output)
20
+ end
21
+
22
+ def role
23
+ ::Capistrano::Configuration.env.primary(:app)
24
+ end
25
+ end
26
+
27
+ private_constant :Hook
28
+
29
+ class Fanout
30
+ require "net/http"
31
+
32
+ def self.call(payload:, hook: Hook)
33
+ ::Net::HTTP.post_form(
34
+ hook.uri, payload: payload.to_json
35
+ )
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ en = {
4
+ deployed_successfully: "Deployed successfully %{emoji}",
5
+ reverted_successfully: "Reverted successfully %{emoji}",
6
+ deployment_failed: "Deployment failed %{emoji}",
7
+ rollback_failed: "Rollback failed %{emoji}",
8
+ stage: ">*Stage:*",
9
+ branch: ">*Branch:*",
10
+ duration: ">*Duration:*",
11
+ revision: "*Current* revision <%{repository_url}/commit/%{current}|%{current}> - " \
12
+ "<%{repository_url}/compare/%{previous}...%{current}|compare> " \
13
+ "(_previous revision was <%{repository_url}/commit/%{previous}|%{previous}>_).",
14
+ nothing_has_changed_since_the_previous_release: "Nothing has changed since the previous release :confounded:.",
15
+ something_went_wrong: "Something went wrong and we couldn't get the exception to display :sob:.",
16
+ deployed_by: ":truck: Deployed By: @%{deployer}"
17
+ }
18
+
19
+ I18n.backend.store_translations(:en, capistrano: {
20
+ slacky: en
21
+ })
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "messaging/base"
4
+ require_relative "messaging/default"
5
+ require_relative "messaging/null"
6
+
7
+ module Capistrano
8
+ module Slacky
9
+ module Messaging
10
+ def self.for(env:)
11
+ klass =
12
+ if ::Capistrano::Slacky.slacky?
13
+ (::Capistrano::Slacky.klass || Default)
14
+ else
15
+ Null
16
+ end
17
+
18
+ klass.new(env: env)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capistrano
4
+ module Slacky
5
+ module Messaging
6
+ UnpermittedAction = Class.new(StandardError) do
7
+ def initialize(action:)
8
+ super("Unpermitted action: #{action}.")
9
+ end
10
+ end
11
+
12
+ class Base
13
+ PERMITTED_ACTIONS = [
14
+ :updated,
15
+ :reverted,
16
+ :failed
17
+ ].freeze
18
+
19
+ private_constant :PERMITTED_ACTIONS
20
+
21
+ def initialize(env:)
22
+ @env = env
23
+ end
24
+
25
+ def payload_for(action:)
26
+ raise UnpermittedAction.new(action: action) unless PERMITTED_ACTIONS.include?(action)
27
+
28
+ public_send("payload_for_#{action}")
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :env
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capistrano
4
+ module Slacky
5
+ module Messaging
6
+ class Default < Base
7
+ def payload_for_updated
8
+ ::Capistrano::Slacky::Facade::Root.new(
9
+ ::Capistrano::Slacky::Facade::Header.new(:deployed_successfully),
10
+ ::Capistrano::Slacky::Facade::Body.new(env: env),
11
+ ::Capistrano::Slacky::Facade::Revision.new(env: env),
12
+ ::Capistrano::Slacky::Facade::Changelog.for(env: env),
13
+ ::Capistrano::Slacky::Facade::DeployedBy.new(env: env)
14
+ ).to_json
15
+ end
16
+
17
+ def payload_for_reverted
18
+ ::Capistrano::Slacky::Facade::Root.new(
19
+ ::Capistrano::Slacky::Facade::Header.new(:reverted_successfully),
20
+ ::Capistrano::Slacky::Facade::Body.new(env: env),
21
+ ::Capistrano::Slacky::Facade::Revision.new(env: env),
22
+ ::Capistrano::Slacky::Facade::DeployedBy.new(env: env)
23
+ ).to_json
24
+ end
25
+
26
+ def payload_for_failed
27
+ ::Capistrano::Slacky::Facade::Root.new(
28
+ ::Capistrano::Slacky::Facade::Header.new(deploying? ? :deployment_failed : :rollback_failed),
29
+ ::Capistrano::Slacky::Facade::Body.new(env: env),
30
+ ::Capistrano::Slacky::Facade::Exception.new,
31
+ ::Capistrano::Slacky::Facade::DeployedBy.new(env: env)
32
+ ).to_json
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capistrano
4
+ module Slacky
5
+ module Messaging
6
+ class Null < Base
7
+ def payload_for_updated
8
+ nil
9
+ end
10
+
11
+ def payload_for_reverted
12
+ nil
13
+ end
14
+
15
+ def payload_for_failed
16
+ nil
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "messaging"
4
+
5
+ module Capistrano
6
+ module Slacky
7
+ class Payload
8
+ def initialize(env:, action:)
9
+ @env = env
10
+ @action = action
11
+ @messaging = ::Capistrano::Slacky::Messaging.for(env: env)
12
+ end
13
+
14
+ def to_json
15
+ {
16
+ username: ::Capistrano::Slacky.username,
17
+ icon_emoji: ::Capistrano::Slacky.icon_emoji,
18
+ channel: ::Capistrano::Slacky.channel
19
+ }.merge(
20
+ @messaging.payload_for(action: @action)
21
+ ).to_json
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "payload"
4
+ require_relative "fanout"
5
+
6
+ module Capistrano
7
+ module Slacky
8
+ class Runner
9
+ def self.call(env:, action:)
10
+ payload = ::Capistrano::Slacky::Payload.new(
11
+ env: env, action: action
12
+ )
13
+
14
+ ::Capistrano::Slacky::Fanout.call(payload: payload)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capistrano
4
+ module Slacky
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :slacky do
4
+ desc "Slacky after a successful deployment"
5
+ task :updated do
6
+ Capistrano::Slacky::Runner.call(
7
+ env: self, action: :updated
8
+ )
9
+ end
10
+
11
+ desc "Slacky after successful rollback"
12
+ task :reverted do
13
+ Capistrano::Slacky::Runner.call(
14
+ env: self, action: :reverted
15
+ )
16
+ end
17
+
18
+ desc "Slacky after failure deployment or rollback"
19
+ task :failed do
20
+ Capistrano::Slacky::Runner.call(
21
+ env: self, action: :failed
22
+ )
23
+ end
24
+
25
+ # task :ping do
26
+ # [:updated, :reverted, :failed].each do |action|
27
+ # Capistrano::Slacky::Runner.call(
28
+ # env: self, action: action
29
+ # )
30
+ # end
31
+ # end
32
+ end
33
+
34
+ before :'deploy:finishing', :'slacky:updated'
35
+ before :'deploy:finishing_rollback', :'slacky:reverted'
36
+ before :'deploy:failed', :'slacky:failed'
37
+
38
+ namespace :load do
39
+ task :defaults do
40
+ append :linked_files, "config/slacky.yml"
41
+ end
42
+ end
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: capistrano-slacky
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Artem Chubchenko
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-05-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: capistrano
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 3.0.0
20
+ - - "~>"
21
+ - !ruby/object:Gem::Version
22
+ version: '3.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 3.0.0
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '3.0'
33
+ - !ruby/object:Gem::Dependency
34
+ name: rake
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '13.0'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '13.0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '3.10'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '3.10'
61
+ - !ruby/object:Gem::Dependency
62
+ name: simplecov
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.21'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '0.21'
75
+ - !ruby/object:Gem::Dependency
76
+ name: webmock
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '3.12'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '3.12'
89
+ description: Send Capistrano deployment status to Slack via the incoming webhooks
90
+ integration
91
+ email: artem.chubchenko@gmail.com
92
+ executables: []
93
+ extensions: []
94
+ extra_rdoc_files: []
95
+ files:
96
+ - CHANGELOG.md
97
+ - LICENSE.md
98
+ - README.md
99
+ - lib/capistrano-slacky.rb
100
+ - lib/capistrano/slacky.rb
101
+ - lib/capistrano/slacky/block.rb
102
+ - lib/capistrano/slacky/block/context.rb
103
+ - lib/capistrano/slacky/block/header.rb
104
+ - lib/capistrano/slacky/block/root.rb
105
+ - lib/capistrano/slacky/block/section.rb
106
+ - lib/capistrano/slacky/command.rb
107
+ - lib/capistrano/slacky/command/diff.rb
108
+ - lib/capistrano/slacky/command/duration.rb
109
+ - lib/capistrano/slacky/configuration.rb
110
+ - lib/capistrano/slacky/facade.rb
111
+ - lib/capistrano/slacky/facade/body.rb
112
+ - lib/capistrano/slacky/facade/changelog.rb
113
+ - lib/capistrano/slacky/facade/deployed_by.rb
114
+ - lib/capistrano/slacky/facade/exception.rb
115
+ - lib/capistrano/slacky/facade/header.rb
116
+ - lib/capistrano/slacky/facade/revision.rb
117
+ - lib/capistrano/slacky/facade/root.rb
118
+ - lib/capistrano/slacky/fanout.rb
119
+ - lib/capistrano/slacky/i18n.rb
120
+ - lib/capistrano/slacky/messaging.rb
121
+ - lib/capistrano/slacky/messaging/base.rb
122
+ - lib/capistrano/slacky/messaging/default.rb
123
+ - lib/capistrano/slacky/messaging/null.rb
124
+ - lib/capistrano/slacky/payload.rb
125
+ - lib/capistrano/slacky/runner.rb
126
+ - lib/capistrano/slacky/version.rb
127
+ - lib/tasks/slacky.rake
128
+ homepage: https://github.com/chubchenko/capistrano-slacky
129
+ licenses:
130
+ - MIT
131
+ metadata:
132
+ bug_tracker_uri: https://github.com/chubchenko/capistrano-slacky/issues
133
+ changelog_uri: https://github.com/chubchenko/capistrano-slacky/blob/master/CHANGELOG.md
134
+ homepage_uri: https://github.com/chubchenko/capistrano-slacky
135
+ source_code_uri: https://github.com/chubchenko/capistrano-slacky
136
+ github_repo: ssh://github.com/chubchenko/capistrano-slacky
137
+ post_install_message:
138
+ rdoc_options: []
139
+ require_paths:
140
+ - lib
141
+ required_ruby_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: 2.5.0
146
+ required_rubygems_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ requirements: []
152
+ rubygems_version: 3.0.8
153
+ signing_key:
154
+ specification_version: 4
155
+ summary: Send Capistrano deployment status to Slack
156
+ test_files: []