@@ -5,3 +5,8 @@ pkg/
|
|||||||
coverage/
|
coverage/
|
||||||
.bundle
|
.bundle
|
||||||
spec/*.lock
|
spec/*.lock
|
||||||
|
doc
|
||||||
|
.yardoc
|
||||||
|
Gemfile.lock
|
||||||
|
gemfiles/Gemfile.2_4_0.lock
|
||||||
|
gemfiles/Gemfile.jruby-9.1.5.0.lock
|
||||||
|
|||||||
+14
-1
@@ -1,5 +1,12 @@
|
|||||||
inherit_from: .rubocop_todo.yml
|
inherit_from: .rubocop_todo.yml
|
||||||
|
|
||||||
|
AllCops:
|
||||||
|
DisplayCopNames: true
|
||||||
|
DisplayStyleGuide: true
|
||||||
|
Exclude:
|
||||||
|
- 'vendor/**/*'
|
||||||
|
- 'gemfiles/**/*'
|
||||||
|
|
||||||
Metrics/LineLength:
|
Metrics/LineLength:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
@@ -53,4 +60,10 @@ Style/RegexpLiteral:
|
|||||||
|
|
||||||
Style/MutableConstant:
|
Style/MutableConstant:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
|
# "Use idx.positive? instead of idx > 0."
|
||||||
|
Style/NumericPredicate:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
Style/SafeNavigation:
|
||||||
|
Enabled: false
|
||||||
+1
-37
@@ -1,20 +1,11 @@
|
|||||||
# This configuration was generated by
|
# This configuration was generated by
|
||||||
# `rubocop --auto-gen-config`
|
# `rubocop --auto-gen-config`
|
||||||
# on 2016-02-23 17:18:27 +0200 using RuboCop version 0.37.2.
|
# on 2016-09-30 23:56:15 +0200 using RuboCop version 0.43.0.
|
||||||
# The point is for the user to remove these configuration records
|
# The point is for the user to remove these configuration records
|
||||||
# one by one as the offenses are removed from the code base.
|
# one by one as the offenses are removed from the code base.
|
||||||
# Note that changes in the inspected code, or installation of new
|
# Note that changes in the inspected code, or installation of new
|
||||||
# versions of RuboCop, may require this file to be generated again.
|
# versions of RuboCop, may require this file to be generated again.
|
||||||
|
|
||||||
# Offense count: 1
|
|
||||||
Lint/ImplicitStringConcatenation:
|
|
||||||
Exclude:
|
|
||||||
- 'lib/github_changelog_generator/parser.rb'
|
|
||||||
|
|
||||||
# Offense count: 14
|
|
||||||
Metrics/AbcSize:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Offense count: 1
|
# Offense count: 1
|
||||||
Metrics/CyclomaticComplexity:
|
Metrics/CyclomaticComplexity:
|
||||||
Max: 7
|
Max: 7
|
||||||
@@ -22,30 +13,3 @@ Metrics/CyclomaticComplexity:
|
|||||||
# Offense count: 2
|
# Offense count: 2
|
||||||
Metrics/PerceivedComplexity:
|
Metrics/PerceivedComplexity:
|
||||||
Max: 8
|
Max: 8
|
||||||
|
|
||||||
# Offense count: 1
|
|
||||||
Style/AccessorMethodName:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Offense count: 10
|
|
||||||
Style/Documentation:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Offense count: 1
|
|
||||||
# Configuration parameters: MinBodyLength.
|
|
||||||
Style/GuardClause:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Offense count: 2
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
# Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles.
|
|
||||||
# SupportedStyles: skip_modifier_ifs, always
|
|
||||||
Style/Next:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Offense count: 3
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
# Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes.
|
|
||||||
# SupportedStyles: slashes, percent_r, mixed
|
|
||||||
Style/RegexpLiteral:
|
|
||||||
Enabled: false
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
2.3.1
|
||||||
+20
-13
@@ -1,23 +1,30 @@
|
|||||||
|
language: ruby
|
||||||
cache:
|
cache:
|
||||||
- bundler
|
- bundler
|
||||||
language: ruby
|
|
||||||
before_install:
|
before_install:
|
||||||
- gem update --system
|
- gem update --system
|
||||||
- gem install bundler
|
- gem install bundler
|
||||||
rvm:
|
|
||||||
- 2.1
|
|
||||||
script: bundle exec rake checks
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
# Test install on clean system
|
- rvm: 2.2.2
|
||||||
- install: true
|
install: true # This skips 'bundle install'
|
||||||
script:
|
script: gem build github_changelog_generator && gem install *.gem
|
||||||
- gem build github_changelog_generator
|
- rvm: 2.2.2
|
||||||
- gem install *.gem
|
install: true # This skips 'bundle install'
|
||||||
- install: true
|
script: gem build github_changelog_generator && bundle install
|
||||||
script:
|
gemfile: spec/install-gem-in-bundler.gemfile
|
||||||
- gem build github_changelog_generator
|
- rvm: 2.1
|
||||||
- bundle install --gemfile spec/install-gem-in-bundler.gemfile
|
gemfile: gemfiles/Gemfile.2_1
|
||||||
|
- rvm: 2.3.1
|
||||||
|
gemfile: gemfiles/Gemfile.2_3_1
|
||||||
|
- rvm: 2.4.0-preview2
|
||||||
|
gemfile: gemfiles/Gemfile.2_4_0
|
||||||
|
- rvm: jruby-9.1.5.0
|
||||||
|
jdk: oraclejdk8
|
||||||
|
gemfile: gemfiles/Gemfile.jruby-9.1.5.0
|
||||||
|
env:
|
||||||
|
- JRUBY_OPTS=--debug
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
email:
|
email:
|
||||||
recipients:
|
recipients:
|
||||||
|
|||||||
+56
-4
@@ -1,5 +1,61 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## [1.13.2](https://github.com/skywinder/github-changelog-generator/tree/1.13.2) (2016-09-29)
|
||||||
|
[Full Changelog](https://github.com/skywinder/github-changelog-generator/compare/1.13.1...1.13.2)
|
||||||
|
|
||||||
|
**Implemented enhancements:**
|
||||||
|
|
||||||
|
- Replace GPL'd colorize gem with MIT-licensed rainbow [\#408](https://github.com/skywinder/github-changelog-generator/pull/408) ([jamesc](https://github.com/jamesc))
|
||||||
|
- Limit number of simultaneous requests to 25 [\#407](https://github.com/skywinder/github-changelog-generator/pull/407) ([jkeiser](https://github.com/jkeiser))
|
||||||
|
- Report actual github error when rate limit exceeded [\#405](https://github.com/skywinder/github-changelog-generator/pull/405) ([jkeiser](https://github.com/jkeiser))
|
||||||
|
- Make error messages print on error [\#404](https://github.com/skywinder/github-changelog-generator/pull/404) ([jkeiser](https://github.com/jkeiser))
|
||||||
|
|
||||||
|
**Fixed bugs:**
|
||||||
|
|
||||||
|
- Fetching events for issues and PRs triggers abuse rate limits [\#406](https://github.com/skywinder/github-changelog-generator/issues/406)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Add bump gem to development deps [\#423](https://github.com/skywinder/github-changelog-generator/pull/423) ([olleolleolle](https://github.com/olleolleolle))
|
||||||
|
- Skip logger helper in coverage [\#421](https://github.com/skywinder/github-changelog-generator/pull/421) ([olleolleolle](https://github.com/olleolleolle))
|
||||||
|
- Travis: Test on 2.4.0-preview2 [\#420](https://github.com/skywinder/github-changelog-generator/pull/420) ([olleolleolle](https://github.com/olleolleolle))
|
||||||
|
- Travis: Collecting the config, gemspec: extract development deps to Gemfile [\#417](https://github.com/skywinder/github-changelog-generator/pull/417) ([olleolleolle](https://github.com/olleolleolle))
|
||||||
|
- Update README.md [\#415](https://github.com/skywinder/github-changelog-generator/pull/415) ([dijonkitchen](https://github.com/dijonkitchen))
|
||||||
|
- README: Add Gitter badge [\#413](https://github.com/skywinder/github-changelog-generator/pull/413) ([olleolleolle](https://github.com/olleolleolle))
|
||||||
|
- CircleCI hook for Gitter notification [\#411](https://github.com/skywinder/github-changelog-generator/pull/411) ([olleolleolle](https://github.com/olleolleolle))
|
||||||
|
- Spec: avoid Ruby warning about already-defined constant [\#409](https://github.com/skywinder/github-changelog-generator/pull/409) ([olleolleolle](https://github.com/olleolleolle))
|
||||||
|
|
||||||
|
## [1.13.1](https://github.com/skywinder/github-changelog-generator/tree/1.13.1) (2016-07-22)
|
||||||
|
[Full Changelog](https://github.com/skywinder/github-changelog-generator/compare/1.13.0...1.13.1)
|
||||||
|
|
||||||
|
**Implemented enhancements:**
|
||||||
|
|
||||||
|
- Don't constrain runtime deps. [\#400](https://github.com/skywinder/github-changelog-generator/pull/400) ([jkeiser](https://github.com/jkeiser))
|
||||||
|
|
||||||
|
**Fixed bugs:**
|
||||||
|
|
||||||
|
- `--user` flag should not be ignored [\#397](https://github.com/skywinder/github-changelog-generator/issues/397)
|
||||||
|
- GHE not working with --github-site set to an enterprise site [\#395](https://github.com/skywinder/github-changelog-generator/issues/395)
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- Contributors Section [\#398](https://github.com/skywinder/github-changelog-generator/issues/398)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Ability to implicity set user and project from command line [\#401](https://github.com/skywinder/github-changelog-generator/pull/401) ([skywinder](https://github.com/skywinder))
|
||||||
|
- Show how to use it with Rakefile [\#399](https://github.com/skywinder/github-changelog-generator/pull/399) ([edusantana](https://github.com/edusantana))
|
||||||
|
- Adds documentation on using a GHE endpoint [\#396](https://github.com/skywinder/github-changelog-generator/pull/396) ([cormacmccarthy](https://github.com/cormacmccarthy))
|
||||||
|
- Rake task usage: Added a missing option exclude\_tags\_regex [\#393](https://github.com/skywinder/github-changelog-generator/pull/393) ([perlun](https://github.com/perlun))
|
||||||
|
- Parser: YARD docstrings and a rename, and RegExp named capture groups [\#391](https://github.com/skywinder/github-changelog-generator/pull/391) ([olleolleolle](https://github.com/olleolleolle))
|
||||||
|
|
||||||
|
## [1.13.0](https://github.com/skywinder/github-changelog-generator/tree/1.13.0) (2016-07-04)
|
||||||
|
[Full Changelog](https://github.com/skywinder/github-changelog-generator/compare/1.12.1...1.13.0)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Carry PR \#301: usernames\_as\_github\_logins [\#392](https://github.com/skywinder/github-changelog-generator/pull/392) ([olleolleolle](https://github.com/olleolleolle))
|
||||||
|
|
||||||
## [1.12.1](https://github.com/skywinder/github-changelog-generator/tree/1.12.1) (2016-05-09)
|
## [1.12.1](https://github.com/skywinder/github-changelog-generator/tree/1.12.1) (2016-05-09)
|
||||||
[Full Changelog](https://github.com/skywinder/github-changelog-generator/compare/1.12.0...1.12.1)
|
[Full Changelog](https://github.com/skywinder/github-changelog-generator/compare/1.12.0...1.12.1)
|
||||||
|
|
||||||
@@ -509,10 +565,6 @@
|
|||||||
|
|
||||||
- Encapsulate \[ \> \* \_ \ \] signs in issues names [\#34](https://github.com/skywinder/github-changelog-generator/issues/34)
|
- Encapsulate \[ \> \* \_ \ \] signs in issues names [\#34](https://github.com/skywinder/github-changelog-generator/issues/34)
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- Add a Bitdeli Badge to README [\#36](https://github.com/skywinder/github-changelog-generator/pull/36) ([bitdeli-chef](https://github.com/bitdeli-chef))
|
|
||||||
|
|
||||||
## [1.2.1](https://github.com/skywinder/github-changelog-generator/tree/1.2.1) (2014-11-22)
|
## [1.2.1](https://github.com/skywinder/github-changelog-generator/tree/1.2.1) (2014-11-22)
|
||||||
[Full Changelog](https://github.com/skywinder/github-changelog-generator/compare/1.2.0...1.2.1)
|
[Full Changelog](https://github.com/skywinder/github-changelog-generator/compare/1.2.0...1.2.1)
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,30 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
source "https://rubygems.org"
|
source "https://rubygems.org"
|
||||||
|
|
||||||
gemspec
|
gemspec
|
||||||
|
|
||||||
group :test do
|
group :development, :test do
|
||||||
gem "rake"
|
gem "rake"
|
||||||
gem "bundler"
|
gem "bundler"
|
||||||
gem "rubocop"
|
gem "overcommit", ">= 0.31"
|
||||||
gem "overcommit"
|
gem "rubocop", ">= 0.43"
|
||||||
|
end
|
||||||
|
|
||||||
|
group :development do
|
||||||
|
gem "bump"
|
||||||
|
end
|
||||||
|
|
||||||
|
group :test do
|
||||||
|
gem "vcr"
|
||||||
|
gem "multi_json"
|
||||||
|
gem "webmock"
|
||||||
gem "coveralls", "~>0.8", require: false
|
gem "coveralls", "~>0.8", require: false
|
||||||
gem "simplecov", "~>0.10", require: false
|
gem "simplecov", "~>0.10", require: false
|
||||||
gem "codeclimate-test-reporter", "~>0.4"
|
gem "codeclimate-test-reporter", "~>0.4"
|
||||||
|
if RUBY_VERSION > "2"
|
||||||
|
gem "json", "~> 2.0", ">= 2.0.2"
|
||||||
|
else
|
||||||
|
gem "json"
|
||||||
|
end
|
||||||
|
gem "rspec", "< 4"
|
||||||
end
|
end
|
||||||
|
|||||||
-108
@@ -1,108 +0,0 @@
|
|||||||
PATH
|
|
||||||
remote: .
|
|
||||||
specs:
|
|
||||||
github_changelog_generator (1.12.1)
|
|
||||||
colorize (~> 0.7)
|
|
||||||
github_api (~> 0.12)
|
|
||||||
rake (>= 10.0)
|
|
||||||
|
|
||||||
GEM
|
|
||||||
remote: https://rubygems.org/
|
|
||||||
specs:
|
|
||||||
addressable (2.4.0)
|
|
||||||
ast (2.2.0)
|
|
||||||
childprocess (0.5.9)
|
|
||||||
ffi (~> 1.0, >= 1.0.11)
|
|
||||||
codeclimate-test-reporter (0.5.0)
|
|
||||||
simplecov (>= 0.7.1, < 1.0.0)
|
|
||||||
colorize (0.7.7)
|
|
||||||
coveralls (0.8.13)
|
|
||||||
json (~> 1.8)
|
|
||||||
simplecov (~> 0.11.0)
|
|
||||||
term-ansicolor (~> 1.3)
|
|
||||||
thor (~> 0.19.1)
|
|
||||||
tins (~> 1.6.0)
|
|
||||||
descendants_tracker (0.0.4)
|
|
||||||
thread_safe (~> 0.3, >= 0.3.1)
|
|
||||||
diff-lcs (1.2.5)
|
|
||||||
docile (1.1.5)
|
|
||||||
faraday (0.9.2)
|
|
||||||
multipart-post (>= 1.2, < 3)
|
|
||||||
ffi (1.9.10)
|
|
||||||
github_api (0.13.1)
|
|
||||||
addressable (~> 2.4.0)
|
|
||||||
descendants_tracker (~> 0.0.4)
|
|
||||||
faraday (~> 0.8, < 0.10)
|
|
||||||
hashie (>= 3.4)
|
|
||||||
multi_json (>= 1.7.5, < 2.0)
|
|
||||||
oauth2
|
|
||||||
hashie (3.4.4)
|
|
||||||
iniparse (1.4.2)
|
|
||||||
json (1.8.3)
|
|
||||||
jwt (1.5.1)
|
|
||||||
multi_json (1.12.0)
|
|
||||||
multi_xml (0.5.5)
|
|
||||||
multipart-post (2.0.0)
|
|
||||||
oauth2 (1.1.0)
|
|
||||||
faraday (>= 0.8, < 0.10)
|
|
||||||
jwt (~> 1.0, < 1.5.2)
|
|
||||||
multi_json (~> 1.3)
|
|
||||||
multi_xml (~> 0.5)
|
|
||||||
rack (>= 1.2, < 3)
|
|
||||||
overcommit (0.33.0)
|
|
||||||
childprocess (~> 0.5.8)
|
|
||||||
iniparse (~> 1.4)
|
|
||||||
parser (2.3.1.0)
|
|
||||||
ast (~> 2.2)
|
|
||||||
powerpack (0.1.1)
|
|
||||||
rack (1.6.4)
|
|
||||||
rainbow (2.1.0)
|
|
||||||
rake (11.1.2)
|
|
||||||
rspec (3.4.0)
|
|
||||||
rspec-core (~> 3.4.0)
|
|
||||||
rspec-expectations (~> 3.4.0)
|
|
||||||
rspec-mocks (~> 3.4.0)
|
|
||||||
rspec-core (3.4.4)
|
|
||||||
rspec-support (~> 3.4.0)
|
|
||||||
rspec-expectations (3.4.0)
|
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
|
||||||
rspec-support (~> 3.4.0)
|
|
||||||
rspec-mocks (3.4.1)
|
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
|
||||||
rspec-support (~> 3.4.0)
|
|
||||||
rspec-support (3.4.1)
|
|
||||||
rubocop (0.39.0)
|
|
||||||
parser (>= 2.3.0.7, < 3.0)
|
|
||||||
powerpack (~> 0.1)
|
|
||||||
rainbow (>= 1.99.1, < 3.0)
|
|
||||||
ruby-progressbar (~> 1.7)
|
|
||||||
unicode-display_width (~> 1.0, >= 1.0.1)
|
|
||||||
ruby-progressbar (1.8.0)
|
|
||||||
simplecov (0.11.2)
|
|
||||||
docile (~> 1.1.0)
|
|
||||||
json (~> 1.8)
|
|
||||||
simplecov-html (~> 0.10.0)
|
|
||||||
simplecov-html (0.10.0)
|
|
||||||
term-ansicolor (1.3.2)
|
|
||||||
tins (~> 1.0)
|
|
||||||
thor (0.19.1)
|
|
||||||
thread_safe (0.3.5)
|
|
||||||
tins (1.6.0)
|
|
||||||
unicode-display_width (1.0.5)
|
|
||||||
|
|
||||||
PLATFORMS
|
|
||||||
ruby
|
|
||||||
|
|
||||||
DEPENDENCIES
|
|
||||||
bundler
|
|
||||||
codeclimate-test-reporter (~> 0.4)
|
|
||||||
coveralls (~> 0.8)
|
|
||||||
github_changelog_generator!
|
|
||||||
overcommit
|
|
||||||
rake
|
|
||||||
rspec (>= 3.2)
|
|
||||||
rubocop
|
|
||||||
simplecov (~> 0.10)
|
|
||||||
|
|
||||||
BUNDLED WITH
|
|
||||||
1.12.0.rc.2
|
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
[](http://inch-ci.org/github/skywinder/github-changelog-generator)
|
[](http://inch-ci.org/github/skywinder/github-changelog-generator)
|
||||||
[](https://codeclimate.com/github/skywinder/github-changelog-generator)
|
[](https://codeclimate.com/github/skywinder/github-changelog-generator)
|
||||||
[](https://codeclimate.com/github/skywinder/github-changelog-generator)
|
[](https://codeclimate.com/github/skywinder/github-changelog-generator)
|
||||||
|
[](https://gitter.im/github-changelog-generator/chat?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
|
|
||||||
GitHub Changelog Generator 
|
GitHub Changelog Generator 
|
||||||
==================
|
==================
|
||||||
@@ -23,7 +24,7 @@ GitHub Changelog Generator 
|
|||||||
|
|
||||||
### Changelog generation has never been so easy
|
### Changelog generation has never been so easy
|
||||||
|
|
||||||
**Fully automate changelog generation** - This gem generates change log file based on **tags**, **issues** and merged **pull requests** (and splits them into separate lists according labels) from :octocat: GitHub Issue Tracker.
|
**Fully automate changelog generation** - This gem generates change log file based on **tags**, **issues** and merged **pull requests** (and splits them into separate lists according to labels) from :octocat: GitHub Issue Tracker.
|
||||||
|
|
||||||
Since now you don't have to fill your `CHANGELOG.md` manually: just run the script, relax and take a cup of :coffee: before your next release! :tada:
|
Since now you don't have to fill your `CHANGELOG.md` manually: just run the script, relax and take a cup of :coffee: before your next release! :tada:
|
||||||
|
|
||||||
@@ -36,7 +37,9 @@ Because software tools are for people. If you don’t care, why are you contribu
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
[sudo] gem install github_changelog_generator
|
gem install github_changelog_generator
|
||||||
|
|
||||||
|
See also Troubleshooting.
|
||||||
|
|
||||||
## Output example
|
## Output example
|
||||||
|
|
||||||
@@ -77,6 +80,11 @@ Because software tools are for people. If you don’t care, why are you contribu
|
|||||||
- `github_changelog_generator -u github_username -p github_project`
|
- `github_changelog_generator -u github_username -p github_project`
|
||||||
- `github_changelog_generator github_username/github_project`
|
- `github_changelog_generator github_username/github_project`
|
||||||
|
|
||||||
|
- If you are running it against a repository on a Github Enterprise install, you must specify *both* `--github-site` and `--github-api` command line options:
|
||||||
|
|
||||||
|
github_changelog_generator --github-site="https://github.yoursite.com" \
|
||||||
|
--github-api="https://github.yoursite.com/api/v3/"
|
||||||
|
|
||||||
This generates a changelog to the `CHANGELOG.md` file, with pretty markdown formatting.
|
This generates a changelog to the `CHANGELOG.md` file, with pretty markdown formatting.
|
||||||
|
|
||||||
### Params
|
### Params
|
||||||
@@ -133,6 +141,8 @@ we've provided a `rake` task library for your changelog generation.
|
|||||||
Just put something like this in your `Rakefile`:
|
Just put something like this in your `Rakefile`:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
|
require 'github_changelog_generator/task'
|
||||||
|
|
||||||
GitHubChangelogGenerator::RakeTask.new :changelog do |config|
|
GitHubChangelogGenerator::RakeTask.new :changelog do |config|
|
||||||
config.since_tag = '0.1.14'
|
config.since_tag = '0.1.14'
|
||||||
config.future_release = '0.2.0'
|
config.future_release = '0.2.0'
|
||||||
@@ -216,6 +226,31 @@ If you're seeing this warning, please do the following:
|
|||||||
1. Make sure you're providing an OAuth token, so you're not making requests anonymously. Using an OAuth token increases your hourly request maximum from 60 to 5000.
|
1. Make sure you're providing an OAuth token, so you're not making requests anonymously. Using an OAuth token increases your hourly request maximum from 60 to 5000.
|
||||||
2. If you have a large repo with lots of issues/PRs, you can use `--max-issues NUM` to limit the number of issues that are pulled back. For example: `--max-issues 1000`
|
2. If you have a large repo with lots of issues/PRs, you can use `--max-issues NUM` to limit the number of issues that are pulled back. For example: `--max-issues 1000`
|
||||||
|
|
||||||
|
- ***My Ruby version is very old, can I use this?***
|
||||||
|
|
||||||
|
When your Ruby is old, and you don't want to upgrade, and you want to
|
||||||
|
control which libraries you use, you can use Bundler.
|
||||||
|
|
||||||
|
In a Gemfile, perhaps in a non-deployed `:development` group, add this
|
||||||
|
gem:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
group :development do
|
||||||
|
gem 'github_changelog_generator', require: false
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you can keep back dependencies like rack, which currently is only
|
||||||
|
compatible with Ruby >= 2.2.2. So, use an older version for your app by
|
||||||
|
adding a line like this to the Gemfile:
|
||||||
|
|
||||||
|
```
|
||||||
|
gem 'rack', '~> 1.6'
|
||||||
|
```
|
||||||
|
|
||||||
|
This way, you can keep on using github_changelog_generator, even if you
|
||||||
|
can't get the latest version of Ruby installed.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
1. Create an issue and describe your idea
|
1. Create an issue and describe your idea
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
require "bundler"
|
require "bundler"
|
||||||
require "bundler/gem_tasks"
|
require "bundler/gem_tasks"
|
||||||
require "rubocop/rake_task"
|
require "rubocop/rake_task"
|
||||||
@@ -36,4 +37,4 @@ task :copy_man_page_to_manpath do |_t|
|
|||||||
end
|
end
|
||||||
|
|
||||||
task checks: [:rubocop, :rspec]
|
task checks: [:rubocop, :rspec]
|
||||||
task default: [:copy_man_page_to_manpath]
|
task default: [:rubocop, :rspec]
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#! /usr/bin/env ruby
|
#! /usr/bin/env ruby
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "../lib/github_changelog_generator"
|
require_relative "../lib/github_changelog_generator"
|
||||||
GitHubChangelogGenerator::ChangelogGenerator.new.run
|
GitHubChangelogGenerator::ChangelogGenerator.new.run
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#! /usr/bin/env ruby
|
#! /usr/bin/env ruby
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "../lib/github_changelog_generator"
|
require_relative "../lib/github_changelog_generator"
|
||||||
GitHubChangelogGenerator::ChangelogGenerator.new.run
|
GitHubChangelogGenerator::ChangelogGenerator.new.run
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
notify:
|
||||||
|
webhooks:
|
||||||
|
# A list of hook hashes, containing the url field
|
||||||
|
# gitter hook
|
||||||
|
- url: https://webhooks.gitter.im/e/2d81eb1ae7695fdc82c4
|
||||||
|
- url: https://webhooks.gitter.im/e/3ec6a35fad7e9991058e
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
eval_gemfile File.expand_path('../../Gemfile', __FILE__)
|
||||||
|
gem 'rack', '~> 1.6'
|
||||||
|
gem 'activesupport', '~> 4'
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
eval_gemfile File.expand_path('../../Gemfile', __FILE__)
|
||||||
|
gem 'rack', '>= 2'
|
||||||
|
gem 'activesupport', '>= 4'
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
eval_gemfile File.expand_path('../../Gemfile', __FILE__)
|
||||||
|
gem 'rack', '>= 2'
|
||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
eval_gemfile File.expand_path('../../Gemfile', __FILE__)
|
||||||
|
gem 'rack', '>= 2'
|
||||||
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
|
# frozen_string_literal: true
|
||||||
lib = File.expand_path("../lib", __FILE__)
|
lib = File.expand_path("../lib", __FILE__)
|
||||||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||||
require "github_changelog_generator/version"
|
require "github_changelog_generator/version"
|
||||||
@@ -24,11 +25,8 @@ Gem::Specification.new do |spec|
|
|||||||
spec.require_paths = ["lib"]
|
spec.require_paths = ["lib"]
|
||||||
|
|
||||||
spec.add_runtime_dependency "rake", ">= 10.0"
|
spec.add_runtime_dependency "rake", ">= 10.0"
|
||||||
spec.add_runtime_dependency("github_api", ["~> 0.12"])
|
spec.add_runtime_dependency "rainbow", ">= 2.1"
|
||||||
spec.add_runtime_dependency("colorize", ["~> 0.7"])
|
spec.add_runtime_dependency("octokit", ["~> 4.0"])
|
||||||
|
spec.add_runtime_dependency("faraday-http-cache")
|
||||||
spec.add_development_dependency("overcommit", ">= 0.31")
|
spec.add_runtime_dependency("activesupport")
|
||||||
spec.add_development_dependency("rspec", ">= 3.2")
|
|
||||||
spec.add_development_dependency "bundler", ">= 1.7"
|
|
||||||
spec.add_development_dependency("rubocop", ">= 0.31")
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
# Change Log
|
|
||||||
|
|
||||||
## [Unreleased](https://github.com/skywinder/changelog_test/tree/HEAD)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/skywinder/changelog_test/compare/0.0.4...HEAD)
|
|
||||||
|
|
||||||
**Implemented enhancements:**
|
|
||||||
|
|
||||||
- Enchancment [\#9](https://github.com/skywinder/changelog_test/issues/9)
|
|
||||||
- PR with enhancement label [\#16](https://github.com/skywinder/changelog_test/pull/16) ([skywinder](https://github.com/skywinder))
|
|
||||||
|
|
||||||
**Fixed bugs:**
|
|
||||||
|
|
||||||
- BugFix [\#11](https://github.com/skywinder/changelog_test/issues/11)
|
|
||||||
|
|
||||||
**Closed issues:**
|
|
||||||
|
|
||||||
- Issue closed from commit from PR [\#14](https://github.com/skywinder/changelog_test/issues/14)
|
|
||||||
- Issue, closed by PR [\#12](https://github.com/skywinder/changelog_test/issues/12)
|
|
||||||
- Issue [\#10](https://github.com/skywinder/changelog_test/issues/10)
|
|
||||||
- Issue with some other label - Should be in closed issues [\#8](https://github.com/skywinder/changelog_test/issues/8)
|
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- Merged br \(should appear in change log also\) [\#21](https://github.com/skywinder/changelog_test/pull/21) ([skywinder](https://github.com/skywinder))
|
|
||||||
- This a PR with a lot of comments and events [\#17](https://github.com/skywinder/changelog_test/pull/17) ([skywinder](https://github.com/skywinder))
|
|
||||||
- This PR closes 14 from commit [\#15](https://github.com/skywinder/changelog_test/pull/15) ([skywinder](https://github.com/skywinder))
|
|
||||||
- This PR to close \#12 from body [\#13](https://github.com/skywinder/changelog_test/pull/13) ([skywinder](https://github.com/skywinder))
|
|
||||||
|
|
||||||
## [0.0.4](https://github.com/skywinder/changelog_test/tree/0.0.4) (2015-05-22)
|
|
||||||
[Full Changelog](https://github.com/skywinder/changelog_test/compare/v0.0.3...0.0.4)
|
|
||||||
|
|
||||||
**Closed issues:**
|
|
||||||
|
|
||||||
- Test issue, that should appear in 0.0.4 [\#3](https://github.com/skywinder/changelog_test/issues/3)
|
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- Add automatically generated change log file. [\#5](https://github.com/skywinder/changelog_test/pull/5) ([skywinder](https://github.com/skywinder))
|
|
||||||
|
|
||||||
## [v0.0.3](https://github.com/skywinder/changelog_test/tree/v0.0.3) (2015-03-04)
|
|
||||||
[Full Changelog](https://github.com/skywinder/changelog_test/compare/v0.0.2...v0.0.3)
|
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- fix \#3. hotfix. Should appear in v0.0.3 [\#4](https://github.com/skywinder/changelog_test/pull/4) ([skywinder](https://github.com/skywinder))
|
|
||||||
|
|
||||||
## [v0.0.2](https://github.com/skywinder/changelog_test/tree/v0.0.2) (2015-03-04)
|
|
||||||
[Full Changelog](https://github.com/skywinder/changelog_test/compare/v0.0.1...v0.0.2)
|
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- Here is a test hotfix should appear in v.0.0.2 [\#2](https://github.com/skywinder/changelog_test/pull/2) ([skywinder](https://github.com/skywinder))
|
|
||||||
|
|
||||||
## [v0.0.1](https://github.com/skywinder/changelog_test/tree/v0.0.1) (2015-03-02)
|
|
||||||
|
|
||||||
|
|
||||||
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
|
|
||||||
@@ -1,11 +1,16 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "github_api"
|
require "octokit"
|
||||||
|
require "faraday-http-cache"
|
||||||
|
require "logger"
|
||||||
|
require "active_support"
|
||||||
require "json"
|
require "json"
|
||||||
require "colorize"
|
require "multi_json"
|
||||||
require "benchmark"
|
require "benchmark"
|
||||||
|
|
||||||
require_relative "github_changelog_generator/helper"
|
require_relative "github_changelog_generator/helper"
|
||||||
|
require_relative "github_changelog_generator/options"
|
||||||
require_relative "github_changelog_generator/parser"
|
require_relative "github_changelog_generator/parser"
|
||||||
require_relative "github_changelog_generator/parser_file"
|
require_relative "github_changelog_generator/parser_file"
|
||||||
require_relative "github_changelog_generator/generator/generator"
|
require_relative "github_changelog_generator/generator/generator"
|
||||||
|
|||||||
@@ -1,221 +0,0 @@
|
|||||||
module GitHubChangelogGenerator
|
|
||||||
# A Fetcher responsible for all requests to GitHub and all basic manipulation with related data
|
|
||||||
# (such as filtering, validating, e.t.c)
|
|
||||||
#
|
|
||||||
# Example:
|
|
||||||
# fetcher = GitHubChangelogGenerator::Fetcher.new options
|
|
||||||
|
|
||||||
class Fetcher
|
|
||||||
PER_PAGE_NUMBER = 30
|
|
||||||
CHANGELOG_GITHUB_TOKEN = "CHANGELOG_GITHUB_TOKEN"
|
|
||||||
GH_RATE_LIMIT_EXCEEDED_MSG = "Warning: Can't finish operation: GitHub API rate limit exceeded, change log may be " \
|
|
||||||
"missing some issues. You can limit the number of issues fetched using the `--max-issues NUM` argument."
|
|
||||||
NO_TOKEN_PROVIDED = "Warning: No token provided (-t option) and variable $CHANGELOG_GITHUB_TOKEN was not found. " \
|
|
||||||
"This script can make only 50 requests to GitHub API per hour without token!"
|
|
||||||
|
|
||||||
def initialize(options = {})
|
|
||||||
@options = options || {}
|
|
||||||
@user = @options[:user]
|
|
||||||
@project = @options[:project]
|
|
||||||
@github_token = fetch_github_token
|
|
||||||
@github_options = { per_page: PER_PAGE_NUMBER }
|
|
||||||
@github_options[:oauth_token] = @github_token unless @github_token.nil?
|
|
||||||
@github_options[:endpoint] = @options[:github_endpoint] unless @options[:github_endpoint].nil?
|
|
||||||
@github_options[:site] = @options[:github_endpoint] unless @options[:github_site].nil?
|
|
||||||
|
|
||||||
@github = check_github_response { Github.new @github_options }
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns GitHub token. First try to use variable, provided by --token option,
|
|
||||||
# otherwise try to fetch it from CHANGELOG_GITHUB_TOKEN env variable.
|
|
||||||
#
|
|
||||||
# @return [String]
|
|
||||||
def fetch_github_token
|
|
||||||
env_var = @options[:token] ? @options[:token] : (ENV.fetch CHANGELOG_GITHUB_TOKEN, nil)
|
|
||||||
|
|
||||||
Helper.log.warn NO_TOKEN_PROVIDED.yellow unless env_var
|
|
||||||
|
|
||||||
env_var
|
|
||||||
end
|
|
||||||
|
|
||||||
# Fetch all tags from repo
|
|
||||||
# @return [Array] array of tags
|
|
||||||
def get_all_tags
|
|
||||||
print "Fetching tags...\r" if @options[:verbose]
|
|
||||||
|
|
||||||
check_github_response { github_fetch_tags }
|
|
||||||
end
|
|
||||||
|
|
||||||
# This is wrapper with rescue block
|
|
||||||
# @return [Object] returns exactly the same, what you put in the block, but wrap it with begin-rescue block
|
|
||||||
def check_github_response
|
|
||||||
begin
|
|
||||||
value = yield
|
|
||||||
rescue Github::Error::Unauthorized => e
|
|
||||||
Helper.log.error e.body.red
|
|
||||||
abort "Error: wrong GitHub token"
|
|
||||||
rescue Github::Error::Forbidden => e
|
|
||||||
Helper.log.warn e.body.red
|
|
||||||
Helper.log.warn GH_RATE_LIMIT_EXCEEDED_MSG.yellow
|
|
||||||
end
|
|
||||||
value
|
|
||||||
end
|
|
||||||
|
|
||||||
# Fill input array with tags
|
|
||||||
# @return [Array] array of tags in repo
|
|
||||||
def github_fetch_tags
|
|
||||||
tags = []
|
|
||||||
response = @github.repos.tags @options[:user], @options[:project]
|
|
||||||
page_i = 0
|
|
||||||
count_pages = response.count_pages
|
|
||||||
response.each_page do |page|
|
|
||||||
page_i += PER_PAGE_NUMBER
|
|
||||||
print_in_same_line("Fetching tags... #{page_i}/#{count_pages * PER_PAGE_NUMBER}")
|
|
||||||
tags.concat(page)
|
|
||||||
end
|
|
||||||
print_empty_line
|
|
||||||
|
|
||||||
if tags.count == 0
|
|
||||||
Helper.log.warn "Warning: Can't find any tags in repo.\
|
|
||||||
Make sure, that you push tags to remote repo via 'git push --tags'".yellow
|
|
||||||
else
|
|
||||||
Helper.log.info "Found #{tags.count} tags"
|
|
||||||
end
|
|
||||||
tags
|
|
||||||
end
|
|
||||||
|
|
||||||
# This method fetch all closed issues and separate them to pull requests and pure issues
|
|
||||||
# (pull request is kind of issue in term of GitHub)
|
|
||||||
# @return [Tuple] with (issues, pull-requests)
|
|
||||||
def fetch_closed_issues_and_pr
|
|
||||||
print "Fetching closed issues...\r" if @options[:verbose]
|
|
||||||
issues = []
|
|
||||||
|
|
||||||
begin
|
|
||||||
response = @github.issues.list user: @options[:user],
|
|
||||||
repo: @options[:project],
|
|
||||||
state: "closed",
|
|
||||||
filter: "all",
|
|
||||||
labels: nil
|
|
||||||
page_i = 0
|
|
||||||
count_pages = response.count_pages
|
|
||||||
response.each_page do |page|
|
|
||||||
page_i += PER_PAGE_NUMBER
|
|
||||||
print_in_same_line("Fetching issues... #{page_i}/#{count_pages * PER_PAGE_NUMBER}")
|
|
||||||
issues.concat(page)
|
|
||||||
break if @options[:max_issues] && issues.length >= @options[:max_issues]
|
|
||||||
end
|
|
||||||
print_empty_line
|
|
||||||
Helper.log.info "Received issues: #{issues.count}"
|
|
||||||
|
|
||||||
rescue
|
|
||||||
Helper.log.warn GH_RATE_LIMIT_EXCEEDED_MSG.yellow
|
|
||||||
end
|
|
||||||
|
|
||||||
# separate arrays of issues and pull requests:
|
|
||||||
issues.partition do |x|
|
|
||||||
x[:pull_request].nil?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Fetch all pull requests. We need them to detect :merged_at parameter
|
|
||||||
# @return [Array] all pull requests
|
|
||||||
def fetch_closed_pull_requests
|
|
||||||
pull_requests = []
|
|
||||||
begin
|
|
||||||
response = if @options[:release_branch].nil?
|
|
||||||
@github.pull_requests.list @options[:user],
|
|
||||||
@options[:project],
|
|
||||||
state: "closed"
|
|
||||||
else
|
|
||||||
@github.pull_requests.list @options[:user],
|
|
||||||
@options[:project],
|
|
||||||
state: "closed",
|
|
||||||
base: @options[:release_branch]
|
|
||||||
end
|
|
||||||
page_i = 0
|
|
||||||
count_pages = response.count_pages
|
|
||||||
response.each_page do |page|
|
|
||||||
page_i += PER_PAGE_NUMBER
|
|
||||||
log_string = "Fetching merged dates... #{page_i}/#{count_pages * PER_PAGE_NUMBER}"
|
|
||||||
print_in_same_line(log_string)
|
|
||||||
pull_requests.concat(page)
|
|
||||||
end
|
|
||||||
print_empty_line
|
|
||||||
rescue
|
|
||||||
Helper.log.warn GH_RATE_LIMIT_EXCEEDED_MSG.yellow
|
|
||||||
end
|
|
||||||
|
|
||||||
Helper.log.info "Fetching merged dates: #{pull_requests.count}"
|
|
||||||
pull_requests
|
|
||||||
end
|
|
||||||
|
|
||||||
# Print specified line on the same string
|
|
||||||
# @param [String] log_string
|
|
||||||
def print_in_same_line(log_string)
|
|
||||||
print log_string + "\r"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Print long line with spaces on same line to clear prev message
|
|
||||||
def print_empty_line
|
|
||||||
print_in_same_line(" ")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Fetch event for all issues and add them to :events
|
|
||||||
# @param [Array] issues
|
|
||||||
# @return [Void]
|
|
||||||
def fetch_events_async(issues)
|
|
||||||
i = 0
|
|
||||||
max_thread_number = 50
|
|
||||||
threads = []
|
|
||||||
issues.each_slice(max_thread_number) do |issues_slice|
|
|
||||||
issues_slice.each do |issue|
|
|
||||||
threads << Thread.new do
|
|
||||||
begin
|
|
||||||
response = @github.issues.events.list user: @options[:user],
|
|
||||||
repo: @options[:project],
|
|
||||||
issue_number: issue["number"]
|
|
||||||
issue[:events] = []
|
|
||||||
response.each_page do |page|
|
|
||||||
issue[:events].concat(page)
|
|
||||||
end
|
|
||||||
rescue
|
|
||||||
Helper.log.warn GH_RATE_LIMIT_EXCEEDED_MSG.yellow
|
|
||||||
end
|
|
||||||
print_in_same_line("Fetching events for issues and PR: #{i + 1}/#{issues.count}")
|
|
||||||
i += 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
threads.each(&:join)
|
|
||||||
threads = []
|
|
||||||
end
|
|
||||||
|
|
||||||
# to clear line from prev print
|
|
||||||
print_empty_line
|
|
||||||
|
|
||||||
Helper.log.info "Fetching events for issues and PR: #{i}"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Fetch tag time from repo
|
|
||||||
#
|
|
||||||
# @param [Hash] tag
|
|
||||||
# @return [Time] time of specified tag
|
|
||||||
def fetch_date_of_tag(tag)
|
|
||||||
begin
|
|
||||||
commit_data = @github.git_data.commits.get @options[:user],
|
|
||||||
@options[:project],
|
|
||||||
tag["commit"]["sha"]
|
|
||||||
rescue
|
|
||||||
Helper.log.warn GH_RATE_LIMIT_EXCEEDED_MSG.yellow
|
|
||||||
end
|
|
||||||
time_string = commit_data["committer"]["date"]
|
|
||||||
Time.parse(time_string)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Fetch commit for specified event
|
|
||||||
# @return [Hash]
|
|
||||||
def fetch_commit(event)
|
|
||||||
@github.git_data.commits.get @options[:user], @options[:project], event[:commit_id]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
require_relative "../fetcher"
|
# frozen_string_literal: true
|
||||||
|
require_relative "../octo_fetcher"
|
||||||
require_relative "generator_generation"
|
require_relative "generator_generation"
|
||||||
require_relative "generator_fetcher"
|
require_relative "generator_fetcher"
|
||||||
require_relative "generator_processor"
|
require_relative "generator_processor"
|
||||||
@@ -10,25 +11,25 @@ module GitHubChangelogGenerator
|
|||||||
end
|
end
|
||||||
|
|
||||||
class Generator
|
class Generator
|
||||||
attr_accessor :options, :filtered_tags, :github
|
attr_accessor :options, :filtered_tags, :github, :tag_section_mapping
|
||||||
|
|
||||||
# A Generator responsible for all logic, related with change log generation from ready-to-parse issues
|
# A Generator responsible for all logic, related with change log generation from ready-to-parse issues
|
||||||
#
|
#
|
||||||
# Example:
|
# Example:
|
||||||
# generator = GitHubChangelogGenerator::Generator.new
|
# generator = GitHubChangelogGenerator::Generator.new
|
||||||
# content = generator.compound_changelog
|
# content = generator.compound_changelog
|
||||||
def initialize(options = nil)
|
def initialize(options = {})
|
||||||
@options = options || {}
|
@options = options
|
||||||
@tag_times_hash = {}
|
@tag_times_hash = {}
|
||||||
@fetcher = GitHubChangelogGenerator::Fetcher.new @options
|
@fetcher = GitHubChangelogGenerator::OctoFetcher.new(options)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_issues_and_pr
|
def fetch_issues_and_pr
|
||||||
issues, pull_requests = @fetcher.fetch_closed_issues_and_pr
|
issues, pull_requests = @fetcher.fetch_closed_issues_and_pr
|
||||||
|
|
||||||
@pull_requests = @options[:pulls] ? get_filtered_pull_requests(pull_requests) : []
|
@pull_requests = options[:pulls] ? get_filtered_pull_requests(pull_requests) : []
|
||||||
|
|
||||||
@issues = @options[:issues] ? get_filtered_issues(issues) : []
|
@issues = options[:issues] ? get_filtered_issues(issues) : []
|
||||||
|
|
||||||
fetch_events_for_issues_and_pr
|
fetch_events_for_issues_and_pr
|
||||||
detect_actual_closed_dates(@issues + @pull_requests)
|
detect_actual_closed_dates(@issues + @pull_requests)
|
||||||
@@ -60,18 +61,18 @@ module GitHubChangelogGenerator
|
|||||||
newer_tag_link, newer_tag_name, newer_tag_time = detect_link_tag_time(newer_tag)
|
newer_tag_link, newer_tag_name, newer_tag_time = detect_link_tag_time(newer_tag)
|
||||||
|
|
||||||
github_site = options[:github_site] || "https://github.com"
|
github_site = options[:github_site] || "https://github.com"
|
||||||
project_url = "#{github_site}/#{@options[:user]}/#{@options[:project]}"
|
project_url = "#{github_site}/#{options[:user]}/#{options[:project]}"
|
||||||
|
|
||||||
log = generate_header(newer_tag_name, newer_tag_link, newer_tag_time, older_tag_name, project_url)
|
log = generate_header(newer_tag_name, newer_tag_link, newer_tag_time, older_tag_name, project_url)
|
||||||
|
|
||||||
if @options[:issues]
|
if options[:issues]
|
||||||
# Generate issues:
|
# Generate issues:
|
||||||
log += issues_to_log(issues, pull_requests)
|
log += issues_to_log(issues, pull_requests)
|
||||||
end
|
end
|
||||||
|
|
||||||
if @options[:pulls]
|
if options[:pulls]
|
||||||
# Generate pull requests:
|
# Generate pull requests:
|
||||||
log += generate_sub_section(pull_requests, @options[:merge_prefix])
|
log += generate_sub_section(pull_requests, options[:merge_prefix])
|
||||||
end
|
end
|
||||||
|
|
||||||
log
|
log
|
||||||
@@ -86,9 +87,9 @@ module GitHubChangelogGenerator
|
|||||||
log = ""
|
log = ""
|
||||||
bugs_a, enhancement_a, issues_a = parse_by_sections(issues, pull_requests)
|
bugs_a, enhancement_a, issues_a = parse_by_sections(issues, pull_requests)
|
||||||
|
|
||||||
log += generate_sub_section(enhancement_a, @options[:enhancement_prefix])
|
log += generate_sub_section(enhancement_a, options[:enhancement_prefix])
|
||||||
log += generate_sub_section(bugs_a, @options[:bug_prefix])
|
log += generate_sub_section(bugs_a, options[:bug_prefix])
|
||||||
log += generate_sub_section(issues_a, @options[:issue_prefix])
|
log += generate_sub_section(issues_a, options[:issue_prefix])
|
||||||
log
|
log
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -105,32 +106,32 @@ module GitHubChangelogGenerator
|
|||||||
|
|
||||||
issues.each do |dict|
|
issues.each do |dict|
|
||||||
added = false
|
added = false
|
||||||
dict.labels.each do |label|
|
dict["labels"].each do |label|
|
||||||
if @options[:bug_labels].include? label.name
|
if options[:bug_labels].include?(label["name"])
|
||||||
bugs_a.push dict
|
bugs_a.push(dict)
|
||||||
added = true
|
added = true
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
if @options[:enhancement_labels].include? label.name
|
if options[:enhancement_labels].include?(label["name"])
|
||||||
enhancement_a.push dict
|
enhancement_a.push(dict)
|
||||||
added = true
|
added = true
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
issues_a.push dict unless added
|
issues_a.push(dict) unless added
|
||||||
end
|
end
|
||||||
|
|
||||||
added_pull_requests = []
|
added_pull_requests = []
|
||||||
pull_requests.each do |dict|
|
pull_requests.each do |pr|
|
||||||
dict.labels.each do |label|
|
pr["labels"].each do |label|
|
||||||
if @options[:bug_labels].include? label.name
|
if options[:bug_labels].include?(label["name"])
|
||||||
bugs_a.push dict
|
bugs_a.push(pr)
|
||||||
added_pull_requests.push dict
|
added_pull_requests.push(pr)
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
if @options[:enhancement_labels].include? label.name
|
if options[:enhancement_labels].include?(label["name"])
|
||||||
enhancement_a.push dict
|
enhancement_a.push(pr)
|
||||||
added_pull_requests.push dict
|
added_pull_requests.push(pr)
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
module GitHubChangelogGenerator
|
module GitHubChangelogGenerator
|
||||||
class Generator
|
class Generator
|
||||||
|
MAX_THREAD_NUMBER = 25
|
||||||
|
|
||||||
# Fetch event for issues and pull requests
|
# Fetch event for issues and pull requests
|
||||||
# @return [Array] array of fetched issues
|
# @return [Array] array of fetched issues
|
||||||
def fetch_events_for_issues_and_pr
|
def fetch_events_for_issues_and_pr
|
||||||
if @options[:verbose]
|
if options[:verbose]
|
||||||
print "Fetching events for issues and PR: 0/#{@issues.count + @pull_requests.count}\r"
|
print "Fetching events for issues and PR: 0/#{@issues.count + @pull_requests.count}\r"
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -12,48 +15,47 @@ module GitHubChangelogGenerator
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Async fetching of all tags dates
|
# Async fetching of all tags dates
|
||||||
def fetch_tags_dates
|
def fetch_tags_dates(tags)
|
||||||
print "Fetching tag dates...\r" if @options[:verbose]
|
print "Fetching tag dates...\r" if options[:verbose]
|
||||||
# Async fetching tags:
|
# Async fetching tags:
|
||||||
threads = []
|
threads = []
|
||||||
i = 0
|
i = 0
|
||||||
all = @filtered_tags.count
|
all = tags.count
|
||||||
@filtered_tags.each do |tag|
|
tags.each do |tag|
|
||||||
print " \r"
|
print " \r"
|
||||||
threads << Thread.new do
|
threads << Thread.new do
|
||||||
get_time_of_tag(tag)
|
get_time_of_tag(tag)
|
||||||
print "Fetching tags dates: #{i + 1}/#{all}\r" if @options[:verbose]
|
print "Fetching tags dates: #{i + 1}/#{all}\r" if options[:verbose]
|
||||||
i += 1
|
i += 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
threads.each(&:join)
|
threads.each(&:join)
|
||||||
puts "Fetching tags dates: #{i}" if @options[:verbose]
|
puts "Fetching tags dates: #{i}" if options[:verbose]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Find correct closed dates, if issues was closed by commits
|
# Find correct closed dates, if issues was closed by commits
|
||||||
def detect_actual_closed_dates(issues)
|
def detect_actual_closed_dates(issues)
|
||||||
print "Fetching closed dates for issues...\r" if @options[:verbose]
|
print "Fetching closed dates for issues...\r" if options[:verbose]
|
||||||
|
|
||||||
max_thread_number = 50
|
issues.each_slice(MAX_THREAD_NUMBER) do |issues_slice|
|
||||||
issues.each_slice(max_thread_number) do |issues_slice|
|
|
||||||
threads = []
|
threads = []
|
||||||
issues_slice.each do |issue|
|
issues_slice.each do |issue|
|
||||||
threads << Thread.new { find_closed_date_by_commit(issue) }
|
threads << Thread.new { find_closed_date_by_commit(issue) }
|
||||||
end
|
end
|
||||||
threads.each(&:join)
|
threads.each(&:join)
|
||||||
end
|
end
|
||||||
puts "Fetching closed dates for issues: Done!" if @options[:verbose]
|
puts "Fetching closed dates for issues: Done!" if options[:verbose]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Fill :actual_date parameter of specified issue by closed date of the commit, if it was closed by commit.
|
# Fill :actual_date parameter of specified issue by closed date of the commit, if it was closed by commit.
|
||||||
# @param [Hash] issue
|
# @param [Hash] issue
|
||||||
def find_closed_date_by_commit(issue)
|
def find_closed_date_by_commit(issue)
|
||||||
unless issue[:events].nil?
|
unless issue["events"].nil?
|
||||||
# if it's PR -> then find "merged event", in case of usual issue -> fond closed date
|
# if it's PR -> then find "merged event", in case of usual issue -> fond closed date
|
||||||
compare_string = issue[:merged_at].nil? ? "closed" : "merged"
|
compare_string = issue["merged_at"].nil? ? "closed" : "merged"
|
||||||
# reverse! - to find latest closed event. (event goes in date order)
|
# reverse! - to find latest closed event. (event goes in date order)
|
||||||
issue[:events].reverse!.each do |event|
|
issue["events"].reverse!.each do |event|
|
||||||
if event[:event].eql? compare_string
|
if event["event"].eql? compare_string
|
||||||
set_date_from_event(event, issue)
|
set_date_from_event(event, issue)
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
@@ -67,15 +69,17 @@ module GitHubChangelogGenerator
|
|||||||
# @param [Hash] event
|
# @param [Hash] event
|
||||||
# @param [Hash] issue
|
# @param [Hash] issue
|
||||||
def set_date_from_event(event, issue)
|
def set_date_from_event(event, issue)
|
||||||
if event[:commit_id].nil?
|
if event["commit_id"].nil?
|
||||||
issue[:actual_date] = issue[:closed_at]
|
issue["actual_date"] = issue["closed_at"]
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
commit = @fetcher.fetch_commit(event)
|
commit = @fetcher.fetch_commit(event)
|
||||||
issue[:actual_date] = commit[:author][:date]
|
issue["actual_date"] = commit["commit"]["author"]["date"]
|
||||||
|
|
||||||
|
# issue['actual_date'] = commit['author']['date']
|
||||||
rescue
|
rescue
|
||||||
puts "Warning: Can't fetch commit #{event[:commit_id]}. It is probably referenced from another repo.".yellow
|
puts "Warning: Can't fetch commit #{event['commit_id']}. It is probably referenced from another repo."
|
||||||
issue[:actual_date] = issue[:closed_at]
|
issue["actual_date"] = issue["closed_at"]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
module GitHubChangelogGenerator
|
module GitHubChangelogGenerator
|
||||||
class Generator
|
class Generator
|
||||||
# Main function to start change log generation
|
# Main function to start change log generation
|
||||||
@@ -5,48 +6,24 @@ module GitHubChangelogGenerator
|
|||||||
# @return [String] Generated change log file
|
# @return [String] Generated change log file
|
||||||
def compound_changelog
|
def compound_changelog
|
||||||
fetch_and_filter_tags
|
fetch_and_filter_tags
|
||||||
sort_tags_by_date(@filtered_tags)
|
|
||||||
fetch_issues_and_pr
|
fetch_issues_and_pr
|
||||||
|
|
||||||
log = ""
|
log = ""
|
||||||
log += @options[:frontmatter] if @options[:frontmatter]
|
log += options[:frontmatter] if options[:frontmatter]
|
||||||
log += "#{@options[:header]}\n\n"
|
log += "#{options[:header]}\n\n"
|
||||||
|
|
||||||
log += if @options[:unreleased_only]
|
log += if options[:unreleased_only]
|
||||||
generate_log_between_tags(filtered_tags[0], nil)
|
generate_log_between_tags(filtered_tags[0], nil)
|
||||||
else
|
else
|
||||||
generate_log_for_all_tags
|
generate_log_for_all_tags
|
||||||
end
|
end
|
||||||
|
|
||||||
log += File.read(@options[:base]) if File.file?(@options[:base])
|
log += File.read(options[:base]) if File.file?(options[:base])
|
||||||
|
|
||||||
log += "\n\n\\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*"
|
log += "\n\n\\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*"
|
||||||
@log = log
|
@log = log
|
||||||
end
|
end
|
||||||
|
|
||||||
# @return [String] temp method should be removed soon
|
|
||||||
def generate_for_2_tags(log)
|
|
||||||
tag1 = @options[:tag1]
|
|
||||||
tag2 = @options[:tag2]
|
|
||||||
tags_strings = []
|
|
||||||
filtered_tags.each { |x| tags_strings.push(x["name"]) }
|
|
||||||
|
|
||||||
if tags_strings.include?(tag1)
|
|
||||||
if tags_strings.include?(tag2)
|
|
||||||
to_a = tags_strings.map.with_index.to_a
|
|
||||||
hash = Hash[to_a]
|
|
||||||
index1 = hash[tag1]
|
|
||||||
index2 = hash[tag2]
|
|
||||||
log += generate_log_between_tags(all_tags[index1], all_tags[index2])
|
|
||||||
else
|
|
||||||
raise ChangelogGeneratorError, "Can't find tag #{tag2} -> exit".red
|
|
||||||
end
|
|
||||||
else
|
|
||||||
raise ChangelogGeneratorError, "Can't find tag #{tag1} -> exit".red
|
|
||||||
end
|
|
||||||
log
|
|
||||||
end
|
|
||||||
|
|
||||||
# @param [Array] issues List of issues on sub-section
|
# @param [Array] issues List of issues on sub-section
|
||||||
# @param [String] prefix Nae of sub-section
|
# @param [String] prefix Nae of sub-section
|
||||||
# @return [String] Generate ready-to-go sub-section
|
# @return [String] Generate ready-to-go sub-section
|
||||||
@@ -76,21 +53,21 @@ module GitHubChangelogGenerator
|
|||||||
log = ""
|
log = ""
|
||||||
|
|
||||||
# Generate date string:
|
# Generate date string:
|
||||||
time_string = newer_tag_time.strftime @options[:date_format]
|
time_string = newer_tag_time.strftime(options[:date_format])
|
||||||
|
|
||||||
# Generate tag name and link
|
# Generate tag name and link
|
||||||
release_url = if @options[:release_url]
|
release_url = if options[:release_url]
|
||||||
format(@options[:release_url], newer_tag_link)
|
format(options[:release_url], newer_tag_link)
|
||||||
else
|
else
|
||||||
"#{project_url}/tree/#{newer_tag_link}"
|
"#{project_url}/tree/#{newer_tag_link}"
|
||||||
end
|
end
|
||||||
log += if newer_tag_name.equal? @options[:unreleased_label]
|
log += if newer_tag_name.equal?(options[:unreleased_label])
|
||||||
"## [#{newer_tag_name}](#{release_url})\n\n"
|
"## [#{newer_tag_name}](#{release_url})\n\n"
|
||||||
else
|
else
|
||||||
"## [#{newer_tag_name}](#{release_url}) (#{time_string})\n"
|
"## [#{newer_tag_name}](#{release_url}) (#{time_string})\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
if @options[:compare_link] && older_tag_link
|
if options[:compare_link] && older_tag_link
|
||||||
# Generate compare link
|
# Generate compare link
|
||||||
log += "[Full Changelog](#{project_url}/compare/#{older_tag_link}...#{newer_tag_link})\n\n"
|
log += "[Full Changelog](#{project_url}/compare/#{older_tag_link}...#{newer_tag_link})\n\n"
|
||||||
end
|
end
|
||||||
@@ -118,12 +95,12 @@ module GitHubChangelogGenerator
|
|||||||
#
|
#
|
||||||
# @return [Array] filtered issues and pull requests
|
# @return [Array] filtered issues and pull requests
|
||||||
def filter_issues_for_tags(newer_tag, older_tag)
|
def filter_issues_for_tags(newer_tag, older_tag)
|
||||||
filtered_pull_requests = delete_by_time(@pull_requests, :actual_date, older_tag, newer_tag)
|
filtered_pull_requests = delete_by_time(@pull_requests, "actual_date", older_tag, newer_tag)
|
||||||
filtered_issues = delete_by_time(@issues, :actual_date, older_tag, newer_tag)
|
filtered_issues = delete_by_time(@issues, "actual_date", older_tag, newer_tag)
|
||||||
|
|
||||||
newer_tag_name = newer_tag.nil? ? nil : newer_tag["name"]
|
newer_tag_name = newer_tag.nil? ? nil : newer_tag["name"]
|
||||||
|
|
||||||
if @options[:filter_issues_by_milestone]
|
if options[:filter_issues_by_milestone]
|
||||||
# delete excess irrelevant issues (according milestones). Issue #22.
|
# delete excess irrelevant issues (according milestones). Issue #22.
|
||||||
filtered_issues = filter_by_milestone(filtered_issues, newer_tag_name, @issues)
|
filtered_issues = filter_by_milestone(filtered_issues, newer_tag_name, @issues)
|
||||||
filtered_pull_requests = filter_by_milestone(filtered_pull_requests, newer_tag_name, @pull_requests)
|
filtered_pull_requests = filter_by_milestone(filtered_pull_requests, newer_tag_name, @pull_requests)
|
||||||
@@ -134,15 +111,13 @@ module GitHubChangelogGenerator
|
|||||||
# The full cycle of generation for whole project
|
# The full cycle of generation for whole project
|
||||||
# @return [String] The complete change log
|
# @return [String] The complete change log
|
||||||
def generate_log_for_all_tags
|
def generate_log_for_all_tags
|
||||||
puts "Generating log..." if @options[:verbose]
|
puts "Generating log..." if options[:verbose]
|
||||||
|
|
||||||
log = generate_unreleased_section
|
log = generate_unreleased_section
|
||||||
|
|
||||||
(1...filtered_tags.size).each do |index|
|
@tag_section_mapping.each_pair do |_tag_section, left_right_tags|
|
||||||
log += generate_log_between_tags(filtered_tags[index], filtered_tags[index - 1])
|
older_tag, newer_tag = left_right_tags
|
||||||
end
|
log += generate_log_between_tags(older_tag, newer_tag)
|
||||||
if @filtered_tags.count != 0
|
|
||||||
log += generate_log_between_tags(nil, filtered_tags.last)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
log
|
log
|
||||||
@@ -150,7 +125,7 @@ module GitHubChangelogGenerator
|
|||||||
|
|
||||||
def generate_unreleased_section
|
def generate_unreleased_section
|
||||||
log = ""
|
log = ""
|
||||||
if @options[:unreleased]
|
if options[:unreleased]
|
||||||
unreleased_log = generate_log_between_tags(filtered_tags[0], nil)
|
unreleased_log = generate_log_between_tags(filtered_tags[0], nil)
|
||||||
log += unreleased_log if unreleased_log
|
log += unreleased_log if unreleased_log
|
||||||
end
|
end
|
||||||
@@ -160,25 +135,30 @@ module GitHubChangelogGenerator
|
|||||||
# Parse issue and generate single line formatted issue line.
|
# Parse issue and generate single line formatted issue line.
|
||||||
#
|
#
|
||||||
# Example output:
|
# Example output:
|
||||||
# - Add coveralls integration [\#223](https://github.com/skywinder/github-changelog-generator/pull/223) ([skywinder](https://github.com/skywinder))
|
# - Add coveralls integration [\#223](https://github.com/skywinder/github-changelog-generator/pull/223) (@skywinder)
|
||||||
#
|
#
|
||||||
# @param [Hash] issue Fetched issue from GitHub
|
# @param [Hash] issue Fetched issue from GitHub
|
||||||
# @return [String] Markdown-formatted single issue
|
# @return [String] Markdown-formatted single issue
|
||||||
def get_string_for_issue(issue)
|
def get_string_for_issue(issue)
|
||||||
encapsulated_title = encapsulate_string issue[:title]
|
encapsulated_title = encapsulate_string issue["title"]
|
||||||
|
|
||||||
title_with_number = "#{encapsulated_title} [\\##{issue[:number]}](#{issue.html_url})"
|
title_with_number = "#{encapsulated_title} [\\##{issue['number']}](#{issue['html_url']})"
|
||||||
|
issue_line_with_user(title_with_number, issue)
|
||||||
|
end
|
||||||
|
|
||||||
unless issue.pull_request.nil?
|
private
|
||||||
if @options[:author]
|
|
||||||
title_with_number += if issue.user.nil?
|
def issue_line_with_user(line, issue)
|
||||||
" ({Null user})"
|
return line if !options[:author] || issue["pull_request"].nil?
|
||||||
else
|
|
||||||
" ([#{issue.user.login}](#{issue.user.html_url}))"
|
user = issue["user"]
|
||||||
end
|
return "#{line} ({Null user})" unless user
|
||||||
end
|
|
||||||
|
if options[:usernames_as_github_logins]
|
||||||
|
"#{line} (@#{user['login']})"
|
||||||
|
else
|
||||||
|
"#{line} ([#{user['login']}](#{user['html_url']}))"
|
||||||
end
|
end
|
||||||
title_with_number
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
module GitHubChangelogGenerator
|
module GitHubChangelogGenerator
|
||||||
class Generator
|
class Generator
|
||||||
# delete all labels with labels from @options[:exclude_labels] array
|
# delete all labels with labels from options[:exclude_labels] array
|
||||||
# @param [Array] issues
|
# @param [Array] issues
|
||||||
# @return [Array] filtered array
|
# @return [Array] filtered array
|
||||||
def exclude_issues_by_labels(issues)
|
def exclude_issues_by_labels(issues)
|
||||||
return issues if !@options[:exclude_labels] || @options[:exclude_labels].empty?
|
return issues if !options[:exclude_labels] || options[:exclude_labels].empty?
|
||||||
|
|
||||||
issues.reject do |issue|
|
issues.reject do |issue|
|
||||||
labels = issue.labels.map(&:name)
|
labels = issue["labels"].map { |l| l["name"] }
|
||||||
(labels & @options[:exclude_labels]).any?
|
(labels & options[:exclude_labels]).any?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -31,18 +32,18 @@ module GitHubChangelogGenerator
|
|||||||
# @return [Array] issues with milestone #tag_name
|
# @return [Array] issues with milestone #tag_name
|
||||||
def find_issues_to_add(all_issues, tag_name)
|
def find_issues_to_add(all_issues, tag_name)
|
||||||
all_issues.select do |issue|
|
all_issues.select do |issue|
|
||||||
if issue.milestone.nil?
|
if issue["milestone"].nil?
|
||||||
false
|
false
|
||||||
else
|
else
|
||||||
# check, that this milestone in tag list:
|
# check, that this milestone in tag list:
|
||||||
milestone_is_tag = @filtered_tags.find do |tag|
|
milestone_is_tag = @filtered_tags.find do |tag|
|
||||||
tag.name == issue.milestone.title
|
tag["name"] == issue["milestone"]["title"]
|
||||||
end
|
end
|
||||||
|
|
||||||
if milestone_is_tag.nil?
|
if milestone_is_tag.nil?
|
||||||
false
|
false
|
||||||
else
|
else
|
||||||
issue.milestone.title == tag_name
|
issue["milestone"]["title"] == tag_name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -52,11 +53,11 @@ module GitHubChangelogGenerator
|
|||||||
def remove_issues_in_milestones(filtered_issues)
|
def remove_issues_in_milestones(filtered_issues)
|
||||||
filtered_issues.select! do |issue|
|
filtered_issues.select! do |issue|
|
||||||
# leave issues without milestones
|
# leave issues without milestones
|
||||||
if issue.milestone.nil?
|
if issue["milestone"].nil?
|
||||||
true
|
true
|
||||||
else
|
else
|
||||||
# check, that this milestone in tag list:
|
# check, that this milestone in tag list:
|
||||||
@filtered_tags.find { |tag| tag.name == issue.milestone.title }.nil?
|
@filtered_tags.find { |tag| tag["name"] == issue["milestone"]["title"] }.nil?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -67,7 +68,7 @@ module GitHubChangelogGenerator
|
|||||||
# @param [String] older_tag all issues before this tag date will be excluded. May be nil, if it's first tag
|
# @param [String] older_tag all issues before this tag date will be excluded. May be nil, if it's first tag
|
||||||
# @param [String] newer_tag all issue after this tag will be excluded. May be nil for unreleased section
|
# @param [String] newer_tag all issue after this tag will be excluded. May be nil for unreleased section
|
||||||
# @return [Array] filtered issues
|
# @return [Array] filtered issues
|
||||||
def delete_by_time(issues, hash_key = :actual_date, older_tag = nil, newer_tag = nil)
|
def delete_by_time(issues, hash_key = "actual_date", older_tag = nil, newer_tag = nil)
|
||||||
# in case if not tags specified - return unchanged array
|
# in case if not tags specified - return unchanged array
|
||||||
return issues if older_tag.nil? && newer_tag.nil?
|
return issues if older_tag.nil? && newer_tag.nil?
|
||||||
|
|
||||||
@@ -76,7 +77,7 @@ module GitHubChangelogGenerator
|
|||||||
|
|
||||||
issues.select do |issue|
|
issues.select do |issue|
|
||||||
if issue[hash_key]
|
if issue[hash_key]
|
||||||
time = Time.parse(issue[hash_key]).utc
|
time = Time.parse(issue[hash_key].to_s).utc
|
||||||
|
|
||||||
tag_in_range_old = tag_newer_old_tag?(older_tag_time, time)
|
tag_in_range_old = tag_newer_old_tag?(older_tag_time, time)
|
||||||
|
|
||||||
@@ -120,9 +121,9 @@ module GitHubChangelogGenerator
|
|||||||
|
|
||||||
# @return [Array] issues without labels or empty array if add_issues_wo_labels is false
|
# @return [Array] issues without labels or empty array if add_issues_wo_labels is false
|
||||||
def filter_wo_labels(issues)
|
def filter_wo_labels(issues)
|
||||||
if @options[:add_issues_wo_labels]
|
if options[:add_issues_wo_labels]
|
||||||
issues_wo_labels = issues.select do |issue|
|
issues_wo_labels = issues.select do |issue|
|
||||||
!issue.labels.map(&:name).any?
|
!issue["labels"].map { |l| l["name"] }.any?
|
||||||
end
|
end
|
||||||
return issues_wo_labels
|
return issues_wo_labels
|
||||||
end
|
end
|
||||||
@@ -130,11 +131,14 @@ module GitHubChangelogGenerator
|
|||||||
end
|
end
|
||||||
|
|
||||||
def filter_by_include_labels(issues)
|
def filter_by_include_labels(issues)
|
||||||
filtered_issues = @options[:include_labels].nil? ? issues : issues.select do |issue|
|
if options[:include_labels].nil?
|
||||||
labels = issue.labels.map(&:name) & @options[:include_labels]
|
issues
|
||||||
labels.any?
|
else
|
||||||
|
issues.select do |issue|
|
||||||
|
labels = issue["labels"].map { |l| l["name"] } & options[:include_labels]
|
||||||
|
labels.any?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
filtered_issues
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# General filtered function
|
# General filtered function
|
||||||
@@ -150,18 +154,18 @@ module GitHubChangelogGenerator
|
|||||||
# @return [Array] Filtered issues
|
# @return [Array] Filtered issues
|
||||||
def get_filtered_issues(issues)
|
def get_filtered_issues(issues)
|
||||||
issues = filter_array_by_labels(issues)
|
issues = filter_array_by_labels(issues)
|
||||||
puts "Filtered issues: #{issues.count}" if @options[:verbose]
|
puts "Filtered issues: #{issues.count}" if options[:verbose]
|
||||||
issues
|
issues
|
||||||
end
|
end
|
||||||
|
|
||||||
# This method fetches missing params for PR and filter them by specified options
|
# This method fetches missing params for PR and filter them by specified options
|
||||||
# It include add all PR's with labels from @options[:include_labels] array
|
# It include add all PR's with labels from options[:include_labels] array
|
||||||
# And exclude all from :exclude_labels array.
|
# And exclude all from :exclude_labels array.
|
||||||
# @return [Array] filtered PR's
|
# @return [Array] filtered PR's
|
||||||
def get_filtered_pull_requests(pull_requests)
|
def get_filtered_pull_requests(pull_requests)
|
||||||
pull_requests = filter_array_by_labels(pull_requests)
|
pull_requests = filter_array_by_labels(pull_requests)
|
||||||
pull_requests = filter_merged_pull_requests(pull_requests)
|
pull_requests = filter_merged_pull_requests(pull_requests)
|
||||||
puts "Filtered pull requests: #{pull_requests.count}" if @options[:verbose]
|
puts "Filtered pull requests: #{pull_requests.count}" if options[:verbose]
|
||||||
pull_requests
|
pull_requests
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -170,21 +174,21 @@ module GitHubChangelogGenerator
|
|||||||
# :merged_at - is a date, when issue PR was merged.
|
# :merged_at - is a date, when issue PR was merged.
|
||||||
# More correct to use merged date, rather than closed date.
|
# More correct to use merged date, rather than closed date.
|
||||||
def filter_merged_pull_requests(pull_requests)
|
def filter_merged_pull_requests(pull_requests)
|
||||||
print "Fetching merged dates...\r" if @options[:verbose]
|
print "Fetching merged dates...\r" if options[:verbose]
|
||||||
closed_pull_requests = @fetcher.fetch_closed_pull_requests
|
closed_pull_requests = @fetcher.fetch_closed_pull_requests
|
||||||
|
|
||||||
pull_requests.each do |pr|
|
pull_requests.each do |pr|
|
||||||
fetched_pr = closed_pull_requests.find do |fpr|
|
fetched_pr = closed_pull_requests.find do |fpr|
|
||||||
fpr.number == pr.number
|
fpr["number"] == pr["number"]
|
||||||
end
|
end
|
||||||
if fetched_pr
|
if fetched_pr
|
||||||
pr[:merged_at] = fetched_pr[:merged_at]
|
pr["merged_at"] = fetched_pr["merged_at"]
|
||||||
closed_pull_requests.delete(fetched_pr)
|
closed_pull_requests.delete(fetched_pr)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
pull_requests.select! do |pr|
|
pull_requests.select! do |pr|
|
||||||
!pr[:merged_at].nil?
|
!pr["merged_at"].nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
pull_requests
|
pull_requests
|
||||||
|
|||||||
@@ -1,34 +1,61 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
module GitHubChangelogGenerator
|
module GitHubChangelogGenerator
|
||||||
class Generator
|
class Generator
|
||||||
# fetch, filter tags, fetch dates and sort them in time order
|
# fetch, filter tags, fetch dates and sort them in time order
|
||||||
def fetch_and_filter_tags
|
def fetch_and_filter_tags
|
||||||
@filtered_tags = get_filtered_tags(@fetcher.get_all_tags)
|
detect_since_tag
|
||||||
fetch_tags_dates
|
detect_due_tag
|
||||||
|
|
||||||
|
all_tags = @fetcher.get_all_tags
|
||||||
|
included_tags = filter_excluded_tags(all_tags)
|
||||||
|
|
||||||
|
fetch_tags_dates(all_tags) # Creates a Hash @tag_times_hash
|
||||||
|
sorted_tags = sort_tags_by_date(included_tags)
|
||||||
|
@filtered_tags = get_filtered_tags(included_tags)
|
||||||
|
|
||||||
|
@tag_section_mapping = build_tag_section_mapping(@filtered_tags, sorted_tags)
|
||||||
|
|
||||||
|
@filtered_tags
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sort all tags by date
|
# @param [Array] filtered_tags are the tags that need a subsection output
|
||||||
|
# @param [Array] all_tags is the list of all tags ordered from newest -> oldest
|
||||||
|
# @return [Hash] key is the tag to output, value is an array of [Left Tag, Right Tag]
|
||||||
|
# PRs to include in this section will be >= [Left Tag Date] and <= [Right Tag Date]
|
||||||
|
def build_tag_section_mapping(filtered_tags, all_tags)
|
||||||
|
tag_mapping = {}
|
||||||
|
filtered_tags.each do |tag|
|
||||||
|
older_tag_idx = all_tags.index(tag) + 1
|
||||||
|
older_tag = all_tags[older_tag_idx]
|
||||||
|
tag_mapping[tag] = [older_tag, tag]
|
||||||
|
end
|
||||||
|
tag_mapping
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sort all tags by date, newest to oldest
|
||||||
def sort_tags_by_date(tags)
|
def sort_tags_by_date(tags)
|
||||||
puts "Sorting tags..." if @options[:verbose]
|
puts "Sorting tags..." if options[:verbose]
|
||||||
tags.sort_by! do |x|
|
tags.sort_by! do |x|
|
||||||
get_time_of_tag(x)
|
get_time_of_tag(x)
|
||||||
end.reverse!
|
end.reverse!
|
||||||
end
|
end
|
||||||
|
|
||||||
# Try to find tag date in local hash.
|
# Returns date for given GitHub Tag hash
|
||||||
# Otherwise fFetch tag time and put it to local hash file.
|
#
|
||||||
# @param [Hash] tag_name name of the tag
|
# Memoize the date by tag name.
|
||||||
|
#
|
||||||
|
# @param [Hash] tag_name
|
||||||
|
#
|
||||||
# @return [Time] time of specified tag
|
# @return [Time] time of specified tag
|
||||||
def get_time_of_tag(tag_name)
|
def get_time_of_tag(tag_name)
|
||||||
raise ChangelogGeneratorError, "tag_name is nil".red if tag_name.nil?
|
raise ChangelogGeneratorError, "tag_name is nil" if tag_name.nil?
|
||||||
|
|
||||||
name_of_tag = tag_name["name"]
|
name_of_tag = tag_name.fetch("name")
|
||||||
time_for_name = @tag_times_hash[name_of_tag]
|
time_for_tag_name = @tag_times_hash[name_of_tag]
|
||||||
if !time_for_name.nil?
|
return time_for_tag_name if time_for_tag_name
|
||||||
time_for_name
|
|
||||||
else
|
@fetcher.fetch_date_of_tag(tag_name).tap do |time_string|
|
||||||
time_string = @fetcher.fetch_date_of_tag tag_name
|
|
||||||
@tag_times_hash[name_of_tag] = time_string
|
@tag_times_hash[name_of_tag] = time_string
|
||||||
time_string
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -41,12 +68,12 @@ module GitHubChangelogGenerator
|
|||||||
newer_tag_time = newer_tag.nil? ? Time.new : get_time_of_tag(newer_tag)
|
newer_tag_time = newer_tag.nil? ? Time.new : get_time_of_tag(newer_tag)
|
||||||
|
|
||||||
# if it's future release tag - set this value
|
# if it's future release tag - set this value
|
||||||
if newer_tag.nil? && @options[:future_release]
|
if newer_tag.nil? && options[:future_release]
|
||||||
newer_tag_name = @options[:future_release]
|
newer_tag_name = options[:future_release]
|
||||||
newer_tag_link = @options[:future_release]
|
newer_tag_link = options[:future_release]
|
||||||
else
|
else
|
||||||
# put unreleased label if there is no name for the tag
|
# put unreleased label if there is no name for the tag
|
||||||
newer_tag_name = newer_tag.nil? ? @options[:unreleased_label] : newer_tag["name"]
|
newer_tag_name = newer_tag.nil? ? options[:unreleased_label] : newer_tag["name"]
|
||||||
newer_tag_link = newer_tag.nil? ? "HEAD" : newer_tag_name
|
newer_tag_link = newer_tag.nil? ? "HEAD" : newer_tag_name
|
||||||
end
|
end
|
||||||
[newer_tag_link, newer_tag_name, newer_tag_time]
|
[newer_tag_link, newer_tag_name, newer_tag_time]
|
||||||
@@ -54,13 +81,17 @@ module GitHubChangelogGenerator
|
|||||||
|
|
||||||
# @return [Object] try to find newest tag using #Reader and :base option if specified otherwise returns nil
|
# @return [Object] try to find newest tag using #Reader and :base option if specified otherwise returns nil
|
||||||
def detect_since_tag
|
def detect_since_tag
|
||||||
@since_tag ||= @options.fetch(:since_tag) { version_of_first_item }
|
@since_tag ||= options.fetch(:since_tag) { version_of_first_item }
|
||||||
|
end
|
||||||
|
|
||||||
|
def detect_due_tag
|
||||||
|
@due_tag ||= options.fetch(:due_tag, nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
def version_of_first_item
|
def version_of_first_item
|
||||||
return unless File.file?(@options[:base].to_s)
|
return unless File.file?(options[:base].to_s)
|
||||||
|
|
||||||
sections = GitHubChangelogGenerator::Reader.new.read(@options[:base])
|
sections = GitHubChangelogGenerator::Reader.new.read(options[:base])
|
||||||
sections.first["version"] if sections && sections.any?
|
sections.first["version"] if sections && sections.any?
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -69,8 +100,8 @@ module GitHubChangelogGenerator
|
|||||||
# @return [Array]
|
# @return [Array]
|
||||||
def get_filtered_tags(all_tags)
|
def get_filtered_tags(all_tags)
|
||||||
filtered_tags = filter_since_tag(all_tags)
|
filtered_tags = filter_since_tag(all_tags)
|
||||||
filtered_tags = filter_between_tags(filtered_tags)
|
filtered_tags = filter_due_tag(filtered_tags)
|
||||||
filter_excluded_tags(filtered_tags)
|
filter_between_tags(filtered_tags)
|
||||||
end
|
end
|
||||||
|
|
||||||
# @param [Array] all_tags all tags
|
# @param [Array] all_tags all tags
|
||||||
@@ -79,8 +110,8 @@ module GitHubChangelogGenerator
|
|||||||
filtered_tags = all_tags
|
filtered_tags = all_tags
|
||||||
tag = detect_since_tag
|
tag = detect_since_tag
|
||||||
if tag
|
if tag
|
||||||
if all_tags.map(&:name).include? tag
|
if all_tags.map { |t| t["name"] }.include? tag
|
||||||
idx = all_tags.index { |t| t.name == tag }
|
idx = all_tags.index { |t| t["name"] == tag }
|
||||||
filtered_tags = if idx > 0
|
filtered_tags = if idx > 0
|
||||||
all_tags[0..idx - 1]
|
all_tags[0..idx - 1]
|
||||||
else
|
else
|
||||||
@@ -97,13 +128,12 @@ module GitHubChangelogGenerator
|
|||||||
# @return [Array] filtered tags according :due_tag option
|
# @return [Array] filtered tags according :due_tag option
|
||||||
def filter_due_tag(all_tags)
|
def filter_due_tag(all_tags)
|
||||||
filtered_tags = all_tags
|
filtered_tags = all_tags
|
||||||
tag = @options[:due_tag]
|
tag = detect_due_tag
|
||||||
if tag
|
if tag
|
||||||
if (all_tags.count > 0) && (all_tags.map(&:name).include? tag)
|
if all_tags.any? && all_tags.map { |t| t["name"] }.include?(tag)
|
||||||
idx = all_tags.index { |t| t.name == tag }
|
idx = all_tags.index { |t| t["name"] == tag }
|
||||||
last_index = all_tags.count - 1
|
filtered_tags = if idx > 0
|
||||||
filtered_tags = if idx > 0 && idx < last_index
|
all_tags[(idx + 1)..-1]
|
||||||
all_tags[idx + 1..last_index]
|
|
||||||
else
|
else
|
||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
@@ -118,13 +148,15 @@ module GitHubChangelogGenerator
|
|||||||
# @return [Array] filtered tags according :between_tags option
|
# @return [Array] filtered tags according :between_tags option
|
||||||
def filter_between_tags(all_tags)
|
def filter_between_tags(all_tags)
|
||||||
filtered_tags = all_tags
|
filtered_tags = all_tags
|
||||||
if @options[:between_tags]
|
tag_names = filtered_tags.map { |ft| ft["name"] }
|
||||||
@options[:between_tags].each do |tag|
|
|
||||||
unless all_tags.map(&:name).include? tag
|
if options[:between_tags]
|
||||||
|
options[:between_tags].each do |tag|
|
||||||
|
unless tag_names.include?(tag)
|
||||||
Helper.log.warn "Warning: can't find tag #{tag}, specified with --between-tags option."
|
Helper.log.warn "Warning: can't find tag #{tag}, specified with --between-tags option."
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
filtered_tags = all_tags.select { |tag| @options[:between_tags].include? tag.name }
|
filtered_tags = all_tags.select { |tag| options[:between_tags].include?(tag["name"]) }
|
||||||
end
|
end
|
||||||
filtered_tags
|
filtered_tags
|
||||||
end
|
end
|
||||||
@@ -132,9 +164,9 @@ module GitHubChangelogGenerator
|
|||||||
# @param [Array] all_tags all tags
|
# @param [Array] all_tags all tags
|
||||||
# @return [Array] filtered tags according :exclude_tags or :exclude_tags_regex option
|
# @return [Array] filtered tags according :exclude_tags or :exclude_tags_regex option
|
||||||
def filter_excluded_tags(all_tags)
|
def filter_excluded_tags(all_tags)
|
||||||
if @options[:exclude_tags]
|
if options[:exclude_tags]
|
||||||
apply_exclude_tags(all_tags)
|
apply_exclude_tags(all_tags)
|
||||||
elsif @options[:exclude_tags_regex]
|
elsif options[:exclude_tags_regex]
|
||||||
apply_exclude_tags_regex(all_tags)
|
apply_exclude_tags_regex(all_tags)
|
||||||
else
|
else
|
||||||
all_tags
|
all_tags
|
||||||
@@ -144,39 +176,39 @@ module GitHubChangelogGenerator
|
|||||||
private
|
private
|
||||||
|
|
||||||
def apply_exclude_tags(all_tags)
|
def apply_exclude_tags(all_tags)
|
||||||
if @options[:exclude_tags].is_a?(Regexp)
|
if options[:exclude_tags].is_a?(Regexp)
|
||||||
filter_tags_with_regex(all_tags, @options[:exclude_tags])
|
filter_tags_with_regex(all_tags, options[:exclude_tags])
|
||||||
else
|
else
|
||||||
filter_exact_tags(all_tags)
|
filter_exact_tags(all_tags)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def apply_exclude_tags_regex(all_tags)
|
def apply_exclude_tags_regex(all_tags)
|
||||||
filter_tags_with_regex(all_tags, Regexp.new(@options[:exclude_tags_regex]))
|
filter_tags_with_regex(all_tags, Regexp.new(options[:exclude_tags_regex]))
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter_tags_with_regex(all_tags, regex)
|
def filter_tags_with_regex(all_tags, regex)
|
||||||
warn_if_nonmatching_regex(all_tags)
|
warn_if_nonmatching_regex(all_tags)
|
||||||
all_tags.reject { |tag| regex =~ tag.name }
|
all_tags.reject { |tag| regex =~ tag["name"] }
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter_exact_tags(all_tags)
|
def filter_exact_tags(all_tags)
|
||||||
@options[:exclude_tags].each do |tag|
|
options[:exclude_tags].each do |tag|
|
||||||
warn_if_tag_not_found(all_tags, tag)
|
warn_if_tag_not_found(all_tags, tag)
|
||||||
end
|
end
|
||||||
all_tags.reject { |tag| @options[:exclude_tags].include? tag.name }
|
all_tags.reject { |tag| options[:exclude_tags].include?(tag["name"]) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def warn_if_nonmatching_regex(all_tags)
|
def warn_if_nonmatching_regex(all_tags)
|
||||||
unless all_tags.map(&:name).any? { |t| @options[:exclude_tags] =~ t }
|
unless all_tags.map { |t| t["name"] }.any? { |t| options[:exclude_tags] =~ t }
|
||||||
Helper.log.warn "Warning: unable to reject any tag, using regex "\
|
Helper.log.warn "Warning: unable to reject any tag, using regex "\
|
||||||
"#{@options[:exclude_tags].inspect} in --exclude-tags "\
|
"#{options[:exclude_tags].inspect} in --exclude-tags "\
|
||||||
"option."
|
"option."
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def warn_if_tag_not_found(all_tags, tag)
|
def warn_if_tag_not_found(all_tags, tag)
|
||||||
unless all_tags.map(&:name).include? tag
|
unless all_tags.map { |t| t["name"] }.include?(tag)
|
||||||
Helper.log.warn "Warning: can't find tag #{tag}, specified with --exclude-tags option."
|
Helper.log.warn "Warning: can't find tag #{tag}, specified with --exclude-tags option."
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
require "logger"
|
require "logger"
|
||||||
|
require "rainbow"
|
||||||
|
|
||||||
module GitHubChangelogGenerator
|
module GitHubChangelogGenerator
|
||||||
module Helper
|
module Helper
|
||||||
# @return true if the currently running program is a unit test
|
# @return true if the currently running program is a unit test
|
||||||
@@ -6,6 +9,7 @@ module GitHubChangelogGenerator
|
|||||||
defined? SpecHelper
|
defined? SpecHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# :nocov:
|
||||||
@log ||= if test?
|
@log ||= if test?
|
||||||
Logger.new(nil) # don't show any logs when running tests
|
Logger.new(nil) # don't show any logs when running tests
|
||||||
else
|
else
|
||||||
@@ -13,21 +17,16 @@ module GitHubChangelogGenerator
|
|||||||
end
|
end
|
||||||
@log.formatter = proc do |severity, _datetime, _progname, msg|
|
@log.formatter = proc do |severity, _datetime, _progname, msg|
|
||||||
string = "#{msg}\n"
|
string = "#{msg}\n"
|
||||||
|
case severity
|
||||||
if severity == "DEBUG"
|
when "DEBUG" then Rainbow(string).magenta
|
||||||
string = string.magenta
|
when "INFO" then Rainbow(string).white
|
||||||
elsif severity == "INFO"
|
when "WARN" then Rainbow(string).yellow
|
||||||
string = string.white
|
when "ERROR" then Rainbow(string).red
|
||||||
elsif severity == "WARN"
|
when "FATAL" then Rainbow(string).red.bright
|
||||||
string = string.yellow
|
else string
|
||||||
elsif severity == "ERROR"
|
|
||||||
string = string.red
|
|
||||||
elsif severity == "FATAL"
|
|
||||||
string = string.red.bold
|
|
||||||
end
|
end
|
||||||
|
|
||||||
string
|
|
||||||
end
|
end
|
||||||
|
# :nocov:
|
||||||
|
|
||||||
# Logging happens using this method
|
# Logging happens using this method
|
||||||
class << self
|
class << self
|
||||||
|
|||||||
@@ -0,0 +1,333 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
module GitHubChangelogGenerator
|
||||||
|
# A Fetcher responsible for all requests to GitHub and all basic manipulation with related data
|
||||||
|
# (such as filtering, validating, e.t.c)
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# fetcher = GitHubChangelogGenerator::OctoFetcher.new(options)
|
||||||
|
class OctoFetcher
|
||||||
|
PER_PAGE_NUMBER = 100
|
||||||
|
MAX_THREAD_NUMBER = 25
|
||||||
|
CHANGELOG_GITHUB_TOKEN = "CHANGELOG_GITHUB_TOKEN"
|
||||||
|
GH_RATE_LIMIT_EXCEEDED_MSG = "Warning: Can't finish operation: GitHub API rate limit exceeded, change log may be " \
|
||||||
|
"missing some issues. You can limit the number of issues fetched using the `--max-issues NUM` argument."
|
||||||
|
NO_TOKEN_PROVIDED = "Warning: No token provided (-t option) and variable $CHANGELOG_GITHUB_TOKEN was not found. " \
|
||||||
|
"This script can make only 50 requests to GitHub API per hour without token!"
|
||||||
|
|
||||||
|
# @param options [Hash] Options passed in
|
||||||
|
# @option options [String] :user GitHub username
|
||||||
|
# @option options [String] :project GitHub project
|
||||||
|
# @option options [String] :since Only issues updated at or after this time are returned. This is a timestamp in ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ. eg. Time.parse("2016-01-01 10:00:00").iso8601
|
||||||
|
# @option options [Boolean] :http_cache Use ActiveSupport::Cache::FileStore to cache http requests
|
||||||
|
# @option options [Boolean] :cache_file If using http_cache, this is the cache file path
|
||||||
|
# @option options [Boolean] :cache_log If using http_cache, this is the cache log file path
|
||||||
|
def initialize(options = {}) # rubocop:disable Metrics/CyclomaticComplexity
|
||||||
|
@options = options || {}
|
||||||
|
@user = @options[:user]
|
||||||
|
@project = @options[:project]
|
||||||
|
@since = @options[:since]
|
||||||
|
@http_cache = @options[:http_cache]
|
||||||
|
@cache_file = @options.fetch(:cache_file, "/tmp/github-changelog-http-cache") if @http_cache
|
||||||
|
@cache_log = @options.fetch(:cache_log, "/tmp/github-changelog-logger.log") if @http_cache
|
||||||
|
init_cache if @http_cache
|
||||||
|
|
||||||
|
@github_token = fetch_github_token
|
||||||
|
|
||||||
|
@request_options = { per_page: PER_PAGE_NUMBER }
|
||||||
|
@github_options = {}
|
||||||
|
@github_options[:access_token] = @github_token unless @github_token.nil?
|
||||||
|
@github_options[:api_endpoint] = @options[:github_endpoint] unless @options[:github_endpoint].nil?
|
||||||
|
|
||||||
|
client_type = @options[:github_endpoint].nil? ? Octokit::Client : Octokit::EnterpriseAdminClient
|
||||||
|
@client = client_type.new(@github_options)
|
||||||
|
end
|
||||||
|
|
||||||
|
def init_cache
|
||||||
|
middleware_opts = {
|
||||||
|
serializer: Marshal,
|
||||||
|
store: ActiveSupport::Cache::FileStore.new(@cache_file),
|
||||||
|
logger: Logger.new(@cache_log),
|
||||||
|
shared_cache: false
|
||||||
|
}
|
||||||
|
stack = Faraday::RackBuilder.new do |builder|
|
||||||
|
builder.use Faraday::HttpCache, middleware_opts
|
||||||
|
builder.use Octokit::Response::RaiseError
|
||||||
|
builder.adapter Faraday.default_adapter
|
||||||
|
# builder.response :logger
|
||||||
|
end
|
||||||
|
Octokit.middleware = stack
|
||||||
|
end
|
||||||
|
|
||||||
|
# Fetch all tags from repo
|
||||||
|
#
|
||||||
|
# @return [Array <Hash>] array of tags
|
||||||
|
def get_all_tags
|
||||||
|
print "Fetching tags...\r" if @options[:verbose]
|
||||||
|
|
||||||
|
check_github_response { github_fetch_tags }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the number of pages for a API call
|
||||||
|
#
|
||||||
|
# @return [Integer] number of pages for this API call in total
|
||||||
|
def calculate_pages(client, method, request_options)
|
||||||
|
# Makes the first API call so that we can call last_response
|
||||||
|
check_github_response do
|
||||||
|
client.send(method, user_project, @request_options.merge(request_options))
|
||||||
|
end
|
||||||
|
|
||||||
|
last_response = client.last_response
|
||||||
|
|
||||||
|
if (last_pg = last_response.rels[:last])
|
||||||
|
querystring_as_hash(last_pg.href)["page"].to_i
|
||||||
|
else
|
||||||
|
1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Fill input array with tags
|
||||||
|
#
|
||||||
|
# @return [Array <Hash>] array of tags in repo
|
||||||
|
def github_fetch_tags
|
||||||
|
tags = []
|
||||||
|
page_i = 0
|
||||||
|
count_pages = calculate_pages(@client, "tags", {})
|
||||||
|
|
||||||
|
iterate_pages(@client, "tags", {}) do |new_tags|
|
||||||
|
page_i += PER_PAGE_NUMBER
|
||||||
|
print_in_same_line("Fetching tags... #{page_i}/#{count_pages * PER_PAGE_NUMBER}")
|
||||||
|
tags.concat(new_tags)
|
||||||
|
end
|
||||||
|
print_empty_line
|
||||||
|
|
||||||
|
if tags.count == 0
|
||||||
|
Helper.log.warn "Warning: Can't find any tags in repo.\
|
||||||
|
Make sure, that you push tags to remote repo via 'git push --tags'"
|
||||||
|
else
|
||||||
|
Helper.log.info "Found #{tags.count} tags"
|
||||||
|
end
|
||||||
|
# tags are a Sawyer::Resource. Convert to hash
|
||||||
|
tags = tags.map { |h| stringify_keys_deep(h.to_hash) }
|
||||||
|
tags
|
||||||
|
end
|
||||||
|
|
||||||
|
# This method fetch all closed issues and separate them to pull requests and pure issues
|
||||||
|
# (pull request is kind of issue in term of GitHub)
|
||||||
|
#
|
||||||
|
# @return [Tuple] with (issues [Array <Hash>], pull-requests [Array <Hash>])
|
||||||
|
def fetch_closed_issues_and_pr
|
||||||
|
print "Fetching closed issues...\r" if @options[:verbose]
|
||||||
|
issues = []
|
||||||
|
options = {
|
||||||
|
state: "closed",
|
||||||
|
filter: "all",
|
||||||
|
labels: nil
|
||||||
|
}
|
||||||
|
options[:since] = @since unless @since.nil?
|
||||||
|
|
||||||
|
page_i = 0
|
||||||
|
count_pages = calculate_pages(@client, "issues", options)
|
||||||
|
|
||||||
|
iterate_pages(@client, "issues", options) do |new_issues|
|
||||||
|
page_i += PER_PAGE_NUMBER
|
||||||
|
print_in_same_line("Fetching issues... #{page_i}/#{count_pages * PER_PAGE_NUMBER}")
|
||||||
|
issues.concat(new_issues)
|
||||||
|
break if @options[:max_issues] && issues.length >= @options[:max_issues]
|
||||||
|
end
|
||||||
|
print_empty_line
|
||||||
|
Helper.log.info "Received issues: #{issues.count}"
|
||||||
|
|
||||||
|
issues = issues.map { |h| stringify_keys_deep(h.to_hash) }
|
||||||
|
|
||||||
|
# separate arrays of issues and pull requests:
|
||||||
|
issues.partition do |x|
|
||||||
|
x["pull_request"].nil?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Fetch all pull requests. We need them to detect :merged_at parameter
|
||||||
|
#
|
||||||
|
# @return [Array <Hash>] all pull requests
|
||||||
|
def fetch_closed_pull_requests
|
||||||
|
pull_requests = []
|
||||||
|
options = { state: "closed" }
|
||||||
|
|
||||||
|
unless @options[:release_branch].nil?
|
||||||
|
options[:base] = @options[:release_branch]
|
||||||
|
end
|
||||||
|
|
||||||
|
page_i = 0
|
||||||
|
count_pages = calculate_pages(@client, "pull_requests", options)
|
||||||
|
|
||||||
|
iterate_pages(@client, "pull_requests", options) do |new_pr|
|
||||||
|
page_i += PER_PAGE_NUMBER
|
||||||
|
log_string = "Fetching merged dates... #{page_i}/#{count_pages * PER_PAGE_NUMBER}"
|
||||||
|
print_in_same_line(log_string)
|
||||||
|
pull_requests.concat(new_pr)
|
||||||
|
end
|
||||||
|
print_empty_line
|
||||||
|
|
||||||
|
Helper.log.info "Pull Request count: #{pull_requests.count}"
|
||||||
|
pull_requests = pull_requests.map { |h| stringify_keys_deep(h.to_hash) }
|
||||||
|
pull_requests
|
||||||
|
end
|
||||||
|
|
||||||
|
# Fetch event for all issues and add them to 'events'
|
||||||
|
#
|
||||||
|
# @param [Array] issues
|
||||||
|
# @return [Void]
|
||||||
|
def fetch_events_async(issues)
|
||||||
|
i = 0
|
||||||
|
threads = []
|
||||||
|
|
||||||
|
issues.each_slice(MAX_THREAD_NUMBER) do |issues_slice|
|
||||||
|
issues_slice.each do |issue|
|
||||||
|
threads << Thread.new do
|
||||||
|
issue["events"] = []
|
||||||
|
iterate_pages(@client, "issue_events", issue["number"], {}) do |new_event|
|
||||||
|
issue["events"].concat(new_event)
|
||||||
|
end
|
||||||
|
issue["events"] = issue["events"].map { |h| stringify_keys_deep(h.to_hash) }
|
||||||
|
print_in_same_line("Fetching events for issues and PR: #{i + 1}/#{issues.count}")
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
threads.each(&:join)
|
||||||
|
threads = []
|
||||||
|
end
|
||||||
|
|
||||||
|
# to clear line from prev print
|
||||||
|
print_empty_line
|
||||||
|
|
||||||
|
Helper.log.info "Fetching events for issues and PR: #{i}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Fetch tag time from repo
|
||||||
|
#
|
||||||
|
# @param [Hash] tag GitHub data item about a Tag
|
||||||
|
#
|
||||||
|
# @return [Time] time of specified tag
|
||||||
|
def fetch_date_of_tag(tag)
|
||||||
|
commit_data = check_github_response { @client.commit(user_project, tag["commit"]["sha"]) }
|
||||||
|
commit_data = stringify_keys_deep(commit_data.to_hash)
|
||||||
|
|
||||||
|
commit_data["commit"]["committer"]["date"]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Fetch commit for specified event
|
||||||
|
#
|
||||||
|
# @return [Hash]
|
||||||
|
def fetch_commit(event)
|
||||||
|
check_github_response do
|
||||||
|
commit = @client.commit(user_project, event["commit_id"])
|
||||||
|
commit = stringify_keys_deep(commit.to_hash)
|
||||||
|
commit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def stringify_keys_deep(indata)
|
||||||
|
case indata
|
||||||
|
when Array
|
||||||
|
indata.map do |value|
|
||||||
|
stringify_keys_deep(value)
|
||||||
|
end
|
||||||
|
when Hash
|
||||||
|
indata.each_with_object({}) do |(k, v), output|
|
||||||
|
output[k.to_s] = stringify_keys_deep(v)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
indata
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Iterates through all pages until there are no more :next pages to follow
|
||||||
|
# yields the result per page
|
||||||
|
#
|
||||||
|
# @param [Octokit::Client] client
|
||||||
|
# @param [String] method (eg. 'tags')
|
||||||
|
# @return [Integer] total number of pages
|
||||||
|
def iterate_pages(client, method, *args)
|
||||||
|
if args.size == 1 && args.first.is_a?(Hash)
|
||||||
|
request_options = args.delete_at(0)
|
||||||
|
elsif args.size > 1 && args.last.is_a?(Hash)
|
||||||
|
request_options = args.delete_at(args.length - 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
args.push(@request_options.merge(request_options))
|
||||||
|
|
||||||
|
pages = 1
|
||||||
|
|
||||||
|
check_github_response do
|
||||||
|
client.send(method, user_project, *args)
|
||||||
|
end
|
||||||
|
last_response = client.last_response
|
||||||
|
|
||||||
|
yield last_response.data
|
||||||
|
|
||||||
|
until (next_one = last_response.rels[:next]).nil?
|
||||||
|
pages += 1
|
||||||
|
|
||||||
|
last_response = check_github_response { next_one.get }
|
||||||
|
yield last_response.data
|
||||||
|
end
|
||||||
|
|
||||||
|
pages
|
||||||
|
end
|
||||||
|
|
||||||
|
# This is wrapper with rescue block
|
||||||
|
#
|
||||||
|
# @return [Object] returns exactly the same, what you put in the block, but wrap it with begin-rescue block
|
||||||
|
def check_github_response
|
||||||
|
begin
|
||||||
|
value = yield
|
||||||
|
rescue Octokit::Unauthorized => e
|
||||||
|
Helper.log.error e.message
|
||||||
|
abort "Error: wrong GitHub token"
|
||||||
|
rescue Octokit::Forbidden => e
|
||||||
|
Helper.log.warn e.message
|
||||||
|
Helper.log.warn GH_RATE_LIMIT_EXCEEDED_MSG
|
||||||
|
Helper.log.warn @client.rate_limit
|
||||||
|
end
|
||||||
|
value
|
||||||
|
end
|
||||||
|
|
||||||
|
# Print specified line on the same string
|
||||||
|
#
|
||||||
|
# @param [String] log_string
|
||||||
|
def print_in_same_line(log_string)
|
||||||
|
print log_string + "\r"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Print long line with spaces on same line to clear prev message
|
||||||
|
def print_empty_line
|
||||||
|
print_in_same_line(" ")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns GitHub token. First try to use variable, provided by --token option,
|
||||||
|
# otherwise try to fetch it from CHANGELOG_GITHUB_TOKEN env variable.
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
def fetch_github_token
|
||||||
|
env_var = @options[:token] ? @options[:token] : (ENV.fetch CHANGELOG_GITHUB_TOKEN, nil)
|
||||||
|
|
||||||
|
Helper.log.warn NO_TOKEN_PROVIDED unless env_var
|
||||||
|
|
||||||
|
env_var
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [String] helper to return Github "user/project"
|
||||||
|
def user_project
|
||||||
|
"#{@options[:user]}/#{@options[:project]}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns Hash of all querystring variables in given URI.
|
||||||
|
#
|
||||||
|
# @param [String] uri eg. https://api.github.com/repositories/43914960/tags?page=37&foo=1
|
||||||
|
# @return [Hash] of all GET variables. eg. { 'page' => 37, 'foo' => 1 }
|
||||||
|
def querystring_as_hash(uri)
|
||||||
|
Hash[URI.decode_www_form(URI(uri).query || "")]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
require "delegate"
|
||||||
|
module GitHubChangelogGenerator
|
||||||
|
class Options < SimpleDelegator
|
||||||
|
UnsupportedOptionError = Class.new(ArgumentError)
|
||||||
|
|
||||||
|
KNOWN_OPTIONS = [
|
||||||
|
:add_issues_wo_labels,
|
||||||
|
:add_pr_wo_labels,
|
||||||
|
:author,
|
||||||
|
:base,
|
||||||
|
:between_tags,
|
||||||
|
:bug_labels,
|
||||||
|
:bug_prefix,
|
||||||
|
:cache_file,
|
||||||
|
:cache_log,
|
||||||
|
:compare_link,
|
||||||
|
:date_format,
|
||||||
|
:due_tag,
|
||||||
|
:enhancement_labels,
|
||||||
|
:enhancement_prefix,
|
||||||
|
:exclude_labels,
|
||||||
|
:exclude_tags,
|
||||||
|
:exclude_tags_regex,
|
||||||
|
:filter_issues_by_milestone,
|
||||||
|
:frontmatter,
|
||||||
|
:future_release,
|
||||||
|
:git_remote,
|
||||||
|
:github_endpoint,
|
||||||
|
:github_site,
|
||||||
|
:header,
|
||||||
|
:http_cache,
|
||||||
|
:include_labels,
|
||||||
|
:issue_prefix,
|
||||||
|
:issues,
|
||||||
|
:max_issues,
|
||||||
|
:merge_prefix,
|
||||||
|
:output,
|
||||||
|
:project,
|
||||||
|
:pulls,
|
||||||
|
:release_branch,
|
||||||
|
:release_url,
|
||||||
|
:simple_list,
|
||||||
|
:since_tag,
|
||||||
|
:token,
|
||||||
|
:unreleased,
|
||||||
|
:unreleased_label,
|
||||||
|
:unreleased_only,
|
||||||
|
:user,
|
||||||
|
:usernames_as_github_logins,
|
||||||
|
:verbose
|
||||||
|
]
|
||||||
|
|
||||||
|
THESE_ARE_DIFFERENT = [
|
||||||
|
:tag1,
|
||||||
|
:tag2
|
||||||
|
]
|
||||||
|
|
||||||
|
def initialize(values)
|
||||||
|
super(values)
|
||||||
|
unsupported_options.any? && raise(UnsupportedOptionError, unsupported_options.inspect)
|
||||||
|
end
|
||||||
|
|
||||||
|
def []=(key, val)
|
||||||
|
supported_option?(key) || raise(UnsupportedOptionError, key.inspect)
|
||||||
|
values[key] = val
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_hash
|
||||||
|
values
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def values
|
||||||
|
__getobj__
|
||||||
|
end
|
||||||
|
|
||||||
|
def unsupported_options
|
||||||
|
values.keys - supported_options
|
||||||
|
end
|
||||||
|
|
||||||
|
def supported_option?(key)
|
||||||
|
supported_options.include?(key)
|
||||||
|
end
|
||||||
|
|
||||||
|
def supported_options
|
||||||
|
KNOWN_OPTIONS + THESE_ARE_DIFFERENT
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Regular → Executable
+91
-48
@@ -1,4 +1,5 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
# frozen_string_literal: true
|
||||||
require "optparse"
|
require "optparse"
|
||||||
require "pp"
|
require "pp"
|
||||||
require_relative "version"
|
require_relative "version"
|
||||||
@@ -14,7 +15,7 @@ module GitHubChangelogGenerator
|
|||||||
parser = setup_parser(options)
|
parser = setup_parser(options)
|
||||||
parser.parse!
|
parser.parse!
|
||||||
|
|
||||||
user_and_project_from_git(options)
|
fetch_user_and_project(options)
|
||||||
|
|
||||||
abort(parser.banner) unless options[:user] && options[:project]
|
abort(parser.banner) unless options[:user] && options[:project]
|
||||||
|
|
||||||
@@ -23,7 +24,12 @@ module GitHubChangelogGenerator
|
|||||||
options
|
options
|
||||||
end
|
end
|
||||||
|
|
||||||
# @param [Hash] options to display
|
# If options set to verbose, print the parsed options.
|
||||||
|
#
|
||||||
|
# The GitHub `:token` key is censored in the output.
|
||||||
|
#
|
||||||
|
# @param options [Hash] The options to display
|
||||||
|
# @option options [Boolean] :verbose If false this method does nothing
|
||||||
def self.print_options(options)
|
def self.print_options(options)
|
||||||
if options[:verbose]
|
if options[:verbose]
|
||||||
Helper.log.info "Performing task with options:"
|
Helper.log.info "Performing task with options:"
|
||||||
@@ -56,7 +62,7 @@ module GitHubChangelogGenerator
|
|||||||
opts.on("-b", "--base [NAME]", "Optional base file to append generated changes to.") do |last|
|
opts.on("-b", "--base [NAME]", "Optional base file to append generated changes to.") do |last|
|
||||||
options[:base] = last
|
options[:base] = last
|
||||||
end
|
end
|
||||||
opts.on("--bugs-label [LABEL]", "Setup custom label for bug-fixes section. Default is \"**Fixed bugs:**""") do |v|
|
opts.on("--bugs-label [LABEL]", "Setup custom label for bug-fixes section. Default is \"**Fixed bugs:**\"") do |v|
|
||||||
options[:bug_prefix] = v
|
options[:bug_prefix] = v
|
||||||
end
|
end
|
||||||
opts.on("--enhancement-label [LABEL]", "Setup custom label for enhancements section. Default is \"**Implemented enhancements:**\"") do |v|
|
opts.on("--enhancement-label [LABEL]", "Setup custom label for enhancements section. Default is \"**Implemented enhancements:**\"") do |v|
|
||||||
@@ -92,6 +98,9 @@ module GitHubChangelogGenerator
|
|||||||
opts.on("--[no-]author", "Add author of pull-request in the end. Default is true") do |author|
|
opts.on("--[no-]author", "Add author of pull-request in the end. Default is true") do |author|
|
||||||
options[:author] = author
|
options[:author] = author
|
||||||
end
|
end
|
||||||
|
opts.on("--usernames-as-github-logins", "Use GitHub tags instead of Markdown links for the author of an issue or pull-request.") do |v|
|
||||||
|
options[:usernames_as_github_logins] = v
|
||||||
|
end
|
||||||
opts.on("--unreleased-only", "Generate log from unreleased closed issues only.") do |v|
|
opts.on("--unreleased-only", "Generate log from unreleased closed issues only.") do |v|
|
||||||
options[:unreleased_only] = v
|
options[:unreleased_only] = v
|
||||||
end
|
end
|
||||||
@@ -152,6 +161,15 @@ module GitHubChangelogGenerator
|
|||||||
opts.on("--release-branch [RELEASE-BRANCH]", "Limit pull requests to the release branch, such as master or release") do |release_branch|
|
opts.on("--release-branch [RELEASE-BRANCH]", "Limit pull requests to the release branch, such as master or release") do |release_branch|
|
||||||
options[:release_branch] = release_branch
|
options[:release_branch] = release_branch
|
||||||
end
|
end
|
||||||
|
opts.on("--[no-]http-cache", "Use HTTP Cache to cache Github API requests (useful for large repos) Default is true.") do |http_cache|
|
||||||
|
options[:http_cache] = http_cache
|
||||||
|
end
|
||||||
|
opts.on("--cache-file [CACHE-FILE]", "Filename to use for cache. Default is /tmp/github-changelog-http-cache") do |cache_file|
|
||||||
|
options[:cache_file] = cache_file
|
||||||
|
end
|
||||||
|
opts.on("--cache-log [CACHE-LOG]", "Filename to use for cache log. Default is /tmp/github-changelog-logger.log") do |cache_log|
|
||||||
|
options[:cache_log] = cache_log
|
||||||
|
end
|
||||||
opts.on("--[no-]verbose", "Run verbosely. Default is true") do |v|
|
opts.on("--[no-]verbose", "Run verbosely. Default is true") do |v|
|
||||||
options[:verbose] = v
|
options[:verbose] = v
|
||||||
end
|
end
|
||||||
@@ -167,9 +185,9 @@ module GitHubChangelogGenerator
|
|||||||
parser
|
parser
|
||||||
end
|
end
|
||||||
|
|
||||||
# just get default options
|
# @return [Hash] Default options
|
||||||
def self.default_options
|
def self.default_options
|
||||||
{
|
Options.new(
|
||||||
tag1: nil,
|
tag1: nil,
|
||||||
tag2: nil,
|
tag2: nil,
|
||||||
date_format: "%Y-%m-%d",
|
date_format: "%Y-%m-%d",
|
||||||
@@ -195,41 +213,61 @@ module GitHubChangelogGenerator
|
|||||||
issue_prefix: "**Closed issues:**",
|
issue_prefix: "**Closed issues:**",
|
||||||
bug_prefix: "**Fixed bugs:**",
|
bug_prefix: "**Fixed bugs:**",
|
||||||
enhancement_prefix: "**Implemented enhancements:**",
|
enhancement_prefix: "**Implemented enhancements:**",
|
||||||
git_remote: "origin"
|
git_remote: "origin",
|
||||||
}
|
http_cache: true,
|
||||||
|
cache_file: "/tmp/github-changelog-http-cache",
|
||||||
|
cache_log: "/tmp/github-changelog-logger.log"
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.user_and_project_from_git(options)
|
# If `:user` or `:project` not set in options, try setting them
|
||||||
|
# Valid unnamed parameters:
|
||||||
|
# 1) in 1 param: repo_name/project
|
||||||
|
# 2) in 2 params: repo name project
|
||||||
|
def self.fetch_user_and_project(options)
|
||||||
if options[:user].nil? || options[:project].nil?
|
if options[:user].nil? || options[:project].nil?
|
||||||
detect_user_and_project(options, ARGV[0], ARGV[1])
|
user, project = user_and_project_from_git(options, ARGV[0], ARGV[1])
|
||||||
|
options[:user] ||= user
|
||||||
|
options[:project] ||= project
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Detects user and project from git
|
# Sets `:user` and `:project` in `options` from CLI arguments or `git remote`
|
||||||
def self.detect_user_and_project(options, arg0 = nil, arg1 = nil)
|
# @param [String] arg0 first argument in cli
|
||||||
options[:user], options[:project] = user_project_from_option(arg0, arg1, options[:github_site])
|
# @param [String] arg1 second argument in cli
|
||||||
return if options[:user] && options[:project]
|
# @return [Array<String>] user and project, or nil if unsuccessful
|
||||||
|
def self.user_and_project_from_git(options, arg0 = nil, arg1 = nil)
|
||||||
if ENV["RUBYLIB"] =~ /ruby-debug-ide/
|
user, project = user_project_from_option(arg0, arg1, options[:github_site])
|
||||||
options[:user] = "skywinder"
|
unless user && project
|
||||||
options[:project] = "changelog_test"
|
if ENV["RUBYLIB"] =~ /ruby-debug-ide/
|
||||||
else
|
user = "skywinder"
|
||||||
remote = `git config --get remote.#{options[:git_remote]}.url`
|
project = "changelog_test"
|
||||||
options[:user], options[:project] = user_project_from_remote(remote)
|
else
|
||||||
|
remote = `git config --get remote.#{options[:git_remote]}.url`
|
||||||
|
user, project = user_project_from_remote(remote)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
[user, project]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Try to find user and project name from git remote output
|
# Returns GitHub username and project from CLI arguments
|
||||||
#
|
#
|
||||||
# @param [String] output of git remote command
|
# @param arg0 [String] This parameter takes two forms: Either a full
|
||||||
# @return [Array] user and project
|
# GitHub URL, or a 'username/projectname', or
|
||||||
|
# simply a GitHub username
|
||||||
|
# @param arg1 [String] If arg0 is given as a username,
|
||||||
|
# then arg1 can given as a projectname
|
||||||
|
# @param github_site [String] Domain name of GitHub site
|
||||||
|
#
|
||||||
|
# @return [Array, nil] user and project, or nil if unsuccessful
|
||||||
def self.user_project_from_option(arg0, arg1, github_site)
|
def self.user_project_from_option(arg0, arg1, github_site)
|
||||||
user = nil
|
user = nil
|
||||||
project = nil
|
project = nil
|
||||||
github_site ||= "github.com"
|
github_site ||= "github.com"
|
||||||
if arg0 && !arg1
|
if arg0 && !arg1
|
||||||
# this match should parse strings such "https://github.com/skywinder/Github-Changelog-Generator" or "skywinder/Github-Changelog-Generator" to user and name
|
# this match should parse strings such "https://github.com/skywinder/Github-Changelog-Generator" or
|
||||||
puts arg0
|
# "skywinder/Github-Changelog-Generator" to user and name
|
||||||
match = /(?:.+#{Regexp.escape(github_site)}\/)?(.+)\/(.+)/.match(arg0)
|
match = /(?:.+#{Regexp.escape(github_site)}\/)?(.+)\/(.+)/.match(arg0)
|
||||||
|
|
||||||
begin
|
begin
|
||||||
@@ -248,35 +286,40 @@ module GitHubChangelogGenerator
|
|||||||
[user, project]
|
[user, project]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Try to find user and project name from git remote output
|
# These patterns match these formats:
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# origin git@github.com:skywinder/Github-Changelog-Generator.git (fetch)
|
||||||
|
# git@github.com:skywinder/Github-Changelog-Generator.git
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# and
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# origin https://github.com/skywinder/ChangelogMerger (fetch)
|
||||||
|
# https://github.com/skywinder/ChangelogMerger
|
||||||
|
# ```
|
||||||
|
GIT_REMOTE_PATTERNS = [
|
||||||
|
/.*(?:[:\/])(?<user>(?:-|\w|\.)*)\/(?<project>(?:-|\w|\.)*)(?:\.git).*/,
|
||||||
|
/.*\/(?<user>(?:-|\w|\.)*)\/(?<project>(?:-|\w|\.)*).*/
|
||||||
|
]
|
||||||
|
|
||||||
|
# Returns GitHub username and project from git remote output
|
||||||
|
#
|
||||||
|
# @param git_remote_output [String] Output of git remote command
|
||||||
#
|
#
|
||||||
# @param [String] output of git remote command
|
|
||||||
# @return [Array] user and project
|
# @return [Array] user and project
|
||||||
def self.user_project_from_remote(remote)
|
def self.user_project_from_remote(git_remote_output)
|
||||||
# try to find repo in format:
|
|
||||||
# origin git@github.com:skywinder/Github-Changelog-Generator.git (fetch)
|
|
||||||
# git@github.com:skywinder/Github-Changelog-Generator.git
|
|
||||||
regex1 = /.*(?:[:\/])((?:-|\w|\.)*)\/((?:-|\w|\.)*)(?:\.git).*/
|
|
||||||
|
|
||||||
# try to find repo in format:
|
|
||||||
# origin https://github.com/skywinder/ChangelogMerger (fetch)
|
|
||||||
# https://github.com/skywinder/ChangelogMerger
|
|
||||||
regex2 = /.*\/((?:-|\w|\.)*)\/((?:-|\w|\.)*).*/
|
|
||||||
|
|
||||||
remote_structures = [regex1, regex2]
|
|
||||||
|
|
||||||
user = nil
|
user = nil
|
||||||
project = nil
|
project = nil
|
||||||
remote_structures.each do |regex|
|
GIT_REMOTE_PATTERNS.each do |git_remote_pattern|
|
||||||
matches = Regexp.new(regex).match(remote)
|
git_remote_pattern =~ git_remote_output
|
||||||
|
|
||||||
if matches && matches[1] && matches[2]
|
if Regexp.last_match
|
||||||
puts "Detected user:#{matches[1]}, project:#{matches[2]}"
|
user = Regexp.last_match(:user)
|
||||||
user = matches[1]
|
project = Regexp.last_match(:project)
|
||||||
project = matches[2]
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
break unless matches.nil?
|
|
||||||
end
|
end
|
||||||
|
|
||||||
[user, project]
|
[user, project]
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
require "pathname"
|
require "pathname"
|
||||||
|
|
||||||
module GitHubChangelogGenerator
|
module GitHubChangelogGenerator
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
#
|
#
|
||||||
# Author:: Enrico Stahn <mail@enricostahn.com>
|
# Author:: Enrico Stahn <mail@enricostahn.com>
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
require "rake"
|
require "rake"
|
||||||
require "rake/tasklib"
|
require "rake/tasklib"
|
||||||
require "github_changelog_generator"
|
require "github_changelog_generator"
|
||||||
@@ -14,7 +15,7 @@ module GitHubChangelogGenerator
|
|||||||
unreleased_only unreleased unreleased_label
|
unreleased_only unreleased unreleased_label
|
||||||
compare_link include_labels exclude_labels
|
compare_link include_labels exclude_labels
|
||||||
bug_labels enhancement_labels
|
bug_labels enhancement_labels
|
||||||
between_tags exclude_tags since_tag max_issues
|
between_tags exclude_tags exclude_tags_regex since_tag max_issues
|
||||||
github_site github_endpoint simple_list
|
github_site github_endpoint simple_list
|
||||||
future_release release_branch verbose release_url
|
future_release release_branch verbose release_url
|
||||||
base )
|
base )
|
||||||
@@ -46,7 +47,7 @@ module GitHubChangelogGenerator
|
|||||||
# mimick parse_options
|
# mimick parse_options
|
||||||
options = Parser.default_options
|
options = Parser.default_options
|
||||||
|
|
||||||
Parser.user_and_project_from_git(options)
|
Parser.fetch_user_and_project(options)
|
||||||
|
|
||||||
OPTIONS.each do |o|
|
OPTIONS.each do |o|
|
||||||
v = instance_variable_get("@#{o}")
|
v = instance_variable_get("@#{o}")
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
module GitHubChangelogGenerator
|
module GitHubChangelogGenerator
|
||||||
VERSION = "1.12.1"
|
VERSION = "1.13.2"
|
||||||
end
|
end
|
||||||
|
|||||||
+25
-1
@@ -1,3 +1,4 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
#
|
#
|
||||||
# Author:: Enrico Stahn <mail@enricostahn.com>
|
# Author:: Enrico Stahn <mail@enricostahn.com>
|
||||||
#
|
#
|
||||||
@@ -18,6 +19,8 @@
|
|||||||
require "codeclimate-test-reporter"
|
require "codeclimate-test-reporter"
|
||||||
require "simplecov"
|
require "simplecov"
|
||||||
require "coveralls"
|
require "coveralls"
|
||||||
|
require "vcr"
|
||||||
|
require "webmock/rspec"
|
||||||
|
|
||||||
# This module is only used to check the environment is currently a testing env
|
# This module is only used to check the environment is currently a testing env
|
||||||
module SpecHelper
|
module SpecHelper
|
||||||
@@ -28,11 +31,32 @@ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([
|
|||||||
SimpleCov::Formatter::HTMLFormatter,
|
SimpleCov::Formatter::HTMLFormatter,
|
||||||
CodeClimate::TestReporter::Formatter
|
CodeClimate::TestReporter::Formatter
|
||||||
])
|
])
|
||||||
SimpleCov.start
|
SimpleCov.start do
|
||||||
|
add_filter "gemfiles/"
|
||||||
|
end
|
||||||
|
|
||||||
require "github_changelog_generator"
|
require "github_changelog_generator"
|
||||||
require "github_changelog_generator/task"
|
require "github_changelog_generator/task"
|
||||||
|
|
||||||
|
VCR.configure do |c|
|
||||||
|
c.allow_http_connections_when_no_cassette = true
|
||||||
|
c.cassette_library_dir = "spec/vcr"
|
||||||
|
c.ignore_localhost = true
|
||||||
|
c.default_cassette_options = {
|
||||||
|
record: :new_episodes,
|
||||||
|
serialize_with: :json,
|
||||||
|
preserve_exact_body_bytes: true,
|
||||||
|
decode_compressed_response: true
|
||||||
|
}
|
||||||
|
c.filter_sensitive_data("<GITHUB_TOKEN>") do
|
||||||
|
"token #{ENV.fetch('CHANGELOG_GITHUB_TOKEN') { 'frobnitz' }}"
|
||||||
|
end
|
||||||
|
|
||||||
|
c.configure_rspec_metadata!
|
||||||
|
|
||||||
|
c.hook_into :webmock, :faraday
|
||||||
|
end
|
||||||
|
|
||||||
RSpec.configure do |config|
|
RSpec.configure do |config|
|
||||||
config.expect_with :rspec do |expectations|
|
config.expect_with :rspec do |expectations|
|
||||||
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
VALID_TOKEN = "0123456789abcdef"
|
|
||||||
INVALID_TOKEN = "0000000000000000"
|
|
||||||
|
|
||||||
DEFAULT_OPTIONS = { user: "skywinder",
|
|
||||||
project: "changelog_test" }
|
|
||||||
|
|
||||||
def options_with_invalid_token
|
|
||||||
options = DEFAULT_OPTIONS
|
|
||||||
options[:token] = INVALID_TOKEN
|
|
||||||
options
|
|
||||||
end
|
|
||||||
|
|
||||||
describe GitHubChangelogGenerator::Fetcher do
|
|
||||||
before(:all) do
|
|
||||||
@fetcher = GitHubChangelogGenerator::Fetcher.new
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#fetch_github_token" do
|
|
||||||
token = GitHubChangelogGenerator::Fetcher::CHANGELOG_GITHUB_TOKEN
|
|
||||||
context "when token in ENV exist" do
|
|
||||||
before { stub_const("ENV", ENV.to_hash.merge(token => VALID_TOKEN)) }
|
|
||||||
subject { @fetcher.fetch_github_token }
|
|
||||||
it { is_expected.to eq(VALID_TOKEN) }
|
|
||||||
end
|
|
||||||
context "when token in ENV is nil" do
|
|
||||||
before { stub_const("ENV", ENV.to_hash.merge(token => nil)) }
|
|
||||||
subject { @fetcher.fetch_github_token }
|
|
||||||
it { is_expected.to be_nil }
|
|
||||||
end
|
|
||||||
context "when token in options and ENV is nil" do
|
|
||||||
before do
|
|
||||||
stub_const("ENV", ENV.to_hash.merge(token => nil))
|
|
||||||
@fetcher = GitHubChangelogGenerator::Fetcher.new(token: VALID_TOKEN)
|
|
||||||
end
|
|
||||||
subject { @fetcher.fetch_github_token }
|
|
||||||
it { is_expected.to eq(VALID_TOKEN) }
|
|
||||||
end
|
|
||||||
context "when token in options and ENV specified" do
|
|
||||||
before do
|
|
||||||
stub_const("ENV", ENV.to_hash.merge(token => "no_matter_what"))
|
|
||||||
@fetcher = GitHubChangelogGenerator::Fetcher.new(token: VALID_TOKEN)
|
|
||||||
end
|
|
||||||
subject { @fetcher.fetch_github_token }
|
|
||||||
it { is_expected.to eq(VALID_TOKEN) }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#github_fetch_tags" do
|
|
||||||
context "when wrong token provided" do
|
|
||||||
before do
|
|
||||||
options = options_with_invalid_token
|
|
||||||
@fetcher = GitHubChangelogGenerator::Fetcher.new(options)
|
|
||||||
end
|
|
||||||
it "should raise Unauthorized error" do
|
|
||||||
expect { @fetcher.github_fetch_tags }.to raise_error Github::Error::Unauthorized
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
module GitHubChangelogGenerator
|
module GitHubChangelogGenerator
|
||||||
describe Generator do
|
describe Generator do
|
||||||
context "#exclude_issues_by_labels" do
|
context "#exclude_issues_by_labels" do
|
||||||
let(:label) { double("the-bad-label", name: "BAD") }
|
let(:label) { { "name" => "BAD" } }
|
||||||
let(:issue) { double("the-issue-to-be-excluded", labels: [label]) }
|
let(:issue) { { "labels" => [label] } }
|
||||||
let(:good_label) { double("a-good-label", name: "GOOD") }
|
let(:good_label) { { "name" => "GOOD" } }
|
||||||
let(:good_issue) { double("an-issue-to-be-kept", labels: [good_label]) }
|
let(:good_issue) { { "labels" => [good_label] } }
|
||||||
let(:issues) { [issue, good_issue] }
|
let(:issues) { [issue, good_issue] }
|
||||||
subject(:generator) { described_class.new(exclude_labels: %w(BAD BOO)) }
|
subject(:generator) { described_class.new(exclude_labels: %w(BAD BOO)) }
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
describe GitHubChangelogGenerator::Generator do
|
describe GitHubChangelogGenerator::Generator do
|
||||||
def tag_mash_with_name(tag)
|
def tag_with_name(tag)
|
||||||
Hashie::Mash.new.tap { |mash_tag| mash_tag.name = tag }
|
{
|
||||||
|
"name" => tag
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def tags_mash_from_strings(tags_strings)
|
def tags_from_strings(tags_strings)
|
||||||
tags_strings.map do |tag|
|
tags_strings.map do |tag|
|
||||||
tag_mash_with_name(tag)
|
tag_with_name(tag)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -16,20 +19,20 @@ describe GitHubChangelogGenerator::Generator do
|
|||||||
end
|
end
|
||||||
|
|
||||||
subject do
|
subject do
|
||||||
@generator.get_filtered_tags(tags_mash_from_strings(%w(1 2 3)))
|
@generator.get_filtered_tags(tags_from_strings(%w(1 2 3)))
|
||||||
end
|
end
|
||||||
it { is_expected.to be_a(Array) }
|
it { is_expected.to be_a(Array) }
|
||||||
it { is_expected.to match_array(tags_mash_from_strings(%w(1 2 3))) }
|
it { is_expected.to match_array(tags_from_strings(%w(1 2 3))) }
|
||||||
end
|
end
|
||||||
context "when between_tags same as input array" do
|
context "when between_tags same as input array" do
|
||||||
before do
|
before do
|
||||||
@generator = GitHubChangelogGenerator::Generator.new(between_tags: %w(1 2 3))
|
@generator = GitHubChangelogGenerator::Generator.new(between_tags: %w(1 2 3))
|
||||||
end
|
end
|
||||||
subject do
|
subject do
|
||||||
@generator.get_filtered_tags(tags_mash_from_strings(%w(1 2 3)))
|
@generator.get_filtered_tags(tags_from_strings(%w(1 2 3)))
|
||||||
end
|
end
|
||||||
it { is_expected.to be_a(Array) }
|
it { is_expected.to be_a(Array) }
|
||||||
it { is_expected.to match_array(tags_mash_from_strings(%w(1 2 3))) }
|
it { is_expected.to match_array(tags_from_strings(%w(1 2 3))) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when between_tags filled with correct values" do
|
context "when between_tags filled with correct values" do
|
||||||
@@ -37,10 +40,10 @@ describe GitHubChangelogGenerator::Generator do
|
|||||||
@generator = GitHubChangelogGenerator::Generator.new(between_tags: %w(1 2))
|
@generator = GitHubChangelogGenerator::Generator.new(between_tags: %w(1 2))
|
||||||
end
|
end
|
||||||
subject do
|
subject do
|
||||||
@generator.get_filtered_tags(tags_mash_from_strings(%w(1 2 3)))
|
@generator.get_filtered_tags(tags_from_strings(%w(1 2 3)))
|
||||||
end
|
end
|
||||||
it { is_expected.to be_a(Array) }
|
it { is_expected.to be_a(Array) }
|
||||||
it { is_expected.to match_array(tags_mash_from_strings(%w(1 2))) }
|
it { is_expected.to match_array(tags_from_strings(%w(1 2))) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when between_tags filled with invalid values" do
|
context "when between_tags filled with invalid values" do
|
||||||
@@ -49,133 +52,134 @@ describe GitHubChangelogGenerator::Generator do
|
|||||||
end
|
end
|
||||||
|
|
||||||
subject do
|
subject do
|
||||||
@generator.get_filtered_tags(tags_mash_from_strings(%w(1 2 3)))
|
@generator.get_filtered_tags(tags_from_strings(%w(1 2 3)))
|
||||||
end
|
end
|
||||||
it { is_expected.to be_a(Array) }
|
it { is_expected.to be_a(Array) }
|
||||||
it { is_expected.to match_array(tags_mash_from_strings(%w(1))) }
|
it { is_expected.to match_array(tags_from_strings(%w(1))) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#get_filtered_tags" do
|
describe "#get_filtered_tags" do
|
||||||
subject do
|
subject do
|
||||||
generator.get_filtered_tags(tags_mash_from_strings(%w(1 2 3 4 5)))
|
generator.get_filtered_tags(tags_from_strings(%w(1 2 3 4 5)))
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with excluded and between tags" do
|
context "respects between tags" do
|
||||||
let(:generator) { GitHubChangelogGenerator::Generator.new(between_tags: %w(1 2 3), exclude_tags: %w(2)) }
|
let(:generator) { GitHubChangelogGenerator::Generator.new(between_tags: %w(1 2 3)) }
|
||||||
|
|
||||||
it { is_expected.to be_a Array }
|
it { is_expected.to be_a Array }
|
||||||
it { is_expected.to match_array(tags_mash_from_strings(%w(1 3))) }
|
it { is_expected.to match_array(tags_from_strings(%w(1 2 3))) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#filter_excluded_tags" do
|
describe "#filter_excluded_tags" do
|
||||||
subject { generator.filter_excluded_tags(tags_mash_from_strings(%w(1 2 3))) }
|
subject { generator.filter_excluded_tags(tags_from_strings(%w(1 2 3))) }
|
||||||
|
|
||||||
context "with matching string" do
|
context "with matching string" do
|
||||||
let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags: %w(3)) }
|
let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags: %w(3)) }
|
||||||
it { is_expected.to be_a Array }
|
it { is_expected.to be_a Array }
|
||||||
it { is_expected.to match_array(tags_mash_from_strings(%w(1 2))) }
|
it { is_expected.to match_array(tags_from_strings(%w(1 2))) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with non-matching string" do
|
context "with non-matching string" do
|
||||||
let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags: %w(invalid tags)) }
|
let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags: %w(invalid tags)) }
|
||||||
it { is_expected.to be_a Array }
|
it { is_expected.to be_a Array }
|
||||||
it { is_expected.to match_array(tags_mash_from_strings(%w(1 2 3))) }
|
it { is_expected.to match_array(tags_from_strings(%w(1 2 3))) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with matching regex" do
|
context "with matching regex" do
|
||||||
let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags: /[23]/) }
|
let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags: /[23]/) }
|
||||||
it { is_expected.to be_a Array }
|
it { is_expected.to be_a Array }
|
||||||
it { is_expected.to match_array(tags_mash_from_strings(%w(1))) }
|
it { is_expected.to match_array(tags_from_strings(%w(1))) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with non-matching regex" do
|
context "with non-matching regex" do
|
||||||
let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags: /[abc]/) }
|
let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags: /[abc]/) }
|
||||||
it { is_expected.to be_a Array }
|
it { is_expected.to be_a Array }
|
||||||
it { is_expected.to match_array(tags_mash_from_strings(%w(1 2 3))) }
|
it { is_expected.to match_array(tags_from_strings(%w(1 2 3))) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#filter_excluded_tags_regex" do
|
describe "#filter_excluded_tags_regex" do
|
||||||
subject { generator.filter_excluded_tags(tags_mash_from_strings(%w(1 2 3))) }
|
subject { generator.filter_excluded_tags(tags_from_strings(%w(1 2 3))) }
|
||||||
|
|
||||||
context "with matching regex" do
|
context "with matching regex" do
|
||||||
let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags_regex: "[23]") }
|
let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags_regex: "[23]") }
|
||||||
it { is_expected.to be_a Array }
|
it { is_expected.to be_a Array }
|
||||||
it { is_expected.to match_array(tags_mash_from_strings(%w(1))) }
|
it { is_expected.to match_array(tags_from_strings(%w(1))) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with non-matching regex" do
|
context "with non-matching regex" do
|
||||||
let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags_regex: "[45]") }
|
let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags_regex: "[45]") }
|
||||||
it { is_expected.to be_a Array }
|
it { is_expected.to be_a Array }
|
||||||
it { is_expected.to match_array(tags_mash_from_strings(%w(1 2 3))) }
|
it { is_expected.to match_array(tags_from_strings(%w(1 2 3))) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#filter_since_tag" do
|
describe "#filter_since_tag" do
|
||||||
context "with filled array" do
|
context "with filled array" do
|
||||||
subject { generator.filter_since_tag(tags_mash_from_strings(%w(1 2 3))) }
|
subject { generator.filter_since_tag(tags_from_strings(%w(1 2 3))) }
|
||||||
|
|
||||||
context "with valid since tag" do
|
context "with valid since tag" do
|
||||||
let(:generator) { GitHubChangelogGenerator::Generator.new(since_tag: "2") }
|
let(:generator) { GitHubChangelogGenerator::Generator.new(since_tag: "2") }
|
||||||
it { is_expected.to be_a Array }
|
it { is_expected.to be_a Array }
|
||||||
it { is_expected.to match_array(tags_mash_from_strings(%w(1))) }
|
it { is_expected.to match_array(tags_from_strings(%w(1))) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with invalid since tag" do
|
context "with invalid since tag" do
|
||||||
let(:generator) { GitHubChangelogGenerator::Generator.new(since_tag: "Invalid tag") }
|
let(:generator) { GitHubChangelogGenerator::Generator.new(since_tag: "Invalid tag") }
|
||||||
it { is_expected.to be_a Array }
|
it { is_expected.to be_a Array }
|
||||||
it { is_expected.to match_array(tags_mash_from_strings(%w(1 2 3))) }
|
it { is_expected.to match_array(tags_from_strings(%w(1 2 3))) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with empty array" do
|
context "with empty array" do
|
||||||
subject { generator.filter_since_tag(tags_mash_from_strings(%w())) }
|
subject { generator.filter_since_tag(tags_from_strings(%w())) }
|
||||||
|
|
||||||
context "with valid since tag" do
|
context "with valid since tag" do
|
||||||
let(:generator) { GitHubChangelogGenerator::Generator.new(since_tag: "2") }
|
let(:generator) { GitHubChangelogGenerator::Generator.new(since_tag: "2") }
|
||||||
it { is_expected.to be_a Array }
|
it { is_expected.to be_a Array }
|
||||||
it { is_expected.to match_array(tags_mash_from_strings(%w())) }
|
it { is_expected.to match_array(tags_from_strings(%w())) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with invalid since tag" do
|
context "with invalid since tag" do
|
||||||
let(:generator) { GitHubChangelogGenerator::Generator.new(since_tag: "Invalid tag") }
|
let(:generator) { GitHubChangelogGenerator::Generator.new(since_tag: "Invalid tag") }
|
||||||
it { is_expected.to be_a Array }
|
it { is_expected.to be_a Array }
|
||||||
it { is_expected.to match_array(tags_mash_from_strings(%w())) }
|
it { is_expected.to match_array(tags_from_strings(%w())) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#filter_due_tag" do
|
describe "#filter_due_tag" do
|
||||||
context "with filled array" do
|
context "with filled array" do
|
||||||
subject { generator.filter_due_tag(tags_mash_from_strings(%w(1 2 3))) }
|
subject { generator.filter_due_tag(tags_from_strings(%w(1 2 3))) }
|
||||||
|
|
||||||
context "with valid due tag" do
|
context "with valid due tag" do
|
||||||
let(:generator) { GitHubChangelogGenerator::Generator.new(due_tag: "2") }
|
let(:generator) { GitHubChangelogGenerator::Generator.new(due_tag: "2") }
|
||||||
it { is_expected.to be_a Array }
|
it { is_expected.to be_a Array }
|
||||||
it { is_expected.to match_array(tags_mash_from_strings(%w(3))) }
|
it { is_expected.to match_array(tags_from_strings(%w(3))) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with invalid due tag" do
|
context "with invalid due tag" do
|
||||||
let(:generator) { GitHubChangelogGenerator::Generator.new(due_tag: "Invalid tag") }
|
let(:generator) { GitHubChangelogGenerator::Generator.new(due_tag: "Invalid tag") }
|
||||||
it { is_expected.to be_a Array }
|
it { is_expected.to be_a Array }
|
||||||
it { is_expected.to match_array(tags_mash_from_strings(%w(1 2 3))) }
|
it { is_expected.to match_array(tags_from_strings(%w(1 2 3))) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with empty array" do
|
context "with empty array" do
|
||||||
subject { generator.filter_due_tag(tags_mash_from_strings(%w())) }
|
subject { generator.filter_due_tag(tags_from_strings(%w())) }
|
||||||
|
|
||||||
context "with valid due tag" do
|
context "with valid due tag" do
|
||||||
let(:generator) { GitHubChangelogGenerator::Generator.new(due_tag: "2") }
|
let(:generator) { GitHubChangelogGenerator::Generator.new(due_tag: "2") }
|
||||||
it { is_expected.to be_a Array }
|
it { is_expected.to be_a Array }
|
||||||
it { is_expected.to match_array(tags_mash_from_strings(%w())) }
|
it { is_expected.to match_array(tags_from_strings(%w())) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with invalid due tag" do
|
context "with invalid due tag" do
|
||||||
let(:generator) { GitHubChangelogGenerator::Generator.new(due_tag: "Invalid tag") }
|
let(:generator) { GitHubChangelogGenerator::Generator.new(due_tag: "Invalid tag") }
|
||||||
it { is_expected.to be_a Array }
|
it { is_expected.to be_a Array }
|
||||||
it { is_expected.to match_array(tags_mash_from_strings(%w())) }
|
it { is_expected.to match_array(tags_from_strings(%w())) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -190,7 +194,7 @@ describe GitHubChangelogGenerator::Generator do
|
|||||||
end
|
end
|
||||||
context "fetch already filled tag" do
|
context "fetch already filled tag" do
|
||||||
before { @generator.instance_variable_set :@tag_times_hash, "valid_tag" => current_time }
|
before { @generator.instance_variable_set :@tag_times_hash, "valid_tag" => current_time }
|
||||||
subject { @generator.get_time_of_tag tag_mash_with_name("valid_tag") }
|
subject { @generator.get_time_of_tag tag_with_name("valid_tag") }
|
||||||
it { is_expected.to be_a_kind_of(Time) }
|
it { is_expected.to be_a_kind_of(Time) }
|
||||||
it { is_expected.to eq(current_time) }
|
it { is_expected.to eq(current_time) }
|
||||||
end
|
end
|
||||||
@@ -201,7 +205,7 @@ describe GitHubChangelogGenerator::Generator do
|
|||||||
@generator.instance_variable_set :@fetcher, mock
|
@generator.instance_variable_set :@fetcher, mock
|
||||||
end
|
end
|
||||||
subject do
|
subject do
|
||||||
of_tag = @generator.get_time_of_tag(tag_mash_with_name("valid_tag"))
|
of_tag = @generator.get_time_of_tag(tag_with_name("valid_tag"))
|
||||||
of_tag
|
of_tag
|
||||||
end
|
end
|
||||||
it { is_expected.to be_a_kind_of(Time) }
|
it { is_expected.to be_a_kind_of(Time) }
|
||||||
@@ -228,13 +232,13 @@ describe GitHubChangelogGenerator::Generator do
|
|||||||
@generator.sort_tags_by_date(tags)
|
@generator.sort_tags_by_date(tags)
|
||||||
end
|
end
|
||||||
context "sort unsorted tags" do
|
context "sort unsorted tags" do
|
||||||
let(:tags) { tags_mash_from_strings %w(valid_tag1 valid_tag2 valid_tag3) }
|
let(:tags) { tags_from_strings %w(valid_tag1 valid_tag2 valid_tag3) }
|
||||||
|
|
||||||
it { is_expected.to be_a_kind_of(Array) }
|
it { is_expected.to be_a_kind_of(Array) }
|
||||||
it { is_expected.to match_array(tags.reverse!) }
|
it { is_expected.to match_array(tags.reverse!) }
|
||||||
end
|
end
|
||||||
context "sort sorted tags" do
|
context "sort sorted tags" do
|
||||||
let(:tags) { tags_mash_from_strings %w(valid_tag3 valid_tag2 valid_tag1) }
|
let(:tags) { tags_from_strings %w(valid_tag3 valid_tag2 valid_tag1) }
|
||||||
|
|
||||||
it { is_expected.to be_a_kind_of(Array) }
|
it { is_expected.to be_a_kind_of(Array) }
|
||||||
it { is_expected.to match_array(tags) }
|
it { is_expected.to match_array(tags) }
|
||||||
|
|||||||
@@ -0,0 +1,503 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
VALID_TOKEN = "0123456789abcdef"
|
||||||
|
INVALID_TOKEN = "0000000000000000"
|
||||||
|
|
||||||
|
describe GitHubChangelogGenerator::OctoFetcher do
|
||||||
|
let(:options) do
|
||||||
|
{
|
||||||
|
user: "skywinder",
|
||||||
|
project: "changelog_test"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:fetcher) { GitHubChangelogGenerator::OctoFetcher.new(options) }
|
||||||
|
|
||||||
|
describe "#fetch_github_token" do
|
||||||
|
token = GitHubChangelogGenerator::OctoFetcher::CHANGELOG_GITHUB_TOKEN
|
||||||
|
context "when token in ENV exist" do
|
||||||
|
before { stub_const("ENV", ENV.to_hash.merge(token => VALID_TOKEN)) }
|
||||||
|
subject { fetcher.send(:fetch_github_token) }
|
||||||
|
it { is_expected.to eq(VALID_TOKEN) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when token in ENV is nil" do
|
||||||
|
before { stub_const("ENV", ENV.to_hash.merge(token => nil)) }
|
||||||
|
subject { fetcher.send(:fetch_github_token) }
|
||||||
|
it { is_expected.to be_nil }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when token in options and ENV is nil" do
|
||||||
|
let(:options) { { token: VALID_TOKEN } }
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_const("ENV", ENV.to_hash.merge(token => nil))
|
||||||
|
end
|
||||||
|
|
||||||
|
subject { fetcher.send(:fetch_github_token) }
|
||||||
|
it { is_expected.to eq(VALID_TOKEN) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when token in options and ENV specified" do
|
||||||
|
let(:options) { { token: VALID_TOKEN } }
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_const("ENV", ENV.to_hash.merge(token => "no_matter_what"))
|
||||||
|
end
|
||||||
|
|
||||||
|
subject { fetcher.send(:fetch_github_token) }
|
||||||
|
it { is_expected.to eq(VALID_TOKEN) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#get_all_tags" do
|
||||||
|
context "when github_fetch_tags returns tags" do
|
||||||
|
it "returns tags" do
|
||||||
|
mock_tags = ["tag"]
|
||||||
|
allow(fetcher).to receive(:github_fetch_tags).and_return(mock_tags)
|
||||||
|
expect(fetcher.get_all_tags).to eq(mock_tags)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#github_fetch_tags" do
|
||||||
|
context "when wrong token provided", :vcr do
|
||||||
|
let(:options) do
|
||||||
|
{
|
||||||
|
user: "skywinder",
|
||||||
|
project: "changelog_test",
|
||||||
|
token: INVALID_TOKEN
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should raise Unauthorized error" do
|
||||||
|
expect { fetcher.github_fetch_tags }.to raise_error SystemExit, "Error: wrong GitHub token"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when API call is valid", :vcr do
|
||||||
|
it "should return tags" do
|
||||||
|
expected_tags = [{ "name" => "v0.0.3",
|
||||||
|
"zipball_url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/zipball/v0.0.3",
|
||||||
|
"tarball_url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/tarball/v0.0.3",
|
||||||
|
"commit" =>
|
||||||
|
{ "sha" => "a0cba2b1a1ea9011ab07ee1ac140ba5a5eb8bd90",
|
||||||
|
"url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/commits/a0cba2b1a1ea9011ab07ee1ac140ba5a5eb8bd90" } },
|
||||||
|
{ "name" => "v0.0.2",
|
||||||
|
"zipball_url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/zipball/v0.0.2",
|
||||||
|
"tarball_url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/tarball/v0.0.2",
|
||||||
|
"commit" =>
|
||||||
|
{ "sha" => "9b35bb13dcd15b68e7bcbf10cde5eb937a54f710",
|
||||||
|
"url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/commits/9b35bb13dcd15b68e7bcbf10cde5eb937a54f710" } },
|
||||||
|
{ "name" => "v0.0.1",
|
||||||
|
"zipball_url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/zipball/v0.0.1",
|
||||||
|
"tarball_url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/tarball/v0.0.1",
|
||||||
|
"commit" =>
|
||||||
|
{ "sha" => "4c2d6d1ed58bdb24b870dcb5d9f2ceed0283d69d",
|
||||||
|
"url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/commits/4c2d6d1ed58bdb24b870dcb5d9f2ceed0283d69d" } },
|
||||||
|
{ "name" => "0.0.4",
|
||||||
|
"zipball_url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/zipball/0.0.4",
|
||||||
|
"tarball_url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/tarball/0.0.4",
|
||||||
|
"commit" =>
|
||||||
|
{ "sha" => "ece0c3ab7142b21064b885061c55ede00ef6ce94",
|
||||||
|
"url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/commits/ece0c3ab7142b21064b885061c55ede00ef6ce94" } }]
|
||||||
|
|
||||||
|
expect(fetcher.github_fetch_tags).to eq(expected_tags)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return tags count" do
|
||||||
|
tags = fetcher.github_fetch_tags
|
||||||
|
expect(tags.size).to eq(4)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#fetch_closed_issues_and_pr" do
|
||||||
|
context "when API call is valid", :vcr do
|
||||||
|
it "returns issues" do
|
||||||
|
issues, pull_requests = fetcher.fetch_closed_issues_and_pr
|
||||||
|
expect(issues.size).to eq(7)
|
||||||
|
expect(pull_requests.size).to eq(14)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns issue with proper key/values" do
|
||||||
|
issues, _pull_requests = fetcher.fetch_closed_issues_and_pr
|
||||||
|
|
||||||
|
expected_issue = { "url" => "https://api.github.com/repos/skywinder/changelog_test/issues/14",
|
||||||
|
"repository_url" => "https://api.github.com/repos/skywinder/changelog_test",
|
||||||
|
"labels_url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/issues/14/labels{/name}",
|
||||||
|
"comments_url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/issues/14/comments",
|
||||||
|
"events_url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/issues/14/events",
|
||||||
|
"html_url" => "https://github.com/skywinder/changelog_test/issues/14",
|
||||||
|
"id" => 95_419_412,
|
||||||
|
"number" => 14,
|
||||||
|
"title" => "Issue closed from commit from PR",
|
||||||
|
"user" =>
|
||||||
|
{ "login" => "skywinder",
|
||||||
|
"id" => 3_356_474,
|
||||||
|
"avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3",
|
||||||
|
"gravatar_id" => "",
|
||||||
|
"url" => "https://api.github.com/users/skywinder",
|
||||||
|
"html_url" => "https://github.com/skywinder",
|
||||||
|
"followers_url" => "https://api.github.com/users/skywinder/followers",
|
||||||
|
"following_url" =>
|
||||||
|
"https://api.github.com/users/skywinder/following{/other_user}",
|
||||||
|
"gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}",
|
||||||
|
"starred_url" =>
|
||||||
|
"https://api.github.com/users/skywinder/starred{/owner}{/repo}",
|
||||||
|
"subscriptions_url" => "https://api.github.com/users/skywinder/subscriptions",
|
||||||
|
"organizations_url" => "https://api.github.com/users/skywinder/orgs",
|
||||||
|
"repos_url" => "https://api.github.com/users/skywinder/repos",
|
||||||
|
"events_url" => "https://api.github.com/users/skywinder/events{/privacy}",
|
||||||
|
"received_events_url" =>
|
||||||
|
"https://api.github.com/users/skywinder/received_events",
|
||||||
|
"type" => "User",
|
||||||
|
"site_admin" => false },
|
||||||
|
"labels" => [],
|
||||||
|
"state" => "closed",
|
||||||
|
"locked" => false,
|
||||||
|
"assignee" => nil,
|
||||||
|
"assignees" => [],
|
||||||
|
"milestone" => nil,
|
||||||
|
"comments" => 0,
|
||||||
|
"created_at" => "2015-07-16T12:06:08Z",
|
||||||
|
"updated_at" => "2015-07-16T12:21:42Z",
|
||||||
|
"closed_at" => "2015-07-16T12:21:42Z",
|
||||||
|
"body" => "" }
|
||||||
|
|
||||||
|
# Convert times to Time
|
||||||
|
expected_issue.each_pair do |k, v|
|
||||||
|
expected_issue[k] = Time.parse(v) if v =~ /^2015-/
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(issues.first).to eq(expected_issue)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns pull request with proper key/values" do
|
||||||
|
_issues, pull_requests = fetcher.fetch_closed_issues_and_pr
|
||||||
|
|
||||||
|
expected_pr = { "url" => "https://api.github.com/repos/skywinder/changelog_test/issues/21",
|
||||||
|
"repository_url" => "https://api.github.com/repos/skywinder/changelog_test",
|
||||||
|
"labels_url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/issues/21/labels{/name}",
|
||||||
|
"comments_url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/issues/21/comments",
|
||||||
|
"events_url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/issues/21/events",
|
||||||
|
"html_url" => "https://github.com/skywinder/changelog_test/pull/21",
|
||||||
|
"id" => 124_925_759,
|
||||||
|
"number" => 21,
|
||||||
|
"title" => "Merged br (should appear in change log with #20)",
|
||||||
|
"user" =>
|
||||||
|
{ "login" => "skywinder",
|
||||||
|
"id" => 3_356_474,
|
||||||
|
"avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3",
|
||||||
|
"gravatar_id" => "",
|
||||||
|
"url" => "https://api.github.com/users/skywinder",
|
||||||
|
"html_url" => "https://github.com/skywinder",
|
||||||
|
"followers_url" => "https://api.github.com/users/skywinder/followers",
|
||||||
|
"following_url" =>
|
||||||
|
"https://api.github.com/users/skywinder/following{/other_user}",
|
||||||
|
"gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}",
|
||||||
|
"starred_url" =>
|
||||||
|
"https://api.github.com/users/skywinder/starred{/owner}{/repo}",
|
||||||
|
"subscriptions_url" => "https://api.github.com/users/skywinder/subscriptions",
|
||||||
|
"organizations_url" => "https://api.github.com/users/skywinder/orgs",
|
||||||
|
"repos_url" => "https://api.github.com/users/skywinder/repos",
|
||||||
|
"events_url" => "https://api.github.com/users/skywinder/events{/privacy}",
|
||||||
|
"received_events_url" =>
|
||||||
|
"https://api.github.com/users/skywinder/received_events",
|
||||||
|
"type" => "User",
|
||||||
|
"site_admin" => false },
|
||||||
|
"labels" => [],
|
||||||
|
"state" => "closed",
|
||||||
|
"locked" => false,
|
||||||
|
"assignee" => nil,
|
||||||
|
"assignees" => [],
|
||||||
|
"milestone" => nil,
|
||||||
|
"comments" => 0,
|
||||||
|
"created_at" => "2016-01-05T09:24:08Z",
|
||||||
|
"updated_at" => "2016-01-05T09:26:53Z",
|
||||||
|
"closed_at" => "2016-01-05T09:24:27Z",
|
||||||
|
"pull_request" =>
|
||||||
|
{ "url" => "https://api.github.com/repos/skywinder/changelog_test/pulls/21",
|
||||||
|
"html_url" => "https://github.com/skywinder/changelog_test/pull/21",
|
||||||
|
"diff_url" => "https://github.com/skywinder/changelog_test/pull/21.diff",
|
||||||
|
"patch_url" => "https://github.com/skywinder/changelog_test/pull/21.patch" },
|
||||||
|
"body" =>
|
||||||
|
"to test https://github.com/skywinder/github-changelog-generator/pull/305\r\nshould appear in change log with #20" }
|
||||||
|
|
||||||
|
# Convert times to Time
|
||||||
|
expected_pr.each_pair do |k, v|
|
||||||
|
expected_pr[k] = Time.parse(v) if v =~ /^2016-01/
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(pull_requests.first).to eq(expected_pr)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns issues with labels" do
|
||||||
|
issues, _pull_requests = fetcher.fetch_closed_issues_and_pr
|
||||||
|
expected = [[], [], ["Bug"], [], ["enhancement"], ["some label"], []]
|
||||||
|
expect(issues.map { |i| i["labels"].map { |l| l["name"] } }).to eq(expected)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns pull_requests with labels" do
|
||||||
|
_issues, pull_requests = fetcher.fetch_closed_issues_and_pr
|
||||||
|
expected = [[], [], [], [], [], ["enhancement"], [], [], ["invalid"], [], [], [], [], ["invalid"]]
|
||||||
|
expect(pull_requests.map { |i| i["labels"].map { |l| l["name"] } }).to eq(expected)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#fetch_closed_pull_requests" do
|
||||||
|
context "when API call is valid", :vcr do
|
||||||
|
it "returns pull requests" do
|
||||||
|
pull_requests = fetcher.fetch_closed_pull_requests
|
||||||
|
expect(pull_requests.size).to eq(14)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns correct pull request keys" do
|
||||||
|
pull_requests = fetcher.fetch_closed_pull_requests
|
||||||
|
|
||||||
|
pr = pull_requests.first
|
||||||
|
expect(pr.keys).to eq(%w(url id html_url diff_url patch_url issue_url number state locked title user body created_at updated_at closed_at merged_at merge_commit_sha assignee assignees milestone commits_url review_comments_url review_comment_url comments_url statuses_url head base _links))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#fetch_events_async" do
|
||||||
|
context "when API call is valid", :vcr do
|
||||||
|
it "populates issues" do
|
||||||
|
issues = [{ "url" => "https://api.github.com/repos/skywinder/changelog_test/issues/14",
|
||||||
|
"repository_url" => "https://api.github.com/repos/skywinder/changelog_test",
|
||||||
|
"labels_url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/issues/14/labels{/name}",
|
||||||
|
"comments_url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/issues/14/comments",
|
||||||
|
"events_url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/issues/14/events",
|
||||||
|
"html_url" => "https://github.com/skywinder/changelog_test/issues/14",
|
||||||
|
"id" => 95_419_412,
|
||||||
|
"number" => 14,
|
||||||
|
"title" => "Issue closed from commit from PR",
|
||||||
|
"user" =>
|
||||||
|
{ "login" => "skywinder",
|
||||||
|
"id" => 3_356_474,
|
||||||
|
"avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3",
|
||||||
|
"gravatar_id" => "",
|
||||||
|
"url" => "https://api.github.com/users/skywinder",
|
||||||
|
"html_url" => "https://github.com/skywinder",
|
||||||
|
"followers_url" => "https://api.github.com/users/skywinder/followers",
|
||||||
|
"following_url" =>
|
||||||
|
"https://api.github.com/users/skywinder/following{/other_user}",
|
||||||
|
"gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}",
|
||||||
|
"starred_url" =>
|
||||||
|
"https://api.github.com/users/skywinder/starred{/owner}{/repo}",
|
||||||
|
"subscriptions_url" =>
|
||||||
|
"https://api.github.com/users/skywinder/subscriptions",
|
||||||
|
"organizations_url" => "https://api.github.com/users/skywinder/orgs",
|
||||||
|
"repos_url" => "https://api.github.com/users/skywinder/repos",
|
||||||
|
"events_url" => "https://api.github.com/users/skywinder/events{/privacy}",
|
||||||
|
"received_events_url" =>
|
||||||
|
"https://api.github.com/users/skywinder/received_events",
|
||||||
|
"type" => "User",
|
||||||
|
"site_admin" => false },
|
||||||
|
"labels" => [],
|
||||||
|
"state" => "closed",
|
||||||
|
"locked" => false,
|
||||||
|
"assignee" => nil,
|
||||||
|
"assignees" => [],
|
||||||
|
"milestone" => nil,
|
||||||
|
"comments" => 0,
|
||||||
|
"created_at" => "2015-07-16T12:06:08Z",
|
||||||
|
"updated_at" => "2015-07-16T12:21:42Z",
|
||||||
|
"closed_at" => "2015-07-16T12:21:42Z",
|
||||||
|
"body" => "" }]
|
||||||
|
|
||||||
|
# Check that they are blank to begin with
|
||||||
|
expect(issues.first["events"]).to be_nil
|
||||||
|
|
||||||
|
fetcher.fetch_events_async(issues)
|
||||||
|
issue_events = issues.first["events"]
|
||||||
|
|
||||||
|
expected_events = [{ "id" => 357_462_189,
|
||||||
|
"url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/issues/events/357462189",
|
||||||
|
"actor" =>
|
||||||
|
{ "login" => "skywinder",
|
||||||
|
"id" => 3_356_474,
|
||||||
|
"avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3",
|
||||||
|
"gravatar_id" => "",
|
||||||
|
"url" => "https://api.github.com/users/skywinder",
|
||||||
|
"html_url" => "https://github.com/skywinder",
|
||||||
|
"followers_url" => "https://api.github.com/users/skywinder/followers",
|
||||||
|
"following_url" =>
|
||||||
|
"https://api.github.com/users/skywinder/following{/other_user}",
|
||||||
|
"gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}",
|
||||||
|
"starred_url" =>
|
||||||
|
"https://api.github.com/users/skywinder/starred{/owner}{/repo}",
|
||||||
|
"subscriptions_url" =>
|
||||||
|
"https://api.github.com/users/skywinder/subscriptions",
|
||||||
|
"organizations_url" => "https://api.github.com/users/skywinder/orgs",
|
||||||
|
"repos_url" => "https://api.github.com/users/skywinder/repos",
|
||||||
|
"events_url" => "https://api.github.com/users/skywinder/events{/privacy}",
|
||||||
|
"received_events_url" =>
|
||||||
|
"https://api.github.com/users/skywinder/received_events",
|
||||||
|
"type" => "User",
|
||||||
|
"site_admin" => false },
|
||||||
|
"event" => "referenced",
|
||||||
|
"commit_id" => "decfe840d1a1b86e0c28700de5362d3365a29555",
|
||||||
|
"commit_url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/commits/decfe840d1a1b86e0c28700de5362d3365a29555",
|
||||||
|
"created_at" => "2015-07-16T12:21:16Z" },
|
||||||
|
{ "id" => 357_462_542,
|
||||||
|
"url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/issues/events/357462542",
|
||||||
|
"actor" =>
|
||||||
|
{ "login" => "skywinder",
|
||||||
|
"id" => 3_356_474,
|
||||||
|
"avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3",
|
||||||
|
"gravatar_id" => "",
|
||||||
|
"url" => "https://api.github.com/users/skywinder",
|
||||||
|
"html_url" => "https://github.com/skywinder",
|
||||||
|
"followers_url" => "https://api.github.com/users/skywinder/followers",
|
||||||
|
"following_url" =>
|
||||||
|
"https://api.github.com/users/skywinder/following{/other_user}",
|
||||||
|
"gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}",
|
||||||
|
"starred_url" =>
|
||||||
|
"https://api.github.com/users/skywinder/starred{/owner}{/repo}",
|
||||||
|
"subscriptions_url" =>
|
||||||
|
"https://api.github.com/users/skywinder/subscriptions",
|
||||||
|
"organizations_url" => "https://api.github.com/users/skywinder/orgs",
|
||||||
|
"repos_url" => "https://api.github.com/users/skywinder/repos",
|
||||||
|
"events_url" => "https://api.github.com/users/skywinder/events{/privacy}",
|
||||||
|
"received_events_url" =>
|
||||||
|
"https://api.github.com/users/skywinder/received_events",
|
||||||
|
"type" => "User",
|
||||||
|
"site_admin" => false },
|
||||||
|
"event" => "closed",
|
||||||
|
"commit_id" => "decfe840d1a1b86e0c28700de5362d3365a29555",
|
||||||
|
"commit_url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/commits/decfe840d1a1b86e0c28700de5362d3365a29555",
|
||||||
|
"created_at" => "2015-07-16T12:21:42Z" }]
|
||||||
|
|
||||||
|
# Convert times to Time
|
||||||
|
expected_events.map! do |event|
|
||||||
|
event.each_pair do |k, v|
|
||||||
|
event[k] = Time.parse(v) if v =~ /^201[56]-/
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(issue_events).to eq(expected_events)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#fetch_date_of_tag" do
|
||||||
|
context "when API call is valid", :vcr do
|
||||||
|
it "returns date" do
|
||||||
|
tag = { "name" => "v0.0.3",
|
||||||
|
"zipball_url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/zipball/v0.0.3",
|
||||||
|
"tarball_url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/tarball/v0.0.3",
|
||||||
|
"commit" =>
|
||||||
|
{ "sha" => "a0cba2b1a1ea9011ab07ee1ac140ba5a5eb8bd90",
|
||||||
|
"url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/commits/a0cba2b1a1ea9011ab07ee1ac140ba5a5eb8bd90" } }
|
||||||
|
|
||||||
|
dt = fetcher.fetch_date_of_tag(tag)
|
||||||
|
expect(dt).to eq(Time.parse("2015-03-04 19:01:48 UTC"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#querystring_as_hash" do
|
||||||
|
it "works on the blank URL" do
|
||||||
|
expect { fetcher.send(:querystring_as_hash, "") }.not_to raise_error
|
||||||
|
end
|
||||||
|
|
||||||
|
it "where there are no querystring params" do
|
||||||
|
expect { fetcher.send(:querystring_as_hash, "http://example.com") }.not_to raise_error
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns a String-keyed Hash of querystring params" do
|
||||||
|
expect(fetcher.send(:querystring_as_hash, "http://example.com/o.html?str=18&wis=12")).to include("wis" => "12", "str" => "18")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#fetch_commit" do
|
||||||
|
context "when API call is valid", :vcr do
|
||||||
|
it "returns commit" do
|
||||||
|
event = { "id" => 357_462_189,
|
||||||
|
"url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/issues/events/357462189",
|
||||||
|
"actor" =>
|
||||||
|
{ "login" => "skywinder",
|
||||||
|
"id" => 3_356_474,
|
||||||
|
"avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3",
|
||||||
|
"gravatar_id" => "",
|
||||||
|
"url" => "https://api.github.com/users/skywinder",
|
||||||
|
"html_url" => "https://github.com/skywinder",
|
||||||
|
"followers_url" => "https://api.github.com/users/skywinder/followers",
|
||||||
|
"following_url" =>
|
||||||
|
"https://api.github.com/users/skywinder/following{/other_user}",
|
||||||
|
"gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}",
|
||||||
|
"starred_url" =>
|
||||||
|
"https://api.github.com/users/skywinder/starred{/owner}{/repo}",
|
||||||
|
"subscriptions_url" => "https://api.github.com/users/skywinder/subscriptions",
|
||||||
|
"organizations_url" => "https://api.github.com/users/skywinder/orgs",
|
||||||
|
"repos_url" => "https://api.github.com/users/skywinder/repos",
|
||||||
|
"events_url" => "https://api.github.com/users/skywinder/events{/privacy}",
|
||||||
|
"received_events_url" =>
|
||||||
|
"https://api.github.com/users/skywinder/received_events",
|
||||||
|
"type" => "User",
|
||||||
|
"site_admin" => false },
|
||||||
|
"event" => "referenced",
|
||||||
|
"commit_id" => "decfe840d1a1b86e0c28700de5362d3365a29555",
|
||||||
|
"commit_url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/commits/decfe840d1a1b86e0c28700de5362d3365a29555",
|
||||||
|
"created_at" => "2015-07-16T12:21:16Z" }
|
||||||
|
commit = fetcher.fetch_commit(event)
|
||||||
|
|
||||||
|
expectations = [
|
||||||
|
%w(sha decfe840d1a1b86e0c28700de5362d3365a29555),
|
||||||
|
["url",
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/commits/decfe840d1a1b86e0c28700de5362d3365a29555"],
|
||||||
|
# OLD API: "https://api.github.com/repos/skywinder/changelog_test/git/commits/decfe840d1a1b86e0c28700de5362d3365a29555"],
|
||||||
|
["html_url",
|
||||||
|
"https://github.com/skywinder/changelog_test/commit/decfe840d1a1b86e0c28700de5362d3365a29555"],
|
||||||
|
["author",
|
||||||
|
{ "login" => "skywinder", "id" => 3_356_474, "avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3", "gravatar_id" => "", "url" => "https://api.github.com/users/skywinder", "html_url" => "https://github.com/skywinder", "followers_url" => "https://api.github.com/users/skywinder/followers", "following_url" => "https://api.github.com/users/skywinder/following{/other_user}", "gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}", "starred_url" => "https://api.github.com/users/skywinder/starred{/owner}{/repo}", "subscriptions_url" => "https://api.github.com/users/skywinder/subscriptions", "organizations_url" => "https://api.github.com/users/skywinder/orgs", "repos_url" => "https://api.github.com/users/skywinder/repos", "events_url" => "https://api.github.com/users/skywinder/events{/privacy}", "received_events_url" => "https://api.github.com/users/skywinder/received_events", "type" => "User", "site_admin" => false }],
|
||||||
|
["committer",
|
||||||
|
{ "login" => "skywinder", "id" => 3_356_474, "avatar_url" => "https://avatars.githubusercontent.com/u/3356474?v=3", "gravatar_id" => "", "url" => "https://api.github.com/users/skywinder", "html_url" => "https://github.com/skywinder", "followers_url" => "https://api.github.com/users/skywinder/followers", "following_url" => "https://api.github.com/users/skywinder/following{/other_user}", "gists_url" => "https://api.github.com/users/skywinder/gists{/gist_id}", "starred_url" => "https://api.github.com/users/skywinder/starred{/owner}{/repo}", "subscriptions_url" => "https://api.github.com/users/skywinder/subscriptions", "organizations_url" => "https://api.github.com/users/skywinder/orgs", "repos_url" => "https://api.github.com/users/skywinder/repos", "events_url" => "https://api.github.com/users/skywinder/events{/privacy}", "received_events_url" => "https://api.github.com/users/skywinder/received_events", "type" => "User", "site_admin" => false }],
|
||||||
|
["parents",
|
||||||
|
[{ "sha" => "7ec095e5e3caceacedabf44d0b9b10da17c92e51",
|
||||||
|
"url" =>
|
||||||
|
"https://api.github.com/repos/skywinder/changelog_test/commits/7ec095e5e3caceacedabf44d0b9b10da17c92e51",
|
||||||
|
# OLD API: "https://api.github.com/repos/skywinder/changelog_test/git/commits/7ec095e5e3caceacedabf44d0b9b10da17c92e51",
|
||||||
|
"html_url" =>
|
||||||
|
"https://github.com/skywinder/changelog_test/commit/7ec095e5e3caceacedabf44d0b9b10da17c92e51" }]]
|
||||||
|
]
|
||||||
|
|
||||||
|
expectations.each do |property, val|
|
||||||
|
expect(commit[property]).to eq(val)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
RSpec.describe GitHubChangelogGenerator::Options do
|
||||||
|
describe "#initialize" do
|
||||||
|
context "with known options" do
|
||||||
|
it "instantiates successfully" do
|
||||||
|
expect(described_class.new(user: "olle")[:user]).to eq("olle")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with unknown options" do
|
||||||
|
it "raises an error" do
|
||||||
|
expect do
|
||||||
|
described_class.new(
|
||||||
|
git_remote: "origin",
|
||||||
|
strength: "super-strength",
|
||||||
|
wisdom: "deep"
|
||||||
|
)
|
||||||
|
end.to raise_error("[:strength, :wisdom]")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#[]=(key, value)" do
|
||||||
|
let(:options) { described_class.new(git_remote: "origin") }
|
||||||
|
|
||||||
|
context "with known options" do
|
||||||
|
it "sets the option value" do
|
||||||
|
expect do
|
||||||
|
options[:git_remote] = "in the cloud"
|
||||||
|
end.to change { options[:git_remote] }.to "in the cloud"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with unknown options" do
|
||||||
|
it "raises an error" do
|
||||||
|
expect do
|
||||||
|
options[:charisma] = 8
|
||||||
|
end.to raise_error(":charisma")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
describe GitHubChangelogGenerator::ParserFile do
|
describe GitHubChangelogGenerator::ParserFile do
|
||||||
describe ".github_changelog_generator" do
|
describe ".github_changelog_generator" do
|
||||||
let(:options) { {} }
|
let(:options) { {} }
|
||||||
@@ -48,7 +49,7 @@ describe GitHubChangelogGenerator::ParserFile do
|
|||||||
header: "=== Changelog ==="))
|
header: "=== Changelog ==="))
|
||||||
end
|
end
|
||||||
|
|
||||||
context "turns exclude-labels into an Array", bug: '#327' do
|
context "turns exclude-labels into an Array", bug: "#327" do
|
||||||
let(:file) do
|
let(:file) do
|
||||||
StringIO.new(<<EOF
|
StringIO.new(<<EOF
|
||||||
exclude-labels=73a91042-da6f-11e5-9335-1040f38d7f90,7adf83b4-da6f-11e5-ae18-1040f38d7f90
|
exclude-labels=73a91042-da6f-11e5-9335-1040f38d7f90,7adf83b4-da6f-11e5-ae18-1040f38d7f90
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
describe GitHubChangelogGenerator::Parser do
|
describe GitHubChangelogGenerator::Parser do
|
||||||
describe ".user_project_from_remote" do
|
describe ".user_project_from_remote" do
|
||||||
context "when remote is type 1" do
|
context "when remote is type 1" do
|
||||||
@@ -56,5 +57,26 @@ describe GitHubChangelogGenerator::Parser do
|
|||||||
it { is_expected.to be_a(Array) }
|
it { is_expected.to be_a(Array) }
|
||||||
it { is_expected.to match_array([nil, nil]) }
|
it { is_expected.to match_array([nil, nil]) }
|
||||||
end
|
end
|
||||||
|
context "when all args is not nil" do
|
||||||
|
subject { GitHubChangelogGenerator::Parser.user_project_from_option("skywinder/ActionSheetPicker-3.0", "blah", "https://codeclimate.com") }
|
||||||
|
it { is_expected.to be_a(Array) }
|
||||||
|
it { is_expected.to match_array([nil, nil]) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
describe ".fetch_user_and_project" do
|
||||||
|
before do
|
||||||
|
stub_const("ARGV", ["https://github.com/skywinder/github-changelog-generator"])
|
||||||
|
end
|
||||||
|
|
||||||
|
context do
|
||||||
|
let(:valid_user) { "initialized_user" }
|
||||||
|
let(:options) { { user: valid_user } }
|
||||||
|
let(:options_before_change) { options.dup }
|
||||||
|
it "should leave user unchanged" do
|
||||||
|
expect { GitHubChangelogGenerator::Parser.fetch_user_and_project(options) }.to change { options }
|
||||||
|
.from(options_before_change)
|
||||||
|
.to(options_before_change.merge(project: "github-changelog-generator"))
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
#
|
#
|
||||||
# Author:: Enrico Stahn <mail@enricostahn.com>
|
# Author:: Enrico Stahn <mail@enricostahn.com>
|
||||||
#
|
#
|
||||||
@@ -84,10 +85,6 @@ describe GitHubChangelogGenerator::Reader do
|
|||||||
context "angular.js.md" do
|
context "angular.js.md" do
|
||||||
it { is_expected.to be_an(Array) }
|
it { is_expected.to be_an(Array) }
|
||||||
it { is_expected.not_to be_empty }
|
it { is_expected.not_to be_empty }
|
||||||
it do
|
|
||||||
pending("Implement heading_level for parser.")
|
|
||||||
expect(subject.count).to eq(134)
|
|
||||||
end
|
|
||||||
# it do
|
# it do
|
||||||
# pending('Implement heading_level for parser.')
|
# pending('Implement heading_level for parser.')
|
||||||
# expect(subject.first).to include('version' => '1.4.0-beta.6 cookie-liberation')
|
# expect(subject.first).to include('version' => '1.4.0-beta.6 cookie-liberation')
|
||||||
|
|||||||
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
@@ -0,0 +1 @@
|
|||||||
|
{"http_interactions":[{"request":{"method":"get","uri":"https://api.github.com/repos/skywinder/changelog_test/issues/14/events?per_page=100","body":{"encoding":"US-ASCII","base64_string":""},"headers":{"Accept":["application/vnd.github.v3+json"],"User-Agent":["Octokit Ruby Gem 4.3.0"],"Content-Type":["application/json"],"Accept-Encoding":["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"Server":["GitHub.com"],"Date":["Fri, 20 May 2016 06:13:06 GMT"],"Content-Type":["application/json; charset=utf-8"],"Transfer-Encoding":["chunked"],"Status":["200 OK"],"X-Ratelimit-Limit":["60"],"X-Ratelimit-Remaining":["37"],"X-Ratelimit-Reset":["1463724861"],"Cache-Control":["public, max-age=60, s-maxage=60"],"Vary":["Accept","Accept-Encoding"],"Etag":["W/\"4d408c4e8053c706d7255563141ccb80\""],"X-Github-Media-Type":["github.v3; format=json"],"Access-Control-Expose-Headers":["ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval"],"Access-Control-Allow-Origin":["*"],"Content-Security-Policy":["default-src 'none'"],"Strict-Transport-Security":["max-age=31536000; includeSubdomains; preload"],"X-Content-Type-Options":["nosniff"],"X-Frame-Options":["deny"],"X-Xss-Protection":["1; mode=block"],"X-Served-By":["2d7a5e35115884240089368322196939"],"X-Github-Request-Id":["6C2F0F69:7F8B:2084B01:573EAAF2"]},"body":{"encoding":"ASCII-8BIT","base64_string":"W3siaWQiOjM1NzQ2MjE4OSwidXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNv\nbS9yZXBvcy9za3l3aW5kZXIvY2hhbmdlbG9nX3Rlc3QvaXNzdWVzL2V2ZW50\ncy8zNTc0NjIxODkiLCJhY3RvciI6eyJsb2dpbiI6InNreXdpbmRlciIsImlk\nIjozMzU2NDc0LCJhdmF0YXJfdXJsIjoiaHR0cHM6Ly9hdmF0YXJzLmdpdGh1\nYnVzZXJjb250ZW50LmNvbS91LzMzNTY0NzQ/dj0zIiwiZ3JhdmF0YXJfaWQi\nOiIiLCJ1cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3VzZXJzL3NreXdp\nbmRlciIsImh0bWxfdXJsIjoiaHR0cHM6Ly9naXRodWIuY29tL3NreXdpbmRl\nciIsImZvbGxvd2Vyc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3Vz\nZXJzL3NreXdpbmRlci9mb2xsb3dlcnMiLCJmb2xsb3dpbmdfdXJsIjoiaHR0\ncHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9za3l3aW5kZXIvZm9sbG93aW5n\ney9vdGhlcl91c2VyfSIsImdpc3RzX3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1\nYi5jb20vdXNlcnMvc2t5d2luZGVyL2dpc3Rzey9naXN0X2lkfSIsInN0YXJy\nZWRfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9za3l3aW5k\nZXIvc3RhcnJlZHsvb3duZXJ9ey9yZXBvfSIsInN1YnNjcmlwdGlvbnNfdXJs\nIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9za3l3aW5kZXIvc3Vi\nc2NyaXB0aW9ucyIsIm9yZ2FuaXphdGlvbnNfdXJsIjoiaHR0cHM6Ly9hcGku\nZ2l0aHViLmNvbS91c2Vycy9za3l3aW5kZXIvb3JncyIsInJlcG9zX3VybCI6\nImh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vdXNlcnMvc2t5d2luZGVyL3JlcG9z\nIiwiZXZlbnRzX3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vdXNlcnMv\nc2t5d2luZGVyL2V2ZW50c3svcHJpdmFjeX0iLCJyZWNlaXZlZF9ldmVudHNf\ndXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9za3l3aW5kZXIv\ncmVjZWl2ZWRfZXZlbnRzIiwidHlwZSI6IlVzZXIiLCJzaXRlX2FkbWluIjpm\nYWxzZX0sImV2ZW50IjoicmVmZXJlbmNlZCIsImNvbW1pdF9pZCI6ImRlY2Zl\nODQwZDFhMWI4NmUwYzI4NzAwZGU1MzYyZDMzNjVhMjk1NTUiLCJjb21taXRf\ndXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9za3l3aW5kZXIv\nY2hhbmdlbG9nX3Rlc3QvY29tbWl0cy9kZWNmZTg0MGQxYTFiODZlMGMyODcw\nMGRlNTM2MmQzMzY1YTI5NTU1IiwiY3JlYXRlZF9hdCI6IjIwMTUtMDctMTZU\nMTI6MjE6MTZaIn0seyJpZCI6MzU3NDYyNTQyLCJ1cmwiOiJodHRwczovL2Fw\naS5naXRodWIuY29tL3JlcG9zL3NreXdpbmRlci9jaGFuZ2Vsb2dfdGVzdC9p\nc3N1ZXMvZXZlbnRzLzM1NzQ2MjU0MiIsImFjdG9yIjp7ImxvZ2luIjoic2t5\nd2luZGVyIiwiaWQiOjMzNTY0NzQsImF2YXRhcl91cmwiOiJodHRwczovL2F2\nYXRhcnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3UvMzM1NjQ3ND92PTMiLCJn\ncmF2YXRhcl9pZCI6IiIsInVybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20v\ndXNlcnMvc2t5d2luZGVyIiwiaHRtbF91cmwiOiJodHRwczovL2dpdGh1Yi5j\nb20vc2t5d2luZGVyIiwiZm9sbG93ZXJzX3VybCI6Imh0dHBzOi8vYXBpLmdp\ndGh1Yi5jb20vdXNlcnMvc2t5d2luZGVyL2ZvbGxvd2VycyIsImZvbGxvd2lu\nZ191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3VzZXJzL3NreXdpbmRl\nci9mb2xsb3dpbmd7L290aGVyX3VzZXJ9IiwiZ2lzdHNfdXJsIjoiaHR0cHM6\nLy9hcGkuZ2l0aHViLmNvbS91c2Vycy9za3l3aW5kZXIvZ2lzdHN7L2dpc3Rf\naWR9Iiwic3RhcnJlZF91cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3Vz\nZXJzL3NreXdpbmRlci9zdGFycmVkey9vd25lcn17L3JlcG99Iiwic3Vic2Ny\naXB0aW9uc191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3VzZXJzL3Nr\neXdpbmRlci9zdWJzY3JpcHRpb25zIiwib3JnYW5pemF0aW9uc191cmwiOiJo\ndHRwczovL2FwaS5naXRodWIuY29tL3VzZXJzL3NreXdpbmRlci9vcmdzIiwi\ncmVwb3NfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9za3l3\naW5kZXIvcmVwb3MiLCJldmVudHNfdXJsIjoiaHR0cHM6Ly9hcGkuZ2l0aHVi\nLmNvbS91c2Vycy9za3l3aW5kZXIvZXZlbnRzey9wcml2YWN5fSIsInJlY2Vp\ndmVkX2V2ZW50c191cmwiOiJodHRwczovL2FwaS5naXRodWIuY29tL3VzZXJz\nL3NreXdpbmRlci9yZWNlaXZlZF9ldmVudHMiLCJ0eXBlIjoiVXNlciIsInNp\ndGVfYWRtaW4iOmZhbHNlfSwiZXZlbnQiOiJjbG9zZWQiLCJjb21taXRfaWQi\nOiJkZWNmZTg0MGQxYTFiODZlMGMyODcwMGRlNTM2MmQzMzY1YTI5NTU1Iiwi\nY29tbWl0X3VybCI6Imh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vcmVwb3Mvc2t5\nd2luZGVyL2NoYW5nZWxvZ190ZXN0L2NvbW1pdHMvZGVjZmU4NDBkMWExYjg2\nZTBjMjg3MDBkZTUzNjJkMzM2NWEyOTU1NSIsImNyZWF0ZWRfYXQiOiIyMDE1\nLTA3LTE2VDEyOjIxOjQyWiJ9XQ==\n"},"http_version":null},"recorded_at":"Fri, 20 May 2016 06:13:10 GMT"}],"recorded_with":"VCR 3.0.1"}
|
||||||
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
@@ -0,0 +1 @@
|
|||||||
|
{"http_interactions":[{"request":{"method":"get","uri":"https://api.github.com/repos/skywinder/changelog_test/tags?per_page=100","body":{"encoding":"US-ASCII","base64_string":""},"headers":{"Accept":["application/vnd.github.v3+json"],"User-Agent":["Octokit Ruby Gem 4.3.0"],"Content-Type":["application/json"],"Authorization":["token 0000000000000000"],"Accept-Encoding":["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"]}},"response":{"status":{"code":401,"message":"Unauthorized"},"headers":{"Server":["GitHub.com"],"Date":["Fri, 20 May 2016 05:47:03 GMT"],"Content-Type":["application/json; charset=utf-8"],"Content-Length":["83"],"Status":["401 Unauthorized"],"X-Github-Media-Type":["github.v3; format=json"],"X-Ratelimit-Limit":["60"],"X-Ratelimit-Remaining":["42"],"X-Ratelimit-Reset":["1463724861"],"Access-Control-Expose-Headers":["ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval"],"Access-Control-Allow-Origin":["*"],"Content-Security-Policy":["default-src 'none'"],"Strict-Transport-Security":["max-age=31536000; includeSubdomains; preload"],"X-Content-Type-Options":["nosniff"],"X-Frame-Options":["deny"],"X-Xss-Protection":["1; mode=block"],"X-Github-Request-Id":["6C2F0F69:1271A:B8980B7:573EA4D6"]},"body":{"encoding":"UTF-8","base64_string":"eyJtZXNzYWdlIjoiQmFkIGNyZWRlbnRpYWxzIiwiZG9jdW1lbnRhdGlvbl91\ncmwiOiJodHRwczovL2RldmVsb3Blci5naXRodWIuY29tL3YzIn0=\n"},"http_version":null},"recorded_at":"Fri, 20 May 2016 05:47:07 GMT"}],"recorded_with":"VCR 3.0.1"}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"http_interactions":[{"request":{"method":"get","uri":"https://api.github.com/repos/skywinder/changelog_test/tags?per_page=100","body":{"encoding":"US-ASCII","base64_string":""},"headers":{"Accept":["application/vnd.github.v3+json"],"User-Agent":["Octokit Ruby Gem 4.3.0"],"Content-Type":["application/json"],"Authorization":["token 0000000000000000"],"Accept-Encoding":["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"]}},"response":{"status":{"code":401,"message":"Unauthorized"},"headers":{"Server":["GitHub.com"],"Date":["Sat, 02 Jul 2016 14:13:57 GMT"],"Content-Type":["application/json; charset=utf-8"],"Content-Length":["83"],"Status":["401 Unauthorized"],"X-Github-Media-Type":["github.v3; format=json"],"X-Ratelimit-Limit":["60"],"X-Ratelimit-Remaining":["59"],"X-Ratelimit-Reset":["1467472437"],"Access-Control-Expose-Headers":["ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval"],"Access-Control-Allow-Origin":["*"],"Content-Security-Policy":["default-src 'none'"],"Strict-Transport-Security":["max-age=31536000; includeSubdomains; preload"],"X-Content-Type-Options":["nosniff"],"X-Frame-Options":["deny"],"X-Xss-Protection":["1; mode=block"],"X-Github-Request-Id":["54DBAB83:300E:76DBC85:5777CC25"]},"body":{"encoding":"UTF-8","base64_string":"eyJtZXNzYWdlIjoiQmFkIGNyZWRlbnRpYWxzIiwiZG9jdW1lbnRhdGlvbl91\ncmwiOiJodHRwczovL2RldmVsb3Blci5naXRodWIuY29tL3YzIn0=\n"},"http_version":null},"recorded_at":"Sat, 02 Jul 2016 14:13:58 GMT"}],"recorded_with":"VCR 3.0.1"}
|
||||||
Reference in New Issue
Block a user