shopify-cli 1.7.1 → 1.11.0

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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/.github/PULL_REQUEST_TEMPLATE.md +1 -0
  3. data/.github/workflows/build.yml +28 -0
  4. data/.rubocop_todo.yml +15 -2
  5. data/CHANGELOG.md +25 -0
  6. data/Gemfile.lock +15 -15
  7. data/README.md +2 -1
  8. data/dev.yml +3 -0
  9. data/lib/docgen/markdown.rb +1 -1
  10. data/lib/graphql/extension_create.graphql +17 -2
  11. data/lib/graphql/fetch_specifications.graphql +14 -0
  12. data/lib/project_types/extension/cli.rb +8 -10
  13. data/lib/project_types/extension/commands/create.rb +2 -2
  14. data/lib/project_types/extension/commands/extension_command.rb +10 -6
  15. data/lib/project_types/extension/commands/push.rb +2 -2
  16. data/lib/project_types/extension/commands/register.rb +6 -5
  17. data/lib/project_types/extension/commands/serve.rb +64 -22
  18. data/lib/project_types/extension/commands/tunnel.rb +3 -1
  19. data/lib/project_types/extension/extension_project.rb +20 -4
  20. data/lib/project_types/extension/extension_project_keys.rb +2 -1
  21. data/lib/project_types/extension/features/argo.rb +19 -44
  22. data/lib/project_types/extension/features/argo_runtime.rb +63 -0
  23. data/lib/project_types/extension/features/argo_serve.rb +79 -0
  24. data/lib/project_types/extension/features/argo_serve_options.rb +40 -0
  25. data/lib/project_types/extension/features/argo_setup.rb +1 -1
  26. data/lib/project_types/extension/forms/questions/ask_type.rb +16 -5
  27. data/lib/project_types/extension/messages/message_loading.rb +3 -1
  28. data/lib/project_types/extension/messages/messages.rb +9 -6
  29. data/lib/project_types/extension/models/npm_package.rb +14 -0
  30. data/lib/project_types/extension/models/registration.rb +1 -0
  31. data/lib/project_types/extension/models/specification.rb +7 -2
  32. data/lib/project_types/extension/models/specification_handlers/checkout_argo_extension.rb +18 -0
  33. data/lib/project_types/extension/models/specification_handlers/default.rb +35 -2
  34. data/lib/project_types/extension/models/specifications.rb +12 -1
  35. data/lib/project_types/extension/models/version.rb +1 -1
  36. data/lib/project_types/extension/tasks/choose_next_available_port.rb +36 -0
  37. data/lib/project_types/extension/tasks/configure_features.rb +5 -1
  38. data/lib/project_types/extension/tasks/converters/registration_converter.rb +2 -0
  39. data/lib/project_types/extension/tasks/fetch_specifications.rb +8 -28
  40. data/lib/project_types/extension/tasks/find_npm_packages.rb +106 -0
  41. data/lib/project_types/node/commands/generate.rb +0 -22
  42. data/lib/project_types/node/forms/create.rb +10 -1
  43. data/lib/project_types/node/messages/messages.rb +5 -4
  44. data/lib/project_types/rails/forms/create.rb +11 -1
  45. data/lib/project_types/rails/messages/messages.rb +5 -4
  46. data/lib/project_types/script/cli.rb +7 -8
  47. data/lib/project_types/script/commands/create.rb +0 -7
  48. data/lib/project_types/script/commands/push.rb +3 -3
  49. data/lib/project_types/script/config/extension_points.yml +17 -0
  50. data/lib/project_types/script/errors.rb +0 -19
  51. data/lib/project_types/script/forms/create.rb +3 -14
  52. data/lib/project_types/script/graphql/app_script_update_or_create.graphql +5 -5
  53. data/lib/project_types/script/graphql/get_app_scripts.graphql +6 -0
  54. data/lib/project_types/script/graphql/script_service_proxy.graphql +1 -2
  55. data/lib/project_types/script/layers/application/build_script.rb +1 -2
  56. data/lib/project_types/script/layers/application/create_script.rb +32 -49
  57. data/lib/project_types/script/layers/application/extension_points.rb +3 -2
  58. data/lib/project_types/script/layers/application/push_script.rb +5 -5
  59. data/lib/project_types/script/layers/domain/errors.rb +0 -2
  60. data/lib/project_types/script/layers/domain/extension_point.rb +60 -45
  61. data/lib/project_types/script/layers/domain/metadata.rb +18 -25
  62. data/lib/project_types/script/layers/domain/push_package.rb +4 -4
  63. data/lib/project_types/script/layers/domain/script_project.rb +54 -0
  64. data/lib/project_types/script/layers/infrastructure/assemblyscript_project_creator.rb +39 -14
  65. data/lib/project_types/script/layers/infrastructure/assemblyscript_task_runner.rb +14 -43
  66. data/lib/project_types/script/layers/infrastructure/command_runner.rb +19 -0
  67. data/lib/project_types/script/layers/infrastructure/errors.rb +40 -20
  68. data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +12 -13
  69. data/lib/project_types/script/layers/infrastructure/rust_project_creator.rb +9 -10
  70. data/lib/project_types/script/layers/infrastructure/rust_task_runner.rb +6 -7
  71. data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +172 -0
  72. data/lib/project_types/script/layers/infrastructure/script_service.rb +25 -76
  73. data/lib/project_types/script/messages/messages.rb +20 -53
  74. data/lib/project_types/script/tasks/ensure_env.rb +85 -0
  75. data/lib/project_types/script/ui/error_handler.rb +32 -41
  76. data/lib/shopify-cli/context.rb +28 -0
  77. data/lib/shopify-cli/js_system.rb +2 -2
  78. data/lib/shopify-cli/messages/messages.rb +50 -45
  79. data/lib/shopify-cli/method_object.rb +4 -4
  80. data/lib/shopify-cli/oauth.rb +9 -3
  81. data/lib/shopify-cli/packager.rb +1 -1
  82. data/lib/shopify-cli/partners_api/organizations.rb +3 -3
  83. data/lib/shopify-cli/resolve_constant.rb +1 -1
  84. data/lib/shopify-cli/resources/env_file.rb +1 -1
  85. data/lib/shopify-cli/tasks/ensure_dev_store.rb +1 -1
  86. data/lib/shopify-cli/tasks/select_org_and_shop.rb +6 -4
  87. data/lib/shopify-cli/transform_data_structure.rb +2 -2
  88. data/lib/shopify-cli/tunnel.rb +22 -1
  89. data/lib/shopify-cli/version.rb +1 -1
  90. data/lib/shopify_cli.rb +0 -1
  91. data/vendor/deps/smart_properties/REVISION +1 -1
  92. data/vendor/deps/smart_properties/lib/smart_properties/property.rb +7 -1
  93. data/vendor/deps/smart_properties/lib/smart_properties/version.rb +1 -1
  94. metadata +17 -12
  95. data/.travis.yml +0 -14
  96. data/lib/project_types/script/commands/disable.rb +0 -25
  97. data/lib/project_types/script/commands/enable.rb +0 -80
  98. data/lib/project_types/script/graphql/shop_script_delete.graphql +0 -14
  99. data/lib/project_types/script/graphql/shop_script_update_or_create.graphql +0 -28
  100. data/lib/project_types/script/layers/application/disable_script.rb +0 -21
  101. data/lib/project_types/script/layers/application/enable_script.rb +0 -23
  102. data/lib/project_types/script/layers/infrastructure/config_ui_repository.rb +0 -46
  103. data/lib/project_types/script/script_project.rb +0 -64
@@ -0,0 +1,14 @@
1
+ require "semantic/semantic"
2
+
3
+ module Extension
4
+ module Models
5
+ NpmPackage = Struct.new(:name, :version, keyword_init: true) do
6
+ include Comparable
7
+
8
+ def <=>(other)
9
+ return nil unless name == other.name
10
+ Semantic::Version.new(version) <=> Semantic::Version.new(other.version)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -7,6 +7,7 @@ module Extension
7
7
  include SmartProperties
8
8
 
9
9
  property! :id, accepts: Integer
10
+ property! :uuid, accepts: String
10
11
  property! :type, accepts: String
11
12
  property! :title, accepts: String
12
13
  property! :draft_version, accepts: Extension::Models::Version
@@ -7,9 +7,12 @@ module Extension
7
7
  class Argo
8
8
  include SmartProperties
9
9
 
10
- property! :surface_area, converts: :to_str
10
+ property! :surface, converts: :to_str
11
11
  property! :renderer_package_name, converts: :to_str
12
12
  property! :git_template, converts: :to_str
13
+ property! :required_fields, accepts: Array, default: -> { [] }
14
+ property! :required_shop_beta_flags, accepts: Array, default: -> { [] }
15
+ property! :cli_package_name, accepts: String, converts: :to_str, default: ""
13
16
  end
14
17
 
15
18
  def self.build(feature_set_attributes)
@@ -18,14 +21,16 @@ module Extension
18
21
  .call(identifier, namespace: Features)
19
22
  .rescue { OpenStruct }
20
23
  .then { |c| c.new(**feature_attributes) }
21
- .unwrap { |error| raise error }
24
+ .unwrap { |error| raise(error) }
22
25
  end
23
26
  end
24
27
  end
25
28
 
26
29
  property! :identifier
30
+ property :name, converts: :to_str
27
31
  property :graphql_identifier, converts: :to_str
28
32
  property! :features, converts: Features.method(:build), default: -> { [] }
33
+ property! :options, converts: ->(options) { OpenStruct.new(options) }, default: -> { OpenStruct.new }
29
34
 
30
35
  def graphql_identifier
31
36
  super || identifier
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Extension
4
+ module Models
5
+ module SpecificationHandlers
6
+ class CheckoutArgoExtension < Default
7
+ PERMITTED_CONFIG_KEYS = [:metafields, :extension_points]
8
+
9
+ def config(context)
10
+ {
11
+ **Features::ArgoConfig.parse_yaml(context, PERMITTED_CONFIG_KEYS),
12
+ **argo.config(context),
13
+ }
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -19,7 +19,7 @@ module Extension
19
19
  end
20
20
 
21
21
  def name
22
- message("name")
22
+ message("name") || specification.name
23
23
  end
24
24
 
25
25
  def tagline
@@ -42,12 +42,45 @@ module Extension
42
42
  []
43
43
  end
44
44
 
45
+ def choose_port?(context)
46
+ argo_runtime(context).accepts_port?
47
+ end
48
+
49
+ def establish_tunnel?(context)
50
+ argo_runtime(context).accepts_tunnel_url?
51
+ end
52
+
53
+ def serve(context:, port:, tunnel_url:)
54
+ Features::ArgoServe.new(specification_handler: self, argo_runtime: argo_runtime(context),
55
+ context: context, port: port, tunnel_url: tunnel_url).call
56
+ end
57
+
58
+ def renderer_package(context)
59
+ argo.renderer_package(context)
60
+ end
61
+
62
+ def argo_runtime(context)
63
+ @argo_runtime ||= Features::ArgoRuntime.new(
64
+ renderer: renderer_package(context),
65
+ cli: cli_package(context)
66
+ )
67
+ end
68
+
69
+ def cli_package(context)
70
+ cli_package_name = specification.features.argo&.cli_package_name
71
+ return unless cli_package_name
72
+
73
+ js_system = ShopifyCli::JsSystem.new(ctx: context)
74
+ Tasks::FindNpmPackages.exactly_one_of(cli_package_name, js_system: js_system)
75
+ .unwrap { |_e| context.abort(context.message("errors.package_not_found", cli_package_name)) }
76
+ end
77
+
45
78
  protected
46
79
 
47
80
  def argo
48
81
  Features::Argo.new(
49
82
  git_template: specification.features.argo.git_template,
50
- renderer_package_name: specification.features.argo.renderer_package_name,
83
+ renderer_package_name: specification.features.argo.renderer_package_name
51
84
  )
52
85
  end
53
86
 
@@ -26,6 +26,10 @@ module Extension
26
26
  handlers.values.each(&block)
27
27
  end
28
28
 
29
+ def none?
30
+ each.none?
31
+ end
32
+
29
33
  protected
30
34
 
31
35
  def handlers
@@ -37,6 +41,8 @@ module Extension
37
41
  def fetch_specifications_and_build_handlers
38
42
  ShopifyCli::Result
39
43
  .call(&fetch_specifications)
44
+ .map(&ShopifyCli::TransformDataStructure.new(symbolize_keys: true, underscore_keys: true))
45
+ .then(&method(:select_cli_extensions))
40
46
  .then(&Tasks::ConfigureFeatures)
41
47
  .then(&method(:ensure_legacy_compatibility))
42
48
  .then(&method(:build_specifications))
@@ -64,7 +70,8 @@ module Extension
64
70
 
65
71
  def ensure_legacy_compatibility(specification_attribute_sets)
66
72
  specification_attribute_sets.each do |attributes|
67
- next unless attributes.fetch(:identifier) == "product_subscription"
73
+ next unless attributes.fetch(:identifier) == "subscription_management"
74
+ attributes[:identifier] = "product_subscription"
68
75
  attributes[:graphql_identifier] = "SUBSCRIPTION_MANAGEMENT"
69
76
  end
70
77
  end
@@ -72,6 +79,10 @@ module Extension
72
79
  def build_specifications(specification_attribute_sets)
73
80
  specification_attribute_sets.map { |attributes| Models::Specification.new(**attributes) }
74
81
  end
82
+
83
+ def select_cli_extensions(specification_attribute_sets)
84
+ specification_attribute_sets.select { |attributes| attributes.dig(:options, :management_experience) == "cli" }
85
+ end
75
86
  end
76
87
  end
77
88
  end
@@ -9,7 +9,7 @@ module Extension
9
9
  property! :last_user_interaction_at, accepts: Time
10
10
  property :context, accepts: String
11
11
  property :location, accepts: String
12
- property :validation_errors, accepts: Models::ValidationError::IS_VALIDATION_ERROR_LIST, default: []
12
+ property :validation_errors, accepts: Models::ValidationError::IS_VALIDATION_ERROR_LIST, default: -> { [] }
13
13
  end
14
14
  end
15
15
  end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+ require "shopify_cli"
3
+ require "socket"
4
+
5
+ module Extension
6
+ module Tasks
7
+ class ChooseNextAvailablePort
8
+ include ShopifyCli::MethodObject
9
+
10
+ property! :from
11
+ property! :to, default: -> { from + 10 }
12
+ property! :host, default: "localhost"
13
+
14
+ def call
15
+ available_port = port_range(from: from, to: to).find { |p| available?(host, p) }
16
+ raise ArgumentError, "Ports between #{from} and #{to} are unavailable" if available_port.nil?
17
+ available_port
18
+ end
19
+
20
+ private
21
+
22
+ def port_range(from:, to:)
23
+ (from..to)
24
+ end
25
+
26
+ def available?(host, port)
27
+ Socket.tcp(host, port, connect_timeout: 1) do |socket|
28
+ socket.close
29
+ false
30
+ end
31
+ rescue Errno::ECONNREFUSED
32
+ true
33
+ end
34
+ end
35
+ end
36
+ end
@@ -24,7 +24,7 @@ module Extension
24
24
  end
25
25
 
26
26
  def extract_surface_area(argo_configuration)
27
- argo_configuration.fetch(:surface_area) do
27
+ argo_configuration.fetch(:surface) do
28
28
  raise UnspecifiedSurfaceArea, "Argo configuration does not specify surface area"
29
29
  end
30
30
  end
@@ -40,10 +40,14 @@ module Extension
40
40
  admin: {
41
41
  git_template: "https://github.com/Shopify/argo-admin-template.git",
42
42
  renderer_package_name: "@shopify/argo-admin",
43
+ required_fields: [:shop, :api_key],
44
+ required_shop_beta_flags: [:argo_admin_beta],
45
+ cli_package_name: "@shopify/argo-admin-cli",
43
46
  },
44
47
  checkout: {
45
48
  git_template: "https://github.com/Shopify/argo-checkout-template.git",
46
49
  renderer_package_name: "@shopify/argo-checkout",
50
+ cli_package_name: "@shopify/argo-run",
47
51
  },
48
52
  }
49
53
  end
@@ -6,6 +6,7 @@ module Extension
6
6
  module Converters
7
7
  module RegistrationConverter
8
8
  ID_FIELD = "id"
9
+ UUID_FIELD = "uuid"
9
10
  TYPE_FIELD = "type"
10
11
  TITLE_FIELD = "title"
11
12
  DRAFT_VERSION_FIELD = "draftVersion"
@@ -15,6 +16,7 @@ module Extension
15
16
 
16
17
  Models::Registration.new(
17
18
  id: hash[ID_FIELD].to_i,
19
+ uuid: hash[UUID_FIELD],
18
20
  type: hash[TYPE_FIELD],
19
21
  title: hash[TITLE_FIELD],
20
22
  draft_version: VersionConverter.from_hash(context, hash[DRAFT_VERSION_FIELD])
@@ -3,35 +3,15 @@ module Extension
3
3
  class FetchSpecifications
4
4
  include ShopifyCli::MethodObject
5
5
 
6
- def call
7
- [
8
- product_subscription_specification,
9
- checkout_post_purchase_specification,
10
- ]
11
- end
12
-
13
- private
6
+ property :context
7
+ property :api_key
14
8
 
15
- def product_subscription_specification
16
- {
17
- identifier: "product_subscription",
18
- features: {
19
- argo: {
20
- surface_area: "admin",
21
- },
22
- },
23
- }
24
- end
25
-
26
- def checkout_post_purchase_specification
27
- {
28
- identifier: "checkout_post_purchase",
29
- features: {
30
- argo: {
31
- surface_area: "checkout",
32
- },
33
- },
34
- }
9
+ def call
10
+ response = ShopifyCli::PartnersAPI
11
+ .query(context, "fetch_specifications", api_key: api_key)
12
+ .dig("data", "extensionSpecifications")
13
+ context.abort(context.message("tasks.errors.parse_error")) if response.nil?
14
+ response
35
15
  end
36
16
  end
37
17
  end
@@ -0,0 +1,106 @@
1
+ module Extension
2
+ module Tasks
3
+ class FindNpmPackages
4
+ include ShopifyCli::MethodObject
5
+
6
+ property! :js_system, accepts: ShopifyCli::JsSystem
7
+ property! :production_only, accepts: [true, false], default: false, reader: :production_only?
8
+
9
+ def self.at_least_one_of(*package_names, **config)
10
+ new(**config).at_least_one_of(*package_names)
11
+ end
12
+
13
+ def self.all(*package_names, **config)
14
+ new(**config).all(*package_names)
15
+ end
16
+
17
+ def self.exactly_one_of(*package_names, **config)
18
+ new(**config).exactly_one_of(*package_names)
19
+ end
20
+
21
+ def all(*package_names)
22
+ call(*package_names) do |found_packages|
23
+ found_package_names = found_packages.map(&:name)
24
+ next found_packages if Set.new(found_package_names) == Set.new(package_names)
25
+ raise PackageResolutionFailed, format(
26
+ "Missing packages: %s",
27
+ (package_names - found_package_names).join(", ")
28
+ )
29
+ end
30
+ end
31
+
32
+ def at_least_one_of(*package_names)
33
+ call(*package_names) do |found_packages|
34
+ found_package_names = found_packages.map(&:name)
35
+ next found_packages unless (found_package_names & package_names).empty?
36
+ raise PackageResolutionFailed, format(
37
+ "Expected at least one of the following packages: %s",
38
+ package_names.join(", ")
39
+ )
40
+ end
41
+ end
42
+
43
+ def exactly_one_of(*package_names)
44
+ call(*package_names) do |found_packages|
45
+ case found_packages.count
46
+ when 0
47
+ raise PackageResolutionFailed, format(
48
+ "Expected one of the following packages: %s",
49
+ package_names.join(", ")
50
+ )
51
+ when 1
52
+ found_packages.first.tap do |found_package|
53
+ next found_package if package_names.include?(found_package.name)
54
+ raise PackageResolutionFailed, format(
55
+ "Expected the following package: %s",
56
+ found_package.name
57
+ )
58
+ end
59
+ else
60
+ raise PackageResolutionFailed, "Found more than one package"
61
+ end
62
+ end
63
+ end
64
+
65
+ def call(*package_names, &validate)
66
+ validate ||= ->(found_packages) { found_packages }
67
+
68
+ unless package_names.all? { |name| name.is_a?(String) }
69
+ raise ArgumentError, "Expected a list of package names"
70
+ end
71
+
72
+ ShopifyCli::Result
73
+ .call(&method(:list_packages))
74
+ .then(&method(:search_packages).curry[package_names])
75
+ .then(&method(:filter_duplicates))
76
+ .then(&validate)
77
+ end
78
+
79
+ def list_packages
80
+ result, error, status =
81
+ js_system.call(yarn: yarn_list, npm: npm_list, capture_response: true)
82
+ raise error unless status.success?
83
+ result
84
+ end
85
+
86
+ def yarn_list
87
+ production_only? ? %w[list --production] : %w[list]
88
+ end
89
+
90
+ def npm_list
91
+ production_only? ? %w[list --prod --depth=1] : %w[list --depth=1]
92
+ end
93
+
94
+ def search_packages(packages, package_list)
95
+ pattern = /(#{packages.join("|")})@(\d.*)$/
96
+ package_list.scan(pattern).map do |(name, version)|
97
+ Models::NpmPackage.new(name: name, version: version.strip)
98
+ end
99
+ end
100
+
101
+ def filter_duplicates(packages)
102
+ packages.reject { |p| p.version.match(/deduped/) }.uniq
103
+ end
104
+ end
105
+ end
106
+ end
@@ -4,10 +4,6 @@ require "shopify_cli"
4
4
  module Node
5
5
  module Commands
6
6
  class Generate < ShopifyCli::Command
7
- subcommand :Page, "page", Project.project_filepath("commands/generate/page")
8
- subcommand :Billing, "billing", Project.project_filepath("commands/generate/billing")
9
- subcommand :Webhook, "webhook", Project.project_filepath("commands/generate/webhook")
10
-
11
7
  def call(*)
12
8
  @ctx.puts(self.class.help)
13
9
  end
@@ -19,24 +15,6 @@ module Node
19
15
  def self.extended_help
20
16
  help
21
17
  end
22
-
23
- def self.run_generate(script, name, ctx)
24
- stat = ctx.system(script)
25
- unless stat.success?
26
- ctx.abort(response(stat.exitstatus, name, ctx))
27
- end
28
- end
29
-
30
- def self.response(code, name, ctx)
31
- case code
32
- when 1
33
- ctx.message("node.generate.error.generic", name)
34
- when 2
35
- ctx.message("node.generate.error.name_exists", name)
36
- else
37
- ctx.message("node.error.generic")
38
- end
39
- end
40
18
  end
41
19
  end
42
20
  end