MuranoCLI 3.0.4 → 3.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ec3cf38c4b880ec614977a7f6d95fc8b5ac69e68
4
- data.tar.gz: 41b0bf32389f080867f13df0906262881ebad5ed
3
+ metadata.gz: 73e2f9256da32058214bb3f721407d9fd480b951
4
+ data.tar.gz: 947a9c30a1632fae14e8c5e1560a52050bc0748b
5
5
  SHA512:
6
- metadata.gz: 9053361d890f83cf9037bfd811bac313b1893df69daf88a13b5dab2a744c01239c7f19fb49428b1949fb578a8d50ab11a61a3033b8d1dd65f202b706de0cd116
7
- data.tar.gz: af31c49ad0e42dc9a6e6eb4a6953b591b98b58c2547377e9b7e0e8d3ae53fad38256c1c72a699cdb8e18a6848ef489234cbf7408c8739cbc318e0a99f407a2c4
6
+ metadata.gz: 770f834abc53c64993f18fff880379b6ebb2ed24905fb662a856e66b103ac8d36cca955fa893fd9cca8a355cbbe39f08c38a71153450680fa1e8e8c5b493935f
7
+ data.tar.gz: 5f6ccacfaa8a60c09c8e551bbb17844266e5ddb7156f52b701b470c07faaff24fe5203c81b790580f8c3876f110667e4cef2530b815996a0ebda5027838ae247
@@ -7,10 +7,12 @@ report/
7
7
  .rspec_examples.txt
8
8
  Gemfile.lock
9
9
  .bundle/
10
+ tags
10
11
 
11
12
  # 2017-05-12: Testing!
12
13
  *.out
13
14
  .rake_build.out
15
+ curldebug.out
14
16
 
15
17
  rspec-*.html
16
18
  *.rspec.html
data/.trustme.sh CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/bin/bash
2
- # Last Modified: 2017.08.16
2
+ # Last Modified: 2017.09.20
3
3
  # vim:tw=0:ts=2:sw=2:et:norl:spell
4
4
 
5
5
  # WHAT: A Continuous Integration (CI) script for kicking the build
@@ -171,6 +171,19 @@ function rspec_it() {
171
171
  # business as you do when developing.
172
172
  #rspec_it
173
173
 
174
+ function ctags_it() {
175
+ annoucement "CTAGS IT"
176
+ ctags -R \
177
+ --exclude=coverage \
178
+ --exclude=docs \
179
+ --exclude=pkg \
180
+ --exclude=report \
181
+ --exclude=spec \
182
+ --verbose=yes
183
+ /bin/ls -la tags >> ${OUT_FILE}
184
+ }
185
+ ctags_it
186
+
174
187
  time_n=$(date +%s.%N)
175
188
  time_elapsed=$(echo "$time_n - $time_0" | bc -l)
176
189
  annoucement "DONE!"
data/.trustme.vim CHANGED
@@ -23,6 +23,8 @@
23
23
  " autocmd BufWritePost <buffer> silent !./.trustme.sh &
24
24
  "augroup END
25
25
 
26
+ autocmd BufRead *.rb set tags=/exo/clients/exosite/exosite-murcli/tags
27
+
26
28
  "echomsg 'Calling trustme.sh'
27
29
  silent !./.trustme.sh &
28
30
 
data/README.markdown CHANGED
@@ -1,10 +1,5 @@
1
1
  # Murano Command Line Interface (CLI)
2
2
 
3
- [![Gem
4
- Version](https://badge.fury.io/rb/MuranoCLI.svg)](https://badge.fury.io/rb/MuranoCLI)
5
- [![Build Status](https://travis-ci.org/exosite/MuranoCLI.svg?branch=master)](https://travis-ci.org/exosite/MuranoCLI)
6
- [![Inline docs](http://inch-ci.org/github/exosite/MuranoCLI.svg?branch=master)](http://inch-ci.org/github/exosite/MuranoCLI)
7
-
8
3
  Do more from the command line with [Murano](https://exosite.com/platform/).
9
4
 
10
5
  MuranoCLI interacts with Murano and makes different tasks easier.
@@ -1,4 +1,4 @@
1
- # Last Modified: 2017.08.22 /coding: utf-8
1
+ # Last Modified: 2017.09.21 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
@@ -55,6 +55,7 @@ Or set your password with `murano password set <username>`.
55
55
 
56
56
  def login_info
57
57
  warned_once = false
58
+
58
59
  if user.empty?
59
60
  prologue = 'No Murano user account found.'
60
61
  unless $cfg.prompt_if_logged_off
@@ -70,9 +71,8 @@ Or set your password with `murano password set <username>`.
70
71
  $project.refresh_user_name
71
72
  MrMurano::Verbose.whirly_unpause
72
73
  end
73
- pwd_path = $cfg.file_at('passwords', :user)
74
- pwd_file = MrMurano::Passwords.new(pwd_path)
75
- pwd_file.load
74
+
75
+ pwd_file = pwd_file_load
76
76
  user_pass = pwd_file.get(host, user)
77
77
  if user_pass.nil?
78
78
  prologue = "No Murano password found for #{user}."
@@ -85,19 +85,42 @@ Or set your password with `murano password set <username>`.
85
85
  error(%(#{prologue} #{LOGIN_NOTICE}).strip) unless warned_once
86
86
  user_pass = ask('Password: ') { |q| q.echo = '*' }
87
87
  pwd_file.set(host, user, user_pass)
88
+ pwd_file.set(host, user + '/twofactor', nil)
88
89
  pwd_file.save
89
90
  MrMurano::Verbose.whirly_unpause
91
+ else
92
+ @twofactor_token = token_twofactor_lookup(pwd_file)
90
93
  end
91
- creds = {
94
+
95
+ {
92
96
  email: user,
93
97
  password: user_pass,
94
98
  }
95
- creds
99
+ end
100
+
101
+ def pwd_file_load
102
+ pwd_path = $cfg.file_at('passwords', :user)
103
+ pwd_file = MrMurano::Passwords.new(pwd_path)
104
+ pwd_file.load
105
+ pwd_file
106
+ end
107
+
108
+ def token_twofactor_lookup(pwd_file)
109
+ twoftoken = pwd_file.lookup(host, user + '/twofactor')
110
+ if twoftoken.to_s.empty?
111
+ nil
112
+ elsif twoftoken !~ /^[a-fA-F0-9]+$/
113
+ warning "Malformed twofactor token: #{twoftoken}"
114
+ nil
115
+ else
116
+ twoftoken
117
+ end
96
118
  end
97
119
 
98
120
  # ---------------------------------------------------------------------
99
121
 
100
122
  def token
123
+ return '' if defined?(@logging_on) && @logging_on
101
124
  token_fetch if @token.to_s.empty?
102
125
  @token
103
126
  end
@@ -107,32 +130,139 @@ Or set your password with `murano password set <username>`.
107
130
  end
108
131
 
109
132
  def token_fetch
110
- # Cannot have token call token, so cannot use Http::workit.
111
- uri = endpoint('token/')
112
- request = Net::HTTP::Post.new(uri)
113
- request['User-Agent'] = "MrMurano/#{MrMurano::VERSION}"
114
- request.content_type = 'application/json'
115
- curldebug(request)
116
- #request.basic_auth(username(), password())
117
- request.body = JSON.generate(login_info)
133
+ @logging_on = true
134
+ creds = login_info
135
+ @token = nil
136
+
137
+ # If 2fa token found, verify it works.
138
+ unless @twofactor_token.nil?
139
+ get('token/' + @twofactor_token) do |request, http|
140
+ http.request(request) do |response|
141
+ if response.is_a?(Net::HTTPSuccess)
142
+ # response.body is, e.g., "{\"email\":\"xxx@yyy.zzz\",\"ttl\":172800}"
143
+ @token = @twofactor_token
144
+ end
145
+ end
146
+ end
147
+ unless @token.nil?
148
+ @logging_on = false
149
+ return
150
+ end
151
+ @twofactor_token = nil
152
+ end
118
153
 
119
154
  MrMurano::Verbose.whirly_start('Logging in...')
120
- response = http.request(request)
155
+ post('token/', creds) do |request, http|
156
+ http.request(request) do |response|
157
+ reply = JSON.parse(response.body, json_opts)
158
+ if response.is_a?(Net::HTTPSuccess)
159
+ @token = reply[:token]
160
+ elsif response.is_a?(Net::HTTPConflict) && reply[:message] == 'twofactor'
161
+ MrMurano::Verbose.whirly_interject do
162
+ # Prompt user for emailed code.
163
+ token_twofactor_fetch(creds)
164
+ end
165
+ else
166
+ showHttpError(request, response)
167
+ error 'Check to see if username and password are correct.'
168
+ unless ENV['MURANO_PASSWORD'].to_s.empty?
169
+ pwd_path = $cfg.file_at('passwords', :user)
170
+ warning "NOTE: MURANO_PASSWORD specifies the password; it was not read from #{pwd_path}"
171
+ end
172
+ end
173
+ end
174
+ end
121
175
  MrMurano::Verbose.whirly_stop
176
+ @logging_on = false
177
+ end
122
178
 
123
- case response
124
- when Net::HTTPSuccess
125
- token = JSON.parse(response.body, json_opts)
126
- @token = token[:token]
127
- else
128
- showHttpError(request, response)
129
- error 'Check to see if username and password are correct.'
130
- unless ENV['MURANO_PASSWORD'].to_s.empty?
131
- pwd_path = $cfg.file_at('passwords', :user)
132
- warning "NOTE: MURANO_PASSWORD specifies the password; it was not read from #{pwd_path}"
133
- end
134
- @token = nil
179
+ def token_twofactor_fetch(creds)
180
+ error 'Two-factor Authentication'
181
+ warning 'A verification code has been sent to your email.'
182
+ code = ask('Please enter the code here to continue: ').strip
183
+ unless code =~ /^[a-fA-F0-9]+$/
184
+ error 'Expected token to contain only numbers and hexadecimal letters.'
185
+ exit 1
186
+ end
187
+ MrMurano::Verbose.whirly_start('Verifying code...')
188
+
189
+ path = 'key/' + code
190
+
191
+ response = get(path)
192
+ # Response is, e.g., {
193
+ # purpose: "twofactor",
194
+ # status: "exists",
195
+ # email: "xxx@yyy.zzz",
196
+ # bizid: null,
197
+ # businessName: null, }
198
+ return if response.nil?
199
+
200
+ response = post(path, password: creds[:password])
201
+ # Response is, e.g., { "token": "..." }
202
+ return if response.nil?
203
+
204
+ @twofactor_token = response[:token]
205
+ pwd_file = pwd_file_load
206
+ pwd_file.set(host, user + '/twofactor', @twofactor_token)
207
+ pwd_file.save
208
+ @token = @twofactor_token
209
+ MrMurano::Verbose.whirly_stop
210
+
211
+ warning 'Please run `murano logout --token` to clear your two-factor token when finished.'
212
+ end
213
+
214
+ def logout(token_delete_only)
215
+ @logging_on = true
216
+
217
+ pwd_file = pwd_file_load
218
+ twoftoken = token_twofactor_lookup(pwd_file)
219
+
220
+ # First, delete/invalidate the remote token.
221
+ unless twoftoken.to_s.empty?
222
+ @suppress_error = true
223
+ delete('token/' + twoftoken)
224
+ # The response is nil if the token was not recognized, otherwise it's
225
+ # {}. We don't really care, since we're going to forget our copy of
226
+ # the token, anyway.
227
+ @suppress_error = false
228
+ end
229
+
230
+ net_host = verify_set('net.host')
231
+ user_name = verify_set('user.name')
232
+ if net_host && user_name
233
+ pwd_file = MrMurano::Passwords.new
234
+ pwd_file.load
235
+ pwd_file.remove(net_host, user_name) unless token_delete_only
236
+ pwd_file.remove(net_host, user_name + '/twofactor')
237
+ pwd_file.save
238
+ end
239
+
240
+ clear_from_config(net_host, user_name) unless token_delete_only
241
+
242
+ @logging_on = false
243
+ end
244
+
245
+ def clear_from_config(net_host, user_name)
246
+ user_net_host = $cfg.get('net.host', :user)
247
+ user_net_host = $cfg.get('net.host', :defaults) if user_net_host.nil?
248
+ user_user_name = $cfg.get('user.name', :user)
249
+ # Only clear user name from the user config if the net.host
250
+ # or user.name did not come from a different config, like the
251
+ # --project config.
252
+ return unless (user_net_host == net_host) && (user_user_name == user_name)
253
+ $cfg.set('user.name', nil, :user)
254
+ $cfg.set('business.id', nil, :user)
255
+ $cfg.set('business.name', nil, :user)
256
+ end
257
+
258
+ def verify_set(cfg_key)
259
+ cfg_val = $cfg.get(cfg_key)
260
+ if cfg_val.to_s.empty?
261
+ cfg_val = nil
262
+ cfg_key_q = MrMurano::Verbose.fancy_ticks(cfg_key)
263
+ MrMurano::Verbose.warning("No config key #{cfg_key_q}: no password to delete")
135
264
  end
265
+ cfg_val
136
266
  end
137
267
 
138
268
  # ---------------------------------------------------------------------
@@ -1,4 +1,4 @@
1
- # Last Modified: 2017.09.11 /coding: utf-8
1
+ # Last Modified: 2017.09.20 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
@@ -109,12 +109,7 @@ module MrMurano
109
109
 
110
110
  def self.missing_business_id_msg
111
111
  %(
112
- Missing Business ID.
113
- Call `#{MrMurano::EXE_NAME} business list` to get a list of business IDs.
114
- Set the ID temporarily using --config business.id=<ID>
115
- or add to the project config using \`#{MrMurano::EXE_NAME} config business.id <ID>\`
116
- or add to the user config using \`#{MrMurano::EXE_NAME} config business.id <ID> --user\`
117
- or set it interactively using \`#{MrMurano::EXE_NAME} init\`
112
+ business ID not specified. For hints: #{MrMurano::EXE_NAME} business --help
118
113
  ).strip
119
114
  end
120
115
 
@@ -1,4 +1,4 @@
1
- # Last Modified: 2017.08.31 /coding: utf-8
1
+ # Last Modified: 2017.09.20 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
@@ -301,7 +301,7 @@ module MrMurano
301
301
  if the_cmd.name != 'help' && !the_cmd.project_not_required && !@project_exists
302
302
  error %(The "#{the_cmd.name}" command only works in a Murano project.)
303
303
  say INVALID_PROJECT_HINT
304
- # Note that commnander-rb uses an at_exit hook, which we hack around.
304
+ # Note that commander-rb uses an at_exit hook, which we hack around.
305
305
  @runner.command_exit = 1
306
306
  false
307
307
  end
@@ -1,4 +1,4 @@
1
- # Last Modified: 2017.08.31 /coding: utf-8
1
+ # Last Modified: 2017.09.27 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
@@ -9,37 +9,44 @@ module MrMurano
9
9
  class ExchangeElement
10
10
  include HashInit
11
11
 
12
- # The meta is the element hash returned from the platform,
13
- # i.e., all of the other attrs in a Hash.
14
- attr_reader :meta
12
+ # *** BizAPI members.
15
13
 
16
- # The Exchange Element's Business ID context.
17
- attr_accessor :bizid
14
+ # These members are a copy of and ordered according to
15
+ # (the moving target known as) BizAPI. See:
16
+ #
17
+ # <bizapi>/lib/api/route/exchange/schemas/element.js::elementWithoutId
18
+ #
19
+ # But really what's returned is what's in the Mongo store, ha!
18
20
 
19
21
  # The Exchange Element's Element ID.
22
+ # [lb] tempted to say :element_id, but keep it matchy, in spite of #linter.
20
23
  attr_accessor :elementId
21
- #attr_accessor :element_id
22
24
 
23
- # The Purchase ID is nil unless the user has added/purchased this element.
24
- attr_accessor :purchaseId
25
- #attr_accessor :purchase_id
26
-
27
- # The <bizId>/exchange/ endpoint returns a list of flat dictionaries.
28
- # The <bizId>/purchase/ endpoint, on the other hand, puts the remaining
29
- # items in an object under an "element" key.
25
+ # The Exchange Element's Business ID context.
26
+ attr_accessor :bizid
30
27
 
31
- # The type is one of: download
28
+ # The type is one of: download | product | application | contactSales
29
+ # though BizAPI 'list' command also accepts: service
32
30
  attr_accessor :type
33
- #attr_accessor :action_type
34
31
 
35
32
  # The friendly, descriptive name of the Exchange Element.
36
33
  attr_accessor :name
37
34
 
38
- # FIXME/EXPLAIN: Is this what the Lua code calls the service?
39
- attr_accessor :apiServiceName
40
-
41
35
  # The image associated with the Exchange Element; not used by the CLI.
42
36
  attr_accessor :image
37
+ # Contains ancestors:
38
+ # :thumbnail
39
+ # :url
40
+ # :filename
41
+ # :color
42
+ # :type
43
+ # :size
44
+ # :detail
45
+ # :url
46
+ # :filename
47
+ # :color
48
+ # :type
49
+ # :size
43
50
 
44
51
  # The short description describing the Exchange Element.
45
52
  attr_accessor :description
@@ -47,19 +54,75 @@ module MrMurano
47
54
  # The long description describing the Exchange Element.
48
55
  attr_accessor :markdown
49
56
 
50
- # The Murano business tiers to which this element applies.
51
- # One or more of: ["free", "developer", "professional", "enterprise"]
52
- attr_accessor :tiers
57
+ # The source associated with the Exchange Element; not currently used.
58
+ attr_accessor :source
59
+ # Contains ancestors:
60
+ # :from
61
+ # - One of: service | github | attachment | url
62
+ # :name
63
+ # :url
64
+ # :token
53
65
 
54
- # Tags used to describe the Exchange Element.
66
+ # Array of tags used to describe the Exchange Element.
55
67
  attr_accessor :tags
56
68
 
69
+ # The attachment associated with 'attachment' sources; not currently used.
70
+ attr_accessor :attachment
71
+ # Contains ancestors:
72
+ # :download
73
+ # :url
74
+ # :filename
75
+ # :type
76
+ # :size
77
+
78
+ # The contact the user wrote for with the Exchange Element; not currently used.
79
+ attr_accessor :contact
80
+
81
+ # The specs the user wrote for the Exchange Element; not currently used.
82
+ attr_accessor :specs
83
+
84
+ # The active value is boolean; not currently used.
85
+ attr_accessor :active
86
+
87
+ # The access associated with the Exchange Element; not currently used.
88
+ # One of: public | private | network.
89
+ attr_accessor :access
90
+
91
+ # "approval" is not really documented. Code shows values: pending | approved.
92
+ attr_accessor :approval
93
+
94
+ # *** Values returned from BizAPI but defined specially.
95
+
96
+ # The Purchase ID is nil unless the user has added/purchased this element.
97
+ attr_accessor :purchaseId
98
+ #attr_accessor :purchase_id
99
+
100
+ # The <bizId>/exchange/ endpoint returns a list of flat dictionaries.
101
+ # The <bizId>/purchase/ endpoint, on the other hand, puts the remaining
102
+ # items in an object under an "element" key.
103
+
104
+ # FIXME/EXPLAIN: Is this what the Lua code calls the service?
105
+ attr_accessor :apiServiceName
106
+
107
+ # The Murano business tiers to which this element applies.
108
+ # Zero or more of: ["free", "developer", "professional", "enterprise"]
109
+ # NOTE: The 'tiers' values appears to be returned directly from Mongo DB.
110
+ # Specifically, BizAPI sets tiers = [] on new elements, but for global
111
+ # elements (in bootstrap/db/element.json), tiers is non-empty. [lb]
112
+ attr_accessor :tiers
113
+
57
114
  # Actions associated with the Exchange Element.
58
115
  attr_accessor :actions
59
116
  # The actions is an array with one dict element with:
60
117
  # 'url', 'type' (e.g., 'download), and 'primary' (bool).
61
118
 
62
- # MurCLI-only: Based on purchaseId and tiers, state of Element in Business.
119
+ # *** Internal Murano CLI variables (i.e., not in BizAPI).
120
+
121
+ # The meta is the Hash of the Exchange Element returned from the platform.
122
+ attr_reader :meta
123
+
124
+ # Based on purchaseId and tiers, state of Element in Business. One of:
125
+ # :available | :upgrade | :added
63
126
  attr_accessor :statusable
64
127
 
65
128
  ELEM_KEY_TRANSLATE = {
@@ -68,6 +131,7 @@ module MrMurano
68
131
  }.freeze
69
132
 
70
133
  def initialize(*hash)
134
+ @show_errors = $cfg['tool.verbose']
71
135
  hash = [hash] unless hash.is_a? Array
72
136
  camel_cased = {}
73
137
  hash.first.each do |key, val|
@@ -1,4 +1,4 @@
1
- # Last Modified: 2017.08.31 /coding: utf-8
1
+ # Last Modified: 2017.09.27 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
@@ -24,7 +24,21 @@ module MrMurano
24
24
  end
25
25
 
26
26
  def fetch_type(part)
27
- whirly_start('Fetching Elements...')
27
+ # [lb] not super happy about mixing presentation with other logic
28
+ # but this is quick and dirty.
29
+ case part
30
+ when '/element/'
31
+ qualifier = 'All'
32
+ when '/purchase/'
33
+ qualifier = 'Purchased'
34
+ else
35
+ raise 'Unexpected error'
36
+ end
37
+ whirly_start("Fetching #{qualifier} Elements...")
38
+
39
+ # FIXME/2017-09-27: Support Pagination. BizAPI accepts four settings:
40
+ # type, offset, limit, and select.
41
+ # <bizapi>/lib/api/route/exchange/schemas/element.js
28
42
  ret = get('exchange/' + bid + part) do |request, http|
29
43
  response = http.request(request)
30
44
  case response
@@ -50,14 +64,23 @@ module MrMurano
50
64
  lookp = {}
51
65
  # Get the user's Business metadata, including their Business tier.
52
66
  overview if @ometa.nil?
67
+ elems = fetch_elements(lookp)
68
+ fetch_purchased(lookp)
69
+ prepare_elements(elems, **opts)
70
+ end
71
+
72
+ def fetch_elements(lookp)
53
73
  # Fetch the list of Elements, including Added, Available, and Upgradeable.
54
74
  items = fetch_type('/element/')
55
75
  # Prepare a lookup of the Elements.
56
- elems = items.map do |meta|
76
+ items.map do |meta|
57
77
  elem = MrMurano::ExchangeElement.new(meta)
58
78
  lookp[elem.elementId] = elem
59
79
  elem
60
80
  end
81
+ end
82
+
83
+ def fetch_purchased(lookp)
61
84
  # Fetch the list of Purchased elements.
62
85
  items = fetch_type('/purchase/')
63
86
  # Update the list of all Elements to indicate which have been purchased.
@@ -67,8 +90,8 @@ module MrMurano
67
90
  elem.purchaseId = meta[:purchaseId]
68
91
  # Sanity check.
69
92
  meta[:element].each do |key, val|
70
- next if elem.send(key) == val
71
- warning(
93
+ next if verify_purchase_vs_element(elem, key, val)
94
+ verbose(
72
95
  'Unexpected: Exchange Purchase element meta differs: ' \
73
96
  "key: #{key} / elem: #{elem.send(key)} / purchase: #{val}"
74
97
  )
@@ -77,7 +100,13 @@ module MrMurano
77
100
  warning("Unexpected: No Element found for Exchange Purchase: elementId: #{meta[:elementId]}")
78
101
  end
79
102
  end
80
- prepare_elements(elems, **opts)
103
+ end
104
+
105
+ def verify_purchase_vs_element(elem, key, val)
106
+ elem.send(key) == val
107
+ rescue NoMethodError
108
+ verbose("Unexpected: Exchange Element missing key found in Purchase Element: #{key}")
109
+ true
81
110
  end
82
111
 
83
112
  def prepare_elements(elems, filter_id: nil, filter_name: nil, filter_fuzzy: nil)
@@ -1,4 +1,4 @@
1
- # Last Modified: 2017.08.23 /coding: utf-8
1
+ # Last Modified: 2017.09.20 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
@@ -69,6 +69,10 @@ module MrMurano
69
69
  ).strip
70
70
  return ENV['MR_PASSWORD']
71
71
  end
72
+ lookup(host, user)
73
+ end
74
+
75
+ def lookup(host, user)
72
76
  return nil unless @data.is_a?(Hash)
73
77
  return nil unless @data.key?(host)
74
78
  return nil unless @data[host].is_a?(Hash)
@@ -1,4 +1,4 @@
1
- # Last Modified: 2017.09.12 /coding: utf-8
1
+ # Last Modified: 2017.09.21 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
@@ -665,6 +665,8 @@ module MrMurano
665
665
  # @param options [Hash, Commander::Command::Options] Options on operation
666
666
  # @param selected [Array<String>] Filters for _matcher
667
667
  def syncup(options={}, selected=[])
668
+ return 0 unless api_id?
669
+
668
670
  options = elevate_hash(options)
669
671
  options[:asdown] = false
670
672
 
@@ -727,6 +729,8 @@ module MrMurano
727
729
  # @param options [Hash, Commander::Command::Options] Options on operation
728
730
  # @param selected [Array<String>] Filters for _matcher
729
731
  def syncdown(options={}, selected=[])
732
+ return 0 unless api_id?
733
+
730
734
  options = elevate_hash(options)
731
735
  options[:asdown] = true
732
736
  options[:skip_missing_warning] = true
@@ -1,4 +1,4 @@
1
- # Last Modified: 2017.09.11 /coding: utf-8
1
+ # Last Modified: 2017.09.20 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
@@ -20,6 +20,16 @@ command :business do |c|
20
20
  c.summary = %(About business)
21
21
  c.description = %(
22
22
  Commands for working with businesses.
23
+
24
+ If you need to set the business ID, try some of the following:
25
+
26
+ - Get a list of Business IDs: #{MrMurano::EXE_NAME} business list
27
+
28
+ - Specify the ID explictly: #{MrMurano::EXE_NAME} <cmd> --config business.id=<ID>
29
+ Add the ID to a project config: #{MrMurano::EXE_NAME} config business.id <ID>
30
+ Add the ID to the user config: #{MrMurano::EXE_NAME} config business.id <ID> --user
31
+ Setup a project interactively: #{MrMurano::EXE_NAME} init
32
+
23
33
  ).strip
24
34
  c.project_not_required = true
25
35
  c.subcmdgrouphelp = true
@@ -1,4 +1,4 @@
1
- # Last Modified: 2017.08.16 /coding: utf-8
1
+ # Last Modified: 2017.09.13 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
@@ -19,11 +19,38 @@ Set the CORS with `murano cors set`.
19
19
  ).strip
20
20
  c.project_not_required = true
21
21
 
22
+ c.example %(
23
+ Output CORS parameters in an ASCII table.
24
+ ).strip, 'murano cors'
25
+
26
+ c.example %(
27
+ Output CORS parameters as JSON.
28
+ ).strip, 'murano cors --json'
29
+
30
+ c.example %(
31
+ Output CORS parameters in Yaml.
32
+ ).strip, 'murano cors --yaml'
33
+
34
+ c.example %(
35
+ Output CORS parameters as comma-separated values.
36
+ ).strip, 'murano cors --csv'
37
+
38
+ c.example %(
39
+ Output CORS parameters pretty-printed as a Ruby Hash.
40
+ ).strip, 'murano cors --pp'
41
+
22
42
  c.action do |args, _options|
23
43
  c.verify_arg_count!(args)
24
44
  sol = MrMurano::Webservice::Cors.new
25
45
  ret = sol.fetch
26
- sol.outf ret
46
+ sol.outf(ret) do |obj, ios|
47
+ # Called if tool.outformat is 'best' or 'csv' (not 'json', 'yaml', or 'pp').
48
+ headers = obj.keys.sort
49
+ row = []
50
+ headers.each { |key| row << obj[key] }
51
+ rows = [row]
52
+ sol.tabularize({ headers: headers, rows: rows }, ios)
53
+ end
27
54
  end
28
55
  end
29
56
 
@@ -1,4 +1,4 @@
1
- # Last Modified: 2017.08.31 /coding: utf-8
1
+ # Last Modified: 2017.09.20 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
@@ -62,6 +62,7 @@ Element status:
62
62
  cmd_defaults_id_and_name(options)
63
63
 
64
64
  xchg = MrMurano::Exchange.new
65
+ xchg.must_business_id!
65
66
 
66
67
  elems, available, purchased = find_elements(xchg, options, args[0])
67
68
  if options.added.nil?
@@ -169,7 +170,7 @@ def cmd_exchange_header_and_elems(elems, options)
169
170
  # Calculate the width of each column except the last (:description).
170
171
  headers[0..-2].each do |key|
171
172
  elem_with_max = elems.max { |a, b| a.send(key).length <=> b.send(key).length }
172
- width_taken += elem_with_max.send(key).length
173
+ width_taken += elem_with_max.send(key).length unless elem_with_max.nil?
173
174
  width_taken += ' | '.length
174
175
  end
175
176
  width_taken += ' | '.length
@@ -233,6 +234,7 @@ Add an Exchange Element to your Business.
233
234
  cmd_defaults_id_and_name(options)
234
235
 
235
236
  xchg = MrMurano::Exchange.new
237
+ xchg.must_business_id!
236
238
 
237
239
  # If the user specifies filter_id, we could try to fetch that Element
238
240
  # directly (e.g., by calling exchange/<bizId>/element/<elemId>),
@@ -1,4 +1,4 @@
1
- # Last Modified: 2017.08.23 /coding: utf-8
1
+ # Last Modified: 2017.09.21 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
@@ -42,43 +42,16 @@ it will remove that user's password from the password file.
42
42
  Essentially, this command is the same as:
43
43
 
44
44
  murano password delete <username>
45
+ murano password delete <username>/twofactor
45
46
  murano config --unset --user user.name
46
47
  ).strip
47
48
  c.project_not_required = true
48
49
 
49
- c.action do |args, _options|
50
- c.verify_arg_count!(args)
51
-
52
- net_host = verify_set('net.host')
53
- user_name = verify_set('user.name')
54
- if net_host && user_name
55
- psd = MrMurano::Passwords.new
56
- psd.load
57
- psd.remove(net_host, user_name)
58
- psd.save
59
- end
60
-
61
- user_net_host = $cfg.get('net.host', :user)
62
- user_net_host = $cfg.get('net.host', :defaults) if user_net_host.nil?
63
- user_user_name = $cfg.get('user.name', :user)
64
- if (user_net_host == net_host) && (user_user_name == user_name)
65
- # Only clear user name from the user config if the net.host
66
- # or user.name did not come from a different config, like the
67
- # --project config.
68
- $cfg.set('user.name', nil, :user)
69
- $cfg.set('business.id', nil, :user)
70
- $cfg.set('business.name', nil, :user)
71
- end
72
- end
50
+ c.option '--token', 'Remove just the two-factor token'
73
51
 
74
- def verify_set(cfg_key)
75
- cfg_val = $cfg.get(cfg_key)
76
- if cfg_val.to_s.empty?
77
- cfg_val = nil
78
- cfg_key_q = MrMurano::Verbose.fancy_ticks(cfg_key)
79
- MrMurano::Verbose.warning("No config key #{cfg_key_q}: no password to delete")
80
- end
81
- cfg_val
52
+ c.action do |args, options|
53
+ c.verify_arg_count!(args)
54
+ MrMurano::Account.instance.logout(options.token)
82
55
  end
83
56
  end
84
57
 
@@ -1,4 +1,4 @@
1
- # Last Modified: 2017.08.16 /coding: utf-8
1
+ # Last Modified: 2017.09.20 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
@@ -10,15 +10,16 @@ require 'MrMurano/Solution'
10
10
 
11
11
  command :usage do |c|
12
12
  c.syntax = %(murano usage)
13
- c.summary = %(Get usage info for solution(s))
13
+ c.summary = %(Get usage info for the Application and Product)
14
14
  c.description = %(
15
- Get usage info for solution(s).
15
+ Get usage info for the Application and Product.
16
16
  ).strip
17
+ c.project_not_required = true
17
18
 
18
19
  # Add flag: --type [application|product|all].
19
20
  cmd_add_solntype_pickers(c)
20
21
 
21
- c.option '--[no-]all', 'Show usage for all Solutions in Business, not just Project'
22
+ c.option '--[no-]all', 'Show usage for all Solutions in Business'
22
23
  c.option(
23
24
  '--[no-]header', %(Output solution descriptions (default: true))
24
25
  )
data/lib/MrMurano/hash.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Last Modified: 2017.09.07 /coding: utf-8
1
+ # Last Modified: 2017.09.27 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
@@ -113,7 +113,7 @@ module HashInit
113
113
  hash.first.each do |key, val|
114
114
  if respond_to? key
115
115
  send("#{key}=", val)
116
- else
116
+ elsif defined?(@show_errors) && @show_errors
117
117
  $stderr.puts %(HashInit: missing hash key "#{key}")
118
118
  end
119
119
  end
data/lib/MrMurano/http.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Last Modified: 2017.08.24 /coding: utf-8
1
+ # Last Modified: 2017.09.20 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
@@ -108,7 +108,7 @@ module MrMurano
108
108
  # 2017-08-14: MrMurano::Account overrides the token method, and
109
109
  # it doesn't exit if no token, and then we end up here.
110
110
  ensure_token! token
111
- request['Authorization'] = 'token ' + token
111
+ request['Authorization'] = 'token ' + token unless token.to_s.empty?
112
112
  request['User-Agent'] = "MrMurano/#{MrMurano::VERSION}"
113
113
  request
114
114
  end
@@ -1,4 +1,4 @@
1
- # Last Modified: 2017.09.13 /coding: utf-8
1
+ # Last Modified: 2017.09.28 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
@@ -26,7 +26,7 @@ module MrMurano
26
26
  # '3.0.0-beta.2' is changed to '3.0.0.pre.beta.2'
27
27
  # which breaks our build (which expects the version to match herein).
28
28
  # So stick to using the '.pre.X' syntax, which ruby/gems knows.
29
- VERSION = '3.0.4'
29
+ VERSION = '3.0.5'
30
30
  EXE_NAME = File.basename($PROGRAM_NAME)
31
31
  SIGN_UP_URL = 'https://exosite.com/signup/'
32
32
  end
data/spec/Account_spec.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Last Modified: 2017.09.12 /coding: utf-8
1
+ # Last Modified: 2017.09.21 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
@@ -45,6 +45,7 @@ RSpec.describe MrMurano::Account, 'token' do
45
45
  it 'Asks for nothing' do
46
46
  $cfg['user.name'] = 'bob'
47
47
  expect(@pswd).to receive(:get).once.and_return('built')
48
+ expect(@pswd).to receive(:lookup).once.and_return(nil)
48
49
 
49
50
  ret = @acc.login_info
50
51
  expect(ret).to eq(email: 'bob', password: 'built')
@@ -56,6 +57,7 @@ RSpec.describe MrMurano::Account, 'token' do
56
57
  expect(@acc).to receive(:error).once
57
58
  expect($cfg).to receive(:set).with('user.name', 'bob', :user).once.and_call_original
58
59
  expect(@pswd).to receive(:get).once.and_return('built')
60
+ expect(@pswd).to receive(:lookup).once.and_return(nil)
59
61
 
60
62
  ret = @acc.login_info
61
63
  expect(ret).to eq(email: 'bob', password: 'built')
@@ -67,6 +69,7 @@ RSpec.describe MrMurano::Account, 'token' do
67
69
  expect(@acc).to receive(:error).once
68
70
  expect($terminal).to receive(:ask).once.and_return('dog')
69
71
  expect(@pswd).to receive(:set).once.with('bizapi.hosted.exosite.io', 'bob', 'dog')
72
+ expect(@pswd).to receive(:set).once.with('bizapi.hosted.exosite.io', 'bob/twofactor', nil)
70
73
  # 2017-07-31: login_info may exit unless the command okays prompting for the password.
71
74
  # (If we don't set this, login_info exits, which we'd want to
72
75
  # catch with
data/spec/Content_spec.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Last Modified: 2017.09.13 /coding: utf-8
1
+ # Last Modified: 2017.09.20 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
@@ -11,6 +11,14 @@ require 'MrMurano/Content'
11
11
  require 'MrMurano/SyncRoot'
12
12
  require '_workspace'
13
13
 
14
+ # rubocop:disable Style/HashSyntax
15
+ # - 'Use the new Ruby 1.9 hash syntax.'
16
+ # E.g.,
17
+ # :'x-amz-meta-name' => 'Solutionfile.json',
18
+ # because quoted string keys, e.g.,
19
+ # 'x-amz-meta-name': 'Solutionfile.json',
20
+ # are not supported in Ruby 2.0.
21
+
14
22
  RSpec.describe MrMurano::Content::Base do
15
23
  include_context 'WORKSPACE'
16
24
  before(:example) do
@@ -0,0 +1,124 @@
1
+ # Last Modified: 2017.09.28 /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 'fileutils'
9
+ require 'open3'
10
+ require 'pathname'
11
+ require 'cmd_common'
12
+
13
+ # NOTE: This file is a copy of, and subset of, cmd_syncdown_both_spec.rb.
14
+
15
+ RSpec.describe 'murano single sync', :cmd, :needs_password do
16
+ include_context 'CI_CMD'
17
+
18
+ before(:example) do
19
+ @applctn_name = rname('syncdownTestApp')
20
+ out, err, status = Open3.capture3(
21
+ capcmd('murano', 'application', 'create', @applctn_name, '--save')
22
+ )
23
+ expect(err).to eq('')
24
+ soln_id = out
25
+ expect(soln_id.chomp).to match(/^[a-zA-Z0-9]+$/)
26
+ expect(status.exitstatus).to eq(0)
27
+ end
28
+
29
+ after(:example) do
30
+ out, err, status = Open3.capture3(
31
+ capcmd('murano', 'solution', 'delete', '-y', @applctn_name)
32
+ )
33
+ expect(out).to eq('')
34
+ expect(err).to eq('')
35
+ expect(status.exitstatus).to eq(0)
36
+ end
37
+
38
+ context 'without ProjectFile' do
39
+ before(:example) do
40
+ FileUtils.cp_r(File.join(@testdir, 'spec/fixtures/syncable_content/.'), '.')
41
+ FileUtils.move('assets', 'files')
42
+ FileUtils.cp_r(File.join(@testdir, 'spec/fixtures/syncable_conflict/.'), '.')
43
+ end
44
+
45
+ it 'syncdown' do
46
+ out, err, status = Open3.capture3(capcmd('murano', 'syncup'))
47
+ out_lines = out.lines.map { |line| strip_fancy(line) }
48
+ expect(out_lines).to match_array(
49
+ [
50
+ "Adding item table_util\n",
51
+ a_string_starting_with('Updating item '),
52
+ "Updating item user_account\n",
53
+ "Adding item POST_/api/fire\n",
54
+ "Adding item PUT_/api/fire/{code}\n",
55
+ "Adding item DELETE_/api/fire/{code}\n",
56
+ "Adding item GET_/api/onfire\n",
57
+ "Adding item /icon.png\n",
58
+ "Adding item /\n",
59
+ "Adding item /js/script.js\n",
60
+ ]
61
+ )
62
+
63
+ expect(err).to eq('')
64
+ expect(status.exitstatus).to eq(0)
65
+
66
+ FileUtils.rm_r(%w[files modules routes services])
67
+ expect(Dir['**/*']).to eq([])
68
+
69
+ out, err, status = Open3.capture3(capcmd('murano', 'syncdown'))
70
+ out_lines = out.lines.map { |line| strip_fancy(line) }
71
+ expect(out_lines).to match_array(
72
+ [
73
+ "Adding item table_util\n",
74
+ # 2017-08-08: This says updating now because timer.timer is undeletable.
75
+ #"Adding item timer_timer\n",
76
+ "Updating item timer_timer\n",
77
+ "Adding item POST_/api/fire\n",
78
+ "Adding item DELETE_/api/fire/{code}\n",
79
+ "Adding item PUT_/api/fire/{code}\n",
80
+ "Adding item GET_/api/onfire\n",
81
+ "Adding item /js/script.js\n",
82
+ "Adding item /icon.png\n",
83
+ "Adding item /\n",
84
+ ]
85
+ )
86
+ expect(err).to eq('')
87
+ expect(status.exitstatus).to eq(0)
88
+
89
+ after = Dir['**/*'].sort
90
+ expect(after).to include(
91
+ 'files',
92
+ 'files/icon.png',
93
+ 'files/index.html',
94
+ 'files/js',
95
+ 'files/js/script.js',
96
+ 'modules',
97
+ 'modules/table_util.lua',
98
+ 'routes',
99
+ 'routes/api-fire-{code}.delete.lua',
100
+ 'routes/api-fire-{code}.put.lua',
101
+ 'routes/api-fire.post.lua',
102
+ 'routes/api-onfire.get.lua',
103
+ 'services',
104
+ 'services/timer_timer.lua',
105
+ )
106
+
107
+ # A status should show no differences.
108
+ out, err, status = Open3.capture3(capcmd('murano', 'status'))
109
+ expect(err).to eq('')
110
+ expect(out.lines).to match(
111
+ [
112
+ "Nothing new locally\n",
113
+ "Nothing new remotely\n",
114
+ "Nothing that differs\n",
115
+ "Items without a solution:\n",
116
+ " - R Resource\n",
117
+ " - I Interface\n",
118
+ ]
119
+ )
120
+ expect(status.exitstatus).to eq(0)
121
+ end
122
+ end
123
+ end
124
+
@@ -1,4 +1,4 @@
1
- # Last Modified: 2017.09.12 /coding: utf-8
1
+ # Last Modified: 2017.09.25 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
@@ -15,14 +15,18 @@ RSpec.describe 'murano syncdown', :cmd, :needs_password do
15
15
 
16
16
  before(:example) do
17
17
  @product_name = rname('syncdownTestPrd')
18
- out, err, status = Open3.capture3(capcmd('murano', 'product', 'create', @product_name, '--save'))
18
+ out, err, status = Open3.capture3(
19
+ capcmd('murano', 'product', 'create', @product_name, '--save')
20
+ )
19
21
  expect(err).to eq('')
20
22
  soln_id = out
21
23
  expect(soln_id.chomp).to match(/^[a-zA-Z0-9]+$/)
22
24
  expect(status.exitstatus).to eq(0)
23
25
 
24
26
  @applctn_name = rname('syncdownTestApp')
25
- out, err, status = Open3.capture3(capcmd('murano', 'application', 'create', @applctn_name, '--save'))
27
+ out, err, status = Open3.capture3(
28
+ capcmd('murano', 'application', 'create', @applctn_name, '--save')
29
+ )
26
30
  expect(err).to eq('')
27
31
  soln_id = out
28
32
  expect(soln_id.chomp).to match(/^[a-zA-Z0-9]+$/)
@@ -31,7 +35,9 @@ RSpec.describe 'murano syncdown', :cmd, :needs_password do
31
35
  out, err, status = Open3.capture3(capcmd('murano', 'assign', 'set'))
32
36
  #expect(out).to a_string_starting_with("Linked product #{@product_name}")
33
37
  olines = out.lines
34
- expect(strip_fancy(olines[0])).to eq("Linked '#{@product_name}' to '#{@applctn_name}'\n")
38
+ expect(strip_fancy(olines[0])).to eq(
39
+ "Linked '#{@product_name}' to '#{@applctn_name}'\n"
40
+ )
35
41
  expect(olines[1]).to eq("Created default event handler\n")
36
42
  expect(err).to eq('')
37
43
  expect(status.exitstatus).to eq(0)
@@ -40,12 +46,16 @@ RSpec.describe 'murano syncdown', :cmd, :needs_password do
40
46
  after(:example) do
41
47
  # VERIFY/2017-07-03: Skipping assign unset. Murano will clean up, right?
42
48
 
43
- out, err, status = Open3.capture3(capcmd('murano', 'solution', 'delete', '-y', @applctn_name))
49
+ out, err, status = Open3.capture3(
50
+ capcmd('murano', 'solution', 'delete', '-y', @applctn_name)
51
+ )
44
52
  expect(out).to eq('')
45
53
  expect(err).to eq('')
46
54
  expect(status.exitstatus).to eq(0)
47
55
 
48
- out, err, status = Open3.capture3(capcmd('murano', 'solution', 'delete', '--yes', @product_name))
56
+ out, err, status = Open3.capture3(
57
+ capcmd('murano', 'solution', 'delete', '--yes', @product_name)
58
+ )
49
59
  expect(out).to eq('')
50
60
  expect(err).to eq('')
51
61
  expect(status.exitstatus).to eq(0)
@@ -56,8 +66,10 @@ RSpec.describe 'murano syncdown', :cmd, :needs_password do
56
66
  FileUtils.cp_r(File.join(@testdir, 'spec/fixtures/syncable_content/.'), '.')
57
67
  FileUtils.move('assets', 'files')
58
68
  #FileUtils.mkpath('specs')
59
- #FileUtils.copy(File.join(@testdir, 'spec/fixtures/product_spec_files/lightbulb.yaml'),
60
- # 'specs/resources.yaml')
69
+ #FileUtils.copy(
70
+ # File.join(@testdir, 'spec/fixtures/product_spec_files/lightbulb.yaml'),
71
+ # 'specs/resources.yaml'
72
+ #)
61
73
  # 2017-07-03: So long as this command does not syncdown first, these
62
74
  # two files -- that conflict in name with what's on the platform --
63
75
  # won't be a problem (but would be if we synceddown first).
@@ -143,7 +155,8 @@ RSpec.describe 'murano syncdown', :cmd, :needs_password do
143
155
  'routes/api-fire-{code}.put.lua',
144
156
  'routes/api-fire.post.lua',
145
157
  'routes/api-onfire.get.lua',
146
- # 2017-07-03: services/ would not exist if we did not include fixtures/syncable_conflict/.
158
+ # 2017-07-03: services/ would not exist if we did not include
159
+ # fixtures/syncable_conflict/.
147
160
  'services',
148
161
  # 2017-07-13: No longer syncing device2_event; is internal to platform.
149
162
  #'services/device2_event.lua',
@@ -1,4 +1,4 @@
1
- # Last Modified: 2017.09.12 /coding: utf-8
1
+ # Last Modified: 2017.09.20 /coding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright © 2016-2017 Exosite LLC.
@@ -82,7 +82,7 @@ RSpec.describe 'murano syncup', :cmd, :needs_password do
82
82
  # Windows is insane:
83
83
  # "Adding item ........................Administrator.AppData.Local.Temp.2.d20170913-3860-pgji6g.project.modules.table_util\n"
84
84
  #expect(outl[5]).to eq("Adding item table_util\n")
85
- expect(outl[5]).to start_with("Adding item ")
85
+ expect(outl[5]).to start_with('Adding item ')
86
86
  expect(outl[5]).to end_with("table_util\n")
87
87
  #expect(outl[6]).to eq("Updating item c3juj9vnmec000000_event\n")
88
88
  # The order isn't always consistent, so just do start_with.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: MuranoCLI
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.4
4
+ version: 3.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Conrad Tadpol Tilstra
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-09-13 00:00:00.000000000 Z
11
+ date: 2017-09-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: certified
@@ -405,8 +405,8 @@ executables:
405
405
  extensions: []
406
406
  extra_rdoc_files: []
407
407
  files:
408
- - ".agignore"
409
408
  - ".gitignore"
409
+ - ".ignore"
410
410
  - ".rspec"
411
411
  - ".rubocop.yml"
412
412
  - ".travis.yml"
@@ -542,7 +542,8 @@ files:
542
542
  - spec/cmd_setting_application_spec.rb
543
543
  - spec/cmd_setting_product_spec.rb
544
544
  - spec/cmd_status_spec.rb
545
- - spec/cmd_syncdown_spec.rb
545
+ - spec/cmd_syncdown_application_spec.rb
546
+ - spec/cmd_syncdown_both_spec.rb
546
547
  - spec/cmd_syncup_spec.rb
547
548
  - spec/cmd_usage_spec.rb
548
549
  - spec/fixtures/.mrmuranorc