cartage 1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.autotest +8 -0
- data/.gemtest +1 -0
- data/.minitest.rb +2 -0
- data/.travis.yml +36 -0
- data/Cartage.yml.rdoc +271 -0
- data/Contributing.rdoc +66 -0
- data/Gemfile +9 -0
- data/History.rdoc +5 -0
- data/Licence.rdoc +27 -0
- data/Manifest.txt +24 -0
- data/README.rdoc +192 -0
- data/Rakefile +62 -0
- data/bin/cartage +8 -0
- data/cartage.yml.sample +172 -0
- data/lib/cartage.rb +496 -0
- data/lib/cartage/command.rb +59 -0
- data/lib/cartage/config.rb +193 -0
- data/lib/cartage/manifest.rb +218 -0
- data/lib/cartage/manifest/commands.rb +106 -0
- data/lib/cartage/pack_command.rb +14 -0
- data/lib/cartage/plugin.rb +80 -0
- data/test/minitest_config.rb +72 -0
- data/test/test_cartage.rb +261 -0
- data/test/test_cartage_config.rb +47 -0
- metadata +348 -0
@@ -0,0 +1,106 @@
|
|
1
|
+
class Cartage
|
2
|
+
class Manifest
|
3
|
+
# This module provides a command the ability to properly handle the
|
4
|
+
# Cartage::Manifest::MissingError.
|
5
|
+
module HandleMissingManifest #:nodoc:
|
6
|
+
def handle_missing_manifest
|
7
|
+
yield
|
8
|
+
rescue Cartage::Manifest::MissingError => e
|
9
|
+
$stderr.puts e.message
|
10
|
+
command = super_command.commands['manifest'].usage.
|
11
|
+
gsub(/^\s*Usage:\s+/, ' ')
|
12
|
+
$stderr.puts command
|
13
|
+
false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Implement <tt>cartage manifest</tt> to generate Manifest.txt.
|
18
|
+
class ManifestCommand < Cartage::Command #:nodoc:
|
19
|
+
def initialize(cartage)
|
20
|
+
super(cartage, 'manifest')
|
21
|
+
takes_commands(false)
|
22
|
+
short_desc('Update the Cartage Manifest.txt file.')
|
23
|
+
|
24
|
+
@manifest = cartage.manifest
|
25
|
+
end
|
26
|
+
|
27
|
+
def perform(*)
|
28
|
+
@manifest.generate
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Implement <tt>cartage check</tt> to compare the repository against
|
33
|
+
# Manifest.txt.
|
34
|
+
class CheckCommand < Cartage::Command #:nodoc:
|
35
|
+
include HandleMissingManifest
|
36
|
+
|
37
|
+
def initialize(cartage)
|
38
|
+
super(cartage, 'check')
|
39
|
+
takes_commands(false)
|
40
|
+
short_desc('Check the Manifest.txt against the current repository.')
|
41
|
+
|
42
|
+
@manifest = cartage.manifest
|
43
|
+
end
|
44
|
+
|
45
|
+
def perform(*)
|
46
|
+
handle_missing_manifest do
|
47
|
+
@manifest.check
|
48
|
+
raise Cartage::QuietError.new($?.exitstatus) unless $?.success?
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class InstallDefaultIgnoreCommand < Cartage::Command #:nodoc:
|
54
|
+
def initialize(cartage)
|
55
|
+
super(cartage, 'install-ignore')
|
56
|
+
takes_commands(false)
|
57
|
+
short_desc('Installs the default ignore file.')
|
58
|
+
|
59
|
+
self.options do |opts|
|
60
|
+
opts.on('-f', '--force', 'Force write .cartignore.') {
|
61
|
+
@mode = :force
|
62
|
+
}
|
63
|
+
opts.on('-m', '--merge', 'Merge .cartignore with the default.') {
|
64
|
+
@mode = :merge
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
@manifest = cartage.manifest
|
69
|
+
@mode = nil
|
70
|
+
end
|
71
|
+
|
72
|
+
def perform(*)
|
73
|
+
@manifest.install_default_ignore(mode: @mode)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class ShowCommand < Cartage::Command #:nodoc:
|
78
|
+
include HandleMissingManifest
|
79
|
+
|
80
|
+
def initialize(cartage)
|
81
|
+
super(cartage, 'show')
|
82
|
+
takes_commands(false)
|
83
|
+
short_desc('Show the files that will be included in the package.')
|
84
|
+
|
85
|
+
@manifest = cartage.manifest
|
86
|
+
end
|
87
|
+
|
88
|
+
def perform(*)
|
89
|
+
handle_missing_manifest do
|
90
|
+
@manifest.resolve do |tmpfile|
|
91
|
+
puts Pathname(tmpfile).read
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.commands #:nodoc:
|
98
|
+
[
|
99
|
+
ManifestCommand,
|
100
|
+
CheckCommand,
|
101
|
+
InstallDefaultIgnoreCommand,
|
102
|
+
ShowCommand
|
103
|
+
]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class Cartage
|
2
|
+
class PackCommand < Command #:nodoc:
|
3
|
+
def initialize(cartage)
|
4
|
+
super(cartage, 'pack', takes_commands: false)
|
5
|
+
|
6
|
+
Cartage.common_build_options(options, cartage)
|
7
|
+
short_desc('Create a package with Cartage based on the Manifest.')
|
8
|
+
end
|
9
|
+
|
10
|
+
def perform
|
11
|
+
@cartage.pack
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
class Cartage
|
2
|
+
# Cartage::Plugin is the basis of the Cartage plugin system. All plug-ins
|
3
|
+
# must inherit from this class.
|
4
|
+
class Plugin
|
5
|
+
class << self
|
6
|
+
# Register a subclass.
|
7
|
+
def inherited(klass) #:nodoc:
|
8
|
+
registered << klass
|
9
|
+
end
|
10
|
+
|
11
|
+
# The plugins registered with Cartage.
|
12
|
+
def registered
|
13
|
+
@registered ||= []
|
14
|
+
end
|
15
|
+
|
16
|
+
# A utility method that will find all Cartage plugins and load them. A
|
17
|
+
# Cartage plugin is found with 'cartage/*.rb' and descends from
|
18
|
+
# Cartage::Plugin.
|
19
|
+
def load #:nodoc:
|
20
|
+
@found ||= {}
|
21
|
+
@loaded ||= {}
|
22
|
+
@files ||= Gem.find_files('cartage/*.rb')
|
23
|
+
|
24
|
+
@files.reverse.each do |path|
|
25
|
+
name = File.basename(path, '.rb').to_sym
|
26
|
+
@found[name] = path
|
27
|
+
end
|
28
|
+
|
29
|
+
:repeat while @found.map { |name, plugin|
|
30
|
+
load_plugin(name, plugin)
|
31
|
+
}.any?
|
32
|
+
end
|
33
|
+
|
34
|
+
# Decorate the provided class with lazy initialization methods.
|
35
|
+
def decorate(klass) #:nodoc:
|
36
|
+
registered.each do |plugin|
|
37
|
+
name = plugin.name.split(/::/).last.gsub(/([A-Z])/, '_\1').downcase.
|
38
|
+
sub(/^_/, '')
|
39
|
+
ivar = "@#{name}"
|
40
|
+
|
41
|
+
klass.send(:define_method, name) do
|
42
|
+
instance = instance_variable_defined?(ivar) &&
|
43
|
+
instance_variable_get(ivar)
|
44
|
+
|
45
|
+
instance ||= instance_variable_set(ivar, plugin.new(self))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def load_plugin(name, plugin)
|
53
|
+
return if @loaded[name]
|
54
|
+
|
55
|
+
warn "loading #{plugin}" if $DEBUG
|
56
|
+
@loaded[name] = require plugin
|
57
|
+
rescue LoadError => e
|
58
|
+
warn "error loading #{plugin.inspect}: #{e.message}. Skipping..."
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# These are the command classes provided to the cartage binary. Despite the
|
63
|
+
# name being plural, the return can either be a single CmdParse::Command
|
64
|
+
# class, or an array of CmdParse::Command class. These command classes
|
65
|
+
# should inherit from Cartage::Command, since they will be initialized with
|
66
|
+
# a cartage parameter.
|
67
|
+
def self.commands
|
68
|
+
[]
|
69
|
+
end
|
70
|
+
|
71
|
+
# A plug-in is initialized with the Cartage instance that owns it.
|
72
|
+
def initialize(cartage)
|
73
|
+
@cartage = cartage
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
def resolve_config!(*)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# -*- ruby encoding: utf-8 -*-
|
2
|
+
|
3
|
+
gem 'minitest'
|
4
|
+
require 'minitest/autorun'
|
5
|
+
require 'minitest/pretty_diff'
|
6
|
+
require 'minitest/focus'
|
7
|
+
require 'minitest/moar'
|
8
|
+
require 'minitest/bisect'
|
9
|
+
|
10
|
+
require 'cartage'
|
11
|
+
|
12
|
+
module Minitest::ENVStub
|
13
|
+
def stub_env env, options = {}, *block_args, &block
|
14
|
+
mock = lambda { |key|
|
15
|
+
env.fetch(key) { |k|
|
16
|
+
if options[:passthrough]
|
17
|
+
ENV.send(:"__minitest_stub__[]", k)
|
18
|
+
else
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
if defined? Minitest::Moar::Stubbing
|
25
|
+
stub ENV, :[], mock, *block_args, &block
|
26
|
+
else
|
27
|
+
ENV.stub :[], mock, *block_args, &block
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def stub_cartage_repo_url value = nil, &block
|
32
|
+
instance_stub Cartage, :repo_url,
|
33
|
+
-> { value || 'git://host/repo-url.git' }, &block
|
34
|
+
end
|
35
|
+
|
36
|
+
def stub_backticks value
|
37
|
+
Kernel.send(:alias_method, :__stub_backticks__, :`)
|
38
|
+
Kernel.send(:define_method, :`) { |*| value }
|
39
|
+
yield
|
40
|
+
ensure
|
41
|
+
Kernel.send(:undef_method, :`)
|
42
|
+
Kernel.send(:alias_method, :`, :__stub_backticks__)
|
43
|
+
Kernel.send(:undef_method, :__stub_backticks__)
|
44
|
+
end
|
45
|
+
|
46
|
+
def stub_file_open_for_write expected, string_io = StringIO.new
|
47
|
+
mfile = File.singleton_class
|
48
|
+
mfile.send(:alias_method, :__stub_open__, :open)
|
49
|
+
|
50
|
+
fopen = ->(*, &block) {
|
51
|
+
if block
|
52
|
+
block.call(string_io)
|
53
|
+
else
|
54
|
+
string_io
|
55
|
+
end
|
56
|
+
}
|
57
|
+
|
58
|
+
mfile.send(:define_method, :open, &fopen)
|
59
|
+
yield
|
60
|
+
assert_equal expected, string_io.string
|
61
|
+
ensure
|
62
|
+
mfile.send(:undef_method, :open)
|
63
|
+
mfile.send(:alias_method, :open, :__stub_open__)
|
64
|
+
mfile.send(:undef_method, :__stub_open__)
|
65
|
+
end
|
66
|
+
|
67
|
+
def stub_pathname_expand_path value, &block
|
68
|
+
instance_stub Pathname, :expand_path, Pathname(value), &block
|
69
|
+
end
|
70
|
+
|
71
|
+
Minitest::Test.send(:include, self)
|
72
|
+
end
|
@@ -0,0 +1,261 @@
|
|
1
|
+
require 'minitest_config'
|
2
|
+
|
3
|
+
describe Cartage do
|
4
|
+
before do
|
5
|
+
@cartage = Cartage.new
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '#name' do
|
9
|
+
it 'defaults to #default_name' do
|
10
|
+
stub_cartage_repo_url do
|
11
|
+
assert_equal 'repo-url', @cartage.name
|
12
|
+
assert_equal @cartage.default_name, @cartage.name
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'can be overridden' do
|
17
|
+
stub_cartage_repo_url do
|
18
|
+
@cartage.name = 'xyz'
|
19
|
+
assert_equal 'xyz', @cartage.name
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#root_path' do
|
25
|
+
it 'defaults to #default_root_path' do
|
26
|
+
stub_backticks '/x/y/z' do
|
27
|
+
assert_equal Pathname('/x/y/z'), @cartage.root_path
|
28
|
+
assert_equal @cartage.default_root_path, @cartage.root_path
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'can be overridden and creates a Pathname' do
|
33
|
+
stub_cartage_repo_url do
|
34
|
+
@cartage.root_path = '/a/b/c'
|
35
|
+
assert_equal Pathname('/a/b/c'), @cartage.root_path
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'expands the created Pathname' do
|
40
|
+
stub_cartage_repo_url do
|
41
|
+
@cartage.root_path = '/a/b/../c'
|
42
|
+
assert_equal Pathname('/a/c'), @cartage.root_path
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#timestamp' do
|
48
|
+
it 'defaults to #now.utc.strftime("%Y%m%d%H%M%S")' do
|
49
|
+
stub Time, :now, -> { Time.new(2014, 12, 31, 23, 59, 59, "+00:00") } do
|
50
|
+
assert_equal '20141231235959', @cartage.timestamp
|
51
|
+
assert_equal @cartage.default_timestamp, @cartage.timestamp
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'does no validation of assigned values' do
|
56
|
+
@cartage.timestamp = 'not a timestamp'
|
57
|
+
assert_equal 'not a timestamp', @cartage.timestamp
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe '#without_groups' do
|
62
|
+
it 'defaults to %w(development test)' do
|
63
|
+
assert_equal %w(development test), @cartage.without_groups
|
64
|
+
assert_equal @cartage.default_without_groups, @cartage.without_groups
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'wraps arrays correctly' do
|
68
|
+
@cartage.without_groups = 'dit'
|
69
|
+
assert_equal %w(dit), @cartage.without_groups
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe '#base_config' do
|
74
|
+
it 'defaults to an empty OpenStruct' do
|
75
|
+
assert_equal OpenStruct.new, @cartage.base_config
|
76
|
+
assert_equal @cartage.default_base_config, @cartage.base_config
|
77
|
+
refute_same @cartage.default_base_config, @cartage.base_config
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe '#config' do
|
82
|
+
before do
|
83
|
+
data = {
|
84
|
+
x: 1,
|
85
|
+
plugins: {
|
86
|
+
test: {
|
87
|
+
x: 2
|
88
|
+
}
|
89
|
+
},
|
90
|
+
development: {
|
91
|
+
x: 3,
|
92
|
+
plugins: {
|
93
|
+
test: {
|
94
|
+
x: 4
|
95
|
+
}
|
96
|
+
}
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
@config = Cartage::Config.new(Cartage::Config.send(:ostructify, data))
|
101
|
+
|
102
|
+
@cartage.environment = :development
|
103
|
+
@cartage.base_config = @config
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'knows the subset for "development"' do
|
107
|
+
assert_equal @config.development, @cartage.config
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'knows the subset for the "test" plugin in "development"' do
|
111
|
+
assert_equal @config.development.plugins.test,
|
112
|
+
@cartage.config(for_plugin: 'test')
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'knows the subset "test" plugin without environment' do
|
116
|
+
assert_equal @config.plugins.test,
|
117
|
+
@cartage.config(with_environment: false, for_plugin: :test)
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'knows the whole config without environment' do
|
121
|
+
assert_equal @config, @cartage.config(with_environment: false)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe '#bundle_cache' do
|
126
|
+
def assert_bundle_cache_equal(path)
|
127
|
+
path = Pathname(path).join('vendor-bundle.tar.bz2')
|
128
|
+
expected = Pathname(".").join(path)
|
129
|
+
|
130
|
+
instance_stub Pathname, :expand_path, expected do
|
131
|
+
yield if block_given?
|
132
|
+
assert_equal expected, @cartage.bundle_cache
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'defaults to "tmp/vendor-bundle.tar.bz2"' do
|
137
|
+
assert_bundle_cache_equal 'tmp/vendor-bundle.tar.bz2'
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'can be overridden' do
|
141
|
+
assert_bundle_cache_equal 'tmp/vendor-bundle.tar.bz2'
|
142
|
+
assert_bundle_cache_equal 'tmpx/vendor-bundle.tar.bz2' do
|
143
|
+
@cartage.bundle_cache 'tmpx'
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe '#release_hashref' do
|
149
|
+
it 'returns the current hashref' do
|
150
|
+
stub_backticks '12345' do
|
151
|
+
assert_equal '12345', @cartage.release_hashref
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'saves the hashref to a file with save_to:' do
|
156
|
+
stub_backticks '54321' do
|
157
|
+
stub_file_open_for_write '54321' do
|
158
|
+
@cartage.release_hashref(save_to: 'x')
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe '#repo_url' do
|
165
|
+
it 'returns the origin Fetch URL' do
|
166
|
+
origin = <<-origin
|
167
|
+
* remote origin
|
168
|
+
Fetch URL: git@github.com:KineticCafe/cartage.git
|
169
|
+
Push URL: git@github.com:KineticCafe/cartage.git
|
170
|
+
HEAD branch: (not queried)
|
171
|
+
Remote branch: (status not queried)
|
172
|
+
master
|
173
|
+
Local branch configured for 'git pull':
|
174
|
+
master rebases onto remote master
|
175
|
+
Local ref configured for 'git push' (status not queried):
|
176
|
+
(matching) pushes to (matching)
|
177
|
+
origin
|
178
|
+
stub_backticks origin do
|
179
|
+
assert_equal 'git@github.com:KineticCafe/cartage.git',
|
180
|
+
@cartage.repo_url
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
describe '#final_tarball, #final_release_hashref' do
|
186
|
+
before do
|
187
|
+
@cartage.name = 'test'
|
188
|
+
@cartage.timestamp = 'value'
|
189
|
+
@cartage.instance_variable_set(:@root_path, Pathname('source'))
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'sets the final tarball name correctly' do
|
193
|
+
assert_equal Pathname('source/tmp/test-value.tar.bz2'),
|
194
|
+
@cartage.final_tarball
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'sets the final release hashref name correctly' do
|
198
|
+
assert_equal Pathname('source/tmp/test-value-release-hashref.txt'),
|
199
|
+
@cartage.final_release_hashref
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
describe '#display' do
|
204
|
+
it 'does not display a message if not verbose' do
|
205
|
+
@cartage.verbose = false
|
206
|
+
assert_silent do
|
207
|
+
@cartage.display 'hello'
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'displays a message if verbose' do
|
212
|
+
@cartage.verbose = true
|
213
|
+
assert_output "hello\n" do
|
214
|
+
@cartage.display 'hello'
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'does not display a message if verbose and quiet' do
|
219
|
+
@cartage.verbose = true
|
220
|
+
@cartage.quiet = true
|
221
|
+
assert_silent do
|
222
|
+
@cartage.display 'hello'
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
describe '#run (private)' do
|
228
|
+
before do
|
229
|
+
@command = %w(echo yes)
|
230
|
+
end
|
231
|
+
|
232
|
+
it 'displays the output if not verbose and not quiet' do
|
233
|
+
assert_output "yes\n" do
|
234
|
+
@cartage.send(:run, @command)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'displays the command and output if verbose and not quiet' do
|
239
|
+
@cartage.verbose = true
|
240
|
+
assert_output "echo yes\nyes\n" do
|
241
|
+
@cartage.send(:run, @command)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
it 'displays nothing if quiet' do
|
246
|
+
@cartage.verbose = true
|
247
|
+
@cartage.quiet = true
|
248
|
+
assert_silent do
|
249
|
+
@cartage.send(:run, @command)
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
it 'raises an exception on error' do
|
254
|
+
@cartage.quiet = true
|
255
|
+
ex = assert_raises StandardError do
|
256
|
+
@cartage.send(:run, %w(false))
|
257
|
+
end
|
258
|
+
assert_equal %Q(Error running 'false'), ex.message
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|