#!/usr/bin/env ruby require 'optparse' SPEC_TYPE = 'gemspec' :major :minor :patch @options = {:dry_run => false, :bump_number => :patch} OptionParser.new { |opts| opts.banner = 'Usage: bump.rb [options]' opts.on('-d', '--dry-run', 'Dry run') do |v| @options[:dry_run] = v end opts.on('-a', '--major', 'Bump major version') do |v| @options[:bump_number] = :major end opts.on('-m', '--minor', 'Bump minor version') do |v| @options[:bump_number] = :minor end opts.on('-p', '--patch', 'Bump patch version') do |v| @options[:bump_number] = :patch end opts.on('-r', '--revert', 'Revert last bump') do |v| @options[:revert] = v end }.parse! p @options def check_repo_is_clean_or_dry_run value =%x[#{'git status --porcelain'}] if value.empty? puts 'Repo is clean -> continue' else if @options[:dry_run] puts 'Repo not clean, "Dry run" enabled -> continue' else puts 'Repository not clean -> exit' exit end end end def find_spec_file list_of_specs = execute_line("find . -name '*.#{SPEC_TYPE}'") arr = list_of_specs.split("\n") spec_file = '' case arr.count when 0 puts "No #{SPEC_TYPE} files found. -> Exit." exit when 1 spec_file = arr[0] else puts 'Which spec should be used?' arr.each_with_index { |file, index| puts "#{index+1}. #{file}" } input_index = Integer(gets.chomp) spec_file = arr[input_index-1] end if spec_file == nil puts "Can't find specified spec file -> exit" exit end spec_file.sub('./', '') end def find_current_gem_file list_of_specs = execute_line("find . -name '*.gem'") arr = list_of_specs.split("\n") spec_file = '' case arr.count when 0 puts "No #{SPEC_TYPE} files found. -> Exit." exit when 1 spec_file = arr[0] else puts 'Which spec should be used?' arr.each_with_index { |file, index| puts "#{index+1}. #{file}" } input_index = Integer(gets.chomp) spec_file = arr[input_index-1] end if spec_file == nil puts "Can't find specified spec file -> exit" exit end spec_file.sub('./', '') end def find_version_in_podspec(podspec) readme = File.read(podspec) #try to find version in format 1.22.333 re = /(\d+)\.(\d+)\.(\d+)/m match_result = re.match(readme) unless match_result puts 'Not found any versions' exit end puts "Found version #{match_result[0]}" return match_result[0], match_result.captures end def bump_version(versions_array) bumped_result = versions_array.dup bumped_result.map! { |x| x.to_i } case @options[:bump_number] when :major bumped_result[0] += 1 bumped_result[1] = 0 bumped_result[2] = 0 when :minor bumped_result[1] += 1 bumped_result[2] = 0 when :patch bumped_result[2] += 1 else raise('unknown bump_number') end bumped_version = bumped_result.join('.') puts "Bump version: #{versions_array.join('.')} -> #{bumped_version}" bumped_version end def execute_line(line) output = `#{line}` check_exit_status(output) output end def execute_line_if_not_dry_run(line) if @options[:dry_run] puts "Dry run: #{line}" nil else puts line value = %x[#{line}] puts value check_exit_status(value) value end end def check_exit_status(output) if $?.exitstatus != 0 puts "Output:\n#{output}\nExit status = #{$?.exitstatus} ->Terminate script." exit end end def run_bumping_script check_repo_is_clean_or_dry_run spec_file = find_spec_file result, versions_array = find_version_in_podspec(spec_file) bumped_version = bump_version(versions_array) unless @options[:dry_run] puts 'Are you sure? Click Y to continue:' str = gets.chomp if str != 'Y' puts '-> exit' exit end end execute_line_if_not_dry_run("sed -i \"\" \"s/#{result}/#{bumped_version}/\" README.md") 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 tag #{bumped_version}") execute_line_if_not_dry_run('git push') execute_line_if_not_dry_run('git push --tags') execute_line_if_not_dry_run("gem build #{spec_file}") gem = find_current_gem_file execute_line_if_not_dry_run("gem push #{gem}") # execute_line_if_not_dry_run("pod trunk push #{spec_file}") end def revert_last_bump spec_file = find_spec_file result, _ = find_version_in_podspec(spec_file) puts "DELETE tag #{result} and HARD reset HEAD~1?\nClick Y to continue:" str = gets.chomp if str != 'Y' puts '-> exit' exit end 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 push --delete origin #{result}") end if __FILE__ == $0 if @options[:revert] revert_last_bump else run_bumping_script end end