git_multicast 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +17 -1
  3. data/README.md +1 -1
  4. data/git_multicast.gemspec +1 -0
  5. data/lib/git_multicast.rb +3 -8
  6. data/lib/git_multicast/adapter.rb +2 -0
  7. data/lib/git_multicast/{adapters → adapter}/bitbucket.rb +1 -1
  8. data/lib/git_multicast/{adapters → adapter}/github.rb +1 -1
  9. data/lib/git_multicast/cli.rb +38 -6
  10. data/lib/git_multicast/formatter.rb +6 -0
  11. data/lib/git_multicast/formatter/full.rb +18 -0
  12. data/lib/git_multicast/formatter/quiet.rb +13 -0
  13. data/lib/git_multicast/formatter/standard.rb +29 -0
  14. data/lib/git_multicast/formatter/status.rb +17 -0
  15. data/lib/git_multicast/multicaster.rb +31 -0
  16. data/lib/git_multicast/multicaster/clone.rb +35 -0
  17. data/lib/git_multicast/multicaster/pull.rb +30 -0
  18. data/lib/git_multicast/multicaster/status.rb +30 -0
  19. data/lib/git_multicast/repository_fetcher.rb +5 -2
  20. data/lib/git_multicast/task.rb +5 -3
  21. data/lib/git_multicast/task/result.rb +13 -0
  22. data/lib/git_multicast/task/runner.rb +38 -0
  23. data/lib/git_multicast/version.rb +1 -1
  24. data/spec/git_multicast/{adapters → adapter}/bitbucket_spec.rb +7 -3
  25. data/spec/git_multicast/cli_spec.rb +3 -3
  26. data/spec/git_multicast/formatter/full_spec.rb +32 -0
  27. data/spec/git_multicast/formatter/quiet_spec.rb +30 -0
  28. data/spec/git_multicast/formatter/standard_spec.rb +31 -0
  29. data/spec/git_multicast/formatter/status_spec.rb +32 -0
  30. data/spec/git_multicast/multicaster/clone_spec.rb +97 -0
  31. data/spec/git_multicast/multicaster/pull_spec.rb +41 -0
  32. data/spec/git_multicast/multicaster/status_spec.rb +41 -0
  33. data/spec/git_multicast/repository_fetcher_spec.rb +3 -3
  34. data/spec/git_multicast/task/runner_spec.rb +44 -0
  35. data/spec/git_multicast/task_spec.rb +8 -5
  36. metadata +39 -18
  37. data/lib/git_multicast/.rubocop.yml +0 -11
  38. data/lib/git_multicast/adapters.rb +0 -7
  39. data/lib/git_multicast/cloner.rb +0 -36
  40. data/lib/git_multicast/output_formatter.rb +0 -30
  41. data/lib/git_multicast/puller.rb +0 -28
  42. data/lib/git_multicast/statuser.rb +0 -28
  43. data/lib/git_multicast/task_result.rb +0 -7
  44. data/lib/git_multicast/task_runner.rb +0 -40
  45. data/spec/git_multicast/cloner_spec.rb +0 -94
  46. data/spec/git_multicast/output_formatter_spec.rb +0 -30
  47. data/spec/git_multicast/puller_spec.rb +0 -39
  48. data/spec/git_multicast/statuser_spec.rb +0 -39
  49. data/spec/git_multicast/task_runner_spec.rb +0 -53
@@ -0,0 +1,13 @@
1
+ module GitMulticast
2
+ class Task
3
+ Result = Struct.new(:name, :result, :exit_status) do
4
+ def to_s
5
+ result
6
+ end
7
+
8
+ def success?
9
+ exit_status.zero?
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,38 @@
1
+ module GitMulticast
2
+ class Task
3
+ class Runner
4
+ def initialize(tasks)
5
+ @tasks = tasks
6
+ end
7
+
8
+ def run!
9
+ tasks
10
+ .map(&method(:future))
11
+ .map(&:get)
12
+ end
13
+
14
+ protected
15
+
16
+ attr_reader :tasks
17
+
18
+ def future(task)
19
+ PoorMansFuture.new { task.call }
20
+ end
21
+
22
+ class PoorMansFuture
23
+ def initialize
24
+ @thread = Thread.new do
25
+ Thread.current[:output] = yield
26
+ end
27
+ end
28
+
29
+ def get
30
+ thread.join
31
+ thread[:output]
32
+ end
33
+
34
+ attr_reader :thread
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,3 +1,3 @@
1
1
  module GitMulticast
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -1,5 +1,5 @@
1
1
  module GitMulticast
2
- describe Adapters::Bitbucket do
2
+ describe Adapter::Bitbucket do
3
3
  subject(:adapter) { described_class.new(repo) }
4
4
 
5
5
  describe '#adapt' do
@@ -13,8 +13,12 @@ module GitMulticast
13
13
  # I know this is ugly, but well...
14
14
  it do
15
15
  VCR.use_cassette('bitbucket_repo') do
16
- expect(adapt.url).to eq('https://bitbucket.org/api/2.0/repositories/rranelli/cronofaker')
17
- expect(adapt.ssh_url).to eq('ssh://git@bitbucket.org/rranelli/cronofaker.git')
16
+ expect(adapt.url).to eq(
17
+ 'https://bitbucket.org/api/2.0/repositories/rranelli/cronofaker'
18
+ )
19
+ expect(adapt.ssh_url).to eq(
20
+ 'ssh://git@bitbucket.org/rranelli/cronofaker.git'
21
+ )
18
22
  expect(adapt.name).to eq('CronoFaker')
19
23
  expect(adapt.fork).to be_falsy
20
24
  expect(adapt.parent).to be_nil
@@ -16,7 +16,7 @@ module GitMulticast
16
16
  subject(:pull) { cli.pull }
17
17
 
18
18
  it do
19
- expect(Puller).to receive_message_chain(:new, :pull!)
19
+ expect(Multicaster::Pull).to receive_message_chain(:new, :execute!)
20
20
 
21
21
  pull
22
22
  end
@@ -26,7 +26,7 @@ module GitMulticast
26
26
  subject(:status) { cli.status }
27
27
 
28
28
  it do
29
- expect(Statuser).to receive_message_chain(:new, :statuses!)
29
+ expect(Multicaster::Status).to receive_message_chain(:new, :execute!)
30
30
 
31
31
  status
32
32
  end
@@ -38,7 +38,7 @@ module GitMulticast
38
38
  let(:username) { 'someone' }
39
39
 
40
40
  it do
41
- expect(Cloner).to receive_message_chain(:new, :clone!)
41
+ expect(Multicaster::Clone).to receive_message_chain(:new, :execute!)
42
42
 
43
43
  clone
44
44
  end
@@ -0,0 +1,32 @@
1
+ module GitMulticast
2
+ class Formatter
3
+ describe Full do
4
+ subject(:formatter) { described_class.new }
5
+
6
+ let(:name) { 'some action' }
7
+ let(:exit_status) { 0 }
8
+ let(:result_string) { 'stuff to be done' }
9
+
10
+ let(:result) { Task::Result.new(name, result_string, exit_status) }
11
+
12
+ describe '#format' do
13
+ subject(:format) { formatter.format(result) }
14
+
15
+ it do
16
+ is_expected.to match(/\[Success\]/)
17
+ .and match(/#{name}/)
18
+ .and match(/#{result}/)
19
+ end
20
+
21
+ context 'when exit_status is not 0' do
22
+ let(:exit_status) { 999 }
23
+
24
+ it do
25
+ is_expected.to match(/\[Error\]/)
26
+ .and match(/#{result}/)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,30 @@
1
+ module GitMulticast
2
+ class Formatter
3
+ describe Quiet do
4
+ subject(:formatter) { described_class.new }
5
+
6
+ let(:name) { 'some action' }
7
+ let(:status) { 0 }
8
+ let(:result_string) { 'stuff to be done' }
9
+
10
+ let(:result) { Task::Result.new(name, result_string, status) }
11
+
12
+ describe '#format' do
13
+ subject(:format) { formatter.format(result) }
14
+
15
+ it do
16
+ is_expected.to match(/^$/)
17
+ end
18
+
19
+ context 'when exit_status is not 0' do
20
+ let(:status) { 999 }
21
+
22
+ it do
23
+ is_expected.to match(/\[Error\]/)
24
+ .and match(/#{result}/)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,31 @@
1
+ module GitMulticast
2
+ class Formatter
3
+ describe Standard do
4
+ subject(:formatter) { described_class.new }
5
+
6
+ let(:name) { 'some action' }
7
+ let(:exit_status) { 0 }
8
+ let(:result_string) { 'stuff to be done' }
9
+
10
+ let(:result) { Task::Result.new(name, result_string, exit_status) }
11
+
12
+ describe '#format' do
13
+ subject(:format) { formatter.format(result) }
14
+
15
+ it do
16
+ is_expected.to match(/\[Success\]/)
17
+ .and match(/#{name}/)
18
+ end
19
+
20
+ context 'when exit_status is not 0' do
21
+ let(:exit_status) { 999 }
22
+
23
+ it do
24
+ is_expected.to match(/\[Error\]/)
25
+ .and match(/#{result}/)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,32 @@
1
+ module GitMulticast
2
+ class Formatter
3
+ describe Status do
4
+ subject(:formatter) { described_class.new }
5
+
6
+ let(:name) { 'some action' }
7
+ let(:exit_status) { 0 }
8
+ let(:result_string) { '"git add' }
9
+
10
+ let(:result) { Task::Result.new(name, result_string, exit_status) }
11
+
12
+ describe '#format' do
13
+ subject(:format) { formatter.format(result) }
14
+
15
+ it do
16
+ is_expected.to match(/\[No Changes\]/)
17
+ .and match(/#{name}/)
18
+ end
19
+
20
+ context 'when there are changes in the working directory' do
21
+ let(:result_string) { '"git add ' }
22
+
23
+ it do
24
+ is_expected.to match(/\[Changes\]/)
25
+ .and match(/#{name}/)
26
+ .and match(/#{result}/)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,97 @@
1
+ module GitMulticast
2
+ class Multicaster
3
+ describe Clone do
4
+ subject(:cloner) { described_class.new(username, dir) }
5
+
6
+ let(:username) { 'rranelli' }
7
+ let(:dir) { '/kifita/' }
8
+
9
+ let(:repo_name) { 'git_multicast' }
10
+
11
+ before do
12
+ allow(task).to receive(:spawn)
13
+ .and_return(pid)
14
+ allow(task).to receive(:wait)
15
+ .and_return(['', 0])
16
+
17
+ allow(Task).to receive(:new)
18
+ .and_return(task)
19
+ end
20
+
21
+ let(:task) { instance_double(Task, call: result) }
22
+ let(:result) { Task::Result.new(repo_name, 'success', 0) }
23
+
24
+ let(:pid) { 42 }
25
+
26
+ describe '#execute!' do
27
+ subject(:execute!) { cloner.execute! }
28
+
29
+ it do
30
+ VCR.use_cassette('clone_repos') do
31
+ expect(RepositoryFetcher).to receive(:get_all_repos_from_user)
32
+ .with(username)
33
+ .and_call_original
34
+
35
+ execute!
36
+ end
37
+ end
38
+
39
+ it 'creates a task for each fetched repository' do
40
+ VCR.use_cassette('clone_repos') do
41
+ expect(Task).to receive(:new).exactly(43).times
42
+
43
+ execute!
44
+ end
45
+ end
46
+
47
+ it 'creates a task runner and asks it to run all tasks' do
48
+ VCR.use_cassette('clone_repos') do
49
+ expect(Task::Runner).to receive_message_chain(:new, :run!)
50
+ .and_return([])
51
+
52
+ execute!
53
+ end
54
+ end
55
+
56
+ it 'spawns a clone job for each repo' do
57
+ VCR.use_cassette('clone_repos') do
58
+ expect(task).to receive(:call).exactly(43).times
59
+
60
+ execute!
61
+ end
62
+ end
63
+
64
+ it 'spawns a clone with the right parameters' do
65
+ VCR.use_cassette('clone_repos') do
66
+ command = "git clone git@github.com:rranelli/#{repo_name}.git" \
67
+ ' /kifita/git_multicast'
68
+
69
+ expect(Task).to receive(:new)
70
+ .with(repo_name, command)
71
+
72
+ execute!
73
+ end
74
+ end
75
+
76
+ context 'when repo is a fork'do
77
+ let(:repo_name) { 'emacs.d' }
78
+
79
+ it 'adds upstream remote' do
80
+ VCR.use_cassette('clone_repos') do
81
+ expect(Task).to receive(:new)
82
+ .with(
83
+ repo_name,
84
+ "git clone git@github.com:rranelli/#{repo_name}.git " \
85
+ "/kifita/#{repo_name} && git -C \"/kifita/#{repo_name}\"" \
86
+ ' remote add upstream ' \
87
+ "git@github.com:purcell/#{repo_name}.git --fetch"
88
+ )
89
+
90
+ execute!
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,41 @@
1
+ module GitMulticast
2
+ class Multicaster
3
+ describe Pull do
4
+ subject(:puller) { described_class.new(dir) }
5
+
6
+ let(:dir) { '/home/' }
7
+ let(:entries) { %w(one two) }
8
+
9
+ let(:task) { instance_double(Task, call: result) }
10
+ let(:result) { Task::Result.new('fitas', 'success', 0) }
11
+
12
+ before do
13
+ allow(File).to receive(:directory?).and_return(true)
14
+ allow(Dir).to receive(:entries).and_return(entries)
15
+
16
+ allow(Task).to receive(:new)
17
+ .and_return(task)
18
+ end
19
+
20
+ describe '#execute!' do
21
+ subject(:execute!) { puller.execute! }
22
+
23
+ it 'creates a task for each repository' do
24
+ entries.each do |entry|
25
+ expect(Task).to receive(:new)
26
+ .with(entry, "git -C #{entry} pull -r origin")
27
+ end
28
+
29
+ execute!
30
+ end
31
+
32
+ it 'runs tasks using a runner' do
33
+ expect(Task::Runner).to receive(:new)
34
+ .with([task, task]).and_call_original
35
+
36
+ execute!
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,41 @@
1
+ module GitMulticast
2
+ class Multicaster
3
+ describe Status do
4
+ subject(:statuser) { described_class.new(dir) }
5
+
6
+ let(:dir) { '/ki/fita/' }
7
+ let(:entries) { %w(fita1 fita2) }
8
+
9
+ let(:task) { instance_double(Task, call: result) }
10
+ let(:result) { Task::Result.new('fitas', 'success', 0) }
11
+
12
+ before do
13
+ allow(File).to receive(:directory?).and_return(true)
14
+ allow(Dir).to receive(:entries).and_return(entries)
15
+
16
+ allow(Task).to receive(:new)
17
+ .and_return(task)
18
+ end
19
+
20
+ describe '#execute!' do
21
+ subject(:execute!) { statuser.execute! }
22
+
23
+ it 'creates a task for each repository' do
24
+ entries.each do |entry|
25
+ expect(Task).to receive(:new)
26
+ .with(entry, "cd #{entry} && git status")
27
+ end
28
+
29
+ execute!
30
+ end
31
+
32
+ it 'runs tasks using a runner' do
33
+ expect(Task::Runner).to receive(:new)
34
+ .with([task, task]).and_call_original
35
+
36
+ execute!
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -14,7 +14,7 @@ module GitMulticast
14
14
  VCR.use_cassette('repos_from_all_services') do
15
15
  fetchers.each do |fetcher|
16
16
  expect(fetcher).to receive(:get_all_repos_from_user)
17
- .with(username).and_call_original
17
+ .with(username).and_call_original
18
18
  end
19
19
 
20
20
  get_all_repos_from_user
@@ -50,7 +50,7 @@ module GitMulticast
50
50
 
51
51
  it 'adapts with the right adapter' do
52
52
  VCR.use_cassette('github_repo') do
53
- expect(Adapters::Github).to receive_message_chain(
53
+ expect(Adapter::Github).to receive_message_chain(
54
54
  :new, :adapt, :parent
55
55
  )
56
56
 
@@ -99,7 +99,7 @@ module GitMulticast
99
99
 
100
100
  it 'adapts with the right adapter' do
101
101
  VCR.use_cassette('bitbucket_repo') do
102
- expect(Adapters::Bitbucket).to receive(:new).and_return(bb_adapter)
102
+ expect(Adapter::Bitbucket).to receive(:new).and_return(bb_adapter)
103
103
  expect(bb_adapter).to receive(:adapt)
104
104
 
105
105
  get_repo