shopify-cli 0.9.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 (273) hide show
  1. checksums.yaml +7 -0
  2. data/.github/CODEOWNERS +1 -0
  3. data/.github/CODE_OF_CONDUCT.md +73 -0
  4. data/.github/CONTRIBUTING.md +51 -0
  5. data/.github/DESIGN.md +153 -0
  6. data/.github/ISSUE_TEMPLATE.md +38 -0
  7. data/.github/PULL_REQUEST_TEMPLATE.md +22 -0
  8. data/.github/probots.yml +3 -0
  9. data/.gitignore +19 -0
  10. data/.rubocop.yml +47 -0
  11. data/.ruby-version +1 -0
  12. data/.travis.yml +12 -0
  13. data/Gemfile +22 -0
  14. data/Gemfile.lock +77 -0
  15. data/LICENSE.md +7 -0
  16. data/README.md +13 -0
  17. data/Rakefile +101 -0
  18. data/SECURITY.md +59 -0
  19. data/Vagrantfile +17 -0
  20. data/bin/load_shopify.rb +20 -0
  21. data/bin/shopify +32 -0
  22. data/dev.yml +17 -0
  23. data/docs/Gemfile +5 -0
  24. data/docs/Gemfile.lock +248 -0
  25. data/docs/_config.yml +16 -0
  26. data/docs/_data/nav.yml +26 -0
  27. data/docs/_includes/footer.html +15 -0
  28. data/docs/_includes/head.html +19 -0
  29. data/docs/_includes/sidebar_nav.html +22 -0
  30. data/docs/_includes/toc.html +112 -0
  31. data/docs/_layouts/default.html +79 -0
  32. data/docs/app/node/commands/index.md +82 -0
  33. data/docs/app/node/index.md +35 -0
  34. data/docs/app/rails/commands/index.md +80 -0
  35. data/docs/app/rails/index.md +36 -0
  36. data/docs/core/index.md +70 -0
  37. data/docs/css/docs.css +157 -0
  38. data/docs/getting-started/index.md +61 -0
  39. data/docs/help/start-app/index.md +6 -0
  40. data/docs/images/header.png +0 -0
  41. data/docs/index.md +27 -0
  42. data/docs/installing-ruby.md +28 -0
  43. data/ext/shopify-cli/extconf.rb +27 -0
  44. data/install.sh +7 -0
  45. data/lib/docgen/class_template.md.erb +81 -0
  46. data/lib/docgen/index_template.md.erb +5 -0
  47. data/lib/docgen/markdown.rb +101 -0
  48. data/lib/graphql/admin_introspection.graphql +87 -0
  49. data/lib/graphql/all_organizations.graphql +19 -0
  50. data/lib/graphql/all_orgs_with_apps.graphql +30 -0
  51. data/lib/graphql/api_versions.graphql +6 -0
  52. data/lib/graphql/convert_dev_to_test_store.graphql +10 -0
  53. data/lib/graphql/create_app.graphql +20 -0
  54. data/lib/graphql/create_customer.graphql +9 -0
  55. data/lib/graphql/create_draft_order.graphql +8 -0
  56. data/lib/graphql/create_product.graphql +9 -0
  57. data/lib/graphql/extension_create.graphql +21 -0
  58. data/lib/graphql/extension_update_draft.graphql +18 -0
  59. data/lib/graphql/find_organization.graphql +17 -0
  60. data/lib/graphql/get_app_urls.graphql +6 -0
  61. data/lib/graphql/update_dashboard_urls.graphql +8 -0
  62. data/lib/project_types/extension/cli.rb +71 -0
  63. data/lib/project_types/extension/commands/build.rb +29 -0
  64. data/lib/project_types/extension/commands/create.rb +49 -0
  65. data/lib/project_types/extension/commands/extension_command.rb +22 -0
  66. data/lib/project_types/extension/commands/push.rb +69 -0
  67. data/lib/project_types/extension/commands/register.rb +78 -0
  68. data/lib/project_types/extension/commands/serve.rb +24 -0
  69. data/lib/project_types/extension/commands/tunnel.rb +69 -0
  70. data/lib/project_types/extension/extension_project.rb +85 -0
  71. data/lib/project_types/extension/extension_project_keys.rb +10 -0
  72. data/lib/project_types/extension/features/argo.rb +48 -0
  73. data/lib/project_types/extension/features/argo_dependencies.rb +28 -0
  74. data/lib/project_types/extension/features/argo_setup.rb +54 -0
  75. data/lib/project_types/extension/features/argo_setup_step.rb +31 -0
  76. data/lib/project_types/extension/features/argo_setup_steps.rb +53 -0
  77. data/lib/project_types/extension/features/tunnel_url.rb +20 -0
  78. data/lib/project_types/extension/forms/create.rb +52 -0
  79. data/lib/project_types/extension/forms/register.rb +48 -0
  80. data/lib/project_types/extension/messages/message_loading.rb +37 -0
  81. data/lib/project_types/extension/messages/messages.rb +126 -0
  82. data/lib/project_types/extension/models/app.rb +14 -0
  83. data/lib/project_types/extension/models/registration.rb +19 -0
  84. data/lib/project_types/extension/models/type.rb +76 -0
  85. data/lib/project_types/extension/models/types/checkout_post_purchase.rb +20 -0
  86. data/lib/project_types/extension/models/types/subscription_management.rb +20 -0
  87. data/lib/project_types/extension/models/validation_error.rb +17 -0
  88. data/lib/project_types/extension/models/version.rb +15 -0
  89. data/lib/project_types/extension/tasks/converters/registration_converter.rb +26 -0
  90. data/lib/project_types/extension/tasks/converters/validation_error_converter.rb +25 -0
  91. data/lib/project_types/extension/tasks/converters/version_converter.rb +28 -0
  92. data/lib/project_types/extension/tasks/create_extension.rb +31 -0
  93. data/lib/project_types/extension/tasks/get_apps.rb +34 -0
  94. data/lib/project_types/extension/tasks/update_draft.rb +29 -0
  95. data/lib/project_types/extension/tasks/user_errors.rb +45 -0
  96. data/lib/project_types/node/cli.rb +37 -0
  97. data/lib/project_types/node/commands/create.rb +117 -0
  98. data/lib/project_types/node/commands/deploy.rb +22 -0
  99. data/lib/project_types/node/commands/deploy/heroku.rb +91 -0
  100. data/lib/project_types/node/commands/generate.rb +51 -0
  101. data/lib/project_types/node/commands/generate/billing.rb +37 -0
  102. data/lib/project_types/node/commands/generate/page.rb +55 -0
  103. data/lib/project_types/node/commands/generate/webhook.rb +33 -0
  104. data/lib/project_types/node/commands/open.rb +16 -0
  105. data/lib/project_types/node/commands/populate.rb +23 -0
  106. data/lib/project_types/node/commands/populate/customer.rb +31 -0
  107. data/lib/project_types/node/commands/populate/draft_order.rb +28 -0
  108. data/lib/project_types/node/commands/populate/product.rb +30 -0
  109. data/lib/project_types/node/commands/serve.rb +45 -0
  110. data/lib/project_types/node/commands/tunnel.rb +39 -0
  111. data/lib/project_types/node/forms/create.rb +87 -0
  112. data/lib/project_types/node/messages/messages.rb +260 -0
  113. data/lib/project_types/rails/cli.rb +41 -0
  114. data/lib/project_types/rails/commands/create.rb +126 -0
  115. data/lib/project_types/rails/commands/deploy.rb +22 -0
  116. data/lib/project_types/rails/commands/deploy/heroku.rb +113 -0
  117. data/lib/project_types/rails/commands/generate.rb +49 -0
  118. data/lib/project_types/rails/commands/generate/webhook.rb +39 -0
  119. data/lib/project_types/rails/commands/open.rb +16 -0
  120. data/lib/project_types/rails/commands/populate.rb +23 -0
  121. data/lib/project_types/rails/commands/populate/customer.rb +31 -0
  122. data/lib/project_types/rails/commands/populate/draft_order.rb +28 -0
  123. data/lib/project_types/rails/commands/populate/product.rb +30 -0
  124. data/lib/project_types/rails/commands/serve.rb +47 -0
  125. data/lib/project_types/rails/commands/tunnel.rb +39 -0
  126. data/lib/project_types/rails/forms/create.rb +116 -0
  127. data/lib/project_types/rails/gem.rb +56 -0
  128. data/lib/project_types/rails/messages/messages.rb +283 -0
  129. data/lib/project_types/rails/ruby.rb +17 -0
  130. data/lib/project_types/script/cli.rb +76 -0
  131. data/lib/project_types/script/commands/create.rb +45 -0
  132. data/lib/project_types/script/commands/disable.rb +36 -0
  133. data/lib/project_types/script/commands/enable.rb +46 -0
  134. data/lib/project_types/script/commands/push.rb +39 -0
  135. data/lib/project_types/script/config/extension_points.yml +18 -0
  136. data/lib/project_types/script/errors.rb +16 -0
  137. data/lib/project_types/script/forms/create.rb +29 -0
  138. data/lib/project_types/script/forms/enable.rb +24 -0
  139. data/lib/project_types/script/forms/push.rb +19 -0
  140. data/lib/project_types/script/forms/script_form.rb +66 -0
  141. data/lib/project_types/script/graphql/app_script_update_or_create.graphql +27 -0
  142. data/lib/project_types/script/graphql/script_service_proxy.graphql +8 -0
  143. data/lib/project_types/script/graphql/shop_script_delete.graphql +14 -0
  144. data/lib/project_types/script/graphql/shop_script_update_or_create.graphql +28 -0
  145. data/lib/project_types/script/layers/application/build_script.rb +43 -0
  146. data/lib/project_types/script/layers/application/create_script.rb +47 -0
  147. data/lib/project_types/script/layers/application/disable_script.rb +19 -0
  148. data/lib/project_types/script/layers/application/enable_script.rb +21 -0
  149. data/lib/project_types/script/layers/application/extension_points.rb +17 -0
  150. data/lib/project_types/script/layers/application/project_dependencies.rb +34 -0
  151. data/lib/project_types/script/layers/application/push_script.rb +30 -0
  152. data/lib/project_types/script/layers/domain/errors.rb +25 -0
  153. data/lib/project_types/script/layers/domain/extension_point.rb +29 -0
  154. data/lib/project_types/script/layers/domain/push_package.rb +29 -0
  155. data/lib/project_types/script/layers/domain/script.rb +18 -0
  156. data/lib/project_types/script/layers/infrastructure/assemblyscript_dependency_manager.rb +73 -0
  157. data/lib/project_types/script/layers/infrastructure/assemblyscript_tsconfig.rb +38 -0
  158. data/lib/project_types/script/layers/infrastructure/assemblyscript_wasm_builder.rb +39 -0
  159. data/lib/project_types/script/layers/infrastructure/dependency_manager.rb +36 -0
  160. data/lib/project_types/script/layers/infrastructure/errors.rb +38 -0
  161. data/lib/project_types/script/layers/infrastructure/extension_point_repository.rb +31 -0
  162. data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +47 -0
  163. data/lib/project_types/script/layers/infrastructure/script_builder.rb +34 -0
  164. data/lib/project_types/script/layers/infrastructure/script_repository.rb +89 -0
  165. data/lib/project_types/script/layers/infrastructure/script_service.rb +165 -0
  166. data/lib/project_types/script/layers/infrastructure/test_suite_repository.rb +59 -0
  167. data/lib/project_types/script/messages/messages.rb +204 -0
  168. data/lib/project_types/script/script_project.rb +37 -0
  169. data/lib/project_types/script/templates/ts/as-pect.config.js +21 -0
  170. data/lib/project_types/script/ui/error_handler.rb +136 -0
  171. data/lib/project_types/script/ui/strict_spinner.rb +22 -0
  172. data/lib/rubygems_plugin.rb +18 -0
  173. data/lib/shopify-cli/admin_api.rb +99 -0
  174. data/lib/shopify-cli/admin_api/populate_resource_command.rb +165 -0
  175. data/lib/shopify-cli/admin_api/schema.rb +32 -0
  176. data/lib/shopify-cli/api.rb +104 -0
  177. data/lib/shopify-cli/command.rb +67 -0
  178. data/lib/shopify-cli/commands.rb +28 -0
  179. data/lib/shopify-cli/commands/connect.rb +108 -0
  180. data/lib/shopify-cli/commands/create.rb +50 -0
  181. data/lib/shopify-cli/commands/help.rb +79 -0
  182. data/lib/shopify-cli/commands/logout.rb +23 -0
  183. data/lib/shopify-cli/commands/system.rb +135 -0
  184. data/lib/shopify-cli/commands/version.rb +15 -0
  185. data/lib/shopify-cli/context.rb +372 -0
  186. data/lib/shopify-cli/core.rb +9 -0
  187. data/lib/shopify-cli/core/entry_point.rb +40 -0
  188. data/lib/shopify-cli/core/executor.rb +21 -0
  189. data/lib/shopify-cli/core/help_resolver.rb +20 -0
  190. data/lib/shopify-cli/core/monorail.rb +118 -0
  191. data/lib/shopify-cli/db.rb +114 -0
  192. data/lib/shopify-cli/form.rb +40 -0
  193. data/lib/shopify-cli/git.rb +141 -0
  194. data/lib/shopify-cli/helpers.rb +5 -0
  195. data/lib/shopify-cli/helpers/haikunator.rb +92 -0
  196. data/lib/shopify-cli/heroku.rb +97 -0
  197. data/lib/shopify-cli/js_deps.rb +110 -0
  198. data/lib/shopify-cli/js_system.rb +98 -0
  199. data/lib/shopify-cli/messages/messages.rb +287 -0
  200. data/lib/shopify-cli/oauth.rb +192 -0
  201. data/lib/shopify-cli/oauth/servlet.rb +61 -0
  202. data/lib/shopify-cli/options.rb +40 -0
  203. data/lib/shopify-cli/packager.rb +116 -0
  204. data/lib/shopify-cli/partners_api.rb +114 -0
  205. data/lib/shopify-cli/partners_api/organizations.rb +32 -0
  206. data/lib/shopify-cli/process_supervision.rb +187 -0
  207. data/lib/shopify-cli/project.rb +191 -0
  208. data/lib/shopify-cli/project_type.rb +83 -0
  209. data/lib/shopify-cli/resources.rb +5 -0
  210. data/lib/shopify-cli/resources/env_file.rb +96 -0
  211. data/lib/shopify-cli/sub_command.rb +15 -0
  212. data/lib/shopify-cli/task.rb +10 -0
  213. data/lib/shopify-cli/tasks.rb +32 -0
  214. data/lib/shopify-cli/tasks/create_api_client.rb +29 -0
  215. data/lib/shopify-cli/tasks/ensure_dev_store.rb +41 -0
  216. data/lib/shopify-cli/tasks/ensure_env.rb +31 -0
  217. data/lib/shopify-cli/tasks/ensure_loopback_url.rb +20 -0
  218. data/lib/shopify-cli/tasks/update_dashboard_urls.rb +44 -0
  219. data/lib/shopify-cli/tunnel.rb +154 -0
  220. data/lib/shopify-cli/version.rb +3 -0
  221. data/lib/shopify_cli.rb +132 -0
  222. data/shopify-cli.gemspec +40 -0
  223. data/shopify.fish +12 -0
  224. data/shopify.sh +11 -0
  225. data/vendor/deps/cli-kit/REVISION +1 -0
  226. data/vendor/deps/cli-kit/lib/cli/kit.rb +60 -0
  227. data/vendor/deps/cli-kit/lib/cli/kit/autocall.rb +21 -0
  228. data/vendor/deps/cli-kit/lib/cli/kit/base_command.rb +49 -0
  229. data/vendor/deps/cli-kit/lib/cli/kit/command_registry.rb +94 -0
  230. data/vendor/deps/cli-kit/lib/cli/kit/config.rb +133 -0
  231. data/vendor/deps/cli-kit/lib/cli/kit/error_handler.rb +115 -0
  232. data/vendor/deps/cli-kit/lib/cli/kit/executor.rb +81 -0
  233. data/vendor/deps/cli-kit/lib/cli/kit/ini.rb +102 -0
  234. data/vendor/deps/cli-kit/lib/cli/kit/levenshtein.rb +82 -0
  235. data/vendor/deps/cli-kit/lib/cli/kit/logger.rb +76 -0
  236. data/vendor/deps/cli-kit/lib/cli/kit/resolver.rb +60 -0
  237. data/vendor/deps/cli-kit/lib/cli/kit/ruby_backports/enumerable.rb +6 -0
  238. data/vendor/deps/cli-kit/lib/cli/kit/support.rb +9 -0
  239. data/vendor/deps/cli-kit/lib/cli/kit/support/test_helper.rb +244 -0
  240. data/vendor/deps/cli-kit/lib/cli/kit/system.rb +207 -0
  241. data/vendor/deps/cli-kit/lib/cli/kit/util.rb +189 -0
  242. data/vendor/deps/cli-kit/lib/cli/kit/version.rb +5 -0
  243. data/vendor/deps/cli-ui/REVISION +1 -0
  244. data/vendor/deps/cli-ui/lib/cli/ui.rb +187 -0
  245. data/vendor/deps/cli-ui/lib/cli/ui/ansi.rb +153 -0
  246. data/vendor/deps/cli-ui/lib/cli/ui/box.rb +15 -0
  247. data/vendor/deps/cli-ui/lib/cli/ui/color.rb +79 -0
  248. data/vendor/deps/cli-ui/lib/cli/ui/formatter.rb +179 -0
  249. data/vendor/deps/cli-ui/lib/cli/ui/frame.rb +310 -0
  250. data/vendor/deps/cli-ui/lib/cli/ui/glyph.rb +78 -0
  251. data/vendor/deps/cli-ui/lib/cli/ui/progress.rb +88 -0
  252. data/vendor/deps/cli-ui/lib/cli/ui/prompt.rb +248 -0
  253. data/vendor/deps/cli-ui/lib/cli/ui/prompt/interactive_options.rb +472 -0
  254. data/vendor/deps/cli-ui/lib/cli/ui/prompt/options_handler.rb +24 -0
  255. data/vendor/deps/cli-ui/lib/cli/ui/spinner.rb +48 -0
  256. data/vendor/deps/cli-ui/lib/cli/ui/spinner/async.rb +40 -0
  257. data/vendor/deps/cli-ui/lib/cli/ui/spinner/spin_group.rb +241 -0
  258. data/vendor/deps/cli-ui/lib/cli/ui/stdout_router.rb +227 -0
  259. data/vendor/deps/cli-ui/lib/cli/ui/terminal.rb +36 -0
  260. data/vendor/deps/cli-ui/lib/cli/ui/truncater.rb +102 -0
  261. data/vendor/deps/cli-ui/lib/cli/ui/version.rb +5 -0
  262. data/vendor/deps/smart_properties/REVISION +1 -0
  263. data/vendor/deps/smart_properties/lib/smart_properties.rb +174 -0
  264. data/vendor/deps/smart_properties/lib/smart_properties/errors.rb +114 -0
  265. data/vendor/deps/smart_properties/lib/smart_properties/property.rb +162 -0
  266. data/vendor/deps/smart_properties/lib/smart_properties/property_collection.rb +83 -0
  267. data/vendor/deps/smart_properties/lib/smart_properties/validations.rb +8 -0
  268. data/vendor/deps/smart_properties/lib/smart_properties/validations/ancestor.rb +27 -0
  269. data/vendor/deps/smart_properties/lib/smart_properties/version.rb +3 -0
  270. data/vendor/lib/semantic/LICENSE +20 -0
  271. data/vendor/lib/semantic/semantic.rb +4 -0
  272. data/vendor/lib/semantic/version.rb +180 -0
  273. metadata +374 -0
@@ -0,0 +1,5 @@
1
+ module ShopifyCli
2
+ module Resources
3
+ autoload :EnvFile, 'shopify-cli/resources/env_file'
4
+ end
5
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyCli
4
+ module Resources
5
+ class EnvFile
6
+ include SmartProperties
7
+ FILENAME = '.env'
8
+ KEY_MAP = {
9
+ 'SHOPIFY_API_KEY' => :api_key,
10
+ 'SHOPIFY_API_SECRET' => :secret,
11
+ 'SHOP' => :shop,
12
+ 'SCOPES' => :scopes,
13
+ 'HOST' => :host,
14
+ }
15
+
16
+ class << self
17
+ def read(_directory = Dir.pwd)
18
+ input = parse_external_env
19
+ new(input)
20
+ end
21
+
22
+ def parse(directory)
23
+ File.read(File.join(directory, FILENAME))
24
+ .gsub("\r\n", "\n").split("\n").each_with_object({}) do |line, output|
25
+ match = /\A([A-Za-z_0-9]+)=(.*)\z/.match(line)
26
+ if match
27
+ key = match[1]
28
+ output[key] = case match[2]
29
+ # Remove single quotes
30
+ when /\A'(.*)'\z/ then match[2]
31
+ # Remove double quotes and unescape string preserving newline characters
32
+ when /\A"(.*)"\z/ then match[2].gsub('\n', "\n").gsub(/\\(.)/, '\1')
33
+ else match[2]
34
+ end
35
+ end
36
+ output
37
+ end
38
+ end
39
+
40
+ def parse_external_env(directory = Dir.pwd)
41
+ env_details = {}
42
+ extra = {}
43
+ parse(directory).each do |key, value|
44
+ if KEY_MAP[key]
45
+ env_details[KEY_MAP[key]] = value
46
+ else
47
+ extra[key] = value
48
+ end
49
+ end
50
+ env_details[:extra] = extra
51
+ env_details
52
+ end
53
+ end
54
+
55
+ property :api_key, required: true
56
+ property :secret, required: true
57
+ property :shop
58
+ property :scopes
59
+ property :host
60
+ property :extra, default: {}
61
+
62
+ def to_h
63
+ out = {}
64
+ KEY_MAP.each do |key, value|
65
+ out[key] = send(value).to_s if send(value)
66
+ end
67
+ extra.each do |key, value|
68
+ out[key] = value.to_s
69
+ end
70
+ out
71
+ end
72
+
73
+ def write(ctx)
74
+ spin_group = CLI::UI::SpinGroup.new
75
+ spin_group.add(ctx.message('core.env_file.saving_header', FILENAME)) do |spinner|
76
+ output = []
77
+ KEY_MAP.each do |key, value|
78
+ output << "#{key}=#{send(value)}" if send(value)
79
+ end
80
+ extra.each do |key, value|
81
+ output << "#{key}=#{value}"
82
+ end
83
+ ctx.print_task(ctx.message('core.env_file.saving', FILENAME))
84
+ ctx.write(FILENAME, output.join("\n") + "\n")
85
+ spinner.update_title(ctx.message('core.env_file.saved', FILENAME))
86
+ end
87
+ spin_group.wait
88
+ end
89
+
90
+ def update(ctx, field, value)
91
+ self[field] = value
92
+ write(ctx)
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ require 'shopify_cli'
3
+
4
+ module ShopifyCli
5
+ class SubCommand < Command
6
+ class << self
7
+ def call(args, command_name, parent_command)
8
+ cmd = new(@ctx)
9
+ args = cmd.options.parse(@_options, args[1..-1] || [])
10
+ return call_help(parent_command, command_name) if cmd.options.help
11
+ cmd.call(args, command_name)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ require 'shopify_cli'
2
+
3
+ module ShopifyCli
4
+ class Task
5
+ def self.call(*args)
6
+ task = new
7
+ task.call(*args)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,32 @@
1
+ require 'shopify_cli'
2
+
3
+ module ShopifyCli
4
+ module Tasks
5
+ class TaskRegistry
6
+ def initialize
7
+ @tasks = {}
8
+ end
9
+
10
+ def add(const, name)
11
+ @tasks[name] = const
12
+ end
13
+
14
+ def [](name)
15
+ @tasks[name]
16
+ end
17
+ end
18
+
19
+ Registry = TaskRegistry.new
20
+
21
+ def self.register(task, name, path)
22
+ autoload(task, path)
23
+ Registry.add(const_get(task), name)
24
+ end
25
+
26
+ register :CreateApiClient, :create_api_client, 'shopify-cli/tasks/create_api_client'
27
+ register :EnsureEnv, :ensure_env, 'shopify-cli/tasks/ensure_env'
28
+ register :EnsureLoopbackURL, :ensure_loopback_url, 'shopify-cli/tasks/ensure_loopback_url'
29
+ register :EnsureDevStore, :ensure_dev_store, 'shopify-cli/tasks/ensure_dev_store'
30
+ register :UpdateDashboardURLS, :update_dashboard_urls, 'shopify-cli/tasks/update_dashboard_urls'
31
+ end
32
+ end
@@ -0,0 +1,29 @@
1
+ require 'shopify_cli'
2
+
3
+ module ShopifyCli
4
+ module Tasks
5
+ class CreateApiClient < ShopifyCli::Task
6
+ VALID_APP_TYPES = %w(public custom)
7
+ DEFAULT_APP_URL = 'https://shopify.github.io/shopify-app-cli/help/start-app/'
8
+
9
+ def call(ctx, org_id:, title:, type:)
10
+ resp = ShopifyCli::PartnersAPI.query(
11
+ ctx,
12
+ 'create_app',
13
+ org: org_id.to_i,
14
+ title: title,
15
+ type: type,
16
+ app_url: DEFAULT_APP_URL,
17
+ redir: [OAuth::REDIRECT_HOST]
18
+ )
19
+
20
+ user_errors = resp["data"]["appCreate"]["userErrors"]
21
+ if !user_errors.nil? && user_errors.any?
22
+ ctx.abort(user_errors.map { |err| "#{err['field']} #{err['message']}" }.join(", "))
23
+ end
24
+
25
+ resp["data"]["appCreate"]["app"]
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,41 @@
1
+ require 'shopify_cli'
2
+
3
+ module ShopifyCli
4
+ module Tasks
5
+ class EnsureDevStore < ShopifyCli::Task
6
+ def call(ctx)
7
+ @ctx = ctx
8
+ return ctx.puts(ctx.message(
9
+ 'core.tasks.ensure_dev_store.could_not_verify_store', project.env.shop
10
+ )) if shop.nil?
11
+ return if shop['transferDisabled'] == true
12
+ return unless CLI::UI::Prompt.confirm(
13
+ ctx.message('core.tasks.ensure_dev_store.convert_to_dev_store', project.env.shop)
14
+ )
15
+ ShopifyCli::PartnersAPI.query(ctx, 'convert_dev_to_test_store', input: {
16
+ organizationID: shop['orgID'].to_i,
17
+ shopId: shop['shopId'],
18
+ })
19
+ ctx.puts(ctx.message('core.tasks.ensure_dev_store.transfer_disabled', project.env.shop))
20
+ end
21
+
22
+ private
23
+
24
+ def project
25
+ @project ||= ShopifyCli::Project.current
26
+ end
27
+
28
+ def shop
29
+ @shop ||= begin
30
+ current_domain = project.env.shop
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
35
+ end
36
+ end.compact.first
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,31 @@
1
+ require 'shopify_cli'
2
+
3
+ module ShopifyCli
4
+ module Tasks
5
+ class EnsureEnv < ShopifyCli::Task
6
+ def call(ctx)
7
+ @ctx = ctx
8
+ Resources::EnvFile.read(ctx.root)
9
+ rescue Errno::ENOENT
10
+ ask
11
+ end
12
+
13
+ def ask
14
+ api_key = CLI::UI.ask(@ctx.message('core.tasks.ensure_env.api_key_question'))
15
+ api_secret = CLI::UI.ask(@ctx.message('core.tasks.ensure_env.api_secret_key_question'))
16
+ shop = CLI::UI.ask(@ctx.message('core.tasks.ensure_env.development_store_question'))
17
+
18
+ shop.gsub!(/https?\:\/\//, '')
19
+
20
+ env = Resources::EnvFile.new(
21
+ api_key: api_key,
22
+ secret: api_secret,
23
+ shop: shop,
24
+ host: ShopifyCli::Tunnel.start(@ctx),
25
+ scopes: 'write_products,write_customers,write_draft_orders',
26
+ ).write(@ctx)
27
+ env
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,20 @@
1
+ module ShopifyCli
2
+ module Tasks
3
+ class EnsureLoopbackURL < ShopifyCli::Task
4
+ def call(ctx)
5
+ @ctx = ctx
6
+ api_key = Project.current.env.api_key
7
+ result = ShopifyCli::PartnersAPI.query(ctx, 'get_app_urls', apiKey: api_key)
8
+ loopback = OAuth::REDIRECT_HOST
9
+ app = result['data']['app']
10
+ urls = app['redirectUrlWhitelist']
11
+ if urls.grep(/#{loopback}/).empty?
12
+ with_loopback = urls.push(loopback)
13
+ ShopifyCli::PartnersAPI.query(@ctx, 'update_dashboard_urls', input: {
14
+ redirectUrlWhitelist: with_loopback, apiKey: api_key
15
+ })
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,44 @@
1
+ module ShopifyCli
2
+ module Tasks
3
+ class UpdateDashboardURLS < ShopifyCli::Task
4
+ NGROK_REGEX = /https:\/\/([a-z0-9\-]+\.ngrok\.io)(.*)/
5
+
6
+ def call(ctx, url:, callback_url:)
7
+ @ctx = ctx
8
+ project = ShopifyCli::Project.current
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'],
17
+ redirectUrlWhitelist: constructed_urls, apiKey: api_key
18
+ })
19
+ @ctx.puts(@ctx.message('core.tasks.update_dashboard_urls.updated'))
20
+ rescue
21
+ @ctx.puts(@ctx.message('core.tasks.update_dashboard_urls.update_error', ShopifyCli::TOOL_NAME))
22
+ raise
23
+ end
24
+
25
+ def check_application_url(application_url, new_url)
26
+ return false if application_url.match(new_url)
27
+ CLI::UI::Prompt.confirm(@ctx.message('core.tasks.update_dashboard_urls.update_prompt'))
28
+ end
29
+
30
+ def construct_redirect_urls(urls, new_url, callback_url)
31
+ urls.map do |url|
32
+ if (match = url.match(NGROK_REGEX))
33
+ "#{new_url}#{match[2]}"
34
+ else
35
+ url
36
+ end
37
+ end
38
+ if urls.grep(/#{new_url}#{callback_url}/).empty?
39
+ urls.push("#{new_url}#{callback_url}")
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,154 @@
1
+ require 'json'
2
+ require 'fileutils'
3
+ require 'shopify_cli'
4
+ require 'forwardable'
5
+
6
+ module ShopifyCli
7
+ ##
8
+ # Wraps around ngrok functionality to allow you to spawn a ngrok proccess in the
9
+ # background and stop the process when you need to. It also allows control over
10
+ # the ngrok process between application runs.
11
+ class Tunnel
12
+ extend SingleForwardable
13
+
14
+ def_delegators :new, :start, :stop, :auth
15
+
16
+ class FetchUrlError < RuntimeError; end
17
+ class NgrokError < RuntimeError; end
18
+
19
+ PORT = 8081 # port that ngrok will bind to
20
+ # mapping for supported operating systems for where to download ngrok from.
21
+ DOWNLOAD_URLS = {
22
+ mac: 'https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-darwin-amd64.zip',
23
+ linux: 'https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip',
24
+ }
25
+
26
+ ##
27
+ # will find and stop a running tunnel process. It will also output if the
28
+ # operation was successful or not
29
+ #
30
+ # #### Paramters
31
+ #
32
+ # * `ctx` - running context from your command
33
+ #
34
+ def stop(ctx)
35
+ if ShopifyCli::ProcessSupervision.running?(:ngrok)
36
+ if ShopifyCli::ProcessSupervision.stop(:ngrok)
37
+ ctx.puts(ctx.message('core.tunnel.stopped'))
38
+ else
39
+ ctx.abort(ctx.message('core.tunnel.error.stop'))
40
+ end
41
+ else
42
+ ctx.puts(ctx.message('core.tunnel.not_running'))
43
+ end
44
+ end
45
+
46
+ ##
47
+ # start will start a running ngrok process running in the background. It will
48
+ # also output the success of this operation
49
+ #
50
+ # #### Paramters
51
+ #
52
+ # * `ctx` - running context from your command
53
+ # * `port` - port to use to open the ngrok tunnel
54
+ #
55
+ # #### Returns
56
+ #
57
+ # * `url` - the url that the tunnel is now bound to and available to the public
58
+ #
59
+ def start(ctx, port: PORT)
60
+ install(ctx)
61
+ process = ShopifyCli::ProcessSupervision.start(:ngrok, ngrok_command(port))
62
+ log = fetch_url(ctx, process.log_path)
63
+ if log.account
64
+ ctx.puts(ctx.message('core.tunnel.start_with_account', log.url, log.account))
65
+ else
66
+ ctx.puts(ctx.message('core.tunnel.start', log.url))
67
+ end
68
+ log.url
69
+ end
70
+
71
+ ##
72
+ # will add the users authentication token to our version of ngrok to unlock the
73
+ # extended ngrok features
74
+ #
75
+ # #### Paramters
76
+ #
77
+ # * `ctx` - running context from your command
78
+ # * `token` - authentication token provided by ngrok for extended features
79
+ #
80
+ def auth(ctx, token)
81
+ install(ctx)
82
+ ctx.system(File.join(ShopifyCli::CACHE_DIR, 'ngrok'), 'authtoken', token)
83
+ end
84
+
85
+ private
86
+
87
+ def install(ctx)
88
+ return if File.exist?(File.join(ShopifyCli::CACHE_DIR, 'ngrok'))
89
+ spinner = CLI::UI::SpinGroup.new
90
+ spinner.add('Installing ngrok...') do
91
+ zip_dest = File.join(ShopifyCli::CACHE_DIR, 'ngrok.zip')
92
+ unless File.exist?(zip_dest)
93
+ ctx.system('curl', '-o', zip_dest, DOWNLOAD_URLS[ctx.os], chdir: ShopifyCli::CACHE_DIR)
94
+ end
95
+ ctx.system('unzip', '-u', zip_dest, chdir: ShopifyCli::CACHE_DIR)
96
+ ctx.rm(zip_dest)
97
+ end
98
+ spinner.wait
99
+ end
100
+
101
+ def fetch_url(ctx, log_path)
102
+ LogParser.new(log_path)
103
+ rescue RuntimeError => e
104
+ stop(ctx)
105
+ raise e.class, e.message
106
+ end
107
+
108
+ def ngrok_command(port)
109
+ "exec #{File.join(ShopifyCli::CACHE_DIR, 'ngrok')} http -log=stdout -log-level=debug #{port}"
110
+ end
111
+
112
+ class LogParser # :nodoc:
113
+ TIMEOUT = 10
114
+
115
+ attr_reader :url, :account
116
+
117
+ def initialize(log_path)
118
+ @log_path = log_path
119
+ counter = 0
120
+ while counter < TIMEOUT
121
+ parse
122
+ return if url
123
+ counter += 1
124
+ sleep(1)
125
+ end
126
+
127
+ raise FetchUrlError, Context.message('core.tunnel.error.url_fetch_failure') unless url
128
+ end
129
+
130
+ def parse
131
+ @log = File.read(@log_path)
132
+ unless error.empty?
133
+ raise NgrokError, error.first
134
+ end
135
+ parse_account
136
+ parse_url
137
+ end
138
+
139
+ def parse_url
140
+ @url, _ = @log.match(/msg="started tunnel".*url=(https:\/\/.+)/)&.captures
141
+ end
142
+
143
+ def parse_account
144
+ @account, _ = @log.match(/AccountName:([\w\s]+) SessionDuration/)&.captures
145
+ end
146
+
147
+ def error
148
+ @log.scan(/msg="command failed" err="([^"]+)"/).flatten
149
+ end
150
+ end
151
+
152
+ private_constant :LogParser
153
+ end
154
+ end