From 0803a4b80f2900af4ca6a2ee97e6a133b0fd4d50 Mon Sep 17 00:00:00 2001 From: Brooke Angel Date: Thu, 3 Jan 2019 10:28:11 -0800 Subject: [PATCH 01/32] add slack-notifier testing --- Gemfile | 4 ++++ Gemfile.lock | 2 ++ 2 files changed, 6 insertions(+) diff --git a/Gemfile b/Gemfile index eb9c0de..5698791 100644 --- a/Gemfile +++ b/Gemfile @@ -9,6 +9,10 @@ gem 'pry' gem 'pry-doc' gem 'rubocop' +group :development do + gem 'slack-notifier' +end + group :test do gem 'rspec' gem 'rspec-mocks' diff --git a/Gemfile.lock b/Gemfile.lock index 9ba5a68..9712f35 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -57,6 +57,7 @@ GEM sawyer (0.8.1) addressable (>= 2.3.5, < 2.6) faraday (~> 0.8, < 1.0) + slack-notifier (2.3.2) unicode-display_width (1.4.0) yard (0.9.16) @@ -72,6 +73,7 @@ DEPENDENCIES rspec rspec-mocks rubocop + slack-notifier BUNDLED WITH 1.17.1 From 736975f0dacc0494933f6e7bdfa785627ffdde80 Mon Sep 17 00:00:00 2001 From: Brooke Angel Date: Thu, 3 Jan 2019 10:28:25 -0800 Subject: [PATCH 02/32] WIP: format header --- exe/deploy-complexity.rb | 76 +++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 20 deletions(-) diff --git a/exe/deploy-complexity.rb b/exe/deploy-complexity.rb index a88be85..e67bc69 100755 --- a/exe/deploy-complexity.rb +++ b/exe/deploy-complexity.rb @@ -50,6 +50,27 @@ def reference(name) end end +def format_header(to:, revision:) + "*Deploy tag #{to} [#{revision}]*" +end + +def format_summary_stats(commits:, pull_requests:, merges:, time_delta:, shortstat:) + return if commits.empty? + + summary_stats = "%d pull requests of %d merges, %d commits %s\n" % + [pull_requests.count, merges.count, commits.count, time_delta] + + return summary_stats if shortstat.empty? + + summary_stats + shortstat.first.strip +end + +def format_compare_url(commits:, base:, to:, gh_url:) + return if commits.empty? + + COMPARE_FORMAT % [gh_url, reference(base), reference(to)] +end + def pull_requests(merges, gh_url) prs = merges.map do |line| line.match(/pull request #(\d+) from (.*)$/) do |m| @@ -115,26 +136,41 @@ def deploy(base, to, options) pull_requests = pull_requests(merges, gh_url) - puts "Deploy tag %s [%s]" % [to, revision] - if !commits.empty? - puts "%d pull requests of %d merges, %d commits %s" % - [pull_requests.count, merges.count, commits.count, time_delta] - puts shortstat.first.strip unless shortstat.empty? - puts COMPARE_FORMAT % [gh_url, reference(base), reference(to)] - puts - file_changes(ChangedFiles.new(names_only, versioned_url), base: base, to: to) - if pull_requests.any? - # FIXME: there may be commits in the deploy unassociated with a PR - puts "Pull Requests:", pull_requests - else - puts "Commits:", commits - end - puts "Dirstats:", dirstat if dirstat - puts "Stats:", stat if stat - else - puts "redeployed %s %s" % [base, time_delta] - end - puts + # if !commits.empty? + # puts "%d pull requests of %d merges, %d commits %s" % + # [pull_requests.count, merges.count, commits.count, time_delta] + # puts shortstat.first.strip unless shortstat.empty? + # puts COMPARE_FORMAT % [gh_url, reference(base), reference(to)] + # list_migrations(changed_files) + # list_changed_elm_dependencies(changed_files, base: base, to: to) + # list_changed_javascript_dependencies(changed_files, base: base, to: to) + # list_changed_ruby_dependencies(changed_files, base: base, to: to) + # if pull_requests.any? + # # FIXME: there may be commits in the deploy unassociated with a PR + # puts "Pull Requests:", pull_requests + # else + # puts "Commits:", commits + # end + # puts "Dirstats:", dirstat if dirstat + # puts "Stats:", stat if stat + # else + # puts "redeployed %s %s" % [base, time_delta] + # end + # puts + + text = [ + format_header(to: to, revision: revision), + format_summary_stats( + commits: commits, + pull_requests: pull_requests, + merges: merges, + shortstat: shortstat, + time_delta: time_delta + ), + format_compare_url(commits: commits, base: base, to: to, gh_url: gh_url) + ].compact.join("\n") + + puts text end branch = "production" From f130e1a0d2d3a4f0da579e17da48b85fc44a20c0 Mon Sep 17 00:00:00 2001 From: Brooke Angel Date: Thu, 3 Jan 2019 11:15:13 -0800 Subject: [PATCH 03/32] move text formatting to SlackFormatter module --- Gemfile | 1 + Gemfile.lock | 2 + exe/deploy-complexity.rb | 48 ++++++-------------- lib/deploy_complexity/slack_formatter.rb | 57 ++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 34 deletions(-) create mode 100644 lib/deploy_complexity/slack_formatter.rb diff --git a/Gemfile b/Gemfile index 5698791..f7dcc49 100644 --- a/Gemfile +++ b/Gemfile @@ -8,6 +8,7 @@ gemspec gem 'pry' gem 'pry-doc' gem 'rubocop' +gem 'values' group :development do gem 'slack-notifier' diff --git a/Gemfile.lock b/Gemfile.lock index 9712f35..94e1788 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -59,6 +59,7 @@ GEM faraday (~> 0.8, < 1.0) slack-notifier (2.3.2) unicode-display_width (1.4.0) + values (1.8.0) yard (0.9.16) PLATFORMS @@ -74,6 +75,7 @@ DEPENDENCIES rspec-mocks rubocop slack-notifier + values BUNDLED WITH 1.17.1 diff --git a/exe/deploy-complexity.rb b/exe/deploy-complexity.rb index e67bc69..dc34934 100755 --- a/exe/deploy-complexity.rb +++ b/exe/deploy-complexity.rb @@ -3,6 +3,7 @@ require 'time' require 'bundler/setup' +require 'deploy_complexity/slack_formatter' require 'deploy_complexity/version' require 'deploy_complexity/revision_comparator' require 'deploy_complexity/changed_files' @@ -50,27 +51,6 @@ def reference(name) end end -def format_header(to:, revision:) - "*Deploy tag #{to} [#{revision}]*" -end - -def format_summary_stats(commits:, pull_requests:, merges:, time_delta:, shortstat:) - return if commits.empty? - - summary_stats = "%d pull requests of %d merges, %d commits %s\n" % - [pull_requests.count, merges.count, commits.count, time_delta] - - return summary_stats if shortstat.empty? - - summary_stats + shortstat.first.strip -end - -def format_compare_url(commits:, base:, to:, gh_url:) - return if commits.empty? - - COMPARE_FORMAT % [gh_url, reference(base), reference(to)] -end - def pull_requests(merges, gh_url) prs = merges.map do |line| line.match(/pull request #(\d+) from (.*)$/) do |m| @@ -158,19 +138,19 @@ def deploy(base, to, options) # end # puts - text = [ - format_header(to: to, revision: revision), - format_summary_stats( - commits: commits, - pull_requests: pull_requests, - merges: merges, - shortstat: shortstat, - time_delta: time_delta - ), - format_compare_url(commits: commits, base: base, to: to, gh_url: gh_url) - ].compact.join("\n") - - puts text + formatter = SlackFormatter.with( + to: to, + base: base, + revision: revision, + commits: commits, + pull_requests: pull_requests, + merges: merges, + shortstat: shortstat, + time_delta: time_delta, + gh_url: gh_url + ) + + puts formatter.format end branch = "production" diff --git a/lib/deploy_complexity/slack_formatter.rb b/lib/deploy_complexity/slack_formatter.rb new file mode 100644 index 0000000..f1322f8 --- /dev/null +++ b/lib/deploy_complexity/slack_formatter.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require 'values' + +# Formats deploy complexity output for slack +class SlackFormatter < + Value.new( + :to, + :base, + :revision, + :commits, + :pull_requests, + :merges, + :shortstat, + :time_delta, + :gh_url + ) + + COMPARE_FORMAT = "%s/compare/%s...%s" + + def format + text = [header] + + if commits.empty? + text << "redeployed %s %s" % [base, time_delta] + else + text << summary_stats + text << compare_url + text << shortstats + end + + text.compact.join("\n") + end + + private + + attr_reader :deploy_data + + def header + "*Deploy tag #{to} [#{revision}]*" + end + + def summary_stats + "%d pull requests of %d merges, %d commits %s" % + [pull_requests.count, merges.count, commits.count, time_delta] + end + + def shortstats + return if shortstat.empty? + + shortstat.first.strip + end + + def compare_url + COMPARE_FORMAT % [gh_url, reference(base), reference(to)] + end +end From 2c5270e9a511357a0e0d44def01618dacd7bb501 Mon Sep 17 00:00:00 2001 From: Brooke Angel Date: Fri, 4 Jan 2019 11:28:57 -0800 Subject: [PATCH 04/32] remove rubocop enable comments --- exe/deploy-complexity.rb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/exe/deploy-complexity.rb b/exe/deploy-complexity.rb index dc34934..212a296 100755 --- a/exe/deploy-complexity.rb +++ b/exe/deploy-complexity.rb @@ -222,10 +222,3 @@ def deploy(base, to, options) else abort(optparse.to_s) end - -# rubocop:enable Style/FormatStringToken -# rubocop:enable Metrics/AbcSize -# rubocop:enable Metrics/CyclomaticComplexity -# rubocop:enable Metrics/PerceivedComplexity -# rubocop:enable Metrics/MethodLength -# rubocop:enable Metrics/BlockLength From 58df2682a265cc4c8f6bb55a8be12a1653ef38ae Mon Sep 17 00:00:00 2001 From: Brooke Angel Date: Fri, 4 Jan 2019 12:00:52 -0800 Subject: [PATCH 05/32] allow formatting for slack or CLI --- exe/deploy-complexity.rb | 13 +++- lib/deploy_complexity/output_formatter.rb | 77 +++++++++++++++++++++++ lib/deploy_complexity/slack_formatter.rb | 57 ----------------- 3 files changed, 87 insertions(+), 60 deletions(-) create mode 100644 lib/deploy_complexity/output_formatter.rb delete mode 100644 lib/deploy_complexity/slack_formatter.rb diff --git a/exe/deploy-complexity.rb b/exe/deploy-complexity.rb index 212a296..5a7b58c 100755 --- a/exe/deploy-complexity.rb +++ b/exe/deploy-complexity.rb @@ -3,7 +3,7 @@ require 'time' require 'bundler/setup' -require 'deploy_complexity/slack_formatter' +require 'deploy_complexity/output_formatter' require 'deploy_complexity/version' require 'deploy_complexity/revision_comparator' require 'deploy_complexity/changed_files' @@ -94,6 +94,7 @@ def deploy(base, to, options) gh_url = options[:gh_url] dirstat = options[:dirstat] stat = options[:stat] + slack_format = options[:slack_format] range = "#{base}...#{to}" @@ -138,7 +139,7 @@ def deploy(base, to, options) # end # puts - formatter = SlackFormatter.with( + formatter = DeployComplexity::OutputFormatter.with( to: to, base: base, revision: revision, @@ -150,7 +151,11 @@ def deploy(base, to, options) gh_url: gh_url ) - puts formatter.format + if slack_format + puts formatter.format_for_slack + else + puts formatter.format_for_cli + end end branch = "production" @@ -183,6 +188,8 @@ def deploy(base, to, options) "Github project url to construct links from") do |url| options[:gh_url] = url end + opts.on("--slack-format", + "Format output for Slack") { options[:slack_format] = true } opts.on_tail("-v", "--version", "Show version info and exit") do abort <<~BOILERPLATE deploy-complexity.rb #{DeployComplexity::VERSION} diff --git a/lib/deploy_complexity/output_formatter.rb b/lib/deploy_complexity/output_formatter.rb new file mode 100644 index 0000000..8237ca3 --- /dev/null +++ b/lib/deploy_complexity/output_formatter.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +require 'values' + +module DeployComplexity + # Formats deploy complexity output + class OutputFormatter < + Value.new( + :to, + :base, + :revision, + :commits, + :pull_requests, + :merges, + :shortstat, + :time_delta, + :gh_url + ) + + def format_for_slack + text = [] + + text << "*#{header}*" + + if commits.empty? + text << empty_commit_message + else + text << summary_stats + text << compare_url + text << shortstats + end + + text.compact.join("\n") + end + + def format_for_cli + text = [] + + text << header + + if commits.empty? + text << empty_commit_message + else + text << summary_stats + text << compare_url + text << shortstats + end + + text.compact.join("\n") + end + + private + + attr_reader :deploy_data + + def empty_commit_message; end + + def header + "Deploy tag #{to} [#{revision}]" + end + + def summary_stats + "%d pull requests of %d merges, %d commits %s" % + [pull_requests.count, merges.count, commits.count, time_delta] + end + + def shortstats + return if shortstat.empty? + + shortstat.first.strip + end + + def compare_url + "%s/compare/%s...%s" % [gh_url, reference(base), reference(to)] + end + end +end diff --git a/lib/deploy_complexity/slack_formatter.rb b/lib/deploy_complexity/slack_formatter.rb deleted file mode 100644 index f1322f8..0000000 --- a/lib/deploy_complexity/slack_formatter.rb +++ /dev/null @@ -1,57 +0,0 @@ -# frozen_string_literal: true - -require 'values' - -# Formats deploy complexity output for slack -class SlackFormatter < - Value.new( - :to, - :base, - :revision, - :commits, - :pull_requests, - :merges, - :shortstat, - :time_delta, - :gh_url - ) - - COMPARE_FORMAT = "%s/compare/%s...%s" - - def format - text = [header] - - if commits.empty? - text << "redeployed %s %s" % [base, time_delta] - else - text << summary_stats - text << compare_url - text << shortstats - end - - text.compact.join("\n") - end - - private - - attr_reader :deploy_data - - def header - "*Deploy tag #{to} [#{revision}]*" - end - - def summary_stats - "%d pull requests of %d merges, %d commits %s" % - [pull_requests.count, merges.count, commits.count, time_delta] - end - - def shortstats - return if shortstat.empty? - - shortstat.first.strip - end - - def compare_url - COMPARE_FORMAT % [gh_url, reference(base), reference(to)] - end -end From 4400ba6185b584886bda0fad9189e228afdc68bc Mon Sep 17 00:00:00 2001 From: Brooke Angel Date: Fri, 4 Jan 2019 16:04:46 -0800 Subject: [PATCH 06/32] hackily send attachements to slack - fixme --- lib/deploy_complexity/output_formatter.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/deploy_complexity/output_formatter.rb b/lib/deploy_complexity/output_formatter.rb index 8237ca3..11ec47a 100644 --- a/lib/deploy_complexity/output_formatter.rb +++ b/lib/deploy_complexity/output_formatter.rb @@ -30,7 +30,10 @@ def format_for_slack text << shortstats end - text.compact.join("\n") + { + text: text.compact.join("\n"), + attachments: [] + } end def format_for_cli From 17321d6fb1a6e7d029875638c72d4e2fb6d445be Mon Sep 17 00:00:00 2001 From: Brooke Angel Date: Fri, 4 Jan 2019 16:15:32 -0800 Subject: [PATCH 07/32] add formatter spec and output correctly with commits --- exe/deploy-complexity.rb | 4 +- lib/deploy_complexity/output_formatter.rb | 8 +- .../output_formatter_spec.rb | 76 +++++++++++++++++++ 3 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 spec/lib/deploy_complexity/output_formatter_spec.rb diff --git a/exe/deploy-complexity.rb b/exe/deploy-complexity.rb index 5a7b58c..b97b4df 100755 --- a/exe/deploy-complexity.rb +++ b/exe/deploy-complexity.rb @@ -148,7 +148,9 @@ def deploy(base, to, options) merges: merges, shortstat: shortstat, time_delta: time_delta, - gh_url: gh_url + gh_url: gh_url, + base_reference: reference(base), + to_reference: reference(to) ) if slack_format diff --git a/lib/deploy_complexity/output_formatter.rb b/lib/deploy_complexity/output_formatter.rb index 11ec47a..aaf244f 100644 --- a/lib/deploy_complexity/output_formatter.rb +++ b/lib/deploy_complexity/output_formatter.rb @@ -8,6 +8,8 @@ class OutputFormatter < Value.new( :to, :base, + :base_reference, + :to_reference, :revision, :commits, :pull_requests, @@ -56,7 +58,9 @@ def format_for_cli attr_reader :deploy_data - def empty_commit_message; end + def empty_commit_message + "redeployed %s %s" % [base, time_delta] + end def header "Deploy tag #{to} [#{revision}]" @@ -74,7 +78,7 @@ def shortstats end def compare_url - "%s/compare/%s...%s" % [gh_url, reference(base), reference(to)] + "%s/compare/%s...%s" % [gh_url, base_reference, to_reference] end end end diff --git a/spec/lib/deploy_complexity/output_formatter_spec.rb b/spec/lib/deploy_complexity/output_formatter_spec.rb new file mode 100644 index 0000000..dd3d3f2 --- /dev/null +++ b/spec/lib/deploy_complexity/output_formatter_spec.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +require 'deploy_complexity/output_formatter' + +describe DeployComplexity::OutputFormatter do + context "with no commits" do + let(:formatter) do + DeployComplexity::OutputFormatter.with( + to: "to_commit", + base: "base_commit", + revision: "aaaa", + commits: [], + pull_requests: [], + merges: [], + shortstat: "", + time_delta: "0 nanoseconds", + gh_url: "example.com", + base_reference: "base_ref", + to_reference: "to_ref" + ) + end + + it "formats for the CLI" do + expect(formatter.format_for_cli).to eq( + "Deploy tag to_commit [aaaa]\nredeployed base_commit 0 nanoseconds" + ) + end + + it "formats for Slack" do + expect(formatter.format_for_slack).to eq( + text: "*Deploy tag to_commit [aaaa]*\nredeployed base_commit 0 nanoseconds", + attachments: [] + ) + end + end + + context "with commits" do + let(:formatter) do + DeployComplexity::OutputFormatter.with( + to: "to_commit", + base: "base_commit", + revision: "aaaa", + commits: [ + "bbbb Do the thing", + "cccc Do the thing again" + ], + pull_requests: [], + merges: [], + shortstat: "", + time_delta: "0 nanoseconds", + gh_url: "example.com", + base_reference: "base_ref", + to_reference: "to_ref" + ) + end + + it "formats for the CLI" do + expect(formatter.format_for_cli).to eq(<<-TXT.gsub(/^\s+/, "").chomp) + Deploy tag to_commit [aaaa] + 0 pull requests of 0 merges, 2 commits 0 nanoseconds + example.com/compare/base_ref...to_ref + TXT + end + + it "formats for Slack" do + expect(formatter.format_for_slack).to eq( + text: <<-TXT.gsub(/^\s+/, "").chomp, + *Deploy tag to_commit [aaaa]* + 0 pull requests of 0 merges, 2 commits 0 nanoseconds + example.com/compare/base_ref...to_ref + TXT + attachments: [] + ) + end + end +end From 3023b70cc4d123d112e017d8d9bd4d2a00e68868 Mon Sep 17 00:00:00 2001 From: Brooke Angel Date: Fri, 4 Jan 2019 17:02:41 -0800 Subject: [PATCH 08/32] format migrations in deploy complexity output --- exe/deploy-complexity.rb | 13 +++------- lib/deploy_complexity/output_formatter.rb | 24 +++++++++++++++++-- .../output_formatter_spec.rb | 24 ++++++++++++++++--- 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/exe/deploy-complexity.rb b/exe/deploy-complexity.rb index b97b4df..bb04f57 100755 --- a/exe/deploy-complexity.rb +++ b/exe/deploy-complexity.rb @@ -62,15 +62,6 @@ def pull_requests(merges, gh_url) prs.compact.map { |x| "%s/pull/%d %1s %s" % x } end -def list_migrations(changed_files) - migrations = changed_files.migrations - return unless migrations.any? - - puts "Migrations:" - puts migrations - puts -end - def file_changes(changed_files, base:, to:) list_migrations(changed_files) @@ -109,6 +100,7 @@ def deploy(base, to, options) shortstat = `git diff --shortstat --summary #{range}`.split(/\n/) names_only = `git diff --name-only #{range}` versioned_url = "#{gh_url}/blob/#{safe_name(to)}/" + changed_files = ChangedFiles.new(names_only, versioned_url) dirstat = `git diff --dirstat=lines,cumulative #{range}` if dirstat # TODO: investigate summarizing language / spec content based on file suffix, @@ -150,7 +142,8 @@ def deploy(base, to, options) time_delta: time_delta, gh_url: gh_url, base_reference: reference(base), - to_reference: reference(to) + to_reference: reference(to), + migrations: changed_files.migrations ) if slack_format diff --git a/lib/deploy_complexity/output_formatter.rb b/lib/deploy_complexity/output_formatter.rb index aaf244f..652f28a 100644 --- a/lib/deploy_complexity/output_formatter.rb +++ b/lib/deploy_complexity/output_formatter.rb @@ -16,11 +16,14 @@ class OutputFormatter < :merges, :shortstat, :time_delta, - :gh_url + :gh_url, + :migrations ) + # TODO: consider moving slack/cli formatting to separate child classes def format_for_slack text = [] + attachments = [] text << "*#{header}*" @@ -30,11 +33,12 @@ def format_for_slack text << summary_stats text << compare_url text << shortstats + attachments << format_migrations_for_slack if migrations.any? end { text: text.compact.join("\n"), - attachments: [] + attachments: attachments } end @@ -49,6 +53,8 @@ def format_for_cli text << summary_stats text << compare_url text << shortstats + text << shortstats + text << format_migrations_for_cli if migrations.any? end text.compact.join("\n") @@ -80,5 +86,19 @@ def shortstats def compare_url "%s/compare/%s...%s" % [gh_url, base_reference, to_reference] end + + def format_migrations_for_slack + { + title: "Migrations", + text: migrations.join("\n"), + color: "#E6E6FA" + } + end + + def format_migrations_for_cli + text = "Migrations:\n" + + text + migrations.join("\n") + end end end diff --git a/spec/lib/deploy_complexity/output_formatter_spec.rb b/spec/lib/deploy_complexity/output_formatter_spec.rb index dd3d3f2..fa65a13 100644 --- a/spec/lib/deploy_complexity/output_formatter_spec.rb +++ b/spec/lib/deploy_complexity/output_formatter_spec.rb @@ -16,7 +16,8 @@ time_delta: "0 nanoseconds", gh_url: "example.com", base_reference: "base_ref", - to_reference: "to_ref" + to_reference: "to_ref", + migrations: [] ) end @@ -50,7 +51,11 @@ time_delta: "0 nanoseconds", gh_url: "example.com", base_reference: "base_ref", - to_reference: "to_ref" + to_reference: "to_ref", + migrations: [ + "example.com/migrate_awayyyy", + "example.com/migrate_awayyyy_again" + ] ) end @@ -59,6 +64,10 @@ Deploy tag to_commit [aaaa] 0 pull requests of 0 merges, 2 commits 0 nanoseconds example.com/compare/base_ref...to_ref + + Migrations: + example.com/migrate_awayyyy + example.com/migrate_awayyyy_again TXT end @@ -69,7 +78,16 @@ 0 pull requests of 0 merges, 2 commits 0 nanoseconds example.com/compare/base_ref...to_ref TXT - attachments: [] + attachments: [ + { + title: "Migrations", + text: <<-TXT.gsub(/^\s+/, "").chomp, + example.com/migrate_awayyyy + example.com/migrate_awayyyy_again + TXT + color: "#E6E6FA" + } + ] ) end end From 295d37658bfac3611d1561b0deb5c67c2bd167b8 Mon Sep 17 00:00:00 2001 From: Brooke Angel Date: Mon, 7 Jan 2019 11:57:17 -0800 Subject: [PATCH 09/32] add changed elm packages to output formatter --- exe/deploy-complexity.rb | 11 +-- lib/deploy_complexity/output_formatter.rb | 78 ++++++++++++------- lib/deploy_complexity/revision_comparator.rb | 19 ++--- .../output_formatter_spec.rb | 22 +++++- 4 files changed, 77 insertions(+), 53 deletions(-) diff --git a/exe/deploy-complexity.rb b/exe/deploy-complexity.rb index bb04f57..089eebb 100755 --- a/exe/deploy-complexity.rb +++ b/exe/deploy-complexity.rb @@ -63,12 +63,6 @@ def pull_requests(merges, gh_url) end def file_changes(changed_files, base:, to:) - list_migrations(changed_files) - - RevisionComparator.new( - ChangedElmPackages, changed_files.elm_packages, base, to - ).output("Changed Elm packages:") - RevisionComparator.new( ChangedRubyGems, changed_files.ruby_dependencies, base, to ).output("Ruby dependency changes:") @@ -143,7 +137,10 @@ def deploy(base, to, options) gh_url: gh_url, base_reference: reference(base), to_reference: reference(to), - migrations: changed_files.migrations + migrations: changed_files.migrations, + elm_packages: RevisionComparator.new( + ChangedElmPackages, changed_files.elm_packages, base, to + ).output ) if slack_format diff --git a/lib/deploy_complexity/output_formatter.rb b/lib/deploy_complexity/output_formatter.rb index 652f28a..c73e598 100644 --- a/lib/deploy_complexity/output_formatter.rb +++ b/lib/deploy_complexity/output_formatter.rb @@ -3,6 +3,8 @@ require 'values' module DeployComplexity + Attachment = Value.new(:title, :text, :color) + # Formats deploy complexity output class OutputFormatter < Value.new( @@ -17,32 +19,33 @@ class OutputFormatter < :shortstat, :time_delta, :gh_url, - :migrations + :migrations, + :elm_packages ) - # TODO: consider moving slack/cli formatting to separate child classes def format_for_slack - text = [] - attachments = [] - - text << "*#{header}*" - - if commits.empty? - text << empty_commit_message - else - text << summary_stats - text << compare_url - text << shortstats - attachments << format_migrations_for_slack if migrations.any? - end - { text: text.compact.join("\n"), - attachments: attachments + attachments: attachments.map { |a| format_attachment_for_slack(a) } } end def format_for_cli + output = text.compact.join("\n") + + added_attachments = attachments + + return output unless added_attachments.any? + + output + "\n" + + added_attachments.map { |a| format_attachment_for_cli(a) }.join("\n") + end + + private + + attr_reader :deploy_data + + def text text = [] text << header @@ -53,23 +56,36 @@ def format_for_cli text << summary_stats text << compare_url text << shortstats - text << shortstats - text << format_migrations_for_cli if migrations.any? end - text.compact.join("\n") + text end - private + def attachments + return [] if commits.empty? - attr_reader :deploy_data + attachments = [] + + attachments << migration_attachment if migrations.any? + attachments << elm_package_attachment if elm_packages.any? + + attachments + end + + def format_attachment_for_slack(attachment) + attachment.to_h + end + + def format_attachment_for_cli(attachment) + attachment.title + "\n" + attachment.text + end def empty_commit_message "redeployed %s %s" % [base, time_delta] end def header - "Deploy tag #{to} [#{revision}]" + "*Deploy tag #{to} [#{revision}]*" end def summary_stats @@ -87,18 +103,20 @@ def compare_url "%s/compare/%s...%s" % [gh_url, base_reference, to_reference] end - def format_migrations_for_slack - { + def migration_attachment + Attachment.with( title: "Migrations", text: migrations.join("\n"), color: "#E6E6FA" - } + ) end - def format_migrations_for_cli - text = "Migrations:\n" - - text + migrations.join("\n") + def elm_package_attachment + Attachment.with( + title: "Changed Elm Packages", + text: elm_packages.join("\n"), + color: "#FFB6C1" + ) end end end diff --git a/lib/deploy_complexity/revision_comparator.rb b/lib/deploy_complexity/revision_comparator.rb index e5f2425..7b2d06d 100644 --- a/lib/deploy_complexity/revision_comparator.rb +++ b/lib/deploy_complexity/revision_comparator.rb @@ -22,17 +22,12 @@ def changes end end - def output(title) - packages = changes - return unless packages.any? - - puts title - puts packages - puts - rescue StandardError => e - puts "Error parsing: #{title}" - puts e.message - puts e.backtrace - puts + def output + changes + # TODO: bring back error handling + # rescue StandardError => e + # puts "Error parsing: #{title}" + # puts e.message + # puts e.backtrace end end diff --git a/spec/lib/deploy_complexity/output_formatter_spec.rb b/spec/lib/deploy_complexity/output_formatter_spec.rb index fa65a13..50cf83a 100644 --- a/spec/lib/deploy_complexity/output_formatter_spec.rb +++ b/spec/lib/deploy_complexity/output_formatter_spec.rb @@ -17,13 +17,14 @@ gh_url: "example.com", base_reference: "base_ref", to_reference: "to_ref", - migrations: [] + migrations: [], + elm_packages: [] ) end it "formats for the CLI" do expect(formatter.format_for_cli).to eq( - "Deploy tag to_commit [aaaa]\nredeployed base_commit 0 nanoseconds" + "*Deploy tag to_commit [aaaa]*\nredeployed base_commit 0 nanoseconds" ) end @@ -55,19 +56,25 @@ migrations: [ "example.com/migrate_awayyyy", "example.com/migrate_awayyyy_again" + ], + elm_packages: [ + "elm/core: 1.1.0 -> 1.2.0" ] ) end it "formats for the CLI" do expect(formatter.format_for_cli).to eq(<<-TXT.gsub(/^\s+/, "").chomp) - Deploy tag to_commit [aaaa] + *Deploy tag to_commit [aaaa]* 0 pull requests of 0 merges, 2 commits 0 nanoseconds example.com/compare/base_ref...to_ref - Migrations: + Migrations example.com/migrate_awayyyy example.com/migrate_awayyyy_again + + Changed Elm Packages + elm/core: 1.1.0 -> 1.2.0 TXT end @@ -86,6 +93,13 @@ example.com/migrate_awayyyy_again TXT color: "#E6E6FA" + }, + { + title: "Changed Elm Packages", + text: <<-TXT.gsub(/^\s+/, "").chomp, + elm/core: 1.1.0 -> 1.2.0 + TXT + color: "#FFB6C1" } ] ) From 9155a1861efb6e18713a7b68b416e25402662817 Mon Sep 17 00:00:00 2001 From: Brooke Angel Date: Mon, 7 Jan 2019 12:01:26 -0800 Subject: [PATCH 10/32] add changed ruby dependencies to output formatter --- exe/deploy-complexity.rb | 7 +++---- lib/deploy_complexity/output_formatter.rb | 12 +++++++++++- .../deploy_complexity/output_formatter_spec.rb | 16 +++++++++++++++- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/exe/deploy-complexity.rb b/exe/deploy-complexity.rb index 089eebb..22acc08 100755 --- a/exe/deploy-complexity.rb +++ b/exe/deploy-complexity.rb @@ -63,10 +63,6 @@ def pull_requests(merges, gh_url) end def file_changes(changed_files, base:, to:) - RevisionComparator.new( - ChangedRubyGems, changed_files.ruby_dependencies, base, to - ).output("Ruby dependency changes:") - RevisionComparator.new( ChangedJavascriptPackages, changed_files.javascript_dependencies, base, to ).output("Javascript Dependency Changes:") @@ -140,6 +136,9 @@ def deploy(base, to, options) migrations: changed_files.migrations, elm_packages: RevisionComparator.new( ChangedElmPackages, changed_files.elm_packages, base, to + ).output, + ruby_dependencies: RevisionComparator.new( + ChangedRubyGems, changed_files.ruby_dependencies, base, to ).output ) diff --git a/lib/deploy_complexity/output_formatter.rb b/lib/deploy_complexity/output_formatter.rb index c73e598..9d5f8a1 100644 --- a/lib/deploy_complexity/output_formatter.rb +++ b/lib/deploy_complexity/output_formatter.rb @@ -20,7 +20,8 @@ class OutputFormatter < :time_delta, :gh_url, :migrations, - :elm_packages + :elm_packages, + :ruby_dependencies ) def format_for_slack @@ -68,6 +69,7 @@ def attachments attachments << migration_attachment if migrations.any? attachments << elm_package_attachment if elm_packages.any? + attachments << ruby_dependency_attachment if ruby_dependencies.any? attachments end @@ -118,5 +120,13 @@ def elm_package_attachment color: "#FFB6C1" ) end + + def ruby_dependency_attachment + Attachment.with( + title: "Changed Ruby Dependencies", + text: ruby_dependencies.join("\n"), + color: "#B6FFE0" + ) + end end end diff --git a/spec/lib/deploy_complexity/output_formatter_spec.rb b/spec/lib/deploy_complexity/output_formatter_spec.rb index 50cf83a..9dbd8e6 100644 --- a/spec/lib/deploy_complexity/output_formatter_spec.rb +++ b/spec/lib/deploy_complexity/output_formatter_spec.rb @@ -18,7 +18,8 @@ base_reference: "base_ref", to_reference: "to_ref", migrations: [], - elm_packages: [] + elm_packages: [], + ruby_dependencies: [] ) end @@ -59,6 +60,9 @@ ], elm_packages: [ "elm/core: 1.1.0 -> 1.2.0" + ], + ruby_dependencies: [ + "rspec: 3.1.0 -> 3.2.0" ] ) end @@ -75,6 +79,9 @@ Changed Elm Packages elm/core: 1.1.0 -> 1.2.0 + + Changed Ruby Dependencies + rspec: 3.1.0 -> 3.2.0 TXT end @@ -100,6 +107,13 @@ elm/core: 1.1.0 -> 1.2.0 TXT color: "#FFB6C1" + }, + { + title: "Changed Ruby Dependencies", + text: <<-TXT.gsub(/^\s+/, "").chomp, + rspec: 3.1.0 -> 3.2.0 + TXT + color: "#B6FFE0" } ] ) From ed1ab69aef87577b7176ed8108b700b90d81993e Mon Sep 17 00:00:00 2001 From: Brooke Angel Date: Mon, 7 Jan 2019 12:06:45 -0800 Subject: [PATCH 11/32] list javascript dependenices in output formatter --- exe/deploy-complexity.rb | 12 ++++-------- lib/deploy_complexity/output_formatter.rb | 12 +++++++++++- .../deploy_complexity/output_formatter_spec.rb | 16 +++++++++++++++- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/exe/deploy-complexity.rb b/exe/deploy-complexity.rb index 22acc08..4aa9f30 100755 --- a/exe/deploy-complexity.rb +++ b/exe/deploy-complexity.rb @@ -62,14 +62,6 @@ def pull_requests(merges, gh_url) prs.compact.map { |x| "%s/pull/%d %1s %s" % x } end -def file_changes(changed_files, base:, to:) - RevisionComparator.new( - ChangedJavascriptPackages, changed_files.javascript_dependencies, base, to - ).output("Javascript Dependency Changes:") - - # TODO: scan for changes to app/jobs and report changes to params -end - # deploys are the delta from base -> to, so to contains commits to add to base def deploy(base, to, options) gh_url = options[:gh_url] @@ -121,6 +113,7 @@ def deploy(base, to, options) # end # puts + # TODO: scan for changes to app/jobs and report changes to params formatter = DeployComplexity::OutputFormatter.with( to: to, base: base, @@ -139,6 +132,9 @@ def deploy(base, to, options) ).output, ruby_dependencies: RevisionComparator.new( ChangedRubyGems, changed_files.ruby_dependencies, base, to + ).output, + javascript_dependencies: RevisionComparator.new( + ChangedJavascriptPackages, changed_files.javascript_dependencies, base, to ).output ) diff --git a/lib/deploy_complexity/output_formatter.rb b/lib/deploy_complexity/output_formatter.rb index 9d5f8a1..7ffbb55 100644 --- a/lib/deploy_complexity/output_formatter.rb +++ b/lib/deploy_complexity/output_formatter.rb @@ -21,7 +21,8 @@ class OutputFormatter < :gh_url, :migrations, :elm_packages, - :ruby_dependencies + :ruby_dependencies, + :javascript_dependencies ) def format_for_slack @@ -70,6 +71,7 @@ def attachments attachments << migration_attachment if migrations.any? attachments << elm_package_attachment if elm_packages.any? attachments << ruby_dependency_attachment if ruby_dependencies.any? + attachments << javascript_dependency_attachment if javascript_dependencies.any? attachments end @@ -128,5 +130,13 @@ def ruby_dependency_attachment color: "#B6FFE0" ) end + + def javascript_dependency_attachment + Attachment.with( + title: "Changed JavaScript Dependencies", + text: javascript_dependencies.join("\n"), + color: "#B6C6FF" + ) + end end end diff --git a/spec/lib/deploy_complexity/output_formatter_spec.rb b/spec/lib/deploy_complexity/output_formatter_spec.rb index 9dbd8e6..5be92de 100644 --- a/spec/lib/deploy_complexity/output_formatter_spec.rb +++ b/spec/lib/deploy_complexity/output_formatter_spec.rb @@ -19,7 +19,8 @@ to_reference: "to_ref", migrations: [], elm_packages: [], - ruby_dependencies: [] + ruby_dependencies: [], + javascript_dependencies: [] ) end @@ -63,6 +64,9 @@ ], ruby_dependencies: [ "rspec: 3.1.0 -> 3.2.0" + ], + javascript_dependencies: [ + "clipboard: 0.0.1 -> 0.0.5" ] ) end @@ -82,6 +86,9 @@ Changed Ruby Dependencies rspec: 3.1.0 -> 3.2.0 + + Changed JavaScript Dependencies + clipboard: 0.0.1 -> 0.0.5 TXT end @@ -114,6 +121,13 @@ rspec: 3.1.0 -> 3.2.0 TXT color: "#B6FFE0" + }, + { + title: "Changed JavaScript Dependencies", + text: <<-TXT.gsub(/^\s+/, "").chomp, + clipboard: 0.0.1 -> 0.0.5 + TXT + color: "#B6C6FF" } ] ) From c11c658391b488b656d4127f8e4ee2ae25ac74c6 Mon Sep 17 00:00:00 2001 From: Brooke Angel Date: Mon, 7 Jan 2019 12:21:03 -0800 Subject: [PATCH 12/32] show content in deploy complexity output --- lib/deploy_complexity/output_formatter.rb | 19 +++++++++++++++++++ .../output_formatter_spec.rb | 12 ++++++++++++ 2 files changed, 31 insertions(+) diff --git a/lib/deploy_complexity/output_formatter.rb b/lib/deploy_complexity/output_formatter.rb index 7ffbb55..79fce5b 100644 --- a/lib/deploy_complexity/output_formatter.rb +++ b/lib/deploy_complexity/output_formatter.rb @@ -72,6 +72,9 @@ def attachments attachments << elm_package_attachment if elm_packages.any? attachments << ruby_dependency_attachment if ruby_dependencies.any? attachments << javascript_dependency_attachment if javascript_dependencies.any? + # FIXME: there may be commits in the deploy unassociated with a PR + content_attachment = pull_requests.any? ? pull_request_attachment : commits_attachment + attachments << content_attachment attachments end @@ -138,5 +141,21 @@ def javascript_dependency_attachment color: "#B6C6FF" ) end + + def pull_request_attachment + Attachment.with( + title: "Pull Requests", + text: pull_requests.join("\n"), + color: "#FFCCB6" + ) + end + + def commits_attachment + Attachment.with( + title: "Commits", + text: commits.join("\n"), + color: "#FFCCB6" + ) + end end end diff --git a/spec/lib/deploy_complexity/output_formatter_spec.rb b/spec/lib/deploy_complexity/output_formatter_spec.rb index 5be92de..d33ea7c 100644 --- a/spec/lib/deploy_complexity/output_formatter_spec.rb +++ b/spec/lib/deploy_complexity/output_formatter_spec.rb @@ -89,6 +89,10 @@ Changed JavaScript Dependencies clipboard: 0.0.1 -> 0.0.5 + + Commits + bbbb Do the thing + cccc Do the thing again TXT end @@ -128,6 +132,14 @@ clipboard: 0.0.1 -> 0.0.5 TXT color: "#B6C6FF" + }, + { + title: "Commits", + text: <<-TXT.gsub(/^\s+/, "").chomp, + bbbb Do the thing + cccc Do the thing again + TXT + color: "#FFCCB6" } ] ) From d1863b60059346fefd5f6b3e420e8dc626187703 Mon Sep 17 00:00:00 2001 From: Brooke Angel Date: Mon, 7 Jan 2019 12:29:29 -0800 Subject: [PATCH 13/32] add stat and dirstat attachments --- exe/deploy-complexity.rb | 2 ++ lib/deploy_complexity/output_formatter.rb | 20 +++++++++++++++++++ .../output_formatter_spec.rb | 4 ++++ 3 files changed, 26 insertions(+) diff --git a/exe/deploy-complexity.rb b/exe/deploy-complexity.rb index 4aa9f30..54690c8 100755 --- a/exe/deploy-complexity.rb +++ b/exe/deploy-complexity.rb @@ -122,6 +122,8 @@ def deploy(base, to, options) pull_requests: pull_requests, merges: merges, shortstat: shortstat, + dirstat: dirstat, + stat: stat, time_delta: time_delta, gh_url: gh_url, base_reference: reference(base), diff --git a/lib/deploy_complexity/output_formatter.rb b/lib/deploy_complexity/output_formatter.rb index 79fce5b..e38a211 100644 --- a/lib/deploy_complexity/output_formatter.rb +++ b/lib/deploy_complexity/output_formatter.rb @@ -17,6 +17,8 @@ class OutputFormatter < :pull_requests, :merges, :shortstat, + :dirstat, + :stat, :time_delta, :gh_url, :migrations, @@ -75,6 +77,8 @@ def attachments # FIXME: there may be commits in the deploy unassociated with a PR content_attachment = pull_requests.any? ? pull_request_attachment : commits_attachment attachments << content_attachment + attachments << dirstat_attachment if dirstat + attachments << stat_attachment if stat attachments end @@ -157,5 +161,21 @@ def commits_attachment color: "#FFCCB6" ) end + + def stat_attachment + Attachment.with( + title: "Stats", + text: stat, + color: "#D6B6FF" + ) + end + + def dirstat_attachment + Attachment.with( + title: "Dirstats", + text: dirstat, + color: "#B6D8FF" + ) + end end end diff --git a/spec/lib/deploy_complexity/output_formatter_spec.rb b/spec/lib/deploy_complexity/output_formatter_spec.rb index d33ea7c..79ccd19 100644 --- a/spec/lib/deploy_complexity/output_formatter_spec.rb +++ b/spec/lib/deploy_complexity/output_formatter_spec.rb @@ -13,6 +13,8 @@ pull_requests: [], merges: [], shortstat: "", + stat: nil, + dirstat: nil, time_delta: "0 nanoseconds", gh_url: "example.com", base_reference: "base_ref", @@ -51,6 +53,8 @@ pull_requests: [], merges: [], shortstat: "", + stat: nil, + dirstat: nil, time_delta: "0 nanoseconds", gh_url: "example.com", base_reference: "base_ref", From bc5b6b6c4847362c02fe9074bb31a50bdf8e5797 Mon Sep 17 00:00:00 2001 From: Brooke Angel Date: Mon, 7 Jan 2019 12:30:26 -0800 Subject: [PATCH 14/32] remove now-redundant comment --- exe/deploy-complexity.rb | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/exe/deploy-complexity.rb b/exe/deploy-complexity.rb index 54690c8..da4d09d 100755 --- a/exe/deploy-complexity.rb +++ b/exe/deploy-complexity.rb @@ -91,28 +91,6 @@ def deploy(base, to, options) pull_requests = pull_requests(merges, gh_url) - # if !commits.empty? - # puts "%d pull requests of %d merges, %d commits %s" % - # [pull_requests.count, merges.count, commits.count, time_delta] - # puts shortstat.first.strip unless shortstat.empty? - # puts COMPARE_FORMAT % [gh_url, reference(base), reference(to)] - # list_migrations(changed_files) - # list_changed_elm_dependencies(changed_files, base: base, to: to) - # list_changed_javascript_dependencies(changed_files, base: base, to: to) - # list_changed_ruby_dependencies(changed_files, base: base, to: to) - # if pull_requests.any? - # # FIXME: there may be commits in the deploy unassociated with a PR - # puts "Pull Requests:", pull_requests - # else - # puts "Commits:", commits - # end - # puts "Dirstats:", dirstat if dirstat - # puts "Stats:", stat if stat - # else - # puts "redeployed %s %s" % [base, time_delta] - # end - # puts - # TODO: scan for changes to app/jobs and report changes to params formatter = DeployComplexity::OutputFormatter.with( to: to, From 4e3f2875bfa2dd68837a98314736b80a3858b466 Mon Sep 17 00:00:00 2001 From: Brooke Angel Date: Mon, 7 Jan 2019 12:39:22 -0800 Subject: [PATCH 15/32] move everything to deploy complexity namespace --- exe/deploy-complexity.rb | 14 +-- lib/deploy_complexity/changed_dependencies.rb | 92 ++++++++++--------- lib/deploy_complexity/changed_elm_packages.rb | 28 +++--- lib/deploy_complexity/changed_files.rb | 40 ++++---- .../changed_javascript_packages.rb | 18 ++-- lib/deploy_complexity/changed_ruby_gems.rb | 25 ++--- lib/deploy_complexity/revision_comparator.rb | 52 ++++++----- .../changed_elm_packages_spec.rb | 4 +- .../deploy_complexity/changed_files_spec.rb | 4 +- .../changed_javascript_packages_spec.rb | 4 +- .../changed_ruby_gems_spec.rb | 4 +- 11 files changed, 148 insertions(+), 137 deletions(-) diff --git a/exe/deploy-complexity.rb b/exe/deploy-complexity.rb index da4d09d..1b764b6 100755 --- a/exe/deploy-complexity.rb +++ b/exe/deploy-complexity.rb @@ -82,7 +82,7 @@ def deploy(base, to, options) shortstat = `git diff --shortstat --summary #{range}`.split(/\n/) names_only = `git diff --name-only #{range}` versioned_url = "#{gh_url}/blob/#{safe_name(to)}/" - changed_files = ChangedFiles.new(names_only, versioned_url) + changed_files = DeployComplexity::ChangedFiles.new(names_only, versioned_url) dirstat = `git diff --dirstat=lines,cumulative #{range}` if dirstat # TODO: investigate summarizing language / spec content based on file suffix, @@ -107,14 +107,14 @@ def deploy(base, to, options) base_reference: reference(base), to_reference: reference(to), migrations: changed_files.migrations, - elm_packages: RevisionComparator.new( - ChangedElmPackages, changed_files.elm_packages, base, to + elm_packages: DeployComplexity::RevisionComparator.new( + DeployComplexity::ChangedElmPackages, changed_files.elm_packages, base, to ).output, - ruby_dependencies: RevisionComparator.new( - ChangedRubyGems, changed_files.ruby_dependencies, base, to + ruby_dependencies: DeployComplexity::RevisionComparator.new( + DeployComplexity::ChangedRubyGems, changed_files.ruby_dependencies, base, to ).output, - javascript_dependencies: RevisionComparator.new( - ChangedJavascriptPackages, changed_files.javascript_dependencies, base, to + javascript_dependencies: DeployComplexity::RevisionComparator.new( + DeployComplexity::ChangedJavascriptPackages, changed_files.javascript_dependencies, base, to ).output ) diff --git a/lib/deploy_complexity/changed_dependencies.rb b/lib/deploy_complexity/changed_dependencies.rb index 69fb353..b2ad5c3 100644 --- a/lib/deploy_complexity/changed_dependencies.rb +++ b/lib/deploy_complexity/changed_dependencies.rb @@ -1,61 +1,63 @@ # frozen_string_literal: true -# Detects and formats changes in dependencies -# This is the parent class - each type of dependency file should implement -# it's own version of this. See changed_javascript_packages.rb for an example. -class ChangedDependencies - def initialize(file:, old:, new:) - @file = file - @old_dependencies = parse_dependencies(old) - @new_dependencies = parse_dependencies(new) - end +module DeployComplexity + # Detects and formats changes in dependencies + # This is the parent class - each type of dependency file should implement + # it's own version of this. See changed_javascript_packages.rb for an example. + class ChangedDependencies + def initialize(file:, old:, new:) + @file = file + @old_dependencies = parse_dependencies(old) + @new_dependencies = parse_dependencies(new) + end - def changes - [ - format_dependencies("Added", added_dependencies), - format_dependencies("Removed", removed_dependencies), - format_updated_dependencies(updated_dependencies) - ].flatten - end + def changes + [ + format_dependencies("Added", added_dependencies), + format_dependencies("Removed", removed_dependencies), + format_updated_dependencies(updated_dependencies) + ].flatten + end - private + private - # This should be implemented in the child classes - # @param [String] the dependency file to be parsed - # @return [ Object{String => String} ] map of the dependency name to the version - def parse_dependencies(_file) - {} - end + # This should be implemented in the child classes + # @param [String] the dependency file to be parsed + # @return [ Object{String => String} ] map of the dependency name to the version + def parse_dependencies(_file) + {} + end - def added_dependencies - @new_dependencies.dup.delete_if { |package, _| @old_dependencies.key?(package) } - end + def added_dependencies + @new_dependencies.dup.delete_if { |package, _| @old_dependencies.key?(package) } + end - def removed_dependencies - @old_dependencies.dup.delete_if { |package, _| @new_dependencies.key?(package) } - end + def removed_dependencies + @old_dependencies.dup.delete_if { |package, _| @new_dependencies.key?(package) } + end - def updated_dependencies - @new_dependencies.each_with_object({}) do |(package, new_version), changed_dependencies| - next if @old_dependencies[package].nil? - next if @old_dependencies[package] == new_version + def updated_dependencies + @new_dependencies.each_with_object({}) do |(package, new_version), changed_dependencies| + next if @old_dependencies[package].nil? + next if @old_dependencies[package] == new_version - changed_dependencies[package] = { - old: @old_dependencies[package], - new: new_version - } + changed_dependencies[package] = { + old: @old_dependencies[package], + new: new_version + } + end end - end - def format_dependencies(label, dependencies) - dependencies.map do |(package, version)| - "#{label} #{package}: #{version} (#{@file})" + def format_dependencies(label, dependencies) + dependencies.map do |(package, version)| + "#{label} #{package}: #{version} (#{@file})" + end end - end - def format_updated_dependencies(dependencies) - dependencies.map do |(package, versions)| - "Updated #{package}: #{versions.fetch(:old)} -> #{versions.fetch(:new)} (#{@file})" + def format_updated_dependencies(dependencies) + dependencies.map do |(package, versions)| + "Updated #{package}: #{versions.fetch(:old)} -> #{versions.fetch(:new)} (#{@file})" + end end end end diff --git a/lib/deploy_complexity/changed_elm_packages.rb b/lib/deploy_complexity/changed_elm_packages.rb index ac39849..a07c2e1 100644 --- a/lib/deploy_complexity/changed_elm_packages.rb +++ b/lib/deploy_complexity/changed_elm_packages.rb @@ -3,20 +3,22 @@ require 'deploy_complexity/changed_dependencies' require 'json' -# Takes in two elm.json files and detects which packages have changed -class ChangedElmPackages < ChangedDependencies - private +module DeployComplexity + # Takes in two elm.json files and detects which packages have changed + class ChangedElmPackages < ChangedDependencies + private - def parse_dependencies(file) - json = JSON.parse(file) - dependencies = json.fetch("dependencies") - test_dependencies = json.fetch("test-dependencies") + def parse_dependencies(file) + json = JSON.parse(file) + dependencies = json.fetch("dependencies") + test_dependencies = json.fetch("test-dependencies") - [ - dependencies.fetch("direct"), - dependencies.fetch("indirect"), - test_dependencies.fetch("direct"), - test_dependencies.fetch("indirect") - ].inject(&:merge) + [ + dependencies.fetch("direct"), + dependencies.fetch("indirect"), + test_dependencies.fetch("direct"), + test_dependencies.fetch("indirect") + ].inject(&:merge) + end end end diff --git a/lib/deploy_complexity/changed_files.rb b/lib/deploy_complexity/changed_files.rb index 599c3ea..6a9178c 100644 --- a/lib/deploy_complexity/changed_files.rb +++ b/lib/deploy_complexity/changed_files.rb @@ -1,28 +1,30 @@ # frozen_string_literal: true -# Parses git shortstat output -# `git diff --name-only v0.4.0...v0.5.0` -class ChangedFiles - def initialize(names, versioned_url) - @names = names.split(/\n/) - @versioned_url = versioned_url - end +module DeployComplexity + # Parses git shortstat output + # `git diff --name-only v0.4.0...v0.5.0` + class ChangedFiles + def initialize(names, versioned_url) + @names = names.split(/\n/) + @versioned_url = versioned_url + end - def migrations - @names.grep(%r{^db/migrate}).map do |line| - @versioned_url + line.chomp + def migrations + @names.grep(%r{^db/migrate}).map do |line| + @versioned_url + line.chomp + end end - end - def elm_packages - @names.grep(%r{(^|/)elm\.json$}).map(&:chomp) - end + def elm_packages + @names.grep(%r{(^|/)elm\.json$}).map(&:chomp) + end - def ruby_dependencies - @names.grep(%r{(^|/)Gemfile\.lock$}).map(&:chomp) - end + def ruby_dependencies + @names.grep(%r{(^|/)Gemfile\.lock$}).map(&:chomp) + end - def javascript_dependencies - @names.grep(%r{(^|/)package-lock\.json$}).map(&:chomp) + def javascript_dependencies + @names.grep(%r{(^|/)package-lock\.json$}).map(&:chomp) + end end end diff --git a/lib/deploy_complexity/changed_javascript_packages.rb b/lib/deploy_complexity/changed_javascript_packages.rb index 7ba4cea..9d4a33e 100644 --- a/lib/deploy_complexity/changed_javascript_packages.rb +++ b/lib/deploy_complexity/changed_javascript_packages.rb @@ -3,16 +3,18 @@ require 'deploy_complexity/changed_dependencies' require 'json' -# Detects changes in two package-lock.json files -class ChangedJavascriptPackages < ChangedDependencies - private +module DeployComplexity + # Detects changes in two package-lock.json files + class ChangedJavascriptPackages < ChangedDependencies + private - def parse_dependencies(file) - json = JSON.parse(file) - dependencies = json.fetch("dependencies") + def parse_dependencies(file) + json = JSON.parse(file) + dependencies = json.fetch("dependencies") - dependencies.each_with_object({}) do |(dependency, details), collection| - collection[dependency] = details.fetch("version") + dependencies.each_with_object({}) do |(dependency, details), collection| + collection[dependency] = details.fetch("version") + end end end end diff --git a/lib/deploy_complexity/changed_ruby_gems.rb b/lib/deploy_complexity/changed_ruby_gems.rb index 68dc662..34322f9 100644 --- a/lib/deploy_complexity/changed_ruby_gems.rb +++ b/lib/deploy_complexity/changed_ruby_gems.rb @@ -1,23 +1,24 @@ # frozen_string_literal: true require 'deploy_complexity/changed_dependencies' -require 'pry' -# Takes in two package-lock.json files and detects which packages have changed -class ChangedRubyGems < ChangedDependencies - private +module DeployComplexity + # Takes in two package-lock.json files and detects which packages have changed + class ChangedRubyGems < ChangedDependencies + private - # See: https://stackoverflow.com/questions/38800129/parsing-a-gemfile-lock-with-bundler - def parse_dependencies(file) - lockfile_parser = Bundler::LockfileParser.new(file) + # See: https://stackoverflow.com/questions/38800129/parsing-a-gemfile-lock-with-bundler + def parse_dependencies(file) + lockfile_parser = Bundler::LockfileParser.new(file) - lockfile_parser.specs.each_with_object({}) do |spec, collection| - version = spec.version.to_s + lockfile_parser.specs.each_with_object({}) do |spec, collection| + version = spec.version.to_s - # Consider Git sources separately so we know where they are coming from - version += " (GIT #{spec.source.uri} #{spec.source.revision})" if spec.source.is_a?(Bundler::Source::Git) + # Consider Git sources separately so we know where they are coming from + version += " (GIT #{spec.source.uri} #{spec.source.revision})" if spec.source.is_a?(Bundler::Source::Git) - collection[spec.name] = version + collection[spec.name] = version + end end end end diff --git a/lib/deploy_complexity/revision_comparator.rb b/lib/deploy_complexity/revision_comparator.rb index 7b2d06d..84a6179 100644 --- a/lib/deploy_complexity/revision_comparator.rb +++ b/lib/deploy_complexity/revision_comparator.rb @@ -1,33 +1,35 @@ # frozen_string_literal: true -# Compare package changes for a given parser across revisions -class RevisionComparator - def initialize(parser, files, base, to) - @parser = parser - @files = files - @base = base - @to = to - end +module DeployComplexity + # Compare package changes for a given parser across revisions + class RevisionComparator + def initialize(parser, files, base, to) + @parser = parser + @files = files + @base = base + @to = to + end - def source(revision, file) - `git show #{revision}:#{file}` - end + def source(revision, file) + `git show #{revision}:#{file}` + end - def changes - @files.inject([]) do |changes, file| - old = source(@base, file) - new = source(@to, file) - packages = @parser.new(file: file, old: old, new: new) - changes + packages.changes + def changes + @files.inject([]) do |changes, file| + old = source(@base, file) + new = source(@to, file) + packages = @parser.new(file: file, old: old, new: new) + changes + packages.changes + end end - end - def output - changes - # TODO: bring back error handling - # rescue StandardError => e - # puts "Error parsing: #{title}" - # puts e.message - # puts e.backtrace + def output + changes + # TODO: bring back error handling + # rescue StandardError => e + # puts "Error parsing: #{title}" + # puts e.message + # puts e.backtrace + end end end diff --git a/spec/lib/deploy_complexity/changed_elm_packages_spec.rb b/spec/lib/deploy_complexity/changed_elm_packages_spec.rb index f1da04a..174cac1 100644 --- a/spec/lib/deploy_complexity/changed_elm_packages_spec.rb +++ b/spec/lib/deploy_complexity/changed_elm_packages_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' require 'deploy_complexity/changed_elm_packages' -describe ChangedElmPackages do +describe DeployComplexity::ChangedElmPackages do let(:old) do <<-TXT.gsub(/^\s+/, "") { @@ -27,7 +27,7 @@ end subject(:changed_elm_packages) do - ChangedElmPackages.new( + DeployComplexity::ChangedElmPackages.new( file: "file_path", old: old, new: new diff --git a/spec/lib/deploy_complexity/changed_files_spec.rb b/spec/lib/deploy_complexity/changed_files_spec.rb index f4eb996..8a7d51f 100644 --- a/spec/lib/deploy_complexity/changed_files_spec.rb +++ b/spec/lib/deploy_complexity/changed_files_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' require 'deploy_complexity/changed_files' -describe ChangedFiles do +describe DeployComplexity::ChangedFiles do let(:files) do <<-TXT.gsub(/^\s+/, "") app/assets/images/foo/bar.svg @@ -22,7 +22,7 @@ TXT end - subject(:changed_files) { ChangedFiles.new(files, versioned_url) } + subject(:changed_files) { DeployComplexity::ChangedFiles.new(files, versioned_url) } let(:versioned_url) { "https://github.com/NoRedInk/deploy-complexity/blob/v0.5.0/" } diff --git a/spec/lib/deploy_complexity/changed_javascript_packages_spec.rb b/spec/lib/deploy_complexity/changed_javascript_packages_spec.rb index 9d875fc..d7166a2 100644 --- a/spec/lib/deploy_complexity/changed_javascript_packages_spec.rb +++ b/spec/lib/deploy_complexity/changed_javascript_packages_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' require 'deploy_complexity/changed_javascript_packages' -describe ChangedJavascriptPackages do +describe DeployComplexity::ChangedJavascriptPackages do let(:old) do <<-TXT.gsub(/^\s+/, "") { @@ -31,7 +31,7 @@ TXT end subject(:changed_javascript_packages) do - ChangedJavascriptPackages.new( + DeployComplexity::ChangedJavascriptPackages.new( file: "file_path", old: old, new: new diff --git a/spec/lib/deploy_complexity/changed_ruby_gems_spec.rb b/spec/lib/deploy_complexity/changed_ruby_gems_spec.rb index cf8d24b..ec51709 100644 --- a/spec/lib/deploy_complexity/changed_ruby_gems_spec.rb +++ b/spec/lib/deploy_complexity/changed_ruby_gems_spec.rb @@ -3,13 +3,13 @@ require 'spec_helper' require 'deploy_complexity/changed_ruby_gems' -describe ChangedRubyGems do +describe DeployComplexity::ChangedRubyGems do # Parsing relies on whitespace, so we can't really insert these mock gemfiles # inline - Rubocop gets angry let(:old) { File.read("spec/lib/deploy_complexity/spec_helpers/old_gemfile_lock.txt") } subject(:changed_ruby_gems) do - ChangedRubyGems.new( + DeployComplexity::ChangedRubyGems.new( file: "file_path", old: old, new: new From 63871fd120dcbefbf68c8b6d11b01f66c126b333 Mon Sep 17 00:00:00 2001 From: Brooke Angel Date: Mon, 7 Jan 2019 13:57:56 -0800 Subject: [PATCH 16/32] insert proper spacing in cli formatting --- lib/deploy_complexity/output_formatter.rb | 4 ++-- spec/lib/deploy_complexity/output_formatter_spec.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/deploy_complexity/output_formatter.rb b/lib/deploy_complexity/output_formatter.rb index e38a211..a15c7ea 100644 --- a/lib/deploy_complexity/output_formatter.rb +++ b/lib/deploy_complexity/output_formatter.rb @@ -41,8 +41,8 @@ def format_for_cli return output unless added_attachments.any? - output + "\n" + - added_attachments.map { |a| format_attachment_for_cli(a) }.join("\n") + output + "\n\n" + + added_attachments.map { |a| format_attachment_for_cli(a) }.join("\n\n") end private diff --git a/spec/lib/deploy_complexity/output_formatter_spec.rb b/spec/lib/deploy_complexity/output_formatter_spec.rb index 79ccd19..a8f6db2 100644 --- a/spec/lib/deploy_complexity/output_formatter_spec.rb +++ b/spec/lib/deploy_complexity/output_formatter_spec.rb @@ -76,7 +76,7 @@ end it "formats for the CLI" do - expect(formatter.format_for_cli).to eq(<<-TXT.gsub(/^\s+/, "").chomp) + expect(formatter.format_for_cli).to eq(<<-TXT.gsub(/^ +/, "").chomp) *Deploy tag to_commit [aaaa]* 0 pull requests of 0 merges, 2 commits 0 nanoseconds example.com/compare/base_ref...to_ref From 3f5e7ee3ec7437939ee20c9ada1a5381b6fe473a Mon Sep 17 00:00:00 2001 From: Brooke Angel Date: Mon, 7 Jan 2019 15:00:38 -0800 Subject: [PATCH 17/32] catch errors in revision comparator --- lib/deploy_complexity/revision_comparator.rb | 21 +++++---- .../revision_comparator_spec.rb | 45 +++++++++++++++++++ 2 files changed, 57 insertions(+), 9 deletions(-) create mode 100644 spec/lib/deploy_complexity/revision_comparator_spec.rb diff --git a/lib/deploy_complexity/revision_comparator.rb b/lib/deploy_complexity/revision_comparator.rb index 84a6179..4445ce9 100644 --- a/lib/deploy_complexity/revision_comparator.rb +++ b/lib/deploy_complexity/revision_comparator.rb @@ -10,6 +10,18 @@ def initialize(parser, files, base, to) @to = to end + # return [Array] Should return an array of strings, even if there is a parsing error + def output + changes + rescue StandardError => e + [ + e.message, + e.backtrace.join("/n") + ] + end + + private + def source(revision, file) `git show #{revision}:#{file}` end @@ -22,14 +34,5 @@ def changes changes + packages.changes end end - - def output - changes - # TODO: bring back error handling - # rescue StandardError => e - # puts "Error parsing: #{title}" - # puts e.message - # puts e.backtrace - end end end diff --git a/spec/lib/deploy_complexity/revision_comparator_spec.rb b/spec/lib/deploy_complexity/revision_comparator_spec.rb new file mode 100644 index 0000000..521e2bd --- /dev/null +++ b/spec/lib/deploy_complexity/revision_comparator_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'rspec' +require 'deploy_complexity/changed_dependencies' +require 'deploy_complexity/revision_comparator' + +class FakeParser < DeployComplexity::ChangedDependencies + def changes + ["Added a_cool_dependency 4.2"] + end +end + +describe DeployComplexity::RevisionComparator do + subject(:comparator) do + DeployComplexity::RevisionComparator.new( + FakeParser, + ["file.txt"], + "aaa", + "bbb" + ) + end + + context "when there are changes" do + before do + # Don't actually read from git in tests + allow(comparator).to receive(:source).and_return("") + end + + it "should output an array of changes" do + expect(comparator.output).to eq(["Added a_cool_dependency 4.2"]) + end + end + + context "when there is an error" do + before do + allow(comparator).to receive(:source).and_raise(StandardError.new("bad bad")) + end + + it "still kindly outputs an array" do + output = comparator.output + expect(output).to be_an_instance_of(Array) + expect(output.first).to eq("bad bad") + end + end +end From 35e7501bc04171714c96a59d58d038ef38580c1c Mon Sep 17 00:00:00 2001 From: Brooke Angel Date: Tue, 8 Jan 2019 09:34:18 -0800 Subject: [PATCH 18/32] just send short url with PR --- exe/deploy-complexity.rb | 24 ++++++++++++------- lib/deploy_complexity/output_formatter.rb | 5 +++- .../output_formatter_spec.rb | 23 +++++++++++------- 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/exe/deploy-complexity.rb b/exe/deploy-complexity.rb index 1b764b6..9a11dbc 100755 --- a/exe/deploy-complexity.rb +++ b/exe/deploy-complexity.rb @@ -51,15 +51,25 @@ def reference(name) end end +# TODO: consider moving this to a separate parser and testing def pull_requests(merges, gh_url) - prs = merges.map do |line| + merges.map do |line| line.match(/pull request #(\d+) from (.*)$/) do |m| - [gh_url, m[1].to_i, "-", safe_name(m[2])] + { + gh_url: gh_url, + pr_number: m[1].to_i, + joiner: "-", + name: safe_name(m[2]) + } end || line.match(/(\w+)\s+(.*)\(\#(\d+)\)/) do |m| - [gh_url, m[3].to_i, "S", m[2]] # squash merge + { + gh_url: gh_url, + pr_number: m[3].to_i, + joiner: "S", + name: m[2] + } end - end - prs.compact.map { |x| "%s/pull/%d %1s %s" % x } + end.compact end # deploys are the delta from base -> to, so to contains commits to add to base @@ -89,15 +99,13 @@ def deploy(base, to, options) # and possibly per PR, or classify frontend, backend, spec changes stat = `git diff --stat #{range}` if stat - pull_requests = pull_requests(merges, gh_url) - # TODO: scan for changes to app/jobs and report changes to params formatter = DeployComplexity::OutputFormatter.with( to: to, base: base, revision: revision, commits: commits, - pull_requests: pull_requests, + pull_requests: pull_requests(merges, gh_url), merges: merges, shortstat: shortstat, dirstat: dirstat, diff --git a/lib/deploy_complexity/output_formatter.rb b/lib/deploy_complexity/output_formatter.rb index a15c7ea..81d8289 100644 --- a/lib/deploy_complexity/output_formatter.rb +++ b/lib/deploy_complexity/output_formatter.rb @@ -149,7 +149,10 @@ def javascript_dependency_attachment def pull_request_attachment Attachment.with( title: "Pull Requests", - text: pull_requests.join("\n"), + text: pull_requests.map do |pr| + url = "#{pr.fetch(:gh_url)}/pull/#{pr.fetch(:pr_number)}" + "<#{url}|#{pr.fetch(:pr_number)}> - #{pr.fetch(:name)}" + end.join("\n"), color: "#FFCCB6" ) end diff --git a/spec/lib/deploy_complexity/output_formatter_spec.rb b/spec/lib/deploy_complexity/output_formatter_spec.rb index a8f6db2..712ad05 100644 --- a/spec/lib/deploy_complexity/output_formatter_spec.rb +++ b/spec/lib/deploy_complexity/output_formatter_spec.rb @@ -50,7 +50,14 @@ "bbbb Do the thing", "cccc Do the thing again" ], - pull_requests: [], + pull_requests: [ + { + gh_url: "example.com", + pr_number: "1", + joiner: "-", + name: "add-more-cats" + } + ], merges: [], shortstat: "", stat: nil, @@ -78,7 +85,7 @@ it "formats for the CLI" do expect(formatter.format_for_cli).to eq(<<-TXT.gsub(/^ +/, "").chomp) *Deploy tag to_commit [aaaa]* - 0 pull requests of 0 merges, 2 commits 0 nanoseconds + 1 pull requests of 0 merges, 2 commits 0 nanoseconds example.com/compare/base_ref...to_ref Migrations @@ -94,9 +101,8 @@ Changed JavaScript Dependencies clipboard: 0.0.1 -> 0.0.5 - Commits - bbbb Do the thing - cccc Do the thing again + Pull Requests + - add-more-cats TXT end @@ -104,7 +110,7 @@ expect(formatter.format_for_slack).to eq( text: <<-TXT.gsub(/^\s+/, "").chomp, *Deploy tag to_commit [aaaa]* - 0 pull requests of 0 merges, 2 commits 0 nanoseconds + 1 pull requests of 0 merges, 2 commits 0 nanoseconds example.com/compare/base_ref...to_ref TXT attachments: [ @@ -138,10 +144,9 @@ color: "#B6C6FF" }, { - title: "Commits", + title: "Pull Requests", text: <<-TXT.gsub(/^\s+/, "").chomp, - bbbb Do the thing - cccc Do the thing again + - add-more-cats TXT color: "#FFCCB6" } From 48130ec36c7cc2fdba30f542b0d87c7b6e55e496 Mon Sep 17 00:00:00 2001 From: Brooke Angel Date: Tue, 8 Jan 2019 09:36:11 -0800 Subject: [PATCH 19/32] consolidate text joining --- lib/deploy_complexity/output_formatter.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/deploy_complexity/output_formatter.rb b/lib/deploy_complexity/output_formatter.rb index 81d8289..de2e1af 100644 --- a/lib/deploy_complexity/output_formatter.rb +++ b/lib/deploy_complexity/output_formatter.rb @@ -29,13 +29,13 @@ class OutputFormatter < def format_for_slack { - text: text.compact.join("\n"), + text: text, attachments: attachments.map { |a| format_attachment_for_slack(a) } } end def format_for_cli - output = text.compact.join("\n") + output = text added_attachments = attachments @@ -62,7 +62,7 @@ def text text << shortstats end - text + text.compact.join("\n") end def attachments From 5c10f721cd81287d5509847a8b363b809c72b44f Mon Sep 17 00:00:00 2001 From: Brooke Angel Date: Tue, 8 Jan 2019 09:51:53 -0800 Subject: [PATCH 20/32] move formatters to child classes --- exe/deploy-complexity.rb | 19 ++-- lib/deploy_complexity/cli_output_formatter.rb | 25 +++++ lib/deploy_complexity/output_formatter.rb | 32 +----- .../slack_output_formatter.rb | 26 +++++ .../cli_output_formatter_spec.rb | 102 ++++++++++++++++++ ...spec.rb => slack_output_formatter_spec.rb} | 42 ++------ 6 files changed, 175 insertions(+), 71 deletions(-) create mode 100644 lib/deploy_complexity/cli_output_formatter.rb create mode 100644 lib/deploy_complexity/slack_output_formatter.rb create mode 100644 spec/lib/deploy_complexity/cli_output_formatter_spec.rb rename spec/lib/deploy_complexity/{output_formatter_spec.rb => slack_output_formatter_spec.rb} (73%) diff --git a/exe/deploy-complexity.rb b/exe/deploy-complexity.rb index 9a11dbc..b890b5e 100755 --- a/exe/deploy-complexity.rb +++ b/exe/deploy-complexity.rb @@ -3,7 +3,8 @@ require 'time' require 'bundler/setup' -require 'deploy_complexity/output_formatter' +require 'deploy_complexity/slack_output_formatter' +require 'deploy_complexity/cli_output_formatter' require 'deploy_complexity/version' require 'deploy_complexity/revision_comparator' require 'deploy_complexity/changed_files' @@ -100,7 +101,7 @@ def deploy(base, to, options) stat = `git diff --stat #{range}` if stat # TODO: scan for changes to app/jobs and report changes to params - formatter = DeployComplexity::OutputFormatter.with( + formatter_attributes = { to: to, base: base, revision: revision, @@ -124,13 +125,15 @@ def deploy(base, to, options) javascript_dependencies: DeployComplexity::RevisionComparator.new( DeployComplexity::ChangedJavascriptPackages, changed_files.javascript_dependencies, base, to ).output - ) + } - if slack_format - puts formatter.format_for_slack - else - puts formatter.format_for_cli - end + formatter = if slack_format + DeployComplexity::SlackOutputFormatter.with(formatter_attributes) + else + DeployComplexity::CliOutputFormatter.with(formatter_attributes) + end + + puts formatter.format end branch = "production" diff --git a/lib/deploy_complexity/cli_output_formatter.rb b/lib/deploy_complexity/cli_output_formatter.rb new file mode 100644 index 0000000..eac42e4 --- /dev/null +++ b/lib/deploy_complexity/cli_output_formatter.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require "deploy_complexity/output_formatter" + +module DeployComplexity + # Formats deploy complexity output for cli + class CliOutputFormatter < OutputFormatter + def format + output = text + + added_attachments = attachments + + return output unless added_attachments.any? + + output + "\n\n" + + added_attachments.map { |a| format_attachment(a) }.join("\n\n") + end + + private + + def format_attachment(attachment) + attachment.title + "\n" + attachment.text + end + end +end diff --git a/lib/deploy_complexity/output_formatter.rb b/lib/deploy_complexity/output_formatter.rb index de2e1af..4093981 100644 --- a/lib/deploy_complexity/output_formatter.rb +++ b/lib/deploy_complexity/output_formatter.rb @@ -5,7 +5,8 @@ module DeployComplexity Attachment = Value.new(:title, :text, :color) - # Formats deploy complexity output + # Parent class for formatting deploy complexity output + # Child classes (slack, cli) should implement #format and #format_attachment class OutputFormatter < Value.new( :to, @@ -27,23 +28,8 @@ class OutputFormatter < :javascript_dependencies ) - def format_for_slack - { - text: text, - attachments: attachments.map { |a| format_attachment_for_slack(a) } - } - end - - def format_for_cli - output = text - - added_attachments = attachments - - return output unless added_attachments.any? - - output + "\n\n" + - added_attachments.map { |a| format_attachment_for_cli(a) }.join("\n\n") - end + # This should be implemented in child classes and be the final output + def format; end private @@ -83,20 +69,12 @@ def attachments attachments end - def format_attachment_for_slack(attachment) - attachment.to_h - end - - def format_attachment_for_cli(attachment) - attachment.title + "\n" + attachment.text - end - def empty_commit_message "redeployed %s %s" % [base, time_delta] end def header - "*Deploy tag #{to} [#{revision}]*" + "Deploy tag #{to} [#{revision}]" end def summary_stats diff --git a/lib/deploy_complexity/slack_output_formatter.rb b/lib/deploy_complexity/slack_output_formatter.rb new file mode 100644 index 0000000..9767d9e --- /dev/null +++ b/lib/deploy_complexity/slack_output_formatter.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require "deploy_complexity/output_formatter" + +module DeployComplexity + # Formats deploy complexity output for slack + class SlackOutputFormatter < OutputFormatter + def format + { + text: text, + attachments: attachments.map { |a| format_attachment(a) } + } + end + + private + + def format_attachment(attachment) + attachment.to_h + end + + # Override header by making it bold + def header + "*#{super}*" + end + end +end diff --git a/spec/lib/deploy_complexity/cli_output_formatter_spec.rb b/spec/lib/deploy_complexity/cli_output_formatter_spec.rb new file mode 100644 index 0000000..57897ed --- /dev/null +++ b/spec/lib/deploy_complexity/cli_output_formatter_spec.rb @@ -0,0 +1,102 @@ +# frozen_string_literal: true + +require 'deploy_complexity/cli_output_formatter' + +describe DeployComplexity::CliOutputFormatter do + context "with no commits" do + let(:formatter) do + DeployComplexity::CliOutputFormatter.with( + to: "to_commit", + base: "base_commit", + revision: "aaaa", + commits: [], + pull_requests: [], + merges: [], + shortstat: "", + stat: nil, + dirstat: nil, + time_delta: "0 nanoseconds", + gh_url: "example.com", + base_reference: "base_ref", + to_reference: "to_ref", + migrations: [], + elm_packages: [], + ruby_dependencies: [], + javascript_dependencies: [] + ) + end + + it "formats for the CLI" do + expect(formatter.format).to eq( + "Deploy tag to_commit [aaaa]\nredeployed base_commit 0 nanoseconds" + ) + end + end + + context "with commits" do + let(:formatter) do + DeployComplexity::CliOutputFormatter.with( + to: "to_commit", + base: "base_commit", + revision: "aaaa", + commits: [ + "bbbb Do the thing", + "cccc Do the thing again" + ], + pull_requests: [ + { + gh_url: "example.com", + pr_number: "1", + joiner: "-", + name: "add-more-cats" + } + ], + merges: [], + shortstat: "", + stat: nil, + dirstat: nil, + time_delta: "0 nanoseconds", + gh_url: "example.com", + base_reference: "base_ref", + to_reference: "to_ref", + migrations: [ + "example.com/migrate_awayyyy", + "example.com/migrate_awayyyy_again" + ], + elm_packages: [ + "elm/core: 1.1.0 -> 1.2.0" + ], + ruby_dependencies: [ + "rspec: 3.1.0 -> 3.2.0" + ], + javascript_dependencies: [ + "clipboard: 0.0.1 -> 0.0.5" + ] + ) + end + + it "formats for the CLI" do + expect(formatter.format).to eq(<<-TXT.gsub(/^ +/, "").chomp) + Deploy tag to_commit [aaaa] + 1 pull requests of 0 merges, 2 commits 0 nanoseconds + example.com/compare/base_ref...to_ref + + Migrations + example.com/migrate_awayyyy + example.com/migrate_awayyyy_again + + Changed Elm Packages + elm/core: 1.1.0 -> 1.2.0 + + Changed Ruby Dependencies + rspec: 3.1.0 -> 3.2.0 + + Changed JavaScript Dependencies + clipboard: 0.0.1 -> 0.0.5 + + Pull Requests + - add-more-cats + TXT + end + end +end diff --git a/spec/lib/deploy_complexity/output_formatter_spec.rb b/spec/lib/deploy_complexity/slack_output_formatter_spec.rb similarity index 73% rename from spec/lib/deploy_complexity/output_formatter_spec.rb rename to spec/lib/deploy_complexity/slack_output_formatter_spec.rb index 712ad05..2ab4b95 100644 --- a/spec/lib/deploy_complexity/output_formatter_spec.rb +++ b/spec/lib/deploy_complexity/slack_output_formatter_spec.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true -require 'deploy_complexity/output_formatter' +require 'deploy_complexity/slack_output_formatter' -describe DeployComplexity::OutputFormatter do +describe DeployComplexity::SlackOutputFormatter do context "with no commits" do let(:formatter) do - DeployComplexity::OutputFormatter.with( + DeployComplexity::SlackOutputFormatter.with( to: "to_commit", base: "base_commit", revision: "aaaa", @@ -26,14 +26,8 @@ ) end - it "formats for the CLI" do - expect(formatter.format_for_cli).to eq( - "*Deploy tag to_commit [aaaa]*\nredeployed base_commit 0 nanoseconds" - ) - end - it "formats for Slack" do - expect(formatter.format_for_slack).to eq( + expect(formatter.format).to eq( text: "*Deploy tag to_commit [aaaa]*\nredeployed base_commit 0 nanoseconds", attachments: [] ) @@ -42,7 +36,7 @@ context "with commits" do let(:formatter) do - DeployComplexity::OutputFormatter.with( + DeployComplexity::SlackOutputFormatter.with( to: "to_commit", base: "base_commit", revision: "aaaa", @@ -82,32 +76,8 @@ ) end - it "formats for the CLI" do - expect(formatter.format_for_cli).to eq(<<-TXT.gsub(/^ +/, "").chomp) - *Deploy tag to_commit [aaaa]* - 1 pull requests of 0 merges, 2 commits 0 nanoseconds - example.com/compare/base_ref...to_ref - - Migrations - example.com/migrate_awayyyy - example.com/migrate_awayyyy_again - - Changed Elm Packages - elm/core: 1.1.0 -> 1.2.0 - - Changed Ruby Dependencies - rspec: 3.1.0 -> 3.2.0 - - Changed JavaScript Dependencies - clipboard: 0.0.1 -> 0.0.5 - - Pull Requests - - add-more-cats - TXT - end - it "formats for Slack" do - expect(formatter.format_for_slack).to eq( + expect(formatter.format).to eq( text: <<-TXT.gsub(/^\s+/, "").chomp, *Deploy tag to_commit [aaaa]* 1 pull requests of 0 merges, 2 commits 0 nanoseconds From 92553b1266fe123b934f5b78c5a481eddd43240e Mon Sep 17 00:00:00 2001 From: Brooke Angel Date: Tue, 8 Jan 2019 09:54:08 -0800 Subject: [PATCH 21/32] format links for slack but not cli --- lib/deploy_complexity/output_formatter.rb | 2 +- lib/deploy_complexity/slack_output_formatter.rb | 12 ++++++++++++ .../deploy_complexity/cli_output_formatter_spec.rb | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/deploy_complexity/output_formatter.rb b/lib/deploy_complexity/output_formatter.rb index 4093981..554daf6 100644 --- a/lib/deploy_complexity/output_formatter.rb +++ b/lib/deploy_complexity/output_formatter.rb @@ -129,7 +129,7 @@ def pull_request_attachment title: "Pull Requests", text: pull_requests.map do |pr| url = "#{pr.fetch(:gh_url)}/pull/#{pr.fetch(:pr_number)}" - "<#{url}|#{pr.fetch(:pr_number)}> - #{pr.fetch(:name)}" + "#{url} #{pr.fetch(:joiner)} #{pr.fetch(:name)}" end.join("\n"), color: "#FFCCB6" ) diff --git a/lib/deploy_complexity/slack_output_formatter.rb b/lib/deploy_complexity/slack_output_formatter.rb index 9767d9e..e184fc1 100644 --- a/lib/deploy_complexity/slack_output_formatter.rb +++ b/lib/deploy_complexity/slack_output_formatter.rb @@ -22,5 +22,17 @@ def format_attachment(attachment) def header "*#{super}*" end + + # Override parent by fancy formatting links + def pull_request_attachment + Attachment.with( + title: "Pull Requests", + text: pull_requests.map do |pr| + url = "#{pr.fetch(:gh_url)}/pull/#{pr.fetch(:pr_number)}" + "<#{url}|#{pr.fetch(:pr_number)}> - #{pr.fetch(:name)}" + end.join("\n"), + color: "#FFCCB6" + ) + end end end diff --git a/spec/lib/deploy_complexity/cli_output_formatter_spec.rb b/spec/lib/deploy_complexity/cli_output_formatter_spec.rb index 57897ed..1a7fdb6 100644 --- a/spec/lib/deploy_complexity/cli_output_formatter_spec.rb +++ b/spec/lib/deploy_complexity/cli_output_formatter_spec.rb @@ -95,7 +95,7 @@ clipboard: 0.0.1 -> 0.0.5 Pull Requests - - add-more-cats + example.com/pull/1 - add-more-cats TXT end end From 20d8ff01aa2a414deef06dd4689bb413902f37be Mon Sep 17 00:00:00 2001 From: Brooke Angel Date: Tue, 8 Jan 2019 10:02:24 -0800 Subject: [PATCH 22/32] add empty format_attachment method to parent class --- lib/deploy_complexity/output_formatter.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/deploy_complexity/output_formatter.rb b/lib/deploy_complexity/output_formatter.rb index 554daf6..5f31362 100644 --- a/lib/deploy_complexity/output_formatter.rb +++ b/lib/deploy_complexity/output_formatter.rb @@ -35,6 +35,9 @@ def format; end attr_reader :deploy_data + # This should be implemented in child classes + def format_attachment(_attachment); end + def text text = [] From c9c8dc9d40407268bd3078e2eb569958b095354b Mon Sep 17 00:00:00 2001 From: Brooke Angel Date: Tue, 8 Jan 2019 10:22:06 -0800 Subject: [PATCH 23/32] rescue errors in formatter --- lib/deploy_complexity/slack_output_formatter.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/deploy_complexity/slack_output_formatter.rb b/lib/deploy_complexity/slack_output_formatter.rb index e184fc1..000d1bd 100644 --- a/lib/deploy_complexity/slack_output_formatter.rb +++ b/lib/deploy_complexity/slack_output_formatter.rb @@ -10,6 +10,16 @@ def format text: text, attachments: attachments.map { |a| format_attachment(a) } } + rescue StandardError => e + { + text: "Something went wrong formatting output", + attachments: [ + { + title: e.message, + text: e.backtrace.join("\n") + } + ] + } end private From d0106a832b5c58d5b1b4d0e6585542e027ee9989 Mon Sep 17 00:00:00 2001 From: Brooke Angel Date: Tue, 8 Jan 2019 10:36:58 -0800 Subject: [PATCH 24/32] move meat of deploy complexity to separate Deploy module --- exe/deploy-complexity.rb | 134 +--------------------------- lib/deploy_complexity/deploy.rb | 150 ++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+), 131 deletions(-) create mode 100644 lib/deploy_complexity/deploy.rb diff --git a/exe/deploy-complexity.rb b/exe/deploy-complexity.rb index b890b5e..e970a96 100755 --- a/exe/deploy-complexity.rb +++ b/exe/deploy-complexity.rb @@ -1,139 +1,12 @@ #!/usr/bin/env ruby # frozen_string_literal: true -require 'time' require 'bundler/setup' -require 'deploy_complexity/slack_output_formatter' -require 'deploy_complexity/cli_output_formatter' -require 'deploy_complexity/version' -require 'deploy_complexity/revision_comparator' -require 'deploy_complexity/changed_files' -require 'deploy_complexity/changed_elm_packages' -require 'deploy_complexity/changed_javascript_packages' -require 'deploy_complexity/changed_ruby_gems' - -# tag format: production-2016-10-22-0103 or $ENV-YYYY-MM-DD-HHmm -def parse_when(tag) - tag.match(/-(\d{4}-\d{2}-\d{2}-\d{2}\d{2})/) do |m| - Time.strptime(m[1], '%Y-%m-%d-%H%M') - end -end - -COMPARE_FORMAT = "%s/compare/%s...%s" - -def time_between_deploys(from, to) - deploy_time = parse_when(to) - last_time = parse_when(from) - - hours = deploy_time && (deploy_time - last_time) / 60**2 - - if hours.nil? - "pending deploy" - elsif hours < 24 - "after %2.1f %s" % [hours, "hours"] - else - "after %2.1f %s" % [(hours / 24), "days"] - end -end - -# converts origin/master -> master -def safe_name(name) - name.chomp.split(%r{/}).last -end - -# converts a branch name like master into the closest tag or commit sha -def reference(name) - branch = safe_name(name) - tag = `git tag --points-at #{name} | grep #{branch}`.chomp - if tag.empty? - `git rev-parse --short #{branch}`.chomp - else - tag - end -end - -# TODO: consider moving this to a separate parser and testing -def pull_requests(merges, gh_url) - merges.map do |line| - line.match(/pull request #(\d+) from (.*)$/) do |m| - { - gh_url: gh_url, - pr_number: m[1].to_i, - joiner: "-", - name: safe_name(m[2]) - } - end || line.match(/(\w+)\s+(.*)\(\#(\d+)\)/) do |m| - { - gh_url: gh_url, - pr_number: m[3].to_i, - joiner: "S", - name: m[2] - } - end - end.compact -end +require 'deploy_complexity/deploy' +require 'optparse' -# deploys are the delta from base -> to, so to contains commits to add to base def deploy(base, to, options) - gh_url = options[:gh_url] - dirstat = options[:dirstat] - stat = options[:stat] - slack_format = options[:slack_format] - - range = "#{base}...#{to}" - - # tag_revision = `git rev-parse --short #{to}`.chomp - revision = `git rev-list --abbrev-commit -n1 #{to}`.chomp - - time_delta = time_between_deploys(safe_name(base), safe_name(to)) - - commits = `git log --oneline #{range}`.split(/\n/) - merges = commits.grep(/Merges|\#\d+/) - - shortstat = `git diff --shortstat --summary #{range}`.split(/\n/) - names_only = `git diff --name-only #{range}` - versioned_url = "#{gh_url}/blob/#{safe_name(to)}/" - changed_files = DeployComplexity::ChangedFiles.new(names_only, versioned_url) - - dirstat = `git diff --dirstat=lines,cumulative #{range}` if dirstat - # TODO: investigate summarizing language / spec content based on file suffix, - # and possibly per PR, or classify frontend, backend, spec changes - stat = `git diff --stat #{range}` if stat - - # TODO: scan for changes to app/jobs and report changes to params - formatter_attributes = { - to: to, - base: base, - revision: revision, - commits: commits, - pull_requests: pull_requests(merges, gh_url), - merges: merges, - shortstat: shortstat, - dirstat: dirstat, - stat: stat, - time_delta: time_delta, - gh_url: gh_url, - base_reference: reference(base), - to_reference: reference(to), - migrations: changed_files.migrations, - elm_packages: DeployComplexity::RevisionComparator.new( - DeployComplexity::ChangedElmPackages, changed_files.elm_packages, base, to - ).output, - ruby_dependencies: DeployComplexity::RevisionComparator.new( - DeployComplexity::ChangedRubyGems, changed_files.ruby_dependencies, base, to - ).output, - javascript_dependencies: DeployComplexity::RevisionComparator.new( - DeployComplexity::ChangedJavascriptPackages, changed_files.javascript_dependencies, base, to - ).output - } - - formatter = if slack_format - DeployComplexity::SlackOutputFormatter.with(formatter_attributes) - else - DeployComplexity::CliOutputFormatter.with(formatter_attributes) - end - - puts formatter.format + puts DeployComplexity::Deploy.new(base, to, options).generate end branch = "production" @@ -141,7 +14,6 @@ def deploy(base, to, options) action = nil options = {} -require 'optparse' optparse = OptionParser.new do |opts| opts.banner = "Usage: %s [[base branch] deploy branch]" % [File.basename($PROGRAM_NAME)] diff --git a/lib/deploy_complexity/deploy.rb b/lib/deploy_complexity/deploy.rb new file mode 100644 index 0000000..c6811da --- /dev/null +++ b/lib/deploy_complexity/deploy.rb @@ -0,0 +1,150 @@ +# frozen_string_literal: true + +require 'time' +require 'deploy_complexity/slack_output_formatter' +require 'deploy_complexity/cli_output_formatter' +require 'deploy_complexity/version' +require 'deploy_complexity/revision_comparator' +require 'deploy_complexity/changed_files' +require 'deploy_complexity/changed_elm_packages' +require 'deploy_complexity/changed_javascript_packages' +require 'deploy_complexity/changed_ruby_gems' + +module DeployComplexity + # The main module for deploy complexity that parses output from git + # and returns information on the deploy + class Deploy + # deploys are the delta from base -> to, so to contains commits to add to base + + def initialize(base, to, options) + @base = base + @to = to + @options = options + end + + def generate + gh_url = options[:gh_url] + dirstat = options[:dirstat] + stat = options[:stat] + slack_format = options[:slack_format] + + range = "#{base}...#{to}" + + # tag_revision = `git rev-parse --short #{to}`.chomp + revision = `git rev-list --abbrev-commit -n1 #{to}`.chomp + + time_delta = time_between_deploys(safe_name(base), safe_name(to)) + + commits = `git log --oneline #{range}`.split(/\n/) + merges = commits.grep(/Merges|\#\d+/) + + shortstat = `git diff --shortstat --summary #{range}`.split(/\n/) + names_only = `git diff --name-only #{range}` + versioned_url = "#{gh_url}/blob/#{safe_name(to)}/" + changed_files = DeployComplexity::ChangedFiles.new(names_only, versioned_url) + + dirstat = `git diff --dirstat=lines,cumulative #{range}` if dirstat + # TODO: investigate summarizing language / spec content based on file suffix, + # and possibly per PR, or classify frontend, backend, spec changes + stat = `git diff --stat #{range}` if stat + + # TODO: scan for changes to app/jobs and report changes to params + formatter_attributes = { + to: to, + base: base, + revision: revision, + commits: commits, + pull_requests: pull_requests(merges, gh_url), + merges: merges, + shortstat: shortstat, + dirstat: dirstat, + stat: stat, + time_delta: time_delta, + gh_url: gh_url, + base_reference: reference(base), + to_reference: reference(to), + migrations: changed_files.migrations, + elm_packages: DeployComplexity::RevisionComparator.new( + DeployComplexity::ChangedElmPackages, changed_files.elm_packages, base, to + ).output, + ruby_dependencies: DeployComplexity::RevisionComparator.new( + DeployComplexity::ChangedRubyGems, changed_files.ruby_dependencies, base, to + ).output, + javascript_dependencies: DeployComplexity::RevisionComparator.new( + DeployComplexity::ChangedJavascriptPackages, changed_files.javascript_dependencies, base, to + ).output + } + + if slack_format + DeployComplexity::SlackOutputFormatter.with(formatter_attributes).format + else + DeployComplexity::CliOutputFormatter.with(formatter_attributes).format + end + end + + private + + attr_reader :base, :to, :options + + # tag format: production-2016-10-22-0103 or $ENV-YYYY-MM-DD-HHmm + def parse_when(tag) + tag.match(/-(\d{4}-\d{2}-\d{2}-\d{2}\d{2})/) do |m| + Time.strptime(m[1], '%Y-%m-%d-%H%M') + end + end + + COMPARE_FORMAT = "%s/compare/%s...%s" + + def time_between_deploys(from, to) + deploy_time = parse_when(to) + last_time = parse_when(from) + + hours = deploy_time && (deploy_time - last_time) / 60**2 + + if hours.nil? + "pending deploy" + elsif hours < 24 + "after %2.1f %s" % [hours, "hours"] + else + "after %2.1f %s" % [(hours / 24), "days"] + end + end + + # converts origin/master -> master + def safe_name(name) + name.chomp.split(%r{/}).last + end + + # converts a branch name like master into the closest tag or commit sha + def reference(name) + branch = safe_name(name) + tag = `git tag --points-at #{name} | grep #{branch}`.chomp + if tag.empty? + `git rev-parse --short #{branch}`.chomp + else + tag + end + end + + # TODO: consider moving this to a separate parser and testing + def pull_requests(merges, gh_url) + merges.map do |line| + line.match(/pull request #(\d+) from (.*)$/) do |m| + { + gh_url: gh_url, + pr_number: m[1].to_i, + joiner: "-", + name: safe_name(m[2]) + } + end || line.match(/(\w+)\s+(.*)\(\#(\d+)\)/) do |m| + { + gh_url: gh_url, + pr_number: m[3].to_i, + joiner: "S", + name: m[2] + } + end + end.compact + end + end +end From b7a52ab6a33d8ebfc491cd2b2c15b03ac8dbba65 Mon Sep 17 00:00:00 2001 From: Brooke Angel Date: Mon, 7 Jan 2019 15:35:27 -0800 Subject: [PATCH 25/32] regen rubocop config --- .rubocop_todo.yml | 33 ++++++++++++----------------- lib/deploy_complexity/checklists.rb | 5 ----- 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 206e5bf..858b90b 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,45 +1,40 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2019-01-03 16:54:26 -0600 using RuboCop version 0.60.0. +# on 2019-01-08 10:51:37 -0800 using RuboCop version 0.60.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Offense count: 1 -# Cop supports --auto-correct. -Lint/UnneededCopDisableDirective: - Exclude: - - 'lib/deploy_complexity/checklists.rb' - -# Offense count: 6 -# Cop supports --auto-correct. -Lint/UnneededCopEnableDirective: - Exclude: - - 'exe/deploy-complexity.rb' - -# Offense count: 2 +# Offense count: 3 Metrics/AbcSize: - Max: 43 + Max: 53 # Offense count: 1 # Configuration parameters: CountComments, ExcludedMethods. # ExcludedMethods: refine Metrics/BlockLength: - Max: 32 + Max: 34 # Offense count: 2 +# Configuration parameters: CountComments. +Metrics/ClassLength: + Max: 124 + +# Offense count: 5 # Configuration parameters: CountComments, ExcludedMethods. Metrics/MethodLength: - Max: 33 + Max: 45 # Offense count: 1 Metrics/PerceivedComplexity: - Max: 10 + Max: 9 -# Offense count: 11 +# Offense count: 12 # Configuration parameters: EnforcedStyle. # SupportedStyles: annotated, template, unannotated Style/FormatStringToken: Exclude: - 'exe/deploy-complexity.rb' + - 'lib/deploy_complexity/deploy.rb' + - 'lib/deploy_complexity/output_formatter.rb' diff --git a/lib/deploy_complexity/checklists.rb b/lib/deploy_complexity/checklists.rb index fce616c..22e3dbf 100644 --- a/lib/deploy_complexity/checklists.rb +++ b/lib/deploy_complexity/checklists.rb @@ -30,10 +30,6 @@ def for_pr_body # line length checks for now. # rubocop:disable Metrics/LineLength - # Some checks are on quite a few files! They're simple checks, so the - # cyclomatic complexity here is not actually too bad for a human. - # rubocop:disable Metrics/CyclomaticComplexity - class RubyFactoriesChecklist < Checklist def human_name "Ruby Factories" @@ -216,7 +212,6 @@ def relevant_for(files) # all done! # rubocop:enable Style/Documentation # rubocop:enable Metrics/LineLength - # rubocop:enable Metrics/CyclomaticComplexity # Check for checklists, given a list of checkers class Checker From 208f00f429688a7cd9d0841f609fc3e4ba57f7cc Mon Sep 17 00:00:00 2001 From: Charles Comstock Date: Tue, 22 Jan 2019 14:32:29 -0600 Subject: [PATCH 26/32] First pass at directly reporting to slack --- exe/deploy-complexity.rb | 10 ++++++++-- lib/deploy_complexity/deploy.rb | 23 ++++++++++++++++++----- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/exe/deploy-complexity.rb b/exe/deploy-complexity.rb index e970a96..62c9e8f 100755 --- a/exe/deploy-complexity.rb +++ b/exe/deploy-complexity.rb @@ -38,8 +38,14 @@ def deploy(base, to, options) "Github project url to construct links from") do |url| options[:gh_url] = url end - opts.on("--slack-format", - "Format output for Slack") { options[:slack_format] = true } + opts.on("--slack #foo,#bar", Array, + "Report changes to slack channels") do |channels| + if channels.any? && ENV['SLACK_WEBHOOK'] + options[:slack_channels] = channels + else + STDERR.puts "Must specify slack channels & include SLACK_WEBHOOK in environment." + end + end opts.on_tail("-v", "--version", "Show version info and exit") do abort <<~BOILERPLATE deploy-complexity.rb #{DeployComplexity::VERSION} diff --git a/lib/deploy_complexity/deploy.rb b/lib/deploy_complexity/deploy.rb index c6811da..d976416 100644 --- a/lib/deploy_complexity/deploy.rb +++ b/lib/deploy_complexity/deploy.rb @@ -26,7 +26,6 @@ def generate gh_url = options[:gh_url] dirstat = options[:dirstat] stat = options[:stat] - slack_format = options[:slack_format] range = "#{base}...#{to}" @@ -75,11 +74,25 @@ def generate ).output } - if slack_format - DeployComplexity::SlackOutputFormatter.with(formatter_attributes).format - else - DeployComplexity::CliOutputFormatter.with(formatter_attributes).format + if options[:slack_channels].any? + log = DeployComplexity::SlackOutputFormatter.with(formatter_attributes).format + begin + webhook = ENV['SLACK_WEBHOOK'] + require 'slack-notifier' + channels.each do |channel| + notifier = Slack::Notifier.new webhook do + defaults channel: channel, + username: 'DeployComplexity' + end + notifier.ping log + end + rescue StandardError => e + STDERR.puts "Exception thrown while notifying slack!" + STDERR.puts e + end end + + DeployComplexity::CliOutputFormatter.with(formatter_attributes).format end private From cd78cec10ab6fd1aa3f417b5e40bb0f4e33769e3 Mon Sep 17 00:00:00 2001 From: Charles Comstock Date: Tue, 22 Jan 2019 14:36:57 -0600 Subject: [PATCH 27/32] Move slack-notifier & values into gemspec --- Gemfile | 5 ----- Gemfile.lock | 4 ++-- deploy-complexity.gemspec | 2 ++ 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Gemfile b/Gemfile index f7dcc49..eb9c0de 100644 --- a/Gemfile +++ b/Gemfile @@ -8,11 +8,6 @@ gemspec gem 'pry' gem 'pry-doc' gem 'rubocop' -gem 'values' - -group :development do - gem 'slack-notifier' -end group :test do gem 'rspec' diff --git a/Gemfile.lock b/Gemfile.lock index 94e1788..6304550 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,6 +3,8 @@ PATH specs: deploy-complexity (0.6.0) octokit (~> 4.0) + slack-notifier (~> 2.3.2) + values (~> 1.8.0) GEM remote: https://rubygems.org/ @@ -74,8 +76,6 @@ DEPENDENCIES rspec rspec-mocks rubocop - slack-notifier - values BUNDLED WITH 1.17.1 diff --git a/deploy-complexity.gemspec b/deploy-complexity.gemspec index 3ddfa9a..479c107 100644 --- a/deploy-complexity.gemspec +++ b/deploy-complexity.gemspec @@ -27,4 +27,6 @@ Gem::Specification.new do |spec| spec.add_development_dependency "rake", "~> 10.0" spec.add_dependency "octokit", "~> 4.0" + spec.add_dependency "slack-notifier", "~> 2.3.2" + spec.add_dependency "values", "~> 1.8.0" end From 83b8dd7be7fe86fe6f47d51375d0db52c246cd5f Mon Sep 17 00:00:00 2001 From: Charles Comstock Date: Tue, 22 Jan 2019 14:41:33 -0600 Subject: [PATCH 28/32] Extract slack method & set channels appropriately --- lib/deploy_complexity/deploy.rb | 36 +++++++++++++++++---------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/lib/deploy_complexity/deploy.rb b/lib/deploy_complexity/deploy.rb index d976416..8680ea0 100644 --- a/lib/deploy_complexity/deploy.rb +++ b/lib/deploy_complexity/deploy.rb @@ -74,23 +74,7 @@ def generate ).output } - if options[:slack_channels].any? - log = DeployComplexity::SlackOutputFormatter.with(formatter_attributes).format - begin - webhook = ENV['SLACK_WEBHOOK'] - require 'slack-notifier' - channels.each do |channel| - notifier = Slack::Notifier.new webhook do - defaults channel: channel, - username: 'DeployComplexity' - end - notifier.ping log - end - rescue StandardError => e - STDERR.puts "Exception thrown while notifying slack!" - STDERR.puts e - end - end + slack(formatter_attributes, options[:slack_channels]) DeployComplexity::CliOutputFormatter.with(formatter_attributes).format end @@ -99,6 +83,24 @@ def generate attr_reader :base, :to, :options + def slack(formatter_attributes, channels) + log = DeployComplexity::SlackOutputFormatter.with(formatter_attributes).format + begin + webhook = ENV['SLACK_WEBHOOK'] + require 'slack-notifier' + channels.each do |channel| + notifier = Slack::Notifier.new webhook do + defaults channel: channel, + username: 'DeployComplexity' + end + notifier.ping log + end + rescue StandardError => e + STDERR.puts "Exception thrown while notifying slack!" + STDERR.puts e + end + end + # tag format: production-2016-10-22-0103 or $ENV-YYYY-MM-DD-HHmm def parse_when(tag) tag.match(/-(\d{4}-\d{2}-\d{2}-\d{2}\d{2})/) do |m| From d459a2e3411341a2f6eb30c94730cae8141c1718 Mon Sep 17 00:00:00 2001 From: Charles Comstock Date: Tue, 22 Jan 2019 14:46:26 -0600 Subject: [PATCH 29/32] Make sure there is spacing between each deploy listed --- exe/deploy-complexity.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/exe/deploy-complexity.rb b/exe/deploy-complexity.rb index 62c9e8f..ede8903 100755 --- a/exe/deploy-complexity.rb +++ b/exe/deploy-complexity.rb @@ -7,6 +7,7 @@ def deploy(base, to, options) puts DeployComplexity::Deploy.new(base, to, options).generate + puts end branch = "production" From 1e9a38a54b933777f456164750d1b06fef2c89b4 Mon Sep 17 00:00:00 2001 From: Charles Comstock Date: Tue, 22 Jan 2019 16:23:19 -0600 Subject: [PATCH 30/32] Disable block length complaint for OptionParser block --- .rubocop_todo.yml | 14 ++++---------- exe/deploy-complexity.rb | 2 +- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 858b90b..d8d0ba8 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2019-01-08 10:51:37 -0800 using RuboCop version 0.60.0. +# on 2019-01-22 16:22:58 -0600 using RuboCop version 0.60.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -8,23 +8,17 @@ # Offense count: 3 Metrics/AbcSize: - Max: 53 - -# Offense count: 1 -# Configuration parameters: CountComments, ExcludedMethods. -# ExcludedMethods: refine -Metrics/BlockLength: - Max: 34 + Max: 52 # Offense count: 2 # Configuration parameters: CountComments. Metrics/ClassLength: Max: 124 -# Offense count: 5 +# Offense count: 6 # Configuration parameters: CountComments, ExcludedMethods. Metrics/MethodLength: - Max: 45 + Max: 41 # Offense count: 1 Metrics/PerceivedComplexity: diff --git a/exe/deploy-complexity.rb b/exe/deploy-complexity.rb index ede8903..12f0d91 100755 --- a/exe/deploy-complexity.rb +++ b/exe/deploy-complexity.rb @@ -15,7 +15,7 @@ def deploy(base, to, options) action = nil options = {} -optparse = OptionParser.new do |opts| +optparse = OptionParser.new do |opts| # rubocop:disable Metrics/BlockLength opts.banner = "Usage: %s [[base branch] deploy branch]" % [File.basename($PROGRAM_NAME)] opts.on("-b", "--branch BRANCH", String, "Specify the base branch") do |e| From c4c451c62d3fb67535a1a14ec767fbc4a3deeab1 Mon Sep 17 00:00:00 2001 From: Charles Comstock Date: Tue, 22 Jan 2019 18:18:49 -0600 Subject: [PATCH 31/32] Guard against no channels specified --- lib/deploy_complexity/deploy.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/deploy_complexity/deploy.rb b/lib/deploy_complexity/deploy.rb index 8680ea0..2c4eb5c 100644 --- a/lib/deploy_complexity/deploy.rb +++ b/lib/deploy_complexity/deploy.rb @@ -84,6 +84,8 @@ def generate attr_reader :base, :to, :options def slack(formatter_attributes, channels) + return if channels.nil? || channels.none? + log = DeployComplexity::SlackOutputFormatter.with(formatter_attributes).format begin webhook = ENV['SLACK_WEBHOOK'] From fb1612ba93a6203da9ae9c8bd112b7bc91455ee2 Mon Sep 17 00:00:00 2001 From: Charles Comstock Date: Tue, 22 Jan 2019 20:52:44 -0600 Subject: [PATCH 32/32] Extract safe_name into Git submodule to share it --- exe/deploy-complexity.rb | 2 +- lib/deploy_complexity/deploy.rb | 14 +++++--------- lib/deploy_complexity/git.rb | 13 +++++++++++++ 3 files changed, 19 insertions(+), 10 deletions(-) create mode 100644 lib/deploy_complexity/git.rb diff --git a/exe/deploy-complexity.rb b/exe/deploy-complexity.rb index 12f0d91..f89eea7 100755 --- a/exe/deploy-complexity.rb +++ b/exe/deploy-complexity.rb @@ -19,7 +19,7 @@ def deploy(base, to, options) opts.banner = "Usage: %s [[base branch] deploy branch]" % [File.basename($PROGRAM_NAME)] opts.on("-b", "--branch BRANCH", String, "Specify the base branch") do |e| - branch = safe_name(e) || branch + branch = DeployComplexity::Git.safe_name(e) || branch end opts.on("-d", "--deploys [N]", Integer, "Show historical deploys, shows all if N is not specified") do |e| diff --git a/lib/deploy_complexity/deploy.rb b/lib/deploy_complexity/deploy.rb index 2c4eb5c..94f4739 100644 --- a/lib/deploy_complexity/deploy.rb +++ b/lib/deploy_complexity/deploy.rb @@ -9,6 +9,7 @@ require 'deploy_complexity/changed_elm_packages' require 'deploy_complexity/changed_javascript_packages' require 'deploy_complexity/changed_ruby_gems' +require 'deploy_complexity/git' module DeployComplexity # The main module for deploy complexity that parses output from git @@ -32,14 +33,14 @@ def generate # tag_revision = `git rev-parse --short #{to}`.chomp revision = `git rev-list --abbrev-commit -n1 #{to}`.chomp - time_delta = time_between_deploys(safe_name(base), safe_name(to)) + time_delta = time_between_deploys(Git.safe_name(base), Git.safe_name(to)) commits = `git log --oneline #{range}`.split(/\n/) merges = commits.grep(/Merges|\#\d+/) shortstat = `git diff --shortstat --summary #{range}`.split(/\n/) names_only = `git diff --name-only #{range}` - versioned_url = "#{gh_url}/blob/#{safe_name(to)}/" + versioned_url = "#{gh_url}/blob/#{Git.safe_name(to)}/" changed_files = DeployComplexity::ChangedFiles.new(names_only, versioned_url) dirstat = `git diff --dirstat=lines,cumulative #{range}` if dirstat @@ -127,14 +128,9 @@ def time_between_deploys(from, to) end end - # converts origin/master -> master - def safe_name(name) - name.chomp.split(%r{/}).last - end - # converts a branch name like master into the closest tag or commit sha def reference(name) - branch = safe_name(name) + branch = Git.safe_name(name) tag = `git tag --points-at #{name} | grep #{branch}`.chomp if tag.empty? `git rev-parse --short #{branch}`.chomp @@ -151,7 +147,7 @@ def pull_requests(merges, gh_url) gh_url: gh_url, pr_number: m[1].to_i, joiner: "-", - name: safe_name(m[2]) + name: Git.safe_name(m[2]) } end || line.match(/(\w+)\s+(.*)\(\#(\d+)\)/) do |m| { diff --git a/lib/deploy_complexity/git.rb b/lib/deploy_complexity/git.rb new file mode 100644 index 0000000..4685bf2 --- /dev/null +++ b/lib/deploy_complexity/git.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module DeployComplexity + # Helper functions for managing git remotes and branch names + module Git + module_function + + # converts origin/master -> master + def safe_name(name) + name.chomp.sub(%r{^[^/]+/}, '') + end + end +end