chat_notifier 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 11bbd1390ca03c1f99d0ff66a5e3a361f259f40704575db73ae255e67e48b94b
4
+ data.tar.gz: 24781a89995cf1384962fb46b5a60d8ed5095e442af92162a85e5e82489007ee
5
+ SHA512:
6
+ metadata.gz: a2584238466e94fc1efba40198f83761464f6bf399b9e99b8b640eb21a074a13699fe18bb42a4c1be049e1d0ccdb5950d721ce1342c04f1bfbc277f4195be189
7
+ data.tar.gz: f6e4ac759352ba03f7a21a283f3e43eb4202f0fad8ddd7125b1024713fe96bdac3b283a9bc3f5555495005c96bc92b5438307f55cf1b318556b79674c7143f32
data/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## 0.1.0
9
+
10
+ ### Added
11
+
12
+ - Initial code
data/LICENSE.txt ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2023 SOFware LLC
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # ChatNotifier
2
+
3
+ Notify a chat room with data from your test run.
4
+
5
+ ## Installation
6
+
7
+ ```
8
+ gem "chat_notifier", git: "https://github.com/SOFware/chat_notifier.git"
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ Add to your `spec_helper.rb` or `rails_helper.rb`:
14
+
15
+ ```
16
+ require "chat_notifier/rspec_formatter"
17
+ ```
18
+
19
+ ### Debug your Slack setup
20
+
21
+ Create rake task to test the connection to your Slack channel
22
+
23
+ ```ruby
24
+ namespace :chat_notifier do
25
+ task debug: :environment do
26
+ unless ENV["SLACK_WEBHOOK_URL"]
27
+ puts "You MUST set the environment variables for:\nSLACK_WEBHOOK_URL"
28
+ return
29
+ end
30
+ ENV["DEBUG"] = "1"
31
+ ENV["CURRENT_REPOSITORY_URL"] = "https://example.com"
32
+ ENV["TEST_RUN_ID"] = "9999"
33
+ require "chat_notifier"
34
+
35
+ failure = ChatNotifier::DebugExceptionLocation.new(location: "fake/path.rb")
36
+ summary = ChatNotifier::DebugSummary.new(failed_examples: [failure])
37
+
38
+ ChatNotifier.debug!(ENV, summary:)
39
+ end
40
+ end
41
+ ```
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ChatNotifier
4
+ class Chatter
5
+ class Debug < Slack
6
+ Chatter.register self
7
+
8
+ class << self
9
+ def handles?(settings)
10
+ !settings.keys.grep(/DEBUG/).empty?
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ChatNotifier
4
+ class Chatter
5
+ class Slack < self
6
+ Chatter.register self
7
+
8
+ class << self
9
+ def handles?(settings)
10
+ !settings.keys.grep(/SLACK/).empty?
11
+ end
12
+ end
13
+
14
+ def webhook_url
15
+ settings.fetch("SLACK_WEBHOOK_URL", nil)
16
+ end
17
+
18
+ def channel
19
+ settings.fetch("SLACK_NOTIFY_CHANNEL", nil)
20
+ end
21
+
22
+ def payload(data)
23
+ super(Configuration.for(data, self).to_h)
24
+ end
25
+
26
+ class Configuration
27
+ def self.for(messenger, communicator)
28
+ if messenger.failure?
29
+ FailureConfiguration
30
+ else
31
+ self
32
+ end.new(messenger, communicator).to_h
33
+ end
34
+
35
+ def initialize(messenger, communicator)
36
+ @messenger = messenger
37
+ @communicator = communicator
38
+ end
39
+ attr_reader :messenger, :communicator
40
+
41
+ def icon_emoji = ":green_circle:"
42
+
43
+ def to_h
44
+ {
45
+ channel: communicator.channel,
46
+ icon_emoji: icon_emoji
47
+ }.merge(messenger.to_h)
48
+ end
49
+
50
+ class FailureConfiguration < self
51
+ def icon_emoji = ":red_circle:"
52
+ end
53
+
54
+ private_constant :FailureConfiguration
55
+ end
56
+ private_constant :Configuration
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "uri"
5
+ require "json"
6
+
7
+ module ChatNotifier
8
+ # All behavior for interacting with a notification platform
9
+ class Chatter
10
+ def initialize(settings:, repository:, environment:)
11
+ @settings = settings
12
+ @repository = repository
13
+ @environment = environment
14
+ end
15
+
16
+ attr :settings, :repository, :environment
17
+
18
+ class << self
19
+ def handlers
20
+ @handlers ||= []
21
+ end
22
+
23
+ def handles?
24
+ false
25
+ end
26
+
27
+ def register(klass)
28
+ handlers << klass
29
+ end
30
+
31
+ def handling(settings, repository:, environment:)
32
+ handlers.select { |handler| handler.handles?(settings) }.map do |klass|
33
+ klass.new(
34
+ settings: settings,
35
+ repository: repository,
36
+ environment: environment
37
+ )
38
+ end
39
+ end
40
+ end
41
+
42
+ def webhook_url
43
+ end
44
+
45
+ def body
46
+ end
47
+
48
+ def post(messenger)
49
+ uri = URI(webhook_url)
50
+
51
+ Net::HTTP.post(uri, payload(messenger))
52
+ end
53
+
54
+ def conditional_post(messenger)
55
+ return if messenger.success? && !verbose?
56
+
57
+ post(messenger)
58
+ end
59
+
60
+ def verbose?
61
+ !!settings.fetch("NOTIFIER_VERBOSE", false)
62
+ end
63
+
64
+ def payload(data)
65
+ data.to_json
66
+ end
67
+ end
68
+ end
69
+
70
+ require_relative "chatter/slack"
71
+ require_relative "chatter/debug"
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ module ChatNotifier
6
+ class Messenger
7
+ extend Forwardable
8
+
9
+ class << self
10
+ def for(summary, repository:, environment:, app:)
11
+ if summary.failed_examples.empty?
12
+ self
13
+ else
14
+ Failure
15
+ end.new(
16
+ summary:,
17
+ app:,
18
+ repository:,
19
+ environment:
20
+ )
21
+ end
22
+
23
+ def debug=(val)
24
+ prepend Debug if val
25
+ end
26
+ end
27
+
28
+ module Debug
29
+ def message = "This is only a test…"
30
+ end
31
+
32
+ def initialize(summary:, app:, repository:, environment:)
33
+ @summary = summary
34
+ @app = app
35
+ @repository = repository
36
+ @environment = environment
37
+ end
38
+
39
+ attr :summary, :app, :repository, :environment
40
+
41
+ def failures = summary.failed_examples
42
+
43
+ def_delegator :failures, :count
44
+ def_delegators :app, :branch, :sha
45
+ def_delegator :environment, :ruby_version
46
+
47
+ def message
48
+ "#{message_prefix} #{identifier} is OK on branch #{repository.link(branch)}"
49
+ end
50
+
51
+ def lede = message
52
+
53
+ def body = ""
54
+
55
+ def identifier
56
+ "#{app} #{ruby_version} #{sha}"
57
+ end
58
+
59
+ def message_prefix = ":thumbsup:"
60
+
61
+ def success? = true
62
+
63
+ def failure? = !success?
64
+
65
+ def to_h
66
+ {
67
+ text: message
68
+ }
69
+ end
70
+
71
+ def to_hash = to_h
72
+
73
+ class Failure < self
74
+ def count = "#{failures.size} times!"
75
+
76
+ def message_prefix = ":boom:"
77
+
78
+ def success? = false
79
+
80
+ def failure? = !success?
81
+
82
+ def lede
83
+ <<~LEDE.chomp
84
+ #{message_prefix} #{identifier} has failed #{count} in #{branch}
85
+
86
+ #{environment.test_run_url}
87
+ LEDE
88
+ end
89
+
90
+ def message
91
+ <<~MESSAGE.chomp
92
+ #{lede}
93
+
94
+ #{body}
95
+ MESSAGE
96
+ end
97
+
98
+ def body
99
+ failures.flat_map(&:location).join("\n")
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ChatNotifier
4
+ class Repository
5
+ class Debug < self
6
+ def url
7
+ "http://example.com"
8
+ end
9
+
10
+ def link(sha)
11
+ "#{url}/tree/#{sha}"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ChatNotifier
4
+ class Repository
5
+ class Github < self
6
+ def url
7
+ settings.fetch("CURRENT_REPOSITORY_URL", nil)
8
+ end
9
+
10
+ def link(sha)
11
+ "#{url}/tree/#{sha}"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ChatNotifier
4
+ # All information about the location of the source code
5
+ class Repository
6
+ def initialize(settings:)
7
+ @settings = settings
8
+ end
9
+
10
+ attr_reader :settings
11
+
12
+ def self.for(settings)
13
+ if settings["DEBUG"]
14
+ Debug
15
+ else
16
+ Github
17
+ end.new(settings: settings)
18
+ end
19
+
20
+ def link(_sha)
21
+ end
22
+ end
23
+ end
24
+
25
+ require_relative "repository/github"
26
+ require_relative "repository/debug"
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rspec/core/formatters"
4
+ RSpec::Support.require_rspec_core "formatters/base_formatter"
5
+
6
+ require "chat_notifier"
7
+
8
+ module ChatNotifier
9
+ # Formatter for RSpec tests to receive the summary of a test
10
+ # run after the suite has finished.
11
+ class RspecFormatter < RSpec::Core::Formatters::BaseFormatter
12
+ RSpec::Core::Formatters.register self, :dump_summary
13
+
14
+ def dump_summary(summary)
15
+ ChatNotifier.call(summary:)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ChatNotifier
4
+ class TestEnvironment
5
+ class Debug < self
6
+ def url
7
+ "http://example.com"
8
+ end
9
+
10
+ def test_run_url
11
+ "#{url}/test-run/9999999"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ChatNotifier
4
+ class TestEnvironment
5
+ class Github < self
6
+ def test_run_url
7
+ "#{url}/actions/runs/#{run_id}"
8
+ end
9
+
10
+ def run_id
11
+ settings.fetch("TEST_RUN_ID")
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ChatNotifier
4
+ # All information about the place where the test was run
5
+ class TestEnvironment
6
+ def initialize(settings:)
7
+ @settings = settings
8
+ end
9
+
10
+ attr_reader :settings
11
+
12
+ def self.for(settings)
13
+ if settings["DEBUG"]
14
+ Debug
15
+ else
16
+ Github
17
+ end.new(settings: settings)
18
+ end
19
+
20
+ def ruby_version
21
+ "Ruby #{::RUBY_VERSION}"
22
+ end
23
+
24
+ def url
25
+ settings.fetch("CURRENT_REPOSITORY_URL")
26
+ end
27
+
28
+ def test_run_url
29
+ end
30
+ end
31
+ end
32
+
33
+ require_relative "test_environment/github"
34
+ require_relative "test_environment/debug"
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ChatNotifier
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "chat_notifier/version"
4
+
5
+ require_relative "chat_notifier/messenger"
6
+ require_relative "chat_notifier/repository"
7
+ require_relative "chat_notifier/test_environment"
8
+ require_relative "chat_notifier/chatter"
9
+
10
+ module ChatNotifier
11
+ DebugExceptionLocation = Data.define(:location)
12
+ DebugSummary = Data.define(:failed_examples)
13
+
14
+ class << self
15
+ # In order to test this locally see `rake chat_notifier:debug`
16
+ def debug!(env, summary:, notifier: :Debug)
17
+ repository = Repository.for(env)
18
+ environment = TestEnvironment.for(env)
19
+
20
+ chatter = Chatter.const_get(notifier).new(
21
+ settings: env,
22
+ repository: repository,
23
+ environment: environment
24
+ )
25
+ messenger = Messenger.for(
26
+ summary,
27
+ app: Rails.application.class.module_parent,
28
+ repository: repository,
29
+ environment: environment
30
+ )
31
+
32
+ chatter.post(messenger)
33
+ end
34
+
35
+ def call(summary:)
36
+ repository = Repository.for(ENV)
37
+ environment = TestEnvironment.for(ENV)
38
+ chatter = Chatter.handling(
39
+ ENV,
40
+ repository:,
41
+ environment:
42
+ )
43
+
44
+ messenger = Messenger.for(
45
+ summary,
46
+ app: Rails.application.class.module_parent,
47
+ repository:,
48
+ environment:
49
+ )
50
+
51
+ chatter.each do |box|
52
+ box.conditional_post(messenger)
53
+ end
54
+ end
55
+ end
56
+ end
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chat_notifier
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jim Gay
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-04-19 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Send test results to chat
14
+ email:
15
+ - jim@saturnflyer.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - CHANGELOG.md
21
+ - LICENSE.txt
22
+ - README.md
23
+ - lib/chat_notifier.rb
24
+ - lib/chat_notifier/chatter.rb
25
+ - lib/chat_notifier/chatter/debug.rb
26
+ - lib/chat_notifier/chatter/slack.rb
27
+ - lib/chat_notifier/messenger.rb
28
+ - lib/chat_notifier/repository.rb
29
+ - lib/chat_notifier/repository/debug.rb
30
+ - lib/chat_notifier/repository/github.rb
31
+ - lib/chat_notifier/rspec_formatter.rb
32
+ - lib/chat_notifier/test_environment.rb
33
+ - lib/chat_notifier/test_environment/debug.rb
34
+ - lib/chat_notifier/test_environment/github.rb
35
+ - lib/chat_notifier/version.rb
36
+ homepage:
37
+ licenses: []
38
+ metadata:
39
+ source_code_uri: https://github.com/SOFware/chat_notifier.git
40
+ changelog_uri: https://github.com/SOFWare/chat_notifier/blob/master/CHANGELOG.md
41
+ post_install_message:
42
+ rdoc_options: []
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 3.2.0
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ requirements: []
56
+ rubygems_version: 3.4.6
57
+ signing_key:
58
+ specification_version: 4
59
+ summary: Notify chat of test results
60
+ test_files: []