shopify-cli 1.13.1 → 2.1.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 (200) 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/workflows/build.yml +1 -1
  6. data/.gitignore +3 -0
  7. data/.rubocop.yml +3 -1
  8. data/.ruby-version +1 -1
  9. data/CHANGELOG.md +60 -26
  10. data/Gemfile +4 -0
  11. data/Gemfile.lock +32 -0
  12. data/LICENSE +4 -1
  13. data/README.md +94 -26
  14. data/RELEASING.md +31 -7
  15. data/Rakefile +2 -2
  16. data/SECURITY.md +1 -1
  17. data/THEMEKIT_MIGRATION.md +18 -0
  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/api_versions.graphql +1 -1
  36. data/lib/graphql/find_organization.graphql +2 -1
  37. data/lib/project_types/extension/cli.rb +19 -15
  38. data/lib/project_types/extension/commands/build.rb +4 -5
  39. data/lib/project_types/extension/commands/check.rb +44 -0
  40. data/lib/project_types/extension/commands/connect.rb +35 -0
  41. data/lib/project_types/extension/commands/create.rb +12 -16
  42. data/lib/project_types/extension/commands/extension_command.rb +2 -2
  43. data/lib/project_types/extension/commands/info.rb +86 -0
  44. data/lib/project_types/extension/commands/push.rb +8 -7
  45. data/lib/project_types/extension/commands/register.rb +4 -5
  46. data/lib/project_types/extension/commands/serve.rb +5 -8
  47. data/lib/project_types/extension/commands/tunnel.rb +3 -1
  48. data/lib/project_types/extension/errors.rb +9 -0
  49. data/lib/project_types/extension/extension_project.rb +24 -1
  50. data/lib/project_types/extension/extension_project_keys.rb +1 -0
  51. data/lib/project_types/extension/features/argo.rb +6 -6
  52. data/lib/project_types/extension/features/argo_runtime.rb +22 -66
  53. data/lib/project_types/extension/features/argo_serve.rb +25 -18
  54. data/lib/project_types/extension/forms/connect.rb +42 -0
  55. data/lib/project_types/extension/forms/questions/ask_name.rb +14 -6
  56. data/lib/project_types/extension/forms/questions/ask_registration.rb +51 -0
  57. data/lib/project_types/extension/messages/messages.rb +84 -16
  58. data/lib/project_types/extension/models/specification.rb +1 -0
  59. data/lib/project_types/extension/models/specification_handlers/{checkout_argo_extension.rb → checkout_ui_extension.rb} +3 -1
  60. data/lib/project_types/extension/models/specification_handlers/default.rb +13 -3
  61. data/lib/project_types/extension/models/specification_handlers/theme_app_extension.rb +89 -0
  62. data/lib/project_types/extension/models/specifications.rb +1 -0
  63. data/lib/project_types/extension/tasks/configure_features.rb +6 -7
  64. data/lib/project_types/extension/tasks/configure_options.rb +20 -0
  65. data/lib/project_types/extension/tasks/get_extensions.rb +32 -0
  66. data/lib/project_types/node/cli.rb +9 -21
  67. data/lib/project_types/node/commands/connect.rb +8 -2
  68. data/lib/project_types/node/commands/create.rb +9 -5
  69. data/lib/project_types/node/commands/deploy.rb +15 -5
  70. data/lib/project_types/node/commands/deploy/heroku.rb +29 -29
  71. data/lib/project_types/node/commands/generate.rb +4 -2
  72. data/lib/project_types/node/commands/open.rb +4 -2
  73. data/lib/project_types/node/commands/serve.rb +3 -2
  74. data/lib/project_types/node/commands/tunnel.rb +4 -2
  75. data/lib/project_types/node/messages/messages.rb +47 -90
  76. data/lib/project_types/rails/cli.rb +9 -21
  77. data/lib/project_types/rails/commands/connect.rb +8 -2
  78. data/lib/project_types/rails/commands/create.rb +10 -6
  79. data/lib/project_types/rails/commands/deploy.rb +15 -5
  80. data/lib/project_types/rails/commands/deploy/heroku.rb +84 -82
  81. data/lib/project_types/rails/commands/generate.rb +15 -5
  82. data/lib/project_types/rails/commands/generate/webhook.rb +28 -26
  83. data/lib/project_types/rails/commands/open.rb +4 -2
  84. data/lib/project_types/rails/commands/serve.rb +3 -2
  85. data/lib/project_types/rails/commands/tunnel.rb +4 -2
  86. data/lib/project_types/rails/messages/messages.rb +72 -119
  87. data/lib/project_types/script/cli.rb +6 -8
  88. data/lib/project_types/script/commands/create.rb +3 -1
  89. data/lib/project_types/script/commands/push.rb +7 -4
  90. data/lib/project_types/script/graphql/app_script_update_or_create.graphql +9 -3
  91. data/lib/project_types/script/layers/application/create_script.rb +4 -3
  92. data/lib/project_types/script/layers/domain/errors.rb +6 -11
  93. data/lib/project_types/script/layers/domain/push_package.rb +4 -8
  94. data/lib/project_types/script/layers/domain/script_json.rb +32 -0
  95. data/lib/project_types/script/layers/domain/script_project.rb +1 -1
  96. data/lib/project_types/script/layers/infrastructure/errors.rb +13 -17
  97. data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_project_creator.rb +29 -21
  98. data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +2 -4
  99. data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +45 -34
  100. data/lib/project_types/script/layers/infrastructure/script_service.rb +37 -16
  101. data/lib/project_types/script/messages/messages.rb +64 -54
  102. data/lib/project_types/script/tasks/ensure_env.rb +3 -1
  103. data/lib/project_types/script/ui/error_handler.rb +32 -32
  104. data/lib/project_types/theme/cli.rb +16 -27
  105. data/lib/project_types/theme/commands/check.rb +33 -0
  106. data/lib/project_types/theme/commands/delete.rb +64 -0
  107. data/lib/project_types/theme/commands/init.rb +42 -0
  108. data/lib/project_types/theme/commands/language_server.rb +16 -0
  109. data/lib/project_types/theme/commands/package.rb +55 -0
  110. data/lib/project_types/theme/commands/publish.rb +43 -0
  111. data/lib/project_types/theme/commands/pull.rb +51 -0
  112. data/lib/project_types/theme/commands/push.rb +58 -32
  113. data/lib/project_types/theme/commands/serve.rb +8 -16
  114. data/lib/project_types/theme/forms/confirm_store.rb +15 -0
  115. data/lib/project_types/theme/forms/select.rb +59 -0
  116. data/lib/project_types/theme/messages/messages.rb +118 -103
  117. data/lib/project_types/theme/ui/sync_progress_bar.rb +20 -0
  118. data/lib/shopify-cli/admin_api.rb +57 -38
  119. data/lib/shopify-cli/admin_api/populate_resource_command.rb +6 -14
  120. data/lib/shopify-cli/admin_api/schema.rb +1 -10
  121. data/lib/shopify-cli/api.rb +29 -14
  122. data/lib/shopify-cli/command.rb +15 -3
  123. data/lib/shopify-cli/commands.rb +7 -2
  124. data/lib/shopify-cli/commands/help.rb +2 -29
  125. data/lib/shopify-cli/commands/login.rb +95 -0
  126. data/lib/shopify-cli/commands/logout.rb +24 -8
  127. data/lib/shopify-cli/commands/populate.rb +23 -0
  128. data/lib/{project_types/node → shopify-cli}/commands/populate/customer.rb +2 -8
  129. data/lib/{project_types/node → shopify-cli}/commands/populate/draft_order.rb +2 -2
  130. data/lib/{project_types/node → shopify-cli}/commands/populate/product.rb +2 -8
  131. data/lib/shopify-cli/commands/store.rb +15 -0
  132. data/lib/shopify-cli/commands/switch.rb +39 -0
  133. data/lib/shopify-cli/commands/system.rb +12 -0
  134. data/lib/shopify-cli/commands/whoami.rb +28 -0
  135. data/lib/shopify-cli/connect.rb +32 -0
  136. data/lib/shopify-cli/context.rb +65 -4
  137. data/lib/shopify-cli/core/entry_point.rb +3 -22
  138. data/lib/shopify-cli/core/monorail.rb +6 -2
  139. data/lib/shopify-cli/db.rb +4 -4
  140. data/lib/shopify-cli/http_request.rb +16 -0
  141. data/lib/shopify-cli/identity_auth.rb +282 -0
  142. data/lib/shopify-cli/{oauth → identity_auth}/servlet.rb +11 -12
  143. data/lib/shopify-cli/messages/messages.rb +140 -46
  144. data/lib/shopify-cli/packager.rb +5 -5
  145. data/lib/shopify-cli/partners_api.rb +21 -44
  146. data/lib/shopify-cli/partners_api/organizations.rb +8 -0
  147. data/lib/shopify-cli/project_commands.rb +16 -0
  148. data/lib/shopify-cli/project_type.rb +0 -31
  149. data/lib/shopify-cli/shopifolk.rb +8 -11
  150. data/lib/shopify-cli/sub_command.rb +1 -0
  151. data/lib/shopify-cli/tasks.rb +3 -0
  152. data/lib/shopify-cli/tasks/confirm_store.rb +18 -0
  153. data/lib/shopify-cli/tasks/create_api_client.rb +2 -2
  154. data/lib/shopify-cli/tasks/ensure_authenticated.rb +13 -0
  155. data/lib/shopify-cli/tasks/ensure_loopback_url.rb +1 -1
  156. data/lib/shopify-cli/tasks/ensure_project_type.rb +12 -0
  157. data/lib/shopify-cli/tasks/select_org_and_shop.rb +0 -3
  158. data/lib/shopify-cli/theme/dev_server.rb +98 -0
  159. data/lib/shopify-cli/theme/dev_server/certificate_manager.rb +79 -0
  160. data/lib/shopify-cli/theme/dev_server/header_hash.rb +94 -0
  161. data/lib/shopify-cli/theme/dev_server/hot-reload.js +93 -0
  162. data/lib/shopify-cli/theme/dev_server/hot_reload.rb +76 -0
  163. data/lib/shopify-cli/theme/dev_server/local_assets.rb +87 -0
  164. data/lib/shopify-cli/theme/dev_server/proxy.rb +205 -0
  165. data/lib/shopify-cli/theme/dev_server/sse.rb +75 -0
  166. data/lib/shopify-cli/theme/dev_server/watcher.rb +59 -0
  167. data/lib/shopify-cli/theme/dev_server/web_server.rb +140 -0
  168. data/lib/shopify-cli/theme/development_theme.rb +69 -0
  169. data/lib/shopify-cli/theme/file.rb +112 -0
  170. data/lib/shopify-cli/theme/ignore_filter.rb +109 -0
  171. data/lib/shopify-cli/theme/mime_type.rb +34 -0
  172. data/lib/shopify-cli/theme/syncer.rb +332 -0
  173. data/lib/shopify-cli/theme/theme.rb +204 -0
  174. data/lib/shopify-cli/tunnel.rb +1 -1
  175. data/lib/shopify-cli/version.rb +1 -1
  176. data/lib/shopify_cli.rb +18 -11
  177. data/shopify-cli.gemspec +12 -5
  178. data/shopify.fish +1 -1
  179. data/shopify.sh +1 -1
  180. metadata +92 -35
  181. data/.github/workflows/release.yml +0 -59
  182. data/lib/project_types/extension/features/argo_serve_options.rb +0 -42
  183. data/lib/project_types/node/commands/populate.rb +0 -23
  184. data/lib/project_types/rails/commands/populate.rb +0 -23
  185. data/lib/project_types/rails/commands/populate/customer.rb +0 -31
  186. data/lib/project_types/rails/commands/populate/draft_order.rb +0 -28
  187. data/lib/project_types/rails/commands/populate/product.rb +0 -30
  188. data/lib/project_types/script/layers/domain/config_ui.rb +0 -16
  189. data/lib/project_types/theme/commands/connect.rb +0 -54
  190. data/lib/project_types/theme/commands/create.rb +0 -48
  191. data/lib/project_types/theme/commands/deploy.rb +0 -38
  192. data/lib/project_types/theme/commands/generate.rb +0 -20
  193. data/lib/project_types/theme/commands/generate/env.rb +0 -79
  194. data/lib/project_types/theme/forms/connect.rb +0 -34
  195. data/lib/project_types/theme/forms/create.rb +0 -22
  196. data/lib/project_types/theme/tasks/ensure_themekit_installed.rb +0 -78
  197. data/lib/project_types/theme/themekit.rb +0 -113
  198. data/lib/shopify-cli/commands/connect.rb +0 -64
  199. data/lib/shopify-cli/commands/create.rb +0 -50
  200. data/lib/shopify-cli/oauth.rb +0 -198
@@ -0,0 +1,282 @@
1
+ require "base64"
2
+ require "digest"
3
+ require "json"
4
+ require "net/http"
5
+ require "securerandom"
6
+ require "openssl"
7
+ require "shopify_cli"
8
+ require "uri"
9
+ require "webrick"
10
+
11
+ module ShopifyCli
12
+ class IdentityAuth
13
+ include SmartProperties
14
+
15
+ autoload :Servlet, "shopify-cli/identity_auth/servlet"
16
+
17
+ class Error < StandardError; end
18
+ class Timeout < StandardError; end
19
+ LocalRequest = Struct.new(:method, :path, :query, :protocol)
20
+ LOCAL_DEBUG = "SHOPIFY_APP_CLI_LOCAL_PARTNERS"
21
+
22
+ DEFAULT_PORT = 3456
23
+ REDIRECT_HOST = "http://127.0.0.1:#{DEFAULT_PORT}"
24
+
25
+ APPLICATION_SCOPES = {
26
+ "shopify" => %w[https://api.shopify.com/auth/shop.admin.graphql https://api.shopify.com/auth/shop.admin.themes https://api.shopify.com/auth/partners.collaborator-relationships.readonly],
27
+ "storefront_renderer_production" => %w[https://api.shopify.com/auth/shop.storefront-renderer.devtools],
28
+ "partners" => %w[https://api.shopify.com/auth/partners.app.cli.access],
29
+ }
30
+
31
+ APPLICATION_CLIENT_IDS = {
32
+ "shopify" => "7ee65a63608843c577db8b23c4d7316ea0a01bd2f7594f8a9c06ea668c1b775c",
33
+ "storefront_renderer_production" => "ee139b3d-5861-4d45-b387-1bc3ada7811c",
34
+ "partners" => "271e16d403dfa18082ffb3d197bd2b5f4479c3fc32736d69296829cbb28d41a6",
35
+ }
36
+
37
+ DEV_APPLICATION_CLIENT_IDS = {
38
+ "shopify" => "e92482cebb9bfb9fb5a0199cc770fde3de6c8d16b798ee73e36c9d815e070e52",
39
+ "storefront_renderer_production" => "46f603de-894f-488d-9471-5b721280ff49",
40
+ "partners" => "df89d73339ac3c6c5f0a98d9ca93260763e384d51d6038da129889c308973978",
41
+ }
42
+
43
+ EXCHANGE_TOKENS = APPLICATION_SCOPES.keys.map do |key|
44
+ "#{key}_exchange_token".to_sym
45
+ end
46
+
47
+ IDENTITY_ACCESS_TOKENS = %i[
48
+ identity_access_token
49
+ identity_refresh_token
50
+ ]
51
+
52
+ property! :ctx
53
+ property :store, default: -> { ShopifyCli::DB.new }
54
+ property :state_token, accepts: String, default: SecureRandom.hex(30)
55
+ property :code_verifier, accepts: String, default: SecureRandom.hex(30)
56
+
57
+ attr_accessor :response_query
58
+
59
+ def authenticate
60
+ return if refresh_exchange_tokens || refresh_access_tokens
61
+
62
+ initiate_authentication
63
+
64
+ begin
65
+ request_access_token(code: receive_access_code)
66
+ rescue IdentityAuth::Timeout => e
67
+ ctx.abort(e.message)
68
+ end
69
+ request_exchange_tokens
70
+ end
71
+
72
+ def reauthenticate
73
+ return if refresh_exchange_tokens || refresh_access_tokens
74
+ ctx.abort(ctx.message("core.identity_auth.error.reauthenticate", ShopifyCli::TOOL_NAME))
75
+ end
76
+
77
+ def code_challenge
78
+ @code_challenge ||= Base64.urlsafe_encode64(
79
+ OpenSSL::Digest::SHA256.digest(code_verifier),
80
+ padding: false,
81
+ )
82
+ end
83
+
84
+ def server
85
+ @server ||= begin
86
+ server = WEBrick::HTTPServer.new(
87
+ Port: DEFAULT_PORT,
88
+ Logger: WEBrick::Log.new(File.open(File::NULL, "w")),
89
+ AccessLog: [],
90
+ )
91
+ server.mount("/", Servlet, self, state_token)
92
+ server
93
+ end
94
+ end
95
+
96
+ def self.delete_tokens_and_keys
97
+ ShopifyCli::DB.del(*IDENTITY_ACCESS_TOKENS)
98
+ ShopifyCli::DB.del(*EXCHANGE_TOKENS)
99
+ end
100
+
101
+ private
102
+
103
+ def initiate_authentication
104
+ @server_thread = Thread.new { server.start }
105
+ params = {
106
+ client_id: client_id,
107
+ scope: scopes(APPLICATION_SCOPES.values.flatten),
108
+ redirect_uri: REDIRECT_HOST,
109
+ state: state_token,
110
+ response_type: :code,
111
+ }
112
+ params.merge!(challange_params)
113
+ uri = URI.parse("#{auth_url}/authorize")
114
+ uri.query = URI.encode_www_form(params)
115
+ open_browser_authentication(uri)
116
+ end
117
+
118
+ def open_browser_authentication(uri)
119
+ ctx.open_browser_url!(uri)
120
+ end
121
+
122
+ def receive_access_code
123
+ @access_code ||= begin
124
+ @server_thread.join(240)
125
+ raise Timeout, ctx.message("core.identity_auth.error.timeout") if response_query.nil?
126
+ raise Error, response_query["error_description"] unless response_query["error"].nil?
127
+ response_query["code"]
128
+ end
129
+ end
130
+
131
+ def request_access_token(code:)
132
+ resp = post_token_request(
133
+ grant_type: :authorization_code,
134
+ code: code,
135
+ redirect_uri: REDIRECT_HOST,
136
+ client_id: client_id,
137
+ code_verifier: code_verifier,
138
+ )
139
+ store.set(
140
+ identity_access_token: resp["access_token"],
141
+ identity_refresh_token: resp["refresh_token"],
142
+ )
143
+ end
144
+
145
+ def refresh_access_tokens
146
+ return false unless IDENTITY_ACCESS_TOKENS.all? { |key| store.exists?(key) }
147
+
148
+ resp = post_token_request(
149
+ grant_type: :refresh_token,
150
+ access_token: store.get(:identity_access_token),
151
+ refresh_token: store.get(:identity_refresh_token),
152
+ client_id: client_id,
153
+ )
154
+ store.set(
155
+ identity_access_token: resp["access_token"],
156
+ identity_refresh_token: resp["refresh_token"],
157
+ )
158
+
159
+ # Need to refresh the exchange token on successful access token refresh
160
+ request_exchange_tokens
161
+
162
+ true
163
+ rescue
164
+ store.del(*IDENTITY_ACCESS_TOKENS)
165
+ false
166
+ end
167
+
168
+ def refresh_exchange_tokens
169
+ return false unless EXCHANGE_TOKENS.all? { |key| store.exists?(key) }
170
+
171
+ request_exchange_tokens
172
+
173
+ true
174
+ rescue
175
+ store.del(*EXCHANGE_TOKENS)
176
+ false
177
+ end
178
+
179
+ def request_exchange_tokens
180
+ APPLICATION_SCOPES.each do |key, scopes|
181
+ request_exchange_token(key, client_id_for_application(key), scopes)
182
+ end
183
+ end
184
+
185
+ def request_exchange_token(name, audience, additional_scopes)
186
+ return if name == "shopify" && !store.exists?(:shop)
187
+
188
+ params = {
189
+ grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
190
+ requested_token_type: "urn:ietf:params:oauth:token-type:access_token",
191
+ subject_token_type: "urn:ietf:params:oauth:token-type:access_token",
192
+ client_id: client_id,
193
+ audience: audience,
194
+ scope: scopes(additional_scopes),
195
+ subject_token: store.get(:identity_access_token),
196
+ }.tap do |result|
197
+ if name == "shopify"
198
+ result[:destination] = "https://#{store.get(:shop)}/admin"
199
+ end
200
+ end
201
+ # ctx.debug(params)
202
+ resp = post_token_request(params)
203
+ store.set("#{name}_exchange_token".to_sym => resp["access_token"])
204
+ ctx.debug("#{name}_exchange_token: " + resp["access_token"])
205
+ end
206
+
207
+ def post_token_request(params)
208
+ post_request("/token", params)
209
+ end
210
+
211
+ def post_request(endpoint, params)
212
+ uri = URI.parse("#{auth_url}#{endpoint}")
213
+ https = Net::HTTP.new(uri.host, uri.port)
214
+ https.use_ssl = true
215
+ request = Net::HTTP::Post.new(uri.path)
216
+ request["User-Agent"] = "Shopify CLI #{::ShopifyCli::VERSION}"
217
+ request.body = URI.encode_www_form(params)
218
+ res = https.request(request)
219
+ unless res.is_a?(Net::HTTPSuccess)
220
+ error_msg = JSON.parse(res.body)["error_description"]
221
+ shop = store.get(:shop)
222
+ if error_msg.include?("destination")
223
+ store.del(:shop)
224
+ ctx.abort(ctx.message("core.identity_auth.error.invalid_destination", shop))
225
+ end
226
+ raise Error, error_msg
227
+ end
228
+ JSON.parse(res.body)
229
+ end
230
+
231
+ def challange_params
232
+ {
233
+ code_challenge: code_challenge,
234
+ code_challenge_method: "S256",
235
+ }
236
+ end
237
+
238
+ def auth_url
239
+ return "https://accounts.shopify.com/oauth" if ENV[LOCAL_DEBUG].nil?
240
+ "https://identity.myshopify.io/oauth"
241
+ end
242
+
243
+ def client_id_for_application(application_name)
244
+ client_ids = if ENV[LOCAL_DEBUG]
245
+ DEV_APPLICATION_CLIENT_IDS
246
+ else
247
+ APPLICATION_CLIENT_IDS
248
+ end
249
+
250
+ client_ids[application_name]
251
+ end
252
+
253
+ def scopes(additional_scopes = [])
254
+ (["openid"] + additional_scopes).tap do |result|
255
+ result << "employee" if ShopifyCli::Shopifolk.acting_as_shopify_organization?
256
+ end.join(" ")
257
+ end
258
+
259
+ def client_id
260
+ return "fbdb2649-e327-4907-8f67-908d24cfd7e3" if ENV[LOCAL_DEBUG].nil?
261
+
262
+ ctx.abort(ctx.message("core.identity_auth.error.local_identity_not_running")) unless local_identity_running?
263
+
264
+ # Fetch the client ID from the local Identity Dynamic Registration endpoint
265
+ response = post_request("/client", {
266
+ name: "shopify-cli-development",
267
+ public_type: "native",
268
+ })
269
+
270
+ response["client_id"]
271
+ end
272
+
273
+ def local_identity_running?
274
+ Net::HTTP.start("identity.myshopify.io", 443, use_ssl: true, open_timeout: 1, read_timeout: 10) do |http|
275
+ req = Net::HTTP::Get.new(URI.join("https://identity.myshopify.io", "/services/ping"))
276
+ http.request(req).is_a?(Net::HTTPSuccess)
277
+ end
278
+ rescue Timeout::Error, Errno::EHOSTUNREACH, Errno::EHOSTDOWN, Errno::EADDRNOTAVAIL, Errno::ECONNREFUSED
279
+ false
280
+ end
281
+ end
282
+ end
@@ -1,5 +1,5 @@
1
1
  module ShopifyCli
2
- class OAuth
2
+ class IdentityAuth
3
3
  class Servlet < WEBrick::HTTPServlet::AbstractServlet
4
4
  TEMPLATE = %{<!DOCTYPE html>
5
5
  <html>
@@ -13,15 +13,13 @@ module ShopifyCli
13
13
  </html>
14
14
  }
15
15
  AUTOCLOSE_TEMPLATE = %{
16
- <script>
17
- setTimeout(function() { window.close(); }, 3000)
18
- </script>
16
+ <script>window.close();</script>
19
17
  }
20
18
 
21
- def initialize(server, oauth, token)
19
+ def initialize(server, identity_auth, token)
22
20
  super
23
21
  @server = server
24
- @oauth = oauth
22
+ @identity_auth = identity_auth
25
23
  @state_token = token
26
24
  end
27
25
 
@@ -30,16 +28,16 @@ module ShopifyCli
30
28
  respond_with(
31
29
  res,
32
30
  400,
33
- Context.message("core.oauth.servlet.invalid_request_response", req.query["error_description"])
31
+ Context.message("core.identity_auth.servlet.invalid_request_response", req.query["error_description"])
34
32
  )
35
33
  elsif req.query["state"] != @state_token
36
- response_message = Context.message("core.oauth.servlet.invalid_state_response")
34
+ response_message = Context.message("core.identity_auth.servlet.invalid_state_response")
37
35
  req.query.merge!("error" => "invalid_state", "error_description" => response_message)
38
36
  respond_with(res, 403, response_message)
39
37
  else
40
- respond_with(res, 200, Context.message("core.oauth.servlet.success_response"))
38
+ respond_with(res, 200, Context.message("core.identity_auth.servlet.success_response"))
41
39
  end
42
- @oauth.response_query = req.query
40
+ @identity_auth.response_query = req.query
43
41
  @server.shutdown
44
42
  end
45
43
 
@@ -49,8 +47,9 @@ module ShopifyCli
49
47
  status: status,
50
48
  message: message,
51
49
  color: successful ? "black" : "red",
52
- title:
53
- Context.message(successful ? "core.oauth.servlet.authenticated" : "core.oauth.servlet.not_authenticated"),
50
+ title: Context.message(
51
+ successful ? "core.identity_auth.servlet.authenticated" : "core.identity_auth.servlet.not_authenticated"
52
+ ),
54
53
  autoclose: successful ? AUTOCLOSE_TEMPLATE : "",
55
54
  }
56
55
  response.status = status
@@ -7,19 +7,14 @@ module ShopifyCli
7
7
  create: {
8
8
  info: {
9
9
  created: "{{v}} {{green:%s}} was created in the organization's Partner Dashboard {{underline:%s}}",
10
- serve: "{{*}} Change directories to your new project folder {{green:%s}} and run {{command:%s serve}} " \
11
- "to start a local server",
10
+ serve: "{{*}} Change directories to your new project folder {{green:%s}} and run "\
11
+ "{{command:%s %s serve}} to start a local server",
12
12
  install: "{{*}} Then, visit {{underline:%s/test}} to install {{green:%s}} on your Dev Store",
13
13
  },
14
14
  },
15
15
  },
16
16
  core: {
17
17
  connect: {
18
- help: <<~HELP,
19
- Connect (or re-connect) an existing project to a Shopify partner organization and/or a store. Creates or updates the {{green:.env}} file, and creates the {{green:.shopify-cli.yml}} file.
20
- Usage: {{command:%s connect}}
21
- HELP
22
-
23
18
  already_connected_warning: "{{yellow:! This app appears to be already connected}}",
24
19
  project_type_select: "What type of project would you like to connect?",
25
20
  cli_yml_saved: ".shopify-cli.yml saved to project root",
@@ -32,21 +27,8 @@ module ShopifyCli
32
27
  OPEN
33
28
  },
34
29
 
35
- create: {
36
- help: <<~HELP,
37
- Create a new project.
38
- Usage: {{command:%s create [ %s ]}}
39
- HELP
40
-
41
- error: {
42
- invalid_app_type: "{{red:Error}}: invalid app type {{bold:%s}}",
43
- },
44
-
45
- project_type_select: "What type of project would you like to create?",
46
- },
47
-
48
30
  env_file: {
49
- saving_header: "writing %s file...",
31
+ saving_header: "writing %s file",
50
32
  saving: "writing %s file",
51
33
  saved: "%s saved to project root",
52
34
  },
@@ -87,7 +69,7 @@ module ShopifyCli
87
69
  no_commits_made: "No git commits have been made. Please make at least one commit.",
88
70
  },
89
71
 
90
- cloning: "Cloning %s into %s...",
72
+ cloning: "Cloning %s into %s",
91
73
  cloned: "{{v}} Cloned into %s",
92
74
  },
93
75
 
@@ -99,8 +81,6 @@ module ShopifyCli
99
81
  preamble: <<~MESSAGE,
100
82
  Use {{command:%s help <command>}} to display detailed information about a specific command.
101
83
 
102
- {{bold:Available core commands:}}
103
-
104
84
  MESSAGE
105
85
  },
106
86
 
@@ -123,19 +103,41 @@ module ShopifyCli
123
103
  install_error: "An error occurred while installing dependencies",
124
104
  },
125
105
 
126
- installing: "Installing dependencies with %s...",
106
+ installing: "Installing dependencies with %s",
127
107
  installed: "Dependencies installed",
128
- npm_installing_deps: "Installing %d dependencies...",
108
+ npm_installing_deps: "Installing %d dependencies",
129
109
  npm_installed_deps: "%d npm dependencies installed",
130
110
  },
131
111
 
112
+ login: {
113
+ help: <<~HELP,
114
+ Log in to the Shopify CLI by authenticating with a store or partner organization
115
+ Usage: {{command:%s login [--store=STORE]}}
116
+ HELP
117
+ invalid_shop: <<~MESSAGE,
118
+ Invalid store provided (%s). Please provide the store in the following format: my-store.myshopify.com
119
+ MESSAGE
120
+ shop_prompt: <<~PROMPT,
121
+ What store are you connecting to? (e.g. my-store.myshopify.com; do {{bold:NOT}} include protocol part, e.g., https://)
122
+ PROMPT
123
+ },
124
+
132
125
  logout: {
133
126
  help: <<~HELP,
134
- Log out of a currently authenticated partner organization and store, or clear invalid credentials
127
+ Log out of an authenticated partner organization and store, or clear invalid credentials
135
128
  Usage: {{command:%s logout}}
136
129
  HELP
137
130
 
138
- success: "Logged out of partner organization and store",
131
+ success: "Successfully logged out of your account",
132
+ },
133
+
134
+ switch: {
135
+ help: <<~HELP,
136
+ Switch between development stores in your partner organization
137
+ Usage: {{command:%s switch [--store=STORE]}}
138
+ HELP
139
+ disabled_as_shopify_org: "Can't switch development stores logged in as {{green:Shopify partners org}}",
140
+ success: "Switched development store to {{green:%s}}",
139
141
  },
140
142
 
141
143
  monorail: {
@@ -146,9 +148,12 @@ module ShopifyCli
146
148
  MSG
147
149
  },
148
150
 
149
- oauth: {
151
+ identity_auth: {
150
152
  error: {
151
153
  timeout: "Timed out while waiting for response from Shopify",
154
+ local_identity_not_running: "Identity needs to be running locally in order to proceed.",
155
+ reauthenticate: "Please login again with {{command:shopify login}}",
156
+ invalid_destination: "The store %s doesn't exist. Please log out and try again.",
152
157
  },
153
158
 
154
159
  location: {
@@ -166,6 +171,7 @@ module ShopifyCli
166
171
  authenticated: "Authenticated successfully",
167
172
  not_authenticated: "Failed to authenticate",
168
173
  },
174
+ login_prompt: "Please ensure you've logged in with {{command:%s login}} and try again",
169
175
  },
170
176
 
171
177
  options: {
@@ -185,6 +191,10 @@ module ShopifyCli
185
191
  api: {
186
192
  error: {
187
193
  failed_auth: "Failed to authenticate with Shopify. Please try again later.",
194
+ failed_auth_debugging: "{{red:Please provide this information with your report:}}\n%s\n\n",
195
+ forbidden: <<~FORBIDDEN,
196
+ Command not allowed with current login. Please check your login details with {{command:%s whoami}}. You may need to request additional permissions for this action.
197
+ FORBIDDEN
188
198
  internal_server_error: "{{red:{{x}} An unexpected error occurred on Shopify.}}",
189
199
  internal_server_error_debug: "\n{{red:Response details:}}\n%s\n\n",
190
200
  invalid_url: "Invalid URL: %s",
@@ -192,22 +202,78 @@ module ShopifyCli
192
202
  },
193
203
 
194
204
  populate: {
205
+ help: <<~HELP,
206
+ Populate a Shopify store with example customers, orders, or products.
207
+ Usage: {{command:%s populate [ customers | draftorders | products ]}}
208
+ HELP
209
+
210
+ extended_help: <<~HELP,
211
+ {{bold:Subcommands:}}
212
+
213
+ {{cyan:customers [options]}}: Add dummy customers to the specified store.
214
+ Usage: {{command:%1$s populate customers}}
215
+
216
+ {{cyan:draftorders [options]}}: Add dummy orders to the specified store.
217
+ Usage: {{command:%1$s populate draftorders}}
218
+
219
+ {{cyan:products [options]}}: Add dummy products to the specified store.
220
+ Usage: {{command:%1$s populate products}}
221
+
222
+ {{bold:Options:}}
223
+
224
+ {{cyan:--count [integer]}}: The number of dummy items to populate. Defaults to 5.
225
+ {{cyan:--silent}}: Silence the populate output.
226
+ {{cyan:--help}}: Display more options specific to each subcommand.
227
+
228
+ {{bold:Examples:}}
229
+
230
+ {{command:%1$s populate products}}
231
+ Populate your store with 5 additional products.
232
+
233
+ {{command:%1$s populate customers --count 30}}
234
+ Populate your store with 30 additional customers.
235
+
236
+ {{command:%1$s populate draftorders}}
237
+ Populate your store with 5 additional orders.
238
+
239
+ {{command:%1$s populate products --help}}
240
+ Display the list of options available to customize the {{command:%1$s populate products}} command.
241
+ HELP
242
+
243
+ error: {
244
+ no_shop: "No store found. Please run {{command:%s login --store=STORE}} to login to a specific store",
245
+ },
246
+
247
+ customer: {
248
+ added: "%s added to {{green:%s}} at {{underline:%scustomers/%d}}",
249
+ },
250
+
251
+ draft_order: {
252
+ added: "DraftOrder added to {{green:%s}} at {{underline:%sdraft_orders/%d}}",
253
+ },
254
+
195
255
  options: {
196
256
  header: "{{bold:{{cyan:%s}} options:}}",
197
257
  count_help: "Number of resources to generate",
198
258
  },
199
- populating: "Populating %d %ss...",
259
+
260
+ populating: "Populating %d %ss…",
261
+
200
262
  completion_message: <<~COMPLETION_MESSAGE,
201
263
  Successfully added %d %s to {{green:%s}}
202
264
  {{*}} View all %ss at {{underline:%s%ss}}
203
265
  COMPLETION_MESSAGE
266
+
267
+ product: {
268
+ added: "%s added to {{green:%s}} at {{underline:%sproducts/%d}}",
269
+ },
204
270
  },
205
271
 
206
272
  project: {
207
273
  error: {
208
274
  not_in_project: <<~MESSAGE,
209
275
  {{x}} You are not in a Shopify app project
210
- {{yellow:{{*}}}}{{reset: Run}}{{cyan: shopify create}}{{reset: to create your app}}
276
+ {{yellow:{{*}}}}{{reset: Run}}{{cyan: shopify rails create}}{{reset: or}}{{cyan: shopify node create}}{{reset: to create your app}}
211
277
  MESSAGE
212
278
  },
213
279
  },
@@ -239,7 +305,8 @@ module ShopifyCli
239
305
  unknown_option: "{{x}} {{red:unknown option '%s'}}",
240
306
  },
241
307
 
242
- header: "{{bold:Shopify App CLI}}",
308
+ header: "{{bold:Shopify CLI}}",
309
+ shop_header: "{{bold:Current Shop}}",
243
310
  const: "%17s = %s",
244
311
  ruby_header: <<~RUBY_MESSAGE,
245
312
  {{bold:Ruby (via RbConfig)}}
@@ -266,7 +333,20 @@ module ShopifyCli
266
333
  identity_is_shopifolk: "{{v}} Checked user settings: you’re Shopify staff!",
267
334
  },
268
335
 
336
+ store: {
337
+ help: <<~HELP,
338
+ Display current store.
339
+ Usage: {{command:%s store}}
340
+ HELP
341
+ shop: "You're currently logged into {{green:%s}}",
342
+ },
343
+
269
344
  tasks: {
345
+ confirm_store: {
346
+ prompt: "You are currently logged into {{green:%s}}. Do you want to proceed using this store?",
347
+ confirmation: "Proceeding using {{green:%s}}",
348
+ cancelling: "Cancelling…",
349
+ },
270
350
  ensure_env: {
271
351
  organization_select: "To which partner organization does this project belong?",
272
352
  no_development_stores: <<~MESSAGE,
@@ -293,6 +373,9 @@ module ShopifyCli
293
373
  MESSAGE
294
374
  transfer_disabled: "{{v}} Transfer has been disabled on %s.",
295
375
  },
376
+ ensure_project_type: {
377
+ wrong_project_type: "This command can only be run within %s projects.",
378
+ },
296
379
  update_dashboard_urls: {
297
380
  updated: "{{v}} Whitelist URLS updated in Partners Dashboard}}",
298
381
  update_error:
@@ -310,10 +393,11 @@ module ShopifyCli
310
393
  organization_not_found: "Cannot find a partner organization with that ID",
311
394
  shopifolk_notice: <<~MESSAGE,
312
395
  {{i}} As a {{green:Shopify}} employee, the authentication should take you to the Shopify Okta login,
313
- NOT the Partner account login. Please run {{command:%s logout}} and try again.
396
+ NOT the partner account login. Please run {{command:%s logout}} and try again.
314
397
  MESSAGE
315
398
  },
316
- first_party: "Are you working on a 1P (1st Party) app?",
399
+ first_party: "Are you working on a {{green:Shopify project}} on behalf of the"\
400
+ " {{green:Shopify partners org}}?",
317
401
  identified_as_shopify: "We've identified you as a {{green:Shopify}} employee.",
318
402
  organization: "Partner organization {{green:%s (%s)}}",
319
403
  organization_select: "Select partner organization",
@@ -330,18 +414,19 @@ module ShopifyCli
330
414
  "please make sure %s exists within %s before trying again",
331
415
  },
332
416
 
417
+ installing: "Installing ngrok…",
333
418
  not_running: "{{green:x}} ngrok tunnel not running",
419
+ prereq_command_location: "%s @ %s",
334
420
  signup_suggestion: <<~MESSAGE,
335
421
  {{*}} To avoid tunnels that timeout, it is recommended to signup for a free ngrok
336
422
  account at {{underline:https://ngrok.com/signup}}. After you signup, install your
337
- personalized authorization token using {{command:%s tunnel auth <token>}}.
423
+ personalized authorization token using {{command:%s [ node | rails ] tunnel auth <token>}}.
338
424
  MESSAGE
339
425
  start: "{{v}} ngrok tunnel running at {{underline:%s}}",
340
426
  start_with_account: "{{v}} ngrok tunnel running at {{underline:%s}}, with account %s",
341
427
  stopped: "{{green:x}} ngrok tunnel stopped",
342
- timed_out: "{{x}} ngrok tunnel has timed out, restarting ...",
428
+ timed_out: "{{x}} ngrok tunnel has timed out, restarting",
343
429
  will_timeout: "{{*}} This tunnel will timeout in {{red:%s}}",
344
- prereq_command_location: "%s @ %s",
345
430
  },
346
431
 
347
432
  version: {
@@ -358,21 +443,30 @@ module ShopifyCli
358
443
 
359
444
  DEVELOPMENT
360
445
 
361
- shell_shim: <<~MESSAGE,
362
- {{x}} This version of Shopify App CLI is no longer supported. You’ll need to migrate to the new CLI version to continue.
446
+ new_version: <<~MESSAGE,
447
+ {{*}} {{yellow:A new version of Shopify CLI is available! You have version %s and the latest version is %s.
363
448
 
364
- Please visit this page for complete instructions:
365
- {{underline:https://shopify.dev/tools/cli/troubleshooting#migrate-from-a-legacy-version}}
449
+ To upgrade, follow the instructions for the package manager you’re using:
450
+ {{underline:https://shopify.dev/tools/cli/troubleshooting#upgrade-shopify-cli}}}}
366
451
 
367
452
  MESSAGE
453
+ },
368
454
 
369
- new_version: <<~MESSAGE,
370
- {{*}} {{yellow:A new version of Shopify App CLI is available! You have version %s and the latest version is %s.
371
-
372
- To upgrade, follow the instructions for the package manager you’re using:
373
- {{underline:https://shopify.dev/tools/cli/troubleshooting#upgrade-shopify-app-cli}}}}
455
+ whoami: {
456
+ help: <<~HELP,
457
+ Identifies which partner organization or store you are currently logged into.
458
+ Usage: {{command:%s whoami}}
459
+ HELP
460
+ not_logged_in: <<~MESSAGE,
461
+ It doesn't appear that you're logged in. You must log into a partner organization or a store staff account.
374
462
 
463
+ If trying to log into a store staff account, please use {{command:%s login --store=STORE}} to log in.
464
+ MESSAGE
465
+ logged_in_shop_only: <<~MESSAGE,
466
+ Logged into store {{green:%s}} as staff (no partner organizations available for this login)
375
467
  MESSAGE
468
+ logged_in_partner_only: "Logged into partner organization {{green:%s}}",
469
+ logged_in_partner_and_shop: "Logged into store {{green:%s}} in partner organization {{green:%s}}",
376
470
  },
377
471
  },
378
472
  }.freeze