teapot 1.3.1 → 2.0.0.pre.rc1

Sign up to get free protection for your applications and to get access to all the features.
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