how_is 24.0.0 → 25.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.github_changelog_generator +0 -1
  3. data/.rubocop.yml +37 -12
  4. data/.travis.yml +6 -3
  5. data/CHANGELOG.md +56 -0
  6. data/CONTRIBUTING.md +34 -0
  7. data/Gemfile +8 -4
  8. data/ISSUES.md +30 -54
  9. data/README.md +16 -91
  10. data/Rakefile +3 -31
  11. data/bin/prerelease-generate-changelog +1 -1
  12. data/bin/setup +0 -0
  13. data/build-debug.rb +20 -0
  14. data/exe/how_is +25 -22
  15. data/fixtures/vcr_cassettes/how-is-example-empty-repository.yml +334 -1
  16. data/fixtures/vcr_cassettes/how-is-example-repository.yml +350 -1
  17. data/fixtures/vcr_cassettes/how-is-from-config-frontmatter.yml +15234 -1
  18. data/fixtures/vcr_cassettes/how-is-how-is-travis-api-repos-builds.yml +2694 -1
  19. data/fixtures/vcr_cassettes/how-is-with-config-file.yml +15234 -1
  20. data/fixtures/vcr_cassettes/how_is_contributions_additions_count.yml +70 -1
  21. data/fixtures/vcr_cassettes/how_is_contributions_all_contributors.yml +70 -1
  22. data/fixtures/vcr_cassettes/how_is_contributions_changed_files.yml +70 -1
  23. data/fixtures/vcr_cassettes/how_is_contributions_changes.yml +70 -1
  24. data/fixtures/vcr_cassettes/how_is_contributions_commits.yml +70 -1
  25. data/fixtures/vcr_cassettes/how_is_contributions_compare_url.yml +70 -1
  26. data/fixtures/vcr_cassettes/how_is_contributions_default_branch.yml +70 -1
  27. data/fixtures/vcr_cassettes/how_is_contributions_deletions_count.yml +70 -1
  28. data/fixtures/vcr_cassettes/how_is_contributions_new_contributors.yml +70 -1
  29. data/fixtures/vcr_cassettes/how_is_contributions_summary.yml +70 -1
  30. data/fixtures/vcr_cassettes/how_is_contributions_summary_2.yml +70 -1
  31. data/how_is.gemspec +12 -6
  32. data/lib/how_is/cacheable.rb +71 -0
  33. data/lib/how_is/cli.rb +121 -124
  34. data/lib/how_is/config.rb +123 -0
  35. data/lib/how_is/constants.rb +9 -0
  36. data/lib/how_is/date_time_helpers.rb +48 -0
  37. data/lib/how_is/frontmatter.rb +14 -9
  38. data/lib/how_is/report.rb +86 -58
  39. data/lib/how_is/report_collection.rb +113 -0
  40. data/lib/how_is/sources/ci/appveyor.rb +88 -0
  41. data/lib/how_is/sources/ci/travis.rb +159 -0
  42. data/lib/how_is/sources/github/contributions.rb +169 -128
  43. data/lib/how_is/sources/github/issue_fetcher.rb +148 -0
  44. data/lib/how_is/sources/github/issues.rb +86 -235
  45. data/lib/how_is/sources/github/pulls.rb +19 -18
  46. data/lib/how_is/sources/github.rb +40 -18
  47. data/lib/how_is/sources/github_helpers.rb +8 -91
  48. data/lib/how_is/sources.rb +2 -0
  49. data/lib/how_is/template.rb +9 -0
  50. data/lib/how_is/templates/contributions_partial.html +1 -0
  51. data/lib/how_is/templates/{issues_or_pulls_partial.html_template → issues_or_pulls_partial.html} +0 -0
  52. data/lib/how_is/templates/new_contributors_partial.html +5 -0
  53. data/lib/how_is/templates/{report.html_template → report.html} +0 -8
  54. data/lib/how_is/templates/{report_partial.html_template → report_partial.html} +3 -3
  55. data/lib/how_is/text.rb +26 -0
  56. data/lib/how_is/version.rb +2 -1
  57. data/lib/how_is.rb +33 -60
  58. metadata +28 -47
  59. data/.hound.yml +0 -2
  60. data/.rubocop_todo.yml +0 -21
  61. data/lib/how_is/sources/travis.rb +0 -37
  62. data/roadmap.markdown +0 -82
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+ require "how_is/text"
5
+
6
+ module HowIs
7
+ HOME_CONFIG = File.join(Dir.home, ".config", "how_is", "config.yml")
8
+
9
+ # Usage:
10
+ # HowIs::Config
11
+ # .load_site_configs("/path/to/config1.yml", "/path/to/config2.yml")
12
+ # .load_file("./repo-config.yml")
13
+ # Or:
14
+ # HowIs::Config
15
+ # .load_defaults
16
+ # .load_file("./repo-config.yml")
17
+ # Or:
18
+ # HowIs::Config
19
+ # .load_defaults
20
+ # .load({"repository" => "how-is/example-repository"})
21
+ class Config < Hash
22
+ attr_reader :site_configs
23
+
24
+ # If the +HOWIS_USE_ENV+ environment variable is set, load config from
25
+ # the environment.
26
+ #
27
+ # Otherwise, load the the default config file.
28
+ #
29
+ # @return [Hash] A Hash representation of the config.
30
+ def load_defaults
31
+ if ENV["HOWIS_USE_ENV"] == "true"
32
+ load_env
33
+ else
34
+ load_site_configs(HOME_CONFIG)
35
+ end
36
+ end
37
+
38
+ def initialize
39
+ super()
40
+ @site_configs = []
41
+ end
42
+
43
+ # Load the config files as specified via +files+.
44
+ #
45
+ # @param files [Array<String>] The path(s) for config files.
46
+ # @return [Config] The config hash. (+self+)
47
+ def load_site_configs(*files)
48
+ # Allows both:
49
+ # load_site_configs('foo', 'bar')
50
+ # load_site_configs(['foo', bar'])
51
+ # but not:
52
+ # load_site_configs(['foo'], 'bar')
53
+ files = files[0] if files.length == 1 && files[0].is_a?(Array)
54
+
55
+ load_files(*files)
56
+ end
57
+
58
+ # TODO: See if this can be consolidated with load_site_configs.
59
+ def load_files(*file_paths)
60
+ files = (site_configs + file_paths).map { |f| Pathname.new(f) }
61
+
62
+ # Keep only files that exist.
63
+ files.select!(&:file?)
64
+
65
+ # Load the YAML files into Hashes.
66
+ configs = files.map { |file| YAML.safe_load(file.read) }
67
+
68
+ # Apply configs.
69
+ load(*configs)
70
+ end
71
+
72
+ # Take a collection of config hashes and cascade them, meaning values
73
+ # in later ones override values in earlier ones.
74
+ #
75
+ # E.g., this results in +{'a'=>'x', 'c'=>'d'}+:
76
+ # load({'a'=>'b'}, {'c'=>'d'}, {'a'=>'x'})
77
+ #
78
+ # And this results in +{'a'=>['b', 'c']}+:
79
+ # load({'a'=>['b']}, {'a'=>['c']})
80
+ #
81
+ # @param [Array<Hash>] The configuration hashes.
82
+ # @return [Config] The final configuration value.
83
+ def load(*configs)
84
+ configs.each do |config|
85
+ config.each do |k, v|
86
+ if self[k] && self[k].is_a?(Array)
87
+ self[k] += v
88
+ else
89
+ self[k] = v
90
+ end
91
+ end
92
+ end
93
+
94
+ self
95
+ end
96
+
97
+ # Load config info from environment variables.
98
+ #
99
+ # Supported environment variables:
100
+ # - HOWIS_GITHUB_TOKEN: a GitHub authentication token.
101
+ # - HOWIS_GITHUB_USERNAME: the GitHub username corresponding to the token.
102
+ #
103
+ # @return [Config] The resulting configuration.
104
+ def load_env
105
+ HowIs::Text.puts "Using configuration from environment variables."
106
+
107
+ gh_token = ENV["HOWIS_GITHUB_TOKEN"]
108
+ gh_username = ENV["HOWIS_GITHUB_USERNAME"]
109
+
110
+ raise "HOWIS_GITHUB_TOKEN environment variable is not set" \
111
+ unless gh_token
112
+ raise "HOWIS_GITHUB_USERNAME environment variable is not set" \
113
+ unless gh_username
114
+
115
+ load({
116
+ "sources/github" => {
117
+ "username" => gh_username,
118
+ "token" => gh_token,
119
+ },
120
+ })
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HowIs
4
+ # The file name used for a report if one isn't specified.
5
+ DEFAULT_REPORT_FILE = "report.html"
6
+
7
+ # Used by things making HTTP requests.
8
+ USER_AGENT = "how_is/#{HowIs::VERSION} (https://github.com/how-is/how_is/)"
9
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "how_is/version"
4
+ require "date"
5
+
6
+ module HowIs
7
+ ##
8
+ # Various helper functions for working with DateTime objects.
9
+ module DateTimeHelpers
10
+ # Check if +left+ is less than or equal to +right+, where both are string
11
+ # representations of a date.
12
+ #
13
+ # @param left [String] A string representation of a date.
14
+ # @param right [String] A string representation of a date.
15
+ # @return [Boolean] True if +left+ is less-than-or-equal to +right+,
16
+ # otherwise false.
17
+ def date_le(left, right)
18
+ left = str_to_dt(left)
19
+ right = str_to_dt(right)
20
+
21
+ left <= right
22
+ end
23
+
24
+ # Check if +left+ is greater than or equal to +right+, where both are string
25
+ # representations of a date.
26
+ #
27
+ # @param left [String] A string representation of a date.
28
+ # @param right [String] A string representation of a date.
29
+ # @return [Boolean] True if +left+ is greater-than-or-equal to +right+,
30
+ # otherwise false.
31
+ def date_ge(left, right)
32
+ left = str_to_dt(left)
33
+ right = str_to_dt(right)
34
+
35
+ left >= right
36
+ end
37
+
38
+ private
39
+
40
+ # Converts a +String+ representation of a date to a +DateTime+.
41
+ #
42
+ # @param str [String] A date.
43
+ # @return [DateTime] A DateTime representation of +str+.
44
+ def str_to_dt(str)
45
+ DateTime.parse(str)
46
+ end
47
+ end
48
+ end
@@ -1,14 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "how_is/version"
4
+ require "okay/warning_helpers"
4
5
 
5
6
  module HowIs
7
+ ##
8
+ # Module for generating YAML frontmatter, as used by Jekyll and other
9
+ # blog engines.
6
10
  module Frontmatter
11
+ extend Okay::WarningHelpers
12
+
7
13
  # Generates YAML frontmatter, as is used in Jekyll and other blog engines.
8
14
  #
9
15
  # E.g.,
10
16
  # generate_frontmatter({'foo' => "bar %{baz}"}, {'baz' => "asdf"})
11
17
  # => "---\nfoo: bar asdf\n"
18
+ #
19
+ # @param frontmatter [Hash] Frontmatter for the report.
20
+ # @param report_data [Hash] The report data itself.
21
+ # @return [String] A YAML dump of the generated frontmatter.
12
22
  def self.generate(frontmatter, report_data)
13
23
  return "" if frontmatter.nil?
14
24
 
@@ -29,18 +39,13 @@ module HowIs
29
39
  # @example
30
40
  # convert_keys({'foo' => 'bar'}, :to_sym)
31
41
  # # => {:foo => 'bar'}
42
+ # @param data [Hash] The input hash.
43
+ # @param method_name [Symbol] The method name used to convert keys.
44
+ # (E.g. :to_s, :to_sym, etc.)
45
+ # @return [Hash] The converted result.
32
46
  def self.convert_keys(data, method_name)
33
47
  data.map { |k, v| [k.send(method_name), v] }.to_h
34
48
  end
35
49
  private_class_method :convert_keys
36
-
37
- def self.silence_warnings(&_block)
38
- old_verbose = $VERBOSE
39
- $VERBOSE = nil # Disable warnings entirely.
40
- yield
41
- ensure
42
- $VERBOSE = old_verbose
43
- end
44
- private_class_method :silence_warnings
45
50
  end
46
51
  end
data/lib/how_is/report.rb CHANGED
@@ -1,15 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "how_is/frontmatter"
4
+ require "how_is/cacheable"
4
5
  require "how_is/sources/github/contributions"
5
6
  require "how_is/sources/github/issues"
6
7
  require "how_is/sources/github/pulls"
7
- require "how_is/sources/travis"
8
+ require "how_is/sources/ci/travis"
9
+ require "how_is/sources/ci/appveyor"
10
+ require "how_is/template"
11
+ require "json"
8
12
 
9
13
  module HowIs
14
+ ##
15
+ # Class for generating a HowIs report.
10
16
  class Report
11
- def initialize(repository, end_date)
12
- @repository = repository
17
+ def initialize(config, end_date)
18
+ @config = config
19
+ @repository = config["repository"]
13
20
 
14
21
  # NOTE: Use DateTime because it defaults to UTC and that's less gross
15
22
  # than trying to get Date to use UTC.
@@ -19,80 +26,50 @@ module HowIs
19
26
  #
20
27
  # (I'm also guessing/hoping that GitHub's URLs use UTC.)
21
28
  end_dt = DateTime.strptime(end_date, "%Y-%m-%d")
22
-
23
- d = end_dt.day
24
- m = end_dt.month
25
- y = end_dt.year
26
- start_year = y
27
- start_month = m - 1
28
- if start_month <= 0
29
- start_month = 12 - start_month
30
- start_year -= 1
31
- end
32
- start_dt = DateTime.new(start_year, start_month, d)
29
+ start_dt = start_dt_from_end_dt(end_dt)
33
30
 
34
31
  @end_date = end_dt.strftime("%Y-%m-%d")
35
32
  @start_date = start_dt.strftime("%Y-%m-%d")
36
-
37
- @gh_contributions = HowIs::Sources::Github::Contributions.new(repository, @start_date, @end_date)
38
- @gh_issues = HowIs::Sources::Github::Issues.new(repository, @start_date, @end_date)
39
- @gh_pulls = HowIs::Sources::Github::Pulls.new(repository, @start_date, @end_date)
40
- @travis = HowIs::Sources::Travis.new(repository, @start_date, @end_date)
41
33
  end
42
34
 
43
- def to_h(frontmatter_data = nil)
44
- @report_hash ||= {
45
- title: "How is #{@repository}?",
46
- repository: @repository,
47
-
48
- contributions_summary: @gh_contributions.to_html,
49
- issues_summary: @gh_issues.to_html,
50
- pulls_summary: @gh_pulls.to_html,
51
- issues_per_label: @gh_issues.issues_per_label_html,
52
-
53
- issues: @gh_issues.to_a,
54
- pulls: @gh_issues.to_a,
55
-
56
- average_issue_age: @gh_issues.average_age,
57
- average_pull_age: @gh_pulls.average_age,
58
-
59
- oldest_issue_link: @gh_issues.oldest["url"],
60
- oldest_issue_date: @gh_issues.oldest["createdAt"],
35
+ def cache
36
+ @cache ||= Cacheable.new(@config, @start_date, @end_date)
37
+ end
61
38
 
62
- newest_issue_link: @gh_issues.newest["url"],
63
- newest_issue_date: @gh_issues.newest["createdAt"],
39
+ def contributions
40
+ @gh_contributions ||= HowIs::Sources::Github::Contributions.new(@config, @start_date, @end_date, cache)
41
+ end
64
42
 
65
- newest_pull_link: @gh_pulls.newest["url"],
66
- newest_pull_date: @gh_pulls.newest["createdAt"],
43
+ def issues
44
+ @gh_issues ||= HowIs::Sources::Github::Issues.new(@config, @start_date, @end_date, cache)
45
+ end
67
46
 
68
- oldest_pull_link: @gh_pulls.oldest["url"],
69
- oldest_pull_date: @gh_pulls.oldest["createdAt"],
47
+ def pulls
48
+ @gh_pulls ||= HowIs::Sources::Github::Pulls.new(@config, @start_date, @end_date, cache)
49
+ end
70
50
 
71
- travis_builds: @travis.builds.to_h,
51
+ def travis
52
+ @travis ||= HowIs::Sources::CI::Travis.new(@config, @start_date, @end_date, cache)
53
+ end
72
54
 
73
- date: @end_date,
74
- }
55
+ def appveyor
56
+ @appveyor ||= HowIs::Sources::CI::Appveyor.new(@config, @start_date, @end_date, cache)
57
+ end
75
58
 
76
- frontmatter =
77
- if frontmatter_data
78
- HowIs::Frontmatter.generate(frontmatter_data, @report_hash)
79
- else
80
- ""
81
- end
59
+ def to_h(frontmatter_data = nil)
60
+ @report_hash ||= report_hash
61
+ frontmatter = HowIs::Frontmatter.generate(frontmatter_data, @report_hash)
82
62
 
83
63
  @report_hash.merge(frontmatter: frontmatter)
84
64
  end
85
65
 
86
66
  def to_html_partial(frontmatter = nil)
87
- template_data = to_h(frontmatter)
88
-
89
- Kernel.format(HowIs.template("report_partial.html_template"), template_data)
67
+ HowIs::Template.apply("report_partial.html", to_h(frontmatter))
90
68
  end
91
69
 
92
70
  def to_html(frontmatter = nil)
93
71
  template_data = to_h(frontmatter).merge({report: to_html_partial})
94
-
95
- Kernel.format(HowIs.template("report.html_template"), template_data)
72
+ HowIs::Template.apply("report.html", template_data)
96
73
  end
97
74
 
98
75
  def to_json(frontmatter = nil)
@@ -104,9 +81,60 @@ module HowIs
104
81
  end
105
82
 
106
83
  def to_format_for(filename)
107
- format = filename.split(".").last
84
+ format = File.extname(filename)[1..-1]
108
85
  send("to_#{format}")
109
86
  end
110
87
  private :to_format_for
88
+
89
+ def start_dt_from_end_dt(end_dt)
90
+ d = end_dt.day
91
+ m = end_dt.month
92
+ y = end_dt.year
93
+ start_year = y
94
+ start_month = m - 1
95
+ if start_month <= 0
96
+ start_month = 12 - start_month
97
+ start_year -= 1
98
+ end
99
+
100
+ DateTime.new(start_year, start_month, d)
101
+ end
102
+
103
+ # rubocop:disable Metrics/AbcSize
104
+ def report_hash
105
+ {
106
+ title: "How is #{@repository}?",
107
+ repository: @repository,
108
+
109
+ contributions_summary: contributions.to_html,
110
+ new_contributors: contributions.new_contributors_html,
111
+ issues_summary: issues.to_html,
112
+ pulls_summary: pulls.to_html,
113
+
114
+ issues: issues.to_a,
115
+ pulls: issues.to_a,
116
+
117
+ average_issue_age: issues.average_age,
118
+ average_pull_age: pulls.average_age,
119
+
120
+ oldest_issue_link: issues.oldest["url"],
121
+ oldest_issue_date: issues.oldest["createdAt"],
122
+
123
+ newest_issue_link: issues.newest["url"],
124
+ newest_issue_date: issues.newest["createdAt"],
125
+
126
+ newest_pull_link: pulls.newest["url"],
127
+ newest_pull_date: pulls.newest["createdAt"],
128
+
129
+ oldest_pull_link: pulls.oldest["url"],
130
+ oldest_pull_date: pulls.oldest["createdAt"],
131
+
132
+ travis_builds: travis.builds,
133
+ appveyor_builds: appveyor.builds,
134
+
135
+ date: @end_date,
136
+ }
137
+ end
138
+ # rubocop:enable Metrics/AbcSize
111
139
  end
112
140
  end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "how_is/report"
4
+ require "okay/warning_helpers"
5
+
6
+ module HowIs
7
+ ##
8
+ # A class representing a collection of Reports.
9
+ class ReportCollection
10
+ include Okay::WarningHelpers
11
+
12
+ def initialize(config, date)
13
+ @config = config
14
+
15
+ # If the config is in the old format, convert it to the new one.
16
+ unless @config["repositories"]
17
+ @config["repositories"] = [{
18
+ "repository" => @config.delete("repository"),
19
+ "reports" => @config.delete("reports"),
20
+ }]
21
+ end
22
+
23
+ @date = date
24
+ @reports = config["repositories"].map(&method(:fetch_report)).to_h
25
+ end
26
+
27
+ # Generates the metadata for the collection of Reports.
28
+ def metadata(repository)
29
+ end_date = DateTime.strptime(@date, "%Y-%m-%d")
30
+ friendly_end_date = end_date.strftime("%B %d, %y")
31
+
32
+ {
33
+ sanitized_repository: repository.tr("/", "-"),
34
+ repository: repository,
35
+ date: end_date,
36
+ friendly_date: friendly_end_date,
37
+ }
38
+ end
39
+ private :metadata
40
+
41
+ def config_for(repo)
42
+ defaults = @config.fetch("default_reports", {})
43
+ config = @config.dup
44
+ repos = config.delete("repositories")
45
+
46
+ # Find the _last_ one that matches, to allow overriding.
47
+ repo_config = repos.reverse.find { |conf| conf["repository"] == repo }
48
+
49
+ # Use values from default_reports, unless overridden.
50
+ config["repository"] = repo
51
+ config["reports"] = defaults.merge(repo_config.fetch("reports", {}))
52
+ config
53
+ end
54
+ private :config_for
55
+
56
+ def fetch_report(repo_config)
57
+ repo = repo_config["repository"]
58
+ report = Report.new(config_for(repo), @date)
59
+ [repo, report]
60
+ end
61
+ private :fetch_report
62
+
63
+ # Converts a ReportCollection to a Hash.
64
+ #
65
+ # Also good for giving programmers nightmares, I suspect.
66
+ def to_h
67
+ results = {}
68
+ defaults = @config["default_reports"] || {}
69
+
70
+ @config["repositories"].map { |repo_config|
71
+ repo = repo_config["repository"]
72
+ config = config_for(repo)
73
+
74
+ config["reports"].map { |format, report_config|
75
+ # Sometimes report_data has unused keys, which generates a warning, but
76
+ # we're okay with it, so we wrap it with silence_warnings {}.
77
+ filename = silence_warnings {
78
+ tmp_filename = report_config["filename"] || defaults[format]["filename"]
79
+ tmp_filename % metadata(repo)
80
+ }
81
+
82
+ directory = report_config["directory"] || defaults[format]["directory"]
83
+ file = File.join(directory, filename)
84
+
85
+ # Export +report+ to the specified +format+ with the specified
86
+ # +frontmatter+.
87
+ frontmatter = report_config["frontmatter"] || {}
88
+ if defaults.has_key?(format) && defaults[format].has_key?("frontmatter")
89
+ frontmatter = defaults[format]["frontmatter"].merge(frontmatter)
90
+ end
91
+ frontmatter = nil if frontmatter == {}
92
+
93
+ export = @reports[repo].send("to_#{format}", frontmatter)
94
+
95
+ results[file] = export
96
+ }
97
+ }
98
+ results
99
+ end
100
+
101
+ # Save all of the reports to the corresponding files.
102
+ #
103
+ # @return [Array<String>] An array of file paths.
104
+ def save_all
105
+ reports = to_h
106
+ reports.each do |file, report|
107
+ File.write(file, report)
108
+ end
109
+
110
+ reports.keys
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "okay/default"
4
+ require "okay/http"
5
+ require "how_is/constants"
6
+ require "how_is/sources"
7
+ require "how_is/sources/github/contributions"
8
+ require "how_is/text"
9
+
10
+ module HowIs
11
+ module Sources
12
+ module CI
13
+ # Fetches metadata about CI builds from appveyor.com.
14
+ class Appveyor
15
+ # @param repository [String] GitHub repository name, of the format user/repo.
16
+ # @param start_date [String] Start date for the report being generated.
17
+ # @param end_date [String] End date for the report being generated.
18
+ # @param cache [Cacheable] Instance of HowIs::Cacheable to cache API calls
19
+ def initialize(config, start_date, end_date, cache)
20
+ @config = config
21
+ @cache = cache
22
+ @repository = config["repository"]
23
+ @start_date = DateTime.parse(start_date)
24
+ @end_date = DateTime.parse(end_date)
25
+ @default_branch = Okay.default
26
+ end
27
+
28
+ # @return [String] The default branch name.
29
+ def default_branch
30
+ return @default_branch unless @default_branch.nil?
31
+
32
+ contributions =
33
+ HowIs::Sources::GitHub::Contributions.new(@config, nil, nil)
34
+
35
+ @default_branch = contributions.default_branch
36
+ end
37
+
38
+ # Fetches builds for the default branch.
39
+ #
40
+ # @return [Hash] Builds for the default branch.
41
+ def builds
42
+ @builds ||=
43
+ fetch_builds["builds"] \
44
+ .map(&method(:normalize_build)) \
45
+ .select(&method(:in_date_range?))
46
+ rescue Net::HTTPServerException
47
+ # It's not elegant, but it works™.
48
+ []
49
+ end
50
+
51
+ private
52
+
53
+ def in_date_range?(build)
54
+ build["started_at"] >= @start_date &&
55
+ build["started_at"] <= @end_date
56
+ end
57
+
58
+ def normalize_build(build)
59
+ build["started_at"] = DateTime.parse(build["created"])
60
+ build["html_url"] = "https://ci.appveyor.com/project/#{@repository}/build/#{build['buildNumber']}"
61
+ build
62
+ end
63
+
64
+ # Returns API result of /api/projects/:repository.
65
+ # FIXME: This doesn't limit results based on the date range.
66
+ #
67
+ # @return [Hash] API results.
68
+ def fetch_builds
69
+ @cache.cached("appveyor_builds") do
70
+ HowIs::Text.print "Fetching Appveyor build data."
71
+
72
+ ret = Okay::HTTP.get(
73
+ "https://ci.appveyor.com/api/projects/#{@repository}/history",
74
+ parameters: {"recordsNumber" => "100"},
75
+ headers: {
76
+ "Accept" => "application/json",
77
+ "User-Agent" => HowIs::USER_AGENT,
78
+ }
79
+ ).or_raise!.from_json
80
+
81
+ HowIs::Text.puts
82
+ ret
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end