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 +4 -4
- data/.ruby-version +1 -0
- data/.travis.yml +6 -0
- data/Gemfile.lock +18 -16
- data/README.md +7 -4
- data/git_multicast.gemspec +1 -0
- data/lib/git_multicast.rb +4 -0
- data/lib/git_multicast/cli.rb +4 -4
- data/lib/git_multicast/cloner.rb +6 -31
- data/lib/git_multicast/output_formatter.rb +22 -12
- data/lib/git_multicast/puller.rb +13 -16
- data/lib/git_multicast/statuser.rb +12 -26
- data/lib/git_multicast/task.rb +28 -0
- data/lib/git_multicast/task_result.rb +7 -0
- data/lib/git_multicast/task_runner.rb +40 -0
- data/lib/git_multicast/version.rb +1 -1
- data/spec/git_multicast/cli_spec.rb +47 -0
- data/spec/git_multicast/cloner_spec.rb +50 -21
- data/spec/git_multicast/output_formatter_spec.rb +30 -0
- data/spec/git_multicast/puller_spec.rb +25 -41
- data/spec/git_multicast/statuser_spec.rb +17 -22
- data/spec/git_multicast/task_runner_spec.rb +53 -0
- data/spec/git_multicast/task_spec.rb +56 -0
- metadata +25 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 139ef7bd2f4385eee6c08134ddb538f6bdb7ed2a
|
4
|
+
data.tar.gz: 73141c42606731439f6b6605634555ad2af72826
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0a69b5d1b40d79b14b641c49ddb4e13f2bdeebafb0c8a91a0d97581db197fc680428642802d1ac7aa0daff516eb85b0e11b0a97f12ad02311aa0bdfdd9297d8b
|
7
|
+
data.tar.gz: 01949df4a72972df66da92fb6133ef0b66dddf1824e1d024a939170c97b6340efea0146814e94b53f4a973345dab5972e6a391e8a578bf6bc0883ea6aff0af8d
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.1.5
|
data/.travis.yml
ADDED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
git_multicast (0.0
|
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.
|
26
|
+
rake (10.4.2)
|
25
27
|
recursive-open-struct (0.5.0)
|
26
|
-
rspec (3.
|
27
|
-
rspec-core (~> 3.
|
28
|
-
rspec-expectations (~> 3.
|
29
|
-
rspec-mocks (~> 3.
|
30
|
-
rspec-core (3.
|
31
|
-
rspec-support (~> 3.
|
32
|
-
rspec-expectations (3.
|
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.
|
35
|
-
rspec-mocks (3.
|
36
|
-
rspec-support (~> 3.
|
37
|
-
rspec-support (3.
|
38
|
-
safe_yaml (1.0.
|
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.
|
44
|
+
webmock (1.20.4)
|
43
45
|
addressable (>= 2.3.6)
|
44
46
|
crack (>= 0.3.2)
|
45
|
-
yard (0.8.7.
|
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
|
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
|
24
|
-
* Pass options to git pull.
|
25
|
-
*
|
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)
|
data/git_multicast.gemspec
CHANGED
@@ -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'
|
data/lib/git_multicast.rb
CHANGED
@@ -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
|
|
data/lib/git_multicast/cli.rb
CHANGED
@@ -1,24 +1,24 @@
|
|
1
1
|
require 'thor'
|
2
2
|
|
3
|
-
|
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).
|
21
|
+
puts Statuser.new(Dir.pwd).statuses!
|
22
22
|
end
|
23
23
|
|
24
24
|
desc 'git_multicast version', 'Shows currently installed version'
|
data/lib/git_multicast/cloner.rb
CHANGED
@@ -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
|
-
|
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
|
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
|
4
|
-
|
5
|
-
|
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
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
attr_reader :start_time
|
15
25
|
|
16
|
-
|
17
|
-
|
26
|
+
def time_report
|
27
|
+
"in #{Time.now - start_time} seconds" unless start_time.nil?
|
18
28
|
end
|
19
29
|
end
|
20
30
|
end
|
data/lib/git_multicast/puller.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
13
|
+
TaskRunner.new(tasks).run!
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
attr_reader :dir
|
19
19
|
|
20
|
-
|
21
|
-
|
20
|
+
def command(dir)
|
21
|
+
"git -C #{dir} pull -r origin"
|
22
22
|
end
|
23
23
|
|
24
|
-
def
|
25
|
-
|
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
|
12
|
-
|
13
|
-
|
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
|
-
|
13
|
+
TaskRunner.new(tasks).run!
|
16
14
|
end
|
17
15
|
|
18
|
-
|
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
|
-
|
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
|
-
|
32
|
-
|
20
|
+
def command(dir)
|
21
|
+
"cd #{dir} && git status"
|
33
22
|
end
|
34
23
|
|
35
|
-
def
|
36
|
-
|
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,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
|
@@ -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(
|
16
|
-
|
17
|
-
|
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(
|
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(
|
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
|
-
|
39
|
-
|
40
|
-
|
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) { '
|
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(
|
79
|
+
expect(Task).to receive(:new)
|
52
80
|
.with(
|
53
|
-
|
54
|
-
"
|
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:
|
57
|
-
|
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
|
-
|
2
|
-
|
1
|
+
module GitMulticast
|
2
|
+
describe Puller do
|
3
|
+
subject(:puller) { described_class.new(dir) }
|
3
4
|
|
4
|
-
|
5
|
-
|
5
|
+
let(:dir) { '/home/' }
|
6
|
+
let(:entries) { %w(one two) }
|
6
7
|
|
7
|
-
|
8
|
+
let(:task) { instance_double(Task, call: result) }
|
9
|
+
let(:result) { TaskResult.new('fitas', 'success', 0) }
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
+
before do
|
12
|
+
allow(File).to receive(:directory?).and_return(true)
|
13
|
+
allow(Dir).to receive(:entries).and_return(entries)
|
11
14
|
|
12
|
-
|
13
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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
|
-
|
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 '
|
47
|
-
expect
|
48
|
-
|
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(:
|
9
|
-
let(:
|
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(
|
25
|
-
|
26
|
-
|
27
|
-
after do
|
28
|
-
$stdout = STDOUT
|
15
|
+
allow(Task).to receive(:new)
|
16
|
+
.and_return(task)
|
29
17
|
end
|
30
18
|
|
31
|
-
describe '#
|
32
|
-
subject(:
|
19
|
+
describe '#statuses!' do
|
20
|
+
subject(:statuses!) { statuser.statuses! }
|
33
21
|
|
34
|
-
it '
|
22
|
+
it 'creates a task for each repository' do
|
35
23
|
entries.each do |entry|
|
36
|
-
expect(
|
37
|
-
.with("cd #{entry} && git status"
|
24
|
+
expect(Task).to receive(:new)
|
25
|
+
.with(entry, "cd #{entry} && git status")
|
38
26
|
end
|
39
27
|
|
40
|
-
|
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
|
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
|
+
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:
|