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.
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