git_multicast 0.1.0 → 0.2.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 (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