flash_flow 1.0.0
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 +7 -0
- data/.gitignore +10 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +81 -0
- data/LICENSE.txt +22 -0
- data/README.md +152 -0
- data/Rakefile +10 -0
- data/bin/flash_flow +23 -0
- data/flash_flow.gemspec +28 -0
- data/flash_flow.yml.erb.example +83 -0
- data/lib/flash_flow.rb +7 -0
- data/lib/flash_flow/branch_merger.rb +52 -0
- data/lib/flash_flow/cmd_runner.rb +37 -0
- data/lib/flash_flow/config.rb +71 -0
- data/lib/flash_flow/data.rb +6 -0
- data/lib/flash_flow/data/base.rb +58 -0
- data/lib/flash_flow/data/branch.rb +131 -0
- data/lib/flash_flow/data/collection.rb +181 -0
- data/lib/flash_flow/data/github.rb +129 -0
- data/lib/flash_flow/data/store.rb +33 -0
- data/lib/flash_flow/deploy.rb +184 -0
- data/lib/flash_flow/git.rb +248 -0
- data/lib/flash_flow/install.rb +19 -0
- data/lib/flash_flow/issue_tracker.rb +52 -0
- data/lib/flash_flow/issue_tracker/pivotal.rb +160 -0
- data/lib/flash_flow/lock.rb +25 -0
- data/lib/flash_flow/lock/github.rb +91 -0
- data/lib/flash_flow/notifier.rb +24 -0
- data/lib/flash_flow/notifier/hipchat.rb +36 -0
- data/lib/flash_flow/options.rb +36 -0
- data/lib/flash_flow/time_helper.rb +11 -0
- data/lib/flash_flow/version.rb +3 -0
- data/test/lib/data/test_base.rb +10 -0
- data/test/lib/data/test_branch.rb +203 -0
- data/test/lib/data/test_collection.rb +238 -0
- data/test/lib/data/test_github.rb +23 -0
- data/test/lib/data/test_store.rb +53 -0
- data/test/lib/issue_tracker/test_pivotal.rb +221 -0
- data/test/lib/lock/test_github.rb +70 -0
- data/test/lib/test_branch_merger.rb +76 -0
- data/test/lib/test_config.rb +84 -0
- data/test/lib/test_deploy.rb +175 -0
- data/test/lib/test_git.rb +73 -0
- data/test/lib/test_issue_tracker.rb +43 -0
- data/test/lib/test_notifier.rb +33 -0
- data/test/minitest_helper.rb +38 -0
- metadata +217 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
|
3
|
+
module FlashFlow
|
4
|
+
class TestConfig < Minitest::Test
|
5
|
+
def setup
|
6
|
+
@config_hash = {
|
7
|
+
'git' => {
|
8
|
+
'use_rerere' => true,
|
9
|
+
'merge_remote' => 'arbitrary_remote',
|
10
|
+
'merge_branch' => 'acceptance',
|
11
|
+
'master_branch' => 'master',
|
12
|
+
'remotes' => ['origin']
|
13
|
+
},
|
14
|
+
'branch_info_file' => 'some_file.txt',
|
15
|
+
'notifier' => {
|
16
|
+
'class' => {
|
17
|
+
'name' => 'NotifierClass'
|
18
|
+
}
|
19
|
+
},
|
20
|
+
'issue_tracker' => {
|
21
|
+
'class' => {
|
22
|
+
'name' => 'IssueTrackerClass'
|
23
|
+
}
|
24
|
+
},
|
25
|
+
'lock' => {
|
26
|
+
'class' => {
|
27
|
+
'name' => 'LockClass'
|
28
|
+
}
|
29
|
+
},
|
30
|
+
'branches' => {
|
31
|
+
'class' => {
|
32
|
+
'name' => 'BranchClass'
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
reset_config!
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_that_it_sets_all_attrs
|
41
|
+
File.stub(:read, @config_hash.to_yaml) do
|
42
|
+
Config.configure!('unused_file_name.yml')
|
43
|
+
assert('some_file.txt' == config.branch_info_file)
|
44
|
+
assert({
|
45
|
+
'use_rerere' => true,
|
46
|
+
'merge_remote' => 'arbitrary_remote',
|
47
|
+
'merge_branch' => 'acceptance',
|
48
|
+
'master_branch' => 'master',
|
49
|
+
'remotes' => ['origin']
|
50
|
+
} == config.git)
|
51
|
+
assert({ 'class' => { 'name' => 'NotifierClass' }} == config.notifier)
|
52
|
+
assert({ 'class' => { 'name' => 'IssueTrackerClass' }} == config.issue_tracker)
|
53
|
+
assert({ 'class' => { 'name' => 'LockClass' }} == config.lock)
|
54
|
+
assert({ 'class' => { 'name' => 'BranchClass' }} == config.branches)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_that_it_blows_up
|
59
|
+
@config_hash.delete('git')
|
60
|
+
|
61
|
+
File.stub(:read, @config_hash.to_yaml) do
|
62
|
+
assert_raises FlashFlow::Config::IncompleteConfiguration do
|
63
|
+
Config.configure!('unused_file_name.yml')
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_that_it_sets_defaults
|
69
|
+
File.stub(:read, {'git' => {}}.to_yaml) do
|
70
|
+
Config.configure!('unused_file_name.yml')
|
71
|
+
assert('README.rdoc' == config.branch_info_file)
|
72
|
+
assert_nil(config.notifier)
|
73
|
+
assert_nil(config.issue_tracker)
|
74
|
+
assert_nil(config.lock)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def config
|
81
|
+
Config.configuration
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
require 'minitest/stub_any_instance'
|
3
|
+
|
4
|
+
module FlashFlow
|
5
|
+
class TestDeploy < Minitest::Test
|
6
|
+
|
7
|
+
def setup
|
8
|
+
reset_config!
|
9
|
+
config!(git: {
|
10
|
+
'merge_branch' => 'test_acceptance',
|
11
|
+
'merge_remote' => 'test_remote',
|
12
|
+
'master_branch' => 'test_master',
|
13
|
+
'remotes' => ['fake_origin'],
|
14
|
+
'use_rerere' => true
|
15
|
+
})
|
16
|
+
|
17
|
+
@branch = Data::Branch.from_hash({'ref' => 'pushing_branch', 'remote' => 'origin', 'status' => 'fail', 'stories' => []})
|
18
|
+
@deploy = Deploy.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def with_versions(current, written)
|
22
|
+
original_version = FlashFlow::VERSION
|
23
|
+
FlashFlow.send(:remove_const, :VERSION)
|
24
|
+
FlashFlow.const_set(:VERSION, current)
|
25
|
+
data.expect(:version, written)
|
26
|
+
yield
|
27
|
+
data.verify
|
28
|
+
FlashFlow.send(:remove_const, :VERSION)
|
29
|
+
FlashFlow.const_set(:VERSION, original_version)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_version_is_nil
|
33
|
+
with_versions('1.1.1', nil) do
|
34
|
+
assert_nil(@deploy.check_version)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_check_version_greater
|
39
|
+
with_versions('2.0.0', '1.1.1') do
|
40
|
+
assert_nil(@deploy.check_version)
|
41
|
+
end
|
42
|
+
|
43
|
+
with_versions('1.2.0', '1.1.1') do
|
44
|
+
assert_nil(@deploy.check_version)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_check_version_less_raises
|
49
|
+
with_versions('1.1.1', '2.1.0') do
|
50
|
+
assert_raises(RuntimeError) { @deploy.check_version }
|
51
|
+
end
|
52
|
+
|
53
|
+
with_versions('1.2.0', '2.1.0') do
|
54
|
+
assert_raises(RuntimeError) { @deploy.check_version }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_check_version_equal
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_print_errors_with_no_errors
|
63
|
+
data.expect(:failures, {})
|
64
|
+
assert_equal(@deploy.format_errors, 'Success!')
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_print_errors_when_current_branch_cant_merge
|
68
|
+
data.expect(:failures, {'origin/pushing_branch' => @branch})
|
69
|
+
@branch.fail!('some_random_sha')
|
70
|
+
|
71
|
+
current_branch_error = "\nERROR: Your branch did not merge to test_acceptance. Run the following commands to fix the merge conflict and then re-run this script:\n\n git checkout some_random_sha\n git merge pushing_branch\n # Resolve the conflicts\n git add <conflicted files>\n git commit --no-edit"
|
72
|
+
|
73
|
+
@deploy.instance_variable_get('@git'.to_sym).stub(:working_branch, 'pushing_branch') do
|
74
|
+
assert_equal(current_branch_error, @deploy.format_errors)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_print_errors_when_another_branch_cant_merge
|
79
|
+
data.expect(:failures, {'origin/pushing_branch' => @branch})
|
80
|
+
|
81
|
+
other_branch_error = "WARNING: Unable to merge branch origin/pushing_branch to test_acceptance due to conflicts."
|
82
|
+
|
83
|
+
assert_equal(@deploy.format_errors, other_branch_error)
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_check_out_to_working_branch
|
87
|
+
@deploy.stub(:check_repo, true) do
|
88
|
+
Lock::Base.stub_any_instance(:with_lock, -> { raise Lock::Error }) do
|
89
|
+
assert_output(/Failure!/) { @deploy.run }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_deleted_branch
|
95
|
+
data.expect(:mark_deleted, true, [@branch])
|
96
|
+
|
97
|
+
notifier.expect(:deleted_branch, true, [@branch])
|
98
|
+
|
99
|
+
merger.expect(:do_merge, :deleted, [ false ])
|
100
|
+
|
101
|
+
BranchMerger.stub(:new, merger) do
|
102
|
+
@deploy.git_merge(@branch, false)
|
103
|
+
end
|
104
|
+
|
105
|
+
notifier.verify
|
106
|
+
data.verify
|
107
|
+
merger.verify
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_merge_conflict
|
111
|
+
data.expect(:mark_failure, true, [@branch, 'some_sha'])
|
112
|
+
|
113
|
+
notifier.expect(:merge_conflict, true, [@branch])
|
114
|
+
|
115
|
+
merger
|
116
|
+
.expect(:do_merge, :conflict, [ false ])
|
117
|
+
.expect(:conflict_sha, 'some_sha')
|
118
|
+
|
119
|
+
BranchMerger.stub(:new, merger) do
|
120
|
+
@deploy.git_merge(@branch, false)
|
121
|
+
end
|
122
|
+
|
123
|
+
notifier.verify
|
124
|
+
data.verify
|
125
|
+
merger.verify
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_successful_merge
|
129
|
+
data.expect(:mark_success, true, [@branch])
|
130
|
+
data.expect(:set_resolutions, true, [ @branch, { 'filename' => ["resolution_sha"] } ])
|
131
|
+
|
132
|
+
merger.
|
133
|
+
expect(:do_merge, :success, [ false ]).
|
134
|
+
expect(:sha, 'sha').
|
135
|
+
expect(:resolutions, { 'filename' => ["resolution_sha"] })
|
136
|
+
|
137
|
+
BranchMerger.stub(:new, merger) do
|
138
|
+
@deploy.git_merge(@branch, false)
|
139
|
+
end
|
140
|
+
|
141
|
+
data.verify
|
142
|
+
merger.verify
|
143
|
+
assert_equal(@branch.sha, 'sha')
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_ignore_pushing_master_or_acceptance
|
147
|
+
['test_master', 'test_acceptance'].each do |branch|
|
148
|
+
@deploy.instance_variable_get('@git'.to_sym).stub(:working_branch, branch) do
|
149
|
+
refute(@deploy.open_pull_request)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
def merger
|
157
|
+
@merger ||= Minitest::Mock.new
|
158
|
+
end
|
159
|
+
|
160
|
+
def notifier
|
161
|
+
return @notifier if @notifier
|
162
|
+
|
163
|
+
@notifier = Minitest::Mock.new
|
164
|
+
@deploy.instance_variable_set('@notifier'.to_sym, @notifier)
|
165
|
+
end
|
166
|
+
|
167
|
+
def data
|
168
|
+
return @data if @data
|
169
|
+
|
170
|
+
@data = Minitest::Mock.new
|
171
|
+
@deploy.instance_variable_set('@data'.to_sym, @data)
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
|
3
|
+
module FlashFlow
|
4
|
+
class TestGit < Minitest::Test
|
5
|
+
def setup
|
6
|
+
@git_args = {
|
7
|
+
'merge_branch' => 'acceptance',
|
8
|
+
'merge_remote' => 'origin',
|
9
|
+
'master_branch' => 'master',
|
10
|
+
'remotes' => ['origin'],
|
11
|
+
'use_rerere' => true
|
12
|
+
}
|
13
|
+
@cmd_runner = setup_cmd_runner
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_initialize_rerere_checks_flag
|
17
|
+
@git_args['use_rerere'] = false
|
18
|
+
instance.initialize_rerere
|
19
|
+
|
20
|
+
@cmd_runner.verify
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_initialize_rerere_runs_commands
|
24
|
+
@cmd_runner.expect(:run, true, ['mkdir .git/rr-cache'])
|
25
|
+
@cmd_runner.expect(:run, true, ['cp -R rr-cache/* .git/rr-cache/'])
|
26
|
+
|
27
|
+
instance.initialize_rerere
|
28
|
+
@cmd_runner.verify
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_commit_rerere_checks_flag
|
32
|
+
@git_args['use_rerere'] = false
|
33
|
+
instance.commit_rerere([])
|
34
|
+
|
35
|
+
@cmd_runner.verify
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_commit_rerere_runs_commands
|
39
|
+
@cmd_runner.expect(:run, true, ['mkdir rr-cache'])
|
40
|
+
@cmd_runner.expect(:run, true, ['rm -rf rr-cache/*'])
|
41
|
+
@cmd_runner.expect(:run, true, ['cp -R .git/rr-cache/xyz rr-cache/'])
|
42
|
+
@cmd_runner.expect(:run, true, ['cp -R .git/rr-cache/abc rr-cache/'])
|
43
|
+
@cmd_runner.expect(:run, true, ['git add rr-cache/'])
|
44
|
+
@cmd_runner.expect(:run, true, ["git commit -m 'Update rr-cache'"])
|
45
|
+
|
46
|
+
instance.commit_rerere(['xyz', 'abc'])
|
47
|
+
@cmd_runner.verify
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_read_file_from_merge_branch
|
51
|
+
@cmd_runner.expect(:run, true, ["git show origin/acceptance:SomeFilename.txt"])
|
52
|
+
@cmd_runner.expect(:last_stdout, 'some_json', [])
|
53
|
+
@git_args['use_rerere'] = false
|
54
|
+
|
55
|
+
assert_equal(instance.read_file_from_merge_branch('SomeFilename.txt'), 'some_json')
|
56
|
+
@cmd_runner.verify
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
def instance
|
61
|
+
CmdRunner.stub(:new, @cmd_runner) do
|
62
|
+
_instance = Git.new(@git_args)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def setup_cmd_runner
|
67
|
+
cmd_runner = Minitest::Mock.new
|
68
|
+
cmd_runner.expect(:run, true, ['git rev-parse --abbrev-ref HEAD'])
|
69
|
+
cmd_runner.expect(:last_stdout, 'current_branch', [])
|
70
|
+
cmd_runner
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
require 'flash_flow/issue_tracker'
|
3
|
+
|
4
|
+
module FlashFlow
|
5
|
+
module IssueTracker
|
6
|
+
class TestBase < Minitest::Test
|
7
|
+
|
8
|
+
class FakeIssueTracker
|
9
|
+
def initialize(*args); end
|
10
|
+
def stories_pushed; 'pushed!'; end
|
11
|
+
def production_deploy; 'deployed!'; end
|
12
|
+
def stories_delivered; 'delivered!'; end
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_issue_tracker_class_not_set
|
16
|
+
empty_issue_tracker = IssueTracker::Base.new
|
17
|
+
issue_tracker.stub(:git, true) do
|
18
|
+
issue_tracker.stub(:get_branches, true) do
|
19
|
+
assert_nil(empty_issue_tracker.stories_pushed)
|
20
|
+
assert_nil(empty_issue_tracker.stories_delivered)
|
21
|
+
assert_nil(empty_issue_tracker.production_deploy)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_issue_tracker_class_set
|
27
|
+
issue_tracker.stub(:git, true) do
|
28
|
+
issue_tracker.stub(:get_branches, true) do
|
29
|
+
assert_equal(FakeIssueTracker.new.stories_pushed, issue_tracker.stories_pushed)
|
30
|
+
assert_equal(FakeIssueTracker.new.stories_delivered, issue_tracker.stories_delivered)
|
31
|
+
assert_equal(FakeIssueTracker.new.production_deploy, issue_tracker.production_deploy)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def issue_tracker
|
39
|
+
@issue_tracker ||= IssueTracker::Base.new({ 'class' => { 'name' => 'FlashFlow::IssueTracker::TestBase::FakeIssueTracker' }})
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
require 'flash_flow/notifier'
|
3
|
+
|
4
|
+
module FlashFlow
|
5
|
+
module Notifier
|
6
|
+
class TestBase < Minitest::Test
|
7
|
+
|
8
|
+
class FakeNotifier
|
9
|
+
def initialize(_=nil); end
|
10
|
+
|
11
|
+
def merge_conflict(_=nil); 'merge conflict';end
|
12
|
+
def deleted_branch(_=nil); 'deleted_branch';end
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_notifier_class_not_set
|
16
|
+
assert_nil(Notifier::Base.new.merge_conflict(nil))
|
17
|
+
assert_nil(Notifier::Base.new.deleted_branch(nil))
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_notifier_class_set
|
21
|
+
assert_equal(FakeNotifier.new.merge_conflict, notifier.merge_conflict(nil))
|
22
|
+
assert_equal(FakeNotifier.new.deleted_branch, notifier.deleted_branch(nil))
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def notifier
|
28
|
+
Notifier::Base
|
29
|
+
.new('class' => {'name' => 'FlashFlow::Notifier::TestBase::FakeNotifier'})
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'minitest'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'flash_flow'
|
4
|
+
|
5
|
+
ENV['GH_TOKEN'] = 'fake_token'
|
6
|
+
|
7
|
+
class Minitest::Test
|
8
|
+
|
9
|
+
class TestCmdRunner < Minitest::Mock
|
10
|
+
def initialize(opts={}); super(); end
|
11
|
+
def run(_); end
|
12
|
+
def last_success?
|
13
|
+
true
|
14
|
+
end
|
15
|
+
def last_stdout; ''; end
|
16
|
+
def last_stderr; ''; end
|
17
|
+
def last_command; ''; end
|
18
|
+
end
|
19
|
+
|
20
|
+
FlashFlow::CmdRunner = TestCmdRunner
|
21
|
+
|
22
|
+
def reset_config!
|
23
|
+
config = FlashFlow::Config.send(:instance)
|
24
|
+
|
25
|
+
config.instance_variables.each do |i|
|
26
|
+
config.remove_instance_variable(i)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def config!(config_hash)
|
31
|
+
reset_config!
|
32
|
+
|
33
|
+
File.stub(:read, config_hash.to_yaml) do
|
34
|
+
FlashFlow::Config.configure!('fake_file.txt')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|