cartage 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 +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
|