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.
@@ -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