lib/gitscape/base.rb in gitscape-1.6.6 vs lib/gitscape/base.rb in gitscape-1.7.2

- old
+ new

@@ -2,12 +2,10 @@ class Gitscape::Base def initialize - # Use the current directory as our target repository - @repo = Git.open "." # Always add a merge commit at the end of a merge @merge_options = "--no-ff" # Setup additional merge options based on the version of Git we have @@ -25,10 +23,11 @@ "qa" when "live" "live" end end + end def git_working_copy_is_clean puts_changes=true # Check if the working copy is clean, if not, exit @@ -41,13 +40,19 @@ end working_copy_clean end + # Assume the highest branch already merged into live of the form + # release/i[\d]+ is the live branch def live_iteration live_iteration_tag_regex = /^live\/i(\d+)/ toRet = `git tag`.split("\n").select { |tag| live_iteration_tag_regex.match tag }.map { |tag| tag.scan(live_iteration_tag_regex).flatten[0].to_i }.sort.last + # A bit of a chicken-and-egg problem. You might not have any tags for live, so look for something else... + if toRet.nil? + toRet = `git branch -a --merged origin/live`.split("\n").select{|b| /release\/i(\d+)$/.match b}.map{|b| b.scan(/release\/i(\d+)$/).flatten[0].to_i}.sort.last + end toRet end def current_branch_name toRet = `git branch`.scan(/\* (.*)$/).flatten[0] @@ -78,86 +83,128 @@ puts conflicts_status if has_conflicts && puts_conflicts has_conflicts end - def hotfix_start hotfix_branch=nil, options={:push=>false} + def generic_branch_start branch_type, from_branch, new_branch, options # option defaults options[:push] = false if options[:push].nil? # Check that the working copy is clean exit 1 unless git_working_copy_is_clean - puts `git checkout live` - puts `git pull origin` + if new_branch.to_s.length == 0 + raise "*** Improper Usage ***\nExpected Usage: #{branch_type}_start <#{branch_type}_name> [--[no-]push]" + end - if hotfix_branch.to_s.length == 0 - exception_message = "*** Improper Usage ***\nExpected Usage: hotfix_start <hotfix_name> [--[no-]push]" - raise exception_message + puts `git checkout #{from_branch}` + puts `git pull origin #{from_branch}` + + new_branch = "#{branch_type}/#{new_branch}" + puts "=== Creating #{branch_type} branch '#{new_branch}' ===" + + puts `git checkout -b #{new_branch}` + puts `git push origin #{new_branch}` if options[:push] + end + + def hotfix_start new_branch=nil, options={:push=>false} + generic_branch_start 'hotfix', 'live', new_branch, options + end + + def bugfix_start new_branch=nil, options={:push=>false} + name = current_release_branch_name + if name.nil? + puts 'There is not a current release branch. You cannot use this command.' + else + generic_branch_start 'bugfix', name, new_branch, options end + end - hotfix_branch = "hotfix/#{hotfix_branch}" - puts "=== Creating hotfix branch '#{hotfix_branch}' ===" + def feature_start new_branch=nil, options={:push=>false} + generic_branch_start 'feature', 'master', new_branch, options + end - puts `git checkout -b #{hotfix_branch}` - puts `git push origin #{hotfix_branch}` if options[:push] + def hotfix_finish branch_name=nil, options={:env_depth=>:staging, :push=>true, :update_env=>false} + generic_branch_finish 'hotfix', branch_name, options end - def hotfix_finish hotfix_branch=nil, options={:env_depth=>:staging, :push=>true, :update_env=>false} + def bugfix_finish branch_name=nil, options={:env_depth=>:staging, :push=>true, :update_env=>false} + generic_branch_finish 'bugfix', branch_name, options + end + + def feature_finish branch_name=nil, options={:env_depth=>:staging, :push=>true, :update_env=>false} + generic_branch_finish 'feature', branch_name, options + end + + def generic_branch_finish branch_type, source_name, options # option defaults options[:env_depth] = :staging if options[:env_depth].nil? options[:push] = true if options[:push].nil? options[:update_env] = false if options[:update_env].nil? + if (options[:env_depth] == :qa) && branch_type == 'feature' + puts "*** --qa may not be used with feature branches" + exit 1 + end + if (options[:env_depth] == :live) && branch_type != 'hotfix' + puts "*** --live may only be used with hotfix branches" + exit 1 + end + + # Check that the working copy is clean exit 1 unless git_working_copy_is_clean - usage_string = "expected usage: hotfix_finish [<hotfix_branch>] - hotfix_branch: the name of the hotfix branch to finish. - if ommitted, you must currently be on a hotfix branch" - - hotfix_branch = "hotfix/#{hotfix_branch}" + source_branch = "#{branch_type}/#{source_branch}" previous_branch = current_branch_name - if previous_branch.to_s.start_with? "hotfix/" - hotfix_branch = previous_branch + if previous_branch.to_s.start_with? "#{branch_type}/" + source_branch = previous_branch end - if hotfix_branch.to_s.empty? - puts "!!! Not currently on a hotfix branch, and no branch name was provided as an argument !!!" - puts usage_string + if source_branch.to_s.empty? + puts "!!! Not currently on a #{branch_type} branch, and no branch name was provided as an argument !!!" + puts finish_usage_string(branch_type) exit 1 end # Collect the set of branches we'd like to merge the hotfix into merge_branches = ["master"] - merge_branches << current_release_branch_name if [:qa, :live].include?(options[:env_depth]) - merge_branches << "live" if options[:env_depth] == :live + if %w{bugfix hotfix}.include?(branch_type) + if current_release_branch_name.nil? + puts "!!! There is no current release branch: the command will bypass the release and qa branches" + else + merge_branches << current_release_branch_name if [:qa, :live].include?(options[:env_depth]) + end + end + if %w{hotfix}.include?(branch_type) + merge_branches << "live" if options[:env_depth] == :live + end - # Merge the hotfix into merge_branches - puts "=== Merging hotfix into branches #{merge_branches} ===" + # Merge the source branch into merge_branches + puts "=== Merging #{branch_type} into branches #{merge_branches} ===" for branch in merge_branches # Calculate merge_options merge_options = @merge_options merge_options += " --log" if branch == "master" # Attempt merge puts `git checkout #{branch}` puts `git pull` - puts `git merge #{merge_options} #{hotfix_branch}` + puts `git merge #{merge_options} #{source_branch}` # Bail on failures exit 1 if !$?.success? raise "Merge failure(s) on #{branch}.\nResolve conflicts, and run the script again." if git_has_conflicts puts `git push origin #{branch}` if options[:push] puts `git push origin #{branch}:#{@env_branch_by_dev_branch[branch]}` if options[:update_env] # If we just merged the live branch, tag this revision, and push that tag to origin if branch == "live" - puts `git tag live/i#{live_iteration}/#{hotfix_branch}` + puts `git tag live/i#{live_iteration}/#{source_branch}` puts `git push --tags` end end @@ -255,20 +302,20 @@ # Error and conflict checking if !$?.success? then exit 4 end if git_has_conflicts then puts "Merge conflicts when pulling #{release_branch} into live" - puts "Please bother Xavier if you see this message :)" + puts "Please report a problem if you see this message :)" exit 2 end # Ensure there is zero diff between what was tested on origin/qa and the new live critical_diff = `git diff live origin/qa` if critical_diff.length > 0 puts "\n!!! This live merge has code that was not on the qa branch !!!\nDiff:" puts critical_diff - puts "!!! Run the command 'git reset --hard' to undo the merge, and raise this error with Phil and others involved to determine next step !!!" + puts "!!! Run the command 'git reset --hard' to undo the merge, and raise this error with QA and others involved to determine next step !!!" exit 3 end # Record the revision of live used for the release tag live_release_revision = `git log -n1 --oneline`.scan(/(^[^ ]+) .*$/).flatten[0] @@ -280,11 +327,11 @@ # Error and conflict checking if !$?.success? then exit 4 end if git_has_conflicts then puts "Merge conflicts when pulling #{release_branch} into master" - puts "Please bother Xavier if you see this message :)" + puts "Please report a problem if you see this message :)" exit 2 end # Tag the state of live for both release and rollback puts `git tag rollback-to/i#{current_version_number} live~` @@ -315,11 +362,11 @@ # Select which tags to keep. # We currently keep tags which fulfill any of the following # 1. starts with 'service/' # 2. starts with 'rollback-to/' or 'live/', and has an iteration number >= the live_iteration number - 3 tags = `git tag`.split "\n" - tags_to_delete = tags.select { |tag| !(!/^service\//.match(tag).nil? || /^(?:live|rollback-to)\/i(\d+)/.match(tag).to_a[1].to_i >= live_iteration.to_i - 3) } + tags_to_delete = tags.select { |tag| !(!/^service\//.match(tag).nil? || /^(?:live|rollback-to)\/i(\d+)/.match(tag).to_a[1].to_i >= live_iteration - 3) } puts "Deleting the following tags.\nThese changes #{options[:push] ? "will" : "will not"} be pushed to origin.\n" tags_to_delete.each { |tag| puts `git tag -d #{tag}` } tags_to_delete.each { |tag| puts `git push origin :refs/tags/#{tag}` } if options[:push] @@ -333,31 +380,10 @@ else raise "Invalid commit/ref ID: #{commit_id}" end end - def self.run_script(script, quiet=true) - IO.popen(script.split("\n").join(" && ")) do |io| - while (line = io.gets) do - unless quiet - puts line - end - end - end - $?.exitstatus - end - - def promote_branch(head, upstream) - run_script <<-EOH - git fetch - git stash - git checkout #{head} - git reset --hard origin/#{head} - git push -f origin #{head}:#{upstream} - EOH - end - # Get the system's current git version def git_version @git_version ||= `git --version`.strip.split(" ").last end @@ -376,67 +402,12 @@ return local_version[i] > min_version[i] end true # If you get all the way here, all 4 positions match precisely end - def self.result_ok?(result) - if result.nil? or result == 0 - puts "done" - return true - else - puts "failed" - puts "Aborting" - run_script "git checkout master" - return false - end - end - - def self.start_iteration(iteration, projects=PROJECTS) - projects.each do |proj| - print "Cutting branch #{iteration} for #{proj}..." - result = run_script <<-EOH - cd /code/#{proj}/ - git fetch - git branch #{iteration} origin/master - git push origin #{iteration} - git push -f origin #{iteration}:qa - EOH - return unless result_ok?(result) - end - end - - def self.deploy_iteration(iteration, projects=PROJECTS) - date = `date +%Y%m%d-%H%M`.strip - tag = "#{iteration}-#{date}" - puts "Starting deploy of #{iteration}" - puts "Will tag with '#{tag}'" - puts - - projects.each do |proj| - print "Tagging #{proj}..." - result = run_script <<-EOH - cd /code/#{proj}/ - git stash - git checkout qa - git fetch - git reset --hard origin/qa - git tag -a #{tag} -m 'Release to live' - EOH - return unless result_ok?(result) - end - - projects.each do |proj| - print "Pushing #{proj}..." - result = run_script <<-EOH - cd /code/#{proj}/ - git push -f origin qa:live - git push --tags - git checkout master - EOH - #return unless result_ok?(result) - end - - puts - puts "Deploy of #{iteration} completed successfully." + def finish_usage_string(branch_type) + "expected usage: #{branch_type}_finish [<#{branch_type}_branch>] + #{branch_type}_branch: the name of the #{branch_type} branch to finish. + if omitted, you must currently be on a #{branch_type} branch" end end