Created
April 15, 2020 11:57
-
-
Save 3Hren/a996e872db94fc9f674377ccec694f11 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
require 'git' | |
require 'logger' | |
require 'optparse' | |
require 'semantic' | |
require 'semantic/core_ext' | |
require 'tempfile' | |
require 'tty-prompt' | |
DEFAULT_AUTHOR_NAME = 'Evgeny Safronov'.freeze | |
DEFAULT_AUTHOR_MAIL = '[email protected]'.freeze | |
WORKING_DIR = '/Users/esafronov/code/core'.freeze | |
LOG = Logger.new(STDOUT) | |
TYPES = { | |
feat: 'Added', | |
change: 'Changed', | |
fix: 'Fixed', | |
perf: 'Performance' | |
}.freeze | |
# ChangelogManager. | |
class ChangelogManager | |
def initialize(working_dir) | |
@working_dir = working_dir | |
@git = Git.open(working_dir) | |
@versions = [] | |
@list_symbol = '*' | |
end | |
# Find the last version in git tags. | |
# The format must adhere semantic versioning. | |
def last_version | |
read_git_version_tags if @versions.empty? | |
@versions.last | |
end | |
def read_git_version_tags | |
tags = @git.tags.map do |v| | |
LOG.debug "Found version tag #{v.name}" | |
matches = /^v(?<version>\d+.\d+.\d+)$/.match(v.name) | |
matches[:version] unless matches.nil? | |
end | |
@versions = tags.compact.sort_by do |v| | |
Semantic::Version.new(v) | |
end | |
end | |
def last_version_tag | |
"v#{last_version}" | |
end | |
def changelog_path | |
File.join(@working_dir, 'debian/changelog') | |
end | |
def makefile_path | |
File.join(@working_dir, 'Makefile') | |
end | |
def appveyor_path | |
File.join(@working_dir, 'appveyor.yml') | |
end | |
def read_changelog | |
File.read(changelog_path) | |
end | |
def read_makefile | |
File.read(makefile_path) | |
end | |
def read_appveyor | |
File.read(appveyor_path) | |
end | |
def parse_debian_changelog | |
rx = /^(?<name>.*) \((?<version>.*)\) (?<distrib>.*); urgency=(?<urgency>.*)$/ | |
rx.match(read_changelog.lines.first) | |
end | |
def last_debian_version | |
meta = parse_debian_changelog | |
meta[:version] | |
end | |
def makefile_version | |
rx = /VERSION = v(?<version>\d+\.\d+\.\d+)/ | |
meta = rx.match(read_makefile) | |
meta[:version] | |
end | |
def update_makefile_version(version) | |
rx = /VERSION = v(?<version>\d+\.\d+\.\d+)/ | |
content = read_makefile | |
content.gsub! rx, "VERSION = v#{version}" | |
File.write makefile_path, content | |
end | |
def update_appveyor_version(version) | |
rx = /version: (?<version>\d+\.\d+\.\d+)\.{build}/ | |
content = read_appveyor | |
content.gsub! rx, "version: #{version}.{build}" | |
File.write appveyor_path, content | |
end | |
def do_all_stuff | |
changelog = {} | |
version = last_version_tag | |
# Fetching all commits between the last version and HEAD. | |
@git.log(1024).between(version, 'HEAD').each do |v| | |
lines = v.message.lines | |
brief = lines.first.strip | |
matches = /^(?<type>.+):(?<message>.*)$/.match brief | |
if matches.nil? | |
LOG.warn"malformed brief: #{brief}" | |
next | |
end | |
type = TYPES[matches[:type].to_sym] | |
next if type.nil? | |
# Remove type tag. | |
lines[0] = matches[:message] | |
changelog[type] = [] if changelog[type].nil? | |
changelog[type].push(lines) | |
end | |
lines = [] | |
TYPES.values.each do |type| | |
next if changelog[type].nil? | |
changelog[type].each do |entry| | |
brief = entry.first.strip | |
detailed = entry[1..-1] | |
lines.push("#{@list_symbol} #{type}: #{brief}.") | |
found_exclude = false | |
found_internals = false | |
detailed.each do |v| | |
v = v.strip.gsub('\\n', '') | |
next if v.size.zero? | |
found_internals = true if v.include? 'Internals:' | |
next if found_internals | |
found_exclude = true if v.include? 'Changelog: exclude' | |
next if found_exclude | |
reformat_wrapped(v).each do |s| | |
lines.push(" #{s}") | |
end | |
end | |
# End line with dot if missed. | |
lines[-1] << '.' unless lines[-1].end_with? '.' | |
end | |
end | |
lines = lines.map do |line| | |
' ' + line | |
end | |
result = lines.join("\n") | |
result.tr!('`', '"') | |
result | |
end | |
def reformat_wrapped(text, width = 78) | |
lines = [] | |
line = '' | |
text.split(/\s+/).each do |word| | |
if line.size + word.size >= width | |
lines << line | |
line = word | |
elsif line.empty? | |
line = word | |
else | |
line << ' ' << word | |
end | |
end | |
lines << line if line | |
lines | |
end | |
end | |
def main | |
options = {} | |
OptionParser.new do |opts| | |
opts.banner = 'Usage: chaman.rb [options]' | |
opts.on('-v', '--[no-]verbose', 'Run verbosely') do |v| | |
options[:verbose] = v | |
end | |
opts.on('-s', '--simulate', 'Perform no action') do |v| | |
options[:simulate] = v | |
end | |
opts.on_tail('-h', '--help', 'Show this message and exit') do | |
puts opts | |
exit | |
end | |
opts.on_tail('--version', 'Show version and exit') do | |
puts VERSION.join('.') | |
exit | |
end | |
end.parse! | |
LOG.info"Working directory `#{WORKING_DIR}`" | |
manager = ChangelogManager.new WORKING_DIR | |
LOG.info "Last known version in git: #{manager.last_version}" | |
LOG.info "Last known version in debian: #{manager.last_debian_version}" | |
LOG.info "Last known version in Makefile: #{manager.makefile_version}" | |
prompt = TTY::Prompt.new | |
version = Semantic::Version.new manager.last_version | |
version = prompt.select('What version to bump?') do |menu| | |
menu.choice version.increment!(:major).to_s | |
menu.choice version.increment!(:minor).to_s | |
menu.choice version.increment!(:patch).to_s | |
end | |
puts version | |
content = manager.do_all_stuff | |
deb_full_name = ENV['DEBFULLNAME'] || DEFAULT_AUTHOR_NAME | |
deb_email = ENV['DEBEMAIL'] || DEFAULT_AUTHOR_MAIL | |
time = Time.new | |
deb_formatted_date = time.strftime('%a, %d %b %Y %H:%M:%S %z') | |
changelog = manager.read_changelog | |
unless options[:simulate] | |
file = Tempfile.new('changelog') | |
meta = manager.parse_debian_changelog | |
file.write "#{meta[:name]} (#{version}) #{meta[:distrib]}; urgency=#{meta[:urgency]}" | |
file.write "\n\n" | |
file.write content | |
file.write "\n\n" | |
file.write " -- #{deb_full_name} <#{deb_email}> #{deb_formatted_date}" | |
file.write "\n\n" | |
file.write changelog | |
file.close | |
FileUtils.copy_file file.path, manager.changelog_path | |
manager.update_makefile_version version | |
manager.update_appveyor_version version | |
end | |
LOG.info 'Done' | |
end | |
main |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment