shopify-cli 1.1.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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