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