shopify-cli 1.11.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (207) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +1 -1
  3. data/.github/CONTRIBUTING.md +7 -7
  4. data/.github/DESIGN.md +3 -3
  5. data/.github/PULL_REQUEST_TEMPLATE.md +1 -1
  6. data/.github/workflows/build.yml +1 -1
  7. data/.gitignore +3 -0
  8. data/.rubocop.yml +3 -1
  9. data/.ruby-version +1 -1
  10. data/CHANGELOG.md +48 -20
  11. data/Gemfile +4 -0
  12. data/Gemfile.lock +32 -0
  13. data/LICENSE +4 -1
  14. data/README.md +92 -26
  15. data/RELEASING.md +29 -7
  16. data/Rakefile +2 -2
  17. data/SECURITY.md +1 -1
  18. data/bin/load_shopify.rb +1 -1
  19. data/bin/shopify +3 -3
  20. data/dev.yml +1 -1
  21. data/docs/app/node/index.md +1 -1
  22. data/docs/app/rails/index.md +1 -1
  23. data/docs/core/index.md +1 -1
  24. data/docs/getting-started/index.md +1 -1
  25. data/docs/getting-started/install/index.md +1 -1
  26. data/docs/getting-started/migrate/index.md +1 -1
  27. data/docs/getting-started/uninstall/index.md +1 -1
  28. data/docs/getting-started/upgrade/index.md +1 -1
  29. data/docs/help/start-app/index.md +1 -1
  30. data/docs/index.md +1 -1
  31. data/ext/shopify-cli/extconf.rb +17 -5
  32. data/install.sh +1 -1
  33. data/lib/docgen/index_template.md.erb +2 -2
  34. data/lib/graphql/all_orgs_with_extensions.graphql +37 -0
  35. data/lib/graphql/find_organization.graphql +2 -1
  36. data/lib/project_types/extension/cli.rb +18 -15
  37. data/lib/project_types/extension/commands/build.rb +4 -5
  38. data/lib/project_types/extension/commands/connect.rb +35 -0
  39. data/lib/project_types/extension/commands/create.rb +12 -16
  40. data/lib/project_types/extension/commands/extension_command.rb +2 -2
  41. data/lib/project_types/extension/commands/info.rb +86 -0
  42. data/lib/project_types/extension/commands/push.rb +8 -7
  43. data/lib/project_types/extension/commands/register.rb +4 -5
  44. data/lib/project_types/extension/commands/serve.rb +5 -8
  45. data/lib/project_types/extension/commands/tunnel.rb +3 -1
  46. data/lib/project_types/extension/errors.rb +9 -0
  47. data/lib/project_types/extension/extension_project.rb +5 -0
  48. data/lib/project_types/extension/features/argo.rb +6 -6
  49. data/lib/project_types/extension/features/argo_runtime.rb +22 -38
  50. data/lib/project_types/extension/features/argo_serve.rb +25 -20
  51. data/lib/project_types/extension/forms/connect.rb +42 -0
  52. data/lib/project_types/extension/forms/questions/ask_name.rb +14 -6
  53. data/lib/project_types/extension/forms/questions/ask_registration.rb +51 -0
  54. data/lib/project_types/extension/messages/messages.rb +75 -11
  55. data/lib/project_types/extension/models/specification.rb +1 -0
  56. data/lib/project_types/extension/models/specification_handlers/{checkout_argo_extension.rb → checkout_ui_extension.rb} +3 -1
  57. data/lib/project_types/extension/models/specification_handlers/default.rb +21 -6
  58. data/lib/project_types/extension/models/specification_handlers/theme_app_extension.rb +86 -0
  59. data/lib/project_types/extension/models/specifications.rb +1 -0
  60. data/lib/project_types/extension/tasks/configure_features.rb +6 -7
  61. data/lib/project_types/extension/tasks/configure_options.rb +20 -0
  62. data/lib/project_types/extension/tasks/get_extensions.rb +32 -0
  63. data/lib/project_types/node/cli.rb +9 -21
  64. data/lib/project_types/node/commands/connect.rb +8 -2
  65. data/lib/project_types/node/commands/create.rb +9 -5
  66. data/lib/project_types/node/commands/deploy.rb +15 -5
  67. data/lib/project_types/node/commands/deploy/heroku.rb +29 -29
  68. data/lib/project_types/node/commands/generate.rb +4 -2
  69. data/lib/project_types/node/commands/open.rb +4 -2
  70. data/lib/project_types/node/commands/serve.rb +3 -2
  71. data/lib/project_types/node/commands/tunnel.rb +4 -2
  72. data/lib/project_types/node/messages/messages.rb +46 -89
  73. data/lib/project_types/rails/cli.rb +9 -21
  74. data/lib/project_types/rails/commands/connect.rb +8 -2
  75. data/lib/project_types/rails/commands/create.rb +10 -6
  76. data/lib/project_types/rails/commands/deploy.rb +15 -5
  77. data/lib/project_types/rails/commands/deploy/heroku.rb +84 -82
  78. data/lib/project_types/rails/commands/generate.rb +15 -5
  79. data/lib/project_types/rails/commands/generate/webhook.rb +28 -26
  80. data/lib/project_types/rails/commands/open.rb +4 -2
  81. data/lib/project_types/rails/commands/serve.rb +3 -2
  82. data/lib/project_types/rails/commands/tunnel.rb +4 -2
  83. data/lib/project_types/rails/messages/messages.rb +54 -101
  84. data/lib/project_types/script/cli.rb +18 -20
  85. data/lib/project_types/script/commands/create.rb +3 -1
  86. data/lib/project_types/script/commands/push.rb +12 -5
  87. data/lib/project_types/script/config/extension_points.yml +0 -3
  88. data/lib/project_types/script/graphql/app_script_update_or_create.graphql +9 -3
  89. data/lib/project_types/script/layers/application/create_script.rb +6 -5
  90. data/lib/project_types/script/layers/application/push_script.rb +2 -1
  91. data/lib/project_types/script/layers/domain/errors.rb +6 -11
  92. data/lib/project_types/script/layers/domain/push_package.rb +4 -8
  93. data/lib/project_types/script/layers/domain/script_json.rb +32 -0
  94. data/lib/project_types/script/layers/domain/script_project.rb +1 -1
  95. data/lib/project_types/script/layers/infrastructure/errors.rb +14 -18
  96. data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_project_creator.rb +105 -0
  97. data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_task_runner.rb +103 -0
  98. data/lib/project_types/script/layers/infrastructure/languages/project_creator.rb +26 -0
  99. data/lib/project_types/script/layers/infrastructure/languages/rust_project_creator.rb +73 -0
  100. data/lib/project_types/script/layers/infrastructure/languages/rust_task_runner.rb +60 -0
  101. data/lib/project_types/script/layers/infrastructure/languages/task_runner.rb +21 -0
  102. data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +2 -4
  103. data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +45 -34
  104. data/lib/project_types/script/layers/infrastructure/script_service.rb +20 -14
  105. data/lib/project_types/script/messages/messages.rb +66 -55
  106. data/lib/project_types/script/tasks/ensure_env.rb +22 -1
  107. data/lib/project_types/script/ui/error_handler.rb +32 -32
  108. data/lib/project_types/theme/cli.rb +15 -27
  109. data/lib/project_types/theme/commands/check.rb +33 -0
  110. data/lib/project_types/theme/commands/delete.rb +64 -0
  111. data/lib/project_types/theme/commands/language_server.rb +16 -0
  112. data/lib/project_types/theme/commands/package.rb +55 -0
  113. data/lib/project_types/theme/commands/publish.rb +43 -0
  114. data/lib/project_types/theme/commands/pull.rb +51 -0
  115. data/lib/project_types/theme/commands/push.rb +58 -32
  116. data/lib/project_types/theme/commands/serve.rb +7 -17
  117. data/lib/project_types/theme/forms/confirm_store.rb +15 -0
  118. data/lib/project_types/theme/forms/select.rb +59 -0
  119. data/lib/project_types/theme/messages/messages.rb +110 -106
  120. data/lib/project_types/theme/ui/sync_progress_bar.rb +20 -0
  121. data/lib/shopify-cli/admin_api.rb +53 -35
  122. data/lib/shopify-cli/admin_api/populate_resource_command.rb +6 -14
  123. data/lib/shopify-cli/admin_api/schema.rb +1 -10
  124. data/lib/shopify-cli/api.rb +29 -14
  125. data/lib/shopify-cli/command.rb +15 -3
  126. data/lib/shopify-cli/commands.rb +7 -2
  127. data/lib/shopify-cli/commands/help.rb +2 -29
  128. data/lib/shopify-cli/commands/login.rb +95 -0
  129. data/lib/shopify-cli/commands/logout.rb +24 -8
  130. data/lib/shopify-cli/commands/populate.rb +23 -0
  131. data/lib/{project_types/node → shopify-cli}/commands/populate/customer.rb +2 -8
  132. data/lib/{project_types/node → shopify-cli}/commands/populate/draft_order.rb +2 -2
  133. data/lib/{project_types/node → shopify-cli}/commands/populate/product.rb +2 -8
  134. data/lib/shopify-cli/commands/store.rb +15 -0
  135. data/lib/shopify-cli/commands/switch.rb +39 -0
  136. data/lib/shopify-cli/commands/system.rb +12 -0
  137. data/lib/shopify-cli/commands/whoami.rb +28 -0
  138. data/lib/shopify-cli/connect.rb +32 -0
  139. data/lib/shopify-cli/context.rb +65 -4
  140. data/lib/shopify-cli/core/entry_point.rb +3 -22
  141. data/lib/shopify-cli/db.rb +4 -4
  142. data/lib/shopify-cli/http_request.rb +10 -0
  143. data/lib/shopify-cli/identity_auth.rb +282 -0
  144. data/lib/shopify-cli/{oauth → identity_auth}/servlet.rb +11 -12
  145. data/lib/shopify-cli/messages/messages.rb +133 -39
  146. data/lib/shopify-cli/partners_api.rb +21 -41
  147. data/lib/shopify-cli/partners_api/organizations.rb +8 -0
  148. data/lib/shopify-cli/project_commands.rb +16 -0
  149. data/lib/shopify-cli/project_type.rb +0 -31
  150. data/lib/shopify-cli/resources/env_file.rb +1 -1
  151. data/lib/shopify-cli/shopifolk.rb +8 -11
  152. data/lib/shopify-cli/sub_command.rb +1 -0
  153. data/lib/shopify-cli/tasks.rb +3 -0
  154. data/lib/shopify-cli/tasks/confirm_store.rb +18 -0
  155. data/lib/shopify-cli/tasks/create_api_client.rb +2 -2
  156. data/lib/shopify-cli/tasks/ensure_authenticated.rb +13 -0
  157. data/lib/shopify-cli/tasks/ensure_loopback_url.rb +1 -1
  158. data/lib/shopify-cli/tasks/ensure_project_type.rb +12 -0
  159. data/lib/shopify-cli/tasks/select_org_and_shop.rb +0 -3
  160. data/lib/shopify-cli/theme/dev_server.rb +98 -0
  161. data/lib/shopify-cli/theme/dev_server/certificate_manager.rb +79 -0
  162. data/lib/shopify-cli/theme/dev_server/header_hash.rb +94 -0
  163. data/lib/shopify-cli/theme/dev_server/hot-reload.js +93 -0
  164. data/lib/shopify-cli/theme/dev_server/hot_reload.rb +76 -0
  165. data/lib/shopify-cli/theme/dev_server/local_assets.rb +87 -0
  166. data/lib/shopify-cli/theme/dev_server/proxy.rb +205 -0
  167. data/lib/shopify-cli/theme/dev_server/sse.rb +75 -0
  168. data/lib/shopify-cli/theme/dev_server/watcher.rb +59 -0
  169. data/lib/shopify-cli/theme/dev_server/web_server.rb +140 -0
  170. data/lib/shopify-cli/theme/development_theme.rb +69 -0
  171. data/lib/shopify-cli/theme/file.rb +112 -0
  172. data/lib/shopify-cli/theme/ignore_filter.rb +109 -0
  173. data/lib/shopify-cli/theme/mime_type.rb +34 -0
  174. data/lib/shopify-cli/theme/syncer.rb +328 -0
  175. data/lib/shopify-cli/theme/theme.rb +204 -0
  176. data/lib/shopify-cli/version.rb +1 -1
  177. data/lib/shopify_cli.rb +18 -11
  178. data/shopify-cli.gemspec +12 -5
  179. data/shopify.fish +1 -1
  180. data/shopify.sh +1 -1
  181. metadata +95 -41
  182. data/.github/workflows/release.yml +0 -61
  183. data/lib/project_types/extension/features/argo_serve_options.rb +0 -40
  184. data/lib/project_types/node/commands/populate.rb +0 -23
  185. data/lib/project_types/rails/commands/populate.rb +0 -23
  186. data/lib/project_types/rails/commands/populate/customer.rb +0 -31
  187. data/lib/project_types/rails/commands/populate/draft_order.rb +0 -28
  188. data/lib/project_types/rails/commands/populate/product.rb +0 -30
  189. data/lib/project_types/script/layers/domain/config_ui.rb +0 -16
  190. data/lib/project_types/script/layers/infrastructure/assemblyscript_project_creator.rb +0 -95
  191. data/lib/project_types/script/layers/infrastructure/assemblyscript_task_runner.rb +0 -101
  192. data/lib/project_types/script/layers/infrastructure/project_creator.rb +0 -24
  193. data/lib/project_types/script/layers/infrastructure/rust_project_creator.rb +0 -71
  194. data/lib/project_types/script/layers/infrastructure/rust_task_runner.rb +0 -58
  195. data/lib/project_types/script/layers/infrastructure/task_runner.rb +0 -19
  196. data/lib/project_types/theme/commands/connect.rb +0 -54
  197. data/lib/project_types/theme/commands/create.rb +0 -48
  198. data/lib/project_types/theme/commands/deploy.rb +0 -38
  199. data/lib/project_types/theme/commands/generate.rb +0 -20
  200. data/lib/project_types/theme/commands/generate/env.rb +0 -79
  201. data/lib/project_types/theme/forms/connect.rb +0 -34
  202. data/lib/project_types/theme/forms/create.rb +0 -22
  203. data/lib/project_types/theme/tasks/ensure_themekit_installed.rb +0 -78
  204. data/lib/project_types/theme/themekit.rb +0 -113
  205. data/lib/shopify-cli/commands/connect.rb +0 -64
  206. data/lib/shopify-cli/commands/create.rb +0 -50
  207. data/lib/shopify-cli/oauth.rb +0 -198
@@ -45,9 +45,18 @@ module ShopifyCli
45
45
  # ShopifyCli::PartnersAPI.query(@ctx, 'all_organizations')
46
46
  #
47
47
  def query(ctx, query_name, **variables)
48
- authenticated_req(ctx) do
48
+ CLI::Kit::Util.begin do
49
49
  api_client(ctx).query(query_name, variables: variables)
50
+ end.retry_after(API::APIRequestUnauthorizedError, retries: 1) do
51
+ ShopifyCli::IdentityAuth.new(ctx: ctx).reauthenticate
50
52
  end
53
+ rescue API::APIRequestUnauthorizedError => e
54
+ if (request_info = auth_failure_info(ctx, e))
55
+ ctx.puts(ctx.message("core.api.error.failed_auth_debugging", request_info))
56
+ end
57
+ ctx.abort(ctx.message("core.api.error.failed_auth"))
58
+ rescue API::APIRequestNotFoundError
59
+ ctx.puts(ctx.message("core.partners_api.error.account_not_found", ShopifyCli::TOOL_NAME))
51
60
  end
52
61
 
53
62
  def partners_url_for(organization_id, api_client_id, local_debug)
@@ -59,15 +68,6 @@ module ShopifyCli
59
68
 
60
69
  private
61
70
 
62
- def authenticated_req(ctx)
63
- yield
64
- rescue API::APIRequestUnauthorizedError
65
- authenticate(ctx)
66
- retry
67
- rescue API::APIRequestNotFoundError
68
- ctx.puts(ctx.message("core.partners_api.error.account_not_found", ShopifyCli::TOOL_NAME))
69
- end
70
-
71
71
  def api_client(ctx)
72
72
  new(
73
73
  ctx: ctx,
@@ -77,37 +77,12 @@ module ShopifyCli
77
77
  end
78
78
 
79
79
  def access_token(ctx)
80
- ShopifyCli::DB.get(:identity_exchange_token) do
81
- authenticate(ctx)
82
- ShopifyCli::DB.get(:identity_exchange_token)
80
+ ShopifyCli::DB.get(:partners_exchange_token) do
81
+ IdentityAuth.new(ctx: ctx).authenticate
82
+ ShopifyCli::DB.get(:partners_exchange_token)
83
83
  end
84
84
  end
85
85
 
86
- def authenticate(ctx)
87
- OAuth.new(
88
- ctx: ctx,
89
- service: "identity",
90
- client_id: cli_id,
91
- scopes: scopes.join(" "),
92
- request_exchange: partners_id,
93
- ).authenticate("#{auth_endpoint}/oauth")
94
- end
95
-
96
- def partners_id
97
- return "271e16d403dfa18082ffb3d197bd2b5f4479c3fc32736d69296829cbb28d41a6" if ENV[LOCAL_DEBUG].nil?
98
- "df89d73339ac3c6c5f0a98d9ca93260763e384d51d6038da129889c308973978"
99
- end
100
-
101
- def cli_id
102
- return "fbdb2649-e327-4907-8f67-908d24cfd7e3" if ENV[LOCAL_DEBUG].nil?
103
- "e5380e02-312a-7408-5718-e07017e9cf52"
104
- end
105
-
106
- def auth_endpoint
107
- return "https://accounts.shopify.com" if ENV[LOCAL_DEBUG].nil?
108
- "https://identity.myshopify.io"
109
- end
110
-
111
86
  def endpoint
112
87
  return "https://partners.shopify.com" if ENV[LOCAL_DEBUG].nil?
113
88
  "https://partners.myshopify.io/"
@@ -122,10 +97,15 @@ module ShopifyCli
122
97
  "https://#{domain}"
123
98
  end
124
99
 
125
- def scopes
126
- %w[openid https://api.shopify.com/auth/partners.app.cli.access].tap do |result|
127
- result << "employee" if ShopifyCli::Shopifolk.acting_as_shopify_organization?
100
+ def auth_failure_info(ctx, error)
101
+ if error.response
102
+ headers = %w(www-authenticate x-request-id)
103
+ request_info = headers.map { |h| "#{h}: #{error.response[h]}" if error.response.key?(h) }.join("\n")
104
+ ctx.debug("Full headers: #{error.response.each_header.to_h}")
105
+ request_info
128
106
  end
107
+ rescue => e
108
+ ctx.debug("Couldn't fetch auth failure information from #{error}: #{e}")
129
109
  end
130
110
  end
131
111
 
@@ -26,6 +26,14 @@ module ShopifyCli
26
26
  org
27
27
  end
28
28
  end
29
+
30
+ def fetch_with_extensions(ctx, type)
31
+ resp = PartnersAPI.query(ctx, "all_orgs_with_extensions", type: type)
32
+ (resp&.dig("data", "organizations", "nodes") || []).map do |org|
33
+ org["apps"] = (org.dig("apps", "nodes") || [])
34
+ org
35
+ end
36
+ end
29
37
  end
30
38
  end
31
39
  end
@@ -0,0 +1,16 @@
1
+ module ShopifyCli
2
+ class ProjectCommands < Command
3
+ def call(*)
4
+ @ctx.puts(self.class.help)
5
+ end
6
+
7
+ def self.help
8
+ project_type = name.split("::")[0].downcase
9
+ ShopifyCli::Context.message(
10
+ "#{project_type}.help",
11
+ ShopifyCli::TOOL_NAME,
12
+ subcommand_registry.command_names.join(" | ")
13
+ )
14
+ end
15
+ end
16
+ end
@@ -5,7 +5,6 @@ module ShopifyCli
5
5
  class << self
6
6
  attr_accessor :project_type,
7
7
  :project_name,
8
- :project_creator_command_class,
9
8
  :project_load_shallow
10
9
 
11
10
  def repository
@@ -49,36 +48,6 @@ module ShopifyCli
49
48
  @project_name = name
50
49
  end
51
50
 
52
- def creator(command_const)
53
- @project_creator_command_class = command_const
54
- ShopifyCli::Commands::Create.subcommand(command_const, @project_type)
55
- end
56
-
57
- def create_command
58
- const_get(@project_creator_command_class)
59
- end
60
-
61
- def connector(command_const)
62
- @project_connector_command_class = command_const
63
- ShopifyCli::Commands::Connect.subcommand(command_const, @project_type)
64
- end
65
-
66
- def connect_command
67
- if @project_connector_command_class.nil?
68
- nil
69
- else
70
- const_get(@project_connector_command_class)
71
- end
72
- end
73
-
74
- def register_command(const, cmd)
75
- return if project_load_shallow
76
- Context.new.abort(
77
- Context.message("core.project_type.error.cannot_override_core", cmd, const)
78
- ) if Commands.core_command?(cmd)
79
- Commands.register(const, cmd)
80
- end
81
-
82
51
  def register_task(task, name)
83
52
  return if project_load_shallow
84
53
  ShopifyCli::Tasks.register(task, name)
@@ -22,7 +22,7 @@ module ShopifyCli
22
22
  def parse(directory)
23
23
  File.read(File.join(directory, FILENAME))
24
24
  .gsub("\r\n", "\n").split("\n").each_with_object({}) do |line, output|
25
- match = /\A([A-Za-z_0-9]+)=(.*)\z/.match(line)
25
+ match = /\A([A-Za-z_0-9]+)\s*=\s*(.*)\z/.match(line)
26
26
  if match
27
27
  key = match[1]
28
28
  output[key] = case match[2]
@@ -11,8 +11,6 @@ module ShopifyCli
11
11
  FEATURE_NAME = "shopifolk"
12
12
 
13
13
  class << self
14
- attr_writer :acting_as_shopify_organization
15
-
16
14
  ##
17
15
  # will return if the user appears to be a Shopify employee, based on several heuristics
18
16
  #
@@ -29,15 +27,16 @@ module ShopifyCli
29
27
  end
30
28
 
31
29
  def act_as_shopify_organization
32
- @acting_as_shopify_organization = true
30
+ DB.set(acting_as_shopify_organization: true)
33
31
  end
34
32
 
35
33
  def acting_as_shopify_organization?
36
- !!@acting_as_shopify_organization || (Project.has_current? && Project.current.config["shopify_organization"])
34
+ !!(DB.get(:acting_as_shopify_organization) ||
35
+ (Project.has_current? && Project.current.config["shopify_organization"]))
37
36
  end
38
37
 
39
38
  def reset
40
- @acting_as_shopify_organization = nil
39
+ DB.del(:acting_as_shopify_organization)
41
40
  end
42
41
  end
43
42
 
@@ -72,12 +71,10 @@ module ShopifyCli
72
71
  end
73
72
 
74
73
  def ini
75
- @ini ||= begin
76
- if File.exist?(GCLOUD_CONFIG_FILE)
77
- CLI::Kit::Ini
78
- .new(GCLOUD_CONFIG_FILE, default_section: "[#{SECTION}]", convert_types: false)
79
- .tap(&:parse).ini
80
- end
74
+ @ini ||= if File.exist?(GCLOUD_CONFIG_FILE)
75
+ CLI::Kit::Ini
76
+ .new(GCLOUD_CONFIG_FILE, default_section: "[#{SECTION}]", convert_types: false)
77
+ .tap(&:parse).ini
81
78
  end
82
79
  end
83
80
  end
@@ -9,6 +9,7 @@ module ShopifyCli
9
9
  args = cmd.options.parse(@_options, args[1..-1] || [])
10
10
  return call_help(parent_command, command_name) if cmd.options.help
11
11
  run_prerequisites
12
+
12
13
  cmd.call(args, command_name)
13
14
  end
14
15
  end
@@ -30,9 +30,12 @@ module ShopifyCli
30
30
  Registry.add(-> () { const_get(task) }, name)
31
31
  end
32
32
 
33
+ register :ConfirmStore, :confirm_store, "shopify-cli/tasks/confirm_store"
33
34
  register :CreateApiClient, :create_api_client, "shopify-cli/tasks/create_api_client"
35
+ register :EnsureAuthenticated, :ensure_authenticated, "shopify-cli/tasks/ensure_authenticated"
34
36
  register :EnsureEnv, :ensure_env, "shopify-cli/tasks/ensure_env"
35
37
  register :EnsureLoopbackURL, :ensure_loopback_url, "shopify-cli/tasks/ensure_loopback_url"
38
+ register :EnsureProjectType, :ensure_project_type, "shopify-cli/tasks/ensure_project_type"
36
39
  register :EnsureDevStore, :ensure_dev_store, "shopify-cli/tasks/ensure_dev_store"
37
40
  register :SelectOrgAndShop, :select_org_and_shop, "shopify-cli/tasks/select_org_and_shop"
38
41
  register :UpdateDashboardURLS, :update_dashboard_urls, "shopify-cli/tasks/update_dashboard_urls"
@@ -0,0 +1,18 @@
1
+ require "shopify_cli"
2
+
3
+ module ShopifyCli
4
+ module Tasks
5
+ class ConfirmStore < ShopifyCli::Task
6
+ def call(ctx)
7
+ @ctx = ctx
8
+ store = ShopifyCli::AdminAPI.get_shop_or_abort(ctx)
9
+ if CLI::UI::Prompt.confirm(ctx.message("core.tasks.confirm_store.prompt", store), default: false)
10
+ ctx.puts(ctx.message("core.tasks.confirm_store.confirmation", store))
11
+ else
12
+ ctx.puts(ctx.message("core.tasks.confirm_store.cancelling"))
13
+ raise AbortSilent
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -4,7 +4,7 @@ module ShopifyCli
4
4
  module Tasks
5
5
  class CreateApiClient < ShopifyCli::Task
6
6
  VALID_APP_TYPES = %w(public custom)
7
- DEFAULT_APP_URL = "https://shopify.github.io/shopify-app-cli/help/start-app/"
7
+ DEFAULT_APP_URL = "https://shopify.github.io/shopify-cli/help/start-app/"
8
8
 
9
9
  def call(ctx, org_id:, title:, type:)
10
10
  resp = ShopifyCli::PartnersAPI.query(
@@ -14,7 +14,7 @@ module ShopifyCli
14
14
  title: title,
15
15
  type: type,
16
16
  app_url: DEFAULT_APP_URL,
17
- redir: [OAuth::REDIRECT_HOST]
17
+ redir: [IdentityAuth::REDIRECT_HOST]
18
18
  )
19
19
 
20
20
  unless resp
@@ -0,0 +1,13 @@
1
+ require "shopify_cli"
2
+
3
+ module ShopifyCli
4
+ module Tasks
5
+ class EnsureAuthenticated < ShopifyCli::Task
6
+ def call(ctx)
7
+ ctx.abort(
8
+ ctx.message("core.identity_auth.login_prompt", ShopifyCli::TOOL_NAME)
9
+ ) unless ShopifyCli::IdentityAuth::IDENTITY_ACCESS_TOKENS.all? { |key| ShopifyCli::DB.exists?(key) }
10
+ end
11
+ end
12
+ end
13
+ end
@@ -5,7 +5,7 @@ module ShopifyCli
5
5
  @ctx = ctx
6
6
  api_key = Project.current.env.api_key
7
7
  result = ShopifyCli::PartnersAPI.query(ctx, "get_app_urls", apiKey: api_key)
8
- loopback = OAuth::REDIRECT_HOST
8
+ loopback = IdentityAuth::REDIRECT_HOST
9
9
  app = result["data"]["app"]
10
10
  urls = app["redirectUrlWhitelist"]
11
11
  if urls.grep(/#{loopback}/).empty?
@@ -0,0 +1,12 @@
1
+ require "shopify_cli"
2
+
3
+ module ShopifyCli
4
+ module Tasks
5
+ class EnsureProjectType < ShopifyCli::Task
6
+ def call(ctx, project_type)
7
+ return true if project_type.to_sym == ShopifyCli::Project.current_project_type
8
+ ctx.abort(ctx.message("core.tasks.ensure_project_type.wrong_project_type", project_type))
9
+ end
10
+ end
11
+ end
12
+ end
@@ -8,9 +8,6 @@ module ShopifyCli
8
8
  def call(ctx, organization_id: nil, shop_domain: nil)
9
9
  @ctx = ctx
10
10
  return response(organization_id.to_i, shop_domain) unless organization_id.nil? || shop_domain.nil?
11
- if Shopifolk.check && wants_to_run_against_shopify_org?
12
- Shopifolk.act_as_shopify_organization
13
- end
14
11
  org = get_organization(organization_id)
15
12
  unless Shopifolk.acting_as_shopify_organization?
16
13
  shop_domain ||= get_shop_domain(org)
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+ require_relative "development_theme"
3
+ require_relative "ignore_filter"
4
+ require_relative "syncer"
5
+
6
+ require_relative "dev_server/hot_reload"
7
+ require_relative "dev_server/header_hash"
8
+ require_relative "dev_server/local_assets"
9
+ require_relative "dev_server/proxy"
10
+ require_relative "dev_server/sse"
11
+ require_relative "dev_server/watcher"
12
+ require_relative "dev_server/web_server"
13
+ require_relative "dev_server/certificate_manager"
14
+
15
+ require "pathname"
16
+
17
+ module ShopifyCli
18
+ module Theme
19
+ module DevServer
20
+ class << self
21
+ attr_accessor :ctx
22
+
23
+ def start(ctx, root, port: 9292, silent: false)
24
+ @ctx = ctx
25
+ theme = DevelopmentTheme.new(ctx, root: root)
26
+ ignore_filter = IgnoreFilter.from_path(root)
27
+ @syncer = Syncer.new(ctx, theme: theme, ignore_filter: ignore_filter)
28
+ watcher = Watcher.new(ctx, theme: theme, syncer: @syncer, ignore_filter: ignore_filter)
29
+
30
+ # Setup the middleware stack. Mimics Rack::Builder / config.ru, but in reverse order
31
+ @app = Proxy.new(ctx, theme: theme, syncer: @syncer)
32
+ @app = LocalAssets.new(ctx, @app, theme: theme)
33
+ @app = HotReload.new(ctx, @app, theme: theme, watcher: watcher, ignore_filter: ignore_filter)
34
+ stopped = false
35
+
36
+ theme.ensure_exists!
37
+
38
+ trap("INT") do
39
+ stopped = true
40
+ stop
41
+ end
42
+
43
+ CLI::UI::Frame.open(@ctx.message("theme.serve.serve")) do
44
+ ctx.print_task("Syncing theme ##{theme.id} on #{theme.shop}")
45
+ @syncer.start_threads
46
+ if silent
47
+ @syncer.upload_theme!(delay_low_priority_files: true)
48
+ else
49
+ @syncer.upload_theme_with_progress_bar!(delay_low_priority_files: true)
50
+ end
51
+
52
+ return if stopped
53
+
54
+ ctx.puts("")
55
+ ctx.puts("Serving #{theme.root}")
56
+ ctx.puts("")
57
+ ctx.open_url!("http://127.0.0.1:#{port}")
58
+ ctx.puts("")
59
+ ctx.puts("Customize this theme in the Online Store Editor:")
60
+ ctx.puts("{{green:#{theme.editor_url}}}")
61
+ ctx.puts("")
62
+ ctx.puts("Share this theme preview:")
63
+ ctx.puts("{{green:#{theme.preview_url}}}")
64
+ ctx.puts("")
65
+ ctx.puts("(Use Ctrl-C to stop)")
66
+ end
67
+
68
+ logger = if ctx.debug?
69
+ WEBrick::Log.new(nil, WEBrick::BasicLog::INFO)
70
+ else
71
+ WEBrick::Log.new(nil, WEBrick::BasicLog::FATAL)
72
+ end
73
+
74
+ watcher.start
75
+ WebServer.run(
76
+ @app,
77
+ Port: port,
78
+ Logger: logger,
79
+ AccessLog: [],
80
+ )
81
+ watcher.stop
82
+
83
+ rescue ShopifyCli::API::APIRequestForbiddenError,
84
+ ShopifyCli::API::APIRequestUnauthorizedError
85
+ @ctx.abort("You are not authorized to edit themes on #{theme.shop}.\n" \
86
+ "Make sure you are a user of that store, and allowed to edit themes.")
87
+ end
88
+
89
+ def stop
90
+ @ctx.puts("Stopping ...")
91
+ @app.close
92
+ @syncer.shutdown
93
+ WebServer.shutdown
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openssl"
4
+
5
+ module ShopifyCli
6
+ module Theme
7
+ module DevServer
8
+ class CertificateManager
9
+ attr_reader :ctx, :domain_name, :certificate, :private_key
10
+
11
+ ISSUER_EXTENSIONS = [
12
+ ["subjectKeyIdentifier", "hash", false],
13
+ ["authorityKeyIdentifier", "keyid:always", false],
14
+ ]
15
+
16
+ def initialize(ctx, domain_name)
17
+ @ctx = ctx
18
+ @domain_name = domain_name
19
+ end
20
+
21
+ def find_or_create_certificates!
22
+ @private_key = if (private_key_pem = ShopifyCli::DB.get(:ssl_private_key))
23
+ OpenSSL::PKey::RSA.new(private_key_pem)
24
+ else
25
+ OpenSSL::PKey::RSA.new(2048)
26
+ end
27
+
28
+ @certificate = if (certificate_pem = ShopifyCli::DB.get(:ssl_certificate))
29
+ OpenSSL::X509::Certificate.new(certificate_pem)
30
+ else
31
+ x509_certificate = build_x509_certificate
32
+
33
+ sign_certificate!(x509_certificate)
34
+
35
+ x509_certificate
36
+ end
37
+
38
+ ShopifyCli::DB.set(ssl_certificate: certificate.to_pem)
39
+ ShopifyCli::DB.set(ssl_private_key: private_key.to_pem)
40
+ end
41
+
42
+ private
43
+
44
+ def build_x509_certificate
45
+ certificate = OpenSSL::X509::Certificate.new
46
+
47
+ certificate.public_key = private_key.public_key
48
+ certificate.subject = subject
49
+ certificate.version = 2
50
+ certificate.serial = 0x0
51
+
52
+ certificate.not_before = Time.now.utc
53
+ certificate.not_after = Time.now.utc + 365 * 24 * 60 * 60
54
+
55
+ certificate
56
+ end
57
+
58
+ def sign_certificate!(certificate)
59
+ ef = OpenSSL::X509::ExtensionFactory.new
60
+
61
+ ef.subject_certificate = certificate
62
+ ef.issuer_certificate = certificate
63
+
64
+ ISSUER_EXTENSIONS.each do |args|
65
+ certificate.add_extension(ef.create_extension(*args))
66
+ end
67
+
68
+ certificate.add_extension(ef.create_extension("subjectAltName", "DNS:#{@domain_name}", false))
69
+
70
+ certificate.sign(private_key, OpenSSL::Digest.new("SHA256"))
71
+ end
72
+
73
+ def subject
74
+ OpenSSL::X509::Name.parse("/CN=#{@domain_name}/")
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end