MuranoCLI 3.0.0 → 3.0.1
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 +50 -27
- data/.trustme.vim +12 -8
- data/bin/murano +23 -8
- data/docs/basic_example.rst +1 -1
- data/docs/completions/murano_completion-bash +88 -0
- data/lib/MrMurano/Account.rb +3 -3
- data/lib/MrMurano/Business.rb +6 -6
- data/lib/MrMurano/Config-Migrate.rb +1 -3
- data/lib/MrMurano/Config.rb +16 -8
- data/lib/MrMurano/Content.rb +56 -45
- data/lib/MrMurano/Gateway.rb +62 -21
- data/lib/MrMurano/Mock.rb +27 -19
- data/lib/MrMurano/Passwords.rb +7 -7
- data/lib/MrMurano/ReCommander.rb +171 -28
- data/lib/MrMurano/Setting.rb +38 -40
- data/lib/MrMurano/Solution-ServiceConfig.rb +2 -1
- data/lib/MrMurano/Solution-Services.rb +196 -61
- data/lib/MrMurano/Solution-Users.rb +7 -7
- data/lib/MrMurano/Solution.rb +22 -8
- data/lib/MrMurano/SolutionId.rb +10 -4
- data/lib/MrMurano/SubCmdGroupContext.rb +14 -5
- data/lib/MrMurano/SyncAllowed.rb +42 -0
- data/lib/MrMurano/SyncUpDown.rb +122 -65
- data/lib/MrMurano/Webservice-Cors.rb +9 -3
- data/lib/MrMurano/Webservice-Endpoint.rb +39 -33
- data/lib/MrMurano/Webservice-File.rb +35 -24
- data/lib/MrMurano/commands/business.rb +18 -18
- data/lib/MrMurano/commands/content.rb +3 -2
- data/lib/MrMurano/commands/devices.rb +137 -22
- data/lib/MrMurano/commands/globals.rb +8 -2
- data/lib/MrMurano/commands/keystore.rb +3 -2
- data/lib/MrMurano/commands/link.rb +13 -13
- data/lib/MrMurano/commands/login.rb +3 -2
- data/lib/MrMurano/commands/mock.rb +4 -3
- data/lib/MrMurano/commands/password.rb +4 -2
- data/lib/MrMurano/commands/postgresql.rb +5 -3
- data/lib/MrMurano/commands/settings.rb +78 -62
- data/lib/MrMurano/commands/show.rb +79 -74
- data/lib/MrMurano/commands/solution.rb +6 -4
- data/lib/MrMurano/commands/solution_picker.rb +5 -4
- data/lib/MrMurano/commands/status.rb +15 -4
- data/lib/MrMurano/commands/timeseries.rb +3 -2
- data/lib/MrMurano/commands/tsdb.rb +3 -2
- data/lib/MrMurano/hash.rb +6 -6
- data/lib/MrMurano/http.rb +66 -67
- data/lib/MrMurano/makePretty.rb +18 -12
- data/lib/MrMurano/progress.rb +9 -2
- data/lib/MrMurano/verbosing.rb +14 -2
- data/lib/MrMurano/version.rb +2 -2
- data/spec/GatewayDevice_spec.rb +190 -149
- data/spec/Mock_spec.rb +3 -3
- data/spec/Solution-ServiceEventHandler_spec.rb +170 -137
- data/spec/SyncUpDown_spec.rb +205 -191
- metadata +3 -2
@@ -1,4 +1,4 @@
|
|
1
|
-
# Last Modified: 2017.08.
|
1
|
+
# Last Modified: 2017.08.22 /coding: utf-8
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
# Copyright © 2016-2017 Exosite LLC.
|
@@ -34,11 +34,13 @@ module MrMurano
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def remove(id)
|
37
|
+
return unless remove_item_allowed(id)
|
37
38
|
delete('/' + id.to_s)
|
38
39
|
end
|
39
40
|
|
40
41
|
# @param modify Bool: True if item exists already and this is changing it
|
41
42
|
def upload(_local, remote, _modify)
|
43
|
+
return unless upload_item_allowed(remote[@itemkey])
|
42
44
|
# Roles cannot be modified, so must delete and post.
|
43
45
|
delete('/' + remote[@itemkey]) do |request, http|
|
44
46
|
response = http.request(request)
|
@@ -59,9 +61,6 @@ module MrMurano
|
|
59
61
|
# for now, we'll read, modify, write
|
60
62
|
here = []
|
61
63
|
if local.exist?
|
62
|
-
# FIXME/2017-07-18: Security/YAMLLoad: Prefer using YAML.safe_load over YAML.load.
|
63
|
-
# Disabling [rubo]cop for now.
|
64
|
-
# rubocop:disable Security/YAMLLoad
|
65
64
|
local.open('rb') { |io| here = YAML.load(io) }
|
66
65
|
here = [] if here == false
|
67
66
|
end
|
@@ -69,6 +68,7 @@ module MrMurano
|
|
69
68
|
Hash.transform_keys_to_symbols(i)[@itemkey] == item[@itemkey]
|
70
69
|
end
|
71
70
|
here << item.reject { |k, _v| %i[synckey synctype].include? k }
|
71
|
+
return unless download_item_allowed(item[@itemkey])
|
72
72
|
local.open('wb') do |io|
|
73
73
|
io.write here.map { |i| Hash.transform_keys_to_strings(i) }.to_yaml
|
74
74
|
end
|
@@ -79,7 +79,6 @@ module MrMurano
|
|
79
79
|
# for now, we'll read, modify, write
|
80
80
|
here = []
|
81
81
|
if local.exist?
|
82
|
-
# FIXME/2017-07-18: Security/YAMLLoad: Prefer using YAML.safe_load over YAML.load.
|
83
82
|
local.open('rb') { |io| here = YAML.load(io) }
|
84
83
|
here = [] if here == false
|
85
84
|
end
|
@@ -87,6 +86,7 @@ module MrMurano
|
|
87
86
|
here.delete_if do |it|
|
88
87
|
Hash.transform_keys_to_symbols(it)[key] == item[key]
|
89
88
|
end
|
89
|
+
return unless removelocal_item_allowed(item[key])
|
90
90
|
local.open('wb') do |io|
|
91
91
|
io.write here.map { |i| Hash.transform_keys_to_strings(i) }.to_yaml
|
92
92
|
end
|
@@ -109,7 +109,6 @@ module MrMurano
|
|
109
109
|
|
110
110
|
# MAYBE/2017-07-03: Do we care if there are duplicate keys in the yaml? See dup_count.
|
111
111
|
here = []
|
112
|
-
# FIXME/2017-07-18: Security/YAMLLoad: Prefer using YAML.safe_load over YAML.load.
|
113
112
|
from.open { |io| here = YAML.load(io) }
|
114
113
|
here = [] if here == false
|
115
114
|
|
@@ -152,10 +151,11 @@ module MrMurano
|
|
152
151
|
end
|
153
152
|
|
154
153
|
# @param modify Bool: True if item exists already and this is changing it
|
155
|
-
def upload(
|
154
|
+
def upload(local, _remote, _modify)
|
156
155
|
# TODO: figure out APIs for updating users.
|
157
156
|
warning %(Updating Users is not yet implemented.)
|
158
157
|
# post does work if the :password field is set.
|
158
|
+
return unless upload_item_allowed(local)
|
159
159
|
end
|
160
160
|
|
161
161
|
def synckey(item)
|
data/lib/MrMurano/Solution.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Last Modified: 2017.08.
|
1
|
+
# Last Modified: 2017.08.23 /coding: utf-8
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
# Copyright © 2016-2017 Exosite LLC.
|
@@ -96,7 +96,7 @@ module MrMurano
|
|
96
96
|
#query.push ['limit', 20]
|
97
97
|
query.push ['offset', total - remaining]
|
98
98
|
elsif remaining != 0
|
99
|
-
warning "Unexpected: negative remaining:
|
99
|
+
warning "Unexpected: negative remaining: #{fancy_ticks(total)}"
|
100
100
|
remaining = 0
|
101
101
|
end
|
102
102
|
if aggregate.nil?
|
@@ -127,6 +127,8 @@ module MrMurano
|
|
127
127
|
# the results.
|
128
128
|
#path = path || '?select=id,service'
|
129
129
|
matches = list(path)
|
130
|
+
# 2017-08-21: The only caller so far is the link command,
|
131
|
+
# which passes the Solution ID as svc_name.
|
130
132
|
matches.select { |match| match[:service] == svc_name }
|
131
133
|
end
|
132
134
|
|
@@ -240,23 +242,35 @@ module MrMurano
|
|
240
242
|
end
|
241
243
|
end
|
242
244
|
unless @sid.to_s.empty? || sid.to_s == @sid.to_s
|
243
|
-
warning
|
245
|
+
warning(
|
246
|
+
"#{type_name} ID mismatch. Server says #{fancy_ticks(sid)}, " \
|
247
|
+
"but config says #{fancy_ticks(@sid)}."
|
248
|
+
)
|
244
249
|
end
|
245
250
|
self.sid = sid
|
246
251
|
# Verify/set the name.
|
247
252
|
unless @name.to_s.empty? || @meta[:name].to_s == @name.to_s
|
248
|
-
warning
|
253
|
+
warning(
|
254
|
+
"Name mismatch. Server says #{fancy_ticks(@meta[:name])}, " \
|
255
|
+
"but config says #{fancy_ticks(@name)}."
|
256
|
+
)
|
249
257
|
end
|
250
258
|
if !@meta[:name].to_s.empty?
|
251
259
|
set_name(@meta[:name])
|
252
260
|
unless @valid_name || type == :solution
|
253
|
-
warning
|
261
|
+
warning(
|
262
|
+
"Unexpected: Server returned invalid name: #{fancy_ticks(@meta[:name])}"
|
263
|
+
)
|
254
264
|
end
|
255
265
|
elsif @meta[:domain]
|
256
266
|
# This could be a pre-ADC/pre-Murano business.
|
257
|
-
warning
|
267
|
+
warning(
|
268
|
+
"Unexpected: Server returned no name for domain: #{fancy_ticks(@meta[:domain])}"
|
269
|
+
)
|
258
270
|
else
|
259
|
-
warning
|
271
|
+
warning(
|
272
|
+
"Unexpected: Server returned no name for solution: #{fancy_ticks(@meta)}"
|
273
|
+
)
|
260
274
|
end
|
261
275
|
end
|
262
276
|
|
@@ -322,7 +336,7 @@ module MrMurano
|
|
322
336
|
if @name.to_s.empty?
|
323
337
|
''
|
324
338
|
else
|
325
|
-
|
339
|
+
fancy_ticks(@name)
|
326
340
|
end
|
327
341
|
end
|
328
342
|
|
data/lib/MrMurano/SolutionId.rb
CHANGED
@@ -1,14 +1,18 @@
|
|
1
|
-
# Last Modified: 2017.08.
|
1
|
+
# Last Modified: 2017.08.23 /coding: utf-8
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
# Copyright © 2016-2017 Exosite LLC.
|
5
5
|
# License: MIT. See LICENSE.txt.
|
6
6
|
# vim:tw=0:ts=2:sw=2:et:ai
|
7
7
|
|
8
|
+
require 'MrMurano/verbosing'
|
9
|
+
|
8
10
|
module MrMurano
|
9
11
|
module SolutionId
|
10
12
|
INVALID_SID = '-1'
|
11
|
-
UNEXPECTED_TYPE_OR_ERROR_MSG =
|
13
|
+
UNEXPECTED_TYPE_OR_ERROR_MSG = (
|
14
|
+
'Unexpected result type or error: assuming empty instead'
|
15
|
+
)
|
12
16
|
|
13
17
|
attr_reader :sid
|
14
18
|
attr_reader :valid_sid
|
@@ -41,7 +45,9 @@ module MrMurano
|
|
41
45
|
|
42
46
|
def sid=(sid)
|
43
47
|
sid = INVALID_SID if sid.nil? || !sid.is_a?(String) || sid.empty?
|
44
|
-
|
48
|
+
if sid.to_s.empty? || sid == INVALID_SID || (defined?(@sid) && sid != @sid)
|
49
|
+
@valid_sid = false
|
50
|
+
end
|
45
51
|
@sid = sid
|
46
52
|
# MAGIC_NUMBER: The 2nd element is the solution ID, e.g., solution/<sid>/...
|
47
53
|
raise "Unexpected @uriparts_sidex #{@uriparts_sidex}" unless @uriparts_sidex == 1
|
@@ -65,7 +71,7 @@ module MrMurano
|
|
65
71
|
def endpoint(_path='')
|
66
72
|
# This is hopefully just a DEV error, and not something user will ever see!
|
67
73
|
return unless @uriparts[@uriparts_sidex] == INVALID_SID
|
68
|
-
error("Solution ID missing! Invalid
|
74
|
+
error("Solution ID missing! Invalid #{MrMurano::Verbose.fancy_ticks(@solntype)}")
|
69
75
|
exit 2
|
70
76
|
end
|
71
77
|
end
|
@@ -1,16 +1,23 @@
|
|
1
|
+
# Last Modified: 2017.08.21 /coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Copyright © 2016-2017 Exosite LLC.
|
5
|
+
# License: MIT. See LICENSE.txt.
|
6
|
+
# vim:tw=0:ts=2:sw=2:et:ai
|
1
7
|
|
2
8
|
module MrMurano
|
3
9
|
class SubCmdGroupHelp
|
4
|
-
|
10
|
+
attr_reader :description
|
11
|
+
attr_reader :name
|
5
12
|
|
6
13
|
def initialize(command)
|
7
14
|
@name = command.syntax.to_s
|
8
15
|
@description = command.description.to_s
|
9
16
|
@runner = ::Commander::Runner.instance
|
10
17
|
prefix = /^#{command.name.to_s} /
|
11
|
-
cmds = @runner.instance_variable_get(:@commands).select{|n,_| n.to_s =~ prefix}
|
18
|
+
cmds = @runner.instance_variable_get(:@commands).select { |n, _| n.to_s =~ prefix }
|
12
19
|
@commands = cmds
|
13
|
-
als = @runner.instance_variable_get(:@aliases).select{|n,_| n.to_s =~ prefix}
|
20
|
+
als = @runner.instance_variable_get(:@aliases).select { |n, _| n.to_s =~ prefix }
|
14
21
|
@aliases = als
|
15
22
|
|
16
23
|
@options = {}
|
@@ -24,6 +31,8 @@ module MrMurano
|
|
24
31
|
@description
|
25
32
|
when :help
|
26
33
|
nil
|
34
|
+
# rubocop:disable Style/EmptyElse: "Redundant else-clause."
|
35
|
+
# Rubocop seems incorrect about this one.
|
27
36
|
else
|
28
37
|
nil
|
29
38
|
end
|
@@ -37,6 +46,8 @@ module MrMurano
|
|
37
46
|
@commands[name.to_s]
|
38
47
|
end
|
39
48
|
|
49
|
+
# rubocop:disable Style/AccessorMethodName
|
50
|
+
# "Do not prefix reader method names with get_."
|
40
51
|
def get_help
|
41
52
|
hf = @runner.program(:help_formatter).new(self)
|
42
53
|
pc = Commander::HelpFormatter::ProgramContext.new(self).get_binding
|
@@ -45,5 +56,3 @@ module MrMurano
|
|
45
56
|
end
|
46
57
|
end
|
47
58
|
|
48
|
-
|
49
|
-
# vim: set ai et sw=2 ts=2 :
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Last Modified: 2017.08.23 /coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Copyright © 2016-2017 Exosite LLC.
|
5
|
+
# License: MIT. See LICENSE.txt.
|
6
|
+
# vim:tw=0:ts=2:sw=2:et:ai
|
7
|
+
|
8
|
+
#require 'MrMurano/progress'
|
9
|
+
#require 'MrMurano/verbosing'
|
10
|
+
#require 'MrMurano/hash'
|
11
|
+
|
12
|
+
module MrMurano
|
13
|
+
module SyncAllowed
|
14
|
+
def sync_item_allowed(actioning, item_name)
|
15
|
+
if $cfg['tool.dry']
|
16
|
+
MrMurano::Verbose.whirly_interject do
|
17
|
+
say("--dry: Not #{actioning} item: #{fancy_ticks(item_name)}")
|
18
|
+
end
|
19
|
+
false
|
20
|
+
else
|
21
|
+
true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def remove_item_allowed(id)
|
26
|
+
sync_item_allowed('removing', id)
|
27
|
+
end
|
28
|
+
|
29
|
+
def upload_item_allowed(id)
|
30
|
+
sync_item_allowed('uploading', id)
|
31
|
+
end
|
32
|
+
|
33
|
+
def download_item_allowed(id)
|
34
|
+
sync_item_allowed('downloading', id)
|
35
|
+
end
|
36
|
+
|
37
|
+
def removelocal_item_allowed(id)
|
38
|
+
sync_item_allowed('removing-local', id)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
data/lib/MrMurano/SyncUpDown.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Last Modified: 2017.08.
|
1
|
+
# Last Modified: 2017.08.23 /coding: utf-8
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
# Copyright © 2016-2017 Exosite LLC.
|
@@ -18,6 +18,7 @@ require 'MrMurano/verbosing'
|
|
18
18
|
require 'MrMurano/hash'
|
19
19
|
#require 'MrMurano/Config'
|
20
20
|
#require 'MrMurano/ProjectFile'
|
21
|
+
require 'MrMurano/SyncAllowed'
|
21
22
|
##require 'MrMurano/SyncRoot'
|
22
23
|
|
23
24
|
module MrMurano
|
@@ -27,6 +28,8 @@ module MrMurano
|
|
27
28
|
# pulling those things.
|
28
29
|
#
|
29
30
|
module SyncUpDown
|
31
|
+
include SyncAllowed
|
32
|
+
|
30
33
|
# This is one item that can be synced.
|
31
34
|
class Item
|
32
35
|
# @return [String] The name of this item.
|
@@ -49,8 +52,6 @@ module MrMurano
|
|
49
52
|
attr_accessor :synckey
|
50
53
|
# @return [String] The syncable type.
|
51
54
|
attr_accessor :synctype
|
52
|
-
# @return [String] For device2, the event type.
|
53
|
-
attr_accessor :type
|
54
55
|
# @return [String] The updated_at time from the server is used to detect changes.
|
55
56
|
attr_accessor :updated_at
|
56
57
|
# @return [Integer] Positive if multiple conflicting files found for same item.
|
@@ -295,11 +296,10 @@ module MrMurano
|
|
295
296
|
# @param local [Pathname] Full path of where to download to
|
296
297
|
# @param item [Item] The item to download
|
297
298
|
def download(local, item)
|
298
|
-
#
|
299
|
-
#
|
300
|
-
#
|
301
|
-
#
|
302
|
-
local.dirname.mkpath
|
299
|
+
#if item[:bundled]
|
300
|
+
# warning "Not downloading into bundled item #{synckey(item)}"
|
301
|
+
# return
|
302
|
+
#end
|
303
303
|
id = item[@itemkey.to_sym]
|
304
304
|
if id.nil?
|
305
305
|
debug "!!! Missing '#{@itemkey}', using :id instead!"
|
@@ -307,9 +307,14 @@ module MrMurano
|
|
307
307
|
id = item[:id]
|
308
308
|
raise "Both #{@itemkey} and id in item are nil!" if id.nil?
|
309
309
|
end
|
310
|
+
|
311
|
+
relpath = local.relative_path_from(Pathname.pwd).to_s
|
312
|
+
return unless download_item_allowed(relpath)
|
313
|
+
|
314
|
+
local.dirname.mkpath
|
310
315
|
local.open('wb') do |io|
|
311
316
|
fetch(id) do |chunk|
|
312
|
-
io.write chunk
|
317
|
+
io.write config_vars_encode chunk
|
313
318
|
end
|
314
319
|
end
|
315
320
|
update_mtime(local, item)
|
@@ -390,6 +395,7 @@ module MrMurano
|
|
390
395
|
# @param dest [Pathname] Full path of item to be removed
|
391
396
|
# @param item [Item] Full details of item to be removed
|
392
397
|
def removelocal(dest, _item)
|
398
|
+
return unless removelocal_item_allowed(dest)
|
393
399
|
dest.unlink if dest.exist?
|
394
400
|
end
|
395
401
|
|
@@ -408,7 +414,9 @@ module MrMurano
|
|
408
414
|
end
|
409
415
|
|
410
416
|
def diff_item_write(io, merged, _local, _remote)
|
411
|
-
|
417
|
+
contents = merged[:local_path].read
|
418
|
+
contents = config_vars_decode(contents)
|
419
|
+
io << contents
|
412
420
|
end
|
413
421
|
|
414
422
|
#
|
@@ -458,33 +466,36 @@ module MrMurano
|
|
458
466
|
# Get a list of SyncUpDown::Item's, or a class derived thereof.
|
459
467
|
bitems = localitems(location)
|
460
468
|
# Use synckey for quicker merging.
|
461
|
-
# 2017-07-02: Argh. If two files have the same identity, this
|
462
|
-
# simple loop masks that there are two files with the same identity!
|
463
469
|
#bitems.each { |b| items[synckey(b)] = b }
|
464
|
-
|
470
|
+
# 2017-07-02: If two files have the same identity, the simple loop
|
471
|
+
# masks that there are two files with the same identity. So check
|
472
|
+
# first for duplicates, and then process each item.
|
473
|
+
seen = {}
|
465
474
|
bitems.each do |item|
|
466
475
|
skey = synckey(item)
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
# Don't delete the original item, so that other dupes see it.
|
477
|
-
#items.delete(skey)
|
478
|
-
msg = "Duplicate local file(s) found for ‘#{skey}’"
|
479
|
-
msg += " for ‘#{self.class.description}’" if self.class.description.to_s != ''
|
480
|
-
#msg += '!'
|
481
|
-
warning(msg)
|
476
|
+
seen[skey] = seen.key?(skey) && seen[skey] + 1 || 1
|
477
|
+
end
|
478
|
+
counts = {}
|
479
|
+
bitems.each do |item|
|
480
|
+
skey = synckey(item)
|
481
|
+
if seen[skey] > 1
|
482
|
+
if items[skey].nil?
|
483
|
+
items[skey] = MrMurano::EventHandler::EventHandlerItem.new(item)
|
484
|
+
items[skey][:dup_count] = 0
|
482
485
|
end
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
item[
|
487
|
-
|
486
|
+
counts[skey] = counts.key?(skey) && counts[skey] + 1 || 1
|
487
|
+
# Use a unique synckey so all duplicates make it in the list.
|
488
|
+
uniq_synckey = "#{skey}-#{counts[skey]}"
|
489
|
+
item[:dup_count] = counts[skey]
|
490
|
+
# This sets the alias for the output, so duplicates look unique.
|
491
|
+
item[@itemkey.to_sym] = uniq_synckey
|
492
|
+
items[uniq_synckey] = item
|
493
|
+
msg = "Duplicate definition found for #{fancy_ticks(skey)}"
|
494
|
+
if self.class.description.to_s != ''
|
495
|
+
msg += " for #{fancy_ticks(self.class.description)}"
|
496
|
+
end
|
497
|
+
warning(msg)
|
498
|
+
warning(" #{item.local_path}")
|
488
499
|
else
|
489
500
|
items[skey] = item
|
490
501
|
end
|
@@ -495,9 +506,10 @@ module MrMurano
|
|
495
506
|
# MEH/2017-07-31: This message is a little misleading on syncdown,
|
496
507
|
# e.g., in rspec ./spec/cmd_syncdown_spec.rb, one test blows away
|
497
508
|
# local directories and does a syncdown, and on stderr you'll see
|
498
|
-
# Skipping missing location
|
509
|
+
# Skipping missing location
|
510
|
+
# ‘/tmp/d20170731-3150-1f50uj4/project/specs/resources.yaml’ (Resources)
|
499
511
|
# but then later in the syncdown, that directory and file gets created.
|
500
|
-
msg = "Skipping missing location
|
512
|
+
msg = "Skipping missing location #{fancy_ticks(location)}"
|
501
513
|
unless self.class.description.to_s.empty?
|
502
514
|
msg += " (#{Inflecto.pluralize(self.class.description)})"
|
503
515
|
end
|
@@ -605,7 +617,21 @@ module MrMurano
|
|
605
617
|
pattern = pattern.gsub(%r{^\*\*\/}, '')
|
606
618
|
end
|
607
619
|
|
608
|
-
::File.fnmatch(pattern, path)
|
620
|
+
ignore = ::File.fnmatch(pattern, path)
|
621
|
+
debug "Excluded #{path}" if ignore
|
622
|
+
ignore
|
623
|
+
end
|
624
|
+
|
625
|
+
def resolve_config_var_usage!(there, local)
|
626
|
+
# pass; derived classes should implement.
|
627
|
+
end
|
628
|
+
|
629
|
+
def config_vars_decode(script)
|
630
|
+
script
|
631
|
+
end
|
632
|
+
|
633
|
+
def config_vars_encode(script)
|
634
|
+
script
|
609
635
|
end
|
610
636
|
|
611
637
|
#######################################################################
|
@@ -669,16 +695,11 @@ module MrMurano
|
|
669
695
|
|
670
696
|
def syncup_item(item, options, action, verbage)
|
671
697
|
if options[action]
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
else
|
678
|
-
MrMurano::Verbose.whirly_interject do
|
679
|
-
say("--dry: Not #{verbage.downcase} item #{item[:synckey]}")
|
680
|
-
end
|
681
|
-
end
|
698
|
+
# It's up to the callback to check and honor $cfg['tool.dry'].
|
699
|
+
prog_msg = "#{verbage.capitalize} item #{item[:synckey]}"
|
700
|
+
prog_msg += " (#{item[:synctype]})" if $cfg['tool.verbose']
|
701
|
+
sync_update_progress(prog_msg)
|
702
|
+
yield item
|
682
703
|
elsif $cfg['tool.verbose']
|
683
704
|
MrMurano::Verbose.whirly_interject do
|
684
705
|
say("--no-#{action}: Not #{verbage.downcase} item #{item[:synckey]}")
|
@@ -734,15 +755,11 @@ module MrMurano
|
|
734
755
|
|
735
756
|
def syncdown_item(item, into, options, action, verbage)
|
736
757
|
if options[action]
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
yield dest, item
|
743
|
-
else
|
744
|
-
say("--dry: Not #{verbage.downcase} item #{item[:synckey]}")
|
745
|
-
end
|
758
|
+
prog_msg = "#{verbage.capitalize} item #{item[:synckey]}"
|
759
|
+
prog_msg += " (#{item[:synctype]})" if $cfg['tool.verbose']
|
760
|
+
sync_update_progress(prog_msg)
|
761
|
+
dest = tolocalpath(into, item)
|
762
|
+
yield dest, item
|
746
763
|
elsif $cfg['tool.verbose']
|
747
764
|
say("--no-#{action}: Not #{verbage.downcase} item #{item[:synckey]}")
|
748
765
|
end
|
@@ -760,7 +777,7 @@ module MrMurano
|
|
760
777
|
tlcl = Tempfile.new([tolocalname(merged, @itemkey) + '_local_', '.lua'])
|
761
778
|
Pathname.new(tlcl.path).open('wb') do |io|
|
762
779
|
if merged.key?(:script)
|
763
|
-
io << merged[:script]
|
780
|
+
io << config_vars_decode(merged[:script])
|
764
781
|
else
|
765
782
|
# For most items, read the local file.
|
766
783
|
# For resources, it's a bit trickier.
|
@@ -864,15 +881,25 @@ module MrMurano
|
|
864
881
|
|
865
882
|
tomod, unchg = items_mods_and_chgs(options, therebox, localbox)
|
866
883
|
|
884
|
+
clash = items_cull_clashes!([toadd, todel, tomod, unchg])
|
885
|
+
|
867
886
|
if options[:unselected]
|
868
|
-
{
|
887
|
+
{
|
888
|
+
toadd: toadd,
|
889
|
+
todel: todel,
|
890
|
+
tomod: tomod,
|
891
|
+
unchg: unchg,
|
892
|
+
skipd: [],
|
893
|
+
clash: clash,
|
894
|
+
}
|
869
895
|
else
|
870
896
|
{
|
871
|
-
toadd: toadd
|
872
|
-
todel: todel
|
873
|
-
tomod: tomod
|
874
|
-
unchg: unchg
|
897
|
+
toadd: select_selected(toadd),
|
898
|
+
todel: select_selected(todel),
|
899
|
+
tomod: select_selected(tomod),
|
900
|
+
unchg: select_selected(unchg),
|
875
901
|
skipd: [],
|
902
|
+
clash: select_selected(clash),
|
876
903
|
}
|
877
904
|
end
|
878
905
|
end
|
@@ -928,7 +955,7 @@ module MrMurano
|
|
928
955
|
skip_sol = true if tested && !passed
|
929
956
|
end
|
930
957
|
return nil unless skip_sol
|
931
|
-
ret = { toadd: [], todel: [], tomod: [], unchg: [], skipd: [] }
|
958
|
+
ret = { toadd: [], todel: [], tomod: [], unchg: [], skipd: [], clash: [] }
|
932
959
|
ret[:skipd] << { synckey: self.class.description }
|
933
960
|
ret
|
934
961
|
end
|
@@ -945,8 +972,11 @@ module MrMurano
|
|
945
972
|
def items_lists(options, selected)
|
946
973
|
# Fetch arrays of items there, and items here/local.
|
947
974
|
there = list
|
948
|
-
there = _matcher(there, selected)
|
949
975
|
local = locallist(skip_warn: options[:skip_missing_warning])
|
976
|
+
|
977
|
+
resolve_config_var_usage!(there, local)
|
978
|
+
|
979
|
+
there = _matcher(there, selected)
|
950
980
|
local = _matcher(local, selected)
|
951
981
|
|
952
982
|
therebox = {}
|
@@ -960,10 +990,12 @@ module MrMurano
|
|
960
990
|
local.each do |item|
|
961
991
|
skey = synckey(item)
|
962
992
|
# 2017-07-02: Check for local duplicates.
|
963
|
-
|
993
|
+
unless item[:dup_count].nil? || item[:dup_count].zero?
|
994
|
+
skey += "-#{item[:dup_count]}"
|
995
|
+
end
|
964
996
|
item[:synckey] = skey
|
965
997
|
item[:synctype] = self.class.description
|
966
|
-
localbox[
|
998
|
+
localbox[skey] = item
|
967
999
|
end
|
968
1000
|
|
969
1001
|
# Some items are considered "undeletable", meaning if a
|
@@ -990,6 +1022,8 @@ module MrMurano
|
|
990
1022
|
unchg = []
|
991
1023
|
|
992
1024
|
(localbox.keys & therebox.keys).each do |key|
|
1025
|
+
# Skip this item if it's got duplicate conflicts.
|
1026
|
+
next if !localbox[key].is_a?(Hash) && localbox[key].dup_count == 0
|
993
1027
|
# Want 'local' to override 'there' except for itemkey.
|
994
1028
|
if options[:asdown]
|
995
1029
|
mrg = therebox[key].reject { |k, _v| k == @itemkey.to_sym }
|
@@ -1021,6 +1055,29 @@ module MrMurano
|
|
1021
1055
|
list.sort_by(&:name)
|
1022
1056
|
end
|
1023
1057
|
end
|
1058
|
+
|
1059
|
+
def select_selected(items)
|
1060
|
+
items.select { |i| i[:selected] }.map { |i| i.delete(:selected); i }
|
1061
|
+
end
|
1062
|
+
|
1063
|
+
def items_cull_clashes!(items_list)
|
1064
|
+
items_list = [items_list] unless items_list.is_a?(Array)
|
1065
|
+
clash = []
|
1066
|
+
items_list.each do |items|
|
1067
|
+
items.select! do |item|
|
1068
|
+
if item[:dup_count].nil?
|
1069
|
+
true
|
1070
|
+
elsif item[:dup_count].zero?
|
1071
|
+
# This is the control item.
|
1072
|
+
false
|
1073
|
+
else
|
1074
|
+
clash.push(item)
|
1075
|
+
false
|
1076
|
+
end
|
1077
|
+
end
|
1078
|
+
end
|
1079
|
+
clash
|
1080
|
+
end
|
1024
1081
|
end
|
1025
1082
|
end
|
1026
1083
|
|