shopify-cli 1.1.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CONTRIBUTING.md +1 -1
  3. data/CHANGELOG.md +20 -0
  4. data/docs/core/index.md +16 -0
  5. data/docs/getting-started/index.md +3 -2
  6. data/docs/getting-started/install/index.md +55 -9
  7. data/docs/getting-started/uninstall/index.md +1 -1
  8. data/docs/getting-started/upgrade/index.md +8 -4
  9. data/lib/project_types/extension/cli.rb +6 -1
  10. data/lib/project_types/extension/commands/register.rb +1 -1
  11. data/lib/project_types/extension/features/argo/admin.rb +20 -0
  12. data/lib/project_types/extension/features/argo/base.rb +129 -0
  13. data/lib/project_types/extension/features/argo/checkout.rb +20 -0
  14. data/lib/project_types/extension/features/argo_config.rb +60 -0
  15. data/lib/project_types/extension/messages/messages.rb +11 -2
  16. data/lib/project_types/extension/models/type.rb +4 -0
  17. data/lib/project_types/extension/models/types/checkout_post_purchase.rb +6 -3
  18. data/lib/project_types/extension/models/types/product_subscription.rb +24 -0
  19. data/lib/project_types/node/commands/generate/billing.rb +1 -0
  20. data/lib/project_types/node/commands/generate/page.rb +1 -0
  21. data/lib/project_types/node/commands/generate/webhook.rb +1 -0
  22. data/lib/project_types/node/commands/serve.rb +5 -5
  23. data/lib/project_types/node/messages/messages.rb +4 -1
  24. data/lib/project_types/rails/commands/create.rb +4 -1
  25. data/lib/project_types/rails/commands/serve.rb +5 -5
  26. data/lib/project_types/rails/messages/messages.rb +5 -1
  27. data/lib/project_types/script/config/extension_points.yml +4 -4
  28. data/lib/project_types/script/layers/infrastructure/assemblyscript_task_runner.rb +36 -1
  29. data/lib/project_types/script/layers/infrastructure/errors.rb +7 -0
  30. data/lib/project_types/script/layers/infrastructure/script_service.rb +6 -2
  31. data/lib/project_types/script/messages/messages.rb +12 -37
  32. data/lib/project_types/script/ui/error_handler.rb +13 -5
  33. data/lib/shopify-cli/commands/config.rb +33 -1
  34. data/lib/shopify-cli/context.rb +40 -0
  35. data/lib/shopify-cli/core/entry_point.rb +3 -0
  36. data/lib/shopify-cli/git.rb +1 -1
  37. data/lib/shopify-cli/heroku.rb +1 -1
  38. data/lib/shopify-cli/js_system.rb +22 -5
  39. data/lib/shopify-cli/messages/messages.rb +39 -11
  40. data/lib/shopify-cli/project.rb +3 -3
  41. data/lib/shopify-cli/tunnel.rb +11 -2
  42. data/lib/shopify-cli/version.rb +1 -1
  43. metadata +7 -4
  44. data/lib/project_types/extension/features/argo.rb +0 -48
  45. data/lib/project_types/extension/models/types/subscription_management.rb +0 -20
@@ -95,6 +95,15 @@ module Extension
95
95
  node_not_installed: 'Node must be installed to create this extension.',
96
96
  version_too_low: 'Your node version %s does not meet the minimum required version %s',
97
97
  },
98
+ argo_missing_renderer_package_error: '%s Install the missing package and try again.',
99
+ argo_renderer_package_invalid_version_error: <<~MESSAGE,
100
+ The renderer package version is not a valid SemVer Version (http://semver.org)
101
+ MESSAGE
102
+ yarn_install_error: "Something went wrong while running 'yarn install'. %s.",
103
+ yarn_run_script_error: 'Something went wrong while running script. %s.',
104
+ },
105
+ config: {
106
+ unpermitted_keys: '`%s` contains the following unpermitted keys: %s',
98
107
  },
99
108
  },
100
109
  },
@@ -109,8 +118,8 @@ module Extension
109
118
  }
110
119
 
111
120
  TYPES = {
112
- subscription_management: {
113
- name: 'Subscription Management',
121
+ product_subscription: {
122
+ name: 'Product Subscription',
114
123
  tagline: '(limit 1 per app)',
115
124
  overrides: {
116
125
  register: {
@@ -38,6 +38,10 @@ module Extension
38
38
  self.class::IDENTIFIER
39
39
  end
40
40
 
41
+ def graphql_identifier
42
+ identifier
43
+ end
44
+
41
45
  def name
42
46
  message('name')
43
47
  end
@@ -6,13 +6,16 @@ module Extension
6
6
  module Types
7
7
  class CheckoutPostPurchase < Models::Type
8
8
  IDENTIFIER = 'CHECKOUT_POST_PURCHASE'
9
-
9
+ PERMITTED_CONFIG_KEYS = [:metafields]
10
10
  def create(directory_name, context)
11
- Features::Argo.checkout.create(directory_name, IDENTIFIER, context)
11
+ Features::Argo::Checkout.new.create(directory_name, IDENTIFIER, context)
12
12
  end
13
13
 
14
14
  def config(context)
15
- Features::Argo.checkout.config(context)
15
+ {
16
+ **Features::ArgoConfig.parse_yaml(context, PERMITTED_CONFIG_KEYS),
17
+ **Features::Argo::Checkout.new.config(context),
18
+ }
16
19
  end
17
20
  end
18
21
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ require 'base64'
3
+
4
+ module Extension
5
+ module Models
6
+ module Types
7
+ class ProductSubscription < Models::Type
8
+ IDENTIFIER = 'PRODUCT_SUBSCRIPTION'
9
+
10
+ def graphql_identifier
11
+ 'SUBSCRIPTION_MANAGEMENT'
12
+ end
13
+
14
+ def create(directory_name, context)
15
+ Features::Argo::Admin.new.create(directory_name, IDENTIFIER, context)
16
+ end
17
+
18
+ def config(context)
19
+ Features::Argo::Admin.new.config(context)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -19,6 +19,7 @@ module Node
19
19
  end
20
20
  billing_type_name = BILLING_TYPES.key(selected_type)
21
21
  selected_type[0] = File.join(ShopifyCli::Project.current.directory, selected_type[0])
22
+ selected_type[0] = "\"#{selected_type[0]}\""
22
23
  selected_type = selected_type.join(' ')
23
24
 
24
25
  spin_group = CLI::UI::SpinGroup.new
@@ -39,6 +39,7 @@ module Node
39
39
  end
40
40
  page_type_name = PAGE_TYPES.key(selected_type)
41
41
  selected_type[0] = File.join(ShopifyCli::Project.current.directory, selected_type[0])
42
+ selected_type[0] = "\"#{selected_type[0]}\""
42
43
  selected_type = selected_type.join(' ')
43
44
 
44
45
  spin_group = CLI::UI::SpinGroup.new
@@ -17,6 +17,7 @@ module Node
17
17
  end
18
18
 
19
19
  generate_path = File.join(ShopifyCli::Project.current.directory, "node_modules/.bin/generate-node-app")
20
+ generate_path = "\"#{generate_path}\""
20
21
 
21
22
  spin_group = CLI::UI::SpinGroup.new
22
23
  spin_group.add(@ctx.message('node.generate.webhook.generating', selected_type)) do |spinner|
@@ -20,12 +20,12 @@ module Node
20
20
  url: url,
21
21
  callback_url: "/auth/callback",
22
22
  )
23
- if @ctx.mac? && project.env.shop
24
- @ctx.puts(@ctx.message('node.serve.open_info', project.env.shop))
25
- @ctx.on_siginfo do
26
- @ctx.open_url!("#{project.env.host}/auth?shop=#{project.env.shop}")
27
- end
23
+
24
+ if project.env.shop
25
+ project_url = "#{project.env.host}/auth?shop=#{project.env.shop}"
26
+ @ctx.puts("\n" + @ctx.message('node.serve.open_info', project_url) + "\n")
28
27
  end
28
+
29
29
  CLI::UI::Frame.open(@ctx.message('node.serve.running_server')) do
30
30
  env = project.env.to_h
31
31
  env['PORT'] = ShopifyCli::Tunnel::PORT.to_s
@@ -202,7 +202,10 @@ module Node
202
202
  host_must_be_https: "HOST must be a HTTPS url.",
203
203
  },
204
204
 
205
- open_info: "{{*}} Press {{yellow: Control-T}} to open this project in {{green:%s}} ",
205
+ open_info: <<~MESSAGE,
206
+ {{*}} To install and start using your app, open this URL in your browser:
207
+ {{green:%s}}
208
+ MESSAGE
206
209
  running_server: "Running server...",
207
210
  },
208
211
 
@@ -114,6 +114,9 @@ module Rails
114
114
  @ctx.abort(@ctx.message('rails.create.error.install_failure', 'bundler ~>2.0')) unless
115
115
  install_gem('bundler', '~>2.0')
116
116
 
117
+ full_path = File.join(@ctx.root, name)
118
+ @ctx.abort(@ctx.message('rails.create.error.dir_exists', name)) if Dir.exist?(full_path)
119
+
117
120
  CLI::UI::Frame.open(@ctx.message('rails.create.generating_app', name)) do
118
121
  new_command = %w(rails new)
119
122
  new_command += DEFAULT_RAILS_FLAGS
@@ -124,7 +127,7 @@ module Rails
124
127
  syscall(new_command)
125
128
  end
126
129
 
127
- @ctx.root = File.join(@ctx.root, name)
130
+ @ctx.root = full_path
128
131
 
129
132
  File.open(File.join(@ctx.root, '.gitignore'), 'a') { |f| f.write('.env') }
130
133
 
@@ -20,12 +20,12 @@ module Rails
20
20
  url: url,
21
21
  callback_url: "/auth/shopify/callback",
22
22
  )
23
- if @ctx.mac? && project.env.shop
24
- @ctx.puts(@ctx.message('rails.serve.open_info', project.env.shop))
25
- @ctx.on_siginfo do
26
- @ctx.open_url!("#{project.env.host}/login?shop=#{project.env.shop}")
27
- end
23
+
24
+ if project.env.shop
25
+ project_url = "#{project.env.host}/login?shop=#{project.env.shop}"
26
+ @ctx.puts("\n" + @ctx.message('rails.serve.open_info', project_url) + "\n")
28
27
  end
28
+
29
29
  CLI::UI::Frame.open(@ctx.message('rails.serve.running_server')) do
30
30
  env = ShopifyCli::Project.current.env.to_h
31
31
  env.delete('HOST')
@@ -36,6 +36,7 @@ module Rails
36
36
  See {{underline:https://github.com/Shopify/shopify-app-cli/blob/master/docs/installing-ruby.md}}
37
37
  for our recommended method of installing ruby.
38
38
  MSG
39
+ dir_exists: "Project directory %s already exists. Please use a different name.",
39
40
  install_failure: "Error installing %s gem",
40
41
  node_required: "node is required to create a rails project. Download at https://nodejs.org/en/download.",
41
42
  node_version_failure: "Failed to get the current node version. Please make sure it is installed as " \
@@ -219,7 +220,10 @@ module Rails
219
220
  host_must_be_https: "{{red:HOST must be a HTTPS url.}}",
220
221
  },
221
222
 
222
- open_info: "{{*}} Press {{yellow: Control-T}} to open this project in {{green:%s}} ",
223
+ open_info: <<~MESSAGE,
224
+ {{*}} To install and start using your app, open this URL in your browser:
225
+ {{green:%s}}
226
+ MESSAGE
223
227
  running_server: "Running server...",
224
228
  },
225
229
 
@@ -1,24 +1,24 @@
1
1
  discount:
2
2
  assemblyscript:
3
3
  package: "@shopify/extension-point-as-discount"
4
- version: "^0.2.6"
4
+ version: "^0.2.10"
5
5
  sdk-version: "^6.0.0"
6
6
  toolchain-version: "^1.1.0"
7
7
  unit_limit_per_order:
8
8
  assemblyscript:
9
9
  package: "@shopify/extension-point-as-unit-limit-per-order"
10
- version: "^0.1.8"
10
+ version: "^0.1.12"
11
11
  sdk-version: "^6.0.0"
12
12
  toolchain-version: "^1.1.0"
13
13
  payment_filter:
14
14
  assemblyscript:
15
15
  package: "@shopify/extension-point-as-payment-filter"
16
- version: "^0.2.4"
16
+ version: "^0.2.8"
17
17
  sdk-version: "^6.0.0"
18
18
  toolchain-version: "^1.1.0"
19
19
  shipping_filter:
20
20
  assemblyscript:
21
21
  package: "@shopify/extension-point-as-shipping-filter"
22
- version: "^0.2.6"
22
+ version: "^0.2.10"
23
23
  sdk-version: "^6.0.0"
24
24
  toolchain-version: "^1.1.0"
@@ -34,7 +34,9 @@ module Script
34
34
 
35
35
  def dependencies_installed?
36
36
  # Assuming if node_modules folder exist at root of script folder, all deps are installed
37
- ctx.dir_exist?("node_modules")
37
+ return false unless ctx.dir_exist?("node_modules")
38
+ check_if_ep_dependencies_up_to_date!
39
+ true
38
40
  end
39
41
 
40
42
  private
@@ -58,6 +60,39 @@ module Script
58
60
  def bytecode
59
61
  File.read(format(BYTECODE_FILE, name: script_name))
60
62
  end
63
+
64
+ def check_if_ep_dependencies_up_to_date!
65
+ return true if ENV['SHOPIFY_CLI_SCRIPTS_IGNORE_OUTDATED']
66
+
67
+ # ignore exit code since it will not be 0 unless every package is up to date which they probably won't be
68
+ out, _ = ctx.capture2e("npm", "outdated", "--json", "--depth", "0")
69
+ parsed_outdated_check = JSON.parse(out)
70
+ outdated_ep_packages = parsed_outdated_check
71
+ .select { |package_name, _| package_name.start_with?('@shopify/extension-point-as-') }
72
+ .select { |_, version_info| !package_is_up_to_date?(version_info) }
73
+ .keys
74
+ raise Errors::PackagesOutdatedError.new(outdated_ep_packages),
75
+ "NPM packages out of date: #{outdated_ep_packages.join(', ')}" unless outdated_ep_packages.empty?
76
+ end
77
+
78
+ def package_is_up_to_date?(version_info)
79
+ require 'semantic/semantic'
80
+ current_version = version_info['current']
81
+ latest_version = version_info['latest']
82
+
83
+ # making an assumption that the script developer knows what they're doing if they're not referencing a
84
+ # semver version
85
+ begin
86
+ current_version = ::Semantic::Version.new(current_version)
87
+ latest_version = ::Semantic::Version.new(latest_version)
88
+ rescue ArgumentError
89
+ return true
90
+ end
91
+
92
+ return false if current_version.major < latest_version.major
93
+ return false if latest_version.major == 0 && current_version.minor < latest_version.minor
94
+ true
95
+ end
61
96
  end
62
97
  end
63
98
  end
@@ -34,6 +34,13 @@ module Script
34
34
  class ShopScriptConflictError < ScriptProjectError; end
35
35
  class ShopScriptUndefinedError < ScriptProjectError; end
36
36
  class TaskRunnerNotFoundError < ScriptProjectError; end
37
+ class PackagesOutdatedError < ScriptProjectError
38
+ attr_reader :outdated_packages
39
+ def initialize(outdated_packages)
40
+ super("EP packages are outdated and need to be updated: #{outdated_packages.join(', ')}")
41
+ @outdated_packages = outdated_packages
42
+ end
43
+ end
37
44
  end
38
45
  end
39
46
  end
@@ -49,7 +49,7 @@ module Script
49
49
  resp_hash = script_service_request(
50
50
  query_name: query_name,
51
51
  api_key: api_key,
52
- shop_domain: shop_domain,
52
+ shop_domain: format_shop_domain(shop_domain),
53
53
  variables: variables,
54
54
  )
55
55
  user_errors = resp_hash["data"]["shopScriptUpdateOrCreate"]["userErrors"]
@@ -76,7 +76,7 @@ module Script
76
76
  resp_hash = script_service_request(
77
77
  query_name: query_name,
78
78
  api_key: api_key,
79
- shop_domain: shop_domain,
79
+ shop_domain: format_shop_domain(shop_domain),
80
80
  variables: variables,
81
81
  )
82
82
  user_errors = resp_hash["data"]["shopScriptDelete"]["userErrors"]
@@ -91,6 +91,10 @@ module Script
91
91
 
92
92
  private
93
93
 
94
+ def format_shop_domain(shop_domain)
95
+ shop_domain.delete_suffix("/")
96
+ end
97
+
94
98
  class ScriptServiceAPI < ShopifyCli::API
95
99
  property(:api_key, accepts: String)
96
100
  property(:shop_id, accepts: Integer)
@@ -29,7 +29,7 @@ module Script
29
29
  no_existing_orgs_cause: "You don't have any partner organizations.",
30
30
  no_existing_orgs_help: "Please visit https://partners.shopify.com/ to create a partners account.",
31
31
 
32
- no_existing_stores_cause: "You don't have any development stores.",
32
+ no_existing_stores_cause: "You don't have any stores.",
33
33
  no_existing_stores_help: "Visit https://partners.shopify.com/%{organization_id}/stores/ to create one.",
34
34
 
35
35
  project_exists_cause: "Directory with the same name as the script already exists.",
@@ -43,11 +43,9 @@ module Script
43
43
 
44
44
  script_not_found_cause: "Couldn't find script %s for extension point %s",
45
45
 
46
- app_not_installed_cause: "App not installed on development store.",
46
+ app_not_installed_cause: "App not installed on store.",
47
47
 
48
- app_script_not_pushed_help: "Push the script and then try this command again.",
49
-
50
- app_script_undefined_help: "Push script to app.",
48
+ app_script_not_pushed_help: "Script isn't on the app. Run {{command:shopify push}}, and then try again.",
51
49
 
52
50
  build_error_cause: "Something went wrong while building the script.",
53
51
  build_error_help: "Correct the errors and try again.",
@@ -70,7 +68,10 @@ module Script
70
68
  "on this extension point.",
71
69
  shop_script_conflict_help: "Disable that script or uninstall that app and try again.",
72
70
 
73
- shop_script_undefined_cause: "Script is already turned off in development store.",
71
+ shop_script_undefined_cause: "Script is already turned off in store.",
72
+
73
+ packages_outdated_cause: "The following npm packages are out of date: %s.",
74
+ packages_outdated_help: "Update them by running {{cyan:npm install --save-dev %s}}.",
74
75
  },
75
76
 
76
77
  create: {
@@ -100,7 +101,7 @@ module Script
100
101
  HELP
101
102
 
102
103
  error: {
103
- operation_failed: "Script not pushed.",
104
+ operation_failed: "Couldn't push script to app.",
104
105
  },
105
106
 
106
107
  script_pushed: "{{v}} Script pushed to app (API key: %{api_key}).",
@@ -108,21 +109,20 @@ module Script
108
109
 
109
110
  disable: {
110
111
  help: <<~HELP,
111
- Turn off script in development store.
112
+ Turn off script in store.
112
113
  Usage: {{command:%s disable}}
113
114
  HELP
114
115
 
115
116
  error: {
116
117
  operation_failed: "Can't disable script.",
117
- not_pushed_to_app: "Can't disable the script because it hasn't been pushed to the app.",
118
118
  },
119
119
 
120
- script_disabled: "{{v}} Script disabled. Script is turned off in development store.",
120
+ script_disabled: "{{v}} Script disabled. Script is turned off in store.",
121
121
  },
122
122
 
123
123
  enable: {
124
124
  help: <<~HELP,
125
- Turn on script in development store.
125
+ Turn on script in store.
126
126
  Usage: {{command:%s enable}}
127
127
  Options:
128
128
  {{command:--config_props='name1:value1, name2:value2'}} Optional. Define the configuration of your script by passing individual name and value pairs. If used with --config_file, then matching values in --config_props will override those set in the file.
@@ -135,11 +135,10 @@ module Script
135
135
 
136
136
  error: {
137
137
  operation_failed: "Can't enable script.",
138
- not_pushed_to_app: "Can't enable the script because it hasn't been pushed to the app.",
139
138
  },
140
139
 
141
140
  script_enabled: "{{v}} Script enabled. %{type} script %{title} in app (API key: %{api_key}) "\
142
- "is turned on in development store {{green:%{shop_domain}}}",
141
+ "is turned on in store {{green:%{shop_domain}}}",
143
142
  },
144
143
 
145
144
  project_deps: {
@@ -149,35 +148,11 @@ module Script
149
148
  installed: "Missing dependencies installed",
150
149
  },
151
150
 
152
- test: {
153
- help: <<~HELP,
154
- Runs unit tests on your script.
155
- Usage: {{command:%s test}}
156
- HELP
157
-
158
- error: {
159
- operation_failed: "Tests didn't run or they ran with failures.",
160
- },
161
-
162
- running: "Running tests",
163
- success: "{{v}} Tests finished.",
164
- },
165
-
166
151
  forms: {
167
152
  create: {
168
153
  select_extension_point: "Which extension point do you want to use?",
169
154
  script_name: "Script Name",
170
155
  },
171
- script_form: {
172
- ask_app_api_key_default: "Which app do you want this script to belong to?",
173
- ask_shop_domain_default: "Select a development store",
174
- fetching_organizations: "Fetching partner organizations",
175
- fetched_organizations: "Fetched partner organizations",
176
- select_organization: "Select partner organization.",
177
- using_app: "Using app {{green:%{title} (%{api_key})}}.",
178
- using_development_store: "Using development store {{green:%{domain}}}",
179
- using_organization: "Partner organization {{green:%s}}.",
180
- },
181
156
  },
182
157
 
183
158
  application: {
@@ -96,14 +96,11 @@ module Script
96
96
  {
97
97
  cause_of_error: ShopifyCli::Context.message('script.error.app_not_installed_cause'),
98
98
  }
99
- when Layers::Infrastructure::Errors::AppScriptNotPushedError
99
+ when Layers::Infrastructure::Errors::AppScriptNotPushedError,
100
+ Layers::Infrastructure::Errors::AppScriptUndefinedError
100
101
  {
101
102
  cause_of_error: ShopifyCli::Context.message('script.error.app_script_not_pushed_help'),
102
103
  }
103
- when Layers::Infrastructure::Errors::AppScriptUndefinedError
104
- {
105
- help_suggestion: ShopifyCli::Context.message('script.error.app_script_undefined_help'),
106
- }
107
104
  when Layers::Infrastructure::Errors::BuildError
108
105
  {
109
106
  cause_of_error: ShopifyCli::Context.message('script.error.build_error_cause'),
@@ -142,6 +139,17 @@ module Script
142
139
  {
143
140
  cause_of_error: ShopifyCli::Context.message('script.error.shop_script_undefined_cause'),
144
141
  }
142
+ when Layers::Infrastructure::Errors::PackagesOutdatedError
143
+ {
144
+ cause_of_error: ShopifyCli::Context.message(
145
+ 'script.error.packages_outdated_cause',
146
+ e.outdated_packages.join(', ')
147
+ ),
148
+ help_suggestion: ShopifyCli::Context.message(
149
+ 'script.error.packages_outdated_help',
150
+ e.outdated_packages.collect { |package| "#{package}@latest" }.join(' ')
151
+ ),
152
+ }
145
153
  end
146
154
  end
147
155
  end