cartage 2.0 → 2.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/Contributing.md +3 -3
- data/History.md +105 -66
- data/Manifest.txt +1 -0
- data/README.rdoc +15 -5
- data/Rakefile +52 -37
- data/lib/cartage/backport.rb +3 -3
- data/lib/cartage/cli.rb +76 -76
- data/lib/cartage/commands/echo.rb +6 -6
- data/lib/cartage/commands/info.rb +17 -17
- data/lib/cartage/commands/manifest.rb +46 -46
- data/lib/cartage/commands/metadata.rb +10 -0
- data/lib/cartage/commands/pack.rb +6 -6
- data/lib/cartage/config.rb +27 -25
- data/lib/cartage/core.rb +18 -18
- data/lib/cartage/gli_ext.rb +10 -10
- data/lib/cartage/minitest.rb +7 -7
- data/lib/cartage/plugin.rb +14 -10
- data/lib/cartage/plugins/build_tarball.rb +2 -2
- data/lib/cartage/plugins/manifest.rb +85 -85
- data/lib/cartage.rb +138 -67
- data/test/minitest_config.rb +8 -8
- data/test/test_cartage.rb +130 -130
- data/test/test_cartage_build_tarball.rb +22 -22
- data/test/test_cartage_config.rb +27 -27
- data/test/test_cartage_core.rb +36 -36
- data/test/test_cartage_manifest.rb +51 -53
- data/test/test_cartage_plugin.rb +21 -21
- metadata +52 -26
data/lib/cartage.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "pathname"
|
4
|
+
require "json"
|
5
5
|
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
6
|
+
require "cartage/core"
|
7
|
+
require "cartage/plugin"
|
8
|
+
require "cartage/config"
|
9
9
|
|
10
10
|
##
|
11
11
|
# Cartage, a reliable package builder.
|
12
12
|
class Cartage
|
13
|
-
VERSION =
|
13
|
+
VERSION = "2.2.1" # :nodoc:
|
14
14
|
|
15
15
|
# Creates a new Cartage instance. If provided a Cartage::Config object in
|
16
16
|
# +config+, sets the configuration and resolves it. If +config+ is not
|
@@ -30,7 +30,7 @@ class Cartage
|
|
30
30
|
# The default name of the package to be created, derived from the
|
31
31
|
# repository's Git URL.
|
32
32
|
|
33
|
-
attr_accessor_with_default :name, default: -> { File.basename(repo_url,
|
33
|
+
attr_accessor_with_default :name, default: -> { File.basename(repo_url, ".git") }
|
34
34
|
|
35
35
|
##
|
36
36
|
# :attr_accessor: root_path
|
@@ -44,11 +44,11 @@ class Cartage
|
|
44
44
|
# repository.
|
45
45
|
|
46
46
|
attr_reader_with_default :root_path do
|
47
|
-
Pathname(
|
47
|
+
Pathname(`git rev-parse --show-cdup`.chomp).expand_path
|
48
48
|
end
|
49
49
|
|
50
50
|
##
|
51
|
-
def root_path=(v)
|
51
|
+
def root_path=(v) # :nodoc:
|
52
52
|
reset_computed_values
|
53
53
|
@root_path = Pathname(v).expand_path
|
54
54
|
end
|
@@ -65,7 +65,7 @@ class Cartage
|
|
65
65
|
|
66
66
|
attr_accessor_with_default :target,
|
67
67
|
transform: ->(v) { Pathname(v) },
|
68
|
-
default: -> { Pathname(
|
68
|
+
default: -> { Pathname("tmp") }
|
69
69
|
|
70
70
|
##
|
71
71
|
# :attr_accessor: timestamp
|
@@ -78,7 +78,7 @@ class Cartage
|
|
78
78
|
# The default timestamp.
|
79
79
|
|
80
80
|
attr_accessor_with_default :timestamp, default: -> {
|
81
|
-
Time.now.utc.strftime(
|
81
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
82
82
|
}
|
83
83
|
|
84
84
|
##
|
@@ -97,9 +97,9 @@ class Cartage
|
|
97
97
|
end
|
98
98
|
|
99
99
|
##
|
100
|
-
def compression=(value)
|
100
|
+
def compression=(value) # :nodoc:
|
101
101
|
case value
|
102
|
-
when :bzip2, :none, :gzip,
|
102
|
+
when :bzip2, :none, :gzip, "bzip2", "none", "gzip"
|
103
103
|
@compression = value
|
104
104
|
reset_computed_values
|
105
105
|
else
|
@@ -142,10 +142,10 @@ class Cartage
|
|
142
142
|
end
|
143
143
|
|
144
144
|
##
|
145
|
-
def dependency_cache_path=(path)
|
145
|
+
def dependency_cache_path=(path) # :nodoc:
|
146
146
|
@dependency_cache_path = Pathname(path || tmp_path).expand_path
|
147
|
-
@dependency_cache = @dependency_cache_path
|
148
|
-
join("dependency-cache.tar#{tar_compression_extension}")
|
147
|
+
@dependency_cache = @dependency_cache_path
|
148
|
+
.join("dependency-cache.tar#{tar_compression_extension}")
|
149
149
|
end
|
150
150
|
|
151
151
|
# Commands that normally output data will have that output suppressed.
|
@@ -159,7 +159,7 @@ class Cartage
|
|
159
159
|
# by providing the +for_plugin+ or +for_command+ parameters.
|
160
160
|
def config(for_plugin: nil, for_command: nil)
|
161
161
|
if for_plugin && for_command
|
162
|
-
fail ArgumentError,
|
162
|
+
fail ArgumentError, "Cannot get config for plug-in and command together"
|
163
163
|
elsif for_plugin
|
164
164
|
@config.dig(:plugins, for_plugin.to_sym) || OpenStruct.new
|
165
165
|
elsif for_command
|
@@ -171,7 +171,7 @@ class Cartage
|
|
171
171
|
|
172
172
|
# The config file. This should not be used by clients.
|
173
173
|
def config=(cfg) # :nodoc:
|
174
|
-
fail ArgumentError,
|
174
|
+
fail ArgumentError, "No config provided" unless cfg
|
175
175
|
@plugins = Plugins.new
|
176
176
|
@config = cfg
|
177
177
|
resolve_config!
|
@@ -183,7 +183,7 @@ class Cartage
|
|
183
183
|
package: {
|
184
184
|
name: name,
|
185
185
|
repo: {
|
186
|
-
type:
|
186
|
+
type: "git", # Hardcoded until we have other support
|
187
187
|
url: repo_url
|
188
188
|
},
|
189
189
|
hashref: release_hashref,
|
@@ -194,21 +194,21 @@ class Cartage
|
|
194
194
|
|
195
195
|
# Return the release hashref.
|
196
196
|
def release_hashref
|
197
|
-
@release_hashref ||=
|
197
|
+
@release_hashref ||= `git rev-parse HEAD`.chomp
|
198
198
|
end
|
199
199
|
|
200
200
|
# The repository URL.
|
201
201
|
def repo_url
|
202
202
|
unless defined? @repo_url
|
203
|
-
@repo_url =
|
204
|
-
match(/\n\s+Fetch URL: (?<fetch>[^\n]+)/)[:fetch]
|
203
|
+
@repo_url = `git remote show -n origin`
|
204
|
+
.match(/\n\s+Fetch URL: (?<fetch>[^\n]+)/)[:fetch]
|
205
205
|
end
|
206
206
|
@repo_url
|
207
207
|
end
|
208
208
|
|
209
209
|
# The temporary path.
|
210
210
|
def tmp_path
|
211
|
-
@tmp_path ||= root_path.join(
|
211
|
+
@tmp_path ||= root_path.join("tmp")
|
212
212
|
end
|
213
213
|
|
214
214
|
# The working path for the job, in #tmp_path.
|
@@ -236,13 +236,13 @@ class Cartage
|
|
236
236
|
# A utility method for Cartage plug-ins to run a +command+ in the shell. Uses
|
237
237
|
# IO.popen.
|
238
238
|
def run(command)
|
239
|
-
display command.join(
|
239
|
+
display command.join(" ")
|
240
240
|
|
241
|
-
IO.popen(command + [
|
241
|
+
IO.popen(command + [err: %i[child out]]) do |io|
|
242
242
|
__display(io.read(128), partial: true, verbose: true) until io.eof?
|
243
243
|
end
|
244
244
|
|
245
|
-
fail StandardError, "Error running '#{command.join(
|
245
|
+
fail StandardError, "Error running '#{command.join(" ")}'" unless $?.success?
|
246
246
|
end
|
247
247
|
|
248
248
|
# Returns the registered plug-ins, once configuration has been resolved.
|
@@ -274,38 +274,108 @@ class Cartage
|
|
274
274
|
request_build_package
|
275
275
|
end
|
276
276
|
|
277
|
+
# Just save the release metadata.
|
278
|
+
def save_release_metadata(local: false)
|
279
|
+
display "Saving release metadata..."
|
280
|
+
json = JSON.generate(release_metadata)
|
281
|
+
|
282
|
+
if local
|
283
|
+
Pathname(".").join("release-metadata.json").write(json)
|
284
|
+
else
|
285
|
+
work_path.join("release-metadata.json").write(json)
|
286
|
+
final_release_metadata_json.write(json)
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
277
290
|
# Returns the flag to use with +tar+ given the value of +compression+.
|
278
291
|
def tar_compression_flag
|
279
292
|
case compression
|
280
|
-
when :bzip2,
|
281
|
-
|
282
|
-
when :gzip,
|
283
|
-
|
284
|
-
when :none,
|
285
|
-
|
293
|
+
when :bzip2, "bzip2", nil
|
294
|
+
"j"
|
295
|
+
when :gzip, "gzip"
|
296
|
+
"z"
|
297
|
+
when :none, "none"
|
298
|
+
""
|
286
299
|
end
|
287
300
|
end
|
288
301
|
|
289
302
|
# Returns the extension to use with +tar+ given the value of +compression+.
|
290
303
|
def tar_compression_extension
|
291
304
|
case compression
|
292
|
-
when :bzip2,
|
293
|
-
|
294
|
-
when :gzip,
|
295
|
-
|
296
|
-
when :none,
|
297
|
-
|
305
|
+
when :bzip2, "bzip2", nil
|
306
|
+
".bz2"
|
307
|
+
when :gzip, "gzip"
|
308
|
+
".gz"
|
309
|
+
when :none, "none"
|
310
|
+
""
|
298
311
|
end
|
299
312
|
end
|
300
313
|
|
314
|
+
# Recursively copy a provided +path+ to the #work_path, using a tar pipeline.
|
315
|
+
# The target location can be amended by the use of the +to+ parameter as a
|
316
|
+
# relative path to #work_path.
|
317
|
+
#
|
318
|
+
# If a relative +path+ is provided, it will be treated as relative to
|
319
|
+
# #root_path, and it will be used unmodified for writing to the target
|
320
|
+
# location. If an absolute path is provided, only the last part of the path
|
321
|
+
# will be used as the target name.
|
322
|
+
#
|
323
|
+
# An error will be raised if either +path+ or +to+ contains a parent-relative
|
324
|
+
# reference (<tt>../</tt>), or if the tar pipeline fails.
|
325
|
+
#
|
326
|
+
# === Examples
|
327
|
+
#
|
328
|
+
# cartage.recursive_copy('public/assets')
|
329
|
+
#
|
330
|
+
# This will cause <tt><em>root_path</em>/public/assets</tt> to be copied into
|
331
|
+
# <tt><em>work_path</em>/public/assets</tt>.
|
332
|
+
#
|
333
|
+
# cartage.recursive_copy('/tmp/public/assets')
|
334
|
+
#
|
335
|
+
# This will cause <tt>/tmp/public/assets</tt> to be copied into
|
336
|
+
# <tt><em>work_path</em>/assets</tt>.
|
337
|
+
#
|
338
|
+
# cartage.recursive_copy('/tmp/public/assets', to: 'public')
|
339
|
+
#
|
340
|
+
# This will cause <tt>/tmp/public/assets</tt> to be copied into
|
341
|
+
# <tt><em>work_path</em>/public/assets</tt>.
|
342
|
+
def recursive_copy(path, to: nil)
|
343
|
+
path = Pathname(path)
|
344
|
+
to = Pathname(to) if to
|
345
|
+
|
346
|
+
if path.to_s =~ %r{\.\./} || (to && to.to_s =~ %r{\.\./})
|
347
|
+
fail StandardError, "Recursive copy parameters cannot contain '/../'"
|
348
|
+
end
|
349
|
+
|
350
|
+
if path.relative?
|
351
|
+
parent = root_path
|
352
|
+
else
|
353
|
+
parent, path = path.split
|
354
|
+
end
|
355
|
+
|
356
|
+
target = work_path
|
357
|
+
target /= to if to
|
358
|
+
|
359
|
+
tar_cf_cmd = ["tar", "cf", "-", "-h", "-C", parent, path].map(&:to_s)
|
360
|
+
tar_xf_cmd = ["tar", "xf", "-", "-C", target].map(&:to_s)
|
361
|
+
|
362
|
+
IO.popen(tar_cf_cmd) do |cf|
|
363
|
+
IO.popen(tar_xf_cmd, "w") do |xf|
|
364
|
+
xf.write cf.read
|
365
|
+
end
|
366
|
+
|
367
|
+
fail StandardError, "Error running #{tar_xf_cmd.join(" ")}" unless $?.success?
|
368
|
+
end
|
369
|
+
|
370
|
+
fail StandardError, "Error running #{tar_cf_cmd.join(" ")}" unless $?.success?
|
371
|
+
end
|
372
|
+
|
301
373
|
private
|
302
374
|
|
303
375
|
attr_writer :release_hashref
|
304
376
|
|
305
377
|
def resolve_config!
|
306
|
-
fail
|
307
|
-
|
308
|
-
Cartage::Plugin.load_for(singleton_class)
|
378
|
+
fail "No configuration" unless config
|
309
379
|
|
310
380
|
self.disable_dependency_cache = config.disable_dependency_cache
|
311
381
|
self.quiet = config.quiet
|
@@ -318,6 +388,12 @@ class Cartage
|
|
318
388
|
maybe_assign :dependency_cache_path, config.dependency_cache_path
|
319
389
|
maybe_assign :release_hashref, config.release_hashref
|
320
390
|
|
391
|
+
lib = root_path.join("lib").to_s
|
392
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.any? { |l| l == lib }
|
393
|
+
Cartage::Plugin.load(rescan: true)
|
394
|
+
|
395
|
+
Cartage::Plugin.load_for(singleton_class)
|
396
|
+
|
321
397
|
Cartage::Plugin.each do |name|
|
322
398
|
next unless respond_to?(name)
|
323
399
|
plugin = send(name) or next
|
@@ -331,7 +407,7 @@ class Cartage
|
|
331
407
|
|
332
408
|
def maybe_assign(name, value)
|
333
409
|
return if value.nil? || (value.respond_to?(:empty?) && value.empty?) ||
|
334
|
-
|
410
|
+
instance_variable_defined?(:"@#{name}")
|
335
411
|
send(:"#{name}=", value)
|
336
412
|
end
|
337
413
|
|
@@ -355,57 +431,52 @@ class Cartage
|
|
355
431
|
end
|
356
432
|
|
357
433
|
def prepare_work_area
|
358
|
-
display
|
434
|
+
display "Preparing cartage work area..."
|
359
435
|
|
360
436
|
work_path.rmtree if work_path.exist?
|
361
437
|
work_path.mkpath
|
362
438
|
|
363
439
|
manifest.resolve(root_path) do |file_list|
|
364
440
|
tar_cf_cmd = [
|
365
|
-
|
441
|
+
"tar", "cf", "-", "-C", parent, "-h", "-T", file_list
|
366
442
|
].map(&:to_s)
|
367
443
|
|
368
444
|
tar_xf_cmd = [
|
369
|
-
|
445
|
+
"tar", "xf", "-", "-C", work_path, "--strip-components=1"
|
370
446
|
].map(&:to_s)
|
371
447
|
|
372
448
|
IO.popen(tar_cf_cmd) do |cf|
|
373
|
-
IO.popen(tar_xf_cmd,
|
449
|
+
IO.popen(tar_xf_cmd, "w") do |xf|
|
374
450
|
xf.write cf.read
|
375
451
|
end
|
376
452
|
|
377
|
-
fail StandardError, "Error running #{tar_xf_cmd.join(
|
453
|
+
fail StandardError, "Error running #{tar_xf_cmd.join(" ")}" unless $?.success?
|
378
454
|
end
|
379
455
|
|
380
|
-
fail StandardError, "Error running #{tar_cf_cmd.join(
|
456
|
+
fail StandardError, "Error running #{tar_cf_cmd.join(" ")}" unless $?.success?
|
381
457
|
end
|
382
458
|
end
|
383
459
|
|
384
|
-
def save_release_metadata
|
385
|
-
display 'Saving release metadata...'
|
386
|
-
json = JSON.generate(release_metadata)
|
387
|
-
work_path.join('release-metadata.json').write(json)
|
388
|
-
final_release_metadata_json.write(json)
|
389
|
-
end
|
390
|
-
|
391
460
|
def restore_modified_files
|
392
|
-
|
393
|
-
split($/)
|
394
|
-
map(&:split)
|
395
|
-
select { |s, _f| s !~ /\?/ }
|
396
|
-
map(&:last)
|
397
|
-
each { |file|
|
461
|
+
`git status -s`
|
462
|
+
.split($/)
|
463
|
+
.map(&:split)
|
464
|
+
.select { |s, _f| s !~ /\?/ }
|
465
|
+
.map(&:last)
|
466
|
+
.each { |file|
|
398
467
|
restore_modified_file file
|
399
468
|
}
|
400
469
|
end
|
401
470
|
|
402
471
|
def restore_modified_file(filename)
|
472
|
+
return unless work_path.join(filename).exist?
|
473
|
+
|
403
474
|
command = [
|
404
|
-
|
475
|
+
"git", "show", "#{release_hashref}:#{filename}"
|
405
476
|
]
|
406
477
|
|
407
478
|
IO.popen(command) do |show|
|
408
|
-
work_path.join(filename).open(
|
479
|
+
work_path.join(filename).open("w") { |f|
|
409
480
|
f.puts show.read
|
410
481
|
}
|
411
482
|
end
|
@@ -423,16 +494,16 @@ class Cartage
|
|
423
494
|
|
424
495
|
def extract_dependency_cache
|
425
496
|
return if disable_dependency_cache || !dependency_cache.exist?
|
426
|
-
run %W
|
497
|
+
run %W[tar xf#{tar_compression_flag} #{dependency_cache} -C #{work_path}]
|
427
498
|
end
|
428
499
|
|
429
500
|
def create_dependency_cache(paths = [])
|
430
501
|
return if disable_dependency_cache || paths.empty?
|
431
502
|
run [
|
432
|
-
|
503
|
+
"tar",
|
433
504
|
"cf#{tar_compression_flag}",
|
434
505
|
dependency_cache,
|
435
|
-
|
506
|
+
"-C",
|
436
507
|
work_path,
|
437
508
|
*paths
|
438
509
|
].map(&:to_s)
|
@@ -456,4 +527,4 @@ class Cartage
|
|
456
527
|
end
|
457
528
|
end
|
458
529
|
|
459
|
-
require_relative
|
530
|
+
require_relative "cartage/config"
|
data/test/minitest_config.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
gem
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
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
|
+
require "minitest-bonus-assertions"
|
10
10
|
|
11
|
-
require
|
11
|
+
require "cartage/minitest"
|