Merge branch 'develop' into feature/fetcher

Conflicts:
	lib/github_changelog_generator.rb
This commit is contained in:
Petr Korolev 2015-04-03 18:40:11 +03:00
commit ce7f35777b
15 changed files with 192 additions and 192 deletions

View File

@ -4,5 +4,5 @@ Metrics/LineLength:
Enabled: false Enabled: false
#http://viget.com/extend/just-use-double-quoted-ruby-strings #http://viget.com/extend/just-use-double-quoted-ruby-strings
#Style/StringLiterals: Style/StringLiterals:
# EnforcedStyle: double_quotes EnforcedStyle: double_quotes

18
Gemfile
View File

@ -1,14 +1,14 @@
source 'https://rubygems.org' source "https://rubygems.org"
gem 'rake' gem "rake"
gem 'github_api' gem "github_api"
gem 'colorize' gem "colorize"
group :test do group :test do
gem 'rspec' gem "rspec"
gem 'rubocop' gem "rubocop"
gem 'coveralls', require: false gem "coveralls", require: false
gem 'simplecov', require: false gem "simplecov", require: false
gem 'codeclimate-test-reporter' gem "codeclimate-test-reporter"
end end

View File

@ -1,9 +1,9 @@
[![Gem Version](https://badge.fury.io/rb/github_changelog_generator.svg)](http://badge.fury.io/rb/github_changelog_generator) [![Gem Version](https://badge.fury.io/rb/github_changelog_generator.svg)](http://badge.fury.io/rb/github_changelog_generator)
[![Dependency Status](https://gemnasium.com/skywinder/github-changelog-generator.svg)](https://gemnasium.com/skywinder/github-changelog-generator) [![Dependency Status](https://gemnasium.com/skywinder/github-changelog-generator.svg)](https://gemnasium.com/skywinder/github-changelog-generator)
[![Build Status](https://travis-ci.org/skywinder/github-changelog-generator.svg?branch=master)](https://travis-ci.org/skywinder/github-changelog-generator) [![Build Status](https://travis-ci.org/skywinder/github-changelog-generator.svg?branch=master)](https://travis-ci.org/skywinder/github-changelog-generator)
[![Coverage Status](http://img.shields.io/coveralls/skywinder/github-changelog-generator/master.svg)](https://coveralls.io/r/skywinder/github-changelog-generator)
[![Code Climate](https://codeclimate.com/github/skywinder/github-changelog-generator/badges/gpa.svg)](https://codeclimate.com/github/skywinder/github-changelog-generator)
[![Inline docs](http://inch-ci.org/github/skywinder/github-changelog-generator.svg)](http://inch-ci.org/github/skywinder/github-changelog-generator) [![Inline docs](http://inch-ci.org/github/skywinder/github-changelog-generator.svg)](http://inch-ci.org/github/skywinder/github-changelog-generator)
[![Code Climate](https://codeclimate.com/github/skywinder/github-changelog-generator/badges/gpa.svg)](https://codeclimate.com/github/skywinder/github-changelog-generator)
[![Test Coverage](https://codeclimate.com/github/skywinder/github-changelog-generator/badges/coverage.svg)](https://codeclimate.com/github/skywinder/github-changelog-generator)
GitHub Changelog Generator ![GitHub Logo](../master/images/logo.jpg) GitHub Changelog Generator ![GitHub Logo](../master/images/logo.jpg)
================== ==================

View File

@ -1,5 +1,5 @@
require 'rubocop/rake_task' require "rubocop/rake_task"
require 'rspec/core/rake_task' require "rspec/core/rake_task"
RuboCop::RakeTask.new RuboCop::RakeTask.new
RSpec::Core::RakeTask.new(:rspec) RSpec::Core::RakeTask.new(:rspec)

View File

@ -1,4 +1,4 @@
#! /usr/bin/env ruby #! /usr/bin/env ruby
require_relative '../lib/github_changelog_generator' require_relative "../lib/github_changelog_generator"
GitHubChangelogGenerator::ChangelogGenerator.new.compound_changelog GitHubChangelogGenerator::ChangelogGenerator.new.compound_changelog

View File

@ -1,7 +1,7 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
require 'optparse' require "optparse"
SPEC_TYPE = 'gemspec' SPEC_TYPE = "gemspec"
:major :major
:minor :minor
@ -10,21 +10,21 @@ SPEC_TYPE = 'gemspec'
@options = { dry_run: false, bump_number: :patch } @options = { dry_run: false, bump_number: :patch }
OptionParser.new { |opts| OptionParser.new { |opts|
opts.banner = 'Usage: bump.rb [options]' opts.banner = "Usage: bump.rb [options]"
opts.on('-d', '--dry-run', 'Dry run') do |v| opts.on("-d", "--dry-run", "Dry run") do |v|
@options[:dry_run] = v @options[:dry_run] = v
end end
opts.on('-a', '--major', 'Bump major version') do |_v| opts.on("-a", "--major", "Bump major version") do |_v|
@options[:bump_number] = :major @options[:bump_number] = :major
end end
opts.on('-m', '--minor', 'Bump minor version') do |_v| opts.on("-m", "--minor", "Bump minor version") do |_v|
@options[:bump_number] = :minor @options[:bump_number] = :minor
end end
opts.on('-p', '--patch', 'Bump patch version') do |_v| opts.on("-p", "--patch", "Bump patch version") do |_v|
@options[:bump_number] = :patch @options[:bump_number] = :patch
end end
opts.on('-r', '--revert', 'Revert last bump') do |v| opts.on("-r", "--revert", "Revert last bump") do |v|
@options[:revert] = v @options[:revert] = v
end end
}.parse! }.parse!
@ -32,15 +32,15 @@ OptionParser.new { |opts|
p @options p @options
def check_repo_is_clean_or_dry_run def check_repo_is_clean_or_dry_run
value = `#{'git status --porcelain'}` value = `#{"git status --porcelain"}`
if value.empty? if value.empty?
puts 'Repo is clean -> continue' puts "Repo is clean -> continue"
else else
if @options[:dry_run] if @options[:dry_run]
puts 'Repo not clean, "Dry run" enabled -> continue' puts 'Repo not clean, "Dry run" enabled -> continue'
else else
puts 'Repository not clean -> exit' puts "Repository not clean -> exit"
exit exit
end end
end end
@ -50,7 +50,7 @@ def find_spec_file
list_of_specs = execute_line("find . -name '*.#{SPEC_TYPE}'") list_of_specs = execute_line("find . -name '*.#{SPEC_TYPE}'")
arr = list_of_specs.split("\n") arr = list_of_specs.split("\n")
spec_file = '' spec_file = ""
case arr.count case arr.count
when 0 when 0
@ -59,7 +59,7 @@ def find_spec_file
when 1 when 1
spec_file = arr[0] spec_file = arr[0]
else else
puts 'Which spec should be used?' puts "Which spec should be used?"
arr.each_with_index { |file, index| puts "#{index + 1}. #{file}" } arr.each_with_index { |file, index| puts "#{index + 1}. #{file}" }
input_index = Integer(gets.chomp) input_index = Integer(gets.chomp)
spec_file = arr[input_index - 1] spec_file = arr[input_index - 1]
@ -70,14 +70,14 @@ def find_spec_file
exit exit
end end
spec_file.sub('./', '') spec_file.sub("./", "")
end end
def find_current_gem_file def find_current_gem_file
list_of_specs = execute_line("find . -name '*.gem'") list_of_specs = execute_line("find . -name '*.gem'")
arr = list_of_specs.split("\n") arr = list_of_specs.split("\n")
spec_file = '' spec_file = ""
case arr.count case arr.count
when 0 when 0
@ -86,7 +86,7 @@ def find_current_gem_file
when 1 when 1
spec_file = arr[0] spec_file = arr[0]
else else
puts 'Which spec should be used?' puts "Which spec should be used?"
arr.each_with_index { |file, index| puts "#{index + 1}. #{file}" } arr.each_with_index { |file, index| puts "#{index + 1}. #{file}" }
input_index = Integer(gets.chomp) input_index = Integer(gets.chomp)
spec_file = arr[input_index - 1] spec_file = arr[input_index - 1]
@ -97,7 +97,7 @@ def find_current_gem_file
exit exit
end end
spec_file.sub('./', '') spec_file.sub("./", "")
end end
def find_version_in_podspec(podspec) def find_version_in_podspec(podspec)
@ -109,7 +109,7 @@ def find_version_in_podspec(podspec)
match_result = re.match(readme) match_result = re.match(readme)
unless match_result unless match_result
puts 'Not found any versions' puts "Not found any versions"
exit exit
end end
@ -132,10 +132,10 @@ def bump_version(versions_array)
when :patch when :patch
bumped_result[2] += 1 bumped_result[2] += 1
else else
fail('unknown bump_number') fail("unknown bump_number")
end end
bumped_version = bumped_result.join('.') bumped_version = bumped_result.join(".")
puts "Bump version: #{versions_array.join('.')} -> #{bumped_version}" puts "Bump version: #{versions_array.join('.')} -> #{bumped_version}"
bumped_version bumped_version
end end
@ -174,10 +174,10 @@ def run_bumping_script
bumped_version = bump_version(versions_array) bumped_version = bump_version(versions_array)
unless @options[:dry_run] unless @options[:dry_run]
puts 'Are you sure? Press Y to continue:' puts "Are you sure? Press Y to continue:"
str = gets.chomp str = gets.chomp
if str != 'Y' if str != "Y"
puts '-> exit' puts "-> exit"
exit exit
end end
end end
@ -186,8 +186,8 @@ def run_bumping_script
execute_line_if_not_dry_run("sed -i \"\" \"s/#{result}/#{bumped_version}/\" #{spec_file}") execute_line_if_not_dry_run("sed -i \"\" \"s/#{result}/#{bumped_version}/\" #{spec_file}")
execute_line_if_not_dry_run("git commit --all -m \"Update #{$SPEC_TYPE} to version #{bumped_version}\"") execute_line_if_not_dry_run("git commit --all -m \"Update #{$SPEC_TYPE} to version #{bumped_version}\"")
execute_line_if_not_dry_run("git tag #{bumped_version}") execute_line_if_not_dry_run("git tag #{bumped_version}")
execute_line_if_not_dry_run('git push') execute_line_if_not_dry_run("git push")
execute_line_if_not_dry_run('git push --tags') execute_line_if_not_dry_run("git push --tags")
execute_line_if_not_dry_run("gem build #{spec_file}") execute_line_if_not_dry_run("gem build #{spec_file}")
gem = find_current_gem_file gem = find_current_gem_file
@ -201,12 +201,12 @@ def revert_last_bump
puts "DELETE tag #{result} and HARD reset HEAD~1?\nPress Y to continue:" puts "DELETE tag #{result} and HARD reset HEAD~1?\nPress Y to continue:"
str = gets.chomp str = gets.chomp
if str != 'Y' if str != "Y"
puts '-> exit' puts "-> exit"
exit exit
end end
execute_line_if_not_dry_run("git tag -d #{result}") execute_line_if_not_dry_run("git tag -d #{result}")
execute_line_if_not_dry_run('git reset --hard HEAD~1') execute_line_if_not_dry_run("git reset --hard HEAD~1")
execute_line_if_not_dry_run("git push --delete origin #{result}") execute_line_if_not_dry_run("git push --delete origin #{result}")
end end

View File

@ -1,31 +1,31 @@
# coding: utf-8 # coding: utf-8
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"
Gem::Specification.new do |spec| Gem::Specification.new do |spec|
spec.name = 'github_changelog_generator' spec.name = "github_changelog_generator"
spec.version = GitHubChangelogGenerator::VERSION spec.version = GitHubChangelogGenerator::VERSION
spec.default_executable = 'github_changelog_generator' spec.default_executable = "github_changelog_generator"
spec.required_ruby_version = '>= 1.9.3' spec.required_ruby_version = ">= 1.9.3"
spec.authors = ['Petr Korolev'] spec.authors = ["Petr Korolev"]
spec.email = 'sky4winder+github_changelog_generator@gmail.com' spec.email = "sky4winder+github_changelog_generator@gmail.com"
spec.date = `date +"%Y-%m-%d"`.strip! spec.date = `date +"%Y-%m-%d"`.strip!
spec.summary = 'Script, that automatically generate changelog from your tags, issues, labels and pull requests.' spec.summary = "Script, that automatically generate changelog from your tags, issues, labels and pull requests."
spec.description = 'Changelog generation has never been so easy. Fully automate changelog generation - this gem generate change log file based on tags, issues and merged pull requests from Github issue tracker.' spec.description = "Changelog generation has never been so easy. Fully automate changelog generation - this gem generate change log file based on tags, issues and merged pull requests from Github issue tracker."
spec.homepage = 'https://github.com/skywinder/Github-Changelog-Generator' spec.homepage = "https://github.com/skywinder/Github-Changelog-Generator"
spec.license = 'MIT' spec.license = "MIT"
spec.files = `git ls-files -z`.split("\x0") spec.files = `git ls-files -z`.split("\x0")
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ['lib'] spec.require_paths = ["lib"]
spec.add_development_dependency 'bundler', '~> 1.7' spec.add_development_dependency "bundler", "~> 1.7"
spec.add_development_dependency 'rake', '~> 10.0' spec.add_development_dependency "rake", "~> 10.0"
spec.add_runtime_dependency('github_api', ['~> 0.12']) spec.add_runtime_dependency("github_api", ["~> 0.12"])
spec.add_runtime_dependency('colorize', ['~> 0.7']) spec.add_runtime_dependency("colorize", ["~> 0.7"])
end end

View File

@ -1,23 +1,23 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
require 'github_api' require "github_api"
require 'json' require "json"
require 'colorize' require "colorize"
require 'benchmark' require "benchmark"
require_relative 'github_changelog_generator/parser' require_relative "github_changelog_generator/parser"
require_relative 'github_changelog_generator/generator' require_relative "github_changelog_generator/generator"
require_relative 'github_changelog_generator/version' require_relative "github_changelog_generator/version"
require_relative 'github_changelog_generator/reader' require_relative "github_changelog_generator/reader"
require_relative 'github_changelog_generator/fetcher' require_relative "github_changelog_generator/fetcher"
module GitHubChangelogGenerator module GitHubChangelogGenerator
class ChangelogGenerator class ChangelogGenerator
attr_accessor :options, :all_tags, :github attr_accessor :options, :all_tags, :github
PER_PAGE_NUMBER = 30 PER_PAGE_NUMBER = 30
GH_RATE_LIMIT_EXCEEDED_MSG = 'Warning: GitHub API rate limit (5000 per hour) exceeded, change log may be ' \ GH_RATE_LIMIT_EXCEEDED_MSG = "Warning: GitHub API rate limit (5000 per hour) exceeded, change log may be " \
'missing some issues. You can limit the number of issues fetched using the `--max-issues NUM` argument.' "missing some issues. You can limit the number of issues fetched using the `--max-issues NUM` argument."
def initialize def initialize
@options = Parser.parse_options @options = Parser.parse_options
@ -84,16 +84,16 @@ module GitHubChangelogGenerator
threads.each(&:join) threads.each(&:join)
if @options[:verbose] if @options[:verbose]
puts 'Fetching closed dates for issues: Done!' puts "Fetching closed dates for issues: Done!"
end end
end end
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 { |event| issue["events"].reverse!.each { |event|
if event[:event].eql? compare_string if event[:event].eql? compare_string
if event[:commit_id].nil? if event[:commit_id].nil?
issue[:actual_date] = issue[:closed_at] issue[:actual_date] = issue[:closed_at]
@ -123,7 +123,7 @@ module GitHubChangelogGenerator
end end
pull_requests = [] pull_requests = []
begin begin
response = @github.pull_requests.list @options[:user], @options[:project], state: 'closed' response = @github.pull_requests.list @options[:user], @options[:project], state: "closed"
page_i = 0 page_i = 0
response.each_page do |page| response.each_page do |page|
page_i += PER_PAGE_NUMBER page_i += PER_PAGE_NUMBER
@ -146,7 +146,7 @@ module GitHubChangelogGenerator
} }
if @options[:verbose] if @options[:verbose]
puts 'Fetching merged dates: Done!' puts "Fetching merged dates: Done!"
end end
end end
@ -192,7 +192,7 @@ module GitHubChangelogGenerator
tag1 = @options[:tag1] tag1 = @options[:tag1]
tag2 = @options[:tag2] tag2 = @options[:tag2]
tags_strings = [] tags_strings = []
all_tags.each { |x| tags_strings.push(x['name']) } all_tags.each { |x| tags_strings.push(x["name"]) }
if tags_strings.include?(tag1) if tags_strings.include?(tag1)
if tags_strings.include?(tag2) if tags_strings.include?(tag2)
@ -216,8 +216,8 @@ module GitHubChangelogGenerator
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)*"
output_filename = "#{@options[:output]}" output_filename = "#{@options[:output]}"
File.open(output_filename, 'w') { |file| file.write(log) } File.open(output_filename, "w") { |file| file.write(log) }
puts 'Done!' puts "Done!"
puts "Generated log placed in #{`pwd`.strip!}/#{output_filename}" puts "Generated log placed in #{`pwd`.strip!}/#{output_filename}"
end end
@ -225,16 +225,16 @@ module GitHubChangelogGenerator
fetch_tags_dates fetch_tags_dates
if @options[:verbose] if @options[:verbose]
puts 'Sorting tags...' puts "Sorting tags..."
end end
@all_tags.sort_by! { |x| get_time_of_tag(x) }.reverse! @all_tags.sort_by! { |x| get_time_of_tag(x) }.reverse!
if @options[:verbose] if @options[:verbose]
puts 'Generating log...' puts "Generating log..."
end end
log = '' log = ""
if @options[:unreleased] && @all_tags.count != 0 if @options[:unreleased] && @all_tags.count != 0
unreleased_log = generate_log_between_tags(all_tags[0], nil) unreleased_log = generate_log_between_tags(all_tags[0], nil)
@ -314,11 +314,11 @@ module GitHubChangelogGenerator
end end
def fetch_github_token def fetch_github_token
env_var = @options[:token] ? @options[:token] : (ENV.fetch 'CHANGELOG_GITHUB_TOKEN', nil) env_var = @options[:token] ? @options[:token] : (ENV.fetch "CHANGELOG_GITHUB_TOKEN", nil)
unless env_var unless env_var
puts 'Warning: No token provided (-t option) and variable $CHANGELOG_GITHUB_TOKEN was not found.'.yellow puts "Warning: No token provided (-t option) and variable $CHANGELOG_GITHUB_TOKEN was not found.".yellow
puts 'This script can make only 50 requests per hour to GitHub API without a token!'.yellow puts "This script can make only 50 requests per hour to GitHub API without a token!".yellow
end end
@github_token ||= env_var @github_token ||= env_var
@ -329,8 +329,8 @@ module GitHubChangelogGenerator
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"]
older_tag_name = older_tag.nil? ? nil : older_tag['name'] older_tag_name = older_tag.nil? ? nil : older_tag["name"]
if @options[:filter_issues_by_milestone] if @options[:filter_issues_by_milestone]
# delete excess irrelevant issues (according milestones) # delete excess irrelevant issues (according milestones)
@ -340,7 +340,7 @@ module GitHubChangelogGenerator
if filtered_issues.empty? && filtered_pull_requests.empty? && newer_tag.nil? if filtered_issues.empty? && filtered_pull_requests.empty? && newer_tag.nil?
# do not generate empty unreleased section # do not generate empty unreleased section
return '' return ""
end end
create_log(filtered_pull_requests, filtered_issues, newer_tag, older_tag_name) create_log(filtered_pull_requests, filtered_issues, newer_tag, older_tag_name)
@ -382,7 +382,7 @@ module GitHubChangelogGenerator
end end
def delete_by_time(array, hash_key, older_tag = nil, newer_tag = nil) def delete_by_time(array, hash_key, older_tag = nil, newer_tag = nil)
fail 'At least one of the tags should be not nil!' if older_tag.nil? && newer_tag.nil? fail "At least one of the tags should be not nil!" if older_tag.nil? && newer_tag.nil?
newer_tag_time = get_time_of_tag(newer_tag) newer_tag_time = get_time_of_tag(newer_tag)
older_tag_time = get_time_of_tag(older_tag) older_tag_time = get_time_of_tag(older_tag)
@ -421,10 +421,10 @@ module GitHubChangelogGenerator
# @return [String] Ready and parsed section # @return [String] Ready and parsed section
def create_log(pull_requests, issues, newer_tag, older_tag_name = nil) def create_log(pull_requests, issues, newer_tag, older_tag_name = nil)
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)
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
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)
@ -438,12 +438,12 @@ module GitHubChangelogGenerator
issues.each { |dict| issues.each { |dict|
added = false added = false
dict.labels.each { |label| dict.labels.each { |label|
if label.name == 'bug' if label.name == "bug"
bugs_a.push dict bugs_a.push dict
added = true added = true
next next
end end
if label.name == 'enhancement' if label.name == "enhancement"
enhancement_a.push dict enhancement_a.push dict
added = true added = true
next next
@ -471,7 +471,7 @@ module GitHubChangelogGenerator
# @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
def generate_sub_section(issues, prefix) def generate_sub_section(issues, prefix)
log = '' log = ""
if options[:simple_list] != true && issues.any? if options[:simple_list] != true && issues.any?
log += "#{prefix}\n\n" log += "#{prefix}\n\n"
@ -495,7 +495,7 @@ module GitHubChangelogGenerator
# @param [String] project_url - url for current project. # @param [String] project_url - url for current project.
# @return [String] - Generate one ready-to-add section. # @return [String] - Generate one ready-to-add section.
def generate_header(newer_tag_name, newer_tag_link, newer_tag_time, older_tag_link, project_url) def generate_header(newer_tag_name, newer_tag_link, newer_tag_time, older_tag_link, project_url)
log = '' log = ""
# Generate date string: # Generate date string:
time_string = newer_tag_time.strftime @options[:dateformat] time_string = newer_tag_time.strftime @options[:dateformat]
@ -520,17 +520,17 @@ module GitHubChangelogGenerator
return nil return nil
end end
if tag_times_hash[tag_name['name']] if tag_times_hash[tag_name["name"]]
return @tag_times_hash[tag_name['name']] return @tag_times_hash[tag_name["name"]]
end end
begin begin
github_git_data_commits_get = @github.git_data.commits.get @options[:user], @options[:project], tag_name['commit']['sha'] github_git_data_commits_get = @github.git_data.commits.get @options[:user], @options[:project], tag_name["commit"]["sha"]
rescue rescue
puts GH_RATE_LIMIT_EXCEEDED_MSG.yellow puts GH_RATE_LIMIT_EXCEEDED_MSG.yellow
end end
time_string = github_git_data_commits_get['committer']['date'] time_string = github_git_data_commits_get["committer"]["date"]
@tag_times_hash[tag_name['name']] = Time.parse(time_string) @tag_times_hash[tag_name["name"]] = Time.parse(time_string)
end end
def get_filtered_issues def get_filtered_issues
@ -573,7 +573,7 @@ module GitHubChangelogGenerator
issues = [] issues = []
begin begin
response = @github.issues.list user: @options[:user], repo: @options[:project], state: 'closed', filter: 'all', labels: nil response = @github.issues.list user: @options[:user], repo: @options[:project], state: "closed", filter: "all", labels: nil
page_i = 0 page_i = 0
count_pages = response.count_pages count_pages = response.count_pages
response.each_page do |page| response.each_page do |page|
@ -620,7 +620,7 @@ module GitHubChangelogGenerator
issues_slice.each { |issue| issues_slice.each { |issue|
threads << Thread.new { threads << Thread.new {
begin begin
obj = @github.issues.events.list user: @options[:user], repo: @options[:project], issue_number: issue['number'] obj = @github.issues.events.list user: @options[:user], repo: @options[:project], issue_number: issue["number"]
rescue rescue
puts GH_RATE_LIMIT_EXCEEDED_MSG.yellow puts GH_RATE_LIMIT_EXCEEDED_MSG.yellow
end end

View File

@ -27,11 +27,11 @@ module GitHubChangelogGenerator
# #
# @return [String] # @return [String]
def fetch_github_token def fetch_github_token
env_var = @options[:token] ? @options[:token] : (ENV.fetch 'CHANGELOG_GITHUB_TOKEN', nil) env_var = @options[:token] ? @options[:token] : (ENV.fetch "CHANGELOG_GITHUB_TOKEN", nil)
unless env_var unless env_var
puts 'Warning: No token provided (-t option) and variable $CHANGELOG_GITHUB_TOKEN was not found.'.yellow puts "Warning: No token provided (-t option) and variable $CHANGELOG_GITHUB_TOKEN was not found.".yellow
puts 'This script can make only 50 requests to GitHub API per hour without token!'.yellow puts "This script can make only 50 requests to GitHub API per hour without token!".yellow
end end
env_var env_var

View File

@ -19,7 +19,7 @@ module GitHubChangelogGenerator
unless issue.pull_request.nil? unless issue.pull_request.nil?
if @options[:author] if @options[:author]
if issue.user.nil? if issue.user.nil?
title_with_number += ' ({Null user})' title_with_number += " ({Null user})"
else else
title_with_number += " ([#{issue.user.login}](#{issue.user.html_url}))" title_with_number += " ([#{issue.user.login}](#{issue.user.html_url}))"
end end

View File

@ -1,7 +1,7 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
require 'optparse' require "optparse"
require 'pp' require "pp"
require_relative 'version' require_relative "version"
module GitHubChangelogGenerator module GitHubChangelogGenerator
class Parser class Parser
@ -9,8 +9,8 @@ module GitHubChangelogGenerator
options = { options = {
tag1: nil, tag1: nil,
tag2: nil, tag2: nil,
dateformat: '%Y-%m-%d', dateformat: "%Y-%m-%d",
output: 'CHANGELOG.md', output: "CHANGELOG.md",
issues: true, issues: true,
add_issues_wo_labels: true, add_issues_wo_labels: true,
add_pr_wo_labels: true, add_pr_wo_labels: true,
@ -18,7 +18,7 @@ module GitHubChangelogGenerator
filter_issues_by_milestone: true, filter_issues_by_milestone: true,
author: true, author: true,
unreleased: true, unreleased: true,
unreleased_label: 'Unreleased', unreleased_label: "Unreleased",
compare_link: true, compare_link: true,
include_labels: %w(bug enhancement), include_labels: %w(bug enhancement),
exclude_labels: %w(duplicate question invalid wontfix), exclude_labels: %w(duplicate question invalid wontfix),
@ -26,86 +26,86 @@ module GitHubChangelogGenerator
simple_list: false, simple_list: false,
verbose: true, verbose: true,
merge_prefix: '**Merged pull requests:**', merge_prefix: "**Merged pull requests:**",
issue_prefix: '**Closed issues:**', issue_prefix: "**Closed issues:**",
bug_prefix: '**Fixed bugs:**', bug_prefix: "**Fixed bugs:**",
enhancement_prefix: '**Implemented enhancements:**', enhancement_prefix: "**Implemented enhancements:**",
branch: 'origin' branch: "origin"
} }
parser = OptionParser.new do |opts| parser = OptionParser.new do |opts|
opts.banner = 'Usage: github_changelog_generator [options]' opts.banner = "Usage: github_changelog_generator [options]"
opts.on('-u', '--user [USER]', 'Username of the owner of target GitHub repo') do |last| opts.on("-u", "--user [USER]", "Username of the owner of target GitHub repo") do |last|
options[:user] = last options[:user] = last
end end
opts.on('-p', '--project [PROJECT]', 'Name of project on GitHub') do |last| opts.on("-p", "--project [PROJECT]", "Name of project on GitHub") do |last|
options[:project] = last options[:project] = last
end end
opts.on('-t', '--token [TOKEN]', 'To make more than 50 requests per hour your GitHub token is required. You can generate it at: https://github.com/settings/tokens/new') do |last| opts.on("-t", "--token [TOKEN]", "To make more than 50 requests per hour your GitHub token is required. You can generate it at: https://github.com/settings/tokens/new") do |last|
options[:token] = last options[:token] = last
end end
opts.on('-f', '--date-format [FORMAT]', 'Date format. Default is %Y-%m-%d') do |last| opts.on("-f", "--date-format [FORMAT]", "Date format. Default is %Y-%m-%d") do |last|
options[:dateformat] = last options[:dateformat] = last
end end
opts.on('-o', '--output [NAME]', 'Output file. Default is CHANGELOG.md') do |last| opts.on("-o", "--output [NAME]", "Output file. Default is CHANGELOG.md") do |last|
options[:output] = last options[:output] = last
end end
opts.on('--[no-]issues', 'Include closed issues in changelog. Default is true') do |v| opts.on("--[no-]issues", "Include closed issues in changelog. Default is true") do |v|
options[:issues] = v options[:issues] = v
end end
opts.on('--[no-]issues-wo-labels', 'Include closed issues without labels in changelog. Default is true') do |v| opts.on("--[no-]issues-wo-labels", "Include closed issues without labels in changelog. Default is true") do |v|
options[:add_issues_wo_labels] = v options[:add_issues_wo_labels] = v
end end
opts.on('--[no-]pr-wo-labels', 'Include pull requests without labels in changelog. Default is true') do |v| opts.on("--[no-]pr-wo-labels", "Include pull requests without labels in changelog. Default is true") do |v|
options[:add_pr_wo_labels] = v options[:add_pr_wo_labels] = v
end end
opts.on('--[no-]pull-requests', 'Include pull-requests in changelog. Default is true') do |v| opts.on("--[no-]pull-requests", "Include pull-requests in changelog. Default is true") do |v|
options[:pulls] = v options[:pulls] = v
end end
opts.on('--[no-]filter-by-milestone', 'Use milestone to detect when issue was resolved. Default is true') do |last| opts.on("--[no-]filter-by-milestone", "Use milestone to detect when issue was resolved. Default is true") do |last|
options[:filter_issues_by_milestone] = last options[:filter_issues_by_milestone] = last
end end
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('--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
opts.on('--[no-]unreleased', 'Add to log unreleased closed issues. Default is true') do |v| opts.on("--[no-]unreleased", "Add to log unreleased closed issues. Default is true") do |v|
options[:unreleased] = v options[:unreleased] = v
end end
opts.on('--unreleased-label [label]', 'Add to log unreleased closed issues. Default is true') do |v| opts.on("--unreleased-label [label]", "Add to log unreleased closed issues. Default is true") do |v|
options[:unreleased_label] = v options[:unreleased_label] = v
end end
opts.on('--[no-]compare-link', 'Include compare link (Full Changelog) between older version and newer version. Default is true') do |v| opts.on("--[no-]compare-link", "Include compare link (Full Changelog) between older version and newer version. Default is true") do |v|
options[:compare_link] = v options[:compare_link] = v
end end
opts.on('--include-labels x,y,z', Array, 'Only issues with the specified labels will be included in the changelog. Default is \'bug,enhancement\'') do |list| opts.on("--include-labels x,y,z", Array, 'Only issues with the specified labels will be included in the changelog. Default is \'bug,enhancement\'') do |list|
options[:include_labels] = list options[:include_labels] = list
end end
opts.on('--exclude-labels x,y,z', Array, 'Issues with the specified labels will be always excluded from changelog. Default is \'duplicate,question,invalid,wontfix\'') do |list| opts.on("--exclude-labels x,y,z", Array, 'Issues with the specified labels will be always excluded from changelog. Default is \'duplicate,question,invalid,wontfix\'') do |list|
options[:exclude_labels] = list options[:exclude_labels] = list
end end
opts.on('--max-issues [NUMBER]', Integer, 'Max number of issues to fetch from GitHub. Default is unlimited') do |max| opts.on("--max-issues [NUMBER]", Integer, "Max number of issues to fetch from GitHub. Default is unlimited") do |max|
options[:max_issues] = max options[:max_issues] = max
end end
opts.on('--github-site [URL]', 'The Enterprise Github site on which your project is hosted.') do |last| opts.on("--github-site [URL]", "The Enterprise Github site on which your project is hosted.") do |last|
options[:github_site] = last options[:github_site] = last
end end
opts.on('--github-api [URL]', 'The enterprise endpoint to use for your Github API.') do |last| opts.on("--github-api [URL]", "The enterprise endpoint to use for your Github API.") do |last|
options[:github_endpoint] = last options[:github_endpoint] = last
end end
opts.on('--simple-list', 'Create simple list from issues and pull requests. Default is false.') do |v| opts.on("--simple-list", "Create simple list from issues and pull requests. Default is false.") do |v|
options[:simple_list] = v options[:simple_list] = v
end 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
opts.on('-v', '--version', 'Print version number') do |_v| opts.on("-v", "--version", "Print version number") do |_v|
puts "Version: #{GitHubChangelogGenerator::VERSION}" puts "Version: #{GitHubChangelogGenerator::VERSION}"
exit exit
end end
opts.on('-h', '--help', 'Displays Help') do opts.on("-h", "--help", "Displays Help") do
puts opts puts opts
exit exit
end end
@ -114,7 +114,7 @@ module GitHubChangelogGenerator
parser.parse! parser.parse!
if ARGV[0] && !ARGV[1] if ARGV[0] && !ARGV[1]
github_site = options[:github_site] ? options[:github_site] : 'github.com' github_site = options[:github_site] ? options[:github_site] : "github.com"
# 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 "skywinder/Github-Changelog-Generator" to user and name
match = /(?:.+#{Regexp.escape(github_site)}\/)?(.+)\/(.+)/.match(ARGV[0]) match = /(?:.+#{Regexp.escape(github_site)}\/)?(.+)\/(.+)/.match(ARGV[0])
@ -166,9 +166,9 @@ module GitHubChangelogGenerator
end end
if options[:verbose] if options[:verbose]
puts 'Performing task with options:' puts "Performing task with options:"
pp options pp options
puts '' puts ""
end end
options options

View File

@ -25,7 +25,7 @@ module GitHubChangelogGenerator
class Reader class Reader
def initialize(options = {}) def initialize(options = {})
defaults = { defaults = {
heading_level: '##', heading_level: "##",
heading_structures: [ heading_structures: [
/^## \[(?<version>.+?)\]\((?<url>.+?)\)( \((?<date>.+?)\))?$/, /^## \[(?<version>.+?)\]\((?<url>.+?)\)( \((?<date>.+?)\))?$/,
/^## (?<version>.+?)( \((?<date>.+?)\))?$/ /^## (?<version>.+?)( \((?<date>.+?)\))?$/
@ -49,7 +49,7 @@ module GitHubChangelogGenerator
# @param [String] heading Heading from the ChangeLog File # @param [String] heading Heading from the ChangeLog File
# @return [Hash] Returns a structured Hash with version, url and date # @return [Hash] Returns a structured Hash with version, url and date
def parse_heading(heading) def parse_heading(heading)
captures = { 'version' => nil, 'url' => nil, 'date' => nil } captures = { "version" => nil, "url" => nil, "date" => nil }
@heading_structures.each do |regexp| @heading_structures.each do |regexp|
matches = Regexp.new(regexp).match(heading) matches = Regexp.new(regexp).match(heading)
@ -73,7 +73,7 @@ module GitHubChangelogGenerator
headings.each_with_index do |heading, index| headings.each_with_index do |heading, index|
captures = parse_heading(heading) captures = parse_heading(heading)
captures['content'] = sections.at(index + 1) captures["content"] = sections.at(index + 1)
changelog.push captures changelog.push captures
end end

View File

@ -1,3 +1,3 @@
module GitHubChangelogGenerator module GitHubChangelogGenerator
VERSION = '1.3.11' VERSION = "1.3.11"
end end

View File

@ -15,9 +15,9 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
require 'codeclimate-test-reporter' require "codeclimate-test-reporter"
require 'simplecov' require "simplecov"
require 'coveralls' require "coveralls"
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
Coveralls::SimpleCov::Formatter, Coveralls::SimpleCov::Formatter,
@ -26,7 +26,7 @@ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
] ]
SimpleCov.start SimpleCov.start
require 'github_changelog_generator' require "github_changelog_generator"
# This file was generated by the `rspec --init` command. Conventionally, all # This file was generated by the `rspec --init` command. Conventionally, all
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
@ -95,7 +95,7 @@ RSpec.configure do |config|
# Use the documentation formatter for detailed output, # Use the documentation formatter for detailed output,
# unless a formatter has already been configured # unless a formatter has already been configured
# (e.g. via a command-line flag). # (e.g. via a command-line flag).
config.default_formatter = 'doc' config.default_formatter = "doc"
end end
# Print the 10 slowest examples and example groups at the # Print the 10 slowest examples and example groups at the

View File

@ -21,71 +21,71 @@ describe GitHubChangelogGenerator::Reader do
@reader = GitHubChangelogGenerator::Reader.new @reader = GitHubChangelogGenerator::Reader.new
end end
describe '#parse_heading' do describe "#parse_heading" do
context 'when heading is empty' do context "when heading is empty" do
subject { @reader.parse_heading('## ') } subject { @reader.parse_heading("## ") }
it { is_expected.to be_a(Hash) } it { is_expected.to be_a(Hash) }
it { is_expected.to include('version', 'url', 'date') } it { is_expected.to include("version", "url", "date") }
it { is_expected.to include('version' => nil, 'url' => nil, 'date' => nil) } it { is_expected.to include("version" => nil, "url" => nil, "date" => nil) }
# TODO: Doesn't work? # TODO: Doesn't work?
# it { is_expected.to have_all_string_keys } # it { is_expected.to have_all_string_keys }
end end
context 'when given version, url and date' do context "when given version, url and date" do
subject { @reader.parse_heading('## [1.3.10](https://github.com/skywinder/Github-Changelog-Generator/tree/1.3.10) (2015-03-18)') } subject { @reader.parse_heading("## [1.3.10](https://github.com/skywinder/Github-Changelog-Generator/tree/1.3.10) (2015-03-18)") }
it { is_expected.to include('version' => '1.3.10') } it { is_expected.to include("version" => "1.3.10") }
it { is_expected.to include('url' => 'https://github.com/skywinder/Github-Changelog-Generator/tree/1.3.10') } it { is_expected.to include("url" => "https://github.com/skywinder/Github-Changelog-Generator/tree/1.3.10") }
it { is_expected.to include('date' => '2015-03-18') } it { is_expected.to include("date" => "2015-03-18") }
end end
context 'when no url and date is provided' do context "when no url and date is provided" do
subject { @reader.parse_heading('## foobar') } subject { @reader.parse_heading("## foobar") }
it { is_expected.to include('version' => 'foobar', 'url' => nil, 'date' => nil) } it { is_expected.to include("version" => "foobar", "url" => nil, "date" => nil) }
end end
end end
describe '#parse' do describe "#parse" do
context 'when file is empty' do context "when file is empty" do
subject { @reader.parse('') } subject { @reader.parse("") }
it { is_expected.to be_an(Array) } it { is_expected.to be_an(Array) }
it { is_expected.to be_empty } it { is_expected.to be_empty }
end end
context 'when file has only the header' do context "when file has only the header" do
subject { @reader.parse('# Change Log') } subject { @reader.parse("# Change Log") }
it { is_expected.to be_an(Array) } it { is_expected.to be_an(Array) }
it { is_expected.to be_empty } it { is_expected.to be_empty }
end end
end end
describe 'example CHANGELOG files' do describe "example CHANGELOG files" do
subject { @reader.read(File.expand_path(File.join(File.dirname(__FILE__), '..', 'files', self.class.description))) } subject { @reader.read(File.expand_path(File.join(File.dirname(__FILE__), "..", "files", self.class.description))) }
context 'github-changelog-generator.md' do context "github-changelog-generator.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 { expect(subject.count).to eq(28) } it { expect(subject.count).to eq(28) }
it { expect(subject.first).to include('version' => '1.3.10') } it { expect(subject.first).to include("version" => "1.3.10") }
it { expect(subject.first).to include('url' => 'https://github.com/skywinder/Github-Changelog-Generator/tree/1.3.10') } it { expect(subject.first).to include("url" => "https://github.com/skywinder/Github-Changelog-Generator/tree/1.3.10") }
it { expect(subject.first).to include('date' => '2015-03-18') } it { expect(subject.first).to include("date" => "2015-03-18") }
it { expect(subject.first).to include('content') } it { expect(subject.first).to include("content") }
it 'content should not be empty' do it "content should not be empty" do
expect(subject.first['content']).not_to be_empty expect(subject.first["content"]).not_to be_empty
end end
end end
context 'bundler.md' do context "bundler.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 { expect(subject.count).to eq(151) } it { expect(subject.count).to eq(151) }
it { expect(subject.first).to include('version' => '1.9.1') } it { expect(subject.first).to include("version" => "1.9.1") }
it { expect(subject.first).to include('url' => nil) } it { expect(subject.first).to include("url" => nil) }
it { expect(subject.first).to include('date' => '2015-03-21') } it { expect(subject.first).to include("date" => "2015-03-21") }
it { expect(subject.first).to include('content') } it { expect(subject.first).to include("content") }
it 'content should not be empty' do it "content should not be empty" do
expect(subject.first['content']).not_to be_empty expect(subject.first["content"]).not_to be_empty
end end
end end
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 it do
pending('Implement heading_level for parser.') pending("Implement heading_level for parser.")
expect(subject.count).to eq(134) expect(subject.count).to eq(134)
end end
# it do # it do