thegarage-gitx 1.5.4 → 1.5.5.pre1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|