shopify-cli 1.4.1 → 1.8.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 (303) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +2 -2
  3. data/.github/CONTRIBUTING.md +9 -1
  4. data/.github/PULL_REQUEST_TEMPLATE.md +3 -2
  5. data/.github/workflows/release.yml +0 -1
  6. data/.github/workflows/triage.yml +22 -0
  7. data/.rubocop.yml +21 -7
  8. data/.rubocop_todo.yml +2 -15
  9. data/.travis.yml +1 -1
  10. data/CHANGELOG.md +28 -0
  11. data/Gemfile +12 -11
  12. data/Gemfile.lock +17 -14
  13. data/README.md +39 -7
  14. data/RELEASING.md +5 -13
  15. data/Rakefile +32 -28
  16. data/bin/load_shopify.rb +6 -6
  17. data/bin/shopify +2 -2
  18. data/dev.yml +2 -2
  19. data/docs/_config.yml +1 -18
  20. data/docs/app/node/commands/index.md +2 -80
  21. data/docs/app/node/index.md +2 -33
  22. data/docs/app/rails/commands/index.md +2 -78
  23. data/docs/app/rails/index.md +2 -34
  24. data/docs/core/index.md +2 -84
  25. data/docs/getting-started/index.md +2 -25
  26. data/docs/getting-started/install/index.md +1 -118
  27. data/docs/getting-started/migrate/index.md +2 -94
  28. data/docs/getting-started/uninstall/index.md +2 -35
  29. data/docs/getting-started/upgrade/index.md +2 -39
  30. data/docs/help/start-app/index.md +2 -4
  31. data/docs/index.md +2 -24
  32. data/ext/shopify-cli/extconf.rb +7 -7
  33. data/install.sh +1 -1
  34. data/lib/docgen/markdown.rb +12 -12
  35. data/lib/graphql/fetch_specifications.graphql +14 -0
  36. data/lib/{project_types/extension/graphql → graphql}/get_app_by_api_key.graphql +0 -0
  37. data/lib/project_types/extension/cli.rb +54 -47
  38. data/lib/project_types/extension/commands/build.rb +3 -3
  39. data/lib/project_types/extension/commands/create.rb +17 -10
  40. data/lib/project_types/extension/commands/extension_command.rb +12 -5
  41. data/lib/project_types/extension/commands/push.rb +8 -8
  42. data/lib/project_types/extension/commands/register.rb +19 -30
  43. data/lib/project_types/extension/commands/serve.rb +31 -3
  44. data/lib/project_types/extension/commands/tunnel.rb +12 -12
  45. data/lib/project_types/extension/extension_project.rb +8 -4
  46. data/lib/project_types/extension/extension_project_keys.rb +4 -4
  47. data/lib/project_types/extension/features/argo.rb +117 -0
  48. data/lib/project_types/extension/features/argo_config.rb +5 -5
  49. data/lib/project_types/extension/features/argo_dependencies.rb +5 -5
  50. data/lib/project_types/extension/features/argo_setup.rb +2 -2
  51. data/lib/project_types/extension/features/argo_setup_steps.rb +4 -4
  52. data/lib/project_types/extension/forms/create.rb +28 -34
  53. data/lib/project_types/extension/forms/questions/ask_app.rb +53 -0
  54. data/lib/project_types/extension/forms/questions/ask_name.rb +40 -0
  55. data/lib/project_types/extension/forms/questions/ask_type.rb +47 -0
  56. data/lib/project_types/extension/messages/messages.rb +55 -52
  57. data/lib/project_types/extension/models/lazy_specification_handler.rb +12 -0
  58. data/lib/project_types/extension/models/specification.rb +37 -0
  59. data/lib/project_types/extension/models/specification_handlers/checkout_post_purchase.rb +19 -0
  60. data/lib/project_types/extension/models/specification_handlers/default.rb +67 -0
  61. data/lib/project_types/extension/models/specifications.rb +88 -0
  62. data/lib/project_types/extension/tasks/configure_features.rb +52 -0
  63. data/lib/project_types/extension/tasks/converters/app_converter.rb +6 -6
  64. data/lib/project_types/extension/tasks/converters/registration_converter.rb +6 -6
  65. data/lib/project_types/extension/tasks/converters/validation_error_converter.rb +4 -4
  66. data/lib/project_types/extension/tasks/converters/version_converter.rb +7 -7
  67. data/lib/project_types/extension/tasks/create_extension.rb +4 -4
  68. data/lib/project_types/extension/tasks/fetch_specifications.rb +18 -0
  69. data/lib/project_types/extension/tasks/get_app.rb +4 -4
  70. data/lib/project_types/extension/tasks/get_apps.rb +3 -3
  71. data/lib/project_types/extension/tasks/update_draft.rb +4 -4
  72. data/lib/project_types/extension/tasks/user_errors.rb +4 -4
  73. data/lib/project_types/node/cli.rb +19 -16
  74. data/lib/project_types/node/commands/connect.rb +15 -0
  75. data/lib/project_types/node/commands/create.rb +44 -41
  76. data/lib/project_types/node/commands/deploy.rb +4 -4
  77. data/lib/project_types/node/commands/deploy/heroku.rb +24 -24
  78. data/lib/project_types/node/commands/generate.rb +9 -18
  79. data/lib/project_types/node/commands/open.rb +2 -2
  80. data/lib/project_types/node/commands/populate.rb +6 -6
  81. data/lib/project_types/node/commands/populate/customer.rb +5 -5
  82. data/lib/project_types/node/commands/populate/draft_order.rb +5 -5
  83. data/lib/project_types/node/commands/populate/product.rb +5 -5
  84. data/lib/project_types/node/commands/serve.rb +9 -9
  85. data/lib/project_types/node/commands/tunnel.rb +7 -7
  86. data/lib/project_types/node/forms/create.rb +17 -8
  87. data/lib/project_types/node/messages/messages.rb +20 -53
  88. data/lib/project_types/rails/cli.rb +21 -18
  89. data/lib/project_types/rails/commands/connect.rb +15 -0
  90. data/lib/project_types/rails/commands/create.rb +58 -57
  91. data/lib/project_types/rails/commands/deploy.rb +4 -4
  92. data/lib/project_types/rails/commands/deploy/heroku.rb +30 -30
  93. data/lib/project_types/rails/commands/generate.rb +7 -7
  94. data/lib/project_types/rails/commands/generate/webhook.rb +6 -6
  95. data/lib/project_types/rails/commands/open.rb +2 -2
  96. data/lib/project_types/rails/commands/populate.rb +6 -6
  97. data/lib/project_types/rails/commands/populate/customer.rb +5 -5
  98. data/lib/project_types/rails/commands/populate/draft_order.rb +5 -5
  99. data/lib/project_types/rails/commands/populate/product.rb +5 -5
  100. data/lib/project_types/rails/commands/serve.rb +11 -11
  101. data/lib/project_types/rails/commands/tunnel.rb +7 -7
  102. data/lib/project_types/rails/forms/create.rb +35 -25
  103. data/lib/project_types/rails/gem.rb +24 -24
  104. data/lib/project_types/rails/messages/messages.rb +13 -9
  105. data/lib/project_types/rails/ruby.rb +2 -2
  106. data/lib/project_types/script/cli.rb +44 -38
  107. data/lib/project_types/script/commands/create.rb +15 -10
  108. data/lib/project_types/script/commands/disable.rb +3 -3
  109. data/lib/project_types/script/commands/enable.rb +19 -9
  110. data/lib/project_types/script/commands/push.rb +10 -17
  111. data/lib/project_types/script/config/extension_points.yml +30 -12
  112. data/lib/project_types/script/errors.rb +22 -0
  113. data/lib/project_types/script/forms/create.rb +29 -5
  114. data/lib/project_types/script/graphql/app_script_update_or_create.graphql +12 -1
  115. data/lib/project_types/script/layers/application/build_script.rb +19 -19
  116. data/lib/project_types/script/layers/application/create_script.rb +45 -12
  117. data/lib/project_types/script/layers/application/disable_script.rb +2 -2
  118. data/lib/project_types/script/layers/application/enable_script.rb +2 -2
  119. data/lib/project_types/script/layers/application/extension_points.rb +24 -0
  120. data/lib/project_types/script/layers/application/project_dependencies.rb +4 -4
  121. data/lib/project_types/script/layers/application/push_script.rb +15 -18
  122. data/lib/project_types/script/layers/domain/config_ui.rb +16 -0
  123. data/lib/project_types/script/layers/domain/errors.rb +23 -0
  124. data/lib/project_types/script/layers/domain/extension_point.rb +67 -7
  125. data/lib/project_types/script/layers/domain/metadata.rb +55 -0
  126. data/lib/project_types/script/layers/domain/push_package.rb +29 -6
  127. data/lib/project_types/script/layers/infrastructure/assemblyscript_project_creator.rb +45 -55
  128. data/lib/project_types/script/layers/infrastructure/assemblyscript_task_runner.rb +41 -45
  129. data/lib/project_types/script/layers/infrastructure/config_ui_repository.rb +46 -0
  130. data/lib/project_types/script/layers/infrastructure/errors.rb +32 -5
  131. data/lib/project_types/script/layers/infrastructure/extension_point_repository.rb +12 -6
  132. data/lib/project_types/script/layers/infrastructure/project_creator.rb +2 -1
  133. data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +20 -13
  134. data/lib/project_types/script/layers/infrastructure/rust_project_creator.rb +72 -0
  135. data/lib/project_types/script/layers/infrastructure/rust_task_runner.rb +59 -0
  136. data/lib/project_types/script/layers/infrastructure/script_service.rb +39 -17
  137. data/lib/project_types/script/layers/infrastructure/task_runner.rb +4 -3
  138. data/lib/project_types/script/messages/messages.rb +76 -10
  139. data/lib/project_types/script/script_project.rb +26 -16
  140. data/lib/project_types/script/ui/error_handler.rb +135 -50
  141. data/lib/project_types/script/ui/printing_spinner.rb +1 -1
  142. data/lib/project_types/script/ui/strict_spinner.rb +1 -1
  143. data/lib/project_types/theme/cli.rb +40 -0
  144. data/lib/project_types/theme/commands/connect.rb +54 -0
  145. data/lib/project_types/theme/commands/create.rb +48 -0
  146. data/lib/project_types/theme/commands/deploy.rb +38 -0
  147. data/lib/project_types/theme/commands/generate.rb +20 -0
  148. data/lib/project_types/theme/commands/generate/env.rb +79 -0
  149. data/lib/project_types/theme/commands/push.rb +55 -0
  150. data/lib/project_types/theme/commands/serve.rb +31 -0
  151. data/lib/project_types/theme/forms/connect.rb +34 -0
  152. data/lib/project_types/theme/forms/create.rb +22 -0
  153. data/lib/project_types/theme/messages/messages.rb +147 -0
  154. data/lib/project_types/theme/tasks/ensure_themekit_installed.rb +78 -0
  155. data/lib/project_types/theme/themekit.rb +113 -0
  156. data/lib/rubygems_plugin.rb +3 -3
  157. data/lib/shopify-cli/admin_api.rb +52 -12
  158. data/lib/shopify-cli/admin_api/populate_resource_command.rb +17 -17
  159. data/lib/shopify-cli/admin_api/schema.rb +3 -3
  160. data/lib/shopify-cli/api.rb +36 -31
  161. data/lib/shopify-cli/command.rb +1 -1
  162. data/lib/shopify-cli/commands.rb +9 -9
  163. data/lib/shopify-cli/commands/config.rb +28 -28
  164. data/lib/shopify-cli/commands/connect.rb +35 -18
  165. data/lib/shopify-cli/commands/create.rb +5 -5
  166. data/lib/shopify-cli/commands/help.rb +6 -6
  167. data/lib/shopify-cli/commands/logout.rb +3 -3
  168. data/lib/shopify-cli/commands/system.rb +33 -33
  169. data/lib/shopify-cli/commands/version.rb +2 -2
  170. data/lib/shopify-cli/context.rb +43 -22
  171. data/lib/shopify-cli/core.rb +4 -4
  172. data/lib/shopify-cli/core/entry_point.rb +5 -5
  173. data/lib/shopify-cli/core/executor.rb +1 -1
  174. data/lib/shopify-cli/core/help_resolver.rb +2 -2
  175. data/lib/shopify-cli/core/monorail.rb +17 -16
  176. data/lib/shopify-cli/db.rb +2 -2
  177. data/lib/shopify-cli/feature.rb +1 -3
  178. data/lib/shopify-cli/form.rb +1 -1
  179. data/lib/shopify-cli/git.rb +17 -17
  180. data/lib/shopify-cli/helpers.rb +1 -1
  181. data/lib/shopify-cli/helpers/haikunator.rb +1 -1
  182. data/lib/shopify-cli/heroku.rb +28 -28
  183. data/lib/shopify-cli/http_request.rb +21 -9
  184. data/lib/shopify-cli/js_deps.rb +13 -13
  185. data/lib/shopify-cli/js_system.rb +5 -5
  186. data/lib/shopify-cli/lazy_delegator.rb +55 -0
  187. data/lib/shopify-cli/messages/messages.rb +21 -10
  188. data/lib/shopify-cli/method_object.rb +104 -0
  189. data/lib/shopify-cli/oauth.rb +25 -25
  190. data/lib/shopify-cli/oauth/servlet.rb +9 -9
  191. data/lib/shopify-cli/options.rb +3 -3
  192. data/lib/shopify-cli/packager.rb +24 -24
  193. data/lib/shopify-cli/partners_api.rb +38 -16
  194. data/lib/shopify-cli/partners_api/organizations.rb +10 -10
  195. data/lib/shopify-cli/process_supervision.rb +8 -8
  196. data/lib/shopify-cli/project.rb +27 -23
  197. data/lib/shopify-cli/project_type.rb +21 -5
  198. data/lib/shopify-cli/resolve_constant.rb +25 -0
  199. data/lib/shopify-cli/resources.rb +1 -1
  200. data/lib/shopify-cli/resources/env_file.rb +9 -9
  201. data/lib/shopify-cli/result.rb +432 -0
  202. data/lib/shopify-cli/shopifolk.rb +35 -18
  203. data/lib/shopify-cli/sub_command.rb +1 -1
  204. data/lib/shopify-cli/task.rb +9 -1
  205. data/lib/shopify-cli/tasks.rb +7 -7
  206. data/lib/shopify-cli/tasks/create_api_client.rb +13 -4
  207. data/lib/shopify-cli/tasks/ensure_dev_store.rb +12 -12
  208. data/lib/shopify-cli/tasks/ensure_env.rb +18 -15
  209. data/lib/shopify-cli/tasks/ensure_loopback_url.rb +4 -4
  210. data/lib/shopify-cli/tasks/select_org_and_shop.rb +28 -24
  211. data/lib/shopify-cli/tasks/update_dashboard_urls.rb +10 -10
  212. data/lib/shopify-cli/transform_data_structure.rb +86 -0
  213. data/lib/shopify-cli/tunnel.rb +36 -30
  214. data/lib/shopify-cli/version.rb +1 -1
  215. data/lib/shopify_cli.rb +57 -52
  216. data/shopify-cli.gemspec +6 -6
  217. data/shopify.fish +1 -1
  218. data/shopify.sh +1 -1
  219. data/vendor/deps/cli-kit/REVISION +1 -1
  220. data/vendor/deps/cli-kit/lib/cli/kit/logger.rb +2 -2
  221. data/vendor/deps/cli-kit/lib/cli/kit/system.rb +3 -3
  222. data/vendor/deps/cli-ui/REVISION +1 -1
  223. data/vendor/deps/cli-ui/lib/cli/ui.rb +26 -22
  224. data/vendor/deps/cli-ui/lib/cli/ui/ansi.rb +4 -6
  225. data/vendor/deps/cli-ui/lib/cli/ui/frame.rb +3 -3
  226. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_stack.rb +8 -9
  227. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style.rb +1 -1
  228. data/vendor/deps/cli-ui/lib/cli/ui/glyph.rb +1 -0
  229. data/vendor/deps/cli-ui/lib/cli/ui/printer.rb +15 -3
  230. data/vendor/deps/cli-ui/lib/cli/ui/prompt/interactive_options.rb +4 -11
  231. data/vendor/deps/cli-ui/lib/cli/ui/spinner.rb +3 -5
  232. data/vendor/deps/cli-ui/lib/cli/ui/terminal.rb +10 -10
  233. data/vendor/deps/cli-ui/lib/cli/ui/version.rb +1 -1
  234. data/vendor/deps/cli-ui/lib/cli/ui/wrap.rb +56 -0
  235. data/vendor/deps/webrick/.gitignore +9 -0
  236. data/vendor/deps/webrick/Gemfile +3 -0
  237. data/vendor/deps/webrick/LICENSE.txt +22 -0
  238. data/vendor/deps/webrick/README.md +61 -0
  239. data/vendor/deps/webrick/Rakefile +10 -0
  240. data/vendor/deps/webrick/lib/webrick.rb +232 -0
  241. data/vendor/deps/webrick/lib/webrick/accesslog.rb +157 -0
  242. data/vendor/deps/webrick/lib/webrick/cgi.rb +313 -0
  243. data/vendor/deps/webrick/lib/webrick/compat.rb +36 -0
  244. data/vendor/deps/webrick/lib/webrick/config.rb +158 -0
  245. data/vendor/deps/webrick/lib/webrick/cookie.rb +172 -0
  246. data/vendor/deps/webrick/lib/webrick/htmlutils.rb +30 -0
  247. data/vendor/deps/webrick/lib/webrick/httpauth.rb +96 -0
  248. data/vendor/deps/webrick/lib/webrick/httpauth/authenticator.rb +117 -0
  249. data/vendor/deps/webrick/lib/webrick/httpauth/basicauth.rb +116 -0
  250. data/vendor/deps/webrick/lib/webrick/httpauth/digestauth.rb +395 -0
  251. data/vendor/deps/webrick/lib/webrick/httpauth/htdigest.rb +132 -0
  252. data/vendor/deps/webrick/lib/webrick/httpauth/htgroup.rb +97 -0
  253. data/vendor/deps/webrick/lib/webrick/httpauth/htpasswd.rb +158 -0
  254. data/vendor/deps/webrick/lib/webrick/httpauth/userdb.rb +53 -0
  255. data/vendor/deps/webrick/lib/webrick/httpproxy.rb +354 -0
  256. data/vendor/deps/webrick/lib/webrick/httprequest.rb +636 -0
  257. data/vendor/deps/webrick/lib/webrick/httpresponse.rb +564 -0
  258. data/vendor/deps/webrick/lib/webrick/https.rb +152 -0
  259. data/vendor/deps/webrick/lib/webrick/httpserver.rb +294 -0
  260. data/vendor/deps/webrick/lib/webrick/httpservlet.rb +23 -0
  261. data/vendor/deps/webrick/lib/webrick/httpservlet/abstract.rb +152 -0
  262. data/vendor/deps/webrick/lib/webrick/httpservlet/cgi_runner.rb +47 -0
  263. data/vendor/deps/webrick/lib/webrick/httpservlet/cgihandler.rb +126 -0
  264. data/vendor/deps/webrick/lib/webrick/httpservlet/erbhandler.rb +88 -0
  265. data/vendor/deps/webrick/lib/webrick/httpservlet/filehandler.rb +552 -0
  266. data/vendor/deps/webrick/lib/webrick/httpservlet/prochandler.rb +47 -0
  267. data/vendor/deps/webrick/lib/webrick/httpstatus.rb +194 -0
  268. data/vendor/deps/webrick/lib/webrick/httputils.rb +512 -0
  269. data/vendor/deps/webrick/lib/webrick/httpversion.rb +76 -0
  270. data/vendor/deps/webrick/lib/webrick/log.rb +156 -0
  271. data/vendor/deps/webrick/lib/webrick/server.rb +381 -0
  272. data/vendor/deps/webrick/lib/webrick/ssl.rb +215 -0
  273. data/vendor/deps/webrick/lib/webrick/utils.rb +265 -0
  274. data/vendor/deps/webrick/lib/webrick/version.rb +18 -0
  275. data/vendor/deps/webrick/webrick.gemspec +74 -0
  276. data/vendor/gen/template/bin/update-deps +9 -9
  277. metadata +83 -29
  278. data/docs/Gemfile +0 -5
  279. data/docs/Gemfile.lock +0 -258
  280. data/docs/_data/nav.yml +0 -35
  281. data/docs/_includes/footer.html +0 -15
  282. data/docs/_includes/head.html +0 -19
  283. data/docs/_includes/sidebar_nav.html +0 -22
  284. data/docs/_includes/toc.html +0 -112
  285. data/docs/_layouts/default.html +0 -79
  286. data/docs/css/docs.css +0 -157
  287. data/docs/images/header.png +0 -0
  288. data/docs/installing-ruby.md +0 -28
  289. data/lib/project_types/extension/features/argo/admin.rb +0 -20
  290. data/lib/project_types/extension/features/argo/base.rb +0 -129
  291. data/lib/project_types/extension/features/argo/checkout.rb +0 -20
  292. data/lib/project_types/extension/forms/register.rb +0 -47
  293. data/lib/project_types/extension/models/type.rb +0 -81
  294. data/lib/project_types/extension/models/types/checkout_post_purchase.rb +0 -23
  295. data/lib/project_types/extension/models/types/product_subscription.rb +0 -24
  296. data/lib/project_types/node/commands/generate/billing.rb +0 -39
  297. data/lib/project_types/node/commands/generate/page.rb +0 -59
  298. data/lib/project_types/node/commands/generate/webhook.rb +0 -37
  299. data/lib/project_types/script/layers/domain/script.rb +0 -18
  300. data/lib/project_types/script/layers/infrastructure/assemblyscript_tsconfig.rb +0 -38
  301. data/lib/project_types/script/layers/infrastructure/script_repository.rb +0 -59
  302. data/lib/project_types/script/templates/ts/as-pect.config.js +0 -27
  303. data/lib/project_types/script/templates/ts/as-pect.d.ts +0 -1
@@ -5,24 +5,40 @@ module ShopifyCli
5
5
  # The Shopifolk Feature flag will persist between runs so if the flag is enabled or disabled,
6
6
  # it will still be in that same state until the next class invocation.
7
7
  class Shopifolk
8
- GCLOUD_CONFIG_FILE = File.expand_path('~/.config/gcloud/configurations/config_default')
9
- DEV_PATH = '/opt/dev'
10
- SECTION = 'core'
11
- FEATURE_NAME = 'shopifolk'
8
+ GCLOUD_CONFIG_FILE = File.expand_path("~/.config/gcloud/configurations/config_default")
9
+ DEV_PATH = "/opt/dev"
10
+ SECTION = "core"
11
+ FEATURE_NAME = "shopifolk"
12
12
 
13
- ##
14
- # will return if the user appears to be a Shopify employee, based on several heuristics
15
- #
16
- # #### Returns
17
- #
18
- # * `is_shopifolk` - returns true if the user is a Shopify Employee
19
- #
20
- # #### Example
21
- #
22
- # ShopifyCli::Shopifolk.check
23
- #
24
- def self.check
25
- ShopifyCli::Shopifolk.new.shopifolk?
13
+ class << self
14
+ attr_writer :acting_as_shopify_organization
15
+
16
+ ##
17
+ # will return if the user appears to be a Shopify employee, based on several heuristics
18
+ #
19
+ # #### Returns
20
+ #
21
+ # * `is_shopifolk` - returns true if the user is a Shopify Employee
22
+ #
23
+ # #### Example
24
+ #
25
+ # ShopifyCli::Shopifolk.check
26
+ #
27
+ def check
28
+ ShopifyCli::Shopifolk.new.shopifolk?
29
+ end
30
+
31
+ def act_as_shopify_organization
32
+ @acting_as_shopify_organization = true
33
+ end
34
+
35
+ def acting_as_shopify_organization?
36
+ !!@acting_as_shopify_organization || (Project.has_current? && Project.current.config["shopify_organization"])
37
+ end
38
+
39
+ def reset
40
+ @acting_as_shopify_organization = nil
41
+ end
26
42
  end
27
43
 
28
44
  ##
@@ -35,6 +51,7 @@ module ShopifyCli
35
51
  #
36
52
  def shopifolk?
37
53
  return true if Feature.enabled?(FEATURE_NAME)
54
+
38
55
  if shopifolk_by_gcloud? && shopifolk_by_dev?
39
56
  ShopifyCli::Feature.enable(FEATURE_NAME)
40
57
  true
@@ -47,7 +64,7 @@ module ShopifyCli
47
64
  private
48
65
 
49
66
  def shopifolk_by_gcloud?
50
- ini&.dig("[#{SECTION}]", 'account')&.match?(/@shopify.com\z/)
67
+ ini&.dig("[#{SECTION}]", "account")&.match?(/@shopify.com\z/)
51
68
  end
52
69
 
53
70
  def shopifolk_by_dev?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require 'shopify_cli'
2
+ require "shopify_cli"
3
3
 
4
4
  module ShopifyCli
5
5
  class SubCommand < Command
@@ -1,4 +1,4 @@
1
- require 'shopify_cli'
1
+ require "shopify_cli"
2
2
 
3
3
  module ShopifyCli
4
4
  class Task
@@ -6,5 +6,13 @@ module ShopifyCli
6
6
  task = new
7
7
  task.call(*args, **kwargs)
8
8
  end
9
+
10
+ private
11
+
12
+ def wants_to_run_against_shopify_org?
13
+ @ctx.puts(@ctx.message("core.tasks.select_org_and_shop.identified_as_shopify"))
14
+ message = @ctx.message("core.tasks.select_org_and_shop.first_party")
15
+ CLI::UI::Prompt.confirm(message, default: false)
16
+ end
9
17
  end
10
18
  end
@@ -1,4 +1,4 @@
1
- require 'shopify_cli'
1
+ require "shopify_cli"
2
2
 
3
3
  module ShopifyCli
4
4
  module Tasks
@@ -30,11 +30,11 @@ module ShopifyCli
30
30
  Registry.add(-> () { const_get(task) }, name)
31
31
  end
32
32
 
33
- register :CreateApiClient, :create_api_client, 'shopify-cli/tasks/create_api_client'
34
- register :EnsureEnv, :ensure_env, 'shopify-cli/tasks/ensure_env'
35
- register :EnsureLoopbackURL, :ensure_loopback_url, 'shopify-cli/tasks/ensure_loopback_url'
36
- register :EnsureDevStore, :ensure_dev_store, 'shopify-cli/tasks/ensure_dev_store'
37
- register :SelectOrgAndShop, :select_org_and_shop, 'shopify-cli/tasks/select_org_and_shop'
38
- register :UpdateDashboardURLS, :update_dashboard_urls, 'shopify-cli/tasks/update_dashboard_urls'
33
+ register :CreateApiClient, :create_api_client, "shopify-cli/tasks/create_api_client"
34
+ register :EnsureEnv, :ensure_env, "shopify-cli/tasks/ensure_env"
35
+ register :EnsureLoopbackURL, :ensure_loopback_url, "shopify-cli/tasks/ensure_loopback_url"
36
+ register :EnsureDevStore, :ensure_dev_store, "shopify-cli/tasks/ensure_dev_store"
37
+ register :SelectOrgAndShop, :select_org_and_shop, "shopify-cli/tasks/select_org_and_shop"
38
+ register :UpdateDashboardURLS, :update_dashboard_urls, "shopify-cli/tasks/update_dashboard_urls"
39
39
  end
40
40
  end
@@ -1,15 +1,15 @@
1
- require 'shopify_cli'
1
+ require "shopify_cli"
2
2
 
3
3
  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-app-cli/help/start-app/"
8
8
 
9
9
  def call(ctx, org_id:, title:, type:)
10
10
  resp = ShopifyCli::PartnersAPI.query(
11
11
  ctx,
12
- 'create_app',
12
+ "create_app",
13
13
  org: org_id.to_i,
14
14
  title: title,
15
15
  type: type,
@@ -17,9 +17,18 @@ module ShopifyCli
17
17
  redir: [OAuth::REDIRECT_HOST]
18
18
  )
19
19
 
20
+ unless resp
21
+ ctx.abort("Error - empty response")
22
+ end
23
+
24
+ errors = resp.dig("errors")
25
+ if !errors.nil? && errors.any?
26
+ ctx.abort(errors.map { |err| "#{err["field"]} #{err["message"]}" }.join(", "))
27
+ end
28
+
20
29
  user_errors = resp.dig("data", "appCreate", "userErrors")
21
30
  if !user_errors.nil? && user_errors.any?
22
- ctx.abort(user_errors.map { |err| "#{err['field']} #{err['message']}" }.join(", "))
31
+ ctx.abort(user_errors.map { |err| "#{err["field"]} #{err["message"]}" }.join(", "))
23
32
  end
24
33
 
25
34
  ShopifyCli::Core::Monorail.metadata[:api_key] = resp.dig("data", "appCreate", "app", "apiKey")
@@ -1,22 +1,22 @@
1
- require 'shopify_cli'
1
+ require "shopify_cli"
2
2
 
3
3
  module ShopifyCli
4
4
  module Tasks
5
5
  class EnsureDevStore < ShopifyCli::Task
6
6
  def call(ctx)
7
7
  @ctx = ctx
8
- return ctx.puts(ctx.message(
9
- 'core.tasks.ensure_dev_store.could_not_verify_store', project.env.shop
8
+ return ctx.abort(ctx.message(
9
+ "core.tasks.ensure_dev_store.could_not_verify_store", project.env.shop
10
10
  )) if shop.nil?
11
- return if shop['transferDisabled'] == true
11
+ return if shop["transferDisabled"] == true
12
12
  return unless CLI::UI::Prompt.confirm(
13
- ctx.message('core.tasks.ensure_dev_store.convert_to_dev_store', project.env.shop)
13
+ ctx.message("core.tasks.ensure_dev_store.convert_to_dev_store", project.env.shop)
14
14
  )
15
- ShopifyCli::PartnersAPI.query(ctx, 'convert_dev_to_test_store', input: {
16
- organizationID: shop['orgID'].to_i,
17
- shopId: shop['shopId'],
15
+ ShopifyCli::PartnersAPI.query(ctx, "convert_dev_to_test_store", input: {
16
+ organizationID: shop["orgID"].to_i,
17
+ shopId: shop["shopId"],
18
18
  })
19
- ctx.puts(ctx.message('core.tasks.ensure_dev_store.transfer_disabled', project.env.shop))
19
+ ctx.puts(ctx.message("core.tasks.ensure_dev_store.transfer_disabled", project.env.shop))
20
20
  end
21
21
 
22
22
  private
@@ -29,9 +29,9 @@ module ShopifyCli
29
29
  @shop ||= begin
30
30
  current_domain = project.env.shop
31
31
  ShopifyCli::PartnersAPI::Organizations.fetch_all(@ctx).map do |org|
32
- org['stores'].find do |store|
33
- store['orgID'] = org['id']
34
- store['shopDomain'] == current_domain
32
+ org["stores"].find do |store|
33
+ store["orgID"] = org["id"]
34
+ store["shopDomain"] == current_domain
35
35
  end
36
36
  end.compact.first
37
37
  end
@@ -1,4 +1,4 @@
1
- require 'shopify_cli'
1
+ require "shopify_cli"
2
2
 
3
3
  module ShopifyCli
4
4
  module Tasks
@@ -22,14 +22,17 @@ module ShopifyCli
22
22
  private
23
23
 
24
24
  def fetch_org
25
+ if Shopifolk.check && wants_to_run_against_shopify_org?
26
+ Shopifolk.act_as_shopify_organization
27
+ end
25
28
  orgs = PartnersAPI::Organizations.fetch_with_app(@ctx)
26
29
  org_id = if orgs.count == 1
27
30
  orgs.first["id"]
28
31
  else
29
- CLI::UI::Prompt.ask(@ctx.message('core.tasks.ensure_env.organization_select')) do |handler|
32
+ CLI::UI::Prompt.ask(@ctx.message("core.tasks.ensure_env.organization_select")) do |handler|
30
33
  orgs.each do |org|
31
34
  handler.option(
32
- @ctx.message('core.partners_api.org_name_and_id', org['businessName'], org['id'])
35
+ @ctx.message("core.partners_api.org_name_and_id", org["businessName"], org["id"])
33
36
  ) { org["id"] }
34
37
  end
35
38
  end
@@ -41,15 +44,15 @@ module ShopifyCli
41
44
  if apps.count == 1
42
45
  apps.first
43
46
  elsif apps.count == 0
44
- @ctx.puts(@ctx.message('core.tasks.ensure_env.no_apps'))
45
- title = CLI::UI::Prompt.ask(@ctx.message('core.tasks.ensure_env.app_name'))
46
- type = CLI::UI::Prompt.ask(@ctx.message('core.tasks.ensure_env.app_type.select')) do |handler|
47
- handler.option(@ctx.message('core.tasks.ensure_env.app_type.select_public')) { 'public' }
48
- handler.option(@ctx.message('core.tasks.ensure_env.app_type.select_custom')) { 'custom' }
47
+ @ctx.puts(@ctx.message("core.tasks.ensure_env.no_apps"))
48
+ title = CLI::UI::Prompt.ask(@ctx.message("core.tasks.ensure_env.app_name"))
49
+ type = CLI::UI::Prompt.ask(@ctx.message("core.tasks.ensure_env.app_type.select")) do |handler|
50
+ handler.option(@ctx.message("core.tasks.ensure_env.app_type.select_public")) { "public" }
51
+ handler.option(@ctx.message("core.tasks.ensure_env.app_type.select_custom")) { "custom" }
49
52
  end
50
53
  ShopifyCli::Tasks::CreateApiClient.call(@ctx, org_id: org_id, title: title, type: type)
51
54
  else
52
- CLI::UI::Prompt.ask(@ctx.message('core.tasks.ensure_env.app_select')) do |handler|
55
+ CLI::UI::Prompt.ask(@ctx.message("core.tasks.ensure_env.app_select")) do |handler|
53
56
  apps.each { |app| handler.option(app["title"]) { app } }
54
57
  end
55
58
  end
@@ -59,9 +62,9 @@ module ShopifyCli
59
62
  if shops.count == 1
60
63
  shop = shops.first["shopDomain"]
61
64
  elsif shops.count == 0
62
- @ctx.puts(@ctx.message('core.tasks.ensure_env.no_development_stores', id))
65
+ @ctx.puts(@ctx.message("core.tasks.ensure_env.no_development_stores", id))
63
66
  else
64
- shop = CLI::UI::Prompt.ask(@ctx.message('core.tasks.ensure_env.development_store_select')) do |handler|
67
+ shop = CLI::UI::Prompt.ask(@ctx.message("core.tasks.ensure_env.development_store_select")) do |handler|
65
68
  shops.each { |s| handler.option(s["shopName"]) { s["shopDomain"] } }
66
69
  end
67
70
  end
@@ -69,13 +72,13 @@ module ShopifyCli
69
72
  end
70
73
 
71
74
  def write_env(env_data, org)
72
- id = org['id']
73
- app = get_app(id, org['apps'])
75
+ id = org["id"]
76
+ app = get_app(id, org["apps"])
74
77
 
75
- env_data[:shop] = get_shop(org['stores'], id)
78
+ env_data[:shop] = get_shop(org["stores"], id)
76
79
  env_data[:api_key] = app["apiKey"]
77
80
  env_data[:secret] = app["apiSecretKeys"].first["secret"]
78
- env_data[:scopes] = 'write_products,write_customers,write_draft_orders' if env_data[:scopes].nil?
81
+ env_data[:scopes] = "write_products,write_customers,write_draft_orders" if env_data[:scopes].nil?
79
82
  env_data[:extra] = {} if env_data[:extra].nil?
80
83
 
81
84
  Resources::EnvFile.new(
@@ -4,13 +4,13 @@ module ShopifyCli
4
4
  def call(ctx)
5
5
  @ctx = ctx
6
6
  api_key = Project.current.env.api_key
7
- result = ShopifyCli::PartnersAPI.query(ctx, 'get_app_urls', apiKey: api_key)
7
+ result = ShopifyCli::PartnersAPI.query(ctx, "get_app_urls", apiKey: api_key)
8
8
  loopback = OAuth::REDIRECT_HOST
9
- app = result['data']['app']
10
- urls = app['redirectUrlWhitelist']
9
+ app = result["data"]["app"]
10
+ urls = app["redirectUrlWhitelist"]
11
11
  if urls.grep(/#{loopback}/).empty?
12
12
  with_loopback = urls.push(loopback)
13
- ShopifyCli::PartnersAPI.query(@ctx, 'update_dashboard_urls', input: {
13
+ ShopifyCli::PartnersAPI.query(@ctx, "update_dashboard_urls", input: {
14
14
  redirectUrlWhitelist: with_loopback, apiKey: api_key
15
15
  })
16
16
  end
@@ -1,4 +1,4 @@
1
- require 'shopify_cli'
1
+ require "shopify_cli"
2
2
 
3
3
  module ShopifyCli
4
4
  module Tasks
@@ -8,8 +8,13 @@ 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
11
14
  org = get_organization(organization_id)
12
- shop_domain ||= get_shop_domain(org)
15
+ unless Shopifolk.acting_as_shopify_organization?
16
+ shop_domain ||= get_shop_domain(org)
17
+ end
13
18
  ShopifyCli::Core::Monorail.metadata[:organization_id] = org["id"].to_i
14
19
  response(org["id"].to_i, shop_domain)
15
20
  end
@@ -17,10 +22,9 @@ module ShopifyCli
17
22
  private
18
23
 
19
24
  def response(organization_id, shop_domain)
20
- {
21
- organization_id: organization_id,
22
- shop_domain: shop_domain,
23
- }
25
+ result = { organization_id: organization_id }
26
+ result[:shop_domain] = shop_domain if shop_domain
27
+ result
24
28
  end
25
29
 
26
30
  def organizations
@@ -31,45 +35,45 @@ module ShopifyCli
31
35
  @organization ||= if !organization_id.nil?
32
36
  org = ShopifyCli::PartnersAPI::Organizations.fetch(ctx, id: organization_id)
33
37
  if org.nil?
34
- ctx.puts(ctx.message('core.tasks.select_org_and_shop.error.authentication_issue', ShopifyCli::TOOL_NAME))
35
- ctx.abort(ctx.message('core.tasks.select_org_and_shop.error.organization_not_found'))
38
+ ctx.puts(ctx.message("core.tasks.select_org_and_shop.error.authentication_issue", ShopifyCli::TOOL_NAME))
39
+ ctx.abort(ctx.message("core.tasks.select_org_and_shop.error.organization_not_found"))
36
40
  end
37
41
  org
38
42
  elsif organizations.count == 0
39
- ctx.puts(ctx.message('core.tasks.select_org_and_shop.error.partners_notice'))
40
- ctx.puts(ctx.message('core.tasks.select_org_and_shop.authentication_issue', ShopifyCli::TOOL_NAME))
41
- ctx.abort(ctx.message('core.tasks.select_org_and_shop.error.no_organizations'))
43
+ ctx.puts(ctx.message("core.tasks.select_org_and_shop.error.partners_notice"))
44
+ ctx.puts(ctx.message("core.tasks.select_org_and_shop.authentication_issue", ShopifyCli::TOOL_NAME))
45
+ ctx.abort(ctx.message("core.tasks.select_org_and_shop.error.no_organizations"))
42
46
  elsif organizations.count == 1
43
47
  org = organizations.first
44
- ctx.puts(ctx.message('core.tasks.select_org_and_shop.organization', org['businessName'], org['id']))
48
+ ctx.puts(ctx.message("core.tasks.select_org_and_shop.organization", org["businessName"], org["id"]))
45
49
  org
46
50
  else
47
- org_id = CLI::UI::Prompt.ask(ctx.message('core.tasks.select_org_and_shop.organization_select')) do |handler|
51
+ org_id = CLI::UI::Prompt.ask(ctx.message("core.tasks.select_org_and_shop.organization_select")) do |handler|
48
52
  organizations.each do |o|
49
- handler.option(ctx.message('core.partners_api.org_name_and_id', o['businessName'], o['id'])) { o['id'] }
53
+ handler.option(ctx.message("core.partners_api.org_name_and_id", o["businessName"], o["id"])) { o["id"] }
50
54
  end
51
55
  end
52
- organizations.find { |o| o['id'] == org_id }
56
+ organizations.find { |o| o["id"] == org_id }
53
57
  end
54
58
  end
55
59
 
56
60
  def get_shop_domain(organization)
57
- valid_stores = organization['stores'].select do |store|
58
- store['transferDisabled'] == true || store['convertableToPartnerTest'] == true
61
+ valid_stores = organization["stores"].select do |store|
62
+ store["transferDisabled"] == true || store["convertableToPartnerTest"] == true
59
63
  end
60
64
 
61
65
  if valid_stores.count == 0
62
- ctx.puts(ctx.message('core.tasks.select_org_and_shop.error.no_development_stores'))
63
- ctx.puts(ctx.message('core.tasks.select_org_and_shop.create_store', organization['id']))
64
- ctx.puts(ctx.message('core.tasks.select_org_and_shop.authentication_issue', ShopifyCli::TOOL_NAME))
66
+ ctx.puts(ctx.message("core.tasks.select_org_and_shop.error.no_development_stores"))
67
+ ctx.puts(ctx.message("core.tasks.select_org_and_shop.create_store", organization["id"]))
68
+ ctx.puts(ctx.message("core.tasks.select_org_and_shop.authentication_issue", ShopifyCli::TOOL_NAME))
65
69
  elsif valid_stores.count == 1
66
- domain = valid_stores.first['shopDomain']
67
- ctx.puts(ctx.message('core.tasks.select_org_and_shop.development_store', domain))
70
+ domain = valid_stores.first["shopDomain"]
71
+ ctx.puts(ctx.message("core.tasks.select_org_and_shop.development_store", domain))
68
72
  domain
69
73
  else
70
74
  CLI::UI::Prompt.ask(
71
- ctx.message('core.tasks.select_org_and_shop.development_store_select'),
72
- options: valid_stores.map { |s| s['shopDomain'] }
75
+ ctx.message("core.tasks.select_org_and_shop.development_store_select"),
76
+ options: valid_stores.map { |s| s["shopDomain"] }
73
77
  )
74
78
  end
75
79
  end
@@ -7,24 +7,24 @@ module ShopifyCli
7
7
  @ctx = ctx
8
8
  project = ShopifyCli::Project.current
9
9
  api_key = project.env.api_key
10
- result = ShopifyCli::PartnersAPI.query(ctx, 'get_app_urls', apiKey: api_key)
11
- app = result['data']['app']
12
- consent = check_application_url(app['applicationUrl'], url)
13
- constructed_urls = construct_redirect_urls(app['redirectUrlWhitelist'], url, callback_url)
14
- return if url == app['applicationUrl']
15
- ShopifyCli::PartnersAPI.query(@ctx, 'update_dashboard_urls', input: {
16
- applicationUrl: consent ? url : app['applicationUrl'],
10
+ result = ShopifyCli::PartnersAPI.query(ctx, "get_app_urls", apiKey: api_key)
11
+ app = result["data"]["app"]
12
+ consent = check_application_url(app["applicationUrl"], url)
13
+ constructed_urls = construct_redirect_urls(app["redirectUrlWhitelist"], url, callback_url)
14
+ return if url == app["applicationUrl"]
15
+ ShopifyCli::PartnersAPI.query(@ctx, "update_dashboard_urls", input: {
16
+ applicationUrl: consent ? url : app["applicationUrl"],
17
17
  redirectUrlWhitelist: constructed_urls, apiKey: api_key
18
18
  })
19
- @ctx.puts(@ctx.message('core.tasks.update_dashboard_urls.updated'))
19
+ @ctx.puts(@ctx.message("core.tasks.update_dashboard_urls.updated"))
20
20
  rescue
21
- @ctx.puts(@ctx.message('core.tasks.update_dashboard_urls.update_error', ShopifyCli::TOOL_NAME))
21
+ @ctx.puts(@ctx.message("core.tasks.update_dashboard_urls.update_error", ShopifyCli::TOOL_NAME))
22
22
  raise
23
23
  end
24
24
 
25
25
  def check_application_url(application_url, new_url)
26
26
  return false if application_url.match(new_url)
27
- CLI::UI::Prompt.confirm(@ctx.message('core.tasks.update_dashboard_urls.update_prompt'))
27
+ CLI::UI::Prompt.confirm(@ctx.message("core.tasks.update_dashboard_urls.update_prompt"))
28
28
  end
29
29
 
30
30
  def construct_redirect_urls(urls, new_url, callback_url)
@@ -0,0 +1,86 @@
1
+
2
+ module ShopifyCli
3
+ ##
4
+ # `TransformDataStructure` helps to standardize data structure access. It
5
+ # traverses nested data structures and can convert
6
+ #
7
+ # * all strings used as keys to symbols,
8
+ # * all strings used as keys from `CamelCase` to `snake_case` and
9
+ # * associative array containers, e.g. from Hash to OpenStruct.
10
+ #
11
+ # Standardizing how a data structure is accessed greatly reduces the risk
12
+ # of subtle bugs especially when dealing with API responses.
13
+ #
14
+ # TransformDataStructure.new(
15
+ # symbolize_keys: true,
16
+ # underscore_keys: true,
17
+ # associative_array_container: OpenStruct
18
+ # ).call([{"SomeKey" => "value"}]).tap do |result|
19
+ # result.value # => [#<OpenStruct: some_key: "value">]
20
+ # end
21
+ #
22
+ # Since `TransformDataStructure` is a method object, it can easily be chained:
23
+ #
24
+ # require 'open-uri'
25
+ # ShopifyCli::Result
26
+ # .call { open("https://jsonplaceholder.typicode.com/todos/1") }
27
+ # .map(&TransformDataStructure.new(symbolize_keys: true, underscore_keys: true))
28
+ # .value # => { id: 1, user_id: 1, ... }
29
+ #
30
+ class TransformDataStructure
31
+ include ShopifyCli::MethodObject
32
+
33
+ class << self
34
+ private
35
+
36
+ def valid_associative_array_container(klass)
37
+ klass.respond_to?(:new) && klass.method_defined?(:[]=)
38
+ end
39
+ end
40
+
41
+ property! :underscore_keys, accepts: [true, false], default: false, reader: :underscore_keys?
42
+ property! :symbolize_keys, accepts: [true, false], default: false, reader: :symbolize_keys?
43
+ property! :associative_array_container,
44
+ accepts: ->(c) { c.respond_to?(:new) && c.method_defined?(:[]=) },
45
+ default: Hash
46
+
47
+ def call(object)
48
+ case object
49
+ when Array
50
+ object.map(&self).map(&:value)
51
+ when Hash
52
+ object.each.with_object(associative_array_container.new) do |(key, value), result|
53
+ result[transform_key(key)] = call(value).value
54
+ end
55
+ else
56
+ ShopifyCli::Result.success(object)
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def transform_key(key)
63
+ key
64
+ .yield_self(&method(:underscore_key))
65
+ .yield_self(&method(:symbolize_key))
66
+ end
67
+
68
+ def underscore_key(key)
69
+ return key unless underscore_keys? && key.respond_to?(:to_str)
70
+
71
+ key.to_str.dup.tap do |k|
72
+ k.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
73
+ k.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
74
+ k.tr!("-", "_")
75
+ k.gsub!(/\s/, "_")
76
+ k.gsub!(/__+/, "_")
77
+ k.downcase!
78
+ end
79
+ end
80
+
81
+ def symbolize_key(key)
82
+ return key unless symbolize_keys? && key.respond_to?(:to_sym)
83
+ key.to_sym
84
+ end
85
+ end
86
+ end