shopify-cli 1.1.2 → 1.4.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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/.github/PULL_REQUEST_TEMPLATE.md +8 -0
  3. data/.github/workflows/release.yml +62 -0
  4. data/.gitignore +0 -1
  5. data/.rubocop.yml +41 -2
  6. data/.rubocop_todo.yml +24 -0
  7. data/.travis.yml +1 -0
  8. data/CHANGELOG.md +24 -0
  9. data/Gemfile +2 -2
  10. data/Gemfile.lock +35 -36
  11. data/RELEASING.md +24 -26
  12. data/docs/core/index.md +16 -0
  13. data/lib/project_types/extension/cli.rb +6 -1
  14. data/lib/project_types/extension/commands/register.rb +1 -1
  15. data/lib/project_types/extension/features/argo/admin.rb +20 -0
  16. data/lib/project_types/extension/features/argo/base.rb +129 -0
  17. data/lib/project_types/extension/features/argo/checkout.rb +20 -0
  18. data/lib/project_types/extension/features/argo_config.rb +60 -0
  19. data/lib/project_types/extension/messages/messages.rb +11 -2
  20. data/lib/project_types/extension/models/type.rb +4 -0
  21. data/lib/project_types/extension/models/types/checkout_post_purchase.rb +6 -3
  22. data/lib/project_types/extension/models/types/product_subscription.rb +24 -0
  23. data/lib/project_types/node/commands/create.rb +6 -1
  24. data/lib/project_types/rails/commands/create.rb +6 -1
  25. data/lib/project_types/script/config/extension_points.yml +8 -8
  26. data/lib/project_types/script/layers/infrastructure/assemblyscript_project_creator.rb +2 -2
  27. data/lib/project_types/script/layers/infrastructure/assemblyscript_task_runner.rb +36 -1
  28. data/lib/project_types/script/layers/infrastructure/errors.rb +7 -0
  29. data/lib/project_types/script/messages/messages.rb +4 -3
  30. data/lib/project_types/script/ui/error_handler.rb +13 -5
  31. data/lib/shopify-cli/api.rb +5 -9
  32. data/lib/shopify-cli/commands/config.rb +33 -1
  33. data/lib/shopify-cli/commands/system.rb +9 -0
  34. data/lib/shopify-cli/core/entry_point.rb +1 -1
  35. data/lib/shopify-cli/core/monorail.rb +4 -3
  36. data/lib/shopify-cli/http_request.rb +15 -0
  37. data/lib/shopify-cli/js_system.rb +22 -5
  38. data/lib/shopify-cli/messages/messages.rb +30 -10
  39. data/lib/shopify-cli/project.rb +3 -3
  40. data/lib/shopify-cli/shopifolk.rb +67 -0
  41. data/lib/shopify-cli/tasks/create_api_client.rb +4 -2
  42. data/lib/shopify-cli/tasks/select_org_and_shop.rb +1 -0
  43. data/lib/shopify-cli/tunnel.rb +1 -1
  44. data/lib/shopify-cli/version.rb +1 -1
  45. data/lib/shopify_cli.rb +1 -0
  46. metadata +11 -4
  47. data/lib/project_types/extension/features/argo.rb +0 -48
  48. data/lib/project_types/extension/models/types/subscription_management.rb +0 -20
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ module Extension
3
+ module Features
4
+ module Argo
5
+ class Admin < Base
6
+ GIT_TEMPLATE = 'https://github.com/Shopify/argo-admin-template.git'
7
+ RENDERER_PACKAGE = '@shopify/argo-admin'
8
+ private_constant :GIT_TEMPLATE, :RENDERER_PACKAGE
9
+
10
+ def git_template
11
+ GIT_TEMPLATE
12
+ end
13
+
14
+ def renderer_package_name
15
+ RENDERER_PACKAGE
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+ require 'base64'
3
+ require 'shopify_cli'
4
+ require 'semantic/semantic'
5
+
6
+ module Extension
7
+ module Features
8
+ module Argo
9
+ class Base
10
+ include SmartProperties
11
+
12
+ SCRIPT_PATH = %w(build main.js).freeze
13
+
14
+ NPM_LIST_COMMAND = %w(list).freeze
15
+ YARN_LIST_COMMAND = %w(list --pattern).freeze
16
+ NPM_LIST_PARAMETERS = %w(--prod).freeze
17
+ YARN_LIST_PARAMETERS = %w(--production).freeze
18
+ private_constant :NPM_LIST_COMMAND, :YARN_LIST_COMMAND, :NPM_LIST_PARAMETERS, :YARN_LIST_PARAMETERS
19
+
20
+ YARN_INSTALL_COMMAND = %w(install).freeze
21
+ YARN_INSTALL_PARAMETERS = %w(--silent).freeze
22
+ YARN_RUN_COMMAND = %w(run).freeze
23
+ YARN_RUN_SCRIPT_NAME = %w(build).freeze
24
+ private_constant :YARN_INSTALL_COMMAND, :YARN_INSTALL_PARAMETERS, :YARN_RUN_COMMAND, :YARN_RUN_SCRIPT_NAME
25
+
26
+ def create(directory_name, identifier, context)
27
+ Features::ArgoSetup.new(git_template: git_template).call(directory_name, identifier, context)
28
+ end
29
+
30
+ def config(context)
31
+ js_system = ShopifyCli::JsSystem.new(ctx: context)
32
+ if js_system.package_manager == 'yarn'
33
+ run_yarn_install(context, js_system)
34
+ run_yarn_run_script(context, js_system)
35
+ end
36
+ filepath = File.join(context.root, SCRIPT_PATH)
37
+ context.abort(context.message('features.argo.missing_file_error')) unless File.exist?(filepath)
38
+ begin
39
+ {
40
+ renderer_version: extract_argo_renderer_version(context),
41
+ serialized_script: Base64.strict_encode64(File.read(filepath).chomp),
42
+ }
43
+ rescue StandardError
44
+ context.abort(context.message('features.argo.script_prepare_error'))
45
+ end
46
+ end
47
+
48
+ def git_template
49
+ raise NotImplementedError, "'#{__method__}' must be implemented for #{self.class}"
50
+ end
51
+
52
+ def renderer_package_name
53
+ # The renderer_package_name is used as a regex pattern to
54
+ # find a match in the output of yarn or npm list command.
55
+ # Use the full package name as it appears in the template without targeting a version.
56
+ # Examples: "@shopify/some-renderer-package", "argo-renderer-package"
57
+
58
+ raise NotImplementedError, "'#{__method__}' must be implemented for #{self.class}"
59
+ end
60
+
61
+ private
62
+
63
+ def extract_argo_renderer_version(context)
64
+ result = run_list_command(context)
65
+ found_version = find_version_number(context, result)
66
+ context.abort(
67
+ context.message('features.argo.dependencies.argo_renderer_package_invalid_version_error')
68
+ ) if found_version.nil?
69
+ ::Semantic::Version.new(found_version).to_s
70
+ rescue ArgumentError
71
+ context.abort(
72
+ context.message('features.argo.dependencies.argo_renderer_package_invalid_version_error')
73
+ )
74
+ end
75
+
76
+ def find_version_number(context, result)
77
+ packages = result.to_json.split('\n')
78
+ found_package = packages.find do |package|
79
+ package.match(/#{renderer_package_name}@/)
80
+ end
81
+ if found_package.nil?
82
+ error = "'#{renderer_package_name}' not found."
83
+ context.abort(
84
+ context.message('features.argo.dependencies.argo_missing_renderer_package_error', error)
85
+ )
86
+ end
87
+ found_package.split('@')[2]&.strip
88
+ end
89
+
90
+ def run_list_command(context)
91
+ js_system = ShopifyCli::JsSystem.new(ctx: context)
92
+ result, error, status = js_system.call(
93
+ yarn: YARN_LIST_COMMAND + [renderer_package_name] + YARN_LIST_PARAMETERS,
94
+ npm: NPM_LIST_COMMAND + [renderer_package_name] + NPM_LIST_PARAMETERS,
95
+ capture_response: true
96
+ )
97
+ context.abort(
98
+ context.message('features.argo.dependencies.argo_missing_renderer_package_error', error)
99
+ ) unless status.success?
100
+ result
101
+ end
102
+
103
+ def run_yarn_install(context, js_system)
104
+ _result, error, status = js_system.call(
105
+ yarn: YARN_INSTALL_COMMAND + YARN_INSTALL_PARAMETERS,
106
+ npm: [],
107
+ capture_response: true
108
+ )
109
+
110
+ context.abort(
111
+ context.message('features.argo.dependencies.yarn_install_error', error)
112
+ ) unless status.success?
113
+ end
114
+
115
+ def run_yarn_run_script(context, js_system)
116
+ _result, error, status = js_system.call(
117
+ yarn: YARN_RUN_COMMAND + YARN_RUN_SCRIPT_NAME,
118
+ npm: [],
119
+ capture_response: true
120
+ )
121
+
122
+ context.abort(
123
+ context.message('features.argo.dependencies.yarn_run_script_error', error)
124
+ ) unless status.success?
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ module Extension
3
+ module Features
4
+ module Argo
5
+ class Checkout < Base
6
+ GIT_TEMPLATE = 'https://github.com/Shopify/argo-checkout-template.git'
7
+ RENDERER_PACKAGE = '@shopify/argo-checkout'
8
+ private_constant :GIT_TEMPLATE, :RENDERER_PACKAGE
9
+
10
+ def git_template
11
+ GIT_TEMPLATE
12
+ end
13
+
14
+ def renderer_package_name
15
+ RENDERER_PACKAGE
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Extension
4
+ module Features
5
+ class ArgoConfig
6
+ CONFIG_FILE_NAME = 'extension.config.yml'
7
+
8
+ class << self
9
+ def parse_yaml(context, permitted_keys = [])
10
+ file_name = File.join(context.root, CONFIG_FILE_NAME)
11
+
12
+ return {} unless File.size?(file_name)
13
+
14
+ require 'yaml' # takes 20ms, so deferred as late as possible.
15
+ begin
16
+ config = YAML.load_file(file_name)
17
+
18
+ # `YAML.load_file` returns nil if the file is not empty
19
+ # but does not contain any parsable yml data, e.g. only comments
20
+ # We consider this valid
21
+ return {} if config.nil?
22
+
23
+ unless config.is_a?(Hash)
24
+ raise ShopifyCli::Abort, ShopifyCli::Context.message('core.yaml.error.not_hash', CONFIG_FILE_NAME)
25
+ end
26
+
27
+ config.transform_keys!(&:to_sym)
28
+ assert_valid_config(config, permitted_keys) unless permitted_keys.empty?
29
+
30
+ config
31
+ rescue Psych::SyntaxError => e
32
+ raise(
33
+ ShopifyCli::Abort,
34
+ ShopifyCli::Context.message('core.yaml.error.invalid', CONFIG_FILE_NAME, e.message)
35
+ )
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def assert_valid_config(config, permitted_keys)
42
+ unpermitted_keys = config.keys.select do |k|
43
+ !permitted_keys.include?(k)
44
+ end
45
+
46
+ unless unpermitted_keys.empty?
47
+ raise(
48
+ ShopifyCli::Abort,
49
+ ShopifyCli::Context.message(
50
+ 'features.argo.config.unpermitted_keys',
51
+ CONFIG_FILE_NAME,
52
+ unpermitted_keys.map { |k| "\n- #{k}" }.join
53
+ )
54
+ )
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -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
@@ -40,7 +40,7 @@ module Node
40
40
  scopes: 'write_products,write_customers,write_draft_orders',
41
41
  ).write(@ctx)
42
42
 
43
- partners_url = "https://partners.shopify.com/#{form.organization_id}/apps/#{api_client['id']}"
43
+ partners_url = "#{partners_endpoint}/#{form.organization_id}/apps/#{api_client['id']}"
44
44
 
45
45
  @ctx.puts(@ctx.message('node.create.info.created', form.title, partners_url))
46
46
  @ctx.puts(@ctx.message('node.create.info.serve', form.name, ShopifyCli::TOOL_NAME))
@@ -112,6 +112,11 @@ module Node
112
112
  @ctx.debug(e)
113
113
  end
114
114
  end
115
+
116
+ def partners_endpoint
117
+ return 'https://partners.myshopify.io' if @ctx.getenv(ShopifyCli::PartnersAPI::LOCAL_DEBUG)
118
+ 'https://partners.shopify.com'
119
+ end
115
120
  end
116
121
  end
117
122
  end
@@ -55,7 +55,7 @@ module Rails
55
55
  scopes: 'write_products,write_customers,write_draft_orders',
56
56
  ).write(@ctx)
57
57
 
58
- partners_url = "https://partners.shopify.com/#{form.organization_id}/apps/#{api_client['id']}"
58
+ partners_url = "#{partners_endpoint}/#{form.organization_id}/apps/#{api_client['id']}"
59
59
 
60
60
  @ctx.puts(@ctx.message('rails.create.info.created', form.title, partners_url))
61
61
  @ctx.puts(@ctx.message('rails.create.info.serve', form.name, ShopifyCli::TOOL_NAME))
@@ -172,6 +172,11 @@ module Rails
172
172
  def install_gem(name, version = nil)
173
173
  Gem.install(@ctx, name, version)
174
174
  end
175
+
176
+ def partners_endpoint
177
+ return 'https://partners.myshopify.io' if @ctx.getenv(ShopifyCli::PartnersAPI::LOCAL_DEBUG)
178
+ 'https://partners.shopify.com'
179
+ end
175
180
  end
176
181
  end
177
182
  end
@@ -1,24 +1,24 @@
1
1
  discount:
2
2
  assemblyscript:
3
3
  package: "@shopify/extension-point-as-discount"
4
- version: "^0.2.10"
5
- sdk-version: "^6.0.0"
4
+ version: "^0.3.0"
5
+ sdk-version: "^7.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.12"
11
- sdk-version: "^6.0.0"
10
+ version: "^0.2.0"
11
+ sdk-version: "^7.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.8"
17
- sdk-version: "^6.0.0"
16
+ version: "^0.5.0"
17
+ sdk-version: "^7.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.10"
23
- sdk-version: "^6.0.0"
22
+ version: "^0.3.0"
23
+ sdk-version: "^7.0.0"
24
24
  toolchain-version: "^1.1.0"
@@ -86,8 +86,8 @@ module Script
86
86
  "@shopify/scripts-toolchain-as": "#{extension_point.sdks[:ts].toolchain_version}",
87
87
  "#{extension_point.sdks[:ts].package}": "#{extension_point.sdks[:ts].version}",
88
88
  "@as-pect/cli": "4.0.0",
89
- "as-wasi": "^0.2.0",
90
- "assemblyscript": "^0.12.0"
89
+ "as-wasi": "^0.2.1",
90
+ "assemblyscript": "^0.14.0"
91
91
  },
92
92
  "scripts": {
93
93
  "test": "asp --config test/as-pect.config.js --summary --verbose"
@@ -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