lib/gitscape/base.rb in gitscape-1.1 vs lib/gitscape/base.rb in gitscape-1.2

- old
+ new

@@ -19,47 +19,66 @@ def branch_names @repo.branches.map { |b| b.full } end - # Get the system's current git version - def git_version - @git_version ||= `git --version`.strip.split(" ").last - end - def checkout(branch_name) begin @repo.revparse(branch_name) rescue raise Exception.new "No branch '#{branch_name}' found" end puts "Switching to branch '#{branch_name}'..." @repo.checkout(branch_name) end - # Check if the system's git version is at least as recent as the version specified - def git_version_at_least(min_version) - def split_version(v) - v.split(".").map { |x| x.to_i } + def git_working_copy_is_clean puts_changes=true + # Check if the working copy is clean, if not, exit + changes = `git status -uno --ignore-submodules=all --porcelain` + working_copy_clean = changes.length == 0 + if !working_copy_clean && puts_changes + puts "Your working copy is not clean, either commit, stash, or reset your changes then try again." + puts changes end - local_version = split_version(git_version) - min_version = split_version(min_version) - raise "Git version string must have 4 parts" if min_version.size != 4 + working_copy_clean + end - 4.times do |i| - return false unless local_version[i] >= min_version[i] - end - true + def live_current_version_number + current_branch = @repo.current_branch + live_branch = @repo.branch "live" + + `git checkout #{live_branch.full}` unless current_branch == live_branch + + version_file = File.new("version", "r") + live_current_version_number = version_file.read.delete("i").to_i + + `git checkout #{current_branch}` unless current_branch == live_branch + + live_current_version_number end + def git_has_conflicts puts_conflicts=true + conflicts_status = `git status --porcelain | grep UU` + has_conflicts = conflicts_status.length > 0 + + puts conflicts_status if has_conflicts && puts_conflicts + + has_conflicts + end + def hotfix_start(hotfix_branch_name=nil) - checkout "master" + checkout "live" + if hotfix_branch_name.length == 0 + exception_message = "*** Improper Usage ***\nExpected Usage: hotfix_start <hotfix_branch_name>" + raise exception_message + end + hotfix_branch_name = "hotfix/#{hotfix_branch_name}" puts "Creating hotfix branch '#{hotfix_branch_name}'..." - @repo.checkout(@repo.branch.create(hotfix_branch_name)) + @repo.checkout(@repo.branch(hotfix_branch_name).create) end def hotfix_finish(hotfix_branch_name=nil) # TODO: # 1. Tag the new live revision with 'live/<branch_name_without_prefix>' @@ -106,11 +125,100 @@ # Checkout previous branch for user convenience `git checkout #{previous_branch}` end + def release_finish new_version_number=0 + # Check if the working copy is clean, if not, exit + exit 1 unless git_working_copy_is_clean + # Get the right release_branch_name to merge + current_version_number = new_version_number - 1 + if new_version_number == 0 + current_version_number = live_current_version_number + new_version_number = current_version_number + 1 + end + release_branch_name = "release/i#{new_version_number}" + release_branch = @repo.branch release_branch_name + + # Get branch information for checks + branch_keys = ["name", "revision", "message"] + branch_values = `git branch -av`.scan(/^[ \*]*([^ \*]+) +([^ ]+) +(.*)$/) + branches = branch_values.map {|components| Hash[ branch_keys.zip components ] } + branch_revisions = Hash[ branches.map {|branch| [branch["name"], branch["revision"]] } ] + + # Check if the required branches in sync + required_synced_branches = [ [release_branch_name, "remotes/origin/qa"], ["master", "remotes/origin/master"], ["live", "remotes/origin/live"] ] + required_synced_branches.each do |branch_pair| + if branch_revisions[ branch_pair[0] ] != branch_revisions[ branch_pair[0] ] + puts "*** ERROR: The #{branch_pair[0]} branch is not the same as the #{branch_pair[1]} branch. + \tPlease resolve this and try again." + exit 3 + end + end + + # Checkout live + `git checkout live` + + # Record the revision of live used for the rollback tag + live_rollback_revision = `git log -n1 --oneline`.scan(/(^[^ ]+) .*$/).flatten[0] + + merge_options = "--no-ff -s recursive -Xignore-space-change" + + # Merge the release branch into live + `git merge #{merge_options} #{release_branch_name}` + + # Error and conflict checking + if !$?.success? then exit 4 end + if git_has_conflicts then + puts "Merge conflicts when pulling #{release_branch_name} into live" + puts "Please bother Xavier 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 --stat live origin/qa` + if critical_diff.length > 0 + puts "This live merge has code that was not on the qa branch." + 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 whether the release should happen." + exit 3 + end + + # Record the revision of live used for the release tag + live_release_revision = `git log -n1 --oneline`.scan(/(^[^ ]+) .*$/).flatten[0] + + # Merge the release branch into master + `git checkout master` + `git merge #{merge_options} #{release_branch_name}` + + # Error and conflict checking + if !$?.success? then exit 4 end + if git_has_conflicts then + puts "Merge conflicts when pulling #{release_branch_name} into master" + puts "Please bother Xavier if you see this message :)" + exit 2 + end + + # Tag the state of live for both release and rollback + `git tag rollback-to/i#{current_version_number} #{live_rollback_revision}` + if !$?.success? then + puts "=== WARNING: Failed to create rollback-to/i#{current_version_number} tag" + `git tag -d rollback-to/i#{current_version_number}` + end + + `git tag live/i#{new_version_number}/release #{live_release_revision}` + if !$?.success? then + `git tag -d rollback-to/i#{current_version_number}` + `git tag -d live/i#{new_version_number}/release #{live_release_revision}` + exit 4 + end + + `git push origin live --tags` + `git push origin master` + end + # Returns true if the supplied Git commit hash or reference exists def self.commit_exists?(commit_id) `git rev-parse #{commit_id}` if $? == 0 true @@ -136,9 +244,30 @@ 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 + + # Check if the system's git version is at least as recent as the version specified + def git_version_at_least(min_version) + def split_version(v) + v.split(".").map { |x| x.to_i } + end + local_version = split_version(git_version) + min_version = split_version(min_version) + + raise "Git version string must have 4 parts" if min_version.size != 4 + + 4.times do |i| + return false unless local_version[i] >= min_version[i] + end + true end def self.result_ok?(result) if result.nil? or result == 0 puts "done"