shopify-cli 2.15.6 → 2.17.0

Sign up to get free protection for your applications and to get access to all the features.
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