whatsup_github 0.2.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Rakefile CHANGED
@@ -1,14 +1,16 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
3
5
  require 'cucumber'
4
6
  require 'cucumber/rake/task'
5
7
 
6
8
  RSpec::Core::RakeTask.new(:spec)
7
9
 
8
- task :default => :spec
10
+ task default: :spec
9
11
 
10
12
  Cucumber::Rake::Task.new(:features) do |t|
11
- t.cucumber_opts = "--format pretty" # Any valid command line option can go here.
13
+ t.cucumber_opts = '--format pretty' # Any valid command line option can go here.
12
14
  end
13
15
 
14
- task :test => [:spec, :features]
16
+ task test: %i[spec features]
data/bin/console CHANGED
@@ -1,13 +1,14 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- require "bundler/setup"
4
- require "whatsup_github"
4
+ require 'bundler/setup'
5
+ require 'whatsup_github'
5
6
 
6
7
  # You can add fixtures and/or initialization code here to make experimenting
7
8
  # with your gem easier. You can also use a different console, if you like.
8
9
 
9
10
  # (If you use this, don't forget to add pry to your Gemfile!)
10
- require "pry"
11
+ require 'pry'
11
12
  Pry.start
12
13
 
13
14
  # require "irb"
data/exe/whatsup_github CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'whatsup_github/cli'
4
5
  WhatsupGithub::CLI.start(ARGV)
@@ -1,8 +1,10 @@
1
1
  # Parameters for a GitHub search query
2
2
  base_branch: master
3
3
 
4
+ # The list of repositories to scan for pull requests
4
5
  repos:
5
6
  - magento/devdocs
7
+ - magento-commerce/devdocs
6
8
 
7
9
  # Labels also will be used as a 'type' value in the output file
8
10
  labels:
@@ -16,3 +18,11 @@ labels:
16
18
  output_format:
17
19
  - yaml
18
20
  # - markdown
21
+
22
+ # The phrase that is used as a separator in the pull request descripion.
23
+ # All the lines that follows this phrase are captured as 'description' for this PR's entry in the resulted data file.
24
+ magic_word: whatsnew
25
+
26
+ # An organization to check a contributor for membership.
27
+ # Values: 'true', 'false', empry if not configured.
28
+ membership: magento-commerce
@@ -1,6 +1,6 @@
1
- require "whatsup_github/version"
1
+ # frozen_string_literal: true
2
2
 
3
+ require 'whatsup_github/version'
3
4
 
4
5
  module WhatsupGithub
5
-
6
6
  end
@@ -1,14 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'thor'
2
- require "whatsup_github/runner"
4
+ require 'whatsup_github/runner'
3
5
  module WhatsupGithub
4
6
  class CLI < Thor
5
- desc "since DATE", "Filters pull requests since the specified date till now."
7
+ desc 'since DATE', 'Filters pull requests since the specified date till now.'
6
8
 
7
9
  def since(date = Date.today - 7)
8
-
9
10
  runner = WhatsupGithub::Runner.new(Date.parse(date.to_s))
10
11
  runner.run
11
12
  end
12
13
  default_task :since
13
14
  end
14
- end
15
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+
5
+ # Client authorization
6
+ module WhatsupGithub
7
+ # Create a singleton object for Client.
8
+ # Authorize with a GitHub token from $WHATSUP_GITHUB_ACCESS_TOKEN if available
9
+ # Otherwise, use credentials from ~/.netrc
10
+ # Otherwise, continue as a Guest
11
+ class Client
12
+ include Singleton
13
+
14
+ WHATSUP_GITHUB_ACCESS_TOKEN = ENV['WHATSUP_GITHUB_ACCESS_TOKEN']
15
+ private_constant :WHATSUP_GITHUB_ACCESS_TOKEN
16
+
17
+ def initialize
18
+ @client =
19
+ if WHATSUP_GITHUB_ACCESS_TOKEN
20
+ Octokit::Client.new(access_token: WHATSUP_GITHUB_ACCESS_TOKEN)
21
+ elsif File.exist? "#{ENV['HOME']}/.netrc"
22
+ Octokit::Client.new(netrc: true)
23
+ else
24
+ Octokit::Client.new
25
+ end
26
+ end
27
+
28
+ def search_issues(query)
29
+ @client.search_issues(query)
30
+ end
31
+
32
+ def pull_request(repo, number)
33
+ @client.pull_request(repo, number)
34
+ end
35
+
36
+ def org_members(org)
37
+ @client.org_members(org)
38
+ end
39
+ end
40
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'yaml'
2
4
  require 'singleton'
3
5
 
@@ -5,6 +7,7 @@ module WhatsupGithub
5
7
  # Creates readable objects from confirurarion files
6
8
  class Config
7
9
  attr_reader :config
10
+
8
11
  include Singleton
9
12
 
10
13
  def initialize
@@ -18,20 +21,21 @@ module WhatsupGithub
18
21
  FileUtils.cp dist_file, @file
19
22
  end
20
23
  @config = YAML.load_file @file
21
- return {} if !@config
24
+ return {} unless @config
25
+
22
26
  @config
23
27
  end
24
28
 
25
29
  def repos
26
- read.dig 'repos'
30
+ read['repos']
27
31
  end
28
32
 
29
33
  def base_branch
30
- read.dig 'base_branch'
34
+ read['base_branch']
31
35
  end
32
36
 
33
37
  def output_format
34
- read.dig 'output_format'
38
+ read['output_format']
35
39
  end
36
40
 
37
41
  def labels
@@ -41,14 +45,24 @@ module WhatsupGithub
41
45
  def required_labels
42
46
  res = read.dig 'labels', 'required'
43
47
  return [] unless res
48
+
44
49
  res
45
50
  end
46
51
 
47
52
  def optional_labels
48
53
  res = read.dig 'labels', 'optional'
49
54
  return [] unless res
55
+
50
56
  res
51
57
  end
58
+
59
+ def membership
60
+ read['membership']
61
+ end
62
+
63
+ def magic_word
64
+ read['magic_word']
65
+ end
52
66
  end
53
67
  end
54
68
 
@@ -60,4 +74,6 @@ if $PROGRAM_NAME == __FILE__
60
74
  p config.labels
61
75
  p config.required_labels
62
76
  p config.optional_labels
63
- end
77
+ p config.magic_word
78
+ p config.membership
79
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WhatsupGithub
2
4
  # Creates the final table
3
5
  class Generator
@@ -9,7 +11,7 @@ module WhatsupGithub
9
11
  @collector.sort_by_date
10
12
  end
11
13
 
12
- def run formatter, content
14
+ def run(formatter, content)
13
15
  formatter.generate_output_from content
14
16
  end
15
17
  end
@@ -1,8 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'octokit'
2
- require_relative 'config-reader'
4
+ require_relative 'config_reader'
5
+ require_relative 'client'
3
6
 
4
7
  module WhatsupGithub
5
- # Gets pull filtered pull requests from GitHub
8
+ # Gets issues found on GitHub by query
6
9
  class Pulls
7
10
  attr_reader :since, :repo
8
11
 
@@ -11,12 +14,12 @@ module WhatsupGithub
11
14
  @since = args[:since]
12
15
  end
13
16
 
14
- def filtered
15
- issues = []
16
- labels.each do |label|
17
- issues += search_issues(label).items
17
+ def data
18
+ pull_requests = []
19
+ filtered_numbers.each do |number|
20
+ pull_requests << client.pull_request(@repo, number)
18
21
  end
19
- issues
22
+ pull_requests
20
23
  end
21
24
 
22
25
  private
@@ -29,26 +32,61 @@ module WhatsupGithub
29
32
  Config.instance
30
33
  end
31
34
 
32
- def labels
33
- configuration.labels
35
+ def optional_labels
36
+ configuration.optional_labels
37
+ end
38
+
39
+ def required_labels
40
+ configuration.required_labels
41
+ end
42
+
43
+ def magic_word
44
+ configuration.magic_word
34
45
  end
35
46
 
36
47
  def base_branch
37
48
  configuration.base_branch
38
49
  end
39
50
 
51
+ # Authorize with a GitHub token from $WHATSUP_GITHUB_ACCESS_TOKEN if available
52
+ # Otherwise, use credentials from ~/.netrc
53
+ # Otherwise, continue as a Guest
40
54
  def client
41
- Octokit::Client.new(:netrc => true)
55
+ Client.instance
42
56
  end
43
57
 
44
58
  def search_issues(label)
45
59
  auto_paginate
46
- client.search_issues("repo:#{repo} label:\"#{label}\" merged:>=#{since} base:#{base_branch}")
60
+ query = "repo:#{repo} label:\"#{label}\" merged:>=#{since} base:#{base_branch}"
61
+ puts "Searching on GitHub by query #{query}"
62
+ client.search_issues(query)
63
+ end
64
+
65
+ def search_issues_with_magic_word(label)
66
+ auto_paginate
67
+ query = "repo:#{repo} label:\"#{label}\" merged:>=#{since} base:#{base_branch} \"#{magic_word}\" in:body"
68
+ puts "Searching on GitHub by query #{query}"
69
+ client.search_issues(query)
47
70
  end
48
71
 
49
72
  def auto_paginate
50
73
  Octokit.auto_paginate = true
51
74
  end
75
+
76
+ def filtered_issues
77
+ issues = []
78
+ required_labels.each do |label|
79
+ issues += search_issues(label).items
80
+ end
81
+ optional_labels.each do |label|
82
+ issues += search_issues_with_magic_word(label).items
83
+ end
84
+ issues
85
+ end
86
+
87
+ def filtered_numbers
88
+ filtered_issues.map(&:number)
89
+ end
52
90
  end
53
91
  end
54
92
 
@@ -56,5 +94,5 @@ if $PROGRAM_NAME == __FILE__
56
94
  require 'date'
57
95
  two_weeks_ago = (Date.today - 14).to_s
58
96
  pulls = WhatsupGithub::Pulls.new(repo: 'magento/devdocs', since: two_weeks_ago)
59
- p pulls.filtered
60
- end
97
+ p pulls.data
98
+ end
@@ -1,16 +1,35 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module WhatsupGithub
2
4
  # Row to be converted to entry in future table
3
5
  class Row
4
- attr_reader :body, :title, :labels, :pr_number, :assignee, :link
6
+ attr_reader :body,
7
+ :title,
8
+ :labels,
9
+ :link,
10
+ :pr_number,
11
+ :assignee,
12
+ :author,
13
+ :author_url,
14
+ :merge_commit,
15
+ :is_private,
16
+ :membership
17
+
5
18
  def initialize(args)
6
19
  @repo = args[:repo]
20
+ @repo_url = args[:repo_url]
21
+ @is_private = args[:private]
7
22
  @title = args[:pr_title]
8
23
  @body = args[:pr_body]
9
24
  @date = args[:date]
10
25
  @labels = args[:pr_labels]
11
26
  @assignee = args[:assignee]
27
+ @author = args[:author]
28
+ @author_url = args[:author_url]
12
29
  @pr_number = args[:pr_number]
13
30
  @link = args[:pr_url]
31
+ @merge_commit = args[:merge_commit_sha]
32
+ @membership = args[:membership]
14
33
  @config = Config.instance
15
34
  end
16
35
 
@@ -22,6 +41,10 @@ module WhatsupGithub
22
41
  @config.required_labels
23
42
  end
24
43
 
44
+ def magic_word
45
+ @config.magic_word
46
+ end
47
+
25
48
  def versions
26
49
  label_versions = labels.select { |label| label.start_with?(/\d\./) }
27
50
  label_versions.join(', ')
@@ -36,8 +59,10 @@ module WhatsupGithub
36
59
  end
37
60
 
38
61
  def parse_body
39
- whatsnew_splited = body.split('whatsnew')[-1]
40
- newline_splited = whatsnew_splited.split("\n")
62
+ # Split PR description in two parts by 'magic word', and take the second half
63
+ description_splited = body.split(magic_word)[-1]
64
+ # Convert new line separators to <br> tags
65
+ newline_splited = description_splited.split("\n")
41
66
  cleaned_array = newline_splited.map { |e| e.delete "\r\*" }
42
67
  cleaned_array.delete('')
43
68
  striped_array = cleaned_array.map(&:strip)
@@ -47,10 +72,10 @@ module WhatsupGithub
47
72
  def description
48
73
  # If a PR body includes a phrase 'whatsnew', then parse the body.
49
74
  # If there are at least one required label but PR body does not include what's new, warn about missing 'whatsnew'
50
- if body.include?('whatsnew')
75
+ if body.include?(magic_word)
51
76
  parse_body
52
- elsif !(labels & required_labels).empty? && !body.include?('whatsnew')
53
- message = "MISSING whatsnew in the #{type} PR \##{pr_number}: \"#{title}\" assigned to #{assignee} (#{link})"
77
+ else
78
+ message = "MISSING #{magic_word} in the #{type} PR \##{pr_number}: \"#{title}\" assigned to #{assignee} (#{link})"
54
79
  puts message
55
80
  message
56
81
  end
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'row'
2
4
  require_relative 'pulls'
3
- require_relative 'config-reader'
5
+ require_relative 'config_reader'
4
6
 
5
7
  module WhatsupGithub
6
8
  # Creates Row objects for the future table
@@ -24,12 +26,19 @@ module WhatsupGithub
24
26
  pulls(repo).map do |pull|
25
27
  Row.new(
26
28
  repo: repo,
29
+ repo_url: pull.base.repo.html_url,
30
+ private: pull.base.repo.private?,
27
31
  pr_number: pull.number,
28
32
  pr_title: pull.title,
29
33
  pr_body: pull.body,
30
- date: pull.closed_at,
34
+ date: pull.merged_at,
31
35
  pr_labels: label_names(pull.labels),
32
- assignee: assignee(pull.assignee),
36
+ assignee: assignee(pull.assignees),
37
+ membership: member?(pull.user.login),
38
+ merger: pull.merged_by.login,
39
+ merge_commit_sha: pull.merge_commit_sha,
40
+ author: pull.user.login,
41
+ author_url: pull.user.html_url,
33
42
  pr_url: pull.html_url
34
43
  )
35
44
  end
@@ -47,24 +56,45 @@ module WhatsupGithub
47
56
 
48
57
  private
49
58
 
50
- def assignee(assignee)
51
- if assignee.nil?
59
+ def assignee(assignees)
60
+ if assignees.empty?
52
61
  'NOBODY'
53
62
  else
54
- assignee.login
63
+ assignees.map(&:login).join(', ')
55
64
  end
56
65
  end
57
66
 
67
+ def member?(login)
68
+ return nil unless config.membership
69
+
70
+ member_logins.include? login
71
+ end
72
+
58
73
  def label_names(labels)
59
74
  labels.map(&:name)
60
75
  end
61
76
 
62
77
  def pulls(repo)
63
- Pulls.new(repo: repo, since: since).filtered
78
+ Pulls.new(repo: repo, since: since).data
79
+ end
80
+
81
+ def load_members
82
+ return if @members
83
+
84
+ @members = client.org_members(config.membership)
85
+ end
86
+
87
+ def member_logins
88
+ load_members
89
+ @members.map(&:login)
64
90
  end
65
91
 
66
92
  def config
67
93
  Config.instance
68
94
  end
95
+
96
+ def client
97
+ Client.instance
98
+ end
69
99
  end
70
100
  end