whatsup_github 0.2.0 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
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