flash_flow 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.ruby-version +1 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +81 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +152 -0
  8. data/Rakefile +10 -0
  9. data/bin/flash_flow +23 -0
  10. data/flash_flow.gemspec +28 -0
  11. data/flash_flow.yml.erb.example +83 -0
  12. data/lib/flash_flow.rb +7 -0
  13. data/lib/flash_flow/branch_merger.rb +52 -0
  14. data/lib/flash_flow/cmd_runner.rb +37 -0
  15. data/lib/flash_flow/config.rb +71 -0
  16. data/lib/flash_flow/data.rb +6 -0
  17. data/lib/flash_flow/data/base.rb +58 -0
  18. data/lib/flash_flow/data/branch.rb +131 -0
  19. data/lib/flash_flow/data/collection.rb +181 -0
  20. data/lib/flash_flow/data/github.rb +129 -0
  21. data/lib/flash_flow/data/store.rb +33 -0
  22. data/lib/flash_flow/deploy.rb +184 -0
  23. data/lib/flash_flow/git.rb +248 -0
  24. data/lib/flash_flow/install.rb +19 -0
  25. data/lib/flash_flow/issue_tracker.rb +52 -0
  26. data/lib/flash_flow/issue_tracker/pivotal.rb +160 -0
  27. data/lib/flash_flow/lock.rb +25 -0
  28. data/lib/flash_flow/lock/github.rb +91 -0
  29. data/lib/flash_flow/notifier.rb +24 -0
  30. data/lib/flash_flow/notifier/hipchat.rb +36 -0
  31. data/lib/flash_flow/options.rb +36 -0
  32. data/lib/flash_flow/time_helper.rb +11 -0
  33. data/lib/flash_flow/version.rb +3 -0
  34. data/test/lib/data/test_base.rb +10 -0
  35. data/test/lib/data/test_branch.rb +203 -0
  36. data/test/lib/data/test_collection.rb +238 -0
  37. data/test/lib/data/test_github.rb +23 -0
  38. data/test/lib/data/test_store.rb +53 -0
  39. data/test/lib/issue_tracker/test_pivotal.rb +221 -0
  40. data/test/lib/lock/test_github.rb +70 -0
  41. data/test/lib/test_branch_merger.rb +76 -0
  42. data/test/lib/test_config.rb +84 -0
  43. data/test/lib/test_deploy.rb +175 -0
  44. data/test/lib/test_git.rb +73 -0
  45. data/test/lib/test_issue_tracker.rb +43 -0
  46. data/test/lib/test_notifier.rb +33 -0
  47. data/test/minitest_helper.rb +38 -0
  48. 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