teapot 1.3.1 → 2.0.0.pre.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +0 -1
  3. data/.travis.yml +6 -0
  4. data/Gemfile +1 -1
  5. data/bin/teapot +3 -3
  6. data/lib/teapot/command/build.rb +123 -0
  7. data/lib/teapot/{metadata.rb → command/clean.rb} +19 -20
  8. data/lib/teapot/command/create.rb +112 -0
  9. data/lib/teapot/command/fetch.rb +183 -0
  10. data/lib/teapot/command/list.rb +97 -0
  11. data/lib/teapot/command/status.rb +74 -0
  12. data/lib/teapot/command/visualize.rb +70 -0
  13. data/lib/teapot/command.rb +52 -132
  14. data/lib/teapot/context.rb +27 -11
  15. data/lib/teapot/loader.rb +6 -22
  16. data/lib/teapot/target.rb +2 -2
  17. data/lib/teapot/version.rb +1 -1
  18. data/spec/teapot/command_spec.rb +20 -12
  19. data/spec/teapot/context_spec.rb +13 -16
  20. data/teapot.gemspec +6 -4
  21. metadata +35 -45
  22. data/PLANNING.md +0 -20
  23. data/lib/teapot/controller/build.rb +0 -107
  24. data/lib/teapot/controller/clean.rb +0 -35
  25. data/lib/teapot/controller/create.rb +0 -73
  26. data/lib/teapot/controller/fetch.rb +0 -173
  27. data/lib/teapot/controller/generate.rb +0 -45
  28. data/lib/teapot/controller/list.rb +0 -82
  29. data/lib/teapot/controller/visualize.rb +0 -50
  30. data/lib/teapot/controller.rb +0 -81
  31. data/lib/teapot/dependency.rb +0 -25
  32. data/lib/teapot/generator.rb +0 -138
  33. data/lib/teapot/merge.rb +0 -142
  34. data/lib/teapot/repository.rb +0 -135
  35. data/lib/teapot/substitutions.rb +0 -258
  36. data/spec/teapot/generator_spec/teapot.rb +0 -54
  37. data/spec/teapot/generator_spec/template/$NAME.txt +0 -1
  38. data/spec/teapot/generator_spec.rb +0 -46
  39. data/spec/teapot/merge_spec.rb +0 -50
  40. data/spec/teapot/metadata_spec.rb +0 -31
  41. data/spec/teapot/substitutions_spec.rb +0 -65
  42. data/spec/teapot/wait_spec/teapot.rb +0 -41
  43. data/spec/teapot/wait_spec.rb +0 -53
@@ -1,82 +0,0 @@
1
- # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
3
- # Permission is hereby granted, free of charge, to any person obtaining a copy
4
- # of this software and associated documentation files (the "Software"), to deal
5
- # in the Software without restriction, including without limitation the rights
6
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- # copies of the Software, and to permit persons to whom the Software is
8
- # furnished to do so, subject to the following conditions:
9
- #
10
- # The above copyright notice and this permission notice shall be included in
11
- # all copies or substantial portions of the Software.
12
- #
13
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
- # THE SOFTWARE.
20
-
21
- require_relative '../controller'
22
-
23
- module Teapot
24
- class Controller
25
- def list(only = nil)
26
- # Should this somehow consider context.root_package?
27
- context.configuration.packages.each do |package|
28
- # The root package is the local package for this context:
29
- next unless only == nil or only.include?(package.name)
30
-
31
- log "Package #{package.name} (from #{package.path}):".bright
32
-
33
- begin
34
- definitions = context.load(package)
35
-
36
- definitions.each do |definition|
37
- log "\t#{definition}"
38
-
39
- definition.description.each_line do |line|
40
- log "\t\t#{line.chomp}".color(:cyan)
41
- end if definition.description
42
-
43
- case definition
44
- when Project
45
- log "\t\t- Summary: #{definition.summary}" if definition.summary
46
- log "\t\t- License: #{definition.license}" if definition.license
47
- log "\t\t- Website: #{definition.website}" if definition.website
48
- log "\t\t- Version: #{definition.version}" if definition.version
49
-
50
- definition.authors.each do |author|
51
- contact_text = [author.email, author.website].compact.collect{|text|" <#{text}>"}.join
52
- log "\t\t- Author: #{author.name}" + contact_text
53
- end
54
- when Target
55
- definition.dependencies.each do |dependency|
56
- log "\t\t- #{dependency}".color(:red)
57
- end
58
-
59
- definition.provisions.each do |name, provision|
60
- log "\t\t- #{provision}".color(:green)
61
- end
62
- when Configuration
63
- definition.materialize
64
-
65
- definition.packages.each do |package|
66
- log "\t\t- #{package}".color(:green)
67
- end
68
-
69
- definition.imports.select(&:explicit).each do |import|
70
- log "\t\t- unmaterialised import #{import.name}".color(:red)
71
- end
72
- end
73
- end
74
- rescue NonexistantTeapotError => error
75
- log "\t#{error.message}".color(:red)
76
- rescue IncompatibleTeapotError => error
77
- log "\t#{error.message}".color(:red)
78
- end
79
- end
80
- end
81
- end
82
- end
@@ -1,50 +0,0 @@
1
- # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
3
- # Permission is hereby granted, free of charge, to any person obtaining a copy
4
- # of this software and associated documentation files (the "Software"), to deal
5
- # in the Software without restriction, including without limitation the rights
6
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- # copies of the Software, and to permit persons to whom the Software is
8
- # furnished to do so, subject to the following conditions:
9
- #
10
- # The above copyright notice and this permission notice shall be included in
11
- # all copies or substantial portions of the Software.
12
- #
13
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
- # THE SOFTWARE.
20
-
21
- require_relative '../controller'
22
-
23
- require 'graphviz'
24
-
25
- module Teapot
26
- class Controller
27
- def visualize(dependency_names = [], output_path: nil, dependency_name: nil)
28
- configuration = context.configuration
29
-
30
- chain = context.dependency_chain(dependency_names, context.configuration)
31
-
32
- if dependency_name
33
- provider = context.dependencies[dependency_name]
34
-
35
- # TODO The visualisation generated isn't quite right. It's introspecting too much from the packages and not reflecting #ordered and #provisions.
36
- chain = chain.partial(provider)
37
- end
38
-
39
- visualization = Build::Dependency::Visualization.new
40
-
41
- graph = visualization.generate(chain)
42
-
43
- if output
44
- Graphviz::output(graph, :path => output)
45
- end
46
-
47
- return graph
48
- end
49
- end
50
- end
@@ -1,81 +0,0 @@
1
- # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
3
- # Permission is hereby granted, free of charge, to any person obtaining a copy
4
- # of this software and associated documentation files (the "Software"), to deal
5
- # in the Software without restriction, including without limitation the rights
6
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- # copies of the Software, and to permit persons to whom the Software is
8
- # furnished to do so, subject to the following conditions:
9
- #
10
- # The above copyright notice and this permission notice shall be included in
11
- # all copies or substantial portions of the Software.
12
- #
13
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
- # THE SOFTWARE.
20
-
21
- require_relative 'context'
22
- require_relative 'configuration'
23
- require_relative 'version'
24
-
25
- require 'uri'
26
- require 'rainbow'
27
- require 'rainbow/ext/string'
28
- require 'fileutils'
29
-
30
- require 'build/logger'
31
-
32
- module Teapot
33
- class Controller
34
- def initialize(root, options)
35
- @root = Pathname(root)
36
- @options = options
37
-
38
- @log_output = @options.fetch(:log, $stderr)
39
- @logging = @options[:logging]
40
- end
41
-
42
- def verbose?
43
- @logging == :verbose
44
- end
45
-
46
- def quiet?
47
- @logging == :quiet
48
- end
49
-
50
- def logger
51
- @logger ||= Logger.new(@log_output).tap do |logger|
52
- logger.formatter = Build::CompactFormatter.new(verbose: verbose?)
53
-
54
- if verbose?
55
- logger.level = Logger::DEBUG
56
- elsif quiet?
57
- logger.level = Logger::WARN
58
- else
59
- logger.level = Logger::INFO
60
- end
61
- end
62
- end
63
-
64
- def log(*args)
65
- logger.info(*args)
66
- end
67
-
68
- def configuration
69
- @options[:configuration]
70
- end
71
-
72
- def context
73
- @context ||= Context.new(@root, configuration: configuration)
74
- end
75
-
76
- # Reload the current context, e.g. if it's been modified by a generator.
77
- def reload!
78
- @context = nil
79
- end
80
- end
81
- end
@@ -1,25 +0,0 @@
1
- # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
3
- # Permission is hereby granted, free of charge, to any person obtaining a copy
4
- # of this software and associated documentation files (the "Software"), to deal
5
- # in the Software without restriction, including without limitation the rights
6
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- # copies of the Software, and to permit persons to whom the Software is
8
- # furnished to do so, subject to the following conditions:
9
- #
10
- # The above copyright notice and this permission notice shall be included in
11
- # all copies or substantial portions of the Software.
12
- #
13
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
- # THE SOFTWARE.
20
-
21
- require 'build/dependency'
22
-
23
- module Teapot
24
- Dependency = ::Build::Dependency
25
- end
@@ -1,138 +0,0 @@
1
- # Copyright, 2013, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
3
- # Permission is hereby granted, free of charge, to any person obtaining a copy
4
- # of this software and associated documentation files (the "Software"), to deal
5
- # in the Software without restriction, including without limitation the rights
6
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- # copies of the Software, and to permit persons to whom the Software is
8
- # furnished to do so, subject to the following conditions:
9
- #
10
- # The above copyright notice and this permission notice shall be included in
11
- # all copies or substantial portions of the Software.
12
- #
13
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
- # THE SOFTWARE.
20
-
21
- require_relative 'definition'
22
- require_relative 'substitutions'
23
- require_relative 'merge'
24
-
25
- require 'tempfile'
26
-
27
- module Teapot
28
- class GeneratorError < StandardError
29
- def initialize(message, generator = nil)
30
- super(message)
31
-
32
- @generator = generator
33
- end
34
-
35
- attr :generator
36
- end
37
-
38
- class Generator < Definition
39
- def initialize(context, package, name)
40
- super context, package, name
41
-
42
- @generate = nil
43
- end
44
-
45
- def generate(&block)
46
- @generate = Proc.new(&block)
47
- end
48
-
49
- def generate!(*args)
50
- @generate[*args]
51
- end
52
-
53
- def substitute(text, substitutions)
54
- return text unless substitutions
55
-
56
- if Hash === substitutions
57
- pattern = Regexp.new(substitutions.keys.map{|x| Regexp.escape(x)}.join('|'))
58
-
59
- text.gsub(pattern) {|key| substitutions[key]}
60
- else
61
- substitutions.call(text)
62
- end
63
- end
64
-
65
- def write(source, destination, substitutions = nil, mode = "a")
66
- source_path = Pathname(path) + source
67
- destination_path = Pathname(context.root) + destination
68
-
69
- destination_path.dirname.mkpath
70
-
71
- File.open(destination_path, mode) do |file|
72
- text = File.read(source_path)
73
-
74
- file.write substitute(text, substitutions)
75
- end
76
- end
77
-
78
- def append(source, destination, substitutions = nil)
79
- write(source, destination, substitutions, "a")
80
- end
81
-
82
- def merge(source, destination, substitutions = nil)
83
- source_path = Pathname(path) + source
84
- destination_path = Pathname(context.root) + destination
85
-
86
- if destination_path.exist?
87
- temporary_file = Tempfile.new(destination_path.basename.to_s)
88
-
89
- # This functionality works, but needs improvements.
90
- begin
91
- # Need to ask user what to do?
92
- write(source_path, temporary_file.path, substitutions, "w")
93
-
94
- result = Merge::combine(destination.readlines, temporary_file.readlines)
95
-
96
- destination.open("w") do |file|
97
- file.write result.join
98
- end
99
- ensure
100
- temporary_file.unlink
101
- end
102
- else
103
- write(source_path, destination_path, substitutions, "w")
104
- end
105
- end
106
-
107
- def is_binary(path)
108
- if path.exist?
109
- return path.read(1024).bytes.find{|byte| byte >= 0 and byte <= 6}
110
- else
111
- return false
112
- end
113
- end
114
-
115
- def copy_binary(source_path, destination_path)
116
- destination_path.dirname.create
117
- FileUtils.cp source_path, destination_path
118
- end
119
-
120
- def copy(source, destination, substitutions = nil)
121
- source_path = Pathname(path) + source
122
- destination_path = Pathname(context.root) + destination
123
-
124
- if source_path.directory?
125
- source_path.children(false).each do |child_path|
126
- copy(source_path + child_path, destination_path + substitute(child_path.to_s, substitutions), substitutions)
127
- end
128
- else
129
- if is_binary(source_path) or is_binary(destination)
130
- destination_path = Pathname(context.root) + destination
131
- copy_binary(source_path, destination_path)
132
- else
133
- merge(source_path, destination, substitutions)
134
- end
135
- end
136
- end
137
- end
138
- end
data/lib/teapot/merge.rb DELETED
@@ -1,142 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # Copyright, 2013, by Samuel G. D. Williams. <http://www.codeotaku.com>
3
- #
4
- # Permission is hereby granted, free of charge, to any person obtaining a copy
5
- # of this software and associated documentation files (the "Software"), to deal
6
- # in the Software without restriction, including without limitation the rights
7
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
- # copies of the Software, and to permit persons to whom the Software is
9
- # furnished to do so, subject to the following conditions:
10
- #
11
- # The above copyright notice and this permission notice shall be included in
12
- # all copies or substantial portions of the Software.
13
- #
14
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
- # THE SOFTWARE.
21
-
22
- module Teapot
23
- module Merge
24
- Difference = Struct.new(:type, :value)
25
-
26
- def self.combine(old_text, new_text)
27
- lcs = lcs(old_text, new_text)
28
- changes = []
29
-
30
- n = 0; o = 0; l = 0
31
- while o < old_text.size and n < new_text.size and l < lcs.size
32
- if !similar(old_text[o], lcs[l])
33
- changes << Difference.new(:old, old_text[o])
34
- o+=1
35
- elsif !similar(new_text[n], lcs[l])
36
- changes << Difference.new(:new, new_text[n])
37
- n+=1
38
- else
39
- changes << Difference.new(:both, lcs[l])
40
- o+=1; n+=1; l+=1
41
- end
42
- end
43
-
44
- changes.map do |change|
45
- change.value
46
- end
47
- end
48
-
49
- # This code is based directly on the Text gem implementation
50
- # Returns a value representing the "cost" of transforming str1 into str2
51
- def self.levenshtein_distance(s, t)
52
- n = s.length
53
- m = t.length
54
-
55
- return m if n == 0
56
- return n if m == 0
57
-
58
- d = (0..m).to_a
59
- x = nil
60
-
61
- n.times do |i|
62
- e = i+1
63
-
64
- m.times do |j|
65
- cost = (s[i] == t[j]) ? 0 : 1
66
- x = [
67
- d[j+1] + 1, # insertion
68
- e + 1, # deletion
69
- d[j] + cost # substitution
70
- ].min
71
- d[j] = e
72
- e = x
73
- end
74
-
75
- d[m] = x
76
- end
77
-
78
- return x
79
- end
80
-
81
- # Calculate the similarity of two sequences, return true if they are with factor% similarity.
82
- def self.similar(s, t, factor = 0.15)
83
- return true if s == t
84
-
85
- distance = levenshtein_distance(s, t)
86
- average_length = (s.length + t.length) / 2.0
87
-
88
- proximity = (distance.to_f / average_length)
89
-
90
- return proximity <= factor
91
- end
92
-
93
- LCSNode = Struct.new(:value, :previous)
94
-
95
- # Find the Longest Common Subsequence in the given sequences x, y.
96
- def self.lcs(x, y)
97
- # Create the lcs matrix:
98
- m = Array.new(x.length + 1) do
99
- Array.new(y.length + 1) do
100
- LCSNode.new(nil, nil)
101
- end
102
- end
103
-
104
- # LCS(i, 0) and LCS(0, j) are always 0:
105
- for i in 0..x.length do m[i][0].value = 0 end
106
- for j in 0..y.length do m[0][j].value = 0 end
107
-
108
- # Main algorithm, solve row by row:
109
- for i in 1..x.length do
110
- for j in 1..y.length do
111
- if similar(x[i-1], y[j-1])
112
- # Value is based on maximizing the length of the matched strings:
113
- m[i][j].value = m[i-1][j-1].value + (x[i-1].chomp.length + y[j-1].chomp.length) / 2.0
114
- m[i][j].previous = [-1, -1]
115
- else
116
- if m[i-1][j].value >= m[i][j-1].value
117
- m[i][j].value = m[i-1][j].value
118
- m[i][j].previous = [-1, 0]
119
- else
120
- m[i][j].value = m[i][j-1].value
121
- m[i][j].previous = [0, -1]
122
- end
123
- end
124
- end
125
- end
126
-
127
- # Get the solution by following the path backwards from m[x.length][y.length]
128
- lcs = []
129
-
130
- i = x.length; j = y.length
131
- until m[i][j].previous == nil do
132
- if m[i][j].previous == [-1, -1]
133
- lcs << x[i-1]
134
- end
135
-
136
- i, j = i + m[i][j].previous[0], j + m[i][j].previous[1]
137
- end
138
-
139
- return lcs.reverse!
140
- end
141
- end
142
- end
@@ -1,135 +0,0 @@
1
- # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
3
- # Permission is hereby granted, free of charge, to any person obtaining a copy
4
- # of this software and associated documentation files (the "Software"), to deal
5
- # in the Software without restriction, including without limitation the rights
6
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- # copies of the Software, and to permit persons to whom the Software is
8
- # furnished to do so, subject to the following conditions:
9
- #
10
- # The above copyright notice and this permission notice shall be included in
11
- # all copies or substantial portions of the Software.
12
- #
13
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
- # THE SOFTWARE.
20
-
21
- module Teapot
22
- module Git
23
- # This module needs to be refactored. Perhaps use Process::Group.
24
- module Commands
25
- class CommandError < StandardError
26
- end
27
-
28
- def self.run(*args, &block)
29
- options = Hash === args.last ? args.pop : {}
30
-
31
- args = args.flatten.collect(&:to_s)
32
-
33
- puts args.join(' ').color(:blue) + " in #{options[:chdir] || Dir.getwd}"
34
-
35
- pid = Process.spawn(*args, options, &block)
36
- _, result = Process.wait2(pid)
37
-
38
- if result.exitstatus == 0
39
- true
40
- else
41
- raise CommandError.new("Non-zero exit status: #{result} while running #{args.join(' ')}!")
42
- end
43
- end
44
-
45
- def self.run!(*args, &block)
46
- run(*args, &block)
47
- rescue CommandError
48
- false
49
- end
50
- end
51
-
52
- class Repository
53
- def initialize(root, options = {})
54
- @root = root
55
- @options = options
56
- end
57
-
58
- def init!
59
- run!("init", @root)
60
- end
61
-
62
- def clone!(remote_url, branch = nil, commit = nil)
63
- branch_args = branch ? ["--branch", branch] : []
64
-
65
- @root.create
66
-
67
- run!("clone", remote_url, @root, *branch_args)
68
-
69
- if commit
70
- run("reset", "--hard", commit)
71
- end
72
-
73
- run("submodule", "update", "--init", "--recursive")
74
- rescue
75
- #@root.rmtree
76
-
77
- raise
78
- end
79
-
80
- def update(branch, commit = nil)
81
- run("fetch", "origin")
82
- run("checkout", branch)
83
-
84
- # Pull any changes, if you might get the error from above:
85
- # Your branch is behind 'origin/0.1' by 1 commit, and can be fast-forwarded.
86
- run("pull")
87
-
88
- # Checkout the specific version if it was given:
89
- if commit
90
- run("reset", "--hard", commit)
91
- end
92
-
93
- run("submodule", "update", "--init", "--recursive")
94
- end
95
-
96
- def add(files)
97
- if files == :all
98
- run("add", "--all")
99
- else
100
- run("add", *files)
101
- end
102
- end
103
-
104
- def commit(message)
105
- run("commit", "-m", message)
106
- end
107
-
108
- def status
109
- input, output = IO.pipe
110
-
111
- Commands.run("git", "status", "--porcelain", :out => output)
112
-
113
- output.close
114
-
115
- return input.readlines.collect{|line| line.chomp.split(/\s+/, 2)}
116
- end
117
-
118
- private
119
-
120
- def run(*args)
121
- Commands.run("git", *args, :chdir => @root)
122
- end
123
-
124
- def run!(*args)
125
- Commands.run("git", *args)
126
- end
127
- end
128
- end
129
-
130
- module Repository
131
- def self.new(*args)
132
- Git::Repository.new(*args)
133
- end
134
- end
135
- end