shopify-cli 2.11.2 → 2.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +5 -0
  3. data/.github/PULL_REQUEST_TEMPLATE.md +1 -1
  4. data/.github/workflows/shopify.yml +2 -1
  5. data/.rubocop.yml +1 -1
  6. data/.ruby-version +1 -1
  7. data/CHANGELOG.md +44 -1
  8. data/Gemfile.lock +18 -18
  9. data/Rakefile +16 -0
  10. data/bin/shopify +12 -8
  11. data/dev.yml +1 -1
  12. data/docs/users/installation.md +1 -44
  13. data/ext/javy/hashes/javy-arm-macos-v0.2.0.gz.sha256 +1 -0
  14. data/ext/javy/hashes/javy-arm-macos-v0.2.1.gz.sha256 +1 -0
  15. data/ext/javy/hashes/javy-x86_64-linux-v0.2.0.gz.sha256 +1 -0
  16. data/ext/javy/hashes/javy-x86_64-linux-v0.2.1.gz.sha256 +1 -0
  17. data/ext/javy/hashes/javy-x86_64-macos-v0.2.0.gz.sha256 +1 -0
  18. data/ext/javy/hashes/javy-x86_64-macos-v0.2.1.gz.sha256 +1 -0
  19. data/ext/javy/hashes/javy-x86_64-windows-v0.2.0.gz.sha256 +1 -0
  20. data/ext/javy/hashes/javy-x86_64-windows-v0.2.1.gz.sha256 +1 -0
  21. data/ext/javy/version +1 -1
  22. data/lib/project_types/extension/features/argo_setup_steps.rb +4 -6
  23. data/lib/project_types/extension/models/npm_package.rb +19 -1
  24. data/lib/project_types/extension/models/server_config/development_renderer.rb +4 -3
  25. data/lib/project_types/extension/models/specification_handlers/checkout_ui_extension.rb +114 -0
  26. data/lib/project_types/extension/tasks/configure_features.rb +15 -2
  27. data/lib/project_types/extension/tasks/convert_server_config.rb +2 -1
  28. data/lib/project_types/script/cli.rb +2 -4
  29. data/lib/project_types/script/commands/create.rb +5 -5
  30. data/lib/project_types/script/commands/push.rb +4 -6
  31. data/lib/project_types/script/config/extension_points.yml +0 -10
  32. data/lib/project_types/script/errors.rb +1 -1
  33. data/lib/project_types/script/forms/create.rb +7 -20
  34. data/lib/project_types/script/layers/application/build_script.rb +9 -26
  35. data/lib/project_types/script/layers/application/connect_app.rb +3 -2
  36. data/lib/project_types/script/layers/application/create_script.rb +9 -10
  37. data/lib/project_types/script/layers/application/project_dependencies.rb +12 -14
  38. data/lib/project_types/script/layers/application/push_script.rb +14 -10
  39. data/lib/project_types/script/layers/domain/errors.rb +3 -3
  40. data/lib/project_types/script/layers/domain/push_package.rb +6 -0
  41. data/lib/project_types/script/layers/domain/script_config.rb +2 -4
  42. data/lib/project_types/script/layers/domain/script_project.rb +3 -2
  43. data/lib/project_types/script/layers/infrastructure/errors.rb +11 -0
  44. data/lib/project_types/script/layers/infrastructure/languages/project_creator.rb +0 -16
  45. data/lib/project_types/script/layers/infrastructure/languages/task_runner.rb +0 -1
  46. data/lib/project_types/script/layers/infrastructure/languages/tool_version_checker.rb +26 -0
  47. data/lib/project_types/script/layers/infrastructure/languages/typescript_project_creator.rb +22 -10
  48. data/lib/project_types/script/layers/infrastructure/languages/typescript_task_runner.rb +32 -29
  49. data/lib/project_types/script/layers/infrastructure/languages/wasm_project_creator.rb +0 -3
  50. data/lib/project_types/script/layers/infrastructure/languages/wasm_task_runner.rb +1 -1
  51. data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +3 -21
  52. data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +14 -26
  53. data/lib/project_types/script/layers/infrastructure/script_service.rb +4 -2
  54. data/lib/project_types/script/loaders/project.rb +8 -7
  55. data/lib/project_types/script/messages/messages.rb +22 -21
  56. data/lib/project_types/script/ui/error_handler.rb +17 -4
  57. data/lib/project_types/script/ui/strict_spinner.rb +4 -6
  58. data/lib/project_types/theme/cli.rb +2 -0
  59. data/lib/project_types/theme/commands/common/root_helper.rb +71 -0
  60. data/lib/project_types/theme/commands/init.rb +2 -0
  61. data/lib/project_types/theme/commands/list.rb +34 -0
  62. data/lib/project_types/theme/commands/open.rb +65 -0
  63. data/lib/project_types/theme/commands/package.rb +1 -0
  64. data/lib/project_types/theme/commands/pull.rb +18 -10
  65. data/lib/project_types/theme/commands/push.rb +17 -9
  66. data/lib/project_types/theme/commands/serve.rb +6 -2
  67. data/lib/project_types/theme/conversions/base_glob.rb +50 -0
  68. data/lib/project_types/theme/conversions/ignore_glob.rb +15 -0
  69. data/lib/project_types/theme/conversions/include_glob.rb +15 -0
  70. data/lib/project_types/theme/forms/select.rb +11 -39
  71. data/lib/project_types/theme/messages/messages.rb +38 -7
  72. data/lib/project_types/theme/presenters/theme_presenter.rb +48 -0
  73. data/lib/project_types/theme/presenters/themes_presenter.rb +32 -0
  74. data/lib/shopify_cli/api.rb +1 -1
  75. data/lib/shopify_cli/commands/app/create/node.rb +1 -0
  76. data/lib/shopify_cli/commands/app/create/php.rb +1 -0
  77. data/lib/shopify_cli/commands/app/create/rails.rb +1 -0
  78. data/lib/shopify_cli/commands/app/deploy.rb +1 -1
  79. data/lib/shopify_cli/constants.rb +2 -2
  80. data/lib/shopify_cli/context.rb +13 -15
  81. data/lib/shopify_cli/core/entry_point.rb +1 -1
  82. data/lib/shopify_cli/core/monorail.rb +14 -6
  83. data/lib/shopify_cli/environment.rb +6 -0
  84. data/lib/shopify_cli/exception_reporter.rb +2 -0
  85. data/lib/shopify_cli/git.rb +9 -1
  86. data/lib/shopify_cli/messages/messages.rb +21 -1
  87. data/lib/shopify_cli/packager.rb +1 -1
  88. data/lib/shopify_cli/result.rb +14 -0
  89. data/lib/shopify_cli/services/app/create/rails_service.rb +1 -1
  90. data/lib/shopify_cli/tasks/ensure_git_dependency.rb +14 -0
  91. data/lib/shopify_cli/tasks.rb +1 -0
  92. data/lib/shopify_cli/theme/dev_server/hot_reload/remote_file_reloader.rb +5 -5
  93. data/lib/shopify_cli/theme/dev_server/proxy.rb +14 -2
  94. data/lib/shopify_cli/theme/dev_server/watcher.rb +10 -2
  95. data/lib/shopify_cli/theme/development_theme.rb +2 -5
  96. data/lib/shopify_cli/theme/include_filter.rb +4 -2
  97. data/lib/shopify_cli/theme/syncer.rb +40 -36
  98. data/lib/shopify_cli/theme/theme.rb +16 -27
  99. data/lib/shopify_cli/theme/theme_admin_api.rb +71 -0
  100. data/lib/shopify_cli/transform_data_structure.rb +3 -2
  101. data/lib/shopify_cli/version.rb +1 -1
  102. data/shipit.yml +3 -0
  103. data/shopify-cli.gemspec +9 -2
  104. data/shopify-dev +9 -11
  105. metadata +26 -8
  106. data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_project_creator.rb +0 -25
  107. data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_task_runner.rb +0 -98
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "shopify_cli/theme/theme"
4
+ require "shopify_cli/theme/development_theme"
5
+
6
+ module Theme
7
+ class Command
8
+ class Open < ShopifyCLI::Command::SubCommand
9
+ recommend_default_ruby_range
10
+
11
+ options do |parser, flags|
12
+ parser.on("-t", "--theme=NAME_OR_ID") { |theme| flags[:theme] = theme }
13
+ parser.on("-l", "--live") { flags[:live] = true }
14
+ parser.on("-d", "--development") { flags[:development] = true }
15
+ end
16
+
17
+ def call(_args, _name)
18
+ theme = find_theme(**options.flags)
19
+
20
+ @ctx.puts(@ctx.message("theme.open.details", theme.name, theme.editor_url))
21
+ @ctx.open_url!(theme.preview_url)
22
+ end
23
+
24
+ def self.help
25
+ ShopifyCLI::Context.message("theme.open.help", ShopifyCLI::TOOL_NAME, ShopifyCLI::TOOL_NAME)
26
+ end
27
+
28
+ def find_theme(theme: nil, live: nil, development: nil, **_args)
29
+ return theme_by_identifier(theme) if theme
30
+ return live_theme if live
31
+ return development_theme if development
32
+
33
+ select_theme
34
+ end
35
+
36
+ def theme_by_identifier(identifier)
37
+ theme = ShopifyCLI::Theme::Theme.find_by_identifier(@ctx, identifier: identifier)
38
+ theme || not_found_error(identifier)
39
+ end
40
+
41
+ def development_theme
42
+ theme = ShopifyCLI::Theme::DevelopmentTheme.find(@ctx)
43
+ theme || not_found_error("development")
44
+ end
45
+
46
+ def live_theme
47
+ ShopifyCLI::Theme::Theme.live(@ctx)
48
+ end
49
+
50
+ def not_found_error(identifier)
51
+ @ctx.abort(@ctx.message("theme.open.theme_not_found", identifier))
52
+ end
53
+
54
+ def select_theme
55
+ form = Forms::Select.ask(
56
+ @ctx,
57
+ [],
58
+ title: @ctx.message("theme.open.select"),
59
+ root: nil
60
+ )
61
+ form&.theme
62
+ end
63
+ end
64
+ end
65
+ end
@@ -15,6 +15,7 @@ module Theme
15
15
  sections
16
16
  snippets
17
17
  templates
18
+ release-notes.md
18
19
  ]
19
20
 
20
21
  def call(args, _name)
@@ -4,41 +4,49 @@ require "shopify_cli/theme/development_theme"
4
4
  require "shopify_cli/theme/ignore_filter"
5
5
  require "shopify_cli/theme/include_filter"
6
6
  require "shopify_cli/theme/syncer"
7
+ require "project_types/theme/commands/common/root_helper"
8
+ require "project_types/theme/conversions/include_glob"
9
+ require "project_types/theme/conversions/ignore_glob"
7
10
 
8
11
  module Theme
9
12
  class Command
10
13
  class Pull < ShopifyCLI::Command::SubCommand
14
+ include Common::RootHelper
15
+
11
16
  recommend_default_ruby_range
12
17
 
13
18
  options do |parser, flags|
19
+ Conversions::IncludeGlob.register(parser)
20
+ Conversions::IgnoreGlob.register(parser)
21
+
14
22
  parser.on("-n", "--nodelete") { flags[:nodelete] = true }
15
23
  parser.on("-i", "--themeid=ID") { |theme_id| flags[:theme_id] = theme_id }
16
24
  parser.on("-t", "--theme=NAME_OR_ID") { |theme| flags[:theme] = theme }
17
25
  parser.on("-l", "--live") { flags[:live] = true }
18
26
  parser.on("-d", "--development") { flags[:development] = true }
19
- parser.on("-o", "--only=PATTERN") do |pattern|
27
+ parser.on("-o", "--only=PATTERN", Conversions::IncludeGlob) do |pattern|
20
28
  flags[:includes] ||= []
21
- flags[:includes] << pattern
29
+ flags[:includes] += pattern
22
30
  end
23
- parser.on("-x", "--ignore=PATTERN") do |pattern|
31
+ parser.on("-x", "--ignore=PATTERN", Conversions::IgnoreGlob) do |pattern|
24
32
  flags[:ignores] ||= []
25
- flags[:ignores] << pattern
33
+ flags[:ignores] += pattern
26
34
  end
27
35
  end
28
36
 
29
- def call(args, _name)
30
- root = args.first || "."
37
+ def call(_args, name)
38
+ root = root_value(options, name)
31
39
  delete = !options.flags[:nodelete]
32
40
  theme = find_theme(root, **options.flags)
33
41
  return if theme.nil?
34
42
 
35
- include_filter = ShopifyCLI::Theme::IncludeFilter.new(options.flags[:includes])
43
+ include_filter = ShopifyCLI::Theme::IncludeFilter.new(root, options.flags[:includes])
36
44
  ignore_filter = ShopifyCLI::Theme::IgnoreFilter.from_path(root)
37
45
  ignore_filter.add_patterns(options.flags[:ignores]) if options.flags[:ignores]
38
46
 
39
47
  syncer = ShopifyCLI::Theme::Syncer.new(@ctx, theme: theme,
40
- include_filter: include_filter,
41
- ignore_filter: ignore_filter)
48
+ include_filter: include_filter,
49
+ ignore_filter: ignore_filter)
42
50
  begin
43
51
  syncer.start_threads
44
52
  CLI::UI::Frame.open(@ctx.message("theme.pull.pulling", theme.name, theme.id, theme.shop)) do
@@ -75,7 +83,7 @@ module Theme
75
83
 
76
84
  if development
77
85
  dev_theme = ShopifyCLI::Theme::DevelopmentTheme.find(@ctx, root: root)
78
- return dev_theme || @ctx.abort(@ctx.message("theme.pull.theme_not_found", dev_theme.name))
86
+ return dev_theme || @ctx.abort(@ctx.message("theme.pull.theme_not_found", "development"))
79
87
  end
80
88
 
81
89
  select_theme(root)
@@ -4,13 +4,21 @@ require "shopify_cli/theme/development_theme"
4
4
  require "shopify_cli/theme/ignore_filter"
5
5
  require "shopify_cli/theme/include_filter"
6
6
  require "shopify_cli/theme/syncer"
7
+ require "project_types/theme/commands/common/root_helper"
8
+ require "project_types/theme/conversions/include_glob"
9
+ require "project_types/theme/conversions/ignore_glob"
7
10
 
8
11
  module Theme
9
12
  class Command
10
13
  class Push < ShopifyCLI::Command::SubCommand
14
+ include Common::RootHelper
15
+
11
16
  recommend_default_ruby_range
12
17
 
13
18
  options do |parser, flags|
19
+ Conversions::IncludeGlob.register(parser)
20
+ Conversions::IgnoreGlob.register(parser)
21
+
14
22
  parser.on("-n", "--nodelete") { flags[:nodelete] = true }
15
23
  parser.on("-i", "--themeid=ID") { |theme_id| flags[:theme_id] = theme_id }
16
24
  parser.on("-t", "--theme=NAME_OR_ID") { |theme| flags[:theme] = theme }
@@ -20,18 +28,18 @@ module Theme
20
28
  parser.on("-j", "--json") { flags[:json] = true }
21
29
  parser.on("-a", "--allow-live") { flags[:allow_live] = true }
22
30
  parser.on("-p", "--publish") { flags[:publish] = true }
23
- parser.on("-o", "--only=PATTERN") do |pattern|
31
+ parser.on("-o", "--only=PATTERN", Conversions::IncludeGlob) do |pattern|
24
32
  flags[:includes] ||= []
25
- flags[:includes] << pattern
33
+ flags[:includes] += pattern
26
34
  end
27
- parser.on("-x", "--ignore=PATTERN") do |pattern|
35
+ parser.on("-x", "--ignore=PATTERN", Conversions::IgnoreGlob) do |pattern|
28
36
  flags[:ignores] ||= []
29
- flags[:ignores] << pattern
37
+ flags[:ignores] += pattern
30
38
  end
31
39
  end
32
40
 
33
- def call(args, _name)
34
- root = args.first || "."
41
+ def call(_args, name)
42
+ root = root_value(options, name)
35
43
  delete = !options.flags[:nodelete]
36
44
  theme = find_theme(root, **options.flags)
37
45
  return if theme.nil?
@@ -42,13 +50,13 @@ module Theme
42
50
  return unless CLI::UI::Prompt.confirm(question)
43
51
  end
44
52
 
45
- include_filter = ShopifyCLI::Theme::IncludeFilter.new(options.flags[:includes])
53
+ include_filter = ShopifyCLI::Theme::IncludeFilter.new(root, options.flags[:includes])
46
54
  ignore_filter = ShopifyCLI::Theme::IgnoreFilter.from_path(root)
47
55
  ignore_filter.add_patterns(options.flags[:ignores]) if options.flags[:ignores]
48
56
 
49
57
  syncer = ShopifyCLI::Theme::Syncer.new(@ctx, theme: theme,
50
- include_filter: include_filter,
51
- ignore_filter: ignore_filter)
58
+ include_filter: include_filter,
59
+ ignore_filter: ignore_filter)
52
60
  begin
53
61
  syncer.start_threads
54
62
  if options.flags[:json]
@@ -1,9 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
  require "shopify_cli/theme/dev_server"
3
+ require "project_types/theme/commands/common/root_helper"
3
4
 
4
5
  module Theme
5
6
  class Command
6
7
  class Serve < ShopifyCLI::Command::SubCommand
8
+ include Common::RootHelper
9
+
7
10
  recommend_default_ruby_range
8
11
 
9
12
  DEFAULT_HTTP_HOST = "127.0.0.1"
@@ -15,10 +18,11 @@ module Theme
15
18
  parser.on("--live-reload=MODE") { |mode| flags[:mode] = as_reload_mode(mode) }
16
19
  end
17
20
 
18
- def call(*)
21
+ def call(_args, name)
22
+ root = root_value(options, name)
19
23
  flags = options.flags.dup
20
24
  host = flags[:host] || DEFAULT_HTTP_HOST
21
- ShopifyCLI::Theme::DevServer.start(@ctx, ".", host: host, **flags) do |syncer|
25
+ ShopifyCLI::Theme::DevServer.start(@ctx, root, host: host, **flags) do |syncer|
22
26
  UI::SyncProgressBar.new(syncer).progress(:upload_theme!, delay_low_priority_files: true)
23
27
  end
24
28
  rescue ShopifyCLI::Theme::DevServer::AddressBindingError
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Theme
4
+ module Conversions
5
+ class BaseGlob
6
+ class << self
7
+ def register(parser)
8
+ parser.accept(self) { |_val| convert(parser) }
9
+ end
10
+
11
+ def convert(parser)
12
+ argv = parser.default_argv
13
+ option_index = argv.index { |v| options.include?(v) }
14
+
15
+ return [] if option_index.nil?
16
+
17
+ start_index = option_index + 1
18
+ option_by_key = options_map(parser)
19
+ values = []
20
+
21
+ argv[start_index..-1].each do |value|
22
+ return values unless option_by_key[value].nil?
23
+ values << value
24
+ end
25
+
26
+ values
27
+ end
28
+
29
+ def options
30
+ raise "`#{self.class.name}#options` must be defined"
31
+ end
32
+
33
+ private
34
+
35
+ def options_map(parser)
36
+ map = {}
37
+ parser.top.list.each do |option|
38
+ map[option.short.first] = option
39
+ map[option.long.first] = option
40
+ end
41
+ map
42
+ end
43
+
44
+ def parameter?(value)
45
+ value.start_with?("-")
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_glob"
4
+
5
+ module Theme
6
+ module Conversions
7
+ class IgnoreGlob < BaseGlob
8
+ class << self
9
+ def options
10
+ %w(-x --ignore)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_glob"
4
+
5
+ module Theme
6
+ module Conversions
7
+ class IncludeGlob < BaseGlob
8
+ class << self
9
+ def options
10
+ %w(-o --only)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "project_types/theme/presenters/themes_presenter"
4
+
1
5
  module Theme
2
6
  module Forms
3
7
  class Select < ShopifyCLI::Form
@@ -6,53 +10,21 @@ module Theme
6
10
 
7
11
  def ask
8
12
  self.theme = CLI::UI::Prompt.ask(title, allow_empty: false) do |handler|
9
- themes.each do |theme|
13
+ theme_presenters.each do |presenter|
14
+ theme = presenter.theme
15
+
10
16
  next if exclude_roles&.include?(theme.role)
11
17
  next if !include_foreign_developments && theme.foreign_development?
12
- handler.option("#{theme.name} #{theme_tags(theme)}") { theme }
18
+
19
+ handler.option(presenter.to_s(:short)) { theme }
13
20
  end
14
21
  end
15
22
  end
16
23
 
17
24
  private
18
25
 
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
- "italic"
47
- end
48
-
49
- tags = ["{{#{color}:[#{theme.role}]}}"]
50
-
51
- if theme.current_development?
52
- tags << "{{cyan:[yours]}}}}"
53
- end
54
-
55
- tags.join(" ")
26
+ def theme_presenters
27
+ Theme::Presenters::ThemesPresenter.new(ctx, root).all
56
28
  end
57
29
  end
58
30
  end
@@ -7,6 +7,8 @@ module Theme
7
7
  Suite of commands for developing Shopify themes. See {{command:%1$s theme <command> --help}} for usage of each command.
8
8
  Usage: {{command:%1$s theme [ %2$s ]}}
9
9
  HELP
10
+ ensure_user_error: "You are not authorized to edit themes on %s.",
11
+ ensure_user_try_this: "Make sure you are a user of that store, and allowed to edit themes.",
10
12
 
11
13
  init: {
12
14
  help: <<~HELP,
@@ -65,8 +67,8 @@ module Theme
65
67
  {{command:-j, --json}} Output JSON instead of a UI.
66
68
  {{command:-a, --allow-live}} Allow push to a live theme.
67
69
  {{command:-p, --publish}} Publish as the live theme after uploading.
68
- {{command:-o, --only}} Upload only the specified files.
69
- {{command:-x, --ignore}} Skip uploading the specified files.
70
+ {{command:-o, --only}} Upload only the specified files (Multiple flags allowed).
71
+ {{command:-x, --ignore}} Skip uploading the specified files (Multiple flags allowed).
70
72
 
71
73
  Run without options to select theme from a list.
72
74
  HELP
@@ -87,7 +89,7 @@ module Theme
87
89
  {{info:View your theme:}}
88
90
  {{underline:%s}}
89
91
 
90
- {{info:Customize this theme in the Online Store Editor:}}
92
+ {{info:Customize this theme in the Theme Editor:}}
91
93
  {{underline:%s}}
92
94
  DONE
93
95
  name: "Theme name",
@@ -96,7 +98,7 @@ module Theme
96
98
  help: <<~HELP,
97
99
  Uploads the current theme as a development theme to the connected store, then prints theme editor and preview URLs to your terminal. While running, changes will push to the store in real time.
98
100
 
99
- Usage: {{command:%s theme serve}}
101
+ Usage: {{command:%s theme serve [ ROOT ]}}
100
102
 
101
103
  Options:
102
104
  {{command:--port=PORT}} Local port to serve theme preview from.
@@ -130,7 +132,7 @@ module Theme
130
132
  SERVING
131
133
  customize_or_preview: <<~CUSTOMIZE_OR_PREVIEW,
132
134
 
133
- Customize this theme in the Online Store Editor:
135
+ Customize this theme in the Theme Editor:
134
136
  {{green:%s}}
135
137
 
136
138
  Share this theme preview:
@@ -202,8 +204,8 @@ module Theme
202
204
  {{command:-l, --live}} Pull theme files from your remote live theme.
203
205
  {{command:-d, --development}} Pull theme files from your remote development theme.
204
206
  {{command:-n, --nodelete}} Runs the pull command without deleting local files.
205
- {{command:-o, --only}} Download only the specified files.
206
- {{command:-x, --ignore}} Skip downloading the specified files.
207
+ {{command:-o, --only}} Download only the specified files (Multiple flags allowed).
208
+ {{command:-x, --ignore}} Skip downloading the specified files (Multiple flags allowed).
207
209
 
208
210
  Run without options to select theme from a list.
209
211
  HELP
@@ -215,6 +217,35 @@ module Theme
215
217
  WARN
216
218
  theme_not_found: "Theme \"%s\" doesn't exist",
217
219
  },
220
+ open: {
221
+ select: "Select a theme to open",
222
+ theme_not_found: "Theme \"%s\" doesn't exist",
223
+ details: <<~DETAILS,
224
+ {{*}} {{bold:%s}}
225
+
226
+ Customize your theme in the Theme Editor:
227
+ {{green:%s}}
228
+
229
+ DETAILS
230
+ help: <<~HELP,
231
+ {{command:%s theme open}}: Opens the preview of your remote theme.
232
+
233
+ Usage: {{command:%s theme open}}
234
+
235
+ Options:
236
+ {{command:-t, --theme=NAME_OR_ID}} Theme ID or name of your theme.
237
+ {{command:-l, --live}} Open your live theme.
238
+ {{command:-d, --development}} Open your development theme.
239
+ HELP
240
+ },
241
+ list: {
242
+ title: "{{*}} List of {{bold:%s}} themes:",
243
+ help: <<~HELP,
244
+ {{command:%s theme list}}: Lists your remote themes.
245
+
246
+ Usage: {{command:%s theme list}}
247
+ HELP
248
+ },
218
249
  },
219
250
  }.freeze
220
251
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ module Theme
6
+ module Presenters
7
+ class ThemePresenter
8
+ extend Forwardable
9
+
10
+ COLOR_BY_ROLE = {
11
+ "live" => "green",
12
+ "unpublished" => "yellow",
13
+ "development" => "blue",
14
+ }
15
+
16
+ attr_reader :theme
17
+
18
+ def_delegators :theme, :id, :name, :role
19
+
20
+ def initialize(theme)
21
+ @theme = theme
22
+ end
23
+
24
+ def to_s(mode = :long)
25
+ case mode
26
+ when :short
27
+ "{{bold:#{name} #{theme_tags}}}"
28
+ when :long
29
+ "{{green:##{id}}} {{bold:#{name} #{theme_tags}}}"
30
+ else
31
+ inspect
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def theme_tags
38
+ tags = ["{{#{tag_color}:[#{role}]}}"]
39
+ tags << "{{cyan:[yours]}}}}" if theme.current_development?
40
+ tags.join(" ")
41
+ end
42
+
43
+ def tag_color
44
+ COLOR_BY_ROLE[role] || "italic"
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "theme_presenter"
4
+
5
+ module Theme
6
+ module Presenters
7
+ class ThemesPresenter
8
+ ORDER_BY_ROLE = %w(live unpublished development)
9
+
10
+ def initialize(ctx, root)
11
+ @ctx = ctx
12
+ @root = root
13
+ end
14
+
15
+ def all
16
+ all_themes
17
+ .sort_by { |theme| order_by_role(theme) }
18
+ .map { |theme| ThemePresenter.new(theme) }
19
+ end
20
+
21
+ private
22
+
23
+ def order_by_role(theme)
24
+ ORDER_BY_ROLE.index(theme.role) || ORDER_BY_ROLE.size
25
+ end
26
+
27
+ def all_themes
28
+ ShopifyCLI::Theme::Theme.all(@ctx, root: @root)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -85,7 +85,7 @@ module ShopifyCLI
85
85
  end
86
86
  rescue Errno::ETIMEDOUT, Timeout::Error
87
87
  ctx.debug("timeout in #{method} #{uri} with X-Request-Id: #{headers["X-Request-Id"]}")
88
- raise APIRequestTimeoutError.new("Timeout")
88
+ raise APIRequestTimeoutError, "Timeout"
89
89
  end.retry_after(APIRequestRetriableError, retries: 3) do |e|
90
90
  sleep(1) if e.is_a?(APIRequestThrottledError)
91
91
  end
@@ -4,6 +4,7 @@ module ShopifyCLI
4
4
  class Create
5
5
  class Node < ShopifyCLI::Command::AppSubCommand
6
6
  prerequisite_task :ensure_authenticated
7
+ prerequisite_task :ensure_git_dependency
7
8
 
8
9
  recommend_default_node_range
9
10
  recommend_default_ruby_range
@@ -4,6 +4,7 @@ module ShopifyCLI
4
4
  class Create
5
5
  class PHP < ShopifyCLI::Command::AppSubCommand
6
6
  prerequisite_task :ensure_authenticated
7
+ prerequisite_task :ensure_git_dependency
7
8
 
8
9
  options do |parser, flags|
9
10
  parser.on("--name=NAME") { |name| flags[:name] = name }
@@ -4,6 +4,7 @@ module ShopifyCLI
4
4
  class Create
5
5
  class Rails < ShopifyCLI::Command::AppSubCommand
6
6
  prerequisite_task :ensure_authenticated
7
+ prerequisite_task :ensure_git_dependency
7
8
 
8
9
  recommend_default_ruby_range
9
10
  recommend_default_node_range
@@ -2,7 +2,7 @@ module ShopifyCLI
2
2
  module Commands
3
3
  class App
4
4
  class Deploy < ShopifyCLI::Command::AppSubCommand
5
- subcommand :Heroku, "heroku", "shopify_cli/commands/app/deploy/heroku"
5
+ prerequisite_task :ensure_git_dependency
6
6
 
7
7
  recommend_default_ruby_range
8
8
 
@@ -61,12 +61,12 @@ module ShopifyCLI
61
61
  module SupportedVersions
62
62
  module Ruby
63
63
  FROM = "2.6.6"
64
- TO = "3.1.0"
64
+ TO = "3.2.0"
65
65
  end
66
66
 
67
67
  module Node
68
68
  FROM = "14.5.0"
69
- TO = "17.0.0"
69
+ TO = "18.0.0"
70
70
  end
71
71
  end
72
72
 
@@ -103,7 +103,7 @@ module ShopifyCLI
103
103
  # any command run by the context.
104
104
  attr_accessor :env
105
105
 
106
- def initialize(root: Dir.pwd, env: ($original_env || ENV).clone) # :nodoc:
106
+ def initialize(root: Dir.pwd, env: ($original_env || ENV).to_h) # :nodoc:
107
107
  self.root = root
108
108
  self.env = env
109
109
  end
@@ -164,7 +164,7 @@ module ShopifyCLI
164
164
 
165
165
  # will return true while tests are running, either locally or on CI
166
166
  def testing?
167
- ci? || ENV["TEST"]
167
+ ci? || ENV["SHOPIFY_CLI_TEST"]
168
168
  end
169
169
 
170
170
  ##
@@ -570,21 +570,19 @@ module ShopifyCLI
570
570
  trap("INFO", "DEFAULT")
571
571
 
572
572
  fork do
573
- begin
574
- r, w = IO.pipe
573
+ r, w = IO.pipe
574
+ @signal = false
575
+ trap("SIGINFO") do
576
+ @signal = true
577
+ w.write(0)
578
+ end
579
+ while r.read(1)
580
+ next unless @signal
575
581
  @signal = false
576
- trap("SIGINFO") do
577
- @signal = true
578
- w.write(0)
579
- end
580
- while r.read(1)
581
- next unless @signal
582
- @signal = false
583
- yield
584
- end
585
- rescue Interrupt
586
- exit(0)
582
+ yield
587
583
  end
584
+ rescue Interrupt
585
+ exit(0)
588
586
  end
589
587
  end
590
588