shopify-cli 2.15.6 → 2.17.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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +4 -4
  3. data/.github/workflows/shopify.yml +0 -33
  4. data/CHANGELOG.md +35 -1
  5. data/Gemfile.lock +4 -4
  6. data/Tests.dockerfile +1 -1
  7. data/ext/shopify-extensions/version +1 -1
  8. data/lib/graphql/find_organization_with_apps.graphql +20 -0
  9. data/lib/project_types/extension/cli.rb +3 -0
  10. data/lib/project_types/extension/commands/build.rb +0 -2
  11. data/lib/project_types/extension/commands/create.rb +6 -0
  12. data/lib/project_types/extension/commands/serve.rb +8 -3
  13. data/lib/project_types/extension/features/argo.rb +0 -31
  14. data/lib/project_types/extension/features/argo_config.rb +0 -1
  15. data/lib/project_types/extension/features/argo_serve.rb +1 -1
  16. data/lib/project_types/extension/messages/messages.rb +23 -0
  17. data/lib/project_types/extension/models/development_server.rb +2 -1
  18. data/lib/project_types/extension/models/development_server_requirements.rb +24 -4
  19. data/lib/project_types/extension/models/npm_package.rb +15 -3
  20. data/lib/project_types/extension/models/server_config/capabilities.rb +11 -0
  21. data/lib/project_types/extension/models/server_config/development_renderer.rb +6 -1
  22. data/lib/project_types/extension/models/server_config/extension.rb +10 -3
  23. data/lib/project_types/extension/models/server_config/root.rb +0 -2
  24. data/lib/project_types/extension/models/specification_handlers/checkout_ui_extension.rb +1 -1
  25. data/lib/project_types/extension/tasks/configure_features.rb +4 -0
  26. data/lib/project_types/extension/tasks/convert_server_config.rb +8 -4
  27. data/lib/project_types/extension/tasks/execute_commands/base.rb +2 -0
  28. data/lib/project_types/extension/tasks/execute_commands/build.rb +2 -1
  29. data/lib/project_types/extension/tasks/execute_commands/create.rb +0 -3
  30. data/lib/project_types/extension/tasks/execute_commands/outdated_extension_detection.rb +53 -0
  31. data/lib/project_types/extension/tasks/execute_commands/serve.rb +2 -1
  32. data/lib/project_types/extension/tasks/get_apps.rb +6 -9
  33. data/lib/project_types/extension/tasks/get_extensions.rb +12 -11
  34. data/lib/project_types/extension/tasks/merge_server_config.rb +4 -3
  35. data/lib/project_types/script/cli.rb +1 -0
  36. data/lib/project_types/script/config/extension_points.yml +17 -3
  37. data/lib/project_types/script/graphql/app_script_set.graphql +2 -0
  38. data/lib/project_types/script/layers/application/connect_app.rb +1 -1
  39. data/lib/project_types/script/layers/application/push_script.rb +1 -0
  40. data/lib/project_types/script/layers/domain/app_bridge.rb +16 -0
  41. data/lib/project_types/script/layers/domain/script_project.rb +1 -0
  42. data/lib/project_types/script/layers/infrastructure/errors.rb +0 -3
  43. data/lib/project_types/script/layers/infrastructure/languages/project_creator.rb +3 -2
  44. data/lib/project_types/script/layers/infrastructure/languages/task_runner.rb +2 -2
  45. data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +12 -0
  46. data/lib/project_types/script/layers/infrastructure/script_service.rb +5 -0
  47. data/lib/project_types/theme/commands/serve.rb +27 -0
  48. data/lib/project_types/theme/messages/messages.rb +13 -0
  49. data/lib/shopify_cli/admin_api.rb +5 -2
  50. data/lib/shopify_cli/commands/login.rb +2 -2
  51. data/lib/shopify_cli/commands/logout.rb +1 -1
  52. data/lib/shopify_cli/constants.rb +2 -0
  53. data/lib/shopify_cli/context.rb +14 -9
  54. data/lib/shopify_cli/environment.rb +8 -0
  55. data/lib/shopify_cli/partners_api/app_extensions.rb +6 -6
  56. data/lib/shopify_cli/partners_api/organizations.rb +12 -4
  57. data/lib/shopify_cli/services/app/create/rails_service.rb +1 -1
  58. data/lib/shopify_cli/tasks/ensure_env.rb +1 -1
  59. data/lib/shopify_cli/theme/dev_server/hot_reload/sections_index.rb +5 -6
  60. data/lib/shopify_cli/theme/dev_server/local_assets.rb +1 -1
  61. data/lib/shopify_cli/theme/dev_server/watcher.rb +2 -1
  62. data/lib/shopify_cli/theme/dev_server.rb +1 -1
  63. data/lib/shopify_cli/theme/development_theme.rb +11 -1
  64. data/lib/shopify_cli/theme/file.rb +9 -0
  65. data/lib/shopify_cli/theme/theme.rb +4 -0
  66. data/lib/shopify_cli/theme/theme_admin_api.rb +3 -1
  67. data/lib/shopify_cli/version.rb +1 -1
  68. data/{shipit.yml → shipit.rubygems.yml} +0 -0
  69. data/shopify-cli.gemspec +1 -1
  70. metadata +9 -5
@@ -0,0 +1,53 @@
1
+ module Extension
2
+ module Tasks
3
+ module ExecuteCommands
4
+ module OutdatedExtensionDetection
5
+ class OutdatedCheck
6
+ include ShopifyCLI::MethodObject
7
+
8
+ property! :type, accepts: Models::DevelopmentServerRequirements.method(:type_supported?)
9
+ property! :context, accepts: ShopifyCLI::Context
10
+ property! :project, accepts: ShopifyCLI::Project, default: -> { ShopifyCLI::Project.current }
11
+
12
+ def call
13
+ return false if valid?(parse_package)
14
+ context.abort(upgrade_instructions)
15
+ end
16
+
17
+ private
18
+
19
+ def upgrade_instructions
20
+ case type
21
+ when "checkout_ui_extension"
22
+ context.message("errors.outdated_extensions.checkout_ui_extension")
23
+ else
24
+ context.message("errors.outdated_extensions.unknown")
25
+ end
26
+ end
27
+
28
+ def parse_package
29
+ File.open(Pathname(project.directory).join("package.json")) do |file|
30
+ Models::NpmPackage.parse(file)
31
+ end
32
+ end
33
+
34
+ def valid?(package)
35
+ case type
36
+ when "checkout_ui_extension"
37
+ package.dev_dependency?("@shopify/shopify-cli-extensions") &&
38
+ package.script?("build") &&
39
+ package.script?("develop")
40
+ else
41
+ true
42
+ end
43
+ end
44
+ end
45
+
46
+ def call(*)
47
+ return super unless Models::DevelopmentServerRequirements.supported?(type)
48
+ OutdatedCheck.call(type: type, context: context).then { super }
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -5,7 +5,8 @@ module Extension
5
5
  module Tasks
6
6
  module ExecuteCommands
7
7
  class Serve < Base
8
- property! :context, accepts: ShopifyCLI::Context
8
+ prepend OutdatedExtensionDetection
9
+
9
10
  property! :config_file_path, accepts: String
10
11
  property :port, accepts: Integer, default: ShopifyCLI::Constants::Extension::DEFAULT_PORT
11
12
  property :resource_url, accepts: String
@@ -5,20 +5,17 @@ module Extension
5
5
  module Tasks
6
6
  class GetApps < ShopifyCLI::Task
7
7
  def call(context:)
8
- organizations = ShopifyCLI::PartnersAPI::Organizations.fetch_with_app(context)
9
- apps_from_organizations(organizations)
8
+ org_id = ShopifyCLI::DB.get(:organization_id)
9
+ return [] unless org_id
10
+
11
+ organization = ShopifyCLI::PartnersAPI::Organizations.fetch_with_apps(context, id: org_id)
12
+ apps_owned_by_organization(organization)
10
13
  end
11
14
 
12
15
  private
13
16
 
14
- def apps_from_organizations(organizations)
15
- organizations.flat_map do |organization|
16
- apps_owned_by_organization(organization)
17
- end
18
- end
19
-
20
17
  def apps_owned_by_organization(organization)
21
- return [] unless organization.key?("apps") && organization["apps"].any?
18
+ return [] unless organization&.dig("apps")
22
19
 
23
20
  organization["apps"].map do |app|
24
21
  Converters::AppConverter.from_hash(app, organization)
@@ -5,28 +5,29 @@ module Extension
5
5
  module Tasks
6
6
  class GetExtensions < ShopifyCLI::Task
7
7
  def call(context:, type:)
8
- organizations = ShopifyCLI::PartnersAPI::Organizations.fetch_with_extensions(context, type)
9
- extensions_from_organizations(organizations, context: context)
8
+ org_id = ShopifyCLI::DB.get(:organization_id)
9
+ return [] unless org_id
10
+
11
+ organization = ShopifyCLI::PartnersAPI::Organizations.fetch_with_extensions(context, type, id: org_id)
12
+ return [] unless organization_with_apps?(organization)
13
+ extensions_owned_by_organization(organization, context: context)
10
14
  end
11
15
 
12
16
  private
13
17
 
14
- def extensions_from_organizations(organizations, context:)
15
- organizations.flat_map do |organization|
16
- extensions_owned_by_organization(organization, context: context)
17
- end
18
- end
19
-
20
18
  def extensions_owned_by_organization(organization, context:)
21
- return [] unless organization.key?("apps") && organization["apps"].any?
22
-
23
19
  organization["apps"].flat_map do |app|
24
- app["extensionRegistrations"].map do |registration|
20
+ registrations = app["extensionRegistrations"] || []
21
+ registrations.map do |registration|
25
22
  [Converters::AppConverter.from_hash(app, organization),
26
23
  Converters::RegistrationConverter.from_hash(context, registration)]
27
24
  end
28
25
  end
29
26
  end
27
+
28
+ def organization_with_apps?(organization)
29
+ organization&.key?("apps") && organization["apps"].any?
30
+ end
30
31
  end
31
32
  end
32
33
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require "shopify_cli"
3
- require "yaml"
4
3
  require "pathname"
5
4
 
6
5
  module Extension
@@ -10,7 +9,7 @@ module Extension
10
9
 
11
10
  property! :context, accepts: ShopifyCLI::Context
12
11
  property! :file_path, accepts: ->(path) { Pathname(path).yield_self(&:absolute?) }
13
- property :port, accepts: Integer, default: ShopifyCLI::Constants::Extension::DEFAULT_PORT
12
+ property! :port, accepts: Integer, default: ShopifyCLI::Constants::Extension::DEFAULT_PORT
14
13
  property :resource_url, accepts: String
15
14
  property :tunnel_url, accepts: String
16
15
  property! :type, accepts: Models::DevelopmentServerRequirements::SUPPORTED_EXTENSION_TYPES
@@ -33,7 +32,9 @@ module Extension
33
32
  store: project.env.shop || "",
34
33
  title: project.title,
35
34
  tunnel_url: tunnel_url,
36
- type: type
35
+ type: type,
36
+ port: port,
37
+ metafields: config["metafields"]
37
38
  )
38
39
  rescue Psych::SyntaxError => e
39
40
  raise(
@@ -50,6 +50,7 @@ module Script
50
50
  autoload :ExtensionPoint, Project.project_filepath("layers/domain/extension_point")
51
51
  autoload :ScriptConfig, Project.project_filepath("layers/domain/script_config")
52
52
  autoload :ScriptProject, Project.project_filepath("layers/domain/script_project")
53
+ autoload :AppBridge, Project.project_filepath("layers/domain/app_bridge")
53
54
  end
54
55
 
55
56
  module Infrastructure
@@ -7,6 +7,8 @@ payment_methods:
7
7
  repo: "https://github.com/Shopify/scripts-apis-examples"
8
8
  wasm:
9
9
  repo: "https://github.com/Shopify/scripts-apis-examples"
10
+ rust:
11
+ repo: "https://github.com/Shopify/scripts-apis-examples"
10
12
  shipping_methods:
11
13
  domain: 'checkout'
12
14
  libraries:
@@ -36,21 +38,33 @@ delivery_discount_types:
36
38
  repo: "https://github.com/Shopify/scripts-apis-examples"
37
39
  wasm:
38
40
  repo: "https://github.com/Shopify/scripts-apis-examples"
39
- product_discount_type:
41
+ product_discounts:
40
42
  beta: true
41
43
  domain: 'discounts'
42
44
  libraries:
43
45
  wasm:
44
46
  repo: "https://github.com/Shopify/scripts-apis-examples"
45
- order_discount_type:
47
+ rust:
48
+ repo: "https://github.com/Shopify/scripts-apis-examples"
49
+ order_discounts:
46
50
  beta: true
47
51
  domain: 'discounts'
48
52
  libraries:
49
53
  wasm:
50
54
  repo: "https://github.com/Shopify/scripts-apis-examples"
51
- shipping_discount_type:
55
+ rust:
56
+ repo: "https://github.com/Shopify/scripts-apis-examples"
57
+ shipping_discounts:
52
58
  beta: true
53
59
  domain: 'discounts'
54
60
  libraries:
55
61
  wasm:
56
62
  repo: "https://github.com/Shopify/scripts-apis-examples"
63
+ rust:
64
+ repo: "https://github.com/Shopify/scripts-apis-examples"
65
+ shipping_rates_consolidation:
66
+ beta: true
67
+ domain: 'checkout'
68
+ libraries:
69
+ wasm:
70
+ repo: "https://github.com/Shopify/scripts-apis-examples"
@@ -12,6 +12,7 @@ mutation AppScriptSet(
12
12
  $moduleUploadUrl: String!,
13
13
  $library: LibraryInput,
14
14
  $inputQuery: String,
15
+ $appBridge: AppBridgeInput!,
15
16
  ) {
16
17
  appScriptSet(
17
18
  uuid: $uuid
@@ -27,6 +28,7 @@ mutation AppScriptSet(
27
28
  moduleUploadUrl: $moduleUploadUrl,
28
29
  library: $library,
29
30
  inputQuery: $inputQuery,
31
+ appBridge: $appBridge
30
32
  ) {
31
33
  userErrors {
32
34
  field
@@ -21,7 +21,7 @@ module Script
21
21
  if partner_proxy_bypass
22
22
  stubbed_org
23
23
  else
24
- orgs = ShopifyCLI::PartnersAPI::Organizations.fetch_with_app(ctx)
24
+ orgs = ShopifyCLI::PartnersAPI::Organizations.fetch_all_with_apps(ctx)
25
25
  Forms::AskOrg.ask(ctx, orgs, nil).org
26
26
  end
27
27
 
@@ -55,6 +55,7 @@ module Script
55
55
  force: force,
56
56
  metadata: package.metadata,
57
57
  script_config: package.script_config,
58
+ app_bridge: script_project.app_bridge,
58
59
  module_upload_url: module_upload_url,
59
60
  library: package.library,
60
61
  input_query: script_project.input_query,
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Script
4
+ module Layers
5
+ module Domain
6
+ class AppBridge
7
+ attr_reader :create_path, :details_path
8
+
9
+ def initialize(create_path:, details_path:)
10
+ @create_path = create_path
11
+ @details_path = details_path
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -17,6 +17,7 @@ module Script
17
17
  property! :language, accepts: String
18
18
 
19
19
  property :script_config, accepts: ScriptConfig
20
+ property :app_bridge, accepts: AppBridge
20
21
  property :input_query, accepts: String
21
22
 
22
23
  def initialize(*)
@@ -137,8 +137,6 @@ module Script
137
137
  end
138
138
  end
139
139
 
140
- class ProjectCreatorNotFoundError < ScriptProjectError; end
141
-
142
140
  class SystemCallFailureError < ScriptProjectError
143
141
  attr_reader :out, :cmd
144
142
  def initialize(out:, cmd:)
@@ -157,7 +155,6 @@ module Script
157
155
  end
158
156
 
159
157
  class ScriptProjectAlreadyExistsError < ScriptProjectError; end
160
- class TaskRunnerNotFoundError < ScriptProjectError; end
161
158
  class BuildScriptNotFoundError < ScriptProjectError; end
162
159
 
163
160
  class WebAssemblyBinaryNotFoundError < ScriptProjectError
@@ -26,8 +26,9 @@ module Script
26
26
  "wasm" => WasmProjectCreator,
27
27
  }
28
28
 
29
- raise Errors::ProjectCreatorNotFoundError unless project_creators[language]
30
- project_creators[language].new(
29
+ project_creator = project_creators[language] || WasmProjectCreator
30
+
31
+ project_creator.new(
31
32
  ctx: ctx,
32
33
  type: type,
33
34
  project_name: project_name,
@@ -13,8 +13,8 @@ module Script
13
13
  "wasm" => WasmTaskRunner,
14
14
  }
15
15
 
16
- raise Errors::TaskRunnerNotFoundError unless task_runners[language]
17
- task_runners[language].new(ctx)
16
+ task_runner = task_runners[language] || WasmTaskRunner
17
+ task_runner.new(ctx)
18
18
  end
19
19
 
20
20
  def initialize(ctx)
@@ -54,6 +54,7 @@ module Script
54
54
  extension_point_type: extension_point_type,
55
55
  language: language,
56
56
  script_config: script_config_repository.get!,
57
+ app_bridge: app_bridge,
57
58
  input_query: read_input_query,
58
59
  )
59
60
  end
@@ -94,6 +95,7 @@ module Script
94
95
  extension_point_type: extension_point_type,
95
96
  language: language,
96
97
  script_config: script_config,
98
+ app_bridge: app_bridge,
97
99
  )
98
100
  end
99
101
 
@@ -121,6 +123,16 @@ module Script
121
123
  project_config_value("language")&.downcase || default_language
122
124
  end
123
125
 
126
+ def app_bridge
127
+ create_path = project_config_value("app_bridge_create_path") || "/"
128
+ details_path = project_config_value("app_bridge_details_path") || "/"
129
+
130
+ Domain::AppBridge.new(
131
+ create_path: create_path,
132
+ details_path: details_path,
133
+ )
134
+ end
135
+
124
136
  def project_config_value(key)
125
137
  return nil unless project.config.key?(key)
126
138
  project.config[key]
@@ -20,6 +20,7 @@ module Script
20
20
  force: false,
21
21
  metadata:,
22
22
  script_config:,
23
+ app_bridge:,
23
24
  module_upload_url:,
24
25
  library:,
25
26
  input_query: nil
@@ -36,6 +37,10 @@ module Script
36
37
  scriptConfigVersion: script_config.version,
37
38
  configurationUi: script_config.configuration_ui,
38
39
  configurationDefinition: script_config.configuration&.to_json,
40
+ appBridge: {
41
+ createPath: app_bridge.create_path,
42
+ detailsPath: app_bridge.details_path,
43
+ },
39
44
  moduleUploadUrl: module_upload_url,
40
45
  inputQuery: input_query,
41
46
  }
@@ -20,9 +20,12 @@ module Theme
20
20
  end
21
21
 
22
22
  def call(_args, name)
23
+ valid_authentication_method!
24
+
23
25
  root = root_value(options, name)
24
26
  flags = options.flags.dup
25
27
  host = flags[:host] || DEFAULT_HTTP_HOST
28
+
26
29
  ShopifyCLI::Theme::DevServer.start(@ctx, root, host: host, **flags) do |syncer|
27
30
  UI::SyncProgressBar.new(syncer).progress(:upload_theme!, delay_low_priority_files: true)
28
31
  end
@@ -38,6 +41,30 @@ module Theme
38
41
  def self.help
39
42
  ShopifyCLI::Context.message("theme.serve.help", ShopifyCLI::TOOL_NAME)
40
43
  end
44
+
45
+ private
46
+
47
+ def valid_authentication_method!
48
+ if exchange_token && !storefront_renderer_token
49
+ ShopifyCLI::Context.abort(error_message, help_message)
50
+ end
51
+ end
52
+
53
+ def error_message
54
+ ShopifyCLI::Context.message("theme.serve.auth.error_message", ShopifyCLI::TOOL_NAME)
55
+ end
56
+
57
+ def help_message
58
+ ShopifyCLI::Context.message("theme.serve.auth.help_message", ShopifyCLI::TOOL_NAME, ShopifyCLI::TOOL_NAME)
59
+ end
60
+
61
+ def exchange_token
62
+ ShopifyCLI::DB.get(:shopify_exchange_token)
63
+ end
64
+
65
+ def storefront_renderer_token
66
+ ShopifyCLI::DB.get(:storefront_renderer_production_exchange_token)
67
+ end
41
68
  end
42
69
  end
43
70
  end
@@ -8,6 +8,11 @@ module Theme
8
8
  Usage: {{command:%1$s theme [ %2$s ]}}
9
9
  HELP
10
10
  ensure_user_error: "You are not authorized to edit themes on %s.",
11
+ unauthorized_error: <<~EOD,
12
+ You can't use Shopify CLI with development stores if you only have Partner staff member access. If you want to use Shopify CLI to work on a development store, then you should be the store owner or create a staff account on the store.
13
+
14
+ If you're the store owner, then you need to log in to the store directly using the store URL at least once (for example, using %s.myshopify.com/admin) before you log in using Shopify CLI. Logging in to the Shopify admin directly connects the development store with your Shopify login.
15
+ EOD
11
16
  ensure_user_try_this: <<~ENSURE_USER,
12
17
  Check if your user is activated, has permission to edit themes at the store, and try to re-login.
13
18
  ENSURE_USER
@@ -116,6 +121,14 @@ module Theme
116
121
  viewing_theme: "Viewing theme…",
117
122
  syncing_theme: "Syncing theme #%s on %s",
118
123
  open_fail: "Couldn't open the theme",
124
+ auth: {
125
+ error_message: <<~ERROR_MESSAGE,
126
+ It looks like you are using credentials that do not work with {{command:%s theme serve}}.
127
+ ERROR_MESSAGE
128
+ help_message: <<~HELP_MESSAGE,
129
+ Run {{command:%s logout}} and {{command:%s login --password "" --store STORE}} to force the authentication thought your browser.
130
+ HELP_MESSAGE
131
+ },
119
132
  operation: {
120
133
  status: {
121
134
  error: "ERROR",
@@ -98,6 +98,8 @@ module ShopifyCLI
98
98
  end
99
99
 
100
100
  def get_shop_or_abort(ctx)
101
+ env_store = Environment.store
102
+ return env_store unless env_store.nil?
101
103
  ctx.abort(
102
104
  ctx.message("core.populate.error.no_shop", ShopifyCLI::TOOL_NAME)
103
105
  ) unless ShopifyCLI::DB.exists?(:shop)
@@ -119,7 +121,8 @@ module ShopifyCLI
119
121
  end
120
122
 
121
123
  def access_token(ctx, shop)
122
- ShopifyCLI::DB.get(:shopify_exchange_token) do
124
+ env_token = Environment.admin_auth_token
125
+ env_token || ShopifyCLI::DB.get(:shopify_exchange_token) do
123
126
  authenticate(ctx, shop)
124
127
  ShopifyCLI::DB.get(:shopify_exchange_token)
125
128
  end
@@ -153,7 +156,7 @@ module ShopifyCLI
153
156
  def auth_headers(token)
154
157
  {
155
158
  Authorization: "Bearer #{token}",
156
- "X-Shopify-Access-Token" => token, # TODO: Remove when we no longer need private apps
159
+ "X-Shopify-Access-Token" => token,
157
160
  }
158
161
  end
159
162
  end
@@ -26,8 +26,8 @@ module ShopifyCLI
26
26
  end
27
27
  end
28
28
 
29
- # As password auth will soon be deprecated, we enable only in CI
30
- if @ctx.ci? && (password = options.flags[:password] || @ctx.getenv("SHOPIFY_PASSWORD"))
29
+ password = options.flags[:password] || @ctx.getenv("SHOPIFY_PASSWORD")
30
+ if !password.nil? && !password.empty?
31
31
  ShopifyCLI::DB.set(shopify_exchange_token: password)
32
32
  else
33
33
  IdentityAuth.new(ctx: @ctx).authenticate(spinner: true)
@@ -31,7 +31,7 @@ module ShopifyCLI
31
31
  return unless has_shop?
32
32
 
33
33
  ShopifyCLI::Theme::DevelopmentTheme.delete(@ctx)
34
- rescue ShopifyCLI::API::APIRequestError
34
+ rescue ShopifyCLI::API::APIRequestError, ShopifyCLI::Abort, ShopifyCLI::AbortSilent
35
35
  # Ignore since we can't delete it
36
36
  end
37
37
  end
@@ -53,9 +53,11 @@ module ShopifyCLI
53
53
 
54
54
  # Authentication
55
55
  AUTH_TOKEN = "SHOPIFY_CLI_AUTH_TOKEN"
56
+ ADMIN_AUTH_TOKEN = "SHOPIFY_CLI_ADMIN_AUTH_TOKEN"
56
57
 
57
58
  # Monorail
58
59
  MONORAIL_REAL_EVENTS = "MONORAIL_REAL_EVENTS"
60
+ STORE = "SHOPIFY_CLI_STORE"
59
61
  end
60
62
 
61
63
  module SupportedVersions
@@ -17,6 +17,7 @@ module ShopifyCLI
17
17
  GEM_LATEST_URI = URI.parse("https://rubygems.org/api/v1/versions/shopify-cli/latest.json")
18
18
  VERSION_CHECK_SECTION = "versioncheck"
19
19
  LAST_CHECKED_AT_FIELD = "last_checked_at"
20
+ LATEST_VERSION_FIELD = "latest_version"
20
21
  VERSION_CHECK_INTERVAL = 86400
21
22
 
22
23
  class << self
@@ -619,11 +620,17 @@ module ShopifyCLI
619
620
  # : nil otherwise
620
621
  #
621
622
  def new_version
622
- if (time_of_last_check + VERSION_CHECK_INTERVAL) < (now = Time.now.to_i)
623
- update_time_of_last_check(now)
624
- latest_version = retrieve_latest_gem_version
625
- latest_version unless latest_version == ShopifyCLI::VERSION
623
+ if (time_of_last_check + VERSION_CHECK_INTERVAL) < (Time.now.to_i)
624
+ # Fork is not supported in Windows
625
+ if Process.respond_to?(:fork)
626
+ fork { retrieve_latest_gem_version }
627
+ else
628
+ thread = Thread.new { retrieve_latest_gem_version }
629
+ at_exit { thread.join }
630
+ end
626
631
  end
632
+ latest_version = ShopifyCLI::Config.get(VERSION_CHECK_SECTION, LATEST_VERSION_FIELD, default: ShopifyCLI::VERSION)
633
+ latest_version if ::Semantic::Version.new(latest_version) > ::Semantic::Version.new(ShopifyCLI::VERSION)
627
634
  end
628
635
 
629
636
  # Returns file extension depending on OS
@@ -669,17 +676,15 @@ module ShopifyCLI
669
676
  def retrieve_latest_gem_version
670
677
  response = Net::HTTP.get_response(GEM_LATEST_URI)
671
678
  latest = JSON.parse(response.body)
672
- latest["version"]
679
+ ShopifyCLI::Config.set(VERSION_CHECK_SECTION, LATEST_VERSION_FIELD, latest["version"])
673
680
  rescue
674
681
  nil
682
+ ensure
683
+ ShopifyCLI::Config.set(VERSION_CHECK_SECTION, LAST_CHECKED_AT_FIELD, Time.now.to_i)
675
684
  end
676
685
 
677
686
  def time_of_last_check
678
687
  (val = ShopifyCLI::Config.get(VERSION_CHECK_SECTION, LAST_CHECKED_AT_FIELD)) ? val.to_i : 0
679
688
  end
680
-
681
- def update_time_of_last_check(time)
682
- ShopifyCLI::Config.set(VERSION_CHECK_SECTION, LAST_CHECKED_AT_FIELD, time)
683
- end
684
689
  end
685
690
  end
@@ -166,6 +166,14 @@ module ShopifyCLI
166
166
  env_variables[Constants::EnvironmentVariables::AUTH_TOKEN]
167
167
  end
168
168
 
169
+ def self.admin_auth_token(env_variables: ENV)
170
+ env_variables[Constants::EnvironmentVariables::ADMIN_AUTH_TOKEN]
171
+ end
172
+
173
+ def self.store(env_variables: ENV)
174
+ env_variables[Constants::EnvironmentVariables::STORE]
175
+ end
176
+
169
177
  def self.env_variable_truthy?(variable_name, env_variables: ENV)
170
178
  TRUTHY_ENV_VARIABLE_VALUES.include?(env_variables[variable_name.to_s])
171
179
  end
@@ -8,23 +8,23 @@ module ShopifyCLI
8
8
  class PartnersAPI
9
9
  class AppExtensions
10
10
  class << self
11
- def fetch_apps_extensions(ctx, orgs, type)
12
- jobs = apps(orgs).map { |app| AppExtensions::Job.new(ctx, app, type) }
11
+ def fetch_apps_extensions(ctx, org, type)
12
+ jobs = apps(org).map { |app| AppExtensions::Job.new(ctx, app, type) }
13
13
 
14
14
  consume_jobs!(jobs)
15
15
  patch_apps_with_extensions!(jobs)
16
16
 
17
- orgs
17
+ org
18
18
  end
19
19
 
20
20
  private
21
21
 
22
- def apps(orgs)
23
- orgs.flat_map { |org| org["apps"] }
22
+ def apps(org)
23
+ (org && org["apps"]) || []
24
24
  end
25
25
 
26
26
  def consume_jobs!(jobs)
27
- thread_pool = ShopifyCLI::ThreadPool.new
27
+ thread_pool = ShopifyCLI::ThreadPool.new(pool_size: 1)
28
28
  jobs.each do |job|
29
29
  thread_pool.schedule(job)
30
30
  end
@@ -18,7 +18,7 @@ module ShopifyCLI
18
18
  org
19
19
  end
20
20
 
21
- def fetch_with_app(ctx)
21
+ def fetch_all_with_apps(ctx)
22
22
  resp = PartnersAPI.query(ctx, "all_orgs_with_apps")
23
23
  (resp&.dig("data", "organizations", "nodes") || []).map do |org|
24
24
  org["stores"] = (org.dig("stores", "nodes") || [])
@@ -27,9 +27,17 @@ module ShopifyCLI
27
27
  end
28
28
  end
29
29
 
30
- def fetch_with_extensions(ctx, type)
31
- orgs = fetch_with_app(ctx)
32
- AppExtensions.fetch_apps_extensions(ctx, orgs, type)
30
+ def fetch_with_apps(ctx, id:)
31
+ resp = PartnersAPI.query(ctx, "find_organization_with_apps", id: id)
32
+ organization = resp&.dig("data", "organizations", "nodes")&.first
33
+ return unless organization
34
+
35
+ organization.tap { organization["apps"] = (organization.dig("apps", "nodes") || []) }
36
+ end
37
+
38
+ def fetch_with_extensions(ctx, type, id:)
39
+ organization = fetch_with_apps(ctx, id: id)
40
+ AppExtensions.fetch_apps_extensions(ctx, organization, type)
33
41
  end
34
42
  end
35
43
  end
@@ -184,7 +184,7 @@ module ShopifyCLI
184
184
 
185
185
  context.puts(context.message("core.app.create.rails.adding_shopify_gem"))
186
186
  File.open(File.join(context.root, "Gemfile"), "a") do |f|
187
- f.puts "\ngem 'shopify_app', '>=18.1.0'"
187
+ f.puts "\ngem 'shopify_app', '~>19.0.1'"
188
188
  end
189
189
  CLI::UI::Frame.open(context.message("core.app.create.rails.running_bundle_install")) do
190
190
  syscall(%w(bundle install))
@@ -25,7 +25,7 @@ module ShopifyCLI
25
25
  if Shopifolk.check && wants_to_run_against_shopify_org?
26
26
  Shopifolk.act_as_shopify_organization
27
27
  end
28
- orgs = PartnersAPI::Organizations.fetch_with_app(@ctx)
28
+ orgs = PartnersAPI::Organizations.fetch_all_with_apps(@ctx)
29
29
  org_id = if orgs.count == 1
30
30
  orgs.first["id"]
31
31
  else