2014-11-05 14:32:16 +00:00
#!/usr/bin/env ruby
2014-11-06 12:58:12 +00:00
require 'optparse'
2014-11-05 14:29:04 +00:00
require 'github_api'
require 'json'
require 'httparty'
class ChangelogGenerator
attr_accessor :options , :all_tags
def initialize ( )
2014-11-06 12:58:12 +00:00
@options = self . parse_options
2014-11-06 12:52:47 +00:00
if @options [ :token ]
@github = Github . new oauth_token : @options [ :token ]
2014-11-05 14:29:04 +00:00
else
@github = Github . new
end
@all_tags = self . get_all_tags
2014-11-05 15:02:46 +00:00
@pull_requests = self . get_all_closed_pull_requests
2014-11-05 16:31:45 +00:00
@tag_times_hash = { }
2014-11-05 14:29:04 +00:00
end
2014-11-06 12:58:12 +00:00
def parse_options
options = { :tag1 = > nil , :tag2 = > nil , :format = > '%d/%m/%y' }
parser = OptionParser . new { | opts |
2014-11-06 13:31:04 +00:00
opts . banner = 'Usage: changelog_generator.rb -u user_name -p project_name [-t 16-digit-GitHubToken] [options]'
opts . on ( '-u' , '--user [USER]' , 'your username on GitHub' ) do | last |
options [ :user ] = last
end
opts . on ( '-p' , '--project [PROJECT]' , 'name of project on GitHub' ) do | last |
options [ :project ] = last
end
opts . on ( '-t' , '--token [TOKEN]' , 'To make more than 50 requests this app required your OAuth token for GitHub. You can generate it on https://github.com/settings/applications' ) do | last |
options [ :token ] = last
end
2014-11-06 12:58:12 +00:00
opts . on ( '-h' , '--help' , 'Displays Help' ) do
puts opts
exit
end
opts . on ( '-v' , '--[no-]verbose' , 'Run verbosely' ) do | v |
options [ :verbose ] = v
end
opts . on ( '-l' , '--last-changes' , 'generate log between last 2 tags' ) do | last |
options [ :last ] = last
end
opts . on ( '-f' , '--date-format [FORMAT]' , 'date format. default is %d/%m/%y' ) do | last |
options [ :format ] = last
end
}
parser . parse!
#udefined case with 1 parameter:
if ARGV [ 0 ] && ! ARGV [ 1 ]
puts parser . banner
exit
end
if ! options [ :user ] || ! options [ :project ]
puts parser . banner
exit
end
if ARGV [ 1 ]
options [ :tag1 ] = ARGV [ 0 ]
options [ :tag2 ] = ARGV [ 1 ]
end
options
end
2014-11-05 14:29:04 +00:00
def print_json ( json )
puts JSON . pretty_generate ( json )
end
def exec_command ( cmd )
exec_cmd = " cd #{ $project_path } && #{ cmd } "
%x[ #{ exec_cmd } ]
end
def get_all_closed_pull_requests
2014-11-05 15:02:46 +00:00
2014-11-06 12:52:47 +00:00
issues = @github . pull_requests . list @options [ :user ] , @options [ :project ] , :state = > 'closed'
2014-11-05 14:29:04 +00:00
json = issues . body
if @options [ :verbose ]
2014-11-05 15:02:46 +00:00
puts 'Receive all pull requests'
2014-11-05 14:29:04 +00:00
end
json
end
2014-11-05 15:22:18 +00:00
def compund_changelog
2014-11-05 14:29:04 +00:00
if @options [ :verbose ]
puts 'Generating changelog:'
end
2014-11-05 16:35:05 +00:00
2014-11-06 12:52:47 +00:00
log = " # Changelog \n \n "
2014-11-05 14:29:04 +00:00
2014-11-05 15:22:18 +00:00
if @options [ :last ]
2014-11-05 15:56:02 +00:00
log += self . generate_log_between_tags ( self . all_tags [ 0 ] , self . all_tags [ 1 ] )
2014-11-05 15:22:18 +00:00
elsif @options [ :tag1 ] && @options [ :tag2 ]
2014-11-05 15:51:37 +00:00
tag1 = @options [ :tag1 ]
tag2 = @options [ :tag2 ]
tags_strings = [ ]
self . all_tags . each { | x | tags_strings . push ( x [ 'name' ] ) }
if tags_strings . include? ( tag1 )
if tags_strings . include? ( tag2 )
hash = Hash [ tags_strings . map . with_index . to_a ]
index1 = hash [ tag1 ]
index2 = hash [ tag2 ]
log += self . generate_log_between_tags ( self . all_tags [ index1 ] , self . all_tags [ index2 ] )
else
puts " Can't find tag #{ tag2 } -> exit "
exit
end
else
puts " Can't find tag #{ tag1 } -> exit "
exit
end
2014-11-05 15:22:18 +00:00
else
log += self . generate_log_for_all_tags
end
2014-11-05 16:39:39 +00:00
if @options [ :verbose ]
puts log
end
2014-11-06 12:52:47 +00:00
output_filename = " #{ @options [ :project ] } _changelog.md "
2014-11-05 16:39:39 +00:00
File . open ( output_filename , 'w' ) { | file | file . write ( log ) }
puts " Done! Generated log placed in #{ output_filename } "
2014-11-03 11:57:10 +00:00
2014-11-05 14:29:04 +00:00
end
2014-11-03 11:57:10 +00:00
2014-11-05 15:22:18 +00:00
def generate_log_for_all_tags
2014-11-05 16:31:45 +00:00
log = ''
for index in 1 ... self . all_tags . size
log += self . generate_log_between_tags ( self . all_tags [ index - 1 ] , self . all_tags [ index ] )
end
log
2014-11-05 15:22:18 +00:00
end
2014-11-05 14:29:04 +00:00
def is_megred ( number )
2014-11-06 12:52:47 +00:00
@github . pull_requests . merged? @options [ :user ] , @options [ :project ] , number
2014-11-05 14:29:04 +00:00
end
def get_all_merged_pull_requests
json = self . get_all_closed_pull_requests
puts 'Check if the requests is merged... (it can take a while)'
json . delete_if { | req |
merged = self . is_megred ( req [ :number ] )
if @options [ :verbose ]
puts " # #{ req [ :number ] } #{ merged ? 'merged' : 'not merged' } "
end
! merged
}
end
def get_all_tags
2014-11-05 15:02:46 +00:00
2014-11-06 13:31:04 +00:00
url = " https://api.github.com/repos/ #{ @options [ :user ] } / #{ @options [ :project ] } /tags "
2014-11-05 15:02:46 +00:00
if @options [ :verbose ]
2014-11-06 13:31:04 +00:00
puts " Receive tags for repo #{ url } "
2014-11-05 15:02:46 +00:00
end
2014-11-05 14:29:04 +00:00
response = HTTParty . get ( url ,
:headers = > { 'Authorization' = > 'token 8587bb22f6bf125454768a4a19dbcc774ea68d48' ,
'User-Agent' = > 'Changelog-Generator' } )
json_parse = JSON . parse ( response . body )
2014-11-06 12:52:47 +00:00
if @options [ :verbose ]
puts " Found #{ json_parse . count } tags "
end
json_parse
2014-11-05 14:29:04 +00:00
end
2014-11-05 15:02:46 +00:00
def generate_log_between_tags ( since_tag , till_tag )
since_tag_time = self . get_time_of_tag ( since_tag )
till_tag_time = self . get_time_of_tag ( till_tag )
# if we mix up tags order - lits fix it!
if since_tag_time > till_tag_time
since_tag , till_tag = till_tag , since_tag
since_tag_time , till_tag_time = till_tag_time , since_tag_time
end
2014-11-05 16:31:45 +00:00
till_tag_name = till_tag [ 'name' ]
2014-11-05 15:02:46 +00:00
2014-11-05 16:01:58 +00:00
pull_requests = Array . new ( @pull_requests )
2014-11-05 14:29:04 +00:00
pull_requests . delete_if { | req |
t = Time . parse ( req [ :closed_at ] ) . utc
2014-11-05 15:02:46 +00:00
true_classor_false_class = t > since_tag_time
classor_false_class = t < till_tag_time
in_range = ( true_classor_false_class ) && ( classor_false_class )
! in_range
2014-11-05 14:29:04 +00:00
}
2014-11-05 16:31:45 +00:00
self . create_log ( pull_requests , till_tag_name , till_tag_time )
2014-11-05 16:01:58 +00:00
end
2014-11-05 16:31:45 +00:00
def create_log ( pull_requests , tag_name , tag_time )
2014-11-05 14:29:04 +00:00
2014-11-06 12:52:47 +00:00
trimmed_tag = tag_name . tr ( 'v' , '' )
log = " # # [ #{ trimmed_tag } ] (https://github.com/ #{ @options [ :user ] } / #{ @options [ :project ] } /tree/ #{ tag_name } ) \n "
2014-11-05 16:31:45 +00:00
2014-11-06 12:52:47 +00:00
time_string = tag_time . strftime @options [ :format ]
2014-11-05 14:29:04 +00:00
log += " # # # # #{ time_string } \n "
pull_requests . each { | dict |
2014-11-06 12:52:47 +00:00
merge = " #{ dict [ :title ] } [ \\ # #{ dict [ :number ] } ](https://github.com/ #{ @options [ :user ] } / #{ @options [ :project ] } /pull/ #{ dict [ :number ] } ) \n \n "
2014-11-05 14:29:04 +00:00
log += " - #{ merge } "
}
log
end
def get_time_of_tag ( prev_tag )
2014-11-05 15:02:46 +00:00
2014-11-05 16:31:45 +00:00
if @tag_times_hash [ prev_tag [ 'name' ] ]
return @tag_times_hash [ prev_tag [ 'name' ] ]
end
2014-11-05 15:02:46 +00:00
if @options [ :verbose ]
puts " Get time for tag #{ prev_tag [ 'name' ] } "
end
2014-11-06 12:52:47 +00:00
github_git_data_commits_get = @github . git_data . commits . get @options [ :user ] , @options [ :project ] , prev_tag [ 'commit' ] [ 'sha' ]
2014-11-05 15:51:37 +00:00
time_string = github_git_data_commits_get [ 'committer' ] [ 'date' ]
Time . parse ( time_string )
2014-11-05 16:31:45 +00:00
@tag_times_hash [ prev_tag [ 'name' ] ] = Time . parse ( time_string )
2014-11-05 14:29:04 +00:00
end
2014-11-03 11:57:10 +00:00
2014-11-05 11:27:28 +00:00
end
if __FILE__ == $0
2014-11-06 08:37:53 +00:00
ChangelogGenerator . new . compund_changelog
2014-11-04 17:45:10 +00:00
end