git_multicast 0.0.9 → 0.1.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.
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: