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.
- checksums.yaml +4 -4
- data/.github/linters/.ruby-lint.yml +11 -0
- data/.github/workflows/linter.yml +48 -0
- data/.ruby-version +1 -1
- data/.travis.yml +2 -1
- data/CHANGELOG.md +47 -0
- data/Gemfile +4 -2
- data/Gemfile.lock +94 -57
- data/README.md +39 -10
- data/Rakefile +7 -5
- data/bin/console +4 -3
- data/exe/whatsup_github +1 -0
- data/lib/template/.whatsup.yml +10 -0
- data/lib/whatsup_github.rb +2 -2
- data/lib/whatsup_github/cli.rb +5 -4
- data/lib/whatsup_github/client.rb +40 -0
- data/lib/whatsup_github/{config-reader.rb → config_reader.rb} +21 -5
- data/lib/whatsup_github/generator.rb +3 -1
- data/lib/whatsup_github/pulls.rb +51 -13
- data/lib/whatsup_github/row.rb +31 -6
- data/lib/whatsup_github/row_collector.rb +37 -7
- data/lib/whatsup_github/runner.rb +6 -4
- data/lib/whatsup_github/table.rb +2 -1
- data/lib/whatsup_github/version.rb +3 -1
- data/lib/whatsup_github/{yaml-formatter.rb → yaml_formatter.rb} +8 -2
- data/whatsup_github.gemspec +27 -26
- metadata +36 -33
data/Rakefile
CHANGED
@@ -1,14 +1,16 @@
|
|
1
|
-
|
2
|
-
|
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 :
|
10
|
+
task default: :spec
|
9
11
|
|
10
12
|
Cucumber::Rake::Task.new(:features) do |t|
|
11
|
-
t.cucumber_opts =
|
13
|
+
t.cucumber_opts = '--format pretty' # Any valid command line option can go here.
|
12
14
|
end
|
13
15
|
|
14
|
-
task :
|
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
|
4
|
-
require
|
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
|
11
|
+
require 'pry'
|
11
12
|
Pry.start
|
12
13
|
|
13
14
|
# require "irb"
|
data/exe/whatsup_github
CHANGED
data/lib/template/.whatsup.yml
CHANGED
@@ -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
|
data/lib/whatsup_github.rb
CHANGED
data/lib/whatsup_github/cli.rb
CHANGED
@@ -1,14 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'thor'
|
2
|
-
require
|
4
|
+
require 'whatsup_github/runner'
|
3
5
|
module WhatsupGithub
|
4
6
|
class CLI < Thor
|
5
|
-
desc
|
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 {}
|
24
|
+
return {} unless @config
|
25
|
+
|
22
26
|
@config
|
23
27
|
end
|
24
28
|
|
25
29
|
def repos
|
26
|
-
read
|
30
|
+
read['repos']
|
27
31
|
end
|
28
32
|
|
29
33
|
def base_branch
|
30
|
-
read
|
34
|
+
read['base_branch']
|
31
35
|
end
|
32
36
|
|
33
37
|
def output_format
|
34
|
-
read
|
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
|
-
|
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
|
14
|
+
def run(formatter, content)
|
13
15
|
formatter.generate_output_from content
|
14
16
|
end
|
15
17
|
end
|
data/lib/whatsup_github/pulls.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'octokit'
|
2
|
-
require_relative '
|
4
|
+
require_relative 'config_reader'
|
5
|
+
require_relative 'client'
|
3
6
|
|
4
7
|
module WhatsupGithub
|
5
|
-
# Gets
|
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
|
15
|
-
|
16
|
-
|
17
|
-
|
17
|
+
def data
|
18
|
+
pull_requests = []
|
19
|
+
filtered_numbers.each do |number|
|
20
|
+
pull_requests << client.pull_request(@repo, number)
|
18
21
|
end
|
19
|
-
|
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
|
33
|
-
configuration.
|
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
|
-
|
55
|
+
Client.instance
|
42
56
|
end
|
43
57
|
|
44
58
|
def search_issues(label)
|
45
59
|
auto_paginate
|
46
|
-
|
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.
|
60
|
-
end
|
97
|
+
p pulls.data
|
98
|
+
end
|
data/lib/whatsup_github/row.rb
CHANGED
@@ -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,
|
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
|
-
|
40
|
-
|
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?(
|
75
|
+
if body.include?(magic_word)
|
51
76
|
parse_body
|
52
|
-
|
53
|
-
message = "MISSING
|
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 '
|
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.
|
34
|
+
date: pull.merged_at,
|
31
35
|
pr_labels: label_names(pull.labels),
|
32
|
-
assignee: assignee(pull.
|
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(
|
51
|
-
if
|
59
|
+
def assignee(assignees)
|
60
|
+
if assignees.empty?
|
52
61
|
'NOBODY'
|
53
62
|
else
|
54
|
-
|
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).
|
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
|