git_multicast 0.0.9 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0ce4421b0ad7e7ea5b60462062d0b02008b1407b
4
- data.tar.gz: baa0f064f51d915eddc6a58318283ada2746ce39
3
+ metadata.gz: 139ef7bd2f4385eee6c08134ddb538f6bdb7ed2a
4
+ data.tar.gz: 73141c42606731439f6b6605634555ad2af72826
5
5
  SHA512:
6
- metadata.gz: c39d041b47714ed3e223846152b1c0bec5c784d2e6e6d1a6793d8006b0023ce0bebfd23ef1450a8f9af8e8fbc93ad1a764b76753d13ced49c98c5f1a4bf2c74d
7
- data.tar.gz: 2407c8b2a875a01383bc5a45da475ec339cd9b0fc2af43dcbfe0fe970d267ae2ff8f0bdf79041103f4bcfdc0d90c299f36ddb7595213a5a5c220c09d6f93c4cf
6
+ metadata.gz: 0a69b5d1b40d79b14b641c49ddb4e13f2bdeebafb0c8a91a0d97581db197fc680428642802d1ac7aa0daff516eb85b0e11b0a97f12ad02311aa0bdfdd9297d8b
7
+ data.tar.gz: 01949df4a72972df66da92fb6133ef0b66dddf1824e1d024a939170c97b6340efea0146814e94b53f4a973345dab5972e6a391e8a578bf6bc0883ea6aff0af8d
@@ -0,0 +1 @@
1
+ 2.1.5
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - '2.0.0'
4
+ - '2.1.0'
5
+ - '2.1.5'
6
+ script: bundle exec rspec
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- git_multicast (0.0.9)
4
+ git_multicast (0.1.0)
5
+ colorize (~> 0.7)
5
6
  recursive-open-struct (~> 0.5.0)
6
7
  thor (~> 0.19)
7
8
 
@@ -10,6 +11,7 @@ GEM
10
11
  specs:
11
12
  addressable (2.3.6)
12
13
  coderay (1.1.0)
14
+ colorize (0.7.3)
13
15
  crack (0.4.2)
14
16
  safe_yaml (~> 1.0.0)
15
17
  diff-lcs (1.2.5)
@@ -21,28 +23,28 @@ GEM
21
23
  pry-doc (0.6.0)
22
24
  pry (~> 0.9)
23
25
  yard (~> 0.8)
24
- rake (10.3.2)
26
+ rake (10.4.2)
25
27
  recursive-open-struct (0.5.0)
26
- rspec (3.0.0)
27
- rspec-core (~> 3.0.0)
28
- rspec-expectations (~> 3.0.0)
29
- rspec-mocks (~> 3.0.0)
30
- rspec-core (3.0.4)
31
- rspec-support (~> 3.0.0)
32
- rspec-expectations (3.0.4)
28
+ rspec (3.1.0)
29
+ rspec-core (~> 3.1.0)
30
+ rspec-expectations (~> 3.1.0)
31
+ rspec-mocks (~> 3.1.0)
32
+ rspec-core (3.1.7)
33
+ rspec-support (~> 3.1.0)
34
+ rspec-expectations (3.1.2)
33
35
  diff-lcs (>= 1.2.0, < 2.0)
34
- rspec-support (~> 3.0.0)
35
- rspec-mocks (3.0.4)
36
- rspec-support (~> 3.0.0)
37
- rspec-support (3.0.4)
38
- safe_yaml (1.0.3)
36
+ rspec-support (~> 3.1.0)
37
+ rspec-mocks (3.1.3)
38
+ rspec-support (~> 3.1.0)
39
+ rspec-support (3.1.2)
40
+ safe_yaml (1.0.4)
39
41
  slop (3.6.0)
40
42
  thor (0.19.1)
41
43
  vcr (2.9.3)
42
- webmock (1.18.0)
44
+ webmock (1.20.4)
43
45
  addressable (>= 2.3.6)
44
46
  crack (>= 0.3.2)
45
- yard (0.8.7.4)
47
+ yard (0.8.7.6)
46
48
 
47
49
  PLATFORMS
48
50
  ruby
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ [![Build Status](https://travis-ci.org/rranelli/git_multicast.svg?branch=0.1.0)](https://travis-ci.org/rranelli/git_multicast)
2
+ [![Gem Version](https://badge.fury.io/rb/git_multicast.svg)](http://badge.fury.io/rb/git_multicast)
1
3
  [![git_multicast API Documentation](https://www.omniref.com/ruby/gems/git_multicast.png)](https://www.omniref.com/ruby/gems/git_multicast)
2
4
 
3
5
  # Multicast your git actions.
@@ -15,11 +17,12 @@ just as long as cloning the biggest one, and nothing more.
15
17
 
16
18
  Actions currently supported:
17
19
 
18
- * Git clone all repositories of an user or organization (github only).
20
+ * Git clone all repositories of an user or organization (github & bitbucket).
19
21
  * Git pull all repositories in a directory.
22
+ * Git status all repositories in a directory.
20
23
 
21
24
  Actions to be supported:
22
25
 
23
- * Git clone repositories from hosts other than github.
24
- * Pass options to git pull.
25
- * Schedule git mass pull
26
+ * Git clone repositories from Gitlab.
27
+ * Pass options to git pull. (e.g., force, rebase, dry-run, etc.)
28
+ * Pass options to git status. (e.g., show only those that have modifications)
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_dependency 'recursive-open-struct', '~> 0.5.0'
22
22
  spec.add_dependency 'thor', '~> 0.19'
23
+ spec.add_dependency 'colorize', '~> 0.7'
23
24
 
24
25
  spec.add_development_dependency 'bundler', '~> 1.7'
25
26
  spec.add_development_dependency 'rspec', '~> 3.0'
@@ -4,6 +4,10 @@ require_relative 'git_multicast/puller'
4
4
  require_relative 'git_multicast/output_formatter'
5
5
  require_relative 'git_multicast/statuser'
6
6
 
7
+ require_relative 'git_multicast/task_runner'
8
+ require_relative 'git_multicast/task_result'
9
+ require_relative 'git_multicast/task'
10
+
7
11
  require_relative 'git_multicast/adapters'
8
12
  require_relative 'git_multicast/repository_fetcher'
9
13
 
@@ -1,24 +1,24 @@
1
1
  require 'thor'
2
2
 
3
- require_relative '../git_multicast'
3
+ require 'git_multicast'
4
4
 
5
5
  module GitMulticast
6
6
  class Cli < Thor
7
7
  desc 'git_multicast pull', 'Git pulls all repositories contained in\
8
8
  current directory.'
9
9
  def pull
10
- Puller.new(Dir.pwd).pull
10
+ puts Puller.new(Dir.pwd).pull!
11
11
  end
12
12
 
13
13
  desc 'git_multicast clone :username', 'Git pulls all repositories\
14
14
  contained in current directory.'
15
15
  def clone(username)
16
- Cloner.new(username, Dir.pwd).clone!
16
+ puts Cloner.new(username, Dir.pwd).clone!
17
17
  end
18
18
 
19
19
  desc 'git_multicast status', 'Shows status for each repository'
20
20
  def status
21
- Statuser.new(Dir.pwd).get_statuses
21
+ puts Statuser.new(Dir.pwd).statuses!
22
22
  end
23
23
 
24
24
  desc 'git_multicast version', 'Shows currently installed version'
@@ -3,51 +3,26 @@ require 'json'
3
3
 
4
4
  module GitMulticast
5
5
  class Cloner
6
- include Process
7
-
8
6
  def initialize(username, dir)
9
7
  @username = username
10
8
  @dir = dir
11
9
  end
12
10
 
13
11
  def clone!
14
- start_time = Time.now
15
12
  repos = RepositoryFetcher.get_all_repos_from_user(username)
16
- output_status_zip = clone_em_all!(repos)
17
13
 
18
- OutputFormatter.format(output_status_zip, start_time)
14
+ tasks = repos.map do |repo|
15
+ Task.new(repo.name, command(repo))
16
+ end
17
+
18
+ TaskRunner.new(tasks).run!
19
19
  end
20
20
 
21
21
  protected
22
22
 
23
23
  attr_reader :username, :dir
24
24
 
25
- def clone_em_all!(repos)
26
- streams = spawn_processes(repos)
27
-
28
- _, statuses = waitall.transpose
29
-
30
- output = read_output(streams)
31
- output.zip(statuses)
32
- end
33
-
34
- def spawn_processes(repos)
35
- repos.map do |repo|
36
- r, w = IO.pipe
37
- w.write("Clonning: #{repo.name}\n")
38
- spawn(make_command(repo), out: w, err: w)
39
- [r, w]
40
- end
41
- end
42
-
43
- def read_output(streams)
44
- streams.map do |r, w|
45
- w.close unless w.closed?
46
- r.read
47
- end
48
- end
49
-
50
- def make_command(repo)
25
+ def command(repo)
51
26
  if repo.fork
52
27
  parent_repo = RepositoryFetcher.get_repo_parent(repo.url)
53
28
  "git clone #{repo.ssh_url} #{File.join(dir, repo.name)} && \
@@ -1,20 +1,30 @@
1
+ require 'colorize'
2
+
1
3
  module GitMulticast
2
4
  class OutputFormatter
3
- def self.format(output_status_zip, start_time = nil)
4
- # get successes and failures
5
- success_pairs = output_status_zip.select { |_, status| status.success? }
6
- failure_pairs = output_status_zip.reject { |_, status| status.success? }
5
+ def initialize(start_time = nil)
6
+ @start_time = start_time
7
+ end
7
8
 
8
- success_pairs.each do |output, _|
9
- puts(
10
- "#{output} \nsuccess.\n" \
11
- '=============================================='
12
- )
9
+ def single_format(task_result)
10
+ case task_result.exit_status
11
+ when 0
12
+ '[Success]'.green + " #{task_result.name} #{time_report}"
13
+ else
14
+ <<EOF
15
+ #{'[Error]'.red} #{task_result.name}
16
+ #{task_result.result}
17
+ #{'-' * 25}
18
+ EOF
13
19
  end
14
- failure_pairs.each { |output, _| puts "#{output}\nFAILURE!!" }
20
+ end
21
+
22
+ protected
23
+
24
+ attr_reader :start_time
15
25
 
16
- puts '#############################################'
17
- puts "Finished in #{Time.now - start_time} seconds." if start_time
26
+ def time_report
27
+ "in #{Time.now - start_time} seconds" unless start_time.nil?
18
28
  end
19
29
  end
20
30
  end
@@ -1,31 +1,28 @@
1
1
  module GitMulticast
2
2
  class Puller
3
- include Process
4
-
5
- attr_reader :dir
6
-
7
3
  def initialize(dir)
8
4
  @dir = dir
9
5
  end
10
6
 
11
- def pull
12
- dirs = Dir.entries(dir)
7
+ def pull!
8
+ tasks = Dir.entries(dir)
13
9
  .select { |f| File.directory? f }
14
10
  .reject { |f| f =~ /^\./ } # ., .. and .git and the like
11
+ .map { |dir| Task.new(description(dir), command(dir)) }
15
12
 
16
- dirs.each do |dir|
17
- spawn "git -C #{dir} pull -r origin"
18
- end
13
+ TaskRunner.new(tasks).run!
14
+ end
15
+
16
+ protected
17
+
18
+ attr_reader :dir
19
19
 
20
- _, statuses = waitall.transpose
21
- format_result(dirs, statuses)
20
+ def command(dir)
21
+ "git -C #{dir} pull -r origin"
22
22
  end
23
23
 
24
- def format_result(repositories, statuses)
25
- repositories.zip(statuses).each do |repo, status|
26
- puts "Pulled #{repo} successfully" if status && status.success?
27
- puts "Failed to pull #{repo}" unless status && status.success?
28
- end
24
+ def description(dir)
25
+ File.basename(dir)
29
26
  end
30
27
  end
31
28
  end
@@ -1,42 +1,28 @@
1
1
  module GitMulticast
2
2
  class Statuser
3
- include Process
4
-
5
- attr_reader :dir
6
-
7
3
  def initialize(dir)
8
4
  @dir = dir
9
5
  end
10
6
 
11
- def get_statuses
12
- start_time = Time.now
13
- output_status_zip = statuses
7
+ def statuses!
8
+ tasks = Dir.entries(dir)
9
+ .select { |f| File.directory? f }
10
+ .reject { |f| f =~ /^\./ } # ., .. and .git and the like
11
+ .map { |dir| Task.new(description(dir), command(dir)) }
14
12
 
15
- OutputFormatter.format(output_status_zip, start_time)
13
+ TaskRunner.new(tasks).run!
16
14
  end
17
15
 
18
- def statuses
19
- dirs = Dir.entries(dir)
20
- .select { |f| File.directory? f }
21
- .reject { |f| f =~ /^\./ } # ., .. and .git and the like
16
+ protected
22
17
 
23
- streams = dirs.map do |dir|
24
- r, w = IO.pipe
25
- w.write("Repo: #{dir}\n")
26
- spawn("cd #{dir} && git status", out: w, err: w)
27
- [r, w]
28
- end
29
- _, statuses = waitall.transpose
18
+ attr_reader :dir
30
19
 
31
- output = read_output(streams)
32
- output.zip(statuses)
20
+ def command(dir)
21
+ "cd #{dir} && git status"
33
22
  end
34
23
 
35
- def read_output(streams)
36
- streams.map do |r, w|
37
- w.close unless w.closed?
38
- r.read
39
- end
24
+ def description(dir)
25
+ File.basename(dir)
40
26
  end
41
27
  end
42
28
  end
@@ -0,0 +1,28 @@
1
+ module GitMulticast
2
+ class Task
3
+ include Process
4
+
5
+ def initialize(description, command)
6
+ @description, @command = description, command
7
+ end
8
+
9
+ def run!
10
+ r, w = IO.pipe
11
+ w.write("Running: #{description}\n")
12
+ pid = spawn(command, out: w, err: w)
13
+
14
+ _, exit_status = wait(pid)
15
+ w.close unless w.closed?
16
+
17
+ TaskResult.new(description, r.read, exit_status)
18
+ ensure
19
+ w.close unless w.closed?
20
+ end
21
+
22
+ alias_method :call, :run!
23
+
24
+ protected
25
+
26
+ attr_reader :description, :command
27
+ end
28
+ end
@@ -0,0 +1,7 @@
1
+ module GitMulticast
2
+ TaskResult = Struct.new(:name, :result, :exit_status) do
3
+ def to_s
4
+ result
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,40 @@
1
+ module GitMulticast
2
+ class TaskRunner
3
+ include Process
4
+
5
+ def initialize(tasks, formatter = OutputFormatter.new(Time.now))
6
+ @tasks = tasks
7
+ @formatter = formatter
8
+ end
9
+
10
+ def run!
11
+ tasks
12
+ .map(&method(:future))
13
+ .map(&:get)
14
+ .inject('', &:+)
15
+ end
16
+
17
+ protected
18
+
19
+ attr_reader :tasks, :formatter
20
+
21
+ def future(task)
22
+ PoorMansFuture.new { formatter.single_format(task.call) }
23
+ end
24
+
25
+ class PoorMansFuture
26
+ def initialize(&block)
27
+ @thread = Thread.new do
28
+ Thread.current[:output] = block.call
29
+ end
30
+ end
31
+
32
+ def get
33
+ thread.join
34
+ thread[:output]
35
+ end
36
+
37
+ attr_reader :thread
38
+ end
39
+ end
40
+ end
@@ -1,3 +1,3 @@
1
1
  module GitMulticast
2
- VERSION = '0.0.9'
2
+ VERSION = '0.1.0'
3
3
  end
@@ -0,0 +1,47 @@
1
+ require 'git_multicast/cli'
2
+
3
+ module GitMulticast
4
+ describe Cli do
5
+ subject(:cli) { described_class.new }
6
+
7
+ before do
8
+ $stdout = StringIO.new
9
+ end
10
+
11
+ after do
12
+ $stdout = STDOUT
13
+ end
14
+
15
+ describe '#pull' do
16
+ subject(:pull) { cli.pull }
17
+
18
+ it do
19
+ expect(Puller).to receive_message_chain(:new, :pull!)
20
+
21
+ pull
22
+ end
23
+ end
24
+
25
+ describe '#status' do
26
+ subject(:status) { cli.status }
27
+
28
+ it do
29
+ expect(Statuser).to receive_message_chain(:new, :statuses!)
30
+
31
+ status
32
+ end
33
+ end
34
+
35
+ describe '#clone' do
36
+ subject(:clone) { cli.clone(username) }
37
+
38
+ let(:username) { 'someone' }
39
+
40
+ it do
41
+ expect(Cloner).to receive_message_chain(:new, :clone!)
42
+
43
+ clone
44
+ end
45
+ end
46
+ end
47
+ end
@@ -7,27 +7,53 @@ module GitMulticast
7
7
 
8
8
  let(:repo_name) { 'git_multicast' }
9
9
 
10
- let(:pipe) { IO.pipe }
11
- let(:r) { pipe.first }
12
- let(:w) { pipe[1] }
13
-
14
10
  before do
15
- allow(cloner).to receive(:spawn).and_return(nil)
16
- allow(cloner).to receive(:waitall).and_return(
17
- [[nil, double(:success, success?: true)]] * 32
18
- )
19
-
20
- allow(IO).to receive(:pipe).and_return([r, w])
11
+ allow(task).to receive(:spawn)
12
+ .and_return(pid)
13
+ allow(task).to receive(:wait)
14
+ .and_return(['', 0])
21
15
 
22
- allow(OutputFormatter).to receive(:format)
16
+ allow(Task).to receive(:new)
17
+ .and_return(task)
23
18
  end
24
19
 
20
+ let(:task) { instance_double(Task, call: result) }
21
+ let(:result) { TaskResult.new(repo_name, 'success', 0) }
22
+
23
+ let(:pid) { 42 }
24
+
25
25
  describe '#clone!' do
26
26
  subject(:clone!) { cloner.clone! }
27
27
 
28
+ it do
29
+ VCR.use_cassette('clone_repos') do
30
+ expect(RepositoryFetcher).to receive(:get_all_repos_from_user)
31
+ .with(username)
32
+ .and_call_original
33
+
34
+ clone!
35
+ end
36
+ end
37
+
38
+ it 'creates a task for each fetched repository' do
39
+ VCR.use_cassette('clone_repos') do
40
+ expect(Task).to receive(:new).exactly(43).times
41
+
42
+ clone!
43
+ end
44
+ end
45
+
46
+ it 'creates a task runner and asks it to run all tasks' do
47
+ VCR.use_cassette('clone_repos') do
48
+ expect(TaskRunner).to receive_message_chain(:new, :run!)
49
+
50
+ clone!
51
+ end
52
+ end
53
+
28
54
  it 'spawns a clone job for each repo' do
29
55
  VCR.use_cassette('clone_repos') do
30
- expect(cloner).to receive(:spawn).exactly(43).times
56
+ expect(task).to receive(:call).exactly(43).times
31
57
 
32
58
  clone!
33
59
  end
@@ -35,26 +61,29 @@ module GitMulticast
35
61
 
36
62
  it 'spawns a clone with the right parameters' do
37
63
  VCR.use_cassette('clone_repos') do
38
- expect(cloner).to receive(:spawn)
39
- .with("git clone git@github.com:rranelli/#{repo_name}.git" \
40
- ' /kifita/git_multicast', out: w, err: w)
64
+ clone_command = "git clone git@github.com:rranelli/#{repo_name}.git" \
65
+ ' /kifita/git_multicast'
66
+
67
+ expect(Task).to receive(:new)
68
+ .with(repo_name, clone_command)
41
69
 
42
70
  clone!
43
71
  end
44
72
  end
45
73
 
46
74
  context 'when repo is a fork'do
47
- let(:repo_name) { 'git-hooks' }
75
+ let(:repo_name) { 'emacs.d' }
48
76
 
49
77
  it 'adds upstream remote' do
50
78
  VCR.use_cassette('clone_repos') do
51
- expect(cloner).to receive(:spawn)
79
+ expect(Task).to receive(:new)
52
80
  .with(
53
- "git clone git@github.com:rranelli/ruby-#{repo_name}.git " \
54
- "/kifita/ruby-#{repo_name} && git -C \"/kifita/ruby-#{repo_name}\"" \
81
+ repo_name,
82
+ "git clone git@github.com:rranelli/#{repo_name}.git " \
83
+ "/kifita/#{repo_name} && git -C \"/kifita/#{repo_name}\"" \
55
84
  ' remote add upstream ' \
56
- "git@github.com:stupied4ever/#{repo_name}.git --fetch",
57
- out: w, err: w)
85
+ "git@github.com:purcell/#{repo_name}.git --fetch"
86
+ )
58
87
 
59
88
  clone!
60
89
  end
@@ -0,0 +1,30 @@
1
+ module GitMulticast
2
+ describe OutputFormatter do
3
+ subject(:formatter) { described_class.new }
4
+
5
+ let(:name) { "some action" }
6
+ let(:exit_status) { 0 }
7
+ let(:result_string) { "stuff to be done" }
8
+
9
+ let(:result) { TaskResult.new(name, result_string, exit_status) }
10
+
11
+ describe '.single_format' do
12
+ subject(:single_format) { formatter.single_format(result) }
13
+
14
+ it do
15
+ is_expected.to match(/\[Success\]/)
16
+ .and match(/#{name}/)
17
+ end
18
+
19
+ context 'when exit_status is not 0' do
20
+ let(:exit_status) { 999 }
21
+
22
+ it do
23
+ is_expected.to match(/\[Error\]/)
24
+ .and match(/#{result}/)
25
+ .and match(/-+$/)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,54 +1,38 @@
1
- describe GitMulticast::Puller do
2
- subject(:puller) { described_class.new(dir) }
1
+ module GitMulticast
2
+ describe Puller do
3
+ subject(:puller) { described_class.new(dir) }
3
4
 
4
- let(:dir) { '/home/' }
5
- let(:entries) { %w(one two) }
5
+ let(:dir) { '/home/' }
6
+ let(:entries) { %w(one two) }
6
7
 
7
- let(:success) { double(:success, success?: true) }
8
+ let(:task) { instance_double(Task, call: result) }
9
+ let(:result) { TaskResult.new('fitas', 'success', 0) }
8
10
 
9
- before do
10
- $stdout = StringIO.new
11
+ before do
12
+ allow(File).to receive(:directory?).and_return(true)
13
+ allow(Dir).to receive(:entries).and_return(entries)
11
14
 
12
- allow(File).to receive(:directory?).and_return(true)
13
- allow(Dir).to receive(:entries).and_return(entries)
14
-
15
- allow(puller).to receive(:spawn).and_return(nil)
16
- allow(puller).to receive(:waitall).and_return([[1, success], [2, success]])
17
- end
18
-
19
- after do
20
- $stdout = STDOUT
21
- end
22
-
23
- describe '#pull' do
24
- subject(:pull) { puller.pull }
25
-
26
- it 'issues a git pull command for each entry in dir' do
27
- entries.each do |entry|
28
- expect(puller).to receive(:spawn).with("git -C #{entry} pull -r origin")
29
- end
30
-
31
- pull
15
+ allow(Task).to receive(:new)
16
+ .and_return(task)
32
17
  end
33
18
 
34
- it 'formats results' do
35
- expect { pull }.to output(
36
- "Pulled one successfully\nPulled two successfully\n"
37
- ).to_stdout
38
- end
19
+ describe '#pull!' do
20
+ subject(:pull!) { puller.pull! }
21
+
22
+ it 'creates a task for each repository' do
23
+ entries.each do |entry|
24
+ expect(Task).to receive(:new)
25
+ .with(entry, "git -C #{entry} pull -r origin")
26
+ end
39
27
 
40
- context 'with error output' do
41
- before do
42
- allow(puller).to receive(:waitall)
43
- .and_return([[1, success], [2, nil]])
28
+ pull!
44
29
  end
45
30
 
46
- it 'formats results correctly when there is an error in a job' do
47
- expect { pull }.to output(
48
- "Pulled one successfully\nFailed to pull two\n"
49
- ).to_stdout
31
+ it 'runs tasks using a runner' do
32
+ expect(TaskRunner).to receive(:new)
33
+ .with([task, task]).and_call_original
50
34
 
51
- pull
35
+ pull!
52
36
  end
53
37
  end
54
38
  end
@@ -5,39 +5,34 @@ module GitMulticast
5
5
  let(:dir) { '/ki/fita/' }
6
6
  let(:entries) { ['fita1', 'fita2'] }
7
7
 
8
- let(:pipe) { IO.pipe }
9
- let(:r) { pipe.first }
10
- let(:w) { pipe[1] }
8
+ let(:task) { instance_double(Task, call: result) }
9
+ let(:result) { TaskResult.new('fitas', 'success', 0) }
11
10
 
12
11
  before do
13
- $stdout = StringIO.new
14
-
15
- allow(statuser).to receive(:spawn).and_return(nil)
16
- allow(statuser).to receive(:waitall).and_return(
17
- [[nil, double(:success, success?: true)]] * 32
18
- )
19
-
20
- allow(IO).to receive(:pipe).and_return([r, w])
21
12
  allow(File).to receive(:directory?).and_return(true)
22
13
  allow(Dir).to receive(:entries).and_return(entries)
23
14
 
24
- allow(OutputFormatter).to receive(:format)
25
- end
26
-
27
- after do
28
- $stdout = STDOUT
15
+ allow(Task).to receive(:new)
16
+ .and_return(task)
29
17
  end
30
18
 
31
- describe '#get_statuses' do
32
- subject(:get_statuses) { statuser.get_statuses }
19
+ describe '#statuses!' do
20
+ subject(:statuses!) { statuser.statuses! }
33
21
 
34
- it 'spawns a clone job for each repo' do
22
+ it 'creates a task for each repository' do
35
23
  entries.each do |entry|
36
- expect(statuser).to receive(:spawn)
37
- .with("cd #{entry} && git status", out: w, err: w)
24
+ expect(Task).to receive(:new)
25
+ .with(entry, "cd #{entry} && git status")
38
26
  end
39
27
 
40
- get_statuses
28
+ statuses!
29
+ end
30
+
31
+ it 'runs tasks using a runner' do
32
+ expect(TaskRunner).to receive(:new)
33
+ .with([task, task]).and_call_original
34
+
35
+ statuses!
41
36
  end
42
37
  end
43
38
  end
@@ -0,0 +1,53 @@
1
+ module GitMulticast
2
+ describe TaskRunner do
3
+ subject(:runner) { described_class.new(tasks) }
4
+
5
+ let(:tasks) { [hey_task, ho_task] }
6
+
7
+ # Since tasks quack like lambdas, I will be using them here.
8
+ let(:hey_task) { ->() { first_result } }
9
+ let(:ho_task) { ->() { second_result } }
10
+
11
+ let!(:first_result) { TaskResult.new(:one, 'Hey!', 1) }
12
+ let!(:second_result) { TaskResult.new(:two, 'Ho!', 1) }
13
+
14
+ let(:formatter) { OutputFormatter.new }
15
+
16
+ before do
17
+ allow(OutputFormatter).to receive(:new)
18
+ .and_return(formatter)
19
+ end
20
+
21
+ describe '#run!' do
22
+ subject(:run!) { runner.run! }
23
+
24
+ it 'creates a thread for each task'do
25
+ expect(Thread).to receive(:new)
26
+ .exactly(tasks.size).times
27
+ .and_call_original
28
+
29
+ run!
30
+ end
31
+
32
+ it 'calls each task' do
33
+ expect(hey_task).to receive(:call)
34
+ .and_call_original
35
+ expect(ho_task).to receive(:call)
36
+ .and_call_original
37
+
38
+ run!
39
+ end
40
+
41
+ it 'formats each output' do
42
+ expect(formatter).to receive(:single_format)
43
+ .with(first_result)
44
+ .and_call_original
45
+ expect(formatter).to receive(:single_format)
46
+ .with(second_result)
47
+ .and_call_original
48
+
49
+ run!
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,56 @@
1
+ describe GitMulticast::Task do
2
+ subject(:task) { described_class.new(description, command) }
3
+
4
+ let(:description) { 'some_repo' }
5
+ let(:command) { 'git clone some_repo' }
6
+
7
+ let(:pid) { 42 }
8
+
9
+ let(:pipe) { IO.pipe }
10
+ let(:r) { pipe.first }
11
+ let(:w) { pipe[1] }
12
+
13
+ before do
14
+ allow(task).to receive(:spawn)
15
+ .and_return(pid)
16
+ allow(task).to receive(:wait)
17
+ .and_return(['', 0])
18
+
19
+ allow(IO).to receive(:pipe)
20
+ .and_return([r, w])
21
+ allow(r).to receive(:read)
22
+ .and_return('I be output!')
23
+ end
24
+
25
+ describe '#run!' do
26
+ subject(:run!) { task.run! }
27
+
28
+ it 'creates an input and output stream' do
29
+ expect(IO).to receive(:pipe)
30
+ .and_call_original
31
+
32
+ run!
33
+ end
34
+
35
+ it 'spawns a child process' do
36
+ expect(task).to receive(:spawn)
37
+ .with(command, out: w, err: w)
38
+
39
+ run!
40
+ end
41
+
42
+ it 'waits for its process to finish' do
43
+ expect(task).to receive(:wait)
44
+ .with(pid)
45
+
46
+ run!
47
+ end
48
+
49
+ it 'wraps its result in a TaskResult object' do
50
+ expect(GitMulticast::TaskResult).to receive(:new)
51
+ .with(description, 'I be output!', 0)
52
+
53
+ run!
54
+ end
55
+ end
56
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git_multicast
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Renan Ranelli
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-02 00:00:00.000000000 Z
11
+ date: 2014-12-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: recursive-open-struct
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0.19'
41
+ - !ruby/object:Gem::Dependency
42
+ name: colorize
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.7'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.7'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: bundler
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -161,6 +175,8 @@ files:
161
175
  - ".gitignore"
162
176
  - ".rspec"
163
177
  - ".rubocop.yml"
178
+ - ".ruby-version"
179
+ - ".travis.yml"
164
180
  - Gemfile
165
181
  - Gemfile.lock
166
182
  - LICENSE
@@ -181,6 +197,9 @@ files:
181
197
  - lib/git_multicast/repository_fetcher/bitbucket.rb
182
198
  - lib/git_multicast/repository_fetcher/github.rb
183
199
  - lib/git_multicast/statuser.rb
200
+ - lib/git_multicast/task.rb
201
+ - lib/git_multicast/task_result.rb
202
+ - lib/git_multicast/task_runner.rb
184
203
  - lib/git_multicast/version.rb
185
204
  - spec/fixtures/vcr_cassettes/bitbucket_all_user_repos.yml
186
205
  - spec/fixtures/vcr_cassettes/bitbucket_repo.yml
@@ -190,12 +209,16 @@ files:
190
209
  - spec/fixtures/vcr_cassettes/github_repo_parent.yml
191
210
  - spec/fixtures/vcr_cassettes/repos_from_all_services.yml
192
211
  - spec/git_multicast/adapters/bitbucket_spec.rb
212
+ - spec/git_multicast/cli_spec.rb
193
213
  - spec/git_multicast/cloner_spec.rb
214
+ - spec/git_multicast/output_formatter_spec.rb
194
215
  - spec/git_multicast/puller_spec.rb
195
216
  - spec/git_multicast/repository_fetcher/bitbucket_spec.rb
196
217
  - spec/git_multicast/repository_fetcher/github_spec.rb
197
218
  - spec/git_multicast/repository_fetcher_spec.rb
198
219
  - spec/git_multicast/statuser_spec.rb
220
+ - spec/git_multicast/task_runner_spec.rb
221
+ - spec/git_multicast/task_spec.rb
199
222
  - spec/spec_helper.rb
200
223
  homepage: http://github.com/rranelli/git_multicast
201
224
  licenses: