how_is 19.0.0 → 20.0.0
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 +5 -5
- data/.codeclimate.yml +1 -0
- data/.rubocop.yml +16 -0
- data/CHANGELOG.md +30 -3
- data/Gemfile +0 -6
- data/README.md +5 -2
- data/exe/how_is +7 -2
- data/fixtures/vcr_cassettes/how-is-example-empty-repository.yml +446 -727
- data/fixtures/vcr_cassettes/how-is-example-repository.yml +508 -727
- data/fixtures/vcr_cassettes/how-is-from-config-frontmatter.yml +43522 -1218
- data/fixtures/vcr_cassettes/how-is-how-is-travis-api-repos-builds.yml +287 -0
- data/fixtures/vcr_cassettes/how-is-with-config-file.yml +44307 -23286
- data/fixtures/vcr_cassettes/how_is_contributions_additions_count.yml +307 -0
- data/fixtures/vcr_cassettes/how_is_contributions_all_contributors.yml +81 -0
- data/fixtures/vcr_cassettes/how_is_contributions_changed_files.yml +307 -0
- data/fixtures/vcr_cassettes/how_is_contributions_changes.yml +307 -0
- data/fixtures/vcr_cassettes/how_is_contributions_commits.yml +307 -0
- data/fixtures/vcr_cassettes/how_is_contributions_compare_url.yml +74 -0
- data/fixtures/vcr_cassettes/how_is_contributions_deletions_count.yml +307 -0
- data/fixtures/vcr_cassettes/how_is_contributions_new_contributors.yml +234 -0
- data/fixtures/vcr_cassettes/how_is_contributions_summary.yml +378 -0
- data/fixtures/vcr_cassettes/how_is_contributions_summary_2.yml +378 -0
- data/fixtures/vcr_cassettes/how_is_fetcher_call.yml +572 -0
- data/how_is.gemspec +3 -2
- data/lib/how_is/cli.rb +4 -6
- data/lib/how_is/frontmatter.rb +46 -0
- data/lib/how_is/report.rb +59 -63
- data/lib/how_is/sources/github/contributions.rb +164 -0
- data/lib/how_is/sources/github/issues.rb +142 -0
- data/lib/how_is/sources/github/pulls.rb +20 -0
- data/lib/how_is/sources/github.rb +11 -0
- data/lib/how_is/sources/github_helpers.rb +191 -0
- data/lib/how_is/sources/travis.rb +34 -0
- data/lib/how_is/sources.rb +9 -0
- data/lib/how_is/templates/issues_or_pulls_partial.html_template +7 -0
- data/lib/how_is/templates/report.html_template +27 -0
- data/lib/how_is/templates/report_partial.html_template +12 -0
- data/lib/how_is/version.rb +2 -2
- data/lib/how_is.rb +59 -158
- metadata +42 -16
- data/lib/how_is/analyzer.rb +0 -222
- data/lib/how_is/builds.rb +0 -36
- data/lib/how_is/contributions.rb +0 -156
- data/lib/how_is/fetcher.rb +0 -77
- data/lib/how_is/pulse.rb +0 -47
- data/lib/how_is/report/base_report.rb +0 -148
- data/lib/how_is/report/html.rb +0 -120
- data/lib/how_is/report/json.rb +0 -34
@@ -0,0 +1,191 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "how_is/sources/github"
|
4
|
+
require "date"
|
5
|
+
|
6
|
+
module HowIs
|
7
|
+
module Sources
|
8
|
+
module GithubHelpers
|
9
|
+
def obj_to_array_of_hashes(object)
|
10
|
+
object.to_a.map(&:to_h)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Given an Array of issues or pulls, return a Hash specifying how many
|
14
|
+
# issues or pulls use each label.
|
15
|
+
def num_with_label(issues_or_pulls)
|
16
|
+
# Returned hash maps labels to frequency.
|
17
|
+
# E.g., given 10 issues/pulls with label "label1" and 5 with label "label2",
|
18
|
+
# {
|
19
|
+
# "label1" => 10,
|
20
|
+
# "label2" => 5
|
21
|
+
# }
|
22
|
+
|
23
|
+
hash = Hash.new(0)
|
24
|
+
issues_or_pulls.each do |iop|
|
25
|
+
next unless iop["labels"]
|
26
|
+
|
27
|
+
iop["labels"].each do |label|
|
28
|
+
hash[label["name"]] += 1
|
29
|
+
end
|
30
|
+
end
|
31
|
+
hash
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the number of issues with no label.
|
35
|
+
def num_with_no_label(issues)
|
36
|
+
issues.select { |x| x["labels"].empty? }.length
|
37
|
+
end
|
38
|
+
|
39
|
+
# Given an Array of dates, average the timestamps and return the date that
|
40
|
+
# represents.
|
41
|
+
def average_date_for(issues_or_pulls)
|
42
|
+
timestamps = issues_or_pulls.map { |iop| DateTime.parse(iop["created_at"]).strftime("%s").to_i }
|
43
|
+
average_timestamp = timestamps.reduce(:+) / issues_or_pulls.length
|
44
|
+
|
45
|
+
DateTime.strptime(average_timestamp.to_s, "%s")
|
46
|
+
end
|
47
|
+
|
48
|
+
# Given an Array of issues or pulls, return the average age of them.
|
49
|
+
# Returns nil if no issues or pulls are provided.
|
50
|
+
def average_age_for(issues_or_pulls)
|
51
|
+
return nil if issues_or_pulls.empty?
|
52
|
+
|
53
|
+
ages = issues_or_pulls.map { |iop| time_ago_in_seconds(iop["created_at"]) }
|
54
|
+
average_age_in_seconds = ages.reduce(:+) / ages.length
|
55
|
+
|
56
|
+
values = period_pairs_for(average_age_in_seconds).reject { |(v, _)| v.zero? }.map { |(v, k)|
|
57
|
+
k += "s" if v != 1
|
58
|
+
[v, k]
|
59
|
+
}
|
60
|
+
|
61
|
+
most_significant = values[0, 2].map { |x| x.join(" ") }
|
62
|
+
|
63
|
+
value =
|
64
|
+
if most_significant.length < 2
|
65
|
+
most_significant.first
|
66
|
+
else
|
67
|
+
most_significant.join(" and ")
|
68
|
+
end
|
69
|
+
|
70
|
+
"approximately #{value}"
|
71
|
+
end
|
72
|
+
|
73
|
+
def sort_iops_by_created_at(issues_or_pulls)
|
74
|
+
issues_or_pulls.sort_by { |x| DateTime.parse(x["created_at"]) }
|
75
|
+
end
|
76
|
+
|
77
|
+
# Given an Array of issues or pulls, return the oldest.
|
78
|
+
# Returns nil if no issues or pulls are provided.
|
79
|
+
def oldest_for(issues_or_pulls)
|
80
|
+
return nil if issues_or_pulls.empty?
|
81
|
+
|
82
|
+
iop = sort_iops_by_created_at(issues_or_pulls).first
|
83
|
+
|
84
|
+
{
|
85
|
+
created_at: iop["created_at"],
|
86
|
+
link: iop["html_url"],
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
# Given an Array of issues or pulls, return the newest.
|
91
|
+
# Returns nil if no issues or pulls are provided.
|
92
|
+
def newest_for(issues_or_pulls)
|
93
|
+
return nil if issues_or_pulls.empty?
|
94
|
+
|
95
|
+
iop = sort_iops_by_created_at(issues_or_pulls).last
|
96
|
+
|
97
|
+
{
|
98
|
+
created_at: iop["created_at"],
|
99
|
+
link: iop["html_url"],
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
# Given an issue or PR, returns the date it was created.
|
104
|
+
def date_for(issue_or_pull)
|
105
|
+
DateTime.parse(issue_or_pull["created_at"])
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
# Takes an Array of labels, and returns amodified list that includes links
|
111
|
+
# to each label.
|
112
|
+
def with_label_links(labels, repository)
|
113
|
+
labels.map { |label, num_issues|
|
114
|
+
label_link = "https://github.com/#{repository}/issues?q=" + CGI.escape("is:open is:issue label:\"#{label}\"")
|
115
|
+
|
116
|
+
[label, {"link" => label_link, "total" => num_issues}]
|
117
|
+
}.to_h
|
118
|
+
end
|
119
|
+
|
120
|
+
# Returns how many seconds ago a date (as a String) was.
|
121
|
+
def time_ago_in_seconds(x)
|
122
|
+
DateTime.now.strftime("%s").to_i - DateTime.parse(x).strftime("%s").to_i
|
123
|
+
end
|
124
|
+
|
125
|
+
def issue_or_pull_to_hash(iop)
|
126
|
+
return nil if iop.nil?
|
127
|
+
|
128
|
+
ret = {}
|
129
|
+
|
130
|
+
ret["html_url"] = iop["html_url"]
|
131
|
+
ret["number"] = iop["number"]
|
132
|
+
ret["date"] = date_for(iop)
|
133
|
+
|
134
|
+
ret
|
135
|
+
end
|
136
|
+
|
137
|
+
SECONDS_IN_A_YEAR = 31_556_926
|
138
|
+
SECONDS_IN_A_MONTH = 2_629_743
|
139
|
+
SECONDS_IN_A_WEEK = 604_800
|
140
|
+
SECONDS_IN_A_DAY = 86_400
|
141
|
+
|
142
|
+
# Calculates a list of pairs of value and period label.
|
143
|
+
#
|
144
|
+
# @param age_in_seconds [Float]
|
145
|
+
#
|
146
|
+
# @return [Array<Array>] The input age_in_seconds expressed as different
|
147
|
+
# units, as pairs of value and unit name.
|
148
|
+
def period_pairs_for(age_in_seconds)
|
149
|
+
years_remainder = age_in_seconds % SECONDS_IN_A_YEAR
|
150
|
+
|
151
|
+
months_remainder = years_remainder % SECONDS_IN_A_MONTH
|
152
|
+
|
153
|
+
weeks_remainder = months_remainder % SECONDS_IN_A_WEEK
|
154
|
+
|
155
|
+
[
|
156
|
+
[age_in_seconds / SECONDS_IN_A_YEAR, "year"],
|
157
|
+
[years_remainder / SECONDS_IN_A_MONTH, "month"],
|
158
|
+
[months_remainder / SECONDS_IN_A_WEEK, "week"],
|
159
|
+
[weeks_remainder / SECONDS_IN_A_DAY, "day"],
|
160
|
+
]
|
161
|
+
end
|
162
|
+
|
163
|
+
def pluralize(string, number, zero_is_no: false)
|
164
|
+
number_str = number
|
165
|
+
number_str = "no" if number.zero? && zero_is_no
|
166
|
+
|
167
|
+
"#{number_str} #{string}#{(number == 1) ? '' : 's'}"
|
168
|
+
end
|
169
|
+
|
170
|
+
def are_or_is(number)
|
171
|
+
if number == 1
|
172
|
+
"is"
|
173
|
+
else
|
174
|
+
"are"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def pretty_date(date_or_str)
|
179
|
+
if date_or_str.is_a?(DateTime)
|
180
|
+
date = datetime_or_str
|
181
|
+
elsif date_or_str.is_a?(String)
|
182
|
+
date = DateTime.parse(date_or_str)
|
183
|
+
else
|
184
|
+
raise ArgumentError, "expected DateTime or String, got #{date_or_str.class}"
|
185
|
+
end
|
186
|
+
|
187
|
+
date.strftime("%b %_d, %Y")
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "okay/http"
|
4
|
+
require "how_is/sources/github"
|
5
|
+
|
6
|
+
module HowIs::Sources
|
7
|
+
# Fetches metadata about CI builds from travis-ci.org.
|
8
|
+
class Travis
|
9
|
+
# @param repository [String] GitHub repository name, of the format user/repo.
|
10
|
+
# @param end_date [String] End date for the report being generated.
|
11
|
+
def initialize(repository, end_date)
|
12
|
+
@repository = repository
|
13
|
+
# TODO: Do something with end_date.
|
14
|
+
# TODO: Figure out Default Branch of the repo
|
15
|
+
end
|
16
|
+
|
17
|
+
def builds
|
18
|
+
JSON.parse(fetch_builds)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
# Returns API result of /repos/:user/:repo/builds for Push type Travis
|
24
|
+
# events.
|
25
|
+
#
|
26
|
+
# @return [String] JSON result
|
27
|
+
def fetch_builds
|
28
|
+
Okay::HTTP.get(
|
29
|
+
"http://api.travis-ci.org/repos/#{@repository}/builds?event_type=push",
|
30
|
+
headers: {"Accept" => "application/vnd.travis-ci.2+json"}
|
31
|
+
).body
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
%{frontmatter}<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>%{title}</title>
|
5
|
+
<style>
|
6
|
+
body { font: sans-serif; }
|
7
|
+
main {
|
8
|
+
max-width: 600px;
|
9
|
+
max-width: 72ch;
|
10
|
+
margin: auto;
|
11
|
+
}
|
12
|
+
.horizontal-bar-graph {
|
13
|
+
position: relative;
|
14
|
+
width: 100%%; /* lol Kernel.format() disapproves. */
|
15
|
+
}
|
16
|
+
.horizontal-bar-graph .fill {
|
17
|
+
display: inline-block;
|
18
|
+
background: #CCC;
|
19
|
+
}
|
20
|
+
</style>
|
21
|
+
</head>
|
22
|
+
<body>
|
23
|
+
<main>
|
24
|
+
%{report}
|
25
|
+
</main>
|
26
|
+
</body>
|
27
|
+
</html>
|
data/lib/how_is/version.rb
CHANGED
data/lib/how_is.rb
CHANGED
@@ -1,54 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "how_is/version"
|
4
|
-
require "
|
5
|
-
require "
|
4
|
+
require "how_is/report"
|
5
|
+
require "github_api"
|
6
6
|
|
7
|
-
|
7
|
+
module HowIs
|
8
|
+
DEFAULT_REPORT_FILE = "report.html"
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
# HowIs control class used from the CLI tool.
|
12
|
-
#
|
13
|
-
# Generates an analysis and has methods to build reports from it.
|
14
|
-
class HowIs
|
15
|
-
include Contracts::Core
|
16
|
-
|
17
|
-
require "how_is/fetcher"
|
18
|
-
require "how_is/analyzer"
|
19
|
-
require "how_is/report"
|
20
|
-
|
21
|
-
DEFAULT_FORMAT = :html
|
22
|
-
|
23
|
-
##
|
24
|
-
# Generate a HowIs instance, so you can generate reports.
|
25
|
-
#
|
26
|
-
# @param repository [String] The name of a GitHub repository (of the
|
27
|
-
# format <user or organization>/<repository>).
|
28
|
-
# @param analysis [HowIs::Analysis] Optional; if passed, this Analysis
|
29
|
-
# object is used instead of generating one.
|
30
|
-
def initialize(repository, analysis = nil, **kw_args)
|
31
|
-
# If no Analysis is passed, generate one.
|
32
|
-
analysis ||= HowIs.generate_analysis(repository: repository, **kw_args)
|
33
|
-
|
34
|
-
# Used by to_html, to_json, etc.
|
35
|
-
@analysis = analysis
|
10
|
+
def self.new(repository, date)
|
11
|
+
Report.new(repository, date)
|
36
12
|
end
|
37
13
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
Report.export(@analysis, :html)
|
44
|
-
end
|
45
|
-
|
46
|
-
##
|
47
|
-
# Generate a JSON report.
|
48
|
-
#
|
49
|
-
# @return [String] A JSON report.
|
50
|
-
def to_json
|
51
|
-
Report.export(@analysis, :json)
|
14
|
+
def self.github
|
15
|
+
@@github ||=
|
16
|
+
Github.new(auto_pagination: true) do |config|
|
17
|
+
config.basic_auth = ENV["HOWIS_BASIC_AUTH"] if ENV["HOWIS_BASIC_AUTH"]
|
18
|
+
end
|
52
19
|
end
|
53
20
|
|
54
21
|
##
|
@@ -70,140 +37,62 @@ class HowIs
|
|
70
37
|
# @return [HowIs] A HowIs object that can be used for generating other
|
71
38
|
# reports, treating the provided report data as a cache.
|
72
39
|
def self.from_hash(data)
|
73
|
-
analysis = HowIs::
|
40
|
+
analysis = HowIs::Analysis.from_hash(data)
|
74
41
|
|
75
42
|
new(analysis.repository, analysis)
|
76
43
|
end
|
77
44
|
|
78
|
-
##
|
79
|
-
# Returns a list of possible export formats.
|
80
|
-
#
|
81
|
-
# @return [Array<String>] An array of the types of reports you can
|
82
|
-
# generate.
|
83
|
-
def self.supported_formats
|
84
|
-
report_constants = HowIs.constants.grep(/.Report/) - [:BaseReport]
|
85
|
-
report_constants.map { |x| x.to_s.split("Report").first.downcase }
|
86
|
-
end
|
87
|
-
|
88
|
-
##
|
89
|
-
# Returns whether or not the specified +file+ can be exported to.
|
90
|
-
#
|
91
|
-
# @param file [String] A filename.
|
92
|
-
# @return [Boolean] +true+ if HowIs can export to the file, +false+
|
93
|
-
# if it can't.
|
94
|
-
def self.can_export_to?(file)
|
95
|
-
# TODO: Check if the file is writable?
|
96
|
-
supported_formats.include?(file.split(".").last)
|
97
|
-
end
|
98
|
-
|
99
|
-
# Generate an analysis.
|
100
|
-
# TODO: This may make more sense as Analysis.new().
|
101
|
-
# TODO: Nothing overrides +fetcher+ and +analyzer+. Remove ability to do so.
|
102
|
-
# FIXME: THIS CODE AND EVERYTHING ASSOCIATED WITH IT IS A FUCKING ATROCITY.
|
103
|
-
Contract C::KeywordArgs[repository: String,
|
104
|
-
fetcher: C::Optional[Class],
|
105
|
-
analyzer: C::Optional[Class],
|
106
|
-
github: C::Optional[C::Any]] => C::Any
|
107
|
-
def self.generate_analysis(repository:,
|
108
|
-
fetcher: Fetcher.new,
|
109
|
-
analyzer: Analyzer.new,
|
110
|
-
github: nil)
|
111
|
-
raw_data = fetcher.call(repository, github)
|
112
|
-
analysis = analyzer.call(raw_data)
|
113
|
-
|
114
|
-
analysis
|
115
|
-
end
|
116
|
-
|
117
|
-
# Generates YAML frontmatter, as is used in Jekyll and other blog engines.
|
118
|
-
#
|
119
|
-
# E.g.,
|
120
|
-
# generate_frontmatter({'foo' => "bar %{baz}"}, {'baz' => "asdf"})
|
121
|
-
# => "---\nfoo: bar asdf\n"
|
122
|
-
Contract C::HashOf[C::Or[String, Symbol] => String],
|
123
|
-
C::HashOf[C::Or[String, Symbol] => C::Any] => String
|
124
|
-
def self.generate_frontmatter(frontmatter, report_data)
|
125
|
-
frontmatter = convert_keys(frontmatter, :to_s)
|
126
|
-
report_data = convert_keys(report_data, :to_sym)
|
127
|
-
|
128
|
-
frontmatter = frontmatter.map { |k, v|
|
129
|
-
# Sometimes report_data has unused keys, which generates a warning, but
|
130
|
-
# we're okay with it.
|
131
|
-
v = silence_warnings { v % report_data }
|
132
|
-
|
133
|
-
[k, v]
|
134
|
-
}.to_h
|
135
|
-
|
136
|
-
YAML.dump(frontmatter)
|
137
|
-
end
|
138
|
-
|
139
45
|
##
|
140
46
|
# Generates a series of report files based on a config Hash.
|
141
47
|
#
|
142
48
|
# @param config [Hash] A Hash specifying the formats, locations, etc
|
143
49
|
# of the reports to generate.
|
144
|
-
# @param
|
145
|
-
#
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
github: nil,
|
150
|
-
report_class: nil)
|
151
|
-
report_class ||= HowIs::Report
|
152
|
-
|
153
|
-
date = Date.strptime(Time.now.to_i.to_s, "%s")
|
154
|
-
friendly_date = date.strftime("%B %d, %y")
|
155
|
-
|
156
|
-
analysis = HowIs.generate_analysis(repository: config["repository"], github: github)
|
157
|
-
|
158
|
-
report_data = {
|
159
|
-
repository: config["repository"],
|
160
|
-
date: date,
|
161
|
-
friendly_date: friendly_date,
|
162
|
-
}
|
163
|
-
|
164
|
-
generated_reports = {}
|
50
|
+
# @param date [String] A string containing the date (YYYY-MM-DD) that the
|
51
|
+
# report ends on. E.g., for Jan 1-Feb 1 2017, you'd pass 2017-02-01.
|
52
|
+
def self.from_config(config, date)
|
53
|
+
report = Report.new(config["repository"], date)
|
54
|
+
report_data = prepare_report_metadata(config["repository"], date)
|
165
55
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
56
|
+
generated_reports =
|
57
|
+
config["reports"].map { |format, report_config|
|
58
|
+
# Sometimes report_data has unused keys, which generates a warning, but
|
59
|
+
# we're okay with it, so we wrap it with silence_warnings {}.
|
60
|
+
filename = silence_warnings { report_config["filename"] % report_data }
|
61
|
+
file = File.join(report_config["directory"], filename)
|
171
62
|
|
172
|
-
|
63
|
+
report_export = report.send("to_#{format}", report_config["frontmatter"])
|
173
64
|
|
174
|
-
|
65
|
+
[file, report_export]
|
66
|
+
}
|
175
67
|
|
176
|
-
|
177
|
-
|
178
|
-
result
|
179
|
-
end
|
180
|
-
|
181
|
-
generated_reports
|
68
|
+
generated_reports.to_h
|
182
69
|
end
|
183
70
|
|
184
|
-
|
185
|
-
#
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
str.puts "---"
|
192
|
-
str.puts
|
193
|
-
end
|
71
|
+
##
|
72
|
+
# Returns a list of possible export formats.
|
73
|
+
#
|
74
|
+
# @return [Array<String>] An array of the types of reports you can generate.
|
75
|
+
def self.supported_formats
|
76
|
+
["html", "json"]
|
77
|
+
end
|
194
78
|
|
195
|
-
|
79
|
+
def self.template(filename)
|
80
|
+
dir = File.expand_path("./how_is/templates/", __dir__)
|
81
|
+
path = File.join(dir, filename)
|
196
82
|
|
197
|
-
|
83
|
+
open(path).read
|
198
84
|
end
|
199
85
|
|
200
|
-
|
201
|
-
#
|
202
|
-
#
|
203
|
-
|
204
|
-
|
86
|
+
##
|
87
|
+
# Returns whether or not the specified +file+ can be exported to.
|
88
|
+
#
|
89
|
+
# @param file [String] A filename.
|
90
|
+
# @return [Boolean] +true+ if HowIs can export to the file, +false+
|
91
|
+
# if it can't.
|
92
|
+
def self.can_export_to?(file)
|
93
|
+
# TODO: Check if the file is writable?
|
94
|
+
supported_formats.include?(file.split(".").last)
|
205
95
|
end
|
206
|
-
private_class_method :convert_keys
|
207
96
|
|
208
97
|
def self.silence_warnings(&block)
|
209
98
|
with_warnings(nil, &block)
|
@@ -218,4 +107,16 @@ class HowIs
|
|
218
107
|
$VERBOSE = old_verbose
|
219
108
|
end
|
220
109
|
private_class_method :with_warnings
|
110
|
+
|
111
|
+
def self.prepare_report_metadata(repository, date)
|
112
|
+
end_date = DateTime.strptime(date, "%Y-%m-%d")
|
113
|
+
friendly_end_date = end_date.strftime("%B %d, %y")
|
114
|
+
|
115
|
+
{
|
116
|
+
repository: repository,
|
117
|
+
date: end_date,
|
118
|
+
friendly_date: friendly_end_date,
|
119
|
+
}
|
120
|
+
end
|
121
|
+
private_class_method :prepare_report_metadata
|
221
122
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: how_is
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 20.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ellen Marie Dash
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-12-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: github_api
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.18.1
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.18.1
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: contracts
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -39,19 +39,19 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 0.16.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: okay
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 4.0.0
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 4.0.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: bundler
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -164,6 +164,20 @@ dependencies:
|
|
164
164
|
- - ">="
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: pry
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
167
181
|
description:
|
168
182
|
email:
|
169
183
|
- me@duckie.co
|
@@ -197,21 +211,33 @@ files:
|
|
197
211
|
- fixtures/vcr_cassettes/how-is-from-config-frontmatter.yml
|
198
212
|
- fixtures/vcr_cassettes/how-is-how-is-travis-api-repos-builds.yml
|
199
213
|
- fixtures/vcr_cassettes/how-is-with-config-file.yml
|
214
|
+
- fixtures/vcr_cassettes/how_is_contributions_additions_count.yml
|
200
215
|
- fixtures/vcr_cassettes/how_is_contributions_all_contributors.yml
|
216
|
+
- fixtures/vcr_cassettes/how_is_contributions_changed_files.yml
|
217
|
+
- fixtures/vcr_cassettes/how_is_contributions_changes.yml
|
218
|
+
- fixtures/vcr_cassettes/how_is_contributions_commits.yml
|
219
|
+
- fixtures/vcr_cassettes/how_is_contributions_compare_url.yml
|
201
220
|
- fixtures/vcr_cassettes/how_is_contributions_default_branch.yml
|
221
|
+
- fixtures/vcr_cassettes/how_is_contributions_deletions_count.yml
|
202
222
|
- fixtures/vcr_cassettes/how_is_contributions_new_contributors.yml
|
223
|
+
- fixtures/vcr_cassettes/how_is_contributions_summary.yml
|
224
|
+
- fixtures/vcr_cassettes/how_is_contributions_summary_2.yml
|
225
|
+
- fixtures/vcr_cassettes/how_is_fetcher_call.yml
|
203
226
|
- how_is.gemspec
|
204
227
|
- lib/how_is.rb
|
205
|
-
- lib/how_is/analyzer.rb
|
206
|
-
- lib/how_is/builds.rb
|
207
228
|
- lib/how_is/cli.rb
|
208
|
-
- lib/how_is/
|
209
|
-
- lib/how_is/fetcher.rb
|
210
|
-
- lib/how_is/pulse.rb
|
229
|
+
- lib/how_is/frontmatter.rb
|
211
230
|
- lib/how_is/report.rb
|
212
|
-
- lib/how_is/
|
213
|
-
- lib/how_is/
|
214
|
-
- lib/how_is/
|
231
|
+
- lib/how_is/sources.rb
|
232
|
+
- lib/how_is/sources/github.rb
|
233
|
+
- lib/how_is/sources/github/contributions.rb
|
234
|
+
- lib/how_is/sources/github/issues.rb
|
235
|
+
- lib/how_is/sources/github/pulls.rb
|
236
|
+
- lib/how_is/sources/github_helpers.rb
|
237
|
+
- lib/how_is/sources/travis.rb
|
238
|
+
- lib/how_is/templates/issues_or_pulls_partial.html_template
|
239
|
+
- lib/how_is/templates/report.html_template
|
240
|
+
- lib/how_is/templates/report_partial.html_template
|
215
241
|
- lib/how_is/version.rb
|
216
242
|
- roadmap.markdown
|
217
243
|
homepage: https://github.com/how-is/how_is
|
@@ -234,7 +260,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
234
260
|
version: '0'
|
235
261
|
requirements: []
|
236
262
|
rubyforge_project:
|
237
|
-
rubygems_version: 2.
|
263
|
+
rubygems_version: 2.7.3
|
238
264
|
signing_key:
|
239
265
|
specification_version: 4
|
240
266
|
summary: Quantify the health of a GitHub repository.
|