MuranoCLI 3.0.0 → 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|