thegarage-gitx 1.5.4 → 1.5.5.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/thegarage/gitx.rb +0 -2
- data/lib/thegarage/gitx/cli.rb +29 -92
- data/lib/thegarage/gitx/git.rb +145 -58
- data/lib/thegarage/gitx/runner.rb +24 -0
- data/lib/thegarage/gitx/version.rb +1 -1
- data/spec/thegarage/gitx/cli_spec.rb +57 -221
- data/spec/thegarage/gitx/git_spec.rb +231 -0
- data/thegarage-gitx.gemspec +1 -1
- metadata +8 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1b5057c596d4343322b8f10cb5cc5ffa5d88cc5d
|
4
|
+
data.tar.gz: 0a0d051f8550bf0e3570e900bfffa4c11bf17e29
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 363c8537bd5f8a1332a523532d4554f5b7ae72a7cab3c5e943eaf7dbd9239ab1bbe7baac6bc7a97aa8f8f8d86d1992c6c1c19e5bee3c9ed007ac0ce1ab6c28fb
|
7
|
+
data.tar.gz: a6263f625f0c130626419a035d90e8f64c6e7066ff738a1be3dfd3af4c6df791972aff7e81f96ad01859a1195977467d7c8d90827c11cfda5d08c045778d8f9a
|
data/lib/thegarage/gitx.rb
CHANGED
data/lib/thegarage/gitx/cli.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
+
require 'English'
|
1
2
|
require "thor"
|
2
3
|
require 'rest_client'
|
3
4
|
require 'thegarage/gitx'
|
5
|
+
require 'thegarage/gitx/git'
|
4
6
|
require 'thegarage/gitx/github'
|
7
|
+
require 'thegarage/gitx/runner'
|
5
8
|
|
6
9
|
module Thegarage
|
7
10
|
module Gitx
|
@@ -9,10 +12,6 @@ module Thegarage
|
|
9
12
|
include Thor::Actions
|
10
13
|
add_runtime_options!
|
11
14
|
|
12
|
-
include Thegarage::Gitx::Git
|
13
|
-
|
14
|
-
TAGGABLE_BRANCHES = %w(master staging)
|
15
|
-
|
16
15
|
method_option :trace, :type => :boolean, :aliases => '-v'
|
17
16
|
def initialize(*args)
|
18
17
|
super(*args)
|
@@ -28,87 +27,53 @@ module Thegarage
|
|
28
27
|
def reviewrequest
|
29
28
|
fail 'Github authorization token not found' unless github.authorization_token
|
30
29
|
|
31
|
-
|
30
|
+
branch = git.current_branch.name
|
31
|
+
pull_request = github.find_pull_request(branch)
|
32
32
|
if pull_request.nil?
|
33
|
-
update
|
34
|
-
changelog = run_cmd "git log #{Thegarage::Gitx::BASE_BRANCH}...#{
|
35
|
-
pull_request = github.create_pull_request(
|
33
|
+
git.update
|
34
|
+
changelog = runner.run_cmd "git log #{Thegarage::Gitx::BASE_BRANCH}...#{branch} --no-merges --pretty=format:'* %s%n%b'"
|
35
|
+
pull_request = github.create_pull_request(branch, changelog, options)
|
36
36
|
say 'Pull request created: '
|
37
37
|
say pull_request['html_url'], :green
|
38
38
|
end
|
39
39
|
github.assign_pull_request(pull_request, options[:assignee]) if options[:assignee]
|
40
40
|
|
41
|
-
run_cmd "open #{pull_request['html_url']}" if options[:open]
|
41
|
+
runner.run_cmd "open #{pull_request['html_url']}" if options[:open]
|
42
42
|
end
|
43
43
|
|
44
|
-
# TODO: use --no-edit to skip merge messages
|
45
|
-
# TODO: use pull --rebase to skip merge commit
|
46
44
|
desc 'update', 'Update the current branch with latest changes from the remote feature branch and master'
|
47
45
|
def update
|
48
|
-
|
49
|
-
|
50
|
-
say 'updating '
|
51
|
-
say "#{branch} ", :green
|
52
|
-
say "to have most recent changes from "
|
53
|
-
say Thegarage::Gitx::BASE_BRANCH, :green
|
54
|
-
|
55
|
-
run_cmd "git pull origin #{branch}", :allow_failure => true
|
56
|
-
run_cmd "git pull origin #{Thegarage::Gitx::BASE_BRANCH}"
|
57
|
-
run_cmd 'git push origin HEAD'
|
46
|
+
git.update
|
58
47
|
end
|
59
48
|
|
60
49
|
desc 'cleanup', 'Cleanup branches that have been merged into master from the repo'
|
61
50
|
def cleanup
|
62
|
-
|
63
|
-
run_cmd "git pull"
|
64
|
-
run_cmd 'git remote prune origin'
|
65
|
-
|
66
|
-
say "Deleting branches that have been merged into "
|
67
|
-
say Thegarage::Gitx::BASE_BRANCH, :green
|
68
|
-
branches(:merged => true, :remote => true).each do |branch|
|
69
|
-
run_cmd "git push origin --delete #{branch}" unless aggregate_branch?(branch)
|
70
|
-
end
|
71
|
-
branches(:merged => true).each do |branch|
|
72
|
-
run_cmd "git branch -d #{branch}" unless aggregate_branch?(branch)
|
73
|
-
end
|
51
|
+
git.cleanup
|
74
52
|
end
|
75
53
|
|
76
54
|
desc 'track', 'set the current branch to track the remote branch with the same name'
|
77
55
|
def track
|
78
|
-
|
56
|
+
git.track
|
79
57
|
end
|
80
58
|
|
81
59
|
desc 'start', 'start a new git branch with latest changes from master'
|
82
60
|
def start(branch_name = nil)
|
83
|
-
|
84
|
-
example_branch = %w{ api-fix-invalid-auth desktop-cleanup-avatar-markup share-form-add-edit-link }.
|
85
|
-
|
86
|
-
remote_branches = repo.remotes.collect {|b| b.name.split('/').last }
|
87
|
-
until branch_name = ask("What would you like to name your branch? (ex: #{example_branch})") {|q|
|
88
|
-
q.validate = Proc.new { |branch|
|
89
|
-
branch =~ /^[A-Za-z0-9\-_]+$/ && !remote_branches.include?(branch)
|
90
|
-
}
|
91
|
-
}
|
92
|
-
end
|
61
|
+
until git.valid_new_branch_name?(branch_name)
|
62
|
+
example_branch = %w{ api-fix-invalid-auth desktop-cleanup-avatar-markup share-form-add-edit-link }.sample
|
63
|
+
branch_name = ask("What would you like to name your branch? (ex: #{example_branch})")
|
93
64
|
end
|
94
65
|
|
95
|
-
|
96
|
-
run_cmd 'git pull'
|
97
|
-
run_cmd "git checkout -b #{branch_name}"
|
66
|
+
git.start branch_name
|
98
67
|
end
|
99
68
|
|
100
69
|
desc 'share', 'Share the current branch in the remote repository'
|
101
70
|
def share
|
102
|
-
|
71
|
+
git.share
|
103
72
|
end
|
104
73
|
|
105
74
|
desc 'integrate', 'integrate the current branch into one of the aggregate development branches'
|
106
75
|
def integrate(target_branch = 'staging')
|
107
|
-
|
108
|
-
|
109
|
-
update
|
110
|
-
integrate_branch(branch, target_branch)
|
111
|
-
run_cmd "git checkout #{branch}"
|
76
|
+
git.integrate target_branch
|
112
77
|
end
|
113
78
|
|
114
79
|
desc 'nuke', 'nuke the specified aggregate branch and reset it to a known good state'
|
@@ -117,63 +82,35 @@ module Thegarage
|
|
117
82
|
good_branch = options[:destination] || ask("What branch do you want to reset #{bad_branch} to? (default: #{bad_branch})")
|
118
83
|
good_branch = bad_branch if good_branch.length == 0
|
119
84
|
|
120
|
-
last_known_good_tag =
|
121
|
-
raise "No known good tag found for branch: #{good_branch}. Verify tag exists via `git tag -l 'build-#{good_branch}-*'`" unless last_known_good_tag
|
85
|
+
last_known_good_tag = git.current_build_tag(good_branch)
|
122
86
|
return unless yes?("Reset #{bad_branch} to #{last_known_good_tag}? (y/n)", :green)
|
123
87
|
|
124
|
-
|
88
|
+
git.nuke bad_branch, last_known_good_tag
|
125
89
|
end
|
126
90
|
|
127
91
|
desc 'release', 'release the current branch to production'
|
128
92
|
def release
|
129
|
-
|
130
|
-
|
131
|
-
update
|
132
|
-
|
133
|
-
return unless yes?("Release #{branch} to production? (y/n)", :green)
|
134
|
-
run_cmd "git checkout #{Thegarage::Gitx::BASE_BRANCH}"
|
135
|
-
run_cmd "git pull origin #{Thegarage::Gitx::BASE_BRANCH}"
|
136
|
-
run_cmd "git pull . #{branch}"
|
137
|
-
run_cmd "git push origin HEAD"
|
138
|
-
integrate_branch('master', 'staging')
|
139
|
-
cleanup
|
93
|
+
return unless yes?("Release #{git.current_branch.name} to production? (y/n)", :green)
|
94
|
+
git.release
|
140
95
|
end
|
141
96
|
|
142
97
|
desc 'buildtag', 'create a tag for the current Travis-CI build and push it back to origin'
|
143
98
|
def buildtag
|
144
|
-
|
145
|
-
pull_request = ENV['TRAVIS_PULL_REQUEST']
|
146
|
-
|
147
|
-
raise "Unknown branch. ENV['TRAVIS_BRANCH'] is required." unless branch
|
148
|
-
|
149
|
-
if pull_request != 'false'
|
150
|
-
say "Skipping creation of tag for pull request: #{pull_request}"
|
151
|
-
elsif !TAGGABLE_BRANCHES.include?(branch)
|
152
|
-
say "Cannot create build tag for branch: #{branch}. Only #{TAGGABLE_BRANCHES} are supported."
|
153
|
-
else
|
154
|
-
label = "Generated tag from TravisCI build #{ENV['TRAVIS_BUILD_NUMBER']}"
|
155
|
-
create_build_tag(branch, label)
|
156
|
-
end
|
99
|
+
git.buildtag
|
157
100
|
end
|
158
101
|
|
159
102
|
private
|
160
103
|
|
161
|
-
|
162
|
-
|
163
|
-
def run_cmd(cmd, options = {})
|
164
|
-
output = run(cmd, capture: true)
|
165
|
-
success = $CHILD_STATUS.to_i == 0
|
166
|
-
fail "#{cmd} failed" unless success || options[:allow_failure]
|
167
|
-
output
|
104
|
+
def github
|
105
|
+
@github ||= Thegarage::Gitx::Github.new(git.repo, shell)
|
168
106
|
end
|
169
107
|
|
170
|
-
|
171
|
-
|
172
|
-
options[:pretend]
|
108
|
+
def git
|
109
|
+
@git ||= Thegarage::Gitx::Git.new(shell, runner)
|
173
110
|
end
|
174
111
|
|
175
|
-
def
|
176
|
-
@
|
112
|
+
def runner
|
113
|
+
@runner ||= Thegarage::Gitx::Runner.new(shell, options)
|
177
114
|
end
|
178
115
|
end
|
179
116
|
end
|
data/lib/thegarage/gitx/git.rb
CHANGED
@@ -1,24 +1,140 @@
|
|
1
|
-
require 'grit'
|
2
1
|
require 'pathname'
|
2
|
+
require 'rugged'
|
3
3
|
|
4
4
|
module Thegarage
|
5
5
|
module Gitx
|
6
|
-
|
7
|
-
AGGREGATE_BRANCHES = %w
|
8
|
-
RESERVED_BRANCHES = %w
|
6
|
+
class Git
|
7
|
+
AGGREGATE_BRANCHES = %w( staging prototype )
|
8
|
+
RESERVED_BRANCHES = %w( HEAD master next_release ) + AGGREGATE_BRANCHES
|
9
|
+
TAGGABLE_BRANCHES = %w( master staging )
|
10
|
+
|
11
|
+
attr_accessor :shell, :runner, :repo
|
12
|
+
|
13
|
+
def initialize(shell, runner, path = Dir.pwd)
|
14
|
+
@shell = shell
|
15
|
+
@runner = runner
|
16
|
+
root_path = Rugged::Repository.discover(path)
|
17
|
+
@repo = Rugged::Repository.new(root_path)
|
18
|
+
end
|
9
19
|
|
10
|
-
|
11
|
-
|
12
|
-
|
20
|
+
def update
|
21
|
+
shell.say 'Updating '
|
22
|
+
shell.say "#{current_branch.name} ", :green
|
23
|
+
shell.say "with latest changes from "
|
24
|
+
shell.say Thegarage::Gitx::BASE_BRANCH, :green
|
25
|
+
|
26
|
+
runner.run_cmd "git pull origin #{current_branch.name}", :allow_failure => true
|
27
|
+
runner.run_cmd "git pull origin #{Thegarage::Gitx::BASE_BRANCH}"
|
28
|
+
runner.run_cmd 'git push origin HEAD'
|
29
|
+
end
|
30
|
+
|
31
|
+
def track
|
32
|
+
runner.run_cmd "git branch --set-upstream-to origin/#{current_branch.name}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def share
|
36
|
+
runner.run_cmd "git push origin #{current_branch.name}"
|
37
|
+
track
|
38
|
+
end
|
39
|
+
|
40
|
+
def valid_new_branch_name?(branch)
|
41
|
+
remote_branches = Rugged::Branch.each_name(repo, :remote).to_a.map { |branch| branch.split('/').last }
|
42
|
+
branch =~ /^[A-Za-z0-9\-_]+$/ && !remote_branches.include?(branch)
|
43
|
+
end
|
44
|
+
|
45
|
+
def start(branch_name)
|
46
|
+
runner.run_cmd "git checkout #{Thegarage::Gitx::BASE_BRANCH}"
|
47
|
+
runner.run_cmd 'git pull'
|
48
|
+
runner.run_cmd "git checkout -b #{branch_name}"
|
13
49
|
end
|
14
50
|
|
15
|
-
|
51
|
+
def cleanup
|
52
|
+
runner.run_cmd "git checkout #{Thegarage::Gitx::BASE_BRANCH}"
|
53
|
+
runner.run_cmd "git pull"
|
54
|
+
runner.run_cmd 'git remote prune origin'
|
55
|
+
|
56
|
+
shell.say "Deleting branches that have been merged into "
|
57
|
+
shell.say Thegarage::Gitx::BASE_BRANCH, :green
|
58
|
+
branches(:merged => true, :remote => true).each do |branch|
|
59
|
+
runner.run_cmd "git push origin --delete #{branch}" unless aggregate_branch?(branch)
|
60
|
+
end
|
61
|
+
branches(:merged => true).each do |branch|
|
62
|
+
runner.run_cmd "git branch -d #{branch}" unless aggregate_branch?(branch)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def integrate(target_branch = 'staging')
|
67
|
+
update
|
68
|
+
|
69
|
+
branch = current_branch.name
|
70
|
+
integrate_branch(branch, target_branch)
|
71
|
+
runner.run_cmd "git checkout #{branch}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def current_build_tag(branch)
|
75
|
+
last_build_tag = build_tags_for_branch(branch).last
|
76
|
+
raise "No known good tag found for branch: #{branch}. Verify tag exists via `git tag -l 'build-#{branch}-*'`" unless last_build_tag
|
77
|
+
last_build_tag
|
78
|
+
end
|
79
|
+
|
80
|
+
# reset the specified aggregate branch to the same set of commits as the destination branch
|
81
|
+
def nuke(outdated_branch, target_reference)
|
82
|
+
return if outdated_branch == target_reference
|
83
|
+
fail "Only aggregate branches are allowed to be reset: #{AGGREGATE_BRANCHES}" unless aggregate_branch?(outdated_branch)
|
84
|
+
return if migrations_need_to_be_reverted?
|
85
|
+
|
86
|
+
shell.say "Resetting "
|
87
|
+
shell.say "#{outdated_branch} ", :green
|
88
|
+
shell.say "branch to "
|
89
|
+
shell.say target_reference, :green
|
90
|
+
|
91
|
+
runner.run_cmd "git checkout #{Thegarage::Gitx::BASE_BRANCH}"
|
92
|
+
runner.run_cmd "git branch -D #{outdated_branch}", :allow_failure => true
|
93
|
+
runner.run_cmd "git push origin --delete #{outdated_branch}", :allow_failure => true
|
94
|
+
runner.run_cmd "git checkout -b #{outdated_branch} #{target_reference}"
|
95
|
+
runner.run_cmd "git push origin #{outdated_branch}"
|
96
|
+
runner.run_cmd "git branch --set-upstream-to origin/#{outdated_branch}"
|
97
|
+
runner.run_cmd "git checkout #{Thegarage::Gitx::BASE_BRANCH}"
|
98
|
+
end
|
99
|
+
|
100
|
+
# lookup the current branch of the repo
|
16
101
|
def current_branch
|
17
|
-
|
102
|
+
repo.branches.find(&:head?)
|
18
103
|
end
|
19
104
|
|
20
|
-
def
|
21
|
-
|
105
|
+
def release
|
106
|
+
branch = current_branch.name
|
107
|
+
assert_not_protected_branch!(branch, 'release')
|
108
|
+
update
|
109
|
+
|
110
|
+
runner.run_cmd "git checkout #{Thegarage::Gitx::BASE_BRANCH}"
|
111
|
+
runner.run_cmd "git pull origin #{Thegarage::Gitx::BASE_BRANCH}"
|
112
|
+
runner.run_cmd "git pull . #{branch}"
|
113
|
+
runner.run_cmd "git push origin HEAD"
|
114
|
+
integrate('staging')
|
115
|
+
cleanup
|
116
|
+
end
|
117
|
+
|
118
|
+
def buildtag
|
119
|
+
branch = ENV['TRAVIS_BRANCH']
|
120
|
+
pull_request = ENV['TRAVIS_PULL_REQUEST']
|
121
|
+
|
122
|
+
raise "Unknown branch. ENV['TRAVIS_BRANCH'] is required." unless branch
|
123
|
+
|
124
|
+
if pull_request != 'false'
|
125
|
+
shell.say "Skipping creation of tag for pull request: #{pull_request}"
|
126
|
+
elsif !TAGGABLE_BRANCHES.include?(branch)
|
127
|
+
shell.say "Cannot create build tag for branch: #{branch}. Only #{TAGGABLE_BRANCHES} are supported."
|
128
|
+
else
|
129
|
+
label = "Generated tag from TravisCI build #{ENV['TRAVIS_BUILD_NUMBER']}"
|
130
|
+
create_build_tag(branch, label)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
def assert_not_protected_branch!(branch, action)
|
137
|
+
raise "Cannot #{action} reserved branch" if RESERVED_BRANCHES.include?(branch) || aggregate_branch?(branch)
|
22
138
|
end
|
23
139
|
|
24
140
|
# retrieve a list of branches
|
@@ -36,56 +152,27 @@ module Thegarage
|
|
36
152
|
branches.uniq
|
37
153
|
end
|
38
154
|
|
39
|
-
# reset the specified aggregate branch to the same set of commits as the destination branch
|
40
|
-
def nuke_branch(outdated_branch, head_branch)
|
41
|
-
return if outdated_branch == head_branch
|
42
|
-
fail "Only aggregate branches are allowed to be reset: #{AGGREGATE_BRANCHES}" unless aggregate_branch?(outdated_branch)
|
43
|
-
return if migrations_need_to_be_reverted?
|
44
|
-
|
45
|
-
say "Resetting "
|
46
|
-
say "#{outdated_branch} ", :green
|
47
|
-
say "branch to "
|
48
|
-
say head_branch, :green
|
49
|
-
|
50
|
-
run_cmd "git checkout #{Thegarage::Gitx::BASE_BRANCH}"
|
51
|
-
run_cmd "git branch -D #{outdated_branch}", :allow_failure => true
|
52
|
-
run_cmd "git push origin --delete #{outdated_branch}", :allow_failure => true
|
53
|
-
run_cmd "git checkout -b #{outdated_branch} #{head_branch}"
|
54
|
-
share_branch outdated_branch
|
55
|
-
run_cmd "git checkout #{Thegarage::Gitx::BASE_BRANCH}"
|
56
|
-
end
|
57
|
-
|
58
|
-
# share the local branch in the remote repo
|
59
|
-
def share_branch(branch)
|
60
|
-
run_cmd "git push origin #{branch}"
|
61
|
-
track_branch branch
|
62
|
-
end
|
63
|
-
|
64
|
-
def track_branch(branch)
|
65
|
-
run_cmd "git branch --set-upstream-to origin/#{branch}"
|
66
|
-
end
|
67
|
-
|
68
155
|
# integrate a branch into a destination aggregate branch
|
69
156
|
# blow away the local aggregate branch to ensure pulling into most recent "clean" branch
|
70
157
|
def integrate_branch(branch, destination_branch)
|
71
158
|
assert_not_protected_branch!(branch, 'integrate') unless aggregate_branch?(destination_branch)
|
72
159
|
raise "Only aggregate branches are allowed for integration: #{AGGREGATE_BRANCHES}" unless aggregate_branch?(destination_branch) || destination_branch == Thegarage::Gitx::BASE_BRANCH
|
73
|
-
say "Integrating "
|
74
|
-
say "#{branch} ", :green
|
75
|
-
say "into "
|
76
|
-
say destination_branch, :green
|
160
|
+
shell.say "Integrating "
|
161
|
+
shell.say "#{branch} ", :green
|
162
|
+
shell.say "into "
|
163
|
+
shell.say destination_branch, :green
|
77
164
|
|
78
165
|
refresh_branch_from_remote destination_branch
|
79
|
-
run_cmd "git pull . #{branch}"
|
80
|
-
run_cmd "git push origin HEAD"
|
81
|
-
run_cmd "git checkout #{branch}"
|
166
|
+
runner.run_cmd "git pull . #{branch}"
|
167
|
+
runner.run_cmd "git push origin HEAD"
|
168
|
+
runner.run_cmd "git checkout #{branch}"
|
82
169
|
end
|
83
170
|
|
84
171
|
# nuke local branch and pull fresh version from remote repo
|
85
172
|
def refresh_branch_from_remote(destination_branch)
|
86
|
-
run_cmd "git branch -D #{destination_branch}", :allow_failure => true
|
87
|
-
run_cmd "git fetch origin"
|
88
|
-
run_cmd "git checkout #{destination_branch}"
|
173
|
+
runner.run_cmd "git branch -D #{destination_branch}", :allow_failure => true
|
174
|
+
runner.run_cmd "git fetch origin"
|
175
|
+
runner.run_cmd "git checkout #{destination_branch}"
|
89
176
|
end
|
90
177
|
|
91
178
|
def aggregate_branch?(branch)
|
@@ -95,26 +182,26 @@ module Thegarage
|
|
95
182
|
def create_build_tag(branch, label)
|
96
183
|
timestamp = Time.now.utc.strftime '%Y-%m-%d-%H-%M-%S'
|
97
184
|
git_tag = "build-#{branch}-#{timestamp}"
|
98
|
-
run_cmd "git tag #{git_tag} -a -m '#{label}'"
|
99
|
-
run_cmd "git push origin #{git_tag}"
|
185
|
+
runner.run_cmd "git tag #{git_tag} -a -m '#{label}'"
|
186
|
+
runner.run_cmd "git push origin #{git_tag}"
|
100
187
|
end
|
101
188
|
|
102
189
|
def build_tags_for_branch(branch)
|
103
|
-
run_cmd "git fetch --tags"
|
104
|
-
build_tags = run_cmd("git tag -l 'build-#{branch}-*'").split
|
190
|
+
runner.run_cmd "git fetch --tags"
|
191
|
+
build_tags = runner.run_cmd("git tag -l 'build-#{branch}-*'").split
|
105
192
|
build_tags.sort
|
106
193
|
end
|
107
194
|
|
108
195
|
def migrations_need_to_be_reverted?
|
109
196
|
return false unless File.exists?('db/migrate')
|
110
|
-
outdated_migrations = run_cmd("git diff #{head_branch}...#{outdated_branch} --name-only db/migrate").split
|
197
|
+
outdated_migrations = runner.run_cmd("git diff #{head_branch}...#{outdated_branch} --name-only db/migrate").split
|
111
198
|
return false if outdated_migrations.empty?
|
112
199
|
|
113
|
-
say "#{outdated_branch} contains migrations that may need to be reverted. Ensure any reversable migrations are reverted on affected databases before nuking.", :red
|
114
|
-
say 'Example commands to revert outdated migrations:'
|
200
|
+
shell.say "#{outdated_branch} contains migrations that may need to be reverted. Ensure any reversable migrations are reverted on affected databases before nuking.", :red
|
201
|
+
shell.say 'Example commands to revert outdated migrations:'
|
115
202
|
outdated_migrations.reverse.each do |migration|
|
116
203
|
version = File.basename(migration).split('_').first
|
117
|
-
say "rake db:migrate:down VERSION=#{version}"
|
204
|
+
shell.say "rake db:migrate:down VERSION=#{version}"
|
118
205
|
end
|
119
206
|
!yes?("Are you sure you want to nuke #{outdated_branch}? (y/n) ", :green)
|
120
207
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'English'
|
2
|
+
|
3
|
+
module Thegarage
|
4
|
+
module Gitx
|
5
|
+
class Runner
|
6
|
+
attr_accessor :shell, :options
|
7
|
+
|
8
|
+
def initialize(shell, options = {})
|
9
|
+
@shell = shell
|
10
|
+
@options = options
|
11
|
+
end
|
12
|
+
|
13
|
+
# execute a shell command and raise an error if non-zero exit code is returned
|
14
|
+
# return the string output from the command
|
15
|
+
def run_cmd(cmd, options = {})
|
16
|
+
shell.say "$ #{cmd}"
|
17
|
+
output = `#{cmd}`
|
18
|
+
success = $CHILD_STATUS.to_i == 0
|
19
|
+
fail "#{cmd} failed" unless success || options[:allow_failure]
|
20
|
+
output
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -10,17 +10,21 @@ describe Thegarage::Gitx::CLI do
|
|
10
10
|
}
|
11
11
|
end
|
12
12
|
let(:cli) { Thegarage::Gitx::CLI.new(args, options, config) }
|
13
|
+
let(:git) { double('fake git') }
|
14
|
+
let(:github) { double('fake github') }
|
15
|
+
let(:runner) { double('fake runner') }
|
16
|
+
let(:branch) { double('fake branch', name: 'feature-branch') }
|
13
17
|
|
14
18
|
before do
|
15
|
-
|
16
|
-
allow(cli).to receive(:
|
19
|
+
allow(cli).to receive(:git).and_return(git)
|
20
|
+
allow(cli).to receive(:runner).and_return(runner)
|
21
|
+
allow(cli).to receive(:github).and_return(github)
|
22
|
+
allow(git).to receive(:current_branch).and_return(branch)
|
17
23
|
end
|
18
24
|
|
19
25
|
describe '#update' do
|
20
26
|
before do
|
21
|
-
expect(
|
22
|
-
expect(cli).to receive(:run).with('git pull origin master', capture: true).ordered
|
23
|
-
expect(cli).to receive(:run).with('git push origin HEAD', capture: true).ordered
|
27
|
+
expect(git).to receive(:update)
|
24
28
|
|
25
29
|
cli.update
|
26
30
|
end
|
@@ -32,16 +36,7 @@ describe Thegarage::Gitx::CLI do
|
|
32
36
|
describe '#integrate' do
|
33
37
|
context 'when target branch is ommitted' do
|
34
38
|
before do
|
35
|
-
expect(
|
36
|
-
expect(cli).to receive(:run).with("git pull origin master", capture: true).ordered
|
37
|
-
expect(cli).to receive(:run).with("git push origin HEAD", capture: true).ordered
|
38
|
-
expect(cli).to receive(:run).with("git branch -D staging", capture: true).ordered
|
39
|
-
expect(cli).to receive(:run).with("git fetch origin", capture: true).ordered
|
40
|
-
expect(cli).to receive(:run).with("git checkout staging", capture: true).ordered
|
41
|
-
expect(cli).to receive(:run).with("git pull . feature-branch", capture: true).ordered
|
42
|
-
expect(cli).to receive(:run).with("git push origin HEAD", capture: true).ordered
|
43
|
-
expect(cli).to receive(:run).with("git checkout feature-branch", capture: true).ordered
|
44
|
-
expect(cli).to receive(:run).with("git checkout feature-branch", capture: true).ordered
|
39
|
+
expect(git).to receive(:integrate).with('staging')
|
45
40
|
|
46
41
|
cli.integrate
|
47
42
|
end
|
@@ -51,16 +46,7 @@ describe Thegarage::Gitx::CLI do
|
|
51
46
|
end
|
52
47
|
context 'when target branch == prototype' do
|
53
48
|
before do
|
54
|
-
expect(
|
55
|
-
expect(cli).to receive(:run).with("git pull origin master", capture: true).ordered
|
56
|
-
expect(cli).to receive(:run).with("git push origin HEAD", capture: true).ordered
|
57
|
-
expect(cli).to receive(:run).with("git branch -D prototype", capture: true).ordered
|
58
|
-
expect(cli).to receive(:run).with("git fetch origin", capture: true).ordered
|
59
|
-
expect(cli).to receive(:run).with("git checkout prototype", capture: true).ordered
|
60
|
-
expect(cli).to receive(:run).with("git pull . feature-branch", capture: true).ordered
|
61
|
-
expect(cli).to receive(:run).with("git push origin HEAD", capture: true).ordered
|
62
|
-
expect(cli).to receive(:run).with("git checkout feature-branch", capture: true).ordered
|
63
|
-
expect(cli).to receive(:run).with("git checkout feature-branch", capture: true).ordered
|
49
|
+
expect(git).to receive(:integrate).with('prototype')
|
64
50
|
|
65
51
|
cli.integrate 'prototype'
|
66
52
|
end
|
@@ -68,17 +54,6 @@ describe Thegarage::Gitx::CLI do
|
|
68
54
|
should meet_expectations
|
69
55
|
end
|
70
56
|
end
|
71
|
-
context 'when target branch != staging || prototype' do
|
72
|
-
it 'raises an error' do
|
73
|
-
expect(cli).to receive(:run).with("git pull origin feature-branch", capture: true).ordered
|
74
|
-
expect(cli).to receive(:run).with("git pull origin master", capture: true).ordered
|
75
|
-
expect(cli).to receive(:run).with("git push origin HEAD", capture: true).ordered
|
76
|
-
|
77
|
-
lambda {
|
78
|
-
cli.integrate 'some-other-branch'
|
79
|
-
}.should raise_error(/Only aggregate branches are allowed for integration/)
|
80
|
-
end
|
81
|
-
end
|
82
57
|
end
|
83
58
|
|
84
59
|
describe '#release' do
|
@@ -86,9 +61,7 @@ describe Thegarage::Gitx::CLI do
|
|
86
61
|
before do
|
87
62
|
expect(cli).to receive(:yes?).and_return(false)
|
88
63
|
|
89
|
-
expect(
|
90
|
-
expect(cli).to receive(:run).with("git pull origin master", capture: true).ordered
|
91
|
-
expect(cli).to receive(:run).with("git push origin HEAD", capture: true).ordered
|
64
|
+
expect(git).to_not receive(:release)
|
92
65
|
|
93
66
|
cli.release
|
94
67
|
end
|
@@ -99,27 +72,7 @@ describe Thegarage::Gitx::CLI do
|
|
99
72
|
context 'when user confirms release' do
|
100
73
|
before do
|
101
74
|
expect(cli).to receive(:yes?).and_return(true)
|
102
|
-
expect(
|
103
|
-
|
104
|
-
expect(cli).to receive(:run).with("git pull origin feature-branch", capture: true).ordered
|
105
|
-
expect(cli).to receive(:run).with("git pull origin master", capture: true).ordered
|
106
|
-
expect(cli).to receive(:run).with("git push origin HEAD", capture: true).ordered
|
107
|
-
expect(cli).to receive(:run).with("git checkout master", capture: true).ordered
|
108
|
-
expect(cli).to receive(:run).with("git pull origin master", capture: true).ordered
|
109
|
-
expect(cli).to receive(:run).with("git pull . feature-branch", capture: true).ordered
|
110
|
-
expect(cli).to receive(:run).with("git push origin HEAD", capture: true).ordered
|
111
|
-
expect(cli).to receive(:run).with("git branch -D staging", capture: true).ordered
|
112
|
-
expect(cli).to receive(:run).with("git fetch origin", capture: true).ordered
|
113
|
-
expect(cli).to receive(:run).with("git checkout staging", capture: true).ordered
|
114
|
-
expect(cli).to receive(:run).with("git pull . master", capture: true).ordered
|
115
|
-
expect(cli).to receive(:run).with("git push origin HEAD", capture: true).ordered
|
116
|
-
expect(cli).to receive(:run).with("git checkout master", capture: true).ordered
|
117
|
-
expect(cli).to receive(:run).with("git checkout master", capture: true).ordered
|
118
|
-
expect(cli).to receive(:run).with("git pull", capture: true).ordered
|
119
|
-
expect(cli).to receive(:run).with("git remote prune origin", capture: true).ordered
|
120
|
-
expect(cli).to receive(:run).with("git push origin --delete old-merged-feature", capture: true).ordered
|
121
|
-
expect(cli).to receive(:run).with("git branch -d old-merged-feature", capture: true).ordered
|
122
|
-
|
75
|
+
expect(git).to receive(:release)
|
123
76
|
cli.release
|
124
77
|
end
|
125
78
|
it 'runs expected commands' do
|
@@ -132,76 +85,34 @@ describe Thegarage::Gitx::CLI do
|
|
132
85
|
context 'when target branch == prototype and --destination == master' do
|
133
86
|
let(:options) do
|
134
87
|
{
|
135
|
-
destination:
|
136
|
-
}
|
137
|
-
end
|
138
|
-
let(:buildtags) do
|
139
|
-
%w( build-master-2013-10-01-01 ).join("\n")
|
140
|
-
end
|
141
|
-
before do
|
142
|
-
expect(cli).to receive(:yes?).and_return(true)
|
143
|
-
|
144
|
-
expect(cli).to receive(:run).with("git fetch --tags", capture: true).ordered
|
145
|
-
expect(cli).to receive(:run).with("git tag -l 'build-master-*'", capture: true).and_return(buildtags).ordered
|
146
|
-
expect(cli).to receive(:run).with("git checkout master", capture: true).ordered
|
147
|
-
expect(cli).to receive(:run).with("git branch -D prototype", capture: true).ordered
|
148
|
-
expect(cli).to receive(:run).with("git push origin --delete prototype", capture: true).ordered
|
149
|
-
expect(cli).to receive(:run).with("git checkout -b prototype build-master-2013-10-01-01", capture: true).ordered
|
150
|
-
expect(cli).to receive(:run).with("git push origin prototype", capture: true).ordered
|
151
|
-
expect(cli).to receive(:run).with("git branch --set-upstream-to origin/prototype", capture: true).ordered
|
152
|
-
expect(cli).to receive(:run).with("git checkout master", capture: true).ordered
|
153
|
-
|
154
|
-
cli.nuke 'prototype'
|
155
|
-
end
|
156
|
-
it 'runs expected commands' do
|
157
|
-
should meet_expectations
|
158
|
-
end
|
159
|
-
end
|
160
|
-
context 'when target branch == staging and --destination == staging' do
|
161
|
-
let(:options) do
|
162
|
-
{
|
163
|
-
destination: 'staging'
|
88
|
+
destination: good_branch
|
164
89
|
}
|
165
90
|
end
|
166
|
-
let(:
|
167
|
-
|
168
|
-
|
91
|
+
let(:good_branch) { 'master' }
|
92
|
+
let(:bad_branch) { 'prototype' }
|
93
|
+
let(:buildtag) { 'build-master-2013-10-01-01' }
|
169
94
|
before do
|
170
95
|
expect(cli).to receive(:yes?).and_return(true)
|
171
96
|
|
172
|
-
expect(
|
173
|
-
expect(
|
174
|
-
expect(cli).to receive(:run).with("git checkout master", capture: true).ordered
|
175
|
-
expect(cli).to receive(:run).with("git branch -D staging", capture: true).ordered
|
176
|
-
expect(cli).to receive(:run).with("git push origin --delete staging", capture: true).ordered
|
177
|
-
expect(cli).to receive(:run).with("git checkout -b staging build-staging-2013-10-02-02", capture: true).ordered
|
178
|
-
expect(cli).to receive(:run).with("git push origin staging", capture: true).ordered
|
179
|
-
expect(cli).to receive(:run).with("git branch --set-upstream-to origin/staging", capture: true).ordered
|
180
|
-
expect(cli).to receive(:run).with("git checkout master", capture: true).ordered
|
97
|
+
expect(git).to receive(:current_build_tag).with(good_branch).and_return(buildtag)
|
98
|
+
expect(git).to receive(:nuke).with(bad_branch, buildtag)
|
181
99
|
|
182
|
-
cli.nuke
|
100
|
+
cli.nuke bad_branch
|
183
101
|
end
|
184
102
|
it 'runs expected commands' do
|
185
103
|
should meet_expectations
|
186
104
|
end
|
187
105
|
end
|
188
106
|
context 'when target branch == prototype and destination prompt == nil' do
|
189
|
-
let(:
|
190
|
-
|
191
|
-
|
107
|
+
let(:good_branch) { 'master' }
|
108
|
+
let(:bad_branch) { 'prototype' }
|
109
|
+
let(:buildtag) { 'build-master-2013-10-01-01' }
|
192
110
|
before do
|
193
|
-
expect(cli).to receive(:ask).and_return(
|
111
|
+
expect(cli).to receive(:ask).and_return(good_branch)
|
194
112
|
expect(cli).to receive(:yes?).and_return(true)
|
195
113
|
|
196
|
-
expect(
|
197
|
-
expect(
|
198
|
-
expect(cli).to receive(:run).with("git checkout master", capture: true).ordered
|
199
|
-
expect(cli).to receive(:run).with("git branch -D prototype", capture: true).ordered
|
200
|
-
expect(cli).to receive(:run).with("git push origin --delete prototype", capture: true).ordered
|
201
|
-
expect(cli).to receive(:run).with("git checkout -b prototype build-prototype-2013-10-03-03", capture: true).ordered
|
202
|
-
expect(cli).to receive(:run).with("git push origin prototype", capture: true).ordered
|
203
|
-
expect(cli).to receive(:run).with("git branch --set-upstream-to origin/prototype", capture: true).ordered
|
204
|
-
expect(cli).to receive(:run).with("git checkout master", capture: true).ordered
|
114
|
+
expect(git).to receive(:current_build_tag).with(good_branch).and_return(buildtag)
|
115
|
+
expect(git).to receive(:nuke).with(bad_branch, buildtag)
|
205
116
|
|
206
117
|
cli.nuke 'prototype'
|
207
118
|
end
|
@@ -209,49 +120,14 @@ describe Thegarage::Gitx::CLI do
|
|
209
120
|
should meet_expectations
|
210
121
|
end
|
211
122
|
end
|
212
|
-
context 'when target branch == prototype and destination prompt = master' do
|
213
|
-
let(:buildtags) do
|
214
|
-
%w( build-master-2013-10-01-01 ).join("\n")
|
215
|
-
end
|
216
|
-
before do
|
217
|
-
expect(cli).to receive(:ask).and_return('master')
|
218
|
-
expect(cli).to receive(:yes?).and_return(true)
|
219
|
-
|
220
|
-
expect(cli).to receive(:run).with("git fetch --tags", capture: true).ordered
|
221
|
-
expect(cli).to receive(:run).with("git tag -l 'build-master-*'", capture: true).and_return(buildtags).ordered
|
222
|
-
expect(cli).to receive(:run).with("git checkout master", capture: true).ordered
|
223
|
-
expect(cli).to receive(:run).with("git branch -D prototype", capture: true).ordered
|
224
|
-
expect(cli).to receive(:run).with("git push origin --delete prototype", capture: true).ordered
|
225
|
-
expect(cli).to receive(:run).with("git checkout -b prototype build-master-2013-10-01-01", capture: true).ordered
|
226
|
-
expect(cli).to receive(:run).with("git push origin prototype", capture: true).ordered
|
227
|
-
expect(cli).to receive(:run).with("git branch --set-upstream-to origin/prototype", capture: true).ordered
|
228
|
-
expect(cli).to receive(:run).with("git checkout master", capture: true).ordered
|
229
|
-
|
230
|
-
cli.nuke 'prototype'
|
231
|
-
end
|
232
|
-
it 'runs expected commands' do
|
233
|
-
should meet_expectations
|
234
|
-
end
|
235
|
-
end
|
236
|
-
context 'when target branch != staging || prototype' do
|
237
|
-
it 'raises error' do
|
238
|
-
lambda {
|
239
|
-
expect(cli).to receive(:ask).and_return('master')
|
240
|
-
expect(cli).to receive(:yes?).and_return(true)
|
241
|
-
cli.nuke 'not-an-integration-branch'
|
242
|
-
}.should raise_error(/Only aggregate branches are allowed to be reset/)
|
243
|
-
end
|
244
|
-
end
|
245
123
|
context 'when user does not confirm nuking the target branch' do
|
246
|
-
let(:
|
247
|
-
%w( build-master-2013-10-01-01 ).join("\n")
|
248
|
-
end
|
124
|
+
let(:buildtag) { 'build-master-2013-10-01-01' }
|
249
125
|
before do
|
250
126
|
expect(cli).to receive(:ask).and_return('master')
|
251
127
|
expect(cli).to receive(:yes?).and_return(false)
|
252
128
|
|
253
|
-
expect(
|
254
|
-
expect(
|
129
|
+
expect(git).to receive(:current_build_tag).with('master').and_return(buildtag)
|
130
|
+
expect(git).to_not receive(:nuke)
|
255
131
|
|
256
132
|
cli.nuke 'prototype'
|
257
133
|
end
|
@@ -259,23 +135,9 @@ describe Thegarage::Gitx::CLI do
|
|
259
135
|
should meet_expectations
|
260
136
|
end
|
261
137
|
end
|
262
|
-
context 'when no known good build tag found' do
|
263
|
-
let(:buildtags) do
|
264
|
-
''
|
265
|
-
end
|
266
|
-
it 'raises error' do
|
267
|
-
expect(cli).to receive(:ask).and_return('master')
|
268
|
-
|
269
|
-
expect(cli).to receive(:run).with("git fetch --tags", capture: true).ordered
|
270
|
-
expect(cli).to receive(:run).with("git tag -l 'build-master-*'", capture: true).and_return(buildtags).ordered
|
271
|
-
|
272
|
-
expect { cli.nuke('prototype') }.to raise_error(/No known good tag found for branch/)
|
273
|
-
end
|
274
|
-
end
|
275
138
|
end
|
276
139
|
|
277
140
|
describe '#reviewrequest' do
|
278
|
-
let(:github) { double('fake github') }
|
279
141
|
let(:pull_request) do
|
280
142
|
{
|
281
143
|
'html_url' => 'https://path/to/new/pull/request',
|
@@ -284,9 +146,6 @@ describe Thegarage::Gitx::CLI do
|
|
284
146
|
}
|
285
147
|
}
|
286
148
|
end
|
287
|
-
before do
|
288
|
-
allow(cli).to receive(:github).and_return(github)
|
289
|
-
end
|
290
149
|
context 'when pull request does not exist' do
|
291
150
|
let(:authorization_token) { '123123' }
|
292
151
|
let(:changelog) { '* made some fixes' }
|
@@ -295,10 +154,8 @@ describe Thegarage::Gitx::CLI do
|
|
295
154
|
expect(github).to receive(:find_pull_request).and_return(nil)
|
296
155
|
expect(github).to receive(:create_pull_request).and_return(pull_request)
|
297
156
|
|
298
|
-
expect(
|
299
|
-
expect(
|
300
|
-
expect(cli).to receive(:run).with("git push origin HEAD", capture: true).ordered
|
301
|
-
expect(cli).to receive(:run).with("git log master...feature-branch --no-merges --pretty=format:'* %s%n%b'", capture: true).and_return("2013-01-01 did some stuff").ordered
|
157
|
+
expect(git).to receive(:update)
|
158
|
+
expect(runner).to receive(:run_cmd).with("git log master...feature-branch --no-merges --pretty=format:'* %s%n%b'").and_return("2013-01-01 did some stuff").ordered
|
302
159
|
cli.reviewrequest
|
303
160
|
end
|
304
161
|
it 'creates github pull request' do
|
@@ -357,7 +214,7 @@ describe Thegarage::Gitx::CLI do
|
|
357
214
|
expect(github).to receive(:authorization_token).and_return(authorization_token)
|
358
215
|
expect(github).to receive(:find_pull_request).and_return(pull_request)
|
359
216
|
|
360
|
-
expect(
|
217
|
+
expect(runner).to receive(:run_cmd).with("open #{pull_request['html_url']}").ordered
|
361
218
|
cli.reviewrequest
|
362
219
|
end
|
363
220
|
it 'runs open command with pull request url' do
|
@@ -366,56 +223,35 @@ describe Thegarage::Gitx::CLI do
|
|
366
223
|
end
|
367
224
|
end
|
368
225
|
|
369
|
-
describe '#
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
before do
|
374
|
-
ENV['TRAVIS_BRANCH'] = env_travis_branch
|
375
|
-
ENV['TRAVIS_PULL_REQUEST'] = env_travis_pull_request
|
376
|
-
ENV['TRAVIS_BUILD_NUMBER'] = env_travis_build_number
|
226
|
+
describe '#track' do
|
227
|
+
it 'calls git.track' do
|
228
|
+
expect(git).to receive(:track)
|
229
|
+
cli.track
|
377
230
|
end
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
let(:env_travis_branch) { 'master' }
|
385
|
-
let(:env_travis_pull_request) { '45' }
|
386
|
-
before do
|
387
|
-
expect(cli).to receive(:say).with("Skipping creation of tag for pull request: #{ENV['TRAVIS_PULL_REQUEST']}")
|
388
|
-
cli.buildtag
|
389
|
-
end
|
390
|
-
it 'tells us that it is skipping the creation of the tag' do
|
391
|
-
should meet_expectations
|
392
|
-
end
|
231
|
+
end
|
232
|
+
|
233
|
+
describe '#share' do
|
234
|
+
it 'calls git.share' do
|
235
|
+
expect(git).to receive(:share)
|
236
|
+
cli.share
|
393
237
|
end
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
238
|
+
end
|
239
|
+
|
240
|
+
describe '#start' do
|
241
|
+
context 'when user inputs branch that is valid' do
|
242
|
+
it 'calls git.start' do
|
243
|
+
expect(git).to receive(:valid_new_branch_name?).with('new-branch').and_return(true)
|
244
|
+
expect(git).to receive(:start).with('new-branch')
|
245
|
+
|
246
|
+
cli.start 'new-branch'
|
403
247
|
end
|
404
248
|
end
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
expect(cli).to receive(:run).with("git tag build-master-2013-10-30-10-21-28 -a -m 'Generated tag from TravisCI build 24'", capture: true).ordered
|
412
|
-
expect(cli).to receive(:run).with("git push origin build-master-2013-10-30-10-21-28", capture: true).ordered
|
413
|
-
cli.buildtag
|
414
|
-
end
|
415
|
-
end
|
416
|
-
it 'creates a tag for the branch and push it to github' do
|
417
|
-
should meet_expectations
|
418
|
-
end
|
249
|
+
end
|
250
|
+
|
251
|
+
describe '#buildtag' do
|
252
|
+
it 'calls git.buildtag' do
|
253
|
+
expect(git).to receive(:buildtag)
|
254
|
+
cli.buildtag
|
419
255
|
end
|
420
256
|
end
|
421
257
|
end
|
@@ -0,0 +1,231 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'timecop'
|
3
|
+
|
4
|
+
describe Thegarage::Gitx::Git do
|
5
|
+
let(:runner) { double('fake runner') }
|
6
|
+
let(:shell) { double('fake shell', say: nil) }
|
7
|
+
let(:branch) { double('fake git branch', name: 'feature-branch') }
|
8
|
+
subject { Thegarage::Gitx::Git.new(shell, runner) }
|
9
|
+
|
10
|
+
# default current branch to: feature-branch
|
11
|
+
before do
|
12
|
+
allow(subject).to receive(:current_branch).and_return(branch)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#update' do
|
16
|
+
before do
|
17
|
+
allow(shell).to receive(:say)
|
18
|
+
|
19
|
+
expect(runner).to receive(:run_cmd).with('git pull origin feature-branch', allow_failure: true).ordered
|
20
|
+
expect(runner).to receive(:run_cmd).with('git pull origin master').ordered
|
21
|
+
expect(runner).to receive(:run_cmd).with('git push origin HEAD').ordered
|
22
|
+
|
23
|
+
subject.update
|
24
|
+
end
|
25
|
+
it 'runs expected commands' do
|
26
|
+
should meet_expectations
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#track' do
|
31
|
+
before do
|
32
|
+
expect(runner).to receive(:run_cmd).with('git branch --set-upstream-to origin/feature-branch').ordered
|
33
|
+
|
34
|
+
subject.track
|
35
|
+
end
|
36
|
+
it 'runs expected commands' do
|
37
|
+
should meet_expectations
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#share' do
|
42
|
+
before do
|
43
|
+
expect(runner).to receive(:run_cmd).with('git push origin feature-branch').ordered
|
44
|
+
expect(runner).to receive(:run_cmd).with('git branch --set-upstream-to origin/feature-branch').ordered
|
45
|
+
|
46
|
+
subject.share
|
47
|
+
end
|
48
|
+
it 'runs expected commands' do
|
49
|
+
should meet_expectations
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '#integrate' do
|
54
|
+
context 'when target branch is ommitted' do
|
55
|
+
before do
|
56
|
+
expect(subject).to receive(:update)
|
57
|
+
|
58
|
+
expect(runner).to receive(:run_cmd).with("git branch -D staging", allow_failure: true).ordered
|
59
|
+
expect(runner).to receive(:run_cmd).with("git fetch origin").ordered
|
60
|
+
expect(runner).to receive(:run_cmd).with("git checkout staging").ordered
|
61
|
+
expect(runner).to receive(:run_cmd).with("git pull . feature-branch").ordered
|
62
|
+
expect(runner).to receive(:run_cmd).with("git push origin HEAD").ordered
|
63
|
+
expect(runner).to receive(:run_cmd).with("git checkout feature-branch").ordered
|
64
|
+
expect(runner).to receive(:run_cmd).with("git checkout feature-branch").ordered
|
65
|
+
|
66
|
+
subject.integrate
|
67
|
+
end
|
68
|
+
it 'defaults to staging branch' do
|
69
|
+
should meet_expectations
|
70
|
+
end
|
71
|
+
end
|
72
|
+
context 'when target branch == prototype' do
|
73
|
+
before do
|
74
|
+
expect(subject).to receive(:update)
|
75
|
+
|
76
|
+
expect(runner).to receive(:run_cmd).with("git branch -D prototype", allow_failure: true).ordered
|
77
|
+
expect(runner).to receive(:run_cmd).with("git fetch origin").ordered
|
78
|
+
expect(runner).to receive(:run_cmd).with("git checkout prototype").ordered
|
79
|
+
expect(runner).to receive(:run_cmd).with("git pull . feature-branch").ordered
|
80
|
+
expect(runner).to receive(:run_cmd).with("git push origin HEAD").ordered
|
81
|
+
expect(runner).to receive(:run_cmd).with("git checkout feature-branch").ordered
|
82
|
+
expect(runner).to receive(:run_cmd).with("git checkout feature-branch").ordered
|
83
|
+
|
84
|
+
subject.integrate 'prototype'
|
85
|
+
end
|
86
|
+
it 'runs expected commands' do
|
87
|
+
should meet_expectations
|
88
|
+
end
|
89
|
+
end
|
90
|
+
context 'when target branch != staging || prototype' do
|
91
|
+
it 'raises an error' do
|
92
|
+
expect(subject).to receive(:update)
|
93
|
+
|
94
|
+
expect { subject.integrate('some-other-branch') }.to raise_error(/Only aggregate branches are allowed for integration/)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe '#release' do
|
100
|
+
it 'merges feature branch into master' do
|
101
|
+
expect(subject).to receive(:update)
|
102
|
+
|
103
|
+
expect(runner).to receive(:run_cmd).with("git checkout master").ordered
|
104
|
+
expect(runner).to receive(:run_cmd).with("git pull origin master").ordered
|
105
|
+
expect(runner).to receive(:run_cmd).with("git pull . feature-branch").ordered
|
106
|
+
expect(runner).to receive(:run_cmd).with("git push origin HEAD").ordered
|
107
|
+
|
108
|
+
expect(subject).to receive(:integrate).with('staging')
|
109
|
+
expect(subject).to receive(:cleanup)
|
110
|
+
|
111
|
+
subject.release
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe '#nuke' do
|
116
|
+
context 'when target branch == prototype and head is a valid buildtag' do
|
117
|
+
let(:buildtag) { 'build-master-2013-10-01-01' }
|
118
|
+
before do
|
119
|
+
expect(runner).to receive(:run_cmd).with("git checkout master").ordered
|
120
|
+
expect(runner).to receive(:run_cmd).with("git branch -D prototype", allow_failure: true).ordered
|
121
|
+
expect(runner).to receive(:run_cmd).with("git push origin --delete prototype", allow_failure: true).ordered
|
122
|
+
expect(runner).to receive(:run_cmd).with("git checkout -b prototype build-master-2013-10-01-01").ordered
|
123
|
+
expect(runner).to receive(:run_cmd).with("git push origin prototype").ordered
|
124
|
+
expect(runner).to receive(:run_cmd).with("git branch --set-upstream-to origin/prototype").ordered
|
125
|
+
expect(runner).to receive(:run_cmd).with("git checkout master").ordered
|
126
|
+
|
127
|
+
subject.nuke 'prototype', buildtag
|
128
|
+
end
|
129
|
+
it 'runs expected commands' do
|
130
|
+
should meet_expectations
|
131
|
+
end
|
132
|
+
end
|
133
|
+
context 'when target branch == staging and head is a valid buildtag' do
|
134
|
+
let(:buildtag) { 'build-master-2013-10-02-02' }
|
135
|
+
before do
|
136
|
+
expect(runner).to receive(:run_cmd).with("git checkout master").ordered
|
137
|
+
expect(runner).to receive(:run_cmd).with("git branch -D staging", allow_failure: true).ordered
|
138
|
+
expect(runner).to receive(:run_cmd).with("git push origin --delete staging", allow_failure: true).ordered
|
139
|
+
expect(runner).to receive(:run_cmd).with("git checkout -b staging build-master-2013-10-02-02").ordered
|
140
|
+
expect(runner).to receive(:run_cmd).with("git push origin staging").ordered
|
141
|
+
expect(runner).to receive(:run_cmd).with("git branch --set-upstream-to origin/staging").ordered
|
142
|
+
expect(runner).to receive(:run_cmd).with("git checkout master").ordered
|
143
|
+
|
144
|
+
subject.nuke 'staging', buildtag
|
145
|
+
end
|
146
|
+
it 'runs expected commands' do
|
147
|
+
should meet_expectations
|
148
|
+
end
|
149
|
+
end
|
150
|
+
context 'when target branch != staging || prototype' do
|
151
|
+
it 'raises error' do
|
152
|
+
expect { subject.nuke('not-an-integration-branch', 'master') }.to raise_error(/Only aggregate branches are allowed to be reset/)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe '#current_build_tag' do
|
158
|
+
context 'when multiple build tags returned' do
|
159
|
+
let(:buildtags) { %w( build-master-2013-01-01-01 build-master-2013-01-01-02 ).join("\n") }
|
160
|
+
it 'returns the last one' do
|
161
|
+
expect(runner).to receive(:run_cmd).with("git fetch --tags").ordered
|
162
|
+
expect(runner).to receive(:run_cmd).with("git tag -l 'build-master-*'").and_return(buildtags).ordered
|
163
|
+
|
164
|
+
result = subject.current_build_tag 'master'
|
165
|
+
expect(result).to eq 'build-master-2013-01-01-02'
|
166
|
+
end
|
167
|
+
end
|
168
|
+
context 'when no known good build tag found' do
|
169
|
+
let(:buildtags) { '' }
|
170
|
+
it 'raises error' do
|
171
|
+
expect(runner).to receive(:run_cmd).with("git fetch --tags").ordered
|
172
|
+
expect(runner).to receive(:run_cmd).with("git tag -l 'build-master-*'").and_return(buildtags).ordered
|
173
|
+
|
174
|
+
expect { subject.current_build_tag('master') }.to raise_error(/No known good tag found for branch/)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
describe '#buildtag' do
|
180
|
+
let(:env_travis_branch) { nil }
|
181
|
+
let(:env_travis_pull_request) { nil }
|
182
|
+
let(:env_travis_build_number) { nil }
|
183
|
+
before do
|
184
|
+
ENV['TRAVIS_BRANCH'] = env_travis_branch
|
185
|
+
ENV['TRAVIS_PULL_REQUEST'] = env_travis_pull_request
|
186
|
+
ENV['TRAVIS_BUILD_NUMBER'] = env_travis_build_number
|
187
|
+
end
|
188
|
+
context 'when ENV[\'TRAVIS_BRANCH\'] is nil' do
|
189
|
+
it 'raises Unknown Branch error' do
|
190
|
+
expect { subject.buildtag }.to raise_error "Unknown branch. ENV['TRAVIS_BRANCH'] is required."
|
191
|
+
end
|
192
|
+
end
|
193
|
+
context 'when the travis branch is master and the travis pull request is not false' do
|
194
|
+
let(:env_travis_branch) { 'master' }
|
195
|
+
let(:env_travis_pull_request) { '45' }
|
196
|
+
before do
|
197
|
+
expect(shell).to receive(:say).with("Skipping creation of tag for pull request: #{ENV['TRAVIS_PULL_REQUEST']}")
|
198
|
+
subject.buildtag
|
199
|
+
end
|
200
|
+
it 'tells us that it is skipping the creation of the tag' do
|
201
|
+
should meet_expectations
|
202
|
+
end
|
203
|
+
end
|
204
|
+
context 'when the travis branch is NOT master and is not a pull request' do
|
205
|
+
let(:env_travis_branch) { 'random-branch' }
|
206
|
+
let(:env_travis_pull_request) { 'false' }
|
207
|
+
before do
|
208
|
+
expect(shell).to receive(:say).with(/Cannot create build tag for branch: #{ENV['TRAVIS_BRANCH']}/)
|
209
|
+
subject.buildtag
|
210
|
+
end
|
211
|
+
it 'tells us that the branch is not supported' do
|
212
|
+
should meet_expectations
|
213
|
+
end
|
214
|
+
end
|
215
|
+
context 'when the travis branch is master and not a pull request' do
|
216
|
+
let(:env_travis_branch) { 'master' }
|
217
|
+
let(:env_travis_pull_request) { 'false' }
|
218
|
+
let(:env_travis_build_number) { '24' }
|
219
|
+
before do
|
220
|
+
Timecop.freeze(Time.utc(2013, 10, 30, 10, 21, 28)) do
|
221
|
+
expect(runner).to receive(:run_cmd).with("git tag build-master-2013-10-30-10-21-28 -a -m 'Generated tag from TravisCI build 24'").ordered
|
222
|
+
expect(runner).to receive(:run_cmd).with("git push origin build-master-2013-10-30-10-21-28").ordered
|
223
|
+
subject.buildtag
|
224
|
+
end
|
225
|
+
end
|
226
|
+
it 'creates a tag for the branch and push it to github' do
|
227
|
+
should meet_expectations
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
data/thegarage-gitx.gemspec
CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_runtime_dependency "
|
21
|
+
spec.add_runtime_dependency "rugged"
|
22
22
|
spec.add_runtime_dependency "rest-client", ">= 1.4.0"
|
23
23
|
spec.add_runtime_dependency "thor"
|
24
24
|
|
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thegarage-gitx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.5.
|
4
|
+
version: 1.5.5.pre1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Sonnek
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-03-
|
11
|
+
date: 2014-03-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: rugged
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - '>='
|
@@ -178,11 +178,13 @@ files:
|
|
178
178
|
- lib/thegarage/gitx/cli.rb
|
179
179
|
- lib/thegarage/gitx/git.rb
|
180
180
|
- lib/thegarage/gitx/github.rb
|
181
|
+
- lib/thegarage/gitx/runner.rb
|
181
182
|
- lib/thegarage/gitx/string_extensions.rb
|
182
183
|
- lib/thegarage/gitx/version.rb
|
183
184
|
- spec/spec_helper.rb
|
184
185
|
- spec/support/meet_expectations_matcher.rb
|
185
186
|
- spec/thegarage/gitx/cli_spec.rb
|
187
|
+
- spec/thegarage/gitx/git_spec.rb
|
186
188
|
- spec/thegarage/gitx/github_spec.rb
|
187
189
|
- thegarage-gitx.gemspec
|
188
190
|
homepage: ''
|
@@ -200,9 +202,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
200
202
|
version: '0'
|
201
203
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
202
204
|
requirements:
|
203
|
-
- - '
|
205
|
+
- - '>'
|
204
206
|
- !ruby/object:Gem::Version
|
205
|
-
version:
|
207
|
+
version: 1.3.1
|
206
208
|
requirements: []
|
207
209
|
rubyforge_project:
|
208
210
|
rubygems_version: 2.0.3
|
@@ -213,4 +215,5 @@ test_files:
|
|
213
215
|
- spec/spec_helper.rb
|
214
216
|
- spec/support/meet_expectations_matcher.rb
|
215
217
|
- spec/thegarage/gitx/cli_spec.rb
|
218
|
+
- spec/thegarage/gitx/git_spec.rb
|
216
219
|
- spec/thegarage/gitx/github_spec.rb
|