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 +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
|
+
[](https://travis-ci.org/rranelli/git_multicast)
|
2
|
+
[](http://badge.fury.io/rb/git_multicast)
|
1
3
|
[](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:
|