shopify-cli 0.9.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -4
  3. data/docs/getting-started/install/index.md +37 -1
  4. data/docs/getting-started/migrate/index.md +34 -1
  5. data/lib/project_types/extension/cli.rb +1 -1
  6. data/lib/project_types/extension/commands/build.rb +1 -1
  7. data/lib/project_types/node/forms/create.rb +3 -54
  8. data/lib/project_types/node/messages/messages.rb +3 -14
  9. data/lib/project_types/rails/cli.rb +0 -1
  10. data/lib/project_types/rails/forms/create.rb +3 -52
  11. data/lib/project_types/rails/messages/messages.rb +2 -13
  12. data/lib/project_types/script/cli.rb +2 -3
  13. data/lib/project_types/script/commands/create.rb +4 -4
  14. data/lib/project_types/script/commands/disable.rb +4 -14
  15. data/lib/project_types/script/commands/enable.rb +35 -11
  16. data/lib/project_types/script/commands/push.rb +9 -9
  17. data/lib/project_types/script/config/extension_points.yml +9 -3
  18. data/lib/project_types/script/forms/script_form.rb +5 -2
  19. data/lib/project_types/script/layers/application/create_script.rb +7 -6
  20. data/lib/project_types/script/layers/application/disable_script.rb +9 -7
  21. data/lib/project_types/script/layers/application/enable_script.rb +11 -9
  22. data/lib/project_types/script/layers/application/push_script.rb +6 -4
  23. data/lib/project_types/script/layers/infrastructure/assemblyscript_project_creator.rb +2 -2
  24. data/lib/project_types/script/layers/infrastructure/assemblyscript_task_runner.rb +2 -2
  25. data/lib/project_types/script/layers/infrastructure/errors.rb +1 -0
  26. data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +1 -1
  27. data/lib/project_types/script/layers/infrastructure/script_repository.rb +1 -1
  28. data/lib/project_types/script/layers/infrastructure/script_service.rb +2 -0
  29. data/lib/project_types/script/messages/messages.rb +16 -19
  30. data/lib/project_types/script/script_project.rb +8 -4
  31. data/lib/project_types/script/templates/ts/as-pect.config.js +6 -0
  32. data/lib/project_types/script/ui/error_handler.rb +4 -0
  33. data/lib/project_types/script/ui/printing_spinner.rb +75 -0
  34. data/lib/shopify-cli/admin_api.rb +1 -2
  35. data/lib/shopify-cli/admin_api/populate_resource_command.rb +10 -1
  36. data/lib/shopify-cli/admin_api/schema.rb +20 -8
  37. data/lib/shopify-cli/command.rb +2 -5
  38. data/lib/shopify-cli/commands.rb +1 -0
  39. data/lib/shopify-cli/commands/config.rb +44 -0
  40. data/lib/shopify-cli/commands/connect.rb +18 -11
  41. data/lib/shopify-cli/commands/create.rb +1 -1
  42. data/lib/shopify-cli/commands/help.rb +1 -1
  43. data/lib/shopify-cli/commands/system.rb +1 -1
  44. data/lib/shopify-cli/context.rb +10 -1
  45. data/lib/shopify-cli/core.rb +0 -1
  46. data/lib/shopify-cli/core/entry_point.rb +6 -0
  47. data/lib/shopify-cli/core/finalize.rb +13 -0
  48. data/lib/shopify-cli/feature.rb +97 -0
  49. data/lib/shopify-cli/messages/messages.rb +45 -2
  50. data/lib/shopify-cli/partners_api/organizations.rb +7 -7
  51. data/lib/shopify-cli/project_type.rb +2 -5
  52. data/lib/shopify-cli/tasks.rb +1 -0
  53. data/lib/shopify-cli/tasks/ensure_env.rb +0 -1
  54. data/lib/shopify-cli/tasks/select_org_and_shop.rb +77 -0
  55. data/lib/shopify-cli/tasks/update_dashboard_urls.rb +4 -3
  56. data/lib/shopify-cli/tunnel.rb +33 -9
  57. data/lib/shopify-cli/version.rb +1 -1
  58. data/lib/shopify_cli.rb +1 -0
  59. metadata +7 -4
  60. data/lib/project_types/script/forms/enable.rb +0 -24
  61. data/lib/project_types/script/forms/push.rb +0 -19
@@ -24,6 +24,15 @@ module ShopifyCli
24
24
  MESSAGE
25
25
  development_store_select: "Which development store would you like to use?",
26
26
  cli_yml_saved: ".shopify-cli.yml saved to project root",
27
+
28
+ no_apps: 'You have no apps to connect to, creating a new app.',
29
+ app_name: "App name",
30
+ app_type: {
31
+ select: "What type of app are you building?",
32
+ select_public: "Public: An app built for a wide merchant audience.",
33
+ select_custom: "Custom: An app custom built for a single client.",
34
+ selected: "App type {{green:%s}}",
35
+ },
27
36
  },
28
37
 
29
38
  context: {
@@ -52,6 +61,19 @@ module ShopifyCli
52
61
  saved: "%s saved to project root",
53
62
  },
54
63
 
64
+ config: {
65
+ help: <<~HELP,
66
+ Change configuration of how the CLI operates
67
+ Usage: {{command:%s config [ feature ] [ feature_name ] }}
68
+ HELP
69
+ feature: {
70
+ enabled: "{{v}} feature {{green:%s}} was enabled",
71
+ disabled: "{{v}} feature {{green:%s}} was disabled",
72
+ is_enabled: "{{v}} feature {{green:%s}} is enabled",
73
+ is_disabled: "{{v}} feature {{green:%s}} is disabled",
74
+ },
75
+ },
76
+
55
77
  git: {
56
78
  error: {
57
79
  directory_exists: "Project directory already exists. Please create a project with a new name.",
@@ -251,6 +273,20 @@ module ShopifyCli
251
273
  "{{x}} error: For authentication issues, run {{command:%s logout}} to clear invalid credentials",
252
274
  update_prompt: "Do you want to update your application url?",
253
275
  },
276
+ select_org_and_shop: {
277
+ authentication_issue: "For authentication issues, run {{command:%s logout}} to clear invalid credentials",
278
+ create_store: "Visit {{underline:https://partners.shopify.com/%s/stores}} to create one",
279
+ development_store: "Using development store {{green:%s}}",
280
+ development_store_select: "Select a development store",
281
+ error: {
282
+ no_development_stores: "{{x}} No Development Stores available.",
283
+ no_organizations: "No partner organizations available.",
284
+ organization_not_found: "Cannot find a partner organization with that ID",
285
+ partners_notice: "Please visit https://partners.shopify.com/ to create a partners account",
286
+ },
287
+ organization: "Partner organization {{green:%s (%s)}}",
288
+ organization_select: "Select partner organization",
289
+ },
254
290
  },
255
291
 
256
292
  tunnel: {
@@ -259,10 +295,17 @@ module ShopifyCli
259
295
  url_fetch_failure: "Unable to fetch external url",
260
296
  },
261
297
 
262
- stopped: "{{green:x}} ngrok tunnel stopped",
263
298
  not_running: "{{green:x}} ngrok tunnel not running",
264
- start_with_account: "{{v}} ngrok tunnel running at {{underline:%s}}, with account %s",
299
+ signup_suggestion: <<~MESSAGE,
300
+ {{*}} To avoid tunnels that timeout, it is recommended to signup for a free ngrok
301
+ account at {{underline:https://ngrok.com/signup}}. After you signup, install your
302
+ personalized authorization token using {{command:%s tunnel auth <token>}}.
303
+ MESSAGE
265
304
  start: "{{v}} ngrok tunnel running at {{underline:%s}}",
305
+ start_with_account: "{{v}} ngrok tunnel running at {{underline:%s}}, with account %s",
306
+ stopped: "{{green:x}} ngrok tunnel stopped",
307
+ timed_out: "{{x}} ngrok tunnel has timed out, restarting ...",
308
+ will_timeout: "{{*}} This tunnel will timeout in {{red:%s}}",
266
309
  },
267
310
 
268
311
  version: {
@@ -4,25 +4,25 @@ module ShopifyCli
4
4
  class << self
5
5
  def fetch_all(ctx)
6
6
  resp = PartnersAPI.query(ctx, 'all_organizations')
7
- resp['data']['organizations']['nodes'].map do |org|
8
- org['stores'] = org['stores']['nodes']
7
+ (resp.dig('data', 'organizations', 'nodes') || []).map do |org|
8
+ org['stores'] = (org.dig('stores', 'nodes') || [])
9
9
  org
10
10
  end
11
11
  end
12
12
 
13
13
  def fetch(ctx, id:)
14
14
  resp = PartnersAPI.query(ctx, 'find_organization', id: id)
15
- org = resp['data']['organizations']['nodes'].first
15
+ org = resp.dig('data', 'organizations', 'nodes').first
16
16
  return nil if org.nil?
17
- org['stores'] = org['stores']['nodes']
17
+ org['stores'] = (org.dig('stores', 'nodes') || [])
18
18
  org
19
19
  end
20
20
 
21
21
  def fetch_with_app(ctx)
22
22
  resp = PartnersAPI.query(ctx, 'all_orgs_with_apps')
23
- resp['data']['organizations']['nodes'].map do |org|
24
- org['stores'] = org['stores']['nodes']
25
- org['apps'] = org['apps']['nodes']
23
+ (resp.dig('data', 'organizations', 'nodes') || []).map do |org|
24
+ org['stores'] = (org.dig('stores', 'nodes') || [])
25
+ org['apps'] = (org.dig('apps', 'nodes') || [])
26
26
  org
27
27
  end
28
28
  end
@@ -1,11 +1,12 @@
1
1
  module ShopifyCli
2
2
  class ProjectType
3
+ extend Feature::Set
4
+
3
5
  class << self
4
6
  attr_accessor :project_type,
5
7
  :project_name,
6
8
  :project_creator_command_class,
7
9
  :project_load_shallow
8
- attr_reader :hidden
9
10
 
10
11
  def repository
11
12
  @repository ||= []
@@ -53,10 +54,6 @@ module ShopifyCli
53
54
  const_get(@project_creator_command_class)
54
55
  end
55
56
 
56
- def hidden_project_type
57
- @hidden = true
58
- end
59
-
60
57
  def register_command(const, cmd)
61
58
  return if project_load_shallow
62
59
  Context.new.abort(
@@ -27,6 +27,7 @@ module ShopifyCli
27
27
  register :EnsureEnv, :ensure_env, 'shopify-cli/tasks/ensure_env'
28
28
  register :EnsureLoopbackURL, :ensure_loopback_url, 'shopify-cli/tasks/ensure_loopback_url'
29
29
  register :EnsureDevStore, :ensure_dev_store, 'shopify-cli/tasks/ensure_dev_store'
30
+ register :SelectOrgAndShop, :select_org_and_shop, 'shopify-cli/tasks/select_org_and_shop'
30
31
  register :UpdateDashboardURLS, :update_dashboard_urls, 'shopify-cli/tasks/update_dashboard_urls'
31
32
  end
32
33
  end
@@ -14,7 +14,6 @@ module ShopifyCli
14
14
  api_key = CLI::UI.ask(@ctx.message('core.tasks.ensure_env.api_key_question'))
15
15
  api_secret = CLI::UI.ask(@ctx.message('core.tasks.ensure_env.api_secret_key_question'))
16
16
  shop = CLI::UI.ask(@ctx.message('core.tasks.ensure_env.development_store_question'))
17
-
18
17
  shop.gsub!(/https?\:\/\//, '')
19
18
 
20
19
  env = Resources::EnvFile.new(
@@ -0,0 +1,77 @@
1
+ require 'shopify_cli'
2
+
3
+ module ShopifyCli
4
+ module Tasks
5
+ class SelectOrgAndShop < ShopifyCli::Task
6
+ attr_reader :ctx
7
+
8
+ def call(ctx, organization_id: nil, shop_domain: nil)
9
+ @ctx = ctx
10
+ return response(organization_id.to_i, shop_domain) unless organization_id.nil? || shop_domain.nil?
11
+ org = get_organization(organization_id)
12
+ shop_domain ||= get_shop_domain(org)
13
+ response(org["id"].to_i, shop_domain)
14
+ end
15
+
16
+ private
17
+
18
+ def response(organization_id, shop_domain)
19
+ {
20
+ organization_id: organization_id,
21
+ shop_domain: shop_domain,
22
+ }
23
+ end
24
+
25
+ def organizations
26
+ @organizations ||= ShopifyCli::PartnersAPI::Organizations.fetch_all(ctx)
27
+ end
28
+
29
+ def get_organization(organization_id)
30
+ @organization ||= if !organization_id.nil?
31
+ org = ShopifyCli::PartnersAPI::Organizations.fetch(ctx, id: organization_id)
32
+ if org.nil?
33
+ ctx.puts(ctx.message('core.tasks.select_org_and_shop.error.authentication_issue', ShopifyCli::TOOL_NAME))
34
+ ctx.abort(ctx.message('core.tasks.select_org_and_shop.error.organization_not_found'))
35
+ end
36
+ org
37
+ elsif organizations.count == 0
38
+ ctx.puts(ctx.message('core.tasks.select_org_and_shop.error.partners_notice'))
39
+ ctx.puts(ctx.message('core.tasks.select_org_and_shop.authentication_issue', ShopifyCli::TOOL_NAME))
40
+ ctx.abort(ctx.message('core.tasks.select_org_and_shop.error.no_organizations'))
41
+ elsif organizations.count == 1
42
+ org = organizations.first
43
+ ctx.puts(ctx.message('core.tasks.select_org_and_shop.organization', org['businessName'], org['id']))
44
+ org
45
+ else
46
+ org_id = CLI::UI::Prompt.ask(ctx.message('core.tasks.select_org_and_shop.organization_select')) do |handler|
47
+ organizations.each do |o|
48
+ handler.option(ctx.message('core.partners_api.org_name_and_id', o['businessName'], o['id'])) { o['id'] }
49
+ end
50
+ end
51
+ organizations.find { |o| o['id'] == org_id }
52
+ end
53
+ end
54
+
55
+ def get_shop_domain(organization)
56
+ valid_stores = organization['stores'].select do |store|
57
+ store['transferDisabled'] == true || store['convertableToPartnerTest'] == true
58
+ end
59
+
60
+ if valid_stores.count == 0
61
+ ctx.puts(ctx.message('core.tasks.select_org_and_shop.error.no_development_stores'))
62
+ ctx.puts(ctx.message('core.tasks.select_org_and_shop.create_store', organization['id']))
63
+ ctx.puts(ctx.message('core.tasks.select_org_and_shop.authentication_issue', ShopifyCli::TOOL_NAME))
64
+ elsif valid_stores.count == 1
65
+ domain = valid_stores.first['shopDomain']
66
+ ctx.puts(ctx.message('core.tasks.select_org_and_shop.development_store', domain))
67
+ domain
68
+ else
69
+ CLI::UI::Prompt.ask(
70
+ ctx.message('core.tasks.select_org_and_shop.development_store_select'),
71
+ options: valid_stores.map { |s| s['shopDomain'] }
72
+ )
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -28,16 +28,17 @@ module ShopifyCli
28
28
  end
29
29
 
30
30
  def construct_redirect_urls(urls, new_url, callback_url)
31
- urls.map do |url|
31
+ new_urls = urls.map do |url|
32
32
  if (match = url.match(NGROK_REGEX))
33
33
  "#{new_url}#{match[2]}"
34
34
  else
35
35
  url
36
36
  end
37
37
  end
38
- if urls.grep(/#{new_url}#{callback_url}/).empty?
39
- urls.push("#{new_url}#{callback_url}")
38
+ if new_urls.grep(/#{new_url}#{callback_url}/).empty?
39
+ new_urls.push("#{new_url}#{callback_url}")
40
40
  end
41
+ new_urls.uniq
41
42
  end
42
43
  end
43
44
  end
@@ -62,14 +62,19 @@ module ShopifyCli
62
62
  #
63
63
  def start(ctx, port: PORT)
64
64
  install(ctx)
65
- process = ShopifyCli::ProcessSupervision.start(:ngrok, ngrok_command(port))
66
- log = fetch_url(ctx, process.log_path)
67
- if log.account
68
- ctx.puts(ctx.message('core.tunnel.start_with_account', log.url, log.account))
65
+ url, account, seconds_remaining = start_ngrok(ctx, port)
66
+ if account
67
+ ctx.puts(ctx.message('core.tunnel.start_with_account', url, account))
69
68
  else
70
- ctx.puts(ctx.message('core.tunnel.start', log.url))
69
+ if seconds_remaining <= 0
70
+ ctx.puts(ctx.message('core.tunnel.timed_out'))
71
+ url, _account, seconds_remaining = restart_ngrok(ctx, port)
72
+ end
73
+ ctx.puts(ctx.message('core.tunnel.start', url))
74
+ ctx.puts(ctx.message('core.tunnel.will_timeout', seconds_to_hm(seconds_remaining)))
75
+ ctx.puts(ctx.message('core.tunnel.signup_suggestion', ShopifyCli::TOOL_NAME))
71
76
  end
72
- log.url
77
+ url
73
78
  end
74
79
 
75
80
  ##
@@ -138,13 +143,30 @@ module ShopifyCli
138
143
  end
139
144
 
140
145
  def ngrok_command(port)
141
- "exec #{File.join(ShopifyCli::CACHE_DIR, 'ngrok')} http -log=stdout -log-level=debug #{port}"
146
+ "exec #{File.join(ShopifyCli::CACHE_DIR, 'ngrok')} http -inspect=false -log=stdout -log-level=debug #{port}"
142
147
  end
143
148
 
149
+ def seconds_to_hm(seconds)
150
+ format("%d hours %d minutes", seconds / 3600, seconds / 60 % 60)
151
+ end
152
+
153
+ def start_ngrok(ctx, port)
154
+ process = ShopifyCli::ProcessSupervision.start(:ngrok, ngrok_command(port))
155
+ log = fetch_url(ctx, process.log_path)
156
+ seconds_remaining = (process.time.to_i + log.timeout) - Time.now.to_i
157
+ [log.url, log.account, seconds_remaining]
158
+ end
159
+
160
+ def restart_ngrok(ctx, port)
161
+ unless ShopifyCli::ProcessSupervision.stop(:ngrok)
162
+ ctx.abort(ctx.message('core.tunnel.error.stop'))
163
+ end
164
+ start_ngrok(ctx, port)
165
+ end
144
166
  class LogParser # :nodoc:
145
167
  TIMEOUT = 10
146
168
 
147
- attr_reader :url, :account
169
+ attr_reader :url, :account, :timeout
148
170
 
149
171
  def initialize(log_path)
150
172
  @log_path = log_path
@@ -173,7 +195,9 @@ module ShopifyCli
173
195
  end
174
196
 
175
197
  def parse_account
176
- @account, _ = @log.match(/AccountName:([\w\s]+) SessionDuration/)&.captures
198
+ account, timeout, _ = @log.match(/AccountName:([\w\s]*) SessionDuration:([\d]+) PlanName/)&.captures
199
+ @account = account&.empty? ? nil : account
200
+ @timeout = timeout&.empty? ? 0 : timeout.to_i
177
201
  end
178
202
 
179
203
  def error
@@ -1,3 +1,3 @@
1
1
  module ShopifyCli
2
- VERSION = '0.9.3'
2
+ VERSION = '1.0.4'
3
3
  end
@@ -105,6 +105,7 @@ module ShopifyCli
105
105
  autoload :Context, 'shopify-cli/context'
106
106
  autoload :Core, 'shopify-cli/core'
107
107
  autoload :DB, 'shopify-cli/db'
108
+ autoload :Feature, 'shopify-cli/feature'
108
109
  autoload :Form, 'shopify-cli/form'
109
110
  autoload :Git, 'shopify-cli/git'
110
111
  autoload :Helpers, 'shopify-cli/helpers'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shopify-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.3
4
+ version: 1.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-14 00:00:00.000000000 Z
11
+ date: 2020-08-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -216,8 +216,6 @@ files:
216
216
  - lib/project_types/script/config/extension_points.yml
217
217
  - lib/project_types/script/errors.rb
218
218
  - lib/project_types/script/forms/create.rb
219
- - lib/project_types/script/forms/enable.rb
220
- - lib/project_types/script/forms/push.rb
221
219
  - lib/project_types/script/forms/script_form.rb
222
220
  - lib/project_types/script/graphql/app_script_update_or_create.graphql
223
221
  - lib/project_types/script/graphql/script_service_proxy.graphql
@@ -249,6 +247,7 @@ files:
249
247
  - lib/project_types/script/templates/ts/as-pect.config.js
250
248
  - lib/project_types/script/templates/ts/as-pect.d.ts
251
249
  - lib/project_types/script/ui/error_handler.rb
250
+ - lib/project_types/script/ui/printing_spinner.rb
252
251
  - lib/project_types/script/ui/strict_spinner.rb
253
252
  - lib/rubygems_plugin.rb
254
253
  - lib/shopify-cli/admin_api.rb
@@ -257,6 +256,7 @@ files:
257
256
  - lib/shopify-cli/api.rb
258
257
  - lib/shopify-cli/command.rb
259
258
  - lib/shopify-cli/commands.rb
259
+ - lib/shopify-cli/commands/config.rb
260
260
  - lib/shopify-cli/commands/connect.rb
261
261
  - lib/shopify-cli/commands/create.rb
262
262
  - lib/shopify-cli/commands/help.rb
@@ -267,9 +267,11 @@ files:
267
267
  - lib/shopify-cli/core.rb
268
268
  - lib/shopify-cli/core/entry_point.rb
269
269
  - lib/shopify-cli/core/executor.rb
270
+ - lib/shopify-cli/core/finalize.rb
270
271
  - lib/shopify-cli/core/help_resolver.rb
271
272
  - lib/shopify-cli/core/monorail.rb
272
273
  - lib/shopify-cli/db.rb
274
+ - lib/shopify-cli/feature.rb
273
275
  - lib/shopify-cli/form.rb
274
276
  - lib/shopify-cli/git.rb
275
277
  - lib/shopify-cli/helpers.rb
@@ -296,6 +298,7 @@ files:
296
298
  - lib/shopify-cli/tasks/ensure_dev_store.rb
297
299
  - lib/shopify-cli/tasks/ensure_env.rb
298
300
  - lib/shopify-cli/tasks/ensure_loopback_url.rb
301
+ - lib/shopify-cli/tasks/select_org_and_shop.rb
299
302
  - lib/shopify-cli/tasks/update_dashboard_urls.rb
300
303
  - lib/shopify-cli/tunnel.rb
301
304
  - lib/shopify-cli/version.rb
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Script
4
- module Forms
5
- class Enable < ScriptForm
6
- flag_arguments :api_key, :shop_domain
7
-
8
- def ask
9
- self.api_key ||= ask_api_key
10
- self.shop_domain ||= ask_shop_domain
11
- end
12
-
13
- private
14
-
15
- def ask_api_key
16
- ask_app_api_key(organization['apps'], message: ctx.message('script.forms.enable.ask_app_api_key'))
17
- end
18
-
19
- def ask_shop_domain
20
- super(organization, message: ctx.message('script.forms.enable.ask_shop_domain'))
21
- end
22
- end
23
- end
24
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Script
4
- module Forms
5
- class Push < ScriptForm
6
- flag_arguments :api_key, :force
7
-
8
- def ask
9
- self.api_key ||= ask_api_key
10
- end
11
-
12
- private
13
-
14
- def ask_api_key
15
- ask_app_api_key(organization['apps'])
16
- end
17
- end
18
- end
19
- end