shopify-cli 1.12.0 → 2.0.1

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 (208) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +1 -1
  3. data/.github/CONTRIBUTING.md +7 -7
  4. data/.github/DESIGN.md +3 -3
  5. data/.github/PULL_REQUEST_TEMPLATE.md +1 -1
  6. data/.github/workflows/build.yml +1 -1
  7. data/.gitignore +3 -0
  8. data/.rubocop.yml +3 -1
  9. data/.ruby-version +1 -1
  10. data/CHANGELOG.md +52 -21
  11. data/Gemfile +4 -0
  12. data/Gemfile.lock +32 -0
  13. data/LICENSE +4 -1
  14. data/README.md +92 -26
  15. data/RELEASING.md +31 -7
  16. data/Rakefile +2 -2
  17. data/SECURITY.md +1 -1
  18. data/bin/load_shopify.rb +1 -1
  19. data/bin/shopify +3 -3
  20. data/dev.yml +1 -1
  21. data/docs/app/node/index.md +1 -1
  22. data/docs/app/rails/index.md +1 -1
  23. data/docs/core/index.md +1 -1
  24. data/docs/getting-started/index.md +1 -1
  25. data/docs/getting-started/install/index.md +1 -1
  26. data/docs/getting-started/migrate/index.md +1 -1
  27. data/docs/getting-started/uninstall/index.md +1 -1
  28. data/docs/getting-started/upgrade/index.md +1 -1
  29. data/docs/help/start-app/index.md +1 -1
  30. data/docs/index.md +1 -1
  31. data/ext/shopify-cli/extconf.rb +17 -5
  32. data/install.sh +1 -1
  33. data/lib/docgen/index_template.md.erb +2 -2
  34. data/lib/graphql/all_orgs_with_extensions.graphql +37 -0
  35. data/lib/graphql/find_organization.graphql +2 -1
  36. data/lib/project_types/extension/cli.rb +18 -15
  37. data/lib/project_types/extension/commands/build.rb +4 -5
  38. data/lib/project_types/extension/commands/connect.rb +35 -0
  39. data/lib/project_types/extension/commands/create.rb +12 -16
  40. data/lib/project_types/extension/commands/extension_command.rb +2 -2
  41. data/lib/project_types/extension/commands/info.rb +86 -0
  42. data/lib/project_types/extension/commands/push.rb +8 -7
  43. data/lib/project_types/extension/commands/register.rb +4 -5
  44. data/lib/project_types/extension/commands/serve.rb +5 -8
  45. data/lib/project_types/extension/commands/tunnel.rb +3 -1
  46. data/lib/project_types/extension/errors.rb +9 -0
  47. data/lib/project_types/extension/extension_project.rb +5 -0
  48. data/lib/project_types/extension/features/argo.rb +6 -6
  49. data/lib/project_types/extension/features/argo_runtime.rb +22 -59
  50. data/lib/project_types/extension/features/argo_serve.rb +25 -21
  51. data/lib/project_types/extension/forms/connect.rb +42 -0
  52. data/lib/project_types/extension/forms/questions/ask_name.rb +14 -6
  53. data/lib/project_types/extension/forms/questions/ask_registration.rb +51 -0
  54. data/lib/project_types/extension/messages/messages.rb +75 -11
  55. data/lib/project_types/extension/models/specification.rb +1 -0
  56. data/lib/project_types/extension/models/specification_handlers/{checkout_argo_extension.rb → checkout_ui_extension.rb} +3 -1
  57. data/lib/project_types/extension/models/specification_handlers/default.rb +13 -13
  58. data/lib/project_types/extension/models/specification_handlers/theme_app_extension.rb +89 -0
  59. data/lib/project_types/extension/models/specifications.rb +1 -0
  60. data/lib/project_types/extension/tasks/configure_features.rb +6 -7
  61. data/lib/project_types/extension/tasks/configure_options.rb +20 -0
  62. data/lib/project_types/extension/tasks/get_extensions.rb +32 -0
  63. data/lib/project_types/node/cli.rb +9 -21
  64. data/lib/project_types/node/commands/connect.rb +8 -2
  65. data/lib/project_types/node/commands/create.rb +9 -5
  66. data/lib/project_types/node/commands/deploy.rb +15 -5
  67. data/lib/project_types/node/commands/deploy/heroku.rb +29 -29
  68. data/lib/project_types/node/commands/generate.rb +4 -2
  69. data/lib/project_types/node/commands/open.rb +4 -2
  70. data/lib/project_types/node/commands/serve.rb +3 -2
  71. data/lib/project_types/node/commands/tunnel.rb +4 -2
  72. data/lib/project_types/node/messages/messages.rb +46 -89
  73. data/lib/project_types/rails/cli.rb +9 -21
  74. data/lib/project_types/rails/commands/connect.rb +8 -2
  75. data/lib/project_types/rails/commands/create.rb +10 -6
  76. data/lib/project_types/rails/commands/deploy.rb +15 -5
  77. data/lib/project_types/rails/commands/deploy/heroku.rb +84 -82
  78. data/lib/project_types/rails/commands/generate.rb +15 -5
  79. data/lib/project_types/rails/commands/generate/webhook.rb +28 -26
  80. data/lib/project_types/rails/commands/open.rb +4 -2
  81. data/lib/project_types/rails/commands/serve.rb +3 -2
  82. data/lib/project_types/rails/commands/tunnel.rb +4 -2
  83. data/lib/project_types/rails/messages/messages.rb +54 -101
  84. data/lib/project_types/script/cli.rb +18 -20
  85. data/lib/project_types/script/commands/create.rb +3 -1
  86. data/lib/project_types/script/commands/push.rb +12 -5
  87. data/lib/project_types/script/config/extension_points.yml +0 -3
  88. data/lib/project_types/script/graphql/app_script_update_or_create.graphql +9 -3
  89. data/lib/project_types/script/layers/application/create_script.rb +6 -5
  90. data/lib/project_types/script/layers/application/push_script.rb +2 -1
  91. data/lib/project_types/script/layers/domain/errors.rb +6 -11
  92. data/lib/project_types/script/layers/domain/push_package.rb +4 -8
  93. data/lib/project_types/script/layers/domain/script_json.rb +32 -0
  94. data/lib/project_types/script/layers/domain/script_project.rb +1 -1
  95. data/lib/project_types/script/layers/infrastructure/errors.rb +14 -18
  96. data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_project_creator.rb +105 -0
  97. data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_task_runner.rb +103 -0
  98. data/lib/project_types/script/layers/infrastructure/languages/project_creator.rb +26 -0
  99. data/lib/project_types/script/layers/infrastructure/languages/rust_project_creator.rb +73 -0
  100. data/lib/project_types/script/layers/infrastructure/languages/rust_task_runner.rb +60 -0
  101. data/lib/project_types/script/layers/infrastructure/languages/task_runner.rb +21 -0
  102. data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +2 -4
  103. data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +45 -34
  104. data/lib/project_types/script/layers/infrastructure/script_service.rb +37 -16
  105. data/lib/project_types/script/messages/messages.rb +66 -55
  106. data/lib/project_types/script/tasks/ensure_env.rb +22 -1
  107. data/lib/project_types/script/ui/error_handler.rb +32 -32
  108. data/lib/project_types/theme/cli.rb +16 -27
  109. data/lib/project_types/theme/commands/check.rb +33 -0
  110. data/lib/project_types/theme/commands/delete.rb +64 -0
  111. data/lib/project_types/theme/commands/init.rb +42 -0
  112. data/lib/project_types/theme/commands/language_server.rb +16 -0
  113. data/lib/project_types/theme/commands/package.rb +55 -0
  114. data/lib/project_types/theme/commands/publish.rb +43 -0
  115. data/lib/project_types/theme/commands/pull.rb +51 -0
  116. data/lib/project_types/theme/commands/push.rb +58 -32
  117. data/lib/project_types/theme/commands/serve.rb +8 -16
  118. data/lib/project_types/theme/forms/confirm_store.rb +15 -0
  119. data/lib/project_types/theme/forms/select.rb +59 -0
  120. data/lib/project_types/theme/messages/messages.rb +117 -102
  121. data/lib/project_types/theme/ui/sync_progress_bar.rb +20 -0
  122. data/lib/shopify-cli/admin_api.rb +53 -35
  123. data/lib/shopify-cli/admin_api/populate_resource_command.rb +6 -14
  124. data/lib/shopify-cli/admin_api/schema.rb +1 -10
  125. data/lib/shopify-cli/api.rb +29 -14
  126. data/lib/shopify-cli/command.rb +15 -3
  127. data/lib/shopify-cli/commands.rb +7 -2
  128. data/lib/shopify-cli/commands/help.rb +2 -29
  129. data/lib/shopify-cli/commands/login.rb +95 -0
  130. data/lib/shopify-cli/commands/logout.rb +24 -8
  131. data/lib/shopify-cli/commands/populate.rb +23 -0
  132. data/lib/{project_types/node → shopify-cli}/commands/populate/customer.rb +2 -8
  133. data/lib/{project_types/node → shopify-cli}/commands/populate/draft_order.rb +2 -2
  134. data/lib/{project_types/node → shopify-cli}/commands/populate/product.rb +2 -8
  135. data/lib/shopify-cli/commands/store.rb +15 -0
  136. data/lib/shopify-cli/commands/switch.rb +39 -0
  137. data/lib/shopify-cli/commands/system.rb +12 -0
  138. data/lib/shopify-cli/commands/whoami.rb +28 -0
  139. data/lib/shopify-cli/connect.rb +32 -0
  140. data/lib/shopify-cli/context.rb +65 -4
  141. data/lib/shopify-cli/core/entry_point.rb +3 -22
  142. data/lib/shopify-cli/db.rb +4 -4
  143. data/lib/shopify-cli/http_request.rb +16 -0
  144. data/lib/shopify-cli/identity_auth.rb +282 -0
  145. data/lib/shopify-cli/{oauth → identity_auth}/servlet.rb +11 -12
  146. data/lib/shopify-cli/messages/messages.rb +133 -39
  147. data/lib/shopify-cli/partners_api.rb +21 -41
  148. data/lib/shopify-cli/partners_api/organizations.rb +8 -0
  149. data/lib/shopify-cli/project_commands.rb +16 -0
  150. data/lib/shopify-cli/project_type.rb +0 -31
  151. data/lib/shopify-cli/resources/env_file.rb +1 -1
  152. data/lib/shopify-cli/shopifolk.rb +8 -11
  153. data/lib/shopify-cli/sub_command.rb +1 -0
  154. data/lib/shopify-cli/tasks.rb +3 -0
  155. data/lib/shopify-cli/tasks/confirm_store.rb +18 -0
  156. data/lib/shopify-cli/tasks/create_api_client.rb +2 -2
  157. data/lib/shopify-cli/tasks/ensure_authenticated.rb +13 -0
  158. data/lib/shopify-cli/tasks/ensure_loopback_url.rb +1 -1
  159. data/lib/shopify-cli/tasks/ensure_project_type.rb +12 -0
  160. data/lib/shopify-cli/tasks/select_org_and_shop.rb +0 -3
  161. data/lib/shopify-cli/theme/dev_server.rb +98 -0
  162. data/lib/shopify-cli/theme/dev_server/certificate_manager.rb +79 -0
  163. data/lib/shopify-cli/theme/dev_server/header_hash.rb +94 -0
  164. data/lib/shopify-cli/theme/dev_server/hot-reload.js +93 -0
  165. data/lib/shopify-cli/theme/dev_server/hot_reload.rb +76 -0
  166. data/lib/shopify-cli/theme/dev_server/local_assets.rb +87 -0
  167. data/lib/shopify-cli/theme/dev_server/proxy.rb +205 -0
  168. data/lib/shopify-cli/theme/dev_server/sse.rb +75 -0
  169. data/lib/shopify-cli/theme/dev_server/watcher.rb +59 -0
  170. data/lib/shopify-cli/theme/dev_server/web_server.rb +140 -0
  171. data/lib/shopify-cli/theme/development_theme.rb +69 -0
  172. data/lib/shopify-cli/theme/file.rb +112 -0
  173. data/lib/shopify-cli/theme/ignore_filter.rb +109 -0
  174. data/lib/shopify-cli/theme/mime_type.rb +34 -0
  175. data/lib/shopify-cli/theme/syncer.rb +328 -0
  176. data/lib/shopify-cli/theme/theme.rb +204 -0
  177. data/lib/shopify-cli/version.rb +1 -1
  178. data/lib/shopify_cli.rb +18 -11
  179. data/shopify-cli.gemspec +12 -5
  180. data/shopify.fish +1 -1
  181. data/shopify.sh +1 -1
  182. metadata +96 -41
  183. data/.github/workflows/release.yml +0 -61
  184. data/lib/project_types/extension/features/argo_serve_options.rb +0 -41
  185. data/lib/project_types/node/commands/populate.rb +0 -23
  186. data/lib/project_types/rails/commands/populate.rb +0 -23
  187. data/lib/project_types/rails/commands/populate/customer.rb +0 -31
  188. data/lib/project_types/rails/commands/populate/draft_order.rb +0 -28
  189. data/lib/project_types/rails/commands/populate/product.rb +0 -30
  190. data/lib/project_types/script/layers/domain/config_ui.rb +0 -16
  191. data/lib/project_types/script/layers/infrastructure/assemblyscript_project_creator.rb +0 -95
  192. data/lib/project_types/script/layers/infrastructure/assemblyscript_task_runner.rb +0 -101
  193. data/lib/project_types/script/layers/infrastructure/project_creator.rb +0 -24
  194. data/lib/project_types/script/layers/infrastructure/rust_project_creator.rb +0 -71
  195. data/lib/project_types/script/layers/infrastructure/rust_task_runner.rb +0 -58
  196. data/lib/project_types/script/layers/infrastructure/task_runner.rb +0 -19
  197. data/lib/project_types/theme/commands/connect.rb +0 -54
  198. data/lib/project_types/theme/commands/create.rb +0 -48
  199. data/lib/project_types/theme/commands/deploy.rb +0 -38
  200. data/lib/project_types/theme/commands/generate.rb +0 -20
  201. data/lib/project_types/theme/commands/generate/env.rb +0 -79
  202. data/lib/project_types/theme/forms/connect.rb +0 -34
  203. data/lib/project_types/theme/forms/create.rb +0 -22
  204. data/lib/project_types/theme/tasks/ensure_themekit_installed.rb +0 -78
  205. data/lib/project_types/theme/themekit.rb +0 -113
  206. data/lib/shopify-cli/commands/connect.rb +0 -64
  207. data/lib/shopify-cli/commands/create.rb +0 -50
  208. data/lib/shopify-cli/oauth.rb +0 -198
@@ -1,22 +0,0 @@
1
- module Theme
2
- module Forms
3
- class Create < ShopifyCli::Form
4
- attr_accessor :name
5
- flag_arguments :title, :password, :store, :env
6
-
7
- def ask
8
- self.store ||= CLI::UI::Prompt.ask(ctx.message("theme.forms.ask_store"), allow_empty: false)
9
- ctx.puts(ctx.message("theme.forms.create.private_app", store))
10
- self.password ||= CLI::UI::Prompt.ask(ctx.message("theme.forms.ask_password"), allow_empty: false)
11
- self.title ||= CLI::UI::Prompt.ask(ctx.message("theme.forms.create.ask_title"), allow_empty: false)
12
- self.name = self.title.downcase.split(" ").join("_")
13
-
14
- errors = []
15
- errors << "store" if store.strip.empty?
16
- errors << "password" if password.strip.empty?
17
- errors << "title" if title.strip.empty?
18
- ctx.abort(ctx.message("theme.forms.errors", errors.join(", ").capitalize)) unless errors.empty?
19
- end
20
- end
21
- end
22
- end
@@ -1,78 +0,0 @@
1
- module Theme
2
- module Tasks
3
- class EnsureThemekitInstalled < ShopifyCli::Task
4
- URL = "https://shopify-themekit.s3.amazonaws.com/releases/latest.json"
5
- OSMAP = {
6
- mac: "darwin-amd64",
7
- linux: "linux-amd64",
8
- windows: "windows-amd64",
9
- }
10
- VERSION_CHECK_INTERVAL = 604800
11
- VERSION_CHECK_SECTION = "themekit_version_check"
12
- LAST_CHECKED_AT_FIELD = "last_checked_at"
13
-
14
- def call(ctx)
15
- _out, stat = ctx.capture2e(Themekit::THEMEKIT)
16
- unless stat.success?
17
- CLI::UI::Frame.open(ctx.message("theme.tasks.ensure_themekit_installed.installing_themekit")) do
18
- install_themekit(ctx)
19
- end
20
- end
21
-
22
- now = Time.now.to_i
23
- if ShopifyCli::Feature.enabled?(:themekit_auto_update) && (time_of_last_check + VERSION_CHECK_INTERVAL) < now
24
- CLI::UI::Frame.open(ctx.message("theme.tasks.ensure_themekit_installed.updating_themekit")) do
25
- unless Themekit.update(ctx)
26
- ctx.abort(ctx.message("theme.tasks.ensure_themekit_installed.errors.update_fail"))
27
- end
28
- update_time_of_last_check(now)
29
- end
30
- end
31
- end
32
-
33
- private
34
-
35
- def install_themekit(ctx)
36
- require "json"
37
- require "fileutils"
38
- require "digest"
39
- require "open-uri"
40
-
41
- begin
42
- begin
43
- releases = JSON.parse(Net::HTTP.get(URI(URL)))
44
- release = releases["platforms"].find { |r| r["name"] == OSMAP[ctx.os] }
45
- rescue
46
- ctx.abort(ctx.message("theme.tasks.ensure_themekit_installed.errors.releases_fail"))
47
- end
48
-
49
- ctx.puts(ctx.message("theme.tasks.ensure_themekit_installed.downloading", releases["version"]))
50
- _out, stat = ctx.capture2e("curl", "-o", Themekit::THEMEKIT, release["url"])
51
- ctx.abort(ctx.message("theme.tasks.ensure_themekit_installed.errors.write_fail")) unless stat.success?
52
-
53
- ctx.puts(ctx.message("theme.tasks.ensure_themekit_installed.verifying"))
54
- if Digest::MD5.file(Themekit::THEMEKIT) == release["digest"]
55
- FileUtils.chmod("+x", Themekit::THEMEKIT)
56
- ctx.puts(ctx.message("theme.tasks.ensure_themekit_installed.successful"))
57
-
58
- auto = CLI::UI.confirm(ctx.message("theme.tasks.ensure_themekit_installed.auto_update"))
59
- ShopifyCli::Feature.set(:themekit_auto_update, auto)
60
- else
61
- ctx.abort(ctx.message("theme.tasks.ensure_themekit_installed.errors.digest_fail"))
62
- end
63
- rescue StandardError, ShopifyCli::Abort => e
64
- FileUtils.rm(Themekit::THEMEKIT) if File.exist?(Themekit::THEMEKIT)
65
- raise e
66
- end
67
- end
68
-
69
- def time_of_last_check
70
- (val = ShopifyCli::Config.get(VERSION_CHECK_SECTION, LAST_CHECKED_AT_FIELD)) ? val.to_i : 0
71
- end
72
-
73
- def update_time_of_last_check(time)
74
- ShopifyCli::Config.set(VERSION_CHECK_SECTION, LAST_CHECKED_AT_FIELD, time)
75
- end
76
- end
77
- end
78
- end
@@ -1,113 +0,0 @@
1
- module Theme
2
- class Themekit
3
- THEMEKIT = File.join(ShopifyCli.cache_dir, "themekit")
4
-
5
- class << self
6
- def add_flags(flags)
7
- flags.map do |key, value|
8
- flag = "--#{key}"
9
- flag += "=#{value}" if value.is_a?(String)
10
- flag
11
- end
12
- end
13
-
14
- def connect(ctx, store:, password:, themeid:, env:)
15
- command = build_command("get", env)
16
- command << "--password=#{password}"
17
- command << "--store=#{store}"
18
- command << "--themeid=#{themeid}"
19
-
20
- stat = ctx.system(*command)
21
- stat.success?
22
- end
23
-
24
- def create(ctx, password:, store:, name:, env:)
25
- command = build_command("new", env)
26
- command << "--password=#{password}"
27
- command << "--store=#{store}"
28
- command << "--name=#{name}"
29
-
30
- stat = ctx.system(*command)
31
- stat.success?
32
- end
33
-
34
- def deploy(ctx, flags: nil, env:)
35
- unless push(ctx, flags: flags, env: env)
36
- ctx.abort(ctx.message("theme.deploy.push_fail"))
37
- end
38
- ctx.done(ctx.message("theme.deploy.info.pushed"))
39
-
40
- command = build_command("publish", env)
41
- (command << flags).compact!
42
- command.flatten!
43
-
44
- stat = ctx.system(*command)
45
- stat.success?
46
- end
47
-
48
- def generate_env(ctx, store:, password:, themeid:, env:)
49
- command = build_command("configure", env)
50
- command << "--password=#{password}"
51
- command << "--store=#{store}"
52
- command << "--themeid=#{themeid}"
53
-
54
- stat = ctx.system(*command)
55
- stat.success?
56
- end
57
-
58
- def push(ctx, files: nil, flags: nil, remove: false, env:)
59
- action = remove ? "remove" : "deploy"
60
- command = build_command(action, env)
61
-
62
- (command << files << flags).compact!
63
- command.flatten!
64
-
65
- stat = ctx.system(*command)
66
- stat.success?
67
- end
68
-
69
- def query_themes(ctx, store:, password:)
70
- begin
71
- resp = ::ShopifyCli::AdminAPI.rest_request(
72
- ctx,
73
- shop: store,
74
- token: password,
75
- path: "themes.json",
76
- )
77
- rescue ShopifyCli::API::APIRequestUnauthorizedError
78
- ctx.abort(ctx.message("theme.themekit.query_themes.bad_password"))
79
- rescue StandardError
80
- ctx.abort(ctx.message("theme.themekit.query_themes.not_connect"))
81
- end
82
-
83
- resp[1]["themes"].map { |theme| [theme["name"], theme["id"]] }.to_h
84
- end
85
-
86
- def serve(ctx, flags: nil, env:)
87
- command = build_command("open", env)
88
- out, stat = ctx.capture2e(*command)
89
- ctx.puts(out)
90
- ctx.abort(ctx.message("theme.serve.open_fail")) unless stat.success?
91
-
92
- command = build_command("watch", env)
93
- (command << flags).compact!
94
- command.flatten!
95
- ctx.system(*command)
96
- end
97
-
98
- def update(ctx)
99
- command = build_command("update")
100
- ctx.system(*command)
101
- end
102
-
103
- private
104
-
105
- def build_command(action, env = nil)
106
- command = [THEMEKIT, action]
107
- command << "--no-update-notifier"
108
- command << "--env=#{env}" if env
109
- command
110
- end
111
- end
112
- end
113
- end
@@ -1,64 +0,0 @@
1
- require "shopify_cli"
2
-
3
- module ShopifyCli
4
- module Commands
5
- class Connect < ShopifyCli::Command
6
- class << self
7
- def call(args, command_name)
8
- ProjectType.load_type(args[0]) unless args.empty?
9
- super
10
- end
11
-
12
- def help
13
- ShopifyCli::Context.message("core.connect.help", ShopifyCli::TOOL_NAME)
14
- end
15
- end
16
-
17
- def call(args, command_name)
18
- if Project.current&.env
19
- @ctx.puts(@ctx.message("core.connect.already_connected_warning"))
20
- end
21
-
22
- project_type = ask_project_type
23
-
24
- klass = ProjectType.load_type(project_type)&.connect_command
25
-
26
- if klass
27
- klass.ctx = @ctx
28
- klass.call(args, command_name, "connect")
29
- else
30
- app = default_connect(project_type)
31
- @ctx.done(@ctx.message("core.connect.connected", app))
32
- end
33
- end
34
-
35
- def ask_project_type
36
- CLI::UI::Prompt.ask(@ctx.message("core.connect.project_type_select")) do |handler|
37
- ShopifyCli::Commands::Create.all_visible_type.each do |type|
38
- handler.option(type.project_name) { type.project_type }
39
- end
40
- end
41
- end
42
-
43
- def default_connect(project_type)
44
- org = ShopifyCli::Tasks::EnsureEnv.call(@ctx, regenerate: true)
45
- write_cli_yml(project_type, org["id"]) unless Project.has_current?
46
- api_key = Project.current(force_reload: true).env["api_key"]
47
- get_app(org["apps"], api_key).first["title"]
48
- end
49
-
50
- def write_cli_yml(project_type, org_id)
51
- ShopifyCli::Project.write(
52
- @ctx,
53
- project_type: project_type,
54
- organization_id: org_id,
55
- )
56
- @ctx.done(@ctx.message("core.connect.cli_yml_saved"))
57
- end
58
-
59
- def get_app(apps, api_key)
60
- apps.select { |app| app["apiKey"] == api_key }
61
- end
62
- end
63
- end
64
- end
@@ -1,50 +0,0 @@
1
- require "shopify_cli"
2
-
3
- module ShopifyCli
4
- module Commands
5
- class Create < ShopifyCli::Command
6
- def self.call(args, command_name)
7
- ProjectType.load_type(args[0]) unless args.empty?
8
- super
9
- end
10
-
11
- def call(args, command_name)
12
- unless args.empty?
13
- @ctx.puts(@ctx.message("core.create.error.invalid_app_type", args[0]))
14
- return @ctx.puts(self.class.help)
15
- end
16
-
17
- type_name = CLI::UI::Prompt.ask(@ctx.message("core.create.project_type_select")) do |handler|
18
- self.class.all_visible_type.each do |type|
19
- handler.option(type.project_name) { type.project_type }
20
- end
21
- end
22
-
23
- klass = ProjectType.load_type(type_name).create_command
24
- klass.ctx = @ctx
25
- klass.call(args, command_name, "create")
26
- end
27
-
28
- def self.all_visible_type
29
- ProjectType
30
- .load_all
31
- .select { |type| !type.hidden? }
32
- end
33
-
34
- def self.help
35
- project_types = all_visible_type.map(&:project_type).sort.join(" | ")
36
- ShopifyCli::Context.message("core.create.help", ShopifyCli::TOOL_NAME, project_types)
37
- end
38
-
39
- def self.extended_help
40
- <<~HELP
41
- #{
42
- all_visible_type.map do |type|
43
- type.create_command.help
44
- end.join("\n")
45
- }
46
- HELP
47
- end
48
- end
49
- end
50
- end
@@ -1,198 +0,0 @@
1
- require "base64"
2
- require "digest"
3
- require "json"
4
- require "net/http"
5
- require "securerandom"
6
- require "openssl"
7
- require "shopify_cli"
8
- require "uri"
9
- require "webrick"
10
-
11
- module ShopifyCli
12
- class OAuth
13
- include SmartProperties
14
-
15
- autoload :Servlet, "shopify-cli/oauth/servlet"
16
-
17
- class Error < StandardError; end
18
- LocalRequest = Struct.new(:method, :path, :query, :protocol)
19
-
20
- DEFAULT_PORT = 3456
21
- REDIRECT_HOST = "http://127.0.0.1:#{DEFAULT_PORT}"
22
-
23
- property! :ctx
24
- property! :service, accepts: String
25
- property! :client_id, accepts: String
26
- property! :scopes
27
- property :store, default: -> { ShopifyCli::DB.new }
28
- property :secret, accepts: String
29
- property :request_exchange, accepts: String
30
- property :options, default: -> { {} }, accepts: Hash
31
- property :auth_path, default: "/authorize", accepts: ->(path) { path.is_a?(String) && path.start_with?("/") }
32
- property :token_path, default: "/token", accepts: ->(path) { path.is_a?(String) && path.start_with?("/") }
33
- property :state_token, accepts: String, default: SecureRandom.hex(30)
34
- property :code_verifier, accepts: String, default: SecureRandom.hex(30)
35
-
36
- attr_accessor :response_query
37
-
38
- def authenticate(url)
39
- return if refresh_exchange_token(url)
40
- return if refresh_access_token(url)
41
- initiate_authentication(url)
42
- request_access_token(url, code: receive_access_code)
43
- request_exchange_token(url) if should_exchange
44
- end
45
-
46
- def code_challenge
47
- @code_challenge ||= Base64.urlsafe_encode64(
48
- OpenSSL::Digest::SHA256.digest(code_verifier),
49
- padding: false,
50
- )
51
- end
52
-
53
- def server
54
- @server ||= begin
55
- server = WEBrick::HTTPServer.new(
56
- Port: DEFAULT_PORT,
57
- Logger: WEBrick::Log.new(File.open(File::NULL, "w")),
58
- AccessLog: [],
59
- )
60
- server.mount("/", Servlet, self, state_token)
61
- server
62
- end
63
- end
64
-
65
- private
66
-
67
- def initiate_authentication(url)
68
- @server_thread = Thread.new { server.start }
69
- params = {
70
- client_id: client_id,
71
- scope: scopes,
72
- redirect_uri: REDIRECT_HOST,
73
- state: state_token,
74
- response_type: :code,
75
- }
76
- params.merge!(challange_params) if secret.nil?
77
- uri = URI.parse("#{url}#{auth_path}")
78
- uri.query = URI.encode_www_form(params.merge(options))
79
- output_authentication_info(uri)
80
- end
81
-
82
- def output_authentication_info(uri)
83
- login_location = if service == "admin"
84
- ctx.message("core.oauth.location.admin")
85
- elsif Shopifolk.acting_as_shopify_organization?
86
- ctx.message("core.oauth.location.shopifolk")
87
- else
88
- ctx.message("core.oauth.location.partner")
89
- end
90
- ctx.puts(ctx.message("core.oauth.authentication_required", login_location))
91
- ctx.open_url!(uri)
92
- end
93
-
94
- def receive_access_code
95
- @access_code ||= begin
96
- @server_thread.join(240)
97
- raise Error, ctx.message("core.oauth.error.timeout") if response_query.nil?
98
- raise Error, response_query["error_description"] unless response_query["error"].nil?
99
- response_query["code"]
100
- end
101
- end
102
-
103
- def request_access_token(url, code:)
104
- resp = post_token_request(
105
- "#{url}#{token_path}",
106
- {
107
- grant_type: :authorization_code,
108
- code: code,
109
- redirect_uri: REDIRECT_HOST,
110
- client_id: client_id,
111
- }.merge(confirmation_param)
112
- )
113
- store.set(
114
- "#{service}_access_token".to_sym => resp["access_token"],
115
- "#{service}_refresh_token".to_sym => resp["refresh_token"],
116
- )
117
- end
118
-
119
- def refresh_access_token(url)
120
- return false if !store.exists?("#{service}_access_token".to_sym) ||
121
- !store.exists?("#{service}_refresh_token".to_sym)
122
- refresh_token(url)
123
- request_exchange_token(url) if should_exchange
124
- true
125
- rescue
126
- store.del("#{service}_access_token".to_sym, "#{service}_refresh_token".to_sym)
127
- false
128
- end
129
-
130
- def refresh_token(url)
131
- resp = post_token_request(
132
- "#{url}#{token_path}",
133
- grant_type: :refresh_token,
134
- access_token: store.get("#{service}_access_token".to_sym),
135
- refresh_token: store.get("#{service}_refresh_token".to_sym),
136
- client_id: client_id,
137
- )
138
- store.set(
139
- "#{service}_access_token".to_sym => resp["access_token"],
140
- "#{service}_refresh_token".to_sym => resp["refresh_token"],
141
- )
142
- end
143
-
144
- def refresh_exchange_token(url)
145
- return false if !should_exchange || !store.exists?("#{service}_exchange_token".to_sym)
146
- request_exchange_token(url)
147
- true
148
- rescue
149
- store.del("#{service}_exchange_token".to_sym)
150
- false
151
- end
152
-
153
- def request_exchange_token(url)
154
- resp = post_token_request(
155
- "#{url}#{token_path}",
156
- grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
157
- requested_token_type: "urn:ietf:params:oauth:token-type:access_token",
158
- subject_token_type: "urn:ietf:params:oauth:token-type:access_token",
159
- client_id: client_id,
160
- audience: request_exchange,
161
- scope: scopes,
162
- subject_token: store.get("#{service}_access_token".to_sym),
163
- )
164
- store.set("#{service}_exchange_token".to_sym => resp["access_token"])
165
- end
166
-
167
- def post_token_request(url, params)
168
- uri = URI.parse(url)
169
- https = Net::HTTP.new(uri.host, uri.port)
170
- https.use_ssl = true
171
- request = Net::HTTP::Post.new(uri.path)
172
- request["User-Agent"] = "Shopify App CLI #{::ShopifyCli::VERSION}"
173
- request.body = URI.encode_www_form(params)
174
- res = https.request(request)
175
- raise Error, JSON.parse(res.body)["error_description"] unless res.is_a?(Net::HTTPSuccess)
176
- JSON.parse(res.body)
177
- end
178
-
179
- def challange_params
180
- {
181
- code_challenge: code_challenge,
182
- code_challenge_method: "S256",
183
- }
184
- end
185
-
186
- def confirmation_param
187
- if secret.nil?
188
- { code_verifier: code_verifier }
189
- else
190
- { client_secret: secret }
191
- end
192
- end
193
-
194
- def should_exchange
195
- !request_exchange.nil? && !request_exchange.empty?
196
- end
197
- end
198
- end