shopify-cli 1.13.0 → 2.0.2

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 (199) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +1 -1
  3. data/.github/CONTRIBUTING.md +7 -7
  4. data/.github/DESIGN.md +3 -3
  5. data/.github/workflows/build.yml +1 -1
  6. data/.gitignore +3 -0
  7. data/.rubocop.yml +3 -1
  8. data/.ruby-version +1 -1
  9. data/CHANGELOG.md +57 -24
  10. data/Gemfile +4 -0
  11. data/Gemfile.lock +32 -0
  12. data/LICENSE +4 -1
  13. data/README.md +94 -26
  14. data/RELEASING.md +31 -7
  15. data/Rakefile +2 -2
  16. data/SECURITY.md +1 -1
  17. data/THEMEKIT_MIGRATION.md +18 -0
  18. data/bin/load_shopify.rb +1 -1
  19. data/bin/shopify +3 -3
  20. data/dev.yml +1 -1
  21. data/docs/app/node/index.md +1 -1
  22. data/docs/app/rails/index.md +1 -1
  23. data/docs/core/index.md +1 -1
  24. data/docs/getting-started/index.md +1 -1
  25. data/docs/getting-started/install/index.md +1 -1
  26. data/docs/getting-started/migrate/index.md +1 -1
  27. data/docs/getting-started/uninstall/index.md +1 -1
  28. data/docs/getting-started/upgrade/index.md +1 -1
  29. data/docs/help/start-app/index.md +1 -1
  30. data/docs/index.md +1 -1
  31. data/ext/shopify-cli/extconf.rb +17 -5
  32. data/install.sh +1 -1
  33. data/lib/docgen/index_template.md.erb +2 -2
  34. data/lib/graphql/all_orgs_with_extensions.graphql +37 -0
  35. data/lib/graphql/api_versions.graphql +1 -1
  36. data/lib/graphql/find_organization.graphql +2 -1
  37. data/lib/project_types/extension/cli.rb +18 -15
  38. data/lib/project_types/extension/commands/build.rb +4 -5
  39. data/lib/project_types/extension/commands/connect.rb +35 -0
  40. data/lib/project_types/extension/commands/create.rb +12 -16
  41. data/lib/project_types/extension/commands/extension_command.rb +2 -2
  42. data/lib/project_types/extension/commands/info.rb +86 -0
  43. data/lib/project_types/extension/commands/push.rb +8 -7
  44. data/lib/project_types/extension/commands/register.rb +4 -5
  45. data/lib/project_types/extension/commands/serve.rb +5 -8
  46. data/lib/project_types/extension/commands/tunnel.rb +3 -1
  47. data/lib/project_types/extension/errors.rb +9 -0
  48. data/lib/project_types/extension/extension_project.rb +17 -1
  49. data/lib/project_types/extension/extension_project_keys.rb +1 -0
  50. data/lib/project_types/extension/features/argo.rb +6 -6
  51. data/lib/project_types/extension/features/argo_runtime.rb +22 -56
  52. data/lib/project_types/extension/features/argo_serve.rb +25 -18
  53. data/lib/project_types/extension/forms/connect.rb +42 -0
  54. data/lib/project_types/extension/forms/questions/ask_name.rb +14 -6
  55. data/lib/project_types/extension/forms/questions/ask_registration.rb +51 -0
  56. data/lib/project_types/extension/messages/messages.rb +80 -16
  57. data/lib/project_types/extension/models/specification.rb +1 -0
  58. data/lib/project_types/extension/models/specification_handlers/{checkout_argo_extension.rb → checkout_ui_extension.rb} +3 -1
  59. data/lib/project_types/extension/models/specification_handlers/default.rb +13 -3
  60. data/lib/project_types/extension/models/specification_handlers/theme_app_extension.rb +89 -0
  61. data/lib/project_types/extension/models/specifications.rb +1 -0
  62. data/lib/project_types/extension/tasks/configure_features.rb +6 -7
  63. data/lib/project_types/extension/tasks/configure_options.rb +20 -0
  64. data/lib/project_types/extension/tasks/get_extensions.rb +32 -0
  65. data/lib/project_types/node/cli.rb +9 -21
  66. data/lib/project_types/node/commands/connect.rb +8 -2
  67. data/lib/project_types/node/commands/create.rb +9 -5
  68. data/lib/project_types/node/commands/deploy.rb +15 -5
  69. data/lib/project_types/node/commands/deploy/heroku.rb +29 -29
  70. data/lib/project_types/node/commands/generate.rb +4 -2
  71. data/lib/project_types/node/commands/open.rb +4 -2
  72. data/lib/project_types/node/commands/serve.rb +3 -2
  73. data/lib/project_types/node/commands/tunnel.rb +4 -2
  74. data/lib/project_types/node/messages/messages.rb +47 -90
  75. data/lib/project_types/rails/cli.rb +9 -21
  76. data/lib/project_types/rails/commands/connect.rb +8 -2
  77. data/lib/project_types/rails/commands/create.rb +10 -6
  78. data/lib/project_types/rails/commands/deploy.rb +15 -5
  79. data/lib/project_types/rails/commands/deploy/heroku.rb +84 -82
  80. data/lib/project_types/rails/commands/generate.rb +15 -5
  81. data/lib/project_types/rails/commands/generate/webhook.rb +28 -26
  82. data/lib/project_types/rails/commands/open.rb +4 -2
  83. data/lib/project_types/rails/commands/serve.rb +3 -2
  84. data/lib/project_types/rails/commands/tunnel.rb +4 -2
  85. data/lib/project_types/rails/messages/messages.rb +72 -119
  86. data/lib/project_types/script/cli.rb +6 -8
  87. data/lib/project_types/script/commands/create.rb +3 -1
  88. data/lib/project_types/script/commands/push.rb +12 -5
  89. data/lib/project_types/script/graphql/app_script_update_or_create.graphql +9 -3
  90. data/lib/project_types/script/layers/application/create_script.rb +4 -3
  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 +13 -17
  96. data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_project_creator.rb +29 -21
  97. data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +2 -4
  98. data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +45 -34
  99. data/lib/project_types/script/layers/infrastructure/script_service.rb +37 -16
  100. data/lib/project_types/script/messages/messages.rb +66 -55
  101. data/lib/project_types/script/tasks/ensure_env.rb +22 -1
  102. data/lib/project_types/script/ui/error_handler.rb +32 -32
  103. data/lib/project_types/theme/cli.rb +16 -27
  104. data/lib/project_types/theme/commands/check.rb +33 -0
  105. data/lib/project_types/theme/commands/delete.rb +64 -0
  106. data/lib/project_types/theme/commands/init.rb +42 -0
  107. data/lib/project_types/theme/commands/language_server.rb +16 -0
  108. data/lib/project_types/theme/commands/package.rb +55 -0
  109. data/lib/project_types/theme/commands/publish.rb +43 -0
  110. data/lib/project_types/theme/commands/pull.rb +51 -0
  111. data/lib/project_types/theme/commands/push.rb +58 -32
  112. data/lib/project_types/theme/commands/serve.rb +8 -16
  113. data/lib/project_types/theme/forms/confirm_store.rb +15 -0
  114. data/lib/project_types/theme/forms/select.rb +59 -0
  115. data/lib/project_types/theme/messages/messages.rb +118 -103
  116. data/lib/project_types/theme/ui/sync_progress_bar.rb +20 -0
  117. data/lib/shopify-cli/admin_api.rb +57 -38
  118. data/lib/shopify-cli/admin_api/populate_resource_command.rb +6 -14
  119. data/lib/shopify-cli/admin_api/schema.rb +1 -10
  120. data/lib/shopify-cli/api.rb +29 -14
  121. data/lib/shopify-cli/command.rb +15 -3
  122. data/lib/shopify-cli/commands.rb +7 -2
  123. data/lib/shopify-cli/commands/help.rb +2 -29
  124. data/lib/shopify-cli/commands/login.rb +95 -0
  125. data/lib/shopify-cli/commands/logout.rb +24 -8
  126. data/lib/shopify-cli/commands/populate.rb +23 -0
  127. data/lib/{project_types/node → shopify-cli}/commands/populate/customer.rb +2 -8
  128. data/lib/{project_types/node → shopify-cli}/commands/populate/draft_order.rb +2 -2
  129. data/lib/{project_types/node → shopify-cli}/commands/populate/product.rb +2 -8
  130. data/lib/shopify-cli/commands/store.rb +15 -0
  131. data/lib/shopify-cli/commands/switch.rb +39 -0
  132. data/lib/shopify-cli/commands/system.rb +12 -0
  133. data/lib/shopify-cli/commands/whoami.rb +28 -0
  134. data/lib/shopify-cli/connect.rb +32 -0
  135. data/lib/shopify-cli/context.rb +65 -4
  136. data/lib/shopify-cli/core/entry_point.rb +3 -22
  137. data/lib/shopify-cli/core/monorail.rb +6 -2
  138. data/lib/shopify-cli/db.rb +4 -4
  139. data/lib/shopify-cli/http_request.rb +16 -0
  140. data/lib/shopify-cli/identity_auth.rb +282 -0
  141. data/lib/shopify-cli/{oauth → identity_auth}/servlet.rb +11 -12
  142. data/lib/shopify-cli/messages/messages.rb +140 -46
  143. data/lib/shopify-cli/packager.rb +5 -5
  144. data/lib/shopify-cli/partners_api.rb +21 -44
  145. data/lib/shopify-cli/partners_api/organizations.rb +8 -0
  146. data/lib/shopify-cli/project_commands.rb +16 -0
  147. data/lib/shopify-cli/project_type.rb +0 -31
  148. data/lib/shopify-cli/shopifolk.rb +8 -11
  149. data/lib/shopify-cli/sub_command.rb +1 -0
  150. data/lib/shopify-cli/tasks.rb +3 -0
  151. data/lib/shopify-cli/tasks/confirm_store.rb +18 -0
  152. data/lib/shopify-cli/tasks/create_api_client.rb +2 -2
  153. data/lib/shopify-cli/tasks/ensure_authenticated.rb +13 -0
  154. data/lib/shopify-cli/tasks/ensure_loopback_url.rb +1 -1
  155. data/lib/shopify-cli/tasks/ensure_project_type.rb +12 -0
  156. data/lib/shopify-cli/tasks/select_org_and_shop.rb +0 -3
  157. data/lib/shopify-cli/theme/dev_server.rb +98 -0
  158. data/lib/shopify-cli/theme/dev_server/certificate_manager.rb +79 -0
  159. data/lib/shopify-cli/theme/dev_server/header_hash.rb +94 -0
  160. data/lib/shopify-cli/theme/dev_server/hot-reload.js +93 -0
  161. data/lib/shopify-cli/theme/dev_server/hot_reload.rb +76 -0
  162. data/lib/shopify-cli/theme/dev_server/local_assets.rb +87 -0
  163. data/lib/shopify-cli/theme/dev_server/proxy.rb +205 -0
  164. data/lib/shopify-cli/theme/dev_server/sse.rb +75 -0
  165. data/lib/shopify-cli/theme/dev_server/watcher.rb +59 -0
  166. data/lib/shopify-cli/theme/dev_server/web_server.rb +140 -0
  167. data/lib/shopify-cli/theme/development_theme.rb +69 -0
  168. data/lib/shopify-cli/theme/file.rb +112 -0
  169. data/lib/shopify-cli/theme/ignore_filter.rb +109 -0
  170. data/lib/shopify-cli/theme/mime_type.rb +34 -0
  171. data/lib/shopify-cli/theme/syncer.rb +332 -0
  172. data/lib/shopify-cli/theme/theme.rb +204 -0
  173. data/lib/shopify-cli/tunnel.rb +1 -1
  174. data/lib/shopify-cli/version.rb +1 -1
  175. data/lib/shopify_cli.rb +18 -11
  176. data/shopify-cli.gemspec +12 -5
  177. data/shopify.fish +1 -1
  178. data/shopify.sh +1 -1
  179. metadata +91 -35
  180. data/.github/workflows/release.yml +0 -59
  181. data/lib/project_types/extension/features/argo_serve_options.rb +0 -41
  182. data/lib/project_types/node/commands/populate.rb +0 -23
  183. data/lib/project_types/rails/commands/populate.rb +0 -23
  184. data/lib/project_types/rails/commands/populate/customer.rb +0 -31
  185. data/lib/project_types/rails/commands/populate/draft_order.rb +0 -28
  186. data/lib/project_types/rails/commands/populate/product.rb +0 -30
  187. data/lib/project_types/script/layers/domain/config_ui.rb +0 -16
  188. data/lib/project_types/theme/commands/connect.rb +0 -54
  189. data/lib/project_types/theme/commands/create.rb +0 -48
  190. data/lib/project_types/theme/commands/deploy.rb +0 -38
  191. data/lib/project_types/theme/commands/generate.rb +0 -20
  192. data/lib/project_types/theme/commands/generate/env.rb +0 -79
  193. data/lib/project_types/theme/forms/connect.rb +0 -34
  194. data/lib/project_types/theme/forms/create.rb +0 -22
  195. data/lib/project_types/theme/tasks/ensure_themekit_installed.rb +0 -78
  196. data/lib/project_types/theme/themekit.rb +0 -113
  197. data/lib/shopify-cli/commands/connect.rb +0 -64
  198. data/lib/shopify-cli/commands/create.rb +0 -50
  199. data/lib/shopify-cli/oauth.rb +0 -198
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+ require "shopify-cli/theme/theme"
3
+ require "shopify-cli/theme/development_theme"
4
+
5
+ module Theme
6
+ class Command
7
+ class Delete < ShopifyCli::SubCommand
8
+ options do |parser, flags|
9
+ parser.on("-d", "--development") { flags[:development] = true }
10
+ parser.on("-a", "--show-all") { flags[:show_all] = true }
11
+ parser.on("-f", "--force") { flags[:force] = true }
12
+ end
13
+
14
+ def call(args, _name)
15
+ themes = if options.flags[:development]
16
+ [ShopifyCli::Theme::DevelopmentTheme.new(@ctx)]
17
+ elsif args.any?
18
+ args.map { |id| ShopifyCli::Theme::Theme.new(@ctx, id: id) }
19
+ else
20
+ form = Forms::Select.ask(
21
+ @ctx,
22
+ [],
23
+ title: @ctx.message("theme.delete.select"),
24
+ exclude_roles: ["live"],
25
+ include_foreign_developments: options.flags[:show_all],
26
+ )
27
+ return unless form
28
+ [form.theme]
29
+ end
30
+
31
+ deleted = 0
32
+ themes.each do |theme|
33
+ if theme.live?
34
+ @ctx.puts(@ctx.message("theme.delete.live", theme.id))
35
+ next
36
+ elsif !confirm?(theme)
37
+ next
38
+ end
39
+ theme.delete
40
+ deleted += 1
41
+ rescue ShopifyCli::API::APIRequestNotFoundError
42
+ @ctx.puts(@ctx.message("theme.delete.not_found", theme.id))
43
+ end
44
+
45
+ @ctx.done(@ctx.message("theme.delete.done", deleted))
46
+ end
47
+
48
+ def self.help
49
+ ShopifyCli::Context.message("theme.delete.help", ShopifyCli::TOOL_NAME, ShopifyCli::TOOL_NAME)
50
+ end
51
+
52
+ private
53
+
54
+ def confirm?(theme)
55
+ Forms::ConfirmStore.ask(
56
+ @ctx,
57
+ [],
58
+ title: @ctx.message("theme.delete.confirm", theme.name, theme.shop),
59
+ force: options.flags[:force],
60
+ ).confirmed?
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Theme
4
+ class Command
5
+ class Init < ShopifyCli::SubCommand
6
+ options do |parser, flags|
7
+ parser.on("-u", "--clone-url URL") { |url| flags[:clone_url] = url }
8
+ end
9
+
10
+ DEFAULT_CLONE_URL = "https://github.com/Shopify/dawn.git"
11
+
12
+ def call(args, _name)
13
+ name = args.first || ask_name
14
+ clone_url = options.flags[:clone_url] || DEFAULT_CLONE_URL
15
+ clone(clone_url, name)
16
+ end
17
+
18
+ def self.help
19
+ ShopifyCli::Context.message("theme.init.help", ShopifyCli::TOOL_NAME, ShopifyCli::TOOL_NAME)
20
+ end
21
+
22
+ private
23
+
24
+ def ask_name
25
+ CLI::UI::Prompt.ask(@ctx.message("theme.init.ask_name"))
26
+ end
27
+
28
+ def clone(url, name)
29
+ ShopifyCli::Git.clone(url, name)
30
+
31
+ @ctx.root = File.join(@ctx.root, name)
32
+
33
+ begin
34
+ @ctx.rm_r(".git")
35
+ @ctx.rm_r(".github")
36
+ rescue Errno::ENOENT => e
37
+ @ctx.debug(e)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+ require "theme_check"
3
+
4
+ module Theme
5
+ class Command
6
+ class LanguageServer < ShopifyCli::SubCommand
7
+ def call(*)
8
+ ThemeCheck::LanguageServer.start
9
+ end
10
+
11
+ def self.help
12
+ ShopifyCli::Context.message("theme.language_server.help", ShopifyCli::TOOL_NAME)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+ require "pathname"
3
+ require "json"
4
+
5
+ module Theme
6
+ class Command
7
+ class Package < ShopifyCli::SubCommand
8
+ THEME_DIRECTORIES = %w[
9
+ assets
10
+ config
11
+ layout
12
+ locales
13
+ sections
14
+ snippets
15
+ templates
16
+ ]
17
+
18
+ def call(args, _name)
19
+ path = args.first || "."
20
+
21
+ check_prereq_command("zip")
22
+ zip_name = theme_name(path) + ".zip"
23
+ zip(zip_name, path, THEME_DIRECTORIES)
24
+ @ctx.done(@ctx.message("theme.package.done", zip_name))
25
+ end
26
+
27
+ def self.help
28
+ ShopifyCli::Context.message("theme.package.help", ShopifyCli::TOOL_NAME, ShopifyCli::TOOL_NAME)
29
+ end
30
+
31
+ private
32
+
33
+ def check_prereq_command(command)
34
+ cmd_path = @ctx.which(command)
35
+ @ctx.abort(@ctx.message("theme.package.error.prereq_command_required", command)) if cmd_path.nil?
36
+ end
37
+
38
+ def zip(zip_name, path, files)
39
+ @ctx.system("zip", "-r", zip_name, *files, chdir: path)
40
+ end
41
+
42
+ def theme_name(path)
43
+ settings_schema = Pathname.new(path).join("config/settings_schema.json")
44
+ @ctx.abort(@ctx.message("theme.package.error.missing_config")) unless settings_schema.file?
45
+
46
+ content = settings_schema.read
47
+ theme_info = JSON.parse(content).find { |section| section["name"] == "theme_info" }
48
+ theme_name = theme_info&.dig("theme_name")
49
+ @ctx.abort(@ctx.message("theme.package.error.missing_theme_name")) unless theme_name
50
+
51
+ [theme_name, theme_info["theme_version"]].compact.join("-")
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+ require "shopify-cli/theme/theme"
3
+
4
+ module Theme
5
+ class Command
6
+ class Publish < ShopifyCli::SubCommand
7
+ options do |parser, flags|
8
+ parser.on("-f", "--force") { flags[:force] = true }
9
+ end
10
+
11
+ def call(args, *)
12
+ theme = if (theme_id = args.first)
13
+ ShopifyCli::Theme::Theme.new(@ctx, id: theme_id)
14
+ else
15
+ form = Forms::Select.ask(
16
+ @ctx,
17
+ [],
18
+ title: @ctx.message("theme.publish.select"),
19
+ exclude_roles: ["live", "development", "demo"],
20
+ )
21
+ return unless form
22
+ form.theme
23
+ end
24
+
25
+ return unless Forms::ConfirmStore.ask(
26
+ @ctx,
27
+ [],
28
+ title: @ctx.message("theme.publish.confirm", theme.name, theme.shop),
29
+ force: options.flags[:force],
30
+ ).confirmed?
31
+
32
+ theme.publish
33
+ @ctx.done(@ctx.message("theme.publish.done", theme.preview_url))
34
+ rescue ShopifyCli::API::APIRequestNotFoundError
35
+ @ctx.puts(@ctx.message("theme.publish.not_found", theme.id))
36
+ end
37
+
38
+ def self.help
39
+ ShopifyCli::Context.message("theme.publish.help", ShopifyCli::TOOL_NAME, ShopifyCli::TOOL_NAME)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+ require "shopify-cli/theme/theme"
3
+ require "shopify-cli/theme/ignore_filter"
4
+ require "shopify-cli/theme/syncer"
5
+
6
+ module Theme
7
+ class Command
8
+ class Pull < ShopifyCli::SubCommand
9
+ options do |parser, flags|
10
+ parser.on("-n", "--nodelete") { flags[:nodelete] = true }
11
+ parser.on("-i", "--themeid=ID") { |theme_id| flags[:theme_id] = theme_id }
12
+ end
13
+
14
+ def call(args, _name)
15
+ root = args.first || "."
16
+ delete = !options.flags[:nodelete]
17
+
18
+ theme = if (theme_id = options.flags[:theme_id])
19
+ ShopifyCli::Theme::Theme.new(@ctx, root: root, id: theme_id)
20
+ else
21
+ form = Forms::Select.ask(
22
+ @ctx,
23
+ [],
24
+ title: @ctx.message("theme.pull.select"),
25
+ root: root,
26
+ )
27
+ return unless form
28
+ form.theme
29
+ end
30
+
31
+ ignore_filter = ShopifyCli::Theme::IgnoreFilter.from_path(root)
32
+ syncer = ShopifyCli::Theme::Syncer.new(@ctx, theme: theme, ignore_filter: ignore_filter)
33
+ begin
34
+ syncer.start_threads
35
+ CLI::UI::Frame.open(@ctx.message("theme.pull.pulling", theme.name, theme.id, theme.shop)) do
36
+ UI::SyncProgressBar.new(syncer).progress(:download_theme!, delete: delete)
37
+ end
38
+ @ctx.done(@ctx.message("theme.pull.done"))
39
+ rescue ShopifyCli::API::APIRequestNotFoundError
40
+ @ctx.abort(@ctx.message("theme.pull.theme_not_found", theme.id))
41
+ ensure
42
+ syncer.shutdown
43
+ end
44
+ end
45
+
46
+ def self.help
47
+ ShopifyCli::Context.message("theme.pull.help", ShopifyCli::TOOL_NAME, ShopifyCli::TOOL_NAME)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -1,49 +1,75 @@
1
1
  # frozen_string_literal: true
2
- module Theme
3
- module Commands
4
- class Push < ShopifyCli::Command
5
- prerequisite_task :ensure_themekit_installed
2
+ require "shopify-cli/theme/theme"
3
+ require "shopify-cli/theme/development_theme"
4
+ require "shopify-cli/theme/ignore_filter"
5
+ require "shopify-cli/theme/syncer"
6
6
 
7
+ module Theme
8
+ class Command
9
+ class Push < ShopifyCli::SubCommand
7
10
  options do |parser, flags|
8
- parser.on("--remove") { flags["remove"] = true }
9
- parser.on("--nodelete") { flags["nodelete"] = true }
10
- parser.on("--allow-live") { flags["allow-live"] = true }
11
- parser.on("--env=ENV") { |env| flags[:env] = env }
11
+ parser.on("-n", "--nodelete") { flags[:nodelete] = true }
12
+ parser.on("-i", "--themeid=ID") { |theme_id| flags[:theme_id] = theme_id }
13
+ parser.on("-d", "--development") { flags[:development] = true }
14
+ parser.on("-u", "--unpublished") { flags[:unpublished] = true }
15
+ parser.on("-j", "--json") { flags[:json] = true }
16
+ parser.on("-a", "--allow-live") { flags[:allow_live] = true }
17
+ parser.on("-p", "--publish") { flags[:publish] = true }
12
18
  end
13
19
 
14
20
  def call(args, _name)
15
- if options.flags["remove"]
16
- remove = true
17
- options.flags.delete("remove")
18
- end
21
+ root = args.first || "."
22
+ delete = !options.flags[:nodelete]
19
23
 
20
- if options.flags[:env]
21
- env = options.flags[:env]
22
- options.flags.delete(:env)
24
+ theme = if (theme_id = options.flags[:theme_id])
25
+ ShopifyCli::Theme::Theme.new(@ctx, root: root, id: theme_id)
26
+ elsif options.flags[:development]
27
+ theme = ShopifyCli::Theme::DevelopmentTheme.new(@ctx, root: root)
28
+ theme.ensure_exists!
29
+ theme
30
+ elsif options.flags[:unpublished]
31
+ name = CLI::UI::Prompt.ask(@ctx.message("theme.push.name"), allow_empty: false)
32
+ theme = ShopifyCli::Theme::Theme.new(@ctx, root: root, name: name, role: "unpublished")
33
+ theme.create
34
+ theme
35
+ else
36
+ form = Forms::Select.ask(
37
+ @ctx,
38
+ [],
39
+ title: @ctx.message("theme.push.select"),
40
+ root: root,
41
+ )
42
+ return unless form
43
+ form.theme
23
44
  end
24
45
 
25
- flags = Themekit.add_flags(options.flags)
26
-
27
- if remove
28
- CLI::UI::Frame.open(@ctx.message("theme.push.remove")) do
29
- unless CLI::UI::Prompt.confirm(@ctx.message("theme.push.remove_confirm"))
30
- @ctx.abort(@ctx.message("theme.push.remove_abort"))
31
- end
46
+ if theme.live? && !options.flags[:allow_live]
47
+ return unless CLI::UI::Prompt.confirm(@ctx.message("theme.push.live"))
48
+ end
32
49
 
33
- unless Themekit.push(@ctx, files: args, flags: flags, remove: remove, env: env)
34
- @ctx.abort(@ctx.message("theme.push.error.remove_error"))
50
+ ignore_filter = ShopifyCli::Theme::IgnoreFilter.from_path(root)
51
+ syncer = ShopifyCli::Theme::Syncer.new(@ctx, theme: theme, ignore_filter: ignore_filter)
52
+ begin
53
+ syncer.start_threads
54
+ if options.flags[:json]
55
+ syncer.upload_theme!(delete: delete)
56
+ puts(JSON.generate(theme: theme.to_h))
57
+ else
58
+ CLI::UI::Frame.open(@ctx.message("theme.push.info.pushing", theme.name, theme.id, theme.shop)) do
59
+ UI::SyncProgressBar.new(syncer).progress(:upload_theme!, delete: delete)
35
60
  end
36
- end
37
61
 
38
- @ctx.done(@ctx.message("theme.push.info.remove", @ctx.root))
39
- else
40
- CLI::UI::Frame.open(@ctx.message("theme.push.push")) do
41
- unless Themekit.push(@ctx, files: args, flags: flags, remove: remove, env: env)
42
- @ctx.abort(@ctx.message("theme.push.error.push_error"))
62
+ if options.flags[:publish]
63
+ theme.publish
64
+ @ctx.done(@ctx.message("theme.publish.done", theme.preview_url))
65
+ else
66
+ @ctx.done(@ctx.message("theme.push.done", theme.preview_url, theme.editor_url))
43
67
  end
44
68
  end
45
-
46
- @ctx.done(@ctx.message("theme.push.info.push", @ctx.root))
69
+ rescue ShopifyCli::API::APIRequestNotFoundError
70
+ @ctx.abort(@ctx.message("theme.push.theme_not_found", theme.id))
71
+ ensure
72
+ syncer.shutdown
47
73
  end
48
74
  end
49
75
 
@@ -1,25 +1,17 @@
1
1
  # frozen_string_literal: true
2
- module Theme
3
- module Commands
4
- class Serve < ShopifyCli::Command
5
- prerequisite_task :ensure_themekit_installed
2
+ require "shopify-cli/theme/dev_server"
6
3
 
4
+ module Theme
5
+ class Command
6
+ class Serve < ShopifyCli::SubCommand
7
7
  options do |parser, flags|
8
- parser.on("--env=ENV") { |env| flags[:env] = env }
9
- parser.on("--allow-live") { flags["allow-live"] = true }
10
- parser.on("--notify=FILES") { |files| flags["notify"] = files }
8
+ parser.on("--port=PORT") { |port| flags[:port] = port.to_i }
11
9
  end
12
10
 
13
11
  def call(*)
14
- if options.flags[:env]
15
- env = options.flags[:env]
16
- options.flags.delete(:env)
17
- end
18
-
19
- flags = Themekit.add_flags(options.flags)
20
-
21
- CLI::UI::Frame.open(@ctx.message("theme.serve.serve")) do
22
- Themekit.serve(@ctx, flags: flags, env: env)
12
+ flags = options.flags.dup
13
+ ShopifyCli::Theme::DevServer.start(@ctx, ".", **flags) do |syncer|
14
+ UI::SyncProgressBar.new(syncer).progress(:upload_theme!, delay_low_priority_files: true)
23
15
  end
24
16
  end
25
17
 
@@ -0,0 +1,15 @@
1
+ module Theme
2
+ module Forms
3
+ class ConfirmStore < ShopifyCli::Form
4
+ flag_arguments :title, :force
5
+
6
+ def ask
7
+ @confirmed = force || CLI::UI::Prompt.confirm(title, default: false)
8
+ end
9
+
10
+ def confirmed?
11
+ @confirmed
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,59 @@
1
+ module Theme
2
+ module Forms
3
+ class Select < ShopifyCli::Form
4
+ attr_accessor :theme
5
+ flag_arguments :root, :title, :exclude_roles, :include_foreign_developments
6
+
7
+ def ask
8
+ self.theme = CLI::UI::Prompt.ask(title, allow_empty: false) do |handler|
9
+ themes.each do |theme|
10
+ next if exclude_roles&.include?(theme.role)
11
+ next if !include_foreign_developments && theme.foreign_development?
12
+ handler.option("#{theme.name} #{theme_tags(theme)}") { theme }
13
+ end
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def themes
20
+ @themes ||= ShopifyCli::Theme::Theme.all(@ctx, root: root)
21
+ .sort_by { |theme| theme_sort_order(theme) }
22
+ end
23
+
24
+ def theme_sort_order(theme)
25
+ case theme.role
26
+ when "live"
27
+ 0
28
+ when "unpublished"
29
+ 1
30
+ when "development"
31
+ 2
32
+ else
33
+ 3
34
+ end
35
+ end
36
+
37
+ def theme_tags(theme)
38
+ color = case theme.role
39
+ when "live"
40
+ "green"
41
+ when "unpublished"
42
+ "yellow"
43
+ when "development"
44
+ "blue"
45
+ else
46
+ "grey"
47
+ end
48
+
49
+ tags = ["{{#{color}:[#{theme.role}]}}"]
50
+
51
+ if theme.current_development?
52
+ tags << "{{cyan:[yours]}}}}"
53
+ end
54
+
55
+ tags.join(" ")
56
+ end
57
+ end
58
+ end
59
+ end