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.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -1
  3. data/.trustme.plugin +137 -0
  4. data/.trustme.sh +217 -117
  5. data/.trustme.vim +9 -3
  6. data/Gemfile +9 -3
  7. data/MuranoCLI.gemspec +8 -5
  8. data/Rakefile +1 -0
  9. data/dockers/Dockerfile.2.2.9 +6 -3
  10. data/dockers/Dockerfile.2.3.6 +6 -3
  11. data/dockers/Dockerfile.2.4.3 +6 -3
  12. data/dockers/Dockerfile.2.5.0 +6 -3
  13. data/dockers/Dockerfile.GemRelease +10 -8
  14. data/dockers/Dockerfile.m4 +23 -5
  15. data/dockers/docker-test.sh +65 -28
  16. data/docs/completions/murano_completion-bash +751 -57
  17. data/docs/develop.rst +10 -9
  18. data/lib/MrMurano/AccountBase.rb +95 -6
  19. data/lib/MrMurano/Commander-Entry.rb +9 -4
  20. data/lib/MrMurano/Config-Migrate.rb +2 -0
  21. data/lib/MrMurano/Config.rb +94 -26
  22. data/lib/MrMurano/Content.rb +1 -1
  23. data/lib/MrMurano/Exchange.rb +77 -42
  24. data/lib/MrMurano/Gateway.rb +1 -1
  25. data/lib/MrMurano/HttpAuthed.rb +20 -7
  26. data/lib/MrMurano/Logs.rb +10 -1
  27. data/lib/MrMurano/ProjectFile.rb +1 -1
  28. data/lib/MrMurano/ReCommander.rb +129 -73
  29. data/lib/MrMurano/Solution-ServiceConfig.rb +18 -11
  30. data/lib/MrMurano/Solution-Services.rb +78 -50
  31. data/lib/MrMurano/Solution-Users.rb +1 -1
  32. data/lib/MrMurano/Solution.rb +13 -63
  33. data/lib/MrMurano/SyncUpDown-Core.rb +185 -77
  34. data/lib/MrMurano/SyncUpDown-Item.rb +29 -4
  35. data/lib/MrMurano/SyncUpDown.rb +11 -11
  36. data/lib/MrMurano/Webservice-Cors.rb +1 -1
  37. data/lib/MrMurano/Webservice-Endpoint.rb +28 -17
  38. data/lib/MrMurano/Webservice-File.rb +103 -43
  39. data/lib/MrMurano/commands/domain.rb +1 -0
  40. data/lib/MrMurano/commands/element.rb +585 -0
  41. data/lib/MrMurano/commands/exchange.rb +211 -204
  42. data/lib/MrMurano/commands/gb.rb +1 -0
  43. data/lib/MrMurano/commands/globals.rb +17 -7
  44. data/lib/MrMurano/commands/init.rb +115 -101
  45. data/lib/MrMurano/commands/keystore.rb +1 -1
  46. data/lib/MrMurano/commands/logs.rb +2 -1
  47. data/lib/MrMurano/commands/postgresql.rb +17 -7
  48. data/lib/MrMurano/commands/service.rb +572 -0
  49. data/lib/MrMurano/commands/show.rb +7 -3
  50. data/lib/MrMurano/commands/solution.rb +2 -1
  51. data/lib/MrMurano/commands/solution_picker.rb +31 -15
  52. data/lib/MrMurano/commands/status.rb +205 -169
  53. data/lib/MrMurano/commands/sync.rb +70 -38
  54. data/lib/MrMurano/commands/token.rb +59 -14
  55. data/lib/MrMurano/commands/usage.rb +1 -0
  56. data/lib/MrMurano/commands.rb +2 -0
  57. data/lib/MrMurano/hash.rb +91 -0
  58. data/lib/MrMurano/http.rb +55 -6
  59. data/lib/MrMurano/makePretty.rb +47 -0
  60. data/lib/MrMurano/optparse.rb +60 -45
  61. data/lib/MrMurano/variegated/TruthyFalsey.rb +48 -0
  62. data/lib/MrMurano/variegated/ruby_dig.rb +64 -0
  63. data/lib/MrMurano/verbosing.rb +113 -3
  64. data/lib/MrMurano/version.rb +1 -1
  65. data/spec/Account_spec.rb +34 -20
  66. data/spec/Business_spec.rb +12 -9
  67. data/spec/Config_spec.rb +7 -1
  68. data/spec/Content_spec.rb +17 -1
  69. data/spec/GatewayBase_spec.rb +5 -2
  70. data/spec/GatewayDevice_spec.rb +4 -2
  71. data/spec/GatewayResource_spec.rb +4 -1
  72. data/spec/GatewaySettings_spec.rb +4 -1
  73. data/spec/HttpAuthed_spec.rb +73 -0
  74. data/spec/Http_spec.rb +32 -35
  75. data/spec/ProjectFile_spec.rb +1 -1
  76. data/spec/Solution-ServiceConfig_spec.rb +4 -1
  77. data/spec/Solution-ServiceEventHandler_spec.rb +6 -3
  78. data/spec/Solution-ServiceModules_spec.rb +4 -1
  79. data/spec/Solution-UsersRoles_spec.rb +4 -1
  80. data/spec/Solution_spec.rb +4 -1
  81. data/spec/SyncUpDown_spec.rb +1 -1
  82. data/spec/Webservice-Cors_spec.rb +4 -1
  83. data/spec/Webservice-Endpoint_spec.rb +9 -6
  84. data/spec/Webservice-File_spec.rb +17 -4
  85. data/spec/Webservice-Setting_spec.rb +6 -2
  86. data/spec/_workspace.rb +2 -0
  87. data/spec/cmd_common.rb +42 -13
  88. data/spec/cmd_content_spec.rb +17 -7
  89. data/spec/cmd_device_spec.rb +1 -1
  90. data/spec/cmd_domain_spec.rb +2 -2
  91. data/spec/cmd_element_spec.rb +400 -0
  92. data/spec/cmd_exchange_spec.rb +2 -2
  93. data/spec/cmd_init_spec.rb +59 -25
  94. data/spec/cmd_keystore_spec.rb +6 -3
  95. data/spec/cmd_link_spec.rb +10 -5
  96. data/spec/cmd_logs_spec.rb +1 -1
  97. data/spec/cmd_setting_application_spec.rb +18 -15
  98. data/spec/cmd_setting_product_spec.rb +7 -7
  99. data/spec/cmd_status_spec.rb +27 -17
  100. data/spec/cmd_syncdown_application_spec.rb +30 -3
  101. data/spec/cmd_syncdown_both_spec.rb +72 -18
  102. data/spec/cmd_syncup_spec.rb +71 -5
  103. data/spec/cmd_token_spec.rb +2 -2
  104. data/spec/cmd_usage_spec.rb +2 -2
  105. data/spec/dry_run_formatter.rb +27 -0
  106. data/spec/fixtures/dumped_config +8 -0
  107. data/spec/fixtures/exchange_element/element-show.json +1 -0
  108. data/spec/fixtures/exchange_element/swagger-mur-6407__10k.yaml +282 -0
  109. data/spec/fixtures/exchange_element/swagger-mur-6407__20k.yaml +588 -0
  110. data/spec/variegated_TruthyFalsey_spec.rb +29 -0
  111. 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
- trmt = Tempfile.new([tolocalname(merged, @itemkey) + '_remote_', '.lua'])
162
- tlcl = Tempfile.new([tolocalname(merged, @itemkey) + '_local_', '.lua'])
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
- if patterns.empty?
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 _matcher_local_path(item)
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 already globbing or using absolutes.
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
- statuses.each_value { |items| select_selected(items) } unless options[:unselected]
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, or if user wants to skip
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] << { synckey: self.class.description }
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
- there = _matcher(there, selected)
388
- local = _matcher(local, selected)
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
- there.each do |item|
392
- item[:synckey] = synckey(item)
393
- item[:synctype] = self.class.description
394
- therebox[item[:synckey]] = item
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
- local.each do |item|
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
- localbox[skey] = item
457
+ lkup[skey] = item
407
458
  end
459
+ [lkup, dupl]
460
+ end
408
461
 
409
- localbox = resurrect_undeletables(localbox, therebox)
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
- [therebox, localbox]
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
- tomod = []
428
- unchg = []
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
- if options[:asdown]
438
- # Want 'there' to override 'local'.
439
- mrg = local.merge(there)
440
- else
441
- # Want 'local' to override 'there' except for itemkey.
442
- mrg = local.reject { |k, _v| k == @itemkey.to_sym }
443
- mrg = there.merge(mrg)
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
- if docmp(local, there)
447
- if (options[:diff] || local[:phantom] || local[:undeletable]) && mrg[:selected]
448
- mrg[:diff] = dodiff(mrg.to_h, local, there, options)
449
- if mrg[:diff].to_s.empty?
450
- debug %(Clean diff: #{local[:synckey]})
451
- mrg[:diff] = '<Nothing changed (was timestamp difference)>'
452
- unchg << mrg
453
- elsif local[:phantom]
454
- if options[:asdown]
455
- toadd << mrg
456
- else
457
- todel << mrg
458
- end
459
- localbox.delete(key)
460
- else
461
- tomod << mrg
462
- end
463
- else
464
- tomod << mrg
465
- end
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
- unchg << mrg
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 :line
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
- MrMurano::Verbose.error(
84
- "Unable to set key: #{key} / value: #{value} / err: #{err} / self: #{inspect}"
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
@@ -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
- fetch(id) do |chunk|
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
- warn "#{msg} / host_os: #{RbConfig::CONFIG['host_os']} / err: #{err}"
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}: Globs:\n #{sf.join("\n ")}"
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
- debug "#{self.class}: items:\n #{items.map { |it| it[:local_path] }.join("\n ")}"
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
 
@@ -36,7 +36,7 @@ module MrMurano
36
36
  #@project_section = :cors
37
37
  end
38
38
 
39
- def fetch(_id=nil)
39
+ def fetch(_id=nil, _untainted=false)
40
40
  ret = get
41
41
  return [] unless ret.is_a?(Hash) && !ret.key?(:error)
42
42
  if ret.key?(:cors)
@@ -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
- add_terminating_nl = ret[:script].end_with? "\n"
73
- script = ret[:script].lines.map(&:chomp)
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
- # if header is missing add it.
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 = script.join("\n")
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 = 0
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
- line: lineno,
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
- (item_a[:script] != item_b[:script] || item_a[:content_type] != item_b[:content_type])
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