MuranoCLI 3.2.0.beta.1 → 3.2.0.beta.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -1
- data/.trustme.plugin +137 -0
- data/.trustme.sh +217 -117
- data/.trustme.vim +9 -3
- data/Gemfile +9 -3
- data/MuranoCLI.gemspec +8 -5
- data/Rakefile +1 -0
- data/dockers/Dockerfile.2.2.9 +6 -3
- data/dockers/Dockerfile.2.3.6 +6 -3
- data/dockers/Dockerfile.2.4.3 +6 -3
- data/dockers/Dockerfile.2.5.0 +6 -3
- data/dockers/Dockerfile.GemRelease +10 -8
- data/dockers/Dockerfile.m4 +23 -5
- data/dockers/docker-test.sh +65 -28
- data/docs/completions/murano_completion-bash +751 -57
- data/docs/develop.rst +10 -9
- data/lib/MrMurano/AccountBase.rb +95 -6
- data/lib/MrMurano/Commander-Entry.rb +9 -4
- data/lib/MrMurano/Config-Migrate.rb +2 -0
- data/lib/MrMurano/Config.rb +94 -26
- data/lib/MrMurano/Content.rb +1 -1
- data/lib/MrMurano/Exchange.rb +77 -42
- data/lib/MrMurano/Gateway.rb +1 -1
- data/lib/MrMurano/HttpAuthed.rb +20 -7
- data/lib/MrMurano/Logs.rb +10 -1
- data/lib/MrMurano/ProjectFile.rb +1 -1
- data/lib/MrMurano/ReCommander.rb +129 -73
- data/lib/MrMurano/Solution-ServiceConfig.rb +18 -11
- data/lib/MrMurano/Solution-Services.rb +78 -50
- data/lib/MrMurano/Solution-Users.rb +1 -1
- data/lib/MrMurano/Solution.rb +13 -63
- data/lib/MrMurano/SyncUpDown-Core.rb +185 -77
- data/lib/MrMurano/SyncUpDown-Item.rb +29 -4
- data/lib/MrMurano/SyncUpDown.rb +11 -11
- data/lib/MrMurano/Webservice-Cors.rb +1 -1
- data/lib/MrMurano/Webservice-Endpoint.rb +28 -17
- data/lib/MrMurano/Webservice-File.rb +103 -43
- data/lib/MrMurano/commands/domain.rb +1 -0
- data/lib/MrMurano/commands/element.rb +585 -0
- data/lib/MrMurano/commands/exchange.rb +211 -204
- data/lib/MrMurano/commands/gb.rb +1 -0
- data/lib/MrMurano/commands/globals.rb +17 -7
- data/lib/MrMurano/commands/init.rb +115 -101
- data/lib/MrMurano/commands/keystore.rb +1 -1
- data/lib/MrMurano/commands/logs.rb +2 -1
- data/lib/MrMurano/commands/postgresql.rb +17 -7
- data/lib/MrMurano/commands/service.rb +572 -0
- data/lib/MrMurano/commands/show.rb +7 -3
- data/lib/MrMurano/commands/solution.rb +2 -1
- data/lib/MrMurano/commands/solution_picker.rb +31 -15
- data/lib/MrMurano/commands/status.rb +205 -169
- data/lib/MrMurano/commands/sync.rb +70 -38
- data/lib/MrMurano/commands/token.rb +59 -14
- data/lib/MrMurano/commands/usage.rb +1 -0
- data/lib/MrMurano/commands.rb +2 -0
- data/lib/MrMurano/hash.rb +91 -0
- data/lib/MrMurano/http.rb +55 -6
- data/lib/MrMurano/makePretty.rb +47 -0
- data/lib/MrMurano/optparse.rb +60 -45
- data/lib/MrMurano/variegated/TruthyFalsey.rb +48 -0
- data/lib/MrMurano/variegated/ruby_dig.rb +64 -0
- data/lib/MrMurano/verbosing.rb +113 -3
- data/lib/MrMurano/version.rb +1 -1
- data/spec/Account_spec.rb +34 -20
- data/spec/Business_spec.rb +12 -9
- data/spec/Config_spec.rb +7 -1
- data/spec/Content_spec.rb +17 -1
- data/spec/GatewayBase_spec.rb +5 -2
- data/spec/GatewayDevice_spec.rb +4 -2
- data/spec/GatewayResource_spec.rb +4 -1
- data/spec/GatewaySettings_spec.rb +4 -1
- data/spec/HttpAuthed_spec.rb +73 -0
- data/spec/Http_spec.rb +32 -35
- data/spec/ProjectFile_spec.rb +1 -1
- data/spec/Solution-ServiceConfig_spec.rb +4 -1
- data/spec/Solution-ServiceEventHandler_spec.rb +6 -3
- data/spec/Solution-ServiceModules_spec.rb +4 -1
- data/spec/Solution-UsersRoles_spec.rb +4 -1
- data/spec/Solution_spec.rb +4 -1
- data/spec/SyncUpDown_spec.rb +1 -1
- data/spec/Webservice-Cors_spec.rb +4 -1
- data/spec/Webservice-Endpoint_spec.rb +9 -6
- data/spec/Webservice-File_spec.rb +17 -4
- data/spec/Webservice-Setting_spec.rb +6 -2
- data/spec/_workspace.rb +2 -0
- data/spec/cmd_common.rb +42 -13
- data/spec/cmd_content_spec.rb +17 -7
- data/spec/cmd_device_spec.rb +1 -1
- data/spec/cmd_domain_spec.rb +2 -2
- data/spec/cmd_element_spec.rb +400 -0
- data/spec/cmd_exchange_spec.rb +2 -2
- data/spec/cmd_init_spec.rb +59 -25
- data/spec/cmd_keystore_spec.rb +6 -3
- data/spec/cmd_link_spec.rb +10 -5
- data/spec/cmd_logs_spec.rb +1 -1
- data/spec/cmd_setting_application_spec.rb +18 -15
- data/spec/cmd_setting_product_spec.rb +7 -7
- data/spec/cmd_status_spec.rb +27 -17
- data/spec/cmd_syncdown_application_spec.rb +30 -3
- data/spec/cmd_syncdown_both_spec.rb +72 -18
- data/spec/cmd_syncup_spec.rb +71 -5
- data/spec/cmd_token_spec.rb +2 -2
- data/spec/cmd_usage_spec.rb +2 -2
- data/spec/dry_run_formatter.rb +27 -0
- data/spec/fixtures/dumped_config +8 -0
- data/spec/fixtures/exchange_element/element-show.json +1 -0
- data/spec/fixtures/exchange_element/swagger-mur-6407__10k.yaml +282 -0
- data/spec/fixtures/exchange_element/swagger-mur-6407__20k.yaml +588 -0
- data/spec/variegated_TruthyFalsey_spec.rb +29 -0
- metadata +51 -25
@@ -10,9 +10,12 @@ require 'pathname'
|
|
10
10
|
require 'tempfile'
|
11
11
|
require 'MrMurano/verbosing'
|
12
12
|
require 'MrMurano/hash'
|
13
|
+
require 'MrMurano/SyncUpDown-Item'
|
13
14
|
|
14
15
|
module MrMurano
|
15
16
|
module SyncCore
|
17
|
+
include Verbose
|
18
|
+
|
16
19
|
#######################################################################
|
17
20
|
# Methods that provide the core status/syncup/syncdown
|
18
21
|
|
@@ -20,7 +23,7 @@ module MrMurano
|
|
20
23
|
if $cfg['tool.no-progress']
|
21
24
|
say(msg)
|
22
25
|
else
|
23
|
-
verbose(msg + "\n")
|
26
|
+
MrMurano::Verbose.verbose(msg + "\n")
|
24
27
|
end
|
25
28
|
end
|
26
29
|
|
@@ -158,8 +161,21 @@ module MrMurano
|
|
158
161
|
# @param asdown [Boolean] Direction/prespective of diff
|
159
162
|
# @return [String] The diff output
|
160
163
|
def dodiff(merged, local, _there=nil, options={})
|
161
|
-
|
162
|
-
|
164
|
+
localname = tolocalname(merged, @itemkey)
|
165
|
+
# MUR-6488: Beware nested files, e.g., /tmp/js/script.js will
|
166
|
+
# not work, because /tmp/js does not exist!
|
167
|
+
# First, remove the leading path delimiter.
|
168
|
+
localname = localname.gsub(/^#{::File::SEPARATOR}*/, '')
|
169
|
+
# Next, replace remaining delimiters with a dot.
|
170
|
+
localname = localname.tr(::File::SEPARATOR, '.')
|
171
|
+
begin
|
172
|
+
# (lb): Not always Lua, e.g., might be PNG. Postfix doesn't matter, though.
|
173
|
+
trmt = Tempfile.new([localname + '_remote_', '.lua'])
|
174
|
+
tlcl = Tempfile.new([localname + '_local_', '.lua'])
|
175
|
+
rescue StandardError => err
|
176
|
+
MrMurano::Verbose.error("Failed to create temporary file: #{err}")
|
177
|
+
raise
|
178
|
+
end
|
163
179
|
Pathname.new(tlcl.path).open('wb') do |io|
|
164
180
|
# Copy the local file to a temp file, for the diff command.
|
165
181
|
# And for resources, remove the local-only :selected key, as
|
@@ -234,29 +250,35 @@ module MrMurano
|
|
234
250
|
# Check if an item matches a pattern.
|
235
251
|
# @param items [Array<Item>] Of items to filter
|
236
252
|
# @param patterns [Array<String>] Filters for _matcher
|
237
|
-
def _matcher(items, patterns)
|
253
|
+
def _matcher(items, patterns, desc)
|
238
254
|
items.map do |item|
|
239
|
-
|
240
|
-
item[:selected] = true
|
241
|
-
else
|
242
|
-
item[:selected] = patterns.any? do |pattern|
|
243
|
-
if pattern.to_s[0] == '#'
|
244
|
-
match(item, pattern)
|
245
|
-
else
|
246
|
-
lpath = _matcher_local_path(item)
|
247
|
-
_matcher_check_pattern(lpath, pattern)
|
248
|
-
end
|
249
|
-
end
|
250
|
-
end
|
255
|
+
item[:selected] = _matcher_check_patterns(item, patterns)
|
251
256
|
item
|
252
257
|
end
|
258
|
+
_matcher_debug_log(items, desc)
|
259
|
+
items
|
253
260
|
end
|
254
261
|
private :_matcher
|
255
262
|
|
256
|
-
def
|
263
|
+
def _matcher_check_patterns(item, patterns)
|
264
|
+
return true if patterns.empty?
|
265
|
+
patterns.any? do |pattern|
|
266
|
+
if pattern.to_s[0] == '#'
|
267
|
+
match(item, pattern)
|
268
|
+
else
|
269
|
+
lpath = _matcher_local_path(item)
|
270
|
+
_matcher_check_pattern(lpath, pattern)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
private :_matcher_check_patterns
|
275
|
+
|
276
|
+
def _matcher_local_path(item, friendly: false)
|
257
277
|
if !defined?(item.local_path) || item.local_path.nil?
|
258
278
|
into = location
|
259
279
|
tolocalpath(into, item)
|
280
|
+
elsif friendly
|
281
|
+
item.location_friendly(full_path: true)
|
260
282
|
else
|
261
283
|
item[:local_path]
|
262
284
|
end
|
@@ -275,14 +297,27 @@ module MrMurano
|
|
275
297
|
# Be nice and try globbing the start of the path. E.g., if user
|
276
298
|
# specifies pattern=relative/path/to/file.rb, we should retry their
|
277
299
|
# request as */relative/path/to/file.rb. But don't glob if it looks
|
278
|
-
# like the user is
|
300
|
+
# like the user is using an /absolute/path.
|
279
301
|
return false if pattern.to_s.start_with?(File::SEPARATOR)
|
280
|
-
# See if the pattern includes any '*' that are not \-delimited.
|
281
|
-
return false if pattern.to_s =~ /(?<!\\)\*/
|
282
302
|
lpath.fnmatch? "*/#{pattern}"
|
283
303
|
end
|
284
304
|
private :_matcher_check_pattern
|
285
305
|
|
306
|
+
def _matcher_debug_log(items, desc)
|
307
|
+
# If :dup_count is set, there are duplicates of the same item.
|
308
|
+
# Note that the item with dup_count == 0 is a control item, which
|
309
|
+
# we ignore (it was cloned from another item also in the list).
|
310
|
+
culled = items.reject do |item|
|
311
|
+
!item[:dup_count].nil? && item[:dup_count].zero?
|
312
|
+
end
|
313
|
+
items_loci = culled.map do |item|
|
314
|
+
_matcher_local_path(item, friendly: true).to_s
|
315
|
+
end
|
316
|
+
items_list = items_loci.sort.join("\n ")
|
317
|
+
debug "#{self.class}: #{desc}'s matches:\n #{items_list}"
|
318
|
+
end
|
319
|
+
private :_matcher_debug_log
|
320
|
+
|
286
321
|
## Get status of things here verses there
|
287
322
|
#
|
288
323
|
# @param options [Hash, Commander::Command::Options] Options on operation
|
@@ -306,7 +341,17 @@ module MrMurano
|
|
306
341
|
|
307
342
|
items_cull_clashes!(statuses)
|
308
343
|
|
309
|
-
|
344
|
+
# 2018-04-24: (lb): How did I not see this til now? :unselected is :notimplemented.
|
345
|
+
unless options[:unselected]
|
346
|
+
statuses.each do |bucket, items|
|
347
|
+
select_selected(items)
|
348
|
+
next unless $cfg['tool.debug']
|
349
|
+
loci = items.map { |item| item.location_friendly(full_path: true) }
|
350
|
+
selected_paths = loci.sort.join("\n ")
|
351
|
+
selected_paths = (selected_paths.empty? && ' <none>' || "\n ") + selected_paths
|
352
|
+
debug %(#{self.class}: Selected #{bucket} items:#{selected_paths})
|
353
|
+
end
|
354
|
+
end
|
310
355
|
|
311
356
|
statuses
|
312
357
|
end
|
@@ -315,8 +360,8 @@ module MrMurano
|
|
315
360
|
# Get the solution name from the config.
|
316
361
|
# Convert, e.g., application.id => application.name
|
317
362
|
soln_name = $cfg[@solntype.gsub(/(.*)\.id/, '\1.name')]
|
318
|
-
# Skip this syncable if the api_id is not set,
|
319
|
-
# by solution.
|
363
|
+
# Skip this syncable if the api_id is not set,
|
364
|
+
# or if the user wants to skip by solution.
|
320
365
|
skip_sol = false
|
321
366
|
if !api_id? ||
|
322
367
|
(options[:type] == :application && @solntype != 'application.id') ||
|
@@ -364,7 +409,7 @@ module MrMurano
|
|
364
409
|
end
|
365
410
|
return nil unless skip_sol
|
366
411
|
ret = { toadd: [], todel: [], tomod: [], unchg: [], skipd: [], clash: [] }
|
367
|
-
ret[:skipd] <<
|
412
|
+
ret[:skipd] << SyncUpDown::Item.new(synckey: self.class.description)
|
368
413
|
ret
|
369
414
|
end
|
370
415
|
|
@@ -384,31 +429,54 @@ module MrMurano
|
|
384
429
|
|
385
430
|
resolve_config_var_usage!(there, local)
|
386
431
|
|
387
|
-
|
388
|
-
|
432
|
+
debug "#{self.class}: user-specified patterns:\n #{selected.sort.join("\n ")}"
|
433
|
+
there = _matcher(there, selected, :there)
|
434
|
+
local = _matcher(local, selected, :local)
|
389
435
|
|
390
|
-
therebox =
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
end
|
436
|
+
therebox, theredup = items_classify_and_find_duplicates(there)
|
437
|
+
localbox, localdup = items_classify_and_find_duplicates(local)
|
438
|
+
items_log_duplicates_there_local(theredup, localdup)
|
439
|
+
|
440
|
+
localbox = resurrect_undeletables(localbox, therebox)
|
396
441
|
|
397
|
-
localbox
|
398
|
-
|
442
|
+
[therebox, localbox]
|
443
|
+
end
|
444
|
+
|
445
|
+
def items_classify_and_find_duplicates(items)
|
446
|
+
lkup = {}
|
447
|
+
dupl = {}
|
448
|
+
items.each do |item|
|
399
449
|
skey = synckey(item)
|
400
|
-
# 2017-07-02: Check for local duplicates.
|
401
450
|
unless item[:dup_count].nil? || item[:dup_count].zero?
|
451
|
+
dupl[skey] = [] if dupl[skey].nil?
|
452
|
+
dupl[skey].push item
|
402
453
|
skey += "-#{item[:dup_count]}"
|
403
454
|
end
|
404
455
|
item[:synckey] = skey
|
405
456
|
item[:synctype] = self.class.description
|
406
|
-
|
457
|
+
lkup[skey] = item
|
407
458
|
end
|
459
|
+
[lkup, dupl]
|
460
|
+
end
|
408
461
|
|
409
|
-
|
462
|
+
def items_log_duplicates_there_local(theredup, localdup)
|
463
|
+
if !theredup.empty? && $cfg['tool.developer']
|
464
|
+
error %(Unexpected: Duplicate items detected in "there" items!)
|
465
|
+
end
|
466
|
+
items_log_duplicates(theredup)
|
467
|
+
items_log_duplicates(localdup)
|
468
|
+
end
|
410
469
|
|
411
|
-
|
470
|
+
def items_log_duplicates(dupl)
|
471
|
+
dupl.each do |skey, items|
|
472
|
+
msg = 'Duplicate definitions found for '
|
473
|
+
if self.class.description.to_s != ''
|
474
|
+
msg += "#{fancy_ticks(self.class.description)} item: "
|
475
|
+
end
|
476
|
+
msg += fancy_ticks(skey)
|
477
|
+
warning(msg)
|
478
|
+
items.each { |item| warning(" #{item.location_friendly}") }
|
479
|
+
end
|
412
480
|
end
|
413
481
|
|
414
482
|
def items_new_and_old!(statuses, options, therebox, localbox)
|
@@ -424,54 +492,94 @@ module MrMurano
|
|
424
492
|
end
|
425
493
|
|
426
494
|
def items_mods_and_chgs!(statuses, options, therebox, localbox)
|
427
|
-
|
428
|
-
|
429
|
-
toadd = []
|
430
|
-
todel = []
|
431
|
-
|
495
|
+
init_mods_and_chgs_arrs(statuses)
|
496
|
+
verbose "#{self.class}: Checking for changes:"
|
432
497
|
(localbox.keys & therebox.keys).each do |key|
|
433
498
|
local = localbox[key]
|
434
499
|
there = therebox[key]
|
500
|
+
verbose %( #{local.location_friendly(full_path: true)})
|
435
501
|
# Skip this item if it's got duplicate conflicts.
|
436
502
|
next if !local.is_a?(Hash) && local.dup_count == 0
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
end
|
503
|
+
mrg = item_local_there_merged(local, there, options)
|
504
|
+
# MAYBE/2018-04-24: (lb): If both are not :selected, do not bother diffing.
|
505
|
+
dirty_phantom = item_merged_set_status(mrg, statuses, local, there, options)
|
506
|
+
next unless dirty_phantom
|
507
|
+
localbox.delete(key)
|
508
|
+
end
|
509
|
+
end
|
445
510
|
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
511
|
+
def init_mods_and_chgs_arrs(statuses)
|
512
|
+
statuses[:tomod] = []
|
513
|
+
statuses[:unchg] = []
|
514
|
+
# SKIP: statuses[:toadd]
|
515
|
+
# SKIP: statuses[:todel]
|
516
|
+
end
|
517
|
+
|
518
|
+
def item_local_there_merged(local, there, options)
|
519
|
+
if options[:asdown]
|
520
|
+
# Want 'there' to override 'local'.
|
521
|
+
local.merge(there)
|
522
|
+
else
|
523
|
+
# Want 'local' to override 'there' except for itemkey.
|
524
|
+
mrg = local.reject { |k, _v| k == @itemkey.to_sym }
|
525
|
+
there.merge(mrg)
|
526
|
+
end
|
527
|
+
end
|
528
|
+
|
529
|
+
def item_merged_set_status(mrg, statuses, local, there, options)
|
530
|
+
if !docmp(local, there)
|
531
|
+
# Nothing dirty, according to cursory compare using timestamps, hashes, etc.
|
532
|
+
statuses[:unchg] << mrg
|
533
|
+
false
|
534
|
+
else
|
535
|
+
# Do an in-depth compare, e.g., content vs content, and return if phantom.
|
536
|
+
item_dirty_set_status(mrg, statuses, local, there, options)
|
537
|
+
end
|
538
|
+
end
|
539
|
+
|
540
|
+
def item_dirty_set_status(mrg, statuses, local, there, options)
|
541
|
+
if !(options[:diff] || local[:phantom] || local[:undeletable])
|
542
|
+
# NOTE: (lb): We mark an item `modified` if any of these are true:
|
543
|
+
# 1. User is not diffing, e.g., murano-status, or sync-up/-down commands.
|
544
|
+
# 2. The local item is a phantom item, i.e., user deleted locally, but
|
545
|
+
# platform does not let us really delete (we just clear contents),
|
546
|
+
# so we maintain a local `phantom` item to represent this.
|
547
|
+
# 3. The item is undeletable. (Checking this might make checking
|
548
|
+
# if phantom redundant, since only undeletables can be phantom,
|
549
|
+
# I think.)
|
550
|
+
statuses[:tomod] << mrg
|
551
|
+
# Not a phantom item; return false.
|
552
|
+
false
|
553
|
+
else
|
554
|
+
item_merged_diff_status(mrg, statuses, local, there, options)
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
558
|
+
def item_merged_diff_status(mrg, statuses, local, there, options)
|
559
|
+
dirty_phantom = false
|
560
|
+
mrg[:diff] = dodiff(mrg.to_h, local, there, options)
|
561
|
+
if mrg[:diff].to_s.empty?
|
562
|
+
# Nothing changed -- cursory compare was false positive.
|
563
|
+
# FIXME: Update cache or otherwise prevent from recurring.
|
564
|
+
debug %( Clean diff: #{local[:synckey]})
|
565
|
+
mrg[:diff] = '<Nothing changed (was timestamp difference)>'
|
566
|
+
statuses[:unchg] << mrg
|
567
|
+
elsif local[:phantom]
|
568
|
+
# Only exists local or remote, not both, and dirty diff.
|
569
|
+
# I.e., one side may no longer be phantom (might have content).
|
570
|
+
dirty_phantom = true
|
571
|
+
debug %( Drty Phntm: #{local[:synckey]})
|
572
|
+
if options[:asdown]
|
573
|
+
statuses[:toadd] << mrg
|
466
574
|
else
|
467
|
-
|
575
|
+
statuses[:todel] << mrg
|
468
576
|
end
|
577
|
+
else
|
578
|
+
# Dirty diff.
|
579
|
+
debug %( Dirty diff: #{local[:synckey]})
|
580
|
+
statuses[:tomod] << mrg
|
469
581
|
end
|
470
|
-
|
471
|
-
statuses[:tomod] = tomod
|
472
|
-
statuses[:unchg] = unchg
|
473
|
-
statuses[:toadd] += toadd
|
474
|
-
statuses[:todel] += todel
|
582
|
+
dirty_phantom
|
475
583
|
end
|
476
584
|
|
477
585
|
def sort_by_name(list)
|
@@ -25,7 +25,7 @@ module MrMurano
|
|
25
25
|
# @return [String] The Lua code for this item. (not all items use this.)
|
26
26
|
attr_accessor :script
|
27
27
|
# @return [Integer] The line in #local_path where this #script starts.
|
28
|
-
attr_accessor :
|
28
|
+
attr_accessor :line_beg
|
29
29
|
# @return [Integer] The line in #local_path where this #script ends.
|
30
30
|
attr_accessor :line_end
|
31
31
|
# @return [String] If requested, the diff output.
|
@@ -80,9 +80,14 @@ module MrMurano
|
|
80
80
|
def []=(key, value)
|
81
81
|
public_send("#{key}=", value)
|
82
82
|
rescue StandardError => err
|
83
|
-
|
84
|
-
|
85
|
-
)
|
83
|
+
# DEVS: (lb): This isn't an error so much as whatever Item class this
|
84
|
+
# is is missing some members (perhaps they were added recently to the
|
85
|
+
# service, and the CLI has yet to be updated).
|
86
|
+
if $cfg['tool.developer']
|
87
|
+
MrMurano::Verbose.error(
|
88
|
+
"Unable to set key: #{key} / value: #{value} / err: #{err} / self: #{inspect}"
|
89
|
+
)
|
90
|
+
end
|
86
91
|
end
|
87
92
|
|
88
93
|
# Delete a key
|
@@ -153,6 +158,26 @@ module MrMurano
|
|
153
158
|
# MAYBE/2017-07-18: Permanently disable Style/RedundantSelf?
|
154
159
|
self.to_h <=> other.to_h
|
155
160
|
end
|
161
|
+
|
162
|
+
def location_friendly(show_type: false, full_path: false)
|
163
|
+
if !@local_path.nil?
|
164
|
+
desc = @local_path
|
165
|
+
desc = desc.relative_path_from(Pathname.pwd) unless full_path
|
166
|
+
desc = desc.to_s
|
167
|
+
desc = "#{desc}:#{@line_beg}" if !@line_beg.nil? && @line_beg > 0
|
168
|
+
desc = "#{desc}-#{@line_end}" if !@line_end.nil? && @line_end > 0
|
169
|
+
else
|
170
|
+
desc = @synckey
|
171
|
+
end
|
172
|
+
desc += ' [phantom]' if @phantom
|
173
|
+
return desc unless show_type
|
174
|
+
unless @pp_desc.to_s.empty? || (@pp_desc == @synckey)
|
175
|
+
desc += " (#{@pp_desc})"
|
176
|
+
end
|
177
|
+
# NOTE: This path is the endpoint path.
|
178
|
+
desc += " [#{@method} #{@path}]" if (@method && @path)
|
179
|
+
desc
|
180
|
+
end
|
156
181
|
end
|
157
182
|
end
|
158
183
|
end
|
data/lib/MrMurano/SyncUpDown.rb
CHANGED
@@ -22,6 +22,7 @@ module MrMurano
|
|
22
22
|
module SyncUpDown
|
23
23
|
include SyncAllowed
|
24
24
|
include SyncCore
|
25
|
+
include Verbose
|
25
26
|
|
26
27
|
#######################################################################
|
27
28
|
# Methods that must be overridden
|
@@ -186,7 +187,9 @@ module MrMurano
|
|
186
187
|
# than downloading again.
|
187
188
|
local.dirname.mkpath
|
188
189
|
local.open('wb') do |io|
|
189
|
-
|
190
|
+
# Do not modify remote content when diffing, e.g., do not add #ENDPOINT header.
|
191
|
+
untainted = options[:diff]
|
192
|
+
fetch(id, untainted) do |chunk|
|
190
193
|
io.write config_vars_encode chunk
|
191
194
|
end
|
192
195
|
end
|
@@ -211,7 +214,7 @@ module MrMurano
|
|
211
214
|
# (lb): This is okay on Windows. (We really need a better solution.)
|
212
215
|
unless OS.windows?
|
213
216
|
msg = 'Unexpected: touch failed on non-Windows machine'
|
214
|
-
|
217
|
+
warning "#{msg} / host_os: #{RbConfig::CONFIG['host_os']} / err: #{err}"
|
215
218
|
end
|
216
219
|
end
|
217
220
|
end
|
@@ -329,19 +332,13 @@ module MrMurano
|
|
329
332
|
items[skey] = item.clone
|
330
333
|
items[skey][:dup_count] = 0
|
331
334
|
end
|
332
|
-
counts[skey] = counts.key?(skey) && counts[skey] + 1 || 1
|
335
|
+
counts[skey] = counts.key?(skey) && (counts[skey] + 1) || 1
|
333
336
|
# Use a unique synckey so all duplicates make it in the list.
|
334
337
|
uniq_synckey = "#{skey}-#{counts[skey]}"
|
335
338
|
item[:dup_count] = counts[skey]
|
336
339
|
# This sets the alias for the output, so duplicates look unique.
|
337
340
|
item[@itemkey.to_sym] = uniq_synckey
|
338
341
|
items[uniq_synckey] = item
|
339
|
-
msg = "Duplicate definition found for #{fancy_ticks(skey)}"
|
340
|
-
if self.class.description.to_s != ''
|
341
|
-
msg += " for #{fancy_ticks(self.class.description)}"
|
342
|
-
end
|
343
|
-
warning(msg)
|
344
|
-
warning(" #{item.local_path}")
|
345
342
|
else
|
346
343
|
items[skey] = item
|
347
344
|
end
|
@@ -412,7 +409,8 @@ module MrMurano
|
|
412
409
|
debug "#{self.class}: Getting local items from:\n #{from}"
|
413
410
|
search_in = from.to_s
|
414
411
|
sf = searchFor.map { |i| ::File.join(search_in, i) }
|
415
|
-
debug "#{self.class}:
|
412
|
+
debug "#{self.class}: #{@project_section}.include:\n #{sf.sort.join("\n ")}"
|
413
|
+
debug "#{self.class}: #{@project_section}.exclude:\n #{ignoring.sort.join("\n ")}"
|
416
414
|
# 2017-07-27: Add uniq to cull duplicate entries that globbing
|
417
415
|
# all the ways might produce, otherwise status/sync/diff complain
|
418
416
|
# about duplicate resources. I [lb] think this problem has existed
|
@@ -449,7 +447,9 @@ module MrMurano
|
|
449
447
|
#items = items.flatten.compact.sort_by!(&:local_path)
|
450
448
|
#debug "#{self.class}: items:\n #{items.map(&:local_path).join("\n ")}"
|
451
449
|
items = items.flatten.compact.sort_by { |it| it[:local_path] }
|
452
|
-
|
450
|
+
loci = items.map { |it| it.location_friendly(full_path: true) }
|
451
|
+
item_list = loci.sort.join("\n ")
|
452
|
+
debug "#{self.class}: localitems' matches:\n #{item_list}"
|
453
453
|
sort_by_name(items)
|
454
454
|
end
|
455
455
|
|
@@ -60,7 +60,7 @@ module MrMurano
|
|
60
60
|
# sort_by_name(ret)
|
61
61
|
end
|
62
62
|
|
63
|
-
def fetch(id)
|
63
|
+
def fetch(id, untainted=false)
|
64
64
|
ret = get('/' + id.to_s)
|
65
65
|
unless ret.is_a?(Hash) && !ret.key?(:error)
|
66
66
|
error "#{UNEXPECTED_TYPE_OR_ERROR_MSG}: #{ret}"
|
@@ -69,18 +69,32 @@ module MrMurano
|
|
69
69
|
|
70
70
|
ret[:content_type] = 'application/json' if ret[:content_type].empty?
|
71
71
|
|
72
|
-
|
73
|
-
|
72
|
+
if untainted
|
73
|
+
script = ret[:script]
|
74
|
+
else
|
75
|
+
add_terminating_nl = ret[:script].end_with? "\n"
|
76
|
+
script = ret[:script].lines.map(&:chomp)
|
77
|
+
fetch_fix_script_header(script, ret)
|
78
|
+
script = script.join("\n")
|
79
|
+
script += "\n" if add_terminating_nl
|
80
|
+
end
|
81
|
+
|
82
|
+
if block_given?
|
83
|
+
yield script
|
84
|
+
else
|
85
|
+
script
|
86
|
+
end
|
87
|
+
end
|
74
88
|
|
89
|
+
def fetch_fix_script_header(script, ret)
|
75
90
|
aheader = (script.first || '')
|
76
91
|
|
77
92
|
rh = ['--#ENDPOINT', ret[:method].upcase, ret[:path]]
|
78
93
|
rh << ret[:content_type] if ret[:content_type] != 'application/json'
|
79
94
|
rheader = rh.join(' ')
|
80
95
|
|
81
|
-
#
|
96
|
+
# If header is missing add it.
|
82
97
|
# If header is wrong, replace it.
|
83
|
-
|
84
98
|
md = @match_header.match(aheader)
|
85
99
|
if md.nil?
|
86
100
|
# header missing.
|
@@ -95,13 +109,7 @@ module MrMurano
|
|
95
109
|
end
|
96
110
|
# otherwise current header is good.
|
97
111
|
|
98
|
-
script
|
99
|
-
script += "\n" if add_terminating_nl
|
100
|
-
if block_given?
|
101
|
-
yield script
|
102
|
-
else
|
103
|
-
script
|
104
|
-
end
|
112
|
+
script
|
105
113
|
end
|
106
114
|
|
107
115
|
##
|
@@ -167,12 +175,13 @@ module MrMurano
|
|
167
175
|
items = []
|
168
176
|
path = Pathname.new(path) unless path.is_a? Pathname
|
169
177
|
cur = nil
|
170
|
-
lineno =
|
178
|
+
lineno = 1
|
171
179
|
path.readlines.each do |line|
|
180
|
+
lineno += 1
|
172
181
|
md = @match_header.match(line)
|
173
182
|
if !md.nil?
|
174
183
|
# header line.
|
175
|
-
cur[:line_end] = lineno unless cur.nil?
|
184
|
+
cur[:line_end] = lineno - 1 unless cur.nil?
|
176
185
|
items << cur unless cur.nil?
|
177
186
|
# VERIFY/2017-07-03: The syncdown test is revealing a
|
178
187
|
# problem with casing. The original file has a lowercase
|
@@ -193,14 +202,13 @@ module MrMurano
|
|
193
202
|
path: md[:path],
|
194
203
|
content_type: (md[:ctype] || 'application/json'),
|
195
204
|
local_path: path,
|
196
|
-
|
205
|
+
line_beg: lineno,
|
197
206
|
script: up_line,
|
198
207
|
)
|
199
208
|
elsif !cur.nil? && !cur[:script].nil?
|
200
209
|
# 2017-07-02: Frozen string literal: change << to +=
|
201
210
|
cur[:script] += line
|
202
211
|
end
|
203
|
-
lineno += 1
|
204
212
|
end
|
205
213
|
cur[:line_end] = lineno unless cur.nil?
|
206
214
|
items << cur unless cur.nil?
|
@@ -234,7 +242,10 @@ module MrMurano
|
|
234
242
|
if item_b[:script].nil? && item_b[:local_path]
|
235
243
|
item_b[:script] = item_b[:local_path].read
|
236
244
|
end
|
237
|
-
|
245
|
+
cmp = false
|
246
|
+
cmp ||= item_a[:content_type] != item_b[:content_type]
|
247
|
+
cmp ||= item_a[:script] != item_b[:script]
|
248
|
+
cmp
|
238
249
|
end
|
239
250
|
end
|
240
251
|
|