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.
- checksums.yaml +4 -4
- data/.github_changelog_generator +0 -1
- data/.rubocop.yml +37 -12
- data/.travis.yml +6 -3
- data/CHANGELOG.md +56 -0
- data/CONTRIBUTING.md +34 -0
- data/Gemfile +8 -4
- data/ISSUES.md +30 -54
- data/README.md +16 -91
- data/Rakefile +3 -31
- data/bin/prerelease-generate-changelog +1 -1
- data/bin/setup +0 -0
- data/build-debug.rb +20 -0
- data/exe/how_is +25 -22
- data/fixtures/vcr_cassettes/how-is-example-empty-repository.yml +334 -1
- data/fixtures/vcr_cassettes/how-is-example-repository.yml +350 -1
- data/fixtures/vcr_cassettes/how-is-from-config-frontmatter.yml +15234 -1
- data/fixtures/vcr_cassettes/how-is-how-is-travis-api-repos-builds.yml +2694 -1
- data/fixtures/vcr_cassettes/how-is-with-config-file.yml +15234 -1
- data/fixtures/vcr_cassettes/how_is_contributions_additions_count.yml +70 -1
- data/fixtures/vcr_cassettes/how_is_contributions_all_contributors.yml +70 -1
- data/fixtures/vcr_cassettes/how_is_contributions_changed_files.yml +70 -1
- data/fixtures/vcr_cassettes/how_is_contributions_changes.yml +70 -1
- data/fixtures/vcr_cassettes/how_is_contributions_commits.yml +70 -1
- data/fixtures/vcr_cassettes/how_is_contributions_compare_url.yml +70 -1
- data/fixtures/vcr_cassettes/how_is_contributions_default_branch.yml +70 -1
- data/fixtures/vcr_cassettes/how_is_contributions_deletions_count.yml +70 -1
- data/fixtures/vcr_cassettes/how_is_contributions_new_contributors.yml +70 -1
- data/fixtures/vcr_cassettes/how_is_contributions_summary.yml +70 -1
- data/fixtures/vcr_cassettes/how_is_contributions_summary_2.yml +70 -1
- data/how_is.gemspec +12 -6
- data/lib/how_is/cacheable.rb +71 -0
- data/lib/how_is/cli.rb +121 -124
- data/lib/how_is/config.rb +123 -0
- data/lib/how_is/constants.rb +9 -0
- data/lib/how_is/date_time_helpers.rb +48 -0
- data/lib/how_is/frontmatter.rb +14 -9
- data/lib/how_is/report.rb +86 -58
- data/lib/how_is/report_collection.rb +113 -0
- data/lib/how_is/sources/ci/appveyor.rb +88 -0
- data/lib/how_is/sources/ci/travis.rb +159 -0
- data/lib/how_is/sources/github/contributions.rb +169 -128
- data/lib/how_is/sources/github/issue_fetcher.rb +148 -0
- data/lib/how_is/sources/github/issues.rb +86 -235
- data/lib/how_is/sources/github/pulls.rb +19 -18
- data/lib/how_is/sources/github.rb +40 -18
- data/lib/how_is/sources/github_helpers.rb +8 -91
- data/lib/how_is/sources.rb +2 -0
- data/lib/how_is/template.rb +9 -0
- data/lib/how_is/templates/contributions_partial.html +1 -0
- data/lib/how_is/templates/{issues_or_pulls_partial.html_template → issues_or_pulls_partial.html} +0 -0
- data/lib/how_is/templates/new_contributors_partial.html +5 -0
- data/lib/how_is/templates/{report.html_template → report.html} +0 -8
- data/lib/how_is/templates/{report_partial.html_template → report_partial.html} +3 -3
- data/lib/how_is/text.rb +26 -0
- data/lib/how_is/version.rb +2 -1
- data/lib/how_is.rb +33 -60
- metadata +28 -47
- data/.hound.yml +0 -2
- data/.rubocop_todo.yml +0 -21
- data/lib/how_is/sources/travis.rb +0 -37
- data/roadmap.markdown +0 -82
@@ -709,4 +709,73 @@ http_interactions:
|
|
709
709
|
string: "[]"
|
710
710
|
http_version:
|
711
711
|
recorded_at: Sat, 06 Jan 2018 20:10:44 GMT
|
712
|
-
|
712
|
+
- request:
|
713
|
+
method: get
|
714
|
+
uri: https://api.github.com/repos/how-is/example_repository/commits?since=2017-08-01&until=2017-09-01
|
715
|
+
body:
|
716
|
+
encoding: US-ASCII
|
717
|
+
string: ''
|
718
|
+
headers:
|
719
|
+
Accept:
|
720
|
+
- application/vnd.github.v3+json,application/vnd.github.beta+json;q=0.5,application/json;q=0.1
|
721
|
+
Accept-Charset:
|
722
|
+
- utf-8
|
723
|
+
User-Agent:
|
724
|
+
- Github API Ruby Gem 0.18.2
|
725
|
+
Authorization:
|
726
|
+
- Basic ZHVja2luYXRvcjo5MTgyNzc3ZmYzYzAwNjc5NTE5M2E1NzBjZGFjMzI2YjY0NDU5ZGM5
|
727
|
+
Accept-Encoding:
|
728
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
729
|
+
response:
|
730
|
+
status:
|
731
|
+
code: 404
|
732
|
+
message: Not Found
|
733
|
+
headers:
|
734
|
+
Server:
|
735
|
+
- GitHub.com
|
736
|
+
Date:
|
737
|
+
- Thu, 17 Jan 2019 23:31:08 GMT
|
738
|
+
Content-Type:
|
739
|
+
- application/json; charset=utf-8
|
740
|
+
Transfer-Encoding:
|
741
|
+
- chunked
|
742
|
+
Status:
|
743
|
+
- 404 Not Found
|
744
|
+
X-Ratelimit-Limit:
|
745
|
+
- '5000'
|
746
|
+
X-Ratelimit-Remaining:
|
747
|
+
- '4998'
|
748
|
+
X-Ratelimit-Reset:
|
749
|
+
- '1547771467'
|
750
|
+
X-Oauth-Scopes:
|
751
|
+
- ''
|
752
|
+
X-Accepted-Oauth-Scopes:
|
753
|
+
- repo
|
754
|
+
X-Github-Media-Type:
|
755
|
+
- github.v3; format=json
|
756
|
+
Access-Control-Expose-Headers:
|
757
|
+
- ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
|
758
|
+
X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval,
|
759
|
+
X-GitHub-Media-Type
|
760
|
+
Access-Control-Allow-Origin:
|
761
|
+
- "*"
|
762
|
+
Strict-Transport-Security:
|
763
|
+
- max-age=31536000; includeSubdomains; preload
|
764
|
+
X-Frame-Options:
|
765
|
+
- deny
|
766
|
+
X-Content-Type-Options:
|
767
|
+
- nosniff
|
768
|
+
X-Xss-Protection:
|
769
|
+
- 1; mode=block
|
770
|
+
Referrer-Policy:
|
771
|
+
- origin-when-cross-origin, strict-origin-when-cross-origin
|
772
|
+
Content-Security-Policy:
|
773
|
+
- default-src 'none'
|
774
|
+
X-Github-Request-Id:
|
775
|
+
- E2DD:74AE:2EA1532:5A89EC1:5C41103C
|
776
|
+
body:
|
777
|
+
encoding: ASCII-8BIT
|
778
|
+
string: '{"message":"Not Found","documentation_url":"https://developer.github.com/v3/repos/commits/#list-commits-on-a-repository"}'
|
779
|
+
http_version:
|
780
|
+
recorded_at: Thu, 17 Jan 2019 23:31:08 GMT
|
781
|
+
recorded_with: VCR 4.0.0
|
@@ -480,4 +480,73 @@ http_interactions:
|
|
480
480
|
commit","tree":{"sha":"8286e548e330cfe01efcf7189f4df1fa53e777a7","url":"https://api.github.com/repos/how-is/example-repository/git/trees/8286e548e330cfe01efcf7189f4df1fa53e777a7"},"url":"https://api.github.com/repos/how-is/example-repository/git/commits/3794aa1c4b76623748faf280abe5760b76823162","comment_count":0,"verification":{"verified":false,"reason":"unsigned","signature":null,"payload":null}},"url":"https://api.github.com/repos/how-is/example-repository/commits/3794aa1c4b76623748faf280abe5760b76823162","html_url":"https://github.com/how-is/example-repository/commit/3794aa1c4b76623748faf280abe5760b76823162","comments_url":"https://api.github.com/repos/how-is/example-repository/commits/3794aa1c4b76623748faf280abe5760b76823162/comments","author":null,"committer":null,"parents":[{"sha":"9e29405efa433529b86722542b8fb4b34dfd9edd","url":"https://api.github.com/repos/how-is/example-repository/commits/9e29405efa433529b86722542b8fb4b34dfd9edd","html_url":"https://github.com/how-is/example-repository/commit/9e29405efa433529b86722542b8fb4b34dfd9edd"}]}]'
|
481
481
|
http_version:
|
482
482
|
recorded_at: Sat, 06 Jan 2018 20:10:45 GMT
|
483
|
-
|
483
|
+
- request:
|
484
|
+
method: get
|
485
|
+
uri: https://api.github.com/repos/how-is/example_repository
|
486
|
+
body:
|
487
|
+
encoding: US-ASCII
|
488
|
+
string: ''
|
489
|
+
headers:
|
490
|
+
Accept:
|
491
|
+
- application/vnd.github.v3+json,application/vnd.github.beta+json;q=0.5,application/json;q=0.1
|
492
|
+
Accept-Charset:
|
493
|
+
- utf-8
|
494
|
+
User-Agent:
|
495
|
+
- Github API Ruby Gem 0.18.2
|
496
|
+
Authorization:
|
497
|
+
- Basic ZHVja2luYXRvcjo5MTgyNzc3ZmYzYzAwNjc5NTE5M2E1NzBjZGFjMzI2YjY0NDU5ZGM5
|
498
|
+
Accept-Encoding:
|
499
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
500
|
+
response:
|
501
|
+
status:
|
502
|
+
code: 404
|
503
|
+
message: Not Found
|
504
|
+
headers:
|
505
|
+
Server:
|
506
|
+
- GitHub.com
|
507
|
+
Date:
|
508
|
+
- Thu, 17 Jan 2019 23:31:09 GMT
|
509
|
+
Content-Type:
|
510
|
+
- application/json; charset=utf-8
|
511
|
+
Transfer-Encoding:
|
512
|
+
- chunked
|
513
|
+
Status:
|
514
|
+
- 404 Not Found
|
515
|
+
X-Ratelimit-Limit:
|
516
|
+
- '5000'
|
517
|
+
X-Ratelimit-Remaining:
|
518
|
+
- '4990'
|
519
|
+
X-Ratelimit-Reset:
|
520
|
+
- '1547771467'
|
521
|
+
X-Oauth-Scopes:
|
522
|
+
- ''
|
523
|
+
X-Accepted-Oauth-Scopes:
|
524
|
+
- repo
|
525
|
+
X-Github-Media-Type:
|
526
|
+
- github.v3; format=json
|
527
|
+
Access-Control-Expose-Headers:
|
528
|
+
- ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
|
529
|
+
X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval,
|
530
|
+
X-GitHub-Media-Type
|
531
|
+
Access-Control-Allow-Origin:
|
532
|
+
- "*"
|
533
|
+
Strict-Transport-Security:
|
534
|
+
- max-age=31536000; includeSubdomains; preload
|
535
|
+
X-Frame-Options:
|
536
|
+
- deny
|
537
|
+
X-Content-Type-Options:
|
538
|
+
- nosniff
|
539
|
+
X-Xss-Protection:
|
540
|
+
- 1; mode=block
|
541
|
+
Referrer-Policy:
|
542
|
+
- origin-when-cross-origin, strict-origin-when-cross-origin
|
543
|
+
Content-Security-Policy:
|
544
|
+
- default-src 'none'
|
545
|
+
X-Github-Request-Id:
|
546
|
+
- A3D7:74B0:25D2A89:4E64487:5C41103D
|
547
|
+
body:
|
548
|
+
encoding: ASCII-8BIT
|
549
|
+
string: '{"message":"Not Found","documentation_url":"https://developer.github.com/v3/repos/#get"}'
|
550
|
+
http_version:
|
551
|
+
recorded_at: Thu, 17 Jan 2019 23:31:09 GMT
|
552
|
+
recorded_with: VCR 4.0.0
|
@@ -480,4 +480,73 @@ http_interactions:
|
|
480
480
|
commit","tree":{"sha":"8286e548e330cfe01efcf7189f4df1fa53e777a7","url":"https://api.github.com/repos/how-is/example-repository/git/trees/8286e548e330cfe01efcf7189f4df1fa53e777a7"},"url":"https://api.github.com/repos/how-is/example-repository/git/commits/3794aa1c4b76623748faf280abe5760b76823162","comment_count":0,"verification":{"verified":false,"reason":"unsigned","signature":null,"payload":null}},"url":"https://api.github.com/repos/how-is/example-repository/commits/3794aa1c4b76623748faf280abe5760b76823162","html_url":"https://github.com/how-is/example-repository/commit/3794aa1c4b76623748faf280abe5760b76823162","comments_url":"https://api.github.com/repos/how-is/example-repository/commits/3794aa1c4b76623748faf280abe5760b76823162/comments","author":null,"committer":null,"parents":[{"sha":"9e29405efa433529b86722542b8fb4b34dfd9edd","url":"https://api.github.com/repos/how-is/example-repository/commits/9e29405efa433529b86722542b8fb4b34dfd9edd","html_url":"https://github.com/how-is/example-repository/commit/9e29405efa433529b86722542b8fb4b34dfd9edd"}]}]'
|
481
481
|
http_version:
|
482
482
|
recorded_at: Sat, 06 Jan 2018 20:10:45 GMT
|
483
|
-
|
483
|
+
- request:
|
484
|
+
method: get
|
485
|
+
uri: https://api.github.com/repos/how-is/example_repository
|
486
|
+
body:
|
487
|
+
encoding: US-ASCII
|
488
|
+
string: ''
|
489
|
+
headers:
|
490
|
+
Accept:
|
491
|
+
- application/vnd.github.v3+json,application/vnd.github.beta+json;q=0.5,application/json;q=0.1
|
492
|
+
Accept-Charset:
|
493
|
+
- utf-8
|
494
|
+
User-Agent:
|
495
|
+
- Github API Ruby Gem 0.18.2
|
496
|
+
Authorization:
|
497
|
+
- Basic ZHVja2luYXRvcjo5MTgyNzc3ZmYzYzAwNjc5NTE5M2E1NzBjZGFjMzI2YjY0NDU5ZGM5
|
498
|
+
Accept-Encoding:
|
499
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
500
|
+
response:
|
501
|
+
status:
|
502
|
+
code: 404
|
503
|
+
message: Not Found
|
504
|
+
headers:
|
505
|
+
Server:
|
506
|
+
- GitHub.com
|
507
|
+
Date:
|
508
|
+
- Thu, 17 Jan 2019 23:31:09 GMT
|
509
|
+
Content-Type:
|
510
|
+
- application/json; charset=utf-8
|
511
|
+
Transfer-Encoding:
|
512
|
+
- chunked
|
513
|
+
Status:
|
514
|
+
- 404 Not Found
|
515
|
+
X-Ratelimit-Limit:
|
516
|
+
- '5000'
|
517
|
+
X-Ratelimit-Remaining:
|
518
|
+
- '4989'
|
519
|
+
X-Ratelimit-Reset:
|
520
|
+
- '1547771467'
|
521
|
+
X-Oauth-Scopes:
|
522
|
+
- ''
|
523
|
+
X-Accepted-Oauth-Scopes:
|
524
|
+
- repo
|
525
|
+
X-Github-Media-Type:
|
526
|
+
- github.v3; format=json
|
527
|
+
Access-Control-Expose-Headers:
|
528
|
+
- ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
|
529
|
+
X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval,
|
530
|
+
X-GitHub-Media-Type
|
531
|
+
Access-Control-Allow-Origin:
|
532
|
+
- "*"
|
533
|
+
Strict-Transport-Security:
|
534
|
+
- max-age=31536000; includeSubdomains; preload
|
535
|
+
X-Frame-Options:
|
536
|
+
- deny
|
537
|
+
X-Content-Type-Options:
|
538
|
+
- nosniff
|
539
|
+
X-Xss-Protection:
|
540
|
+
- 1; mode=block
|
541
|
+
Referrer-Policy:
|
542
|
+
- origin-when-cross-origin, strict-origin-when-cross-origin
|
543
|
+
Content-Security-Policy:
|
544
|
+
- default-src 'none'
|
545
|
+
X-Github-Request-Id:
|
546
|
+
- B1CE:74B1:1A6A899:3C03775:5C41103D
|
547
|
+
body:
|
548
|
+
encoding: ASCII-8BIT
|
549
|
+
string: '{"message":"Not Found","documentation_url":"https://developer.github.com/v3/repos/#get"}'
|
550
|
+
http_version:
|
551
|
+
recorded_at: Thu, 17 Jan 2019 23:31:09 GMT
|
552
|
+
recorded_with: VCR 4.0.0
|
data/how_is.gemspec
CHANGED
@@ -19,20 +19,26 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
|
-
|
23
|
-
|
22
|
+
# how_is only supports Ruby versions under "normal maintenance".
|
23
|
+
# This number should be updated when a Ruby version goes into security
|
24
|
+
# maintenance.
|
25
|
+
#
|
26
|
+
# Ruby maintenance info: https://www.ruby-lang.org/en/downloads/branches/
|
27
|
+
#
|
28
|
+
# NOTE: Update Gemfile when this is updated!
|
29
|
+
spec.required_ruby_version = "~> 2.4"
|
24
30
|
|
25
|
-
spec.add_runtime_dependency "
|
31
|
+
spec.add_runtime_dependency "github_api", "~> 0.18.1"
|
32
|
+
spec.add_runtime_dependency "okay", "~> 11.0"
|
26
33
|
|
27
34
|
spec.add_runtime_dependency "json"
|
28
35
|
|
29
|
-
spec.add_development_dependency "bundler", "~>
|
36
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
30
37
|
spec.add_development_dependency "rake", "~> 12.3"
|
31
|
-
spec.add_development_dependency "rspec", "~> 3.
|
38
|
+
spec.add_development_dependency "rspec", "~> 3.8"
|
32
39
|
spec.add_development_dependency "timecop", "~> 0.9.1"
|
33
40
|
spec.add_development_dependency "vcr", "~> 4.0"
|
34
41
|
spec.add_development_dependency "webmock"
|
35
42
|
spec.add_development_dependency "rubocop", "~> 0.49.1"
|
36
43
|
spec.add_development_dependency "github_changelog_generator"
|
37
|
-
spec.add_development_dependency "pry"
|
38
44
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "digest"
|
4
|
+
require "tmpdir"
|
5
|
+
|
6
|
+
module HowIs
|
7
|
+
# Class for use in caching expensive operations
|
8
|
+
class Cacheable
|
9
|
+
def initialize(config, start_date, end_date, tmpdir = Dir.mktmpdir)
|
10
|
+
@config = config
|
11
|
+
@start_date = start_date
|
12
|
+
@end_date = end_date
|
13
|
+
@tmpdir = tmpdir
|
14
|
+
end
|
15
|
+
|
16
|
+
def cached(key, extra_digest = nil)
|
17
|
+
cache = @config["cache"]
|
18
|
+
return yield if cache.nil?
|
19
|
+
|
20
|
+
hash_key = []
|
21
|
+
hash_key << Digest::SHA1.hexdigest(extra_digest) if extra_digest
|
22
|
+
hash_key << Digest::SHA1.hexdigest(@config.to_json)
|
23
|
+
cache_key = File.join(@start_date, @end_date, key, hash_key.join("-"))
|
24
|
+
|
25
|
+
case cache["type"]
|
26
|
+
when "marshal"
|
27
|
+
MarshalCache.cached(cache_key, @tmpdir) { yield }
|
28
|
+
when "self"
|
29
|
+
# Can provide your own cache in HowIs.new
|
30
|
+
# e.g.
|
31
|
+
# cache_mechanism = ->(cache_key, config, block) do
|
32
|
+
# if cached?
|
33
|
+
# cached_value
|
34
|
+
# else
|
35
|
+
# block.call
|
36
|
+
# end
|
37
|
+
# end
|
38
|
+
# HowIs.new("owner/repo", date, cache_mechanism)
|
39
|
+
cache["cache_mechanism"].call(cache_key, @config, ->() { yield })
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# This is only okay on a local system
|
44
|
+
module MarshalCache
|
45
|
+
class << self
|
46
|
+
# rubocop:disable Security/MarshalLoad
|
47
|
+
def cached(key, tmpdir)
|
48
|
+
require "fileutils"
|
49
|
+
|
50
|
+
path = File.join(tmpdir, "how_is", key)
|
51
|
+
FileUtils.mkdir_p(File.dirname(path))
|
52
|
+
|
53
|
+
ret = nil
|
54
|
+
if File.exist?(path)
|
55
|
+
File.open(path, "rb") do |f|
|
56
|
+
ret = Marshal.load(f)
|
57
|
+
end
|
58
|
+
ret
|
59
|
+
else
|
60
|
+
ret = yield
|
61
|
+
File.open(path, "wb") do |file|
|
62
|
+
Marshal.dump(ret, file)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
ret
|
66
|
+
end
|
67
|
+
# rubocop:enable Security/MarshalLoad
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/how_is/cli.rb
CHANGED
@@ -1,138 +1,135 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "how_is"
|
4
|
-
require "
|
4
|
+
require "how_is/constants"
|
5
|
+
require "okay/simple_opts"
|
5
6
|
|
6
|
-
module HowIs
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
module HowIs
|
8
|
+
##
|
9
|
+
# Parses command-line arguments for how_is.
|
10
|
+
class CLI
|
11
|
+
MissingArgument = Class.new(OptionParser::MissingArgument)
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
end
|
13
|
+
REPO_REGEXP = /.+\/.+/
|
14
|
+
DATE_REGEXP = /\d\d\d\d-\d\d-\d\d/
|
14
15
|
|
15
|
-
|
16
|
-
# a valid JSON report.
|
17
|
-
class InvalidInputFileError < OptionsError
|
18
|
-
end
|
16
|
+
attr_reader :options, :help_text
|
19
17
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
def self.parse(*args)
|
19
|
+
new.parse(*args)
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@options = nil
|
24
|
+
@help_text = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
# Parses an Array of command-line arguments into an equivalent Hash.
|
28
|
+
#
|
29
|
+
# The results of this can be used to control the behavior of the rest
|
30
|
+
# of the library.
|
31
|
+
#
|
32
|
+
# @params argv [Array] An array of command-line arguments, e.g. +ARGV+.
|
33
|
+
# @return [Hash] A Hash containing data used to control HowIs behavior.
|
34
|
+
def parse(argv)
|
35
|
+
parser, options = parse_main(argv)
|
36
|
+
|
37
|
+
# Options that are mutually-exclusive with everything else.
|
38
|
+
options = {:help => true} if options[:help]
|
39
|
+
options = {:version => true} if options[:version]
|
40
|
+
|
41
|
+
validate_options!(options)
|
42
|
+
|
43
|
+
@options = options
|
44
|
+
@help_text = parser.to_s
|
45
|
+
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# parse_main() is as short as can be managed. It's fine as-is.
|
52
|
+
# rubocop:disable Metrics/MethodLength
|
24
53
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
opts.
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
| Where:
|
40
|
-
| REPOSITORY is <GitHub username or org>/<repository name>.
|
41
|
-
| and
|
42
|
-
| REPORT_DATE is the last day the report covers, in the format YYYY-mm-dd.
|
43
|
-
|
|
44
|
-
| E.g., if you wanted to check https://github.com/how-is/how_is for
|
45
|
-
| November 01 2016 through December 01 2016, you'd run:
|
46
|
-
| how_is how-is/how_is 2016-12-01
|
47
|
-
|
|
54
|
+
# This does a significant chunk of the work for parse().
|
55
|
+
#
|
56
|
+
# @return [Array] An array containing the +OptionParser+ and the result
|
57
|
+
# of running it.
|
58
|
+
def parse_main(argv)
|
59
|
+
defaults = {
|
60
|
+
report: HowIs::DEFAULT_REPORT_FILE,
|
61
|
+
}
|
62
|
+
|
63
|
+
opts = Okay::SimpleOpts.new(defaults: defaults)
|
64
|
+
|
65
|
+
opts.banner = <<~EOF
|
66
|
+
Usage: how_is --repository REPOSITORY --date REPORT_DATE [--output REPORT_FILE]
|
67
|
+
how_is --config CONFIG_FILE --date REPORT_DATE
|
48
68
|
EOF
|
49
69
|
|
50
|
-
opts.separator ""
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
70
|
+
opts.separator "\nOptions:"
|
71
|
+
|
72
|
+
opts.simple("--config CONFIG_FILE",
|
73
|
+
"YAML config file for automated reports.",
|
74
|
+
:config)
|
75
|
+
|
76
|
+
opts.simple("--no-user-config",
|
77
|
+
"Don't load user configuration file.",
|
78
|
+
:no_user_config)
|
79
|
+
|
80
|
+
opts.simple("--env-config",
|
81
|
+
"Use environment variables for configuration.",
|
82
|
+
"Read first: https://how-is.github.io/config",
|
83
|
+
:env_login)
|
84
|
+
|
85
|
+
opts.simple("--repository USER/REPO", REPO_REGEXP,
|
86
|
+
"Repository to generate a report for.",
|
87
|
+
:repository)
|
88
|
+
|
89
|
+
opts.simple("--date YYYY-MM-DD", DATE_REGEXP, "Last date of the report.",
|
90
|
+
:date)
|
91
|
+
|
92
|
+
opts.simple("--output REPORT_FILE", format_regexp,
|
93
|
+
"Output file for the report.",
|
94
|
+
"Supported file formats: #{formats}.",
|
95
|
+
:report)
|
96
|
+
|
97
|
+
opts.simple("--verbose", "Print debug information.", :verbose)
|
98
|
+
opts.simple("-v", "--version", "Prints version information.", :version)
|
99
|
+
opts.simple("-h", "--help", "Print help text.", :help)
|
100
|
+
|
101
|
+
[opts, opts.parse(argv)]
|
80
102
|
end
|
81
|
-
|
82
|
-
# rubocop:enable Metrics/
|
83
|
-
|
84
|
-
#
|
85
|
-
#
|
86
|
-
#
|
87
|
-
|
88
|
-
|
89
|
-
#
|
90
|
-
|
91
|
-
|
92
|
-
if options
|
93
|
-
|
94
|
-
options
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
elsif options[:config]
|
99
|
-
# If --config is passed, _only_ accept --config.
|
100
|
-
options = keep_only.call(options, :config)
|
101
|
-
if argv.length >= 1
|
102
|
-
options[:date] = argv.delete_at(0)
|
103
|
-
else
|
104
|
-
raise HowIsArgumentError, "Expected date."
|
105
|
-
end
|
106
|
-
else
|
107
|
-
# If we get here, we're generating a report from the command line,
|
108
|
-
# without using --from or --config.
|
109
|
-
|
110
|
-
# If --report isn't specified, default to HowIs::DEFAULT_REPORT_FILE.
|
111
|
-
options[:report] ||= HowIs::DEFAULT_REPORT_FILE
|
112
|
-
|
113
|
-
# If we can't export to the specified file, raise an exception.
|
114
|
-
unless HowIs.can_export_to?(options[:report])
|
115
|
-
raise InvalidOutputFileError, "Invalid file: #{options[:report]}. Supported formats: #{HowIs.supported_formats.join(', ')}"
|
116
|
-
end
|
117
|
-
|
118
|
-
if argv.length >= 2
|
119
|
-
options[:repository] = argv.delete_at(0)
|
120
|
-
options[:date] = argv.delete_at(0)
|
121
|
-
else
|
122
|
-
raise HowIsArgumentError, "Expected both repository and date."
|
123
|
-
end
|
103
|
+
|
104
|
+
# rubocop:enable Metrics/MethodLength
|
105
|
+
|
106
|
+
# Given an +options+ Hash, determine if we got a valid combination of
|
107
|
+
# options.
|
108
|
+
#
|
109
|
+
# 1. Anything with `--help` and `--version` is always valid.
|
110
|
+
# 2. Otherwise, `--repository` or `--config` is required.
|
111
|
+
# 3. If `--repository` or `--config` is required, so is `--date`.
|
112
|
+
#
|
113
|
+
# @param options [Hash] The result of CLI#parse().
|
114
|
+
# @raise [MissingArgument] if we did not get a valid options Hash.
|
115
|
+
def validate_options!(options)
|
116
|
+
return if options[:help] || options[:version]
|
117
|
+
raise MissingArgument, "--date" unless options[:date]
|
118
|
+
raise MissingArgument, "--repository or --config" unless
|
119
|
+
options[:repository] || options[:config]
|
124
120
|
end
|
125
121
|
|
126
|
-
#
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
#
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
122
|
+
# @return [String] A comma-separated list of supported formats.
|
123
|
+
def formats
|
124
|
+
HowIs.supported_formats.join(", ")
|
125
|
+
end
|
126
|
+
|
127
|
+
# @return [Regexp] a +Regexp+ object which matches any path ending
|
128
|
+
# with an extension corresponding to a supported format.
|
129
|
+
def format_regexp
|
130
|
+
regexp_parts = HowIs.supported_formats.map { |x| Regexp.escape(x) }
|
131
|
+
|
132
|
+
/.+\.(#{regexp_parts.join("|")})/
|
133
|
+
end
|
137
134
|
end
|
138
135
|
end
|