shopify-cli 1.3.1 → 1.7.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 (301) 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 +10 -1
  5. data/.github/workflows/release.yml +61 -0
  6. data/.github/workflows/triage.yml +22 -0
  7. data/.gitignore +0 -1
  8. data/.rubocop.yml +61 -8
  9. data/.rubocop_todo.yml +11 -0
  10. data/.travis.yml +1 -0
  11. data/CHANGELOG.md +30 -0
  12. data/Gemfile +12 -11
  13. data/Gemfile.lock +39 -37
  14. data/README.md +39 -7
  15. data/RELEASING.md +19 -29
  16. data/Rakefile +32 -28
  17. data/bin/load_shopify.rb +6 -6
  18. data/bin/shopify +2 -2
  19. data/dev.yml +2 -2
  20. data/docs/_config.yml +1 -18
  21. data/docs/app/node/commands/index.md +2 -80
  22. data/docs/app/node/index.md +2 -33
  23. data/docs/app/rails/commands/index.md +2 -78
  24. data/docs/app/rails/index.md +2 -34
  25. data/docs/core/index.md +2 -84
  26. data/docs/getting-started/index.md +2 -25
  27. data/docs/getting-started/install/index.md +1 -118
  28. data/docs/getting-started/migrate/index.md +2 -94
  29. data/docs/getting-started/uninstall/index.md +2 -35
  30. data/docs/getting-started/upgrade/index.md +2 -39
  31. data/docs/help/start-app/index.md +2 -4
  32. data/docs/index.md +2 -24
  33. data/ext/shopify-cli/extconf.rb +7 -7
  34. data/install.sh +1 -1
  35. data/lib/docgen/markdown.rb +11 -11
  36. data/lib/{project_types/extension/graphql → graphql}/get_app_by_api_key.graphql +0 -0
  37. data/lib/project_types/extension/cli.rb +64 -47
  38. data/lib/project_types/extension/commands/build.rb +3 -3
  39. data/lib/project_types/extension/commands/create.rb +16 -9
  40. data/lib/project_types/extension/commands/extension_command.rb +8 -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 +2 -2
  44. data/lib/project_types/extension/commands/tunnel.rb +12 -12
  45. data/lib/project_types/extension/extension_project.rb +4 -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 +36 -0
  56. data/lib/project_types/extension/messages/messages.rb +53 -52
  57. data/lib/project_types/extension/models/lazy_specification_handler.rb +12 -0
  58. data/lib/project_types/extension/models/specification.rb +35 -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 +77 -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 +38 -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 +46 -38
  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 +7 -7
  87. data/lib/project_types/node/messages/messages.rb +19 -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 +60 -54
  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 +24 -24
  103. data/lib/project_types/rails/gem.rb +24 -24
  104. data/lib/project_types/rails/messages/messages.rb +12 -9
  105. data/lib/project_types/rails/ruby.rb +2 -2
  106. data/lib/project_types/script/cli.rb +42 -38
  107. data/lib/project_types/script/commands/create.rb +13 -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 +17 -12
  112. data/lib/project_types/script/errors.rb +38 -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 +18 -19
  116. data/lib/project_types/script/layers/application/create_script.rb +13 -11
  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 +12 -18
  122. data/lib/project_types/script/layers/domain/errors.rb +7 -0
  123. data/lib/project_types/script/layers/domain/extension_point.rb +62 -7
  124. data/lib/project_types/script/layers/domain/metadata.rb +55 -0
  125. data/lib/project_types/script/layers/domain/push_package.rb +29 -6
  126. data/lib/project_types/script/layers/infrastructure/assemblyscript_project_creator.rb +19 -54
  127. data/lib/project_types/script/layers/infrastructure/assemblyscript_task_runner.rb +49 -18
  128. data/lib/project_types/script/layers/infrastructure/errors.rb +17 -1
  129. data/lib/project_types/script/layers/infrastructure/extension_point_repository.rb +12 -6
  130. data/lib/project_types/script/layers/infrastructure/project_creator.rb +2 -1
  131. data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +20 -13
  132. data/lib/project_types/script/layers/infrastructure/rust_project_creator.rb +72 -0
  133. data/lib/project_types/script/layers/infrastructure/rust_task_runner.rb +59 -0
  134. data/lib/project_types/script/layers/infrastructure/script_service.rb +26 -16
  135. data/lib/project_types/script/layers/infrastructure/task_runner.rb +4 -3
  136. data/lib/project_types/script/messages/messages.rb +67 -10
  137. data/lib/project_types/script/script_project.rb +47 -16
  138. data/lib/project_types/script/ui/error_handler.rb +115 -45
  139. data/lib/project_types/script/ui/printing_spinner.rb +1 -1
  140. data/lib/project_types/script/ui/strict_spinner.rb +1 -1
  141. data/lib/project_types/theme/cli.rb +40 -0
  142. data/lib/project_types/theme/commands/connect.rb +54 -0
  143. data/lib/project_types/theme/commands/create.rb +48 -0
  144. data/lib/project_types/theme/commands/deploy.rb +38 -0
  145. data/lib/project_types/theme/commands/generate.rb +20 -0
  146. data/lib/project_types/theme/commands/generate/env.rb +79 -0
  147. data/lib/project_types/theme/commands/push.rb +55 -0
  148. data/lib/project_types/theme/commands/serve.rb +31 -0
  149. data/lib/project_types/theme/forms/connect.rb +34 -0
  150. data/lib/project_types/theme/forms/create.rb +22 -0
  151. data/lib/project_types/theme/messages/messages.rb +147 -0
  152. data/lib/project_types/theme/tasks/ensure_themekit_installed.rb +78 -0
  153. data/lib/project_types/theme/themekit.rb +113 -0
  154. data/lib/rubygems_plugin.rb +3 -3
  155. data/lib/shopify-cli/admin_api.rb +52 -12
  156. data/lib/shopify-cli/admin_api/populate_resource_command.rb +17 -17
  157. data/lib/shopify-cli/admin_api/schema.rb +3 -3
  158. data/lib/shopify-cli/api.rb +38 -37
  159. data/lib/shopify-cli/command.rb +1 -1
  160. data/lib/shopify-cli/commands.rb +9 -9
  161. data/lib/shopify-cli/commands/config.rb +28 -28
  162. data/lib/shopify-cli/commands/connect.rb +35 -18
  163. data/lib/shopify-cli/commands/create.rb +5 -5
  164. data/lib/shopify-cli/commands/help.rb +6 -6
  165. data/lib/shopify-cli/commands/logout.rb +3 -3
  166. data/lib/shopify-cli/commands/system.rb +40 -31
  167. data/lib/shopify-cli/commands/version.rb +2 -2
  168. data/lib/shopify-cli/context.rb +43 -22
  169. data/lib/shopify-cli/core.rb +4 -4
  170. data/lib/shopify-cli/core/entry_point.rb +6 -6
  171. data/lib/shopify-cli/core/executor.rb +1 -1
  172. data/lib/shopify-cli/core/help_resolver.rb +2 -2
  173. data/lib/shopify-cli/core/monorail.rb +21 -19
  174. data/lib/shopify-cli/db.rb +2 -2
  175. data/lib/shopify-cli/feature.rb +1 -3
  176. data/lib/shopify-cli/form.rb +1 -1
  177. data/lib/shopify-cli/git.rb +17 -17
  178. data/lib/shopify-cli/helpers.rb +1 -1
  179. data/lib/shopify-cli/helpers/haikunator.rb +1 -1
  180. data/lib/shopify-cli/heroku.rb +28 -28
  181. data/lib/shopify-cli/http_request.rb +27 -0
  182. data/lib/shopify-cli/js_deps.rb +13 -13
  183. data/lib/shopify-cli/js_system.rb +5 -5
  184. data/lib/shopify-cli/lazy_delegator.rb +55 -0
  185. data/lib/shopify-cli/messages/messages.rb +24 -10
  186. data/lib/shopify-cli/method_object.rb +104 -0
  187. data/lib/shopify-cli/oauth.rb +25 -25
  188. data/lib/shopify-cli/oauth/servlet.rb +9 -9
  189. data/lib/shopify-cli/options.rb +3 -3
  190. data/lib/shopify-cli/packager.rb +24 -24
  191. data/lib/shopify-cli/partners_api.rb +38 -16
  192. data/lib/shopify-cli/partners_api/organizations.rb +10 -10
  193. data/lib/shopify-cli/process_supervision.rb +8 -8
  194. data/lib/shopify-cli/project.rb +27 -23
  195. data/lib/shopify-cli/project_type.rb +21 -5
  196. data/lib/shopify-cli/resolve_constant.rb +25 -0
  197. data/lib/shopify-cli/resources.rb +1 -1
  198. data/lib/shopify-cli/resources/env_file.rb +9 -9
  199. data/lib/shopify-cli/result.rb +432 -0
  200. data/lib/shopify-cli/shopifolk.rb +84 -0
  201. data/lib/shopify-cli/sub_command.rb +1 -1
  202. data/lib/shopify-cli/task.rb +9 -1
  203. data/lib/shopify-cli/tasks.rb +7 -7
  204. data/lib/shopify-cli/tasks/create_api_client.rb +17 -6
  205. data/lib/shopify-cli/tasks/ensure_dev_store.rb +11 -11
  206. data/lib/shopify-cli/tasks/ensure_env.rb +18 -15
  207. data/lib/shopify-cli/tasks/ensure_loopback_url.rb +4 -4
  208. data/lib/shopify-cli/tasks/select_org_and_shop.rb +29 -24
  209. data/lib/shopify-cli/tasks/update_dashboard_urls.rb +10 -10
  210. data/lib/shopify-cli/transform_data_structure.rb +86 -0
  211. data/lib/shopify-cli/tunnel.rb +36 -30
  212. data/lib/shopify-cli/version.rb +1 -1
  213. data/lib/shopify_cli.rb +57 -51
  214. data/shopify-cli.gemspec +6 -6
  215. data/shopify.fish +1 -1
  216. data/shopify.sh +1 -1
  217. data/vendor/deps/cli-kit/REVISION +1 -1
  218. data/vendor/deps/cli-kit/lib/cli/kit/logger.rb +2 -2
  219. data/vendor/deps/cli-kit/lib/cli/kit/system.rb +3 -3
  220. data/vendor/deps/cli-ui/REVISION +1 -1
  221. data/vendor/deps/cli-ui/lib/cli/ui.rb +26 -22
  222. data/vendor/deps/cli-ui/lib/cli/ui/ansi.rb +4 -6
  223. data/vendor/deps/cli-ui/lib/cli/ui/frame.rb +3 -3
  224. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_stack.rb +8 -9
  225. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style.rb +1 -1
  226. data/vendor/deps/cli-ui/lib/cli/ui/glyph.rb +1 -0
  227. data/vendor/deps/cli-ui/lib/cli/ui/printer.rb +15 -3
  228. data/vendor/deps/cli-ui/lib/cli/ui/prompt/interactive_options.rb +4 -11
  229. data/vendor/deps/cli-ui/lib/cli/ui/spinner.rb +3 -5
  230. data/vendor/deps/cli-ui/lib/cli/ui/terminal.rb +10 -10
  231. data/vendor/deps/cli-ui/lib/cli/ui/version.rb +1 -1
  232. data/vendor/deps/cli-ui/lib/cli/ui/wrap.rb +56 -0
  233. data/vendor/deps/webrick/.gitignore +9 -0
  234. data/vendor/deps/webrick/Gemfile +3 -0
  235. data/vendor/deps/webrick/LICENSE.txt +22 -0
  236. data/vendor/deps/webrick/README.md +61 -0
  237. data/vendor/deps/webrick/Rakefile +10 -0
  238. data/vendor/deps/webrick/lib/webrick.rb +232 -0
  239. data/vendor/deps/webrick/lib/webrick/accesslog.rb +157 -0
  240. data/vendor/deps/webrick/lib/webrick/cgi.rb +313 -0
  241. data/vendor/deps/webrick/lib/webrick/compat.rb +36 -0
  242. data/vendor/deps/webrick/lib/webrick/config.rb +158 -0
  243. data/vendor/deps/webrick/lib/webrick/cookie.rb +172 -0
  244. data/vendor/deps/webrick/lib/webrick/htmlutils.rb +30 -0
  245. data/vendor/deps/webrick/lib/webrick/httpauth.rb +96 -0
  246. data/vendor/deps/webrick/lib/webrick/httpauth/authenticator.rb +117 -0
  247. data/vendor/deps/webrick/lib/webrick/httpauth/basicauth.rb +116 -0
  248. data/vendor/deps/webrick/lib/webrick/httpauth/digestauth.rb +395 -0
  249. data/vendor/deps/webrick/lib/webrick/httpauth/htdigest.rb +132 -0
  250. data/vendor/deps/webrick/lib/webrick/httpauth/htgroup.rb +97 -0
  251. data/vendor/deps/webrick/lib/webrick/httpauth/htpasswd.rb +158 -0
  252. data/vendor/deps/webrick/lib/webrick/httpauth/userdb.rb +53 -0
  253. data/vendor/deps/webrick/lib/webrick/httpproxy.rb +354 -0
  254. data/vendor/deps/webrick/lib/webrick/httprequest.rb +636 -0
  255. data/vendor/deps/webrick/lib/webrick/httpresponse.rb +564 -0
  256. data/vendor/deps/webrick/lib/webrick/https.rb +152 -0
  257. data/vendor/deps/webrick/lib/webrick/httpserver.rb +294 -0
  258. data/vendor/deps/webrick/lib/webrick/httpservlet.rb +23 -0
  259. data/vendor/deps/webrick/lib/webrick/httpservlet/abstract.rb +152 -0
  260. data/vendor/deps/webrick/lib/webrick/httpservlet/cgi_runner.rb +47 -0
  261. data/vendor/deps/webrick/lib/webrick/httpservlet/cgihandler.rb +126 -0
  262. data/vendor/deps/webrick/lib/webrick/httpservlet/erbhandler.rb +88 -0
  263. data/vendor/deps/webrick/lib/webrick/httpservlet/filehandler.rb +552 -0
  264. data/vendor/deps/webrick/lib/webrick/httpservlet/prochandler.rb +47 -0
  265. data/vendor/deps/webrick/lib/webrick/httpstatus.rb +194 -0
  266. data/vendor/deps/webrick/lib/webrick/httputils.rb +512 -0
  267. data/vendor/deps/webrick/lib/webrick/httpversion.rb +76 -0
  268. data/vendor/deps/webrick/lib/webrick/log.rb +156 -0
  269. data/vendor/deps/webrick/lib/webrick/server.rb +381 -0
  270. data/vendor/deps/webrick/lib/webrick/ssl.rb +215 -0
  271. data/vendor/deps/webrick/lib/webrick/utils.rb +265 -0
  272. data/vendor/deps/webrick/lib/webrick/version.rb +18 -0
  273. data/vendor/deps/webrick/webrick.gemspec +74 -0
  274. data/vendor/gen/template/bin/update-deps +9 -9
  275. metadata +84 -29
  276. data/docs/Gemfile +0 -5
  277. data/docs/Gemfile.lock +0 -258
  278. data/docs/_data/nav.yml +0 -35
  279. data/docs/_includes/footer.html +0 -15
  280. data/docs/_includes/head.html +0 -19
  281. data/docs/_includes/sidebar_nav.html +0 -22
  282. data/docs/_includes/toc.html +0 -112
  283. data/docs/_layouts/default.html +0 -79
  284. data/docs/css/docs.css +0 -157
  285. data/docs/images/header.png +0 -0
  286. data/docs/installing-ruby.md +0 -28
  287. data/lib/project_types/extension/features/argo/admin.rb +0 -20
  288. data/lib/project_types/extension/features/argo/base.rb +0 -129
  289. data/lib/project_types/extension/features/argo/checkout.rb +0 -20
  290. data/lib/project_types/extension/forms/register.rb +0 -47
  291. data/lib/project_types/extension/models/type.rb +0 -81
  292. data/lib/project_types/extension/models/types/checkout_post_purchase.rb +0 -23
  293. data/lib/project_types/extension/models/types/product_subscription.rb +0 -24
  294. data/lib/project_types/node/commands/generate/billing.rb +0 -39
  295. data/lib/project_types/node/commands/generate/page.rb +0 -59
  296. data/lib/project_types/node/commands/generate/webhook.rb +0 -37
  297. data/lib/project_types/script/layers/domain/script.rb +0 -18
  298. data/lib/project_types/script/layers/infrastructure/assemblyscript_tsconfig.rb +0 -38
  299. data/lib/project_types/script/layers/infrastructure/script_repository.rb +0 -59
  300. data/lib/project_types/script/templates/ts/as-pect.config.js +0 -27
  301. data/lib/project_types/script/templates/ts/as-pect.d.ts +0 -1
@@ -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
  ##
@@ -34,7 +34,8 @@ module ShopifyCli
34
34
  # project = ShopifyCli::Project.current
35
35
  #
36
36
  def current(force_reload: false)
37
- at(Dir.pwd, force_reload: force_reload)
37
+ clear if force_reload
38
+ at(Dir.pwd)
38
39
  end
39
40
 
40
41
  ##
@@ -63,7 +64,7 @@ module ShopifyCli
63
64
  #
64
65
  def current_project_type
65
66
  return unless has_current?
66
- current.config['project_type'].to_sym
67
+ current.config["project_type"].to_sym
67
68
  end
68
69
 
69
70
  ##
@@ -83,33 +84,36 @@ module ShopifyCli
83
84
  # type = ShopifyCli::Project.current_project_type
84
85
  #
85
86
  def write(ctx, project_type:, organization_id:, **identifiers)
86
- require 'yaml' # takes 20ms, so deferred as late as possible.
87
+ require "yaml" # takes 20ms, so deferred as late as possible.
87
88
  content = Hash[{ project_type: project_type, organization_id: organization_id.to_i }
88
89
  .merge(identifiers)
89
90
  .collect { |k, v| [k.to_s, v] }]
91
+ content["shopify_organization"] = true if Shopifolk.acting_as_shopify_organization?
90
92
 
91
- ctx.write('.shopify-cli.yml', YAML.dump(content))
93
+ ctx.write(".shopify-cli.yml", YAML.dump(content))
94
+ clear
92
95
  end
93
96
 
94
97
  def project_name
95
98
  File.basename(current.directory)
96
99
  end
97
100
 
98
- private
101
+ def clear
102
+ @at = nil
103
+ @dir = nil
104
+ end
99
105
 
100
- def directory(dir, force_reload: false)
101
- @dir = nil if force_reload
106
+ private
102
107
 
108
+ def directory(dir)
103
109
  @dir ||= Hash.new { |h, k| h[k] = __directory(k) }
104
110
  @dir[dir]
105
111
  end
106
112
 
107
- def at(dir, force_reload: false)
108
- @at = nil if force_reload
109
-
110
- proj_dir = directory(dir, force_reload: force_reload)
113
+ def at(dir)
114
+ proj_dir = directory(dir)
111
115
  unless proj_dir
112
- raise(ShopifyCli::Abort, Context.message('core.project.error.not_in_project'))
116
+ raise(ShopifyCli::Abort, Context.message("core.project.error.not_in_project"))
113
117
  end
114
118
  @at ||= Hash.new { |h, k| h[k] = new(directory: k) }
115
119
  @at[proj_dir]
@@ -117,8 +121,8 @@ module ShopifyCli
117
121
 
118
122
  def __directory(curr)
119
123
  loop do
120
- return nil if curr == '/' || /^[A-Z]:\/$/.match?(curr)
121
- file = File.join(curr, '.shopify-cli.yml')
124
+ return nil if curr == "/" || /^[A-Z]:\/$/.match?(curr)
125
+ file = File.join(curr, ".shopify-cli.yml")
122
126
  return curr if File.exist?(file)
123
127
  curr = File.dirname(curr)
124
128
  end
@@ -164,15 +168,15 @@ module ShopifyCli
164
168
  #
165
169
  def config
166
170
  @config ||= begin
167
- config = load_yaml_file('.shopify-cli.yml')
171
+ config = load_yaml_file(".shopify-cli.yml")
168
172
  unless config.is_a?(Hash)
169
- raise ShopifyCli::Abort, Context.message('core.yaml.error.not_hash', '.shopify-cli.yml')
173
+ raise ShopifyCli::Abort, Context.message("core.yaml.error.not_hash", ".shopify-cli.yml")
170
174
  end
171
175
 
172
176
  # The app_type key was deprecated in favour of project_type, so replace it
173
- if config.key?('app_type')
174
- config['project_type'] = config['app_type']
175
- config.delete('app_type')
177
+ if config.key?("app_type")
178
+ config["project_type"] = config["app_type"]
179
+ config.delete("app_type")
176
180
  end
177
181
 
178
182
  config
@@ -183,16 +187,16 @@ module ShopifyCli
183
187
 
184
188
  def load_yaml_file(relative_path)
185
189
  f = File.join(directory, relative_path)
186
- require 'yaml' # takes 20ms, so deferred as late as possible.
190
+ require "yaml" # takes 20ms, so deferred as late as possible.
187
191
  begin
188
192
  YAML.load_file(f)
189
193
  rescue Psych::SyntaxError => e
190
- raise(ShopifyCli::Abort, Context.message('core.yaml.error.invalid', relative_path, e.message))
194
+ raise(ShopifyCli::Abort, Context.message("core.yaml.error.invalid", relative_path, e.message))
191
195
  # rescue Errno::EACCES => e
192
196
  # TODO
193
197
  # Dev::Helpers::EaccesHandler.diagnose_and_raise(f, e, mode: :read)
194
198
  rescue Errno::ENOENT
195
- raise ShopifyCli::Abort, Context.message('core.yaml.error.not_found', f)
199
+ raise ShopifyCli::Abort, Context.message("core.yaml.error.not_found", f)
196
200
  end
197
201
  end
198
202
  end
@@ -21,7 +21,7 @@ module ShopifyCli
21
21
  end
22
22
 
23
23
  def load_type(current_type, shallow = false)
24
- filepath = File.join(ShopifyCli::ROOT, 'lib', 'project_types', current_type.to_s, 'cli.rb')
24
+ filepath = File.join(ShopifyCli::ROOT, "lib", "project_types", current_type.to_s, "cli.rb")
25
25
  return unless File.exist?(filepath)
26
26
  @shallow_load = shallow
27
27
  @current_type = current_type
@@ -32,7 +32,7 @@ module ShopifyCli
32
32
  end
33
33
 
34
34
  def load_all
35
- Dir.glob(File.join(ShopifyCli::ROOT, 'lib', 'project_types', '*', 'cli.rb')).map do |filepath|
35
+ Dir.glob(File.join(ShopifyCli::ROOT, "lib", "project_types", "*", "cli.rb")).map do |filepath|
36
36
  load_type(filepath.split(File::Separator)[-2].to_sym, true)
37
37
  end
38
38
  end
@@ -45,8 +45,11 @@ module ShopifyCli
45
45
  File.join(ShopifyCli::PROJECT_TYPES_DIR, project_type.to_s, path)
46
46
  end
47
47
 
48
- def creator(name, command_const)
48
+ def title(name)
49
49
  @project_name = name
50
+ end
51
+
52
+ def creator(command_const)
50
53
  @project_creator_command_class = command_const
51
54
  ShopifyCli::Commands::Create.subcommand(command_const, @project_type)
52
55
  end
@@ -55,17 +58,30 @@ module ShopifyCli
55
58
  const_get(@project_creator_command_class)
56
59
  end
57
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
+
58
74
  def register_command(const, cmd)
59
75
  return if project_load_shallow
60
76
  Context.new.abort(
61
- Context.message('core.project_type.error.cannot_override_core', cmd, const)
77
+ Context.message("core.project_type.error.cannot_override_core", cmd, const)
62
78
  ) if Commands.core_command?(cmd)
63
79
  Commands.register(const, cmd)
64
80
  end
65
81
 
66
82
  def register_task(task, name)
67
83
  return if project_load_shallow
68
- ShopifyCli::Task.register(task, name)
84
+ ShopifyCli::Tasks.register(task, name)
69
85
  end
70
86
 
71
87
  def register_messages(messages)
@@ -0,0 +1,25 @@
1
+ ##
2
+ # `ResolveConstant` implements constant resolution. It is implemented as a
3
+ # `MethodObject` and therefore returns a result object. By default, constants
4
+ # are resolved relative to `Kernal`, but the top-level namespace is
5
+ # configurable:
6
+ #
7
+ # ShopifyCli::Resolve.call(:object).value # => Object
8
+ # ShopifyCli::Resolve.call('minitest/test').value # => MiniTest::Test
9
+ # ShopifyCli::Resolve.call(:test, namespace: MiniTest) # => MiniTest::Test
10
+ #
11
+ module ShopifyCli
12
+ class ResolveConstant
13
+ include ShopifyCli::MethodObject
14
+ property! :namespace, accepts: ->(ns) { ns.respond_to?(:const_get) }, default: Kernel
15
+
16
+ def call(name)
17
+ name
18
+ .to_s
19
+ .split(%r{/|:{2}})
20
+ .map { |const_name| const_name.split(/[_-]/).map(&:capitalize).join("") }
21
+ .join("::")
22
+ .yield_self { |const_name| namespace.const_get(const_name) }
23
+ end
24
+ end
25
+ end
@@ -1,5 +1,5 @@
1
1
  module ShopifyCli
2
2
  module Resources
3
- autoload :EnvFile, 'shopify-cli/resources/env_file'
3
+ autoload :EnvFile, "shopify-cli/resources/env_file"
4
4
  end
5
5
  end
@@ -4,13 +4,13 @@ module ShopifyCli
4
4
  module Resources
5
5
  class EnvFile
6
6
  include SmartProperties
7
- FILENAME = '.env'
7
+ FILENAME = ".env"
8
8
  KEY_MAP = {
9
- 'SHOPIFY_API_KEY' => :api_key,
10
- 'SHOPIFY_API_SECRET' => :secret,
11
- 'SHOP' => :shop,
12
- 'SCOPES' => :scopes,
13
- 'HOST' => :host,
9
+ "SHOPIFY_API_KEY" => :api_key,
10
+ "SHOPIFY_API_SECRET" => :secret,
11
+ "SHOP" => :shop,
12
+ "SCOPES" => :scopes,
13
+ "HOST" => :host,
14
14
  }
15
15
 
16
16
  class << self
@@ -72,7 +72,7 @@ module ShopifyCli
72
72
 
73
73
  def write(ctx)
74
74
  spin_group = CLI::UI::SpinGroup.new
75
- spin_group.add(ctx.message('core.env_file.saving_header', FILENAME)) do |spinner|
75
+ spin_group.add(ctx.message("core.env_file.saving_header", FILENAME)) do |spinner|
76
76
  output = []
77
77
  KEY_MAP.each do |key, value|
78
78
  output << "#{key}=#{send(value)}" if send(value)
@@ -80,9 +80,9 @@ module ShopifyCli
80
80
  extra.each do |key, value|
81
81
  output << "#{key}=#{value}"
82
82
  end
83
- ctx.print_task(ctx.message('core.env_file.saving', FILENAME))
83
+ ctx.print_task(ctx.message("core.env_file.saving", FILENAME))
84
84
  ctx.write(FILENAME, output.join("\n") + "\n")
85
- spinner.update_title(ctx.message('core.env_file.saved', FILENAME))
85
+ spinner.update_title(ctx.message("core.env_file.saved", FILENAME))
86
86
  end
87
87
  spin_group.wait
88
88
  end
@@ -0,0 +1,432 @@
1
+ module ShopifyCli
2
+ ##
3
+ # This module defines two containers for wrapping the result of an action. One
4
+ # for signifying the successful execution of an action and one for signifying
5
+ # a failure. Both containers implement the same API, which has been designed
6
+ # to simplify transforming a result through a series of steps and centralize
7
+ # the error handling in one place. The implementation is heavily inspired by a
8
+ # concept known as result monads in other languages. Consider the following
9
+ # example that uses lambda expressions as stand-ins for more complex method
10
+ # objects:
11
+ #
12
+ # require 'open-uri'
13
+ # Todo = Struct.new(:title, :completed)
14
+ #
15
+ # fetch_data = ->(url) { open(url) }
16
+ # parse_data = ->(json) { JSON.parse(json) }
17
+ # build_todo = ->(attrs) do
18
+ # Todo.new(attrs.fetch(:title), attrs.fetch(:completed))
19
+ # end
20
+ #
21
+ # Result.wrap(&fetch_data)
22
+ # .call("https://jsonplaceholder.typicode.com/todos/1")
23
+ # .then(&parse_data)
24
+ # .then(&build_todo)
25
+ # .map(&:title)
26
+ # .unwrap(nil) # => String | nil
27
+ #
28
+ # If everything goes well, this code returns the title of the to do that is
29
+ # being fetched from `https://jsonplaceholder.typicode.com/todos/1`. However,
30
+ # there are several possible failure scenarios:
31
+ #
32
+ # * fetching the data could fail due to a network error,
33
+ # * the data returned from the server might not be valid JSON, or
34
+ # * the data is valid but does not have the right shape.
35
+ #
36
+ # If any of these scenarios arises, all subsequent `then` and `map` blocks are
37
+ # skipped until the result is either unwrapped or we manually recover from the
38
+ # failure by specifying a `rescue` clause:
39
+ #
40
+ # Result.wrap { raise "Boom!" }
41
+ # .rescue { |e| e.message.upcase }
42
+ # .unwrap(nil) # => "BOOM!"
43
+ #
44
+ # In the event of a failure that hasn't been rescued from,
45
+ # `unwrap` returns the fallback value specified by the caller:
46
+ #
47
+ # Result.wrap { raise "Boom!" }.unwrap(nil) # => nil
48
+ # Result.wrap { raise "Boom!" }.unwrap { |e| e.message } # => "Boom!"
49
+ #
50
+ module Result
51
+ class Error < RuntimeError; end
52
+ class UnexpectedSuccess < Error; end
53
+ class UnexpectedFailure < Error; end
54
+
55
+ ##
56
+ # Implements a container for wrapping a success value. The main purpose of
57
+ # the container is to support further transformations of the result and
58
+ # centralize error handling should any of the subsequent transformations
59
+ # fail:
60
+ #
61
+ # result = Result
62
+ # .new("{}")
63
+ # .then { |json| JSON.parse(json) }
64
+ # .tap do |result|
65
+ # result.success? # => true
66
+ # result.value # => {}
67
+ # .then { |data| data.fetch(:firstname) }
68
+ # .tap do |result|
69
+ # result.failure? # => true
70
+ # result.error # => KeyError
71
+ # end
72
+ #
73
+ # `Success` implements two transformation functions: `then` and `map`. The
74
+ # former makes no assumption regarding the return value of the
75
+ # transformation. The latter on the other hand expects the transformation to
76
+ # be successful. If this assumption is violated, program execution is
77
+ # interrupted and an error is raised. As the purpose of result objects is to
78
+ # guard against exactly that. This is generally a flaw and requires the code
79
+ # to either be hardened or to substitute the call to `map` with a call to
80
+ # `then`. `map` should only be used for transformations that cannot fail and
81
+ # when the caller wants to state exactly that fact.
82
+ #
83
+ class Success
84
+ attr_reader :value
85
+
86
+ ##
87
+ # initializes a new `Success` from an arbitrary value.
88
+ def initialize(value)
89
+ @value = value
90
+ end
91
+
92
+ ##
93
+ # always returns true to indicate that this result represents a success.
94
+ #
95
+ def success?
96
+ true
97
+ end
98
+
99
+ ##
100
+ # always returns false to indicate that this result represents a success.
101
+ #
102
+ def failure?
103
+ false
104
+ end
105
+
106
+ ##
107
+ # raises an `UnexpectedSuccess` as a `Failure` does not carry an error
108
+ # value.
109
+ #
110
+ def error
111
+ raise UnexpectedSuccess
112
+ end
113
+
114
+ ##
115
+ # returns a new `Success` wrapping the result of the given block. The
116
+ # block is called with the current value. If the block raises an exception
117
+ # or returns a `Failure`, an exception is raised. `map` assumes any
118
+ # transformation to succeed. Transformations that are expected to fail under
119
+ # certain conditions should only be transformed using `then`:
120
+ #
121
+ # Success
122
+ # .new(nil)
123
+ # .map { |n| n + 1 } # => raises NoMethodError
124
+ #
125
+ # Therefore, map should only be used here if the previous success value is
126
+ # guaranteed to be a number or if the block handles nil cases properly:
127
+ #
128
+ # Success
129
+ # .new(nil)
130
+ # .map { |n| (n || 0) + 1 }
131
+ # .value # => 1
132
+ #
133
+ def map(&block)
134
+ self.then(&block).tap do |result|
135
+ return result if result.success?
136
+
137
+ result.unwrap { |error| error }.tap do |error|
138
+ case error
139
+ when Exception
140
+ raise error
141
+ else
142
+ raise UnexpectedFailure, error
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ ##
149
+ # returns a new result by wrapping the return value of the block. The
150
+ # block is invoked with the current success value. The result can either
151
+ # be a `Success` or a `Failure`. The former is the default. The latter
152
+ # occurs when executing the block either
153
+ #
154
+ # - raised an exception,
155
+ # - returned an instance of a subclass of `Exception`, or
156
+ # - returned a `Failure`.
157
+ #
158
+ # The example below illustrates this behavior:
159
+ #
160
+ # result = Success
161
+ # .new(1)
162
+ # .then { |n| n + 1 }
163
+ # .tap do |result|
164
+ # result.success? # => true
165
+ # result.value # => 2
166
+ # end
167
+ #
168
+ # result.then { |n| n / 0 }.error # => ZeroDivisionError
169
+ # result.then { RuntimeError.new }.error # => RuntimeError
170
+ # result.then { Failure.new("Boom!") }.error # => "Boom!"
171
+ #
172
+ def then(&block)
173
+ Result.wrap(&block).call(@value)
174
+ end
175
+
176
+ ##
177
+ # is a no-op and simply returns itself. Only a `Failure` can be
178
+ # transformed using `rescue`.
179
+ #
180
+ def rescue
181
+ self
182
+ end
183
+
184
+ ##
185
+ # returns the success value and ignores the fallback value that was either
186
+ # provided as a method argument or by passing a block. However, the caller
187
+ # is still required to specify a fallback value to ensure that in the
188
+ # event of a `Failure` program execution can continue in a controlled
189
+ # manner:
190
+ #
191
+ # Success.new(1).unwrap(0) => 1
192
+ #
193
+ def unwrap(*args, &block)
194
+ raise ArgumentError, "expected either a fallback value or a block" unless (args.length == 1) ^ block
195
+ @value
196
+ end
197
+ end
198
+
199
+ ##
200
+ # Implements a container for wrapping an error value. In many cases, the
201
+ # error value is going to be an exception but other values are fully
202
+ # supported:
203
+ #
204
+ # Failure
205
+ # .new(RuntimeError.new("Something went wrong"))
206
+ # .error # => RuntimeError.new
207
+ #
208
+ # Failure
209
+ # .new("Something went wrong")
210
+ # .error # => "Something went wrong"
211
+ #
212
+ # `Failure` does not support transformations with `then` and `map`. When any
213
+ # of these two methods is invoked on a `Failure`, the `Failure` itself is
214
+ # returned unless it is rescued from or unwrapped. This enables the caller to
215
+ # build optimistic transformation chains and defer error handling:
216
+ #
217
+ # Failure
218
+ # .new(nil)
219
+ # .then { |json| JSON.parse(json) } # Ignored
220
+ # .then(&:with_indifferent_access) # Ignored
221
+ # .then { |data| data.values_at(:firstname, :lastname) } # Ignored
222
+ # .unwrap(Person.new("John", "Doe")) # => Person
223
+ #
224
+ # Alternatively, we could resucue from the error and then proceed with the
225
+ # remanining transformations:
226
+ #
227
+ # Person = Struct.new(:firstname, :lastname)
228
+ # Failure
229
+ # .new(nil)
230
+ # .then { |json| JSON.parse(json) } # Ignored
231
+ # .then(&:with_indifferent_access) # Ignored
232
+ # .rescue { {firstname: "John", lastname: "Doe" }}
233
+ # .then { |data| data.values_at(:firstname, :lastname) } # Executed
234
+ # .then { |members| Person.new(*members) } # Executed
235
+ # .unwrap(nil) # => Person
236
+ #
237
+ class Failure
238
+ attr_reader :error
239
+
240
+ ##
241
+ # initializes a new `Failure` from an arbitrary value. In many cases, this
242
+ # value is going to be an instance of a subclass of `Exception` but any
243
+ # type is supported.
244
+ #
245
+ def initialize(error)
246
+ @error = error
247
+ end
248
+
249
+ ##
250
+ # always returns `false` to indicate that this result represents a failure.
251
+ #
252
+ def success?
253
+ false
254
+ end
255
+
256
+ ##
257
+ # Always returns `true` to indicate that this result represents a failure.
258
+ #
259
+ def failure?
260
+ true
261
+ end
262
+
263
+ ##
264
+ # raises an `ShopifyCli::Result::UnexpectedError` as a
265
+ # `ShopifyCli::Result::Failure` does not carry a success value.
266
+ #
267
+ def value
268
+ raise UnexpectedFailure
269
+ end
270
+
271
+ ##
272
+ # is a no-op and simply returns itself. This is essential to skip
273
+ # transformation steps in a chain once an error has occurred.
274
+ #
275
+ def map
276
+ self
277
+ end
278
+
279
+ ##
280
+ # is a no-op and simply returns itself. This is essential to skip
281
+ # transformation steps in a chain once an error has occurred.
282
+ #
283
+ def then
284
+ self
285
+ end
286
+
287
+ ##
288
+ # can be used to recover from a failure or produce a new failure with a
289
+ # different error.
290
+ #
291
+ # Failure
292
+ # .new("Something went wrong")
293
+ # .rescue { |msg| [msg, "but we fixed it!"].join(" "") }
294
+ # .tap do |result|
295
+ # result.success? # => true
296
+ # result.value # => "Something went wrong but we fixed it!"
297
+ # end
298
+ #
299
+ # `rescue` is opinionated when it comes to the return value of the block.
300
+ # If the return value is an `Exception` – either one that was raised or an
301
+ # instance of a subclass of `Exception` – a `Failure` is returned. Any
302
+ # other value results in a `Success` unless the value has been explicitly
303
+ # wrapped in a `Failure`:
304
+ #
305
+ # Failure
306
+ # .new(RuntimeError.new)
307
+ # .rescue { "All good! "}
308
+ # .success? # => true
309
+ #
310
+ # Failure
311
+ # .new(RuntimeError.new)
312
+ # .rescue { Failure.new("Still broken!") }
313
+ # .success? # => false
314
+ #
315
+ def rescue(&block)
316
+ Result.wrap(&block).call(@error)
317
+ end
318
+
319
+ ##
320
+ # returns the fallback value specified by the caller. The fallback value
321
+ # can be provided as a method argument or as a block. If a block is given,
322
+ # it receives the error as its first and only argument:
323
+ #
324
+ # failure = Failure.new(RuntimeError.new("Something went wrong!"))
325
+ #
326
+ # failure.unwrap(nil) # => nil
327
+ # failure.unwrap { |e| e.message } # => "Something went wrong!"
328
+ #
329
+ # #### Parameters
330
+ #
331
+ # * `*args` should be an `Array` with zero or one element
332
+ # * `&block` should be a Proc that takes zero or one argument
333
+ #
334
+ # #### Raises
335
+ #
336
+ # * `ArgumentError` if both a fallback argument and a block is provided
337
+ #
338
+ def unwrap(*args, &block)
339
+ raise ArgumentError, "expected either a fallback value or a block" unless (args.length == 1) ^ block
340
+ block ? block.call(@error) : args.pop
341
+ end
342
+ end
343
+
344
+ ##
345
+ # wraps the given value into a `ShopifyCli::Result::Success` container
346
+ #
347
+ # #### Parameters
348
+ #
349
+ # * `value` a value of arbitrary type
350
+ #
351
+ def self.success(value)
352
+ Result::Success.new(value)
353
+ end
354
+
355
+ ##
356
+ # wraps the given value into a `ShopifyCli::Result::Failure` container
357
+ #
358
+ # #### Parameters
359
+ #
360
+ # * `error` a value of arbitrary type
361
+ #
362
+ def self.failure(error)
363
+ Result::Failure.new(error)
364
+ end
365
+
366
+ ##
367
+ # takes either a value or a block and chooses the appropriate result
368
+ # container based on the type of the value or the type of the block's return
369
+ # value. If the type is an exception, it is wrapped in a
370
+ # `ShopifyCli::Result::Failure` and otherwise in a
371
+ # `ShopifyCli::Result::Success`. If a block was provided instead of value, a
372
+ # `Proc` is returned and the result wrapping doesn't occur until the block
373
+ # is invoked.
374
+ #
375
+ # #### Parameters
376
+ #
377
+ # * `*args` should be an `Array` with zero or one element
378
+ # * `&block` should be a `Proc` that takes zero or one argument
379
+ #
380
+ # #### Returns
381
+ #
382
+ # Returns either a `Result::Success`, `Result::Failure` or a `Proc` that
383
+ # produces one of the former when invoked.
384
+ #
385
+ # #### Examples
386
+ #
387
+ # Result.wrap(1) # => ShopifyCli::Result::Success
388
+ # Result.wrap(RuntimeError.new) # => ShopifyCli::Result::Failure
389
+ #
390
+ # Result.wrap { 1 } # => Proc
391
+ # Result.wrap { 1 }.call # => ShopifyCli::Result::Success
392
+ # Result.wrap { raise }.call # => ShopifyCli::Result::Failure
393
+ #
394
+ # Result.wrap { |s| s.upcase }.call("hello").tap do |result|
395
+ # result # => Result::Success
396
+ # result.value # => "HELLO"
397
+ # end
398
+ #
399
+ def self.wrap(*values, &block)
400
+ raise ArgumentError, "expected either a value or a block" unless (values.length == 1) ^ block
401
+
402
+ if values.length == 1
403
+ values.pop.yield_self do |value|
404
+ case value
405
+ when Result::Success, Result::Failure
406
+ value
407
+ when NilClass, Exception
408
+ Result.failure(value)
409
+ else
410
+ Result.success(value)
411
+ end
412
+ end
413
+ else
414
+ ->(*args) do
415
+ begin
416
+ wrap(block.call(*args))
417
+ rescue Exception => error # rubocop:disable Lint/RescueException
418
+ wrap(error)
419
+ end
420
+ end
421
+ end
422
+ end
423
+
424
+ ##
425
+ # Wraps the given block and invokes it with the passed arguments.
426
+ #
427
+ def self.call(*args, &block)
428
+ raise ArgumentError, "expected a block" unless block
429
+ wrap(&block).call(*args)
430
+ end
431
+ end
432
+ end