MuranoCLI 3.2.0.beta.1 → 3.2.0.beta.5
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 +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
|
|