shopify-cli 2.15.0 → 2.15.3

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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/stale.yml +7 -2
  3. data/.vscode/settings.json +1 -2
  4. data/CHANGELOG.md +70 -19
  5. data/Gemfile +1 -0
  6. data/Gemfile.lock +39 -7
  7. data/Rakefile +48 -0
  8. data/ext/javy/hashes/javy-arm-macos-v0.3.0.gz.sha256 +1 -0
  9. data/ext/javy/hashes/javy-x86_64-linux-v0.3.0.gz.sha256 +1 -0
  10. data/ext/javy/hashes/javy-x86_64-macos-v0.3.0.gz.sha256 +1 -0
  11. data/ext/javy/hashes/javy-x86_64-windows-v0.3.0.gz.sha256 +1 -0
  12. data/ext/javy/version +1 -1
  13. data/ext/shopify-extensions/version +1 -1
  14. data/lib/project_types/extension/commands/check.rb +6 -1
  15. data/lib/project_types/extension/forms/questions/ask_template.rb +5 -8
  16. data/lib/project_types/extension/messages/messages.rb +11 -3
  17. data/lib/project_types/extension/models/development_server_requirements.rb +14 -7
  18. data/lib/project_types/extension/models/server_config/root.rb +2 -0
  19. data/lib/project_types/extension/models/specification_handlers/beacon_extension.rb +57 -0
  20. data/lib/project_types/extension/models/specification_handlers/beacon_extension_utils/script_config.rb +33 -0
  21. data/lib/project_types/extension/models/specification_handlers/beacon_extension_utils/script_config_repository.rb +75 -0
  22. data/lib/project_types/extension/models/specification_handlers/checkout_ui_extension.rb +16 -1
  23. data/lib/project_types/extension/tasks/configure_options.rb +2 -1
  24. data/lib/project_types/extension/tasks/convert_server_config.rb +13 -2
  25. data/lib/project_types/extension/tasks/merge_server_config.rb +5 -2
  26. data/lib/project_types/script/cli.rb +1 -0
  27. data/lib/project_types/script/config/extension_points.yml +18 -0
  28. data/lib/project_types/script/layers/application/create_script.rb +14 -6
  29. data/lib/project_types/script/layers/infrastructure/errors.rb +17 -0
  30. data/lib/project_types/script/layers/infrastructure/languages/project_creator.rb +6 -21
  31. data/lib/project_types/script/layers/infrastructure/script_service.rb +2 -0
  32. data/lib/project_types/script/layers/infrastructure/sparse_checkout_details.rb +35 -0
  33. data/lib/project_types/script/messages/messages.rb +3 -0
  34. data/lib/project_types/script/ui/error_handler.rb +11 -0
  35. data/lib/project_types/theme/cli.rb +1 -0
  36. data/lib/project_types/theme/commands/check.rb +4 -1
  37. data/lib/project_types/theme/commands/open.rb +2 -2
  38. data/lib/project_types/theme/commands/push.rb +1 -3
  39. data/lib/project_types/theme/commands/serve.rb +1 -0
  40. data/lib/project_types/theme/commands/share.rb +56 -0
  41. data/lib/project_types/theme/messages/messages.rb +71 -11
  42. data/lib/shopify_cli/changelog.rb +148 -0
  43. data/lib/shopify_cli/command.rb +7 -0
  44. data/lib/shopify_cli/command_options/command_serve_options.rb +10 -0
  45. data/lib/shopify_cli/commands/app/serve.rb +7 -7
  46. data/lib/shopify_cli/commands/login.rb +5 -2
  47. data/lib/shopify_cli/context.rb +13 -0
  48. data/lib/shopify_cli/git.rb +36 -0
  49. data/lib/shopify_cli/identity_auth.rb +24 -4
  50. data/lib/shopify_cli/messages/messages.rb +26 -5
  51. data/lib/shopify_cli/release.rb +194 -0
  52. data/lib/shopify_cli/sed.rb +19 -0
  53. data/lib/shopify_cli/services/app/create/rails_service.rb +10 -2
  54. data/lib/shopify_cli/services/app/serve/node_service.rb +2 -25
  55. data/lib/shopify_cli/services/app/serve/php_service.rb +2 -25
  56. data/lib/shopify_cli/services/app/serve/rails_service.rb +8 -28
  57. data/lib/shopify_cli/services/app/serve/serve_service.rb +57 -0
  58. data/lib/shopify_cli/services.rb +1 -0
  59. data/lib/shopify_cli/tasks/update_dashboard_urls.rb +7 -9
  60. data/lib/shopify_cli/theme/dev_server/hot-reload.js +40 -13
  61. data/lib/shopify_cli/theme/dev_server/hot_reload/remote_file_reloader.rb +1 -1
  62. data/lib/shopify_cli/theme/dev_server/hot_reload/sections_index.rb +51 -0
  63. data/lib/shopify_cli/theme/dev_server/hot_reload.rb +6 -1
  64. data/lib/shopify_cli/theme/dev_server/local_assets.rb +1 -1
  65. data/lib/shopify_cli/theme/dev_server/remote_watcher/json_files_update_job.rb +35 -0
  66. data/lib/shopify_cli/theme/dev_server/remote_watcher.rb +44 -0
  67. data/lib/shopify_cli/theme/dev_server/watcher.rb +2 -8
  68. data/lib/shopify_cli/theme/dev_server.rb +18 -5
  69. data/lib/shopify_cli/theme/file.rb +15 -4
  70. data/lib/shopify_cli/theme/syncer/checksums.rb +60 -0
  71. data/lib/shopify_cli/theme/syncer/forms/apply_to_all.rb +39 -0
  72. data/lib/shopify_cli/theme/syncer/forms/apply_to_all_form.rb +35 -0
  73. data/lib/shopify_cli/theme/syncer/forms/base_strategy_form.rb +62 -0
  74. data/lib/shopify_cli/theme/syncer/forms/select_delete_strategy.rb +27 -0
  75. data/lib/shopify_cli/theme/syncer/forms/select_update_strategy.rb +28 -0
  76. data/lib/shopify_cli/theme/syncer/ignore_helper.rb +33 -0
  77. data/lib/shopify_cli/theme/syncer/json_delete_handler.rb +51 -0
  78. data/lib/shopify_cli/theme/syncer/json_update_handler.rb +82 -0
  79. data/lib/shopify_cli/theme/syncer/merger.rb +53 -0
  80. data/lib/shopify_cli/theme/syncer/operation.rb +1 -1
  81. data/lib/shopify_cli/theme/syncer.rb +79 -63
  82. data/lib/shopify_cli/theme/theme.rb +26 -4
  83. data/lib/shopify_cli/theme/theme_admin_api.rb +23 -8
  84. data/lib/shopify_cli/thread_pool/job.rb +10 -2
  85. data/lib/shopify_cli/thread_pool.rb +15 -3
  86. data/lib/shopify_cli/tunnel.rb +9 -0
  87. data/lib/shopify_cli/version.rb +1 -1
  88. data/shopify-cli.gemspec +3 -1
  89. data/vendor/deps/cli-ui/lib/cli/ui/os.rb +8 -0
  90. metadata +30 -3
@@ -8,8 +8,9 @@ module Theme
8
8
  Usage: {{command:%1$s theme [ %2$s ]}}
9
9
  HELP
10
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.",
12
-
11
+ ensure_user_try_this: <<~ENSURE_USER,
12
+ Check if your user is activated, has permission to edit themes at the store, and try to re-login.
13
+ ENSURE_USER
13
14
  init: {
14
15
  help: <<~HELP,
15
16
  {{command:%s theme init}}: Clones a Git repository to use as a starting point for building a new theme.
@@ -101,13 +102,14 @@ module Theme
101
102
  Usage: {{command:%s theme serve [ ROOT ]}}
102
103
 
103
104
  Options:
104
- {{command:--port=PORT}} Local port to serve theme preview from.
105
- {{command:--poll}} Force polling to detect file changes.
106
- {{command:--host=HOST}} Set which network interface the web server listens on. The default value is 127.0.0.1.
107
- {{command:--live-reload=MODE}} The live reload mode switches the server behavior when a file is modified:
108
- - {{command:hot-reload}} Hot reloads local changes to CSS and sections (default)
109
- - {{command:full-page}} Always refreshes the entire page
110
- - {{command:off}} Deactivate live reload
105
+ {{command:--port=PORT}} Local port to serve theme preview from.
106
+ {{command:--poll}} Force polling to detect file changes.
107
+ {{command:--host=HOST}} Set which network interface the web server listens on. The default value is 127.0.0.1.
108
+ {{command:--theme-editor-sync}} Synchronize Theme Editor updates in the local theme files.
109
+ {{command:--live-reload=MODE}} The live reload mode switches the server behavior when a file is modified:
110
+ - {{command:hot-reload}} Hot reloads local changes to CSS and sections (default)
111
+ - {{command:full-page}} Always refreshes the entire page
112
+ - {{command:off}} Deactivate live reload
111
113
  HELP
112
114
  reload_mode_is_not_valid: "The live reload mode `%s` is not valid.",
113
115
  try_a_valid_reload_mode: "Try a valid live reload mode: %s.",
@@ -121,18 +123,56 @@ module Theme
121
123
  fixed: "Fixed",
122
124
  },
123
125
  },
126
+ syncer: {
127
+ forms: {
128
+ apply_to_all: {
129
+ title: "Would like apply this to all the other %s files?",
130
+ yes: "Yes",
131
+ no: "No",
132
+ },
133
+ update_strategy: {
134
+ title_context: <<~TITLE,
135
+
136
+ The local file {{command:%s}} is different from the remote version in the development theme."
137
+ TITLE
138
+ title_question: "What would you like to do?",
139
+ keep_remote: "Keep the remote version",
140
+ keep_local: "Keep the local version",
141
+ union_merge: "Merge files (it may break the local file)",
142
+ exit: "Exit",
143
+ },
144
+ delete_strategy: {
145
+ title_context: <<~TITLE,
146
+
147
+ The local file {{command:%s}} has been recently removed, but it's present on your remote development theme.",
148
+ TITLE
149
+ title_question: "What would you like to do?",
150
+ delete: "Delete permanently",
151
+ restore: "Restore with the remote version",
152
+ exit: "Exit",
153
+ },
154
+ },
155
+ },
124
156
  error: {
125
157
  address_binding_error: "Couldn't bind to localhost."\
126
158
  " To serve your theme, set a different address with {{command:%s theme serve --host=<address>}}",
159
+ invalid_subdirectory: <<~MESSAGE,
160
+ The presence of %s in the directory structure isn't supported.
161
+
162
+ Move any files to a parent folder, then delete unsupported subdirectories.
163
+
164
+ • Required directory structure: https://shopify.dev/themes/architecture#directory-structure-and-component-types
165
+ MESSAGE
127
166
  },
128
167
  serving: <<~SERVING,
129
168
 
130
169
  Serving %s
131
170
 
132
171
  SERVING
172
+ download_changes: ", and use 'theme pull' to get the changes",
133
173
  customize_or_preview: <<~CUSTOMIZE_OR_PREVIEW,
134
174
 
135
- Customize this theme in the Theme Editor:
175
+ Customize this theme in the Theme Editor%s:
136
176
  {{green:%s}}
137
177
 
138
178
  Share this theme preview:
@@ -142,7 +182,7 @@ module Theme
142
182
  CUSTOMIZE_OR_PREVIEW
143
183
  ensure_user: <<~ENSURE_USER,
144
184
  You are not authorized to edit themes on %s.
145
- Make sure you are a user of that store, and allowed to edit themes.
185
+ Check if your user is activated, has permission to edit themes at the store, and try to re-login.
146
186
  ENSURE_USER
147
187
  address_already_in_use: "The address \"%s\" is already in use.",
148
188
  try_port_option: "Use the --port=PORT option to serve the theme in a different port.",
@@ -152,6 +192,7 @@ module Theme
152
192
  Check your theme for errors, suggestions, and best practices.
153
193
  Usage: {{command:%s check}}
154
194
  HELP
195
+ error: "Theme check failed with error:\n%s",
155
196
  },
156
197
  delete: {
157
198
  help: <<~HELP,
@@ -223,6 +264,9 @@ module Theme
223
264
  details: <<~DETAILS,
224
265
  {{*}} {{bold:%s}}
225
266
 
267
+ Preview your theme:
268
+ {{green:%s}}
269
+
226
270
  Customize your theme in the Theme Editor:
227
271
  {{green:%s}}
228
272
 
@@ -246,6 +290,22 @@ module Theme
246
290
  Usage: {{command:%s theme list}}
247
291
  HELP
248
292
  },
293
+ share: {
294
+ help: <<~HELP,
295
+ {{command:%s theme share}}: Creates a shareable, unpublished, and new theme on your theme library with a randomized name.
296
+ Works like an alias to {{command:theme push -u -t=RANDOMIZED_NAME}}.
297
+
298
+ Usage: {{command:%s theme share [ ROOT ]}}
299
+ HELP
300
+ done: <<~DONE,
301
+ {{green:The {{bold:%s}} theme was pushed successfully}}
302
+
303
+ {{info:Share your theme preview:}}
304
+ {{underline:%s}}
305
+
306
+ DONE
307
+ upload: "Pushing theme files to %s (#%s) on %s",
308
+ },
249
309
  },
250
310
  }.freeze
251
311
  end
@@ -0,0 +1,148 @@
1
+ require "date"
2
+ require "shopify_cli/sed"
3
+ require "octokit"
4
+
5
+ module ShopifyCLI
6
+ class Changelog
7
+ CHANGELOG_FILE = File.join(ShopifyCLI::ROOT, "CHANGELOG.md")
8
+ CHANGE_CATEGORIES = %w(Added Changed Deprecated Removed Fixed Security)
9
+
10
+ def initialize
11
+ load(File.read(CHANGELOG_FILE))
12
+ end
13
+
14
+ def update_version!(new_version)
15
+ changes[new_version] = changes["Unreleased"]
16
+ changes[new_version][:date] = Date.today.iso8601
17
+ changes["Unreleased"] = { changes: [], date: nil }
18
+ save!
19
+ end
20
+
21
+ def update!
22
+ pr = pr_for_current_branch
23
+ category = CLI::UI::Prompt.ask("What type of change?", options: CHANGE_CATEGORIES)
24
+ add_change(category, { pr_id: pr.number, desc: pr.title })
25
+ save!
26
+ end
27
+
28
+ def release_notes(version)
29
+ changes[version][:changes].map do |change_category, changes|
30
+ <<~CHANGES
31
+ ### #{change_category}
32
+ #{changes.map { |change| entry(**change) }.join("\n")}
33
+ CHANGES
34
+ end.join("\n")
35
+ end
36
+
37
+ def add_change(category, change)
38
+ changes["Unreleased"][:changes][category] << change
39
+ end
40
+
41
+ def entry(pr_id:, desc:)
42
+ "* [##{pr_id}](https://github.com/Shopify/shopify-cli/pull/#{pr_id}): #{desc}"
43
+ end
44
+
45
+ def full_contents
46
+ sorted_changes = changes.each_key.sort_by do |change|
47
+ if change == "Unreleased"
48
+ [Float::INFINITY] * 3 # end of the list
49
+ else
50
+ major, minor, patch = change.split(".").map(&:to_i)
51
+ [major, minor, patch]
52
+ end
53
+ end.reverse
54
+ [
55
+ heading,
56
+ *sorted_changes.each.map { |version| release_notes_with_header(version) }.join,
57
+ remainder,
58
+ ].map { |section| section.chomp << "\n" }.join
59
+ end
60
+
61
+ def save!
62
+ File.write(CHANGELOG_FILE, full_contents)
63
+ end
64
+
65
+ private
66
+
67
+ attr_reader :heading, :remainder
68
+
69
+ def release_notes_with_header(version)
70
+ header_line =
71
+ if version == "Unreleased"
72
+ "[Unreleased]"
73
+ else
74
+ date = changes[version][:date]
75
+ "Version #{version}#{" - #{date}" if date}"
76
+ end
77
+
78
+ [
79
+ "## #{header_line}",
80
+ release_notes(version),
81
+ ].reject(&:empty?).map { |section| section.chomp << "\n\n" }.join
82
+ end
83
+
84
+ def changes
85
+ @changes ||= Hash.new do |h, k|
86
+ h[k] = {
87
+ date: nil,
88
+ changes: Hash.new do |h2, k2|
89
+ h2[k2] = []
90
+ end,
91
+ }
92
+ end
93
+ end
94
+
95
+ def load(log)
96
+ state = :initial
97
+ change_category = nil
98
+ current_version = nil
99
+ @heading = ""
100
+ @remainder = ""
101
+ log.each_line do |line|
102
+ case state
103
+ when :initial
104
+ if line.chomp == "\#\# [Unreleased]"
105
+ state = :unreleased
106
+ current_version = "Unreleased"
107
+ # Ensure Unreleased changeset exists even if no changes have happened yet
108
+ changes["Unreleased"]
109
+ else
110
+ @heading << line
111
+ end
112
+ when :unreleased, :prior_versions
113
+ if /\A\#\#\# (?<category>\w+)/ =~ line
114
+ change_category = category
115
+ elsif %r{\A\* \[\#(?<id>\d+)\]\(https://github.com/Shopify/shopify-cli/pull/\k<id>\): (?<desc>.+)\n} =~ line
116
+ changes[current_version][:changes][change_category] << { pr_id: id, desc: desc }
117
+ elsif /\A\#\# Version (?<version>\d+\.\d+\.\d+)( - (?<date>\d{4}-\d{2}-\d{2}))?/ =~ line
118
+ current_version = version
119
+ state = :prior_versions
120
+ major, minor, _patch = current_version.split(".")
121
+ if major.to_i <= 2 && minor.to_i < 7
122
+ # Changelog starts to become irregular in 2.6.x
123
+ state = :finished
124
+ end
125
+ changes[current_version][:date] = date unless state == :finished
126
+ elsif !line.match?(/\s*\n/)
127
+ raise "Unrecognized line: #{line.inspect}"
128
+ end
129
+ end
130
+ @remainder << line if state == :finished
131
+ end
132
+ end
133
+
134
+ def pr_for_current_branch
135
+ current_branch = %x(git branch --show-current).chomp
136
+ search_term = "repo:Shopify/shopify-cli is:pr is:open head:#{current_branch}"
137
+ results = Octokit::Client.new.search_issues(search_term)
138
+ case results.total_count
139
+ when 0
140
+ raise "PR not opened yet!"
141
+ when (2..)
142
+ raise "Multiple open PRs, not sure which one to use for changelog!"
143
+ end
144
+
145
+ results.items.first
146
+ end
147
+ end
148
+ end
@@ -36,6 +36,13 @@ module ShopifyCLI
36
36
  end
37
37
  rescue OptionParser::InvalidOption => error
38
38
  arg = error.args.first
39
+ store_name = arg.match(/\A--(?<store_name>.*\.myshopify\.com)\z/)&.[](:store_name)
40
+ if store_name && !arg.match?(/\A--(store|shop)=/)
41
+ # Sometimes it may look like --invalidoption=https://storename.myshopify.com
42
+ store_name = store_name.sub(%r{\A(.*=)?(https?://)?}, "")
43
+ raise ShopifyCLI::Abort,
44
+ @ctx.message("core.errors.option_parser.invalid_option_store_equals", arg, store_name)
45
+ end
39
46
  raise ShopifyCLI::Abort, @ctx.message("core.errors.option_parser.invalid_option", arg)
40
47
  rescue OptionParser::MissingArgument => error
41
48
  arg = error.args.first
@@ -20,6 +20,10 @@ module ShopifyCLI
20
20
  end
21
21
  host
22
22
  end
23
+
24
+ def no_update
25
+ options.flags[:no_update] || false
26
+ end
23
27
  end
24
28
  end
25
29
 
@@ -37,6 +41,12 @@ module ShopifyCLI
37
41
  parser.on("--port=PORT") { |port| flags[:port] = port }
38
42
  end
39
43
  end
44
+
45
+ def parse_no_update_option
46
+ options do |parser, flags|
47
+ parser.on("--no-update") { flags[:no_update] = true }
48
+ end
49
+ end
40
50
  end
41
51
  end
42
52
  end
@@ -8,12 +8,9 @@ module ShopifyCLI
8
8
 
9
9
  recommend_default_ruby_range
10
10
 
11
- options do |parser, flags|
12
- parser.on("--host=HOST") do |h|
13
- flags[:host] = h.gsub('"', "")
14
- end
15
- parser.on("--port=PORT") { |port| flags[:port] = port }
16
- end
11
+ parse_host_option
12
+ parse_port_option
13
+ parse_no_update_option
17
14
 
18
15
  def call(*)
19
16
  case detect_app
@@ -21,18 +18,21 @@ module ShopifyCLI
21
18
  Services::App::Serve::RailsService.call(
22
19
  host: host,
23
20
  port: port,
21
+ no_update: no_update,
24
22
  context: @ctx
25
23
  )
26
24
  when :node
27
25
  Services::App::Serve::NodeService.call(
28
26
  host: host,
29
27
  port: port,
28
+ no_update: no_update,
30
29
  context: @ctx
31
30
  )
32
31
  when :php
33
32
  Services::App::Serve::PHPService.call(
34
33
  host: host,
35
34
  port: port,
35
+ no_update: no_update,
36
36
  context: @ctx
37
37
  )
38
38
  end
@@ -43,7 +43,7 @@ module ShopifyCLI
43
43
  end
44
44
 
45
45
  def self.extended_help
46
- ShopifyCLI::Context.message("app.core.serve.extended_help")
46
+ ShopifyCLI::Context.message("core.app.serve.extended_help")
47
47
  end
48
48
  end
49
49
  end
@@ -30,7 +30,7 @@ module ShopifyCLI
30
30
  if @ctx.ci? && (password = options.flags[:password] || @ctx.getenv("SHOPIFY_PASSWORD"))
31
31
  ShopifyCLI::DB.set(shopify_exchange_token: password)
32
32
  else
33
- IdentityAuth.new(ctx: @ctx).authenticate
33
+ IdentityAuth.new(ctx: @ctx).authenticate(spinner: true)
34
34
  org = select_organization
35
35
  ShopifyCLI::DB.set(organization_id: org["id"].to_i) unless org.nil?
36
36
  Whoami.call([], "whoami")
@@ -75,7 +75,10 @@ module ShopifyCLI
75
75
  private
76
76
 
77
77
  def select_organization
78
- organizations = ShopifyCLI::PartnersAPI::Organizations.fetch_all(@ctx)
78
+ organizations = []
79
+ CLI::UI::Spinner.spin(@ctx.message("core.login.spinner.loading_organizations")) do
80
+ organizations = ShopifyCLI::PartnersAPI::Organizations.fetch_all(@ctx)
81
+ end
79
82
 
80
83
  if organizations.count == 0
81
84
  nil
@@ -4,6 +4,7 @@ require "fileutils"
4
4
  require "rbconfig"
5
5
  require "net/http"
6
6
  require "json"
7
+ require "bundler"
7
8
 
8
9
  module ShopifyCLI
9
10
  ##
@@ -642,6 +643,18 @@ module ShopifyCLI
642
643
  end
643
644
  end
644
645
 
646
+ # Uses bundle to grab the version of a gem
647
+ #
648
+ # #### Parameters
649
+ # - gem: the name of the gem to check
650
+ #
651
+ # #### Returns
652
+ # - version: a Semantic::Version object with the gem version
653
+ def ruby_gem_version(gem)
654
+ version = Bundler.load.specs.find { |s| s.name == gem }.version
655
+ ::Semantic::Version.new(version.to_s)
656
+ end
657
+
645
658
  private
646
659
 
647
660
  def ctx_path(fname)
@@ -102,6 +102,42 @@ module ShopifyCLI
102
102
  branches
103
103
  end
104
104
 
105
+ ##
106
+ # Run git three-way file merge (it doesn't require an initialized git repository)
107
+ #
108
+ # #### Parameters
109
+ #
110
+ # * `current_file - string path of the current file
111
+ # * `base_file` - string path of the base file
112
+ # * `other_file` - string path of the other file
113
+ # * `opts` - list of "git merge-file" options. Valid values:
114
+ # - "-q" - do not warn about conflicts
115
+ # - "--diff3" - show conflicts
116
+ # - "--ours" - resolve conflicts favoring lines from `current_file`
117
+ # - "--theirs" - resolve conflicts favoring lines from `other_file`
118
+ # - "--union" - resolve conflicts favoring lines from both files
119
+ # - "-p" - send results to standard output instead of
120
+ # overwriting the `current_file`
121
+ # * `ctx` - the current running context of your command, defaults to a new context
122
+ #
123
+ # #### Returns
124
+ #
125
+ # * standard output from git
126
+ #
127
+ # #### Example
128
+ #
129
+ # output = ShopifyCLI::Git.merge_file(current_file, base_file, other_file, opts, ctx: ctx)
130
+ #
131
+ def merge_file(current_file, base_file, other_file, opts = [], ctx: Context.new)
132
+ output, status = ctx.capture2e("git", "merge-file", current_file, base_file, other_file, *opts)
133
+
134
+ unless status.success?
135
+ ctx.abort(ctx.message("core.git.error.merge_failed"))
136
+ end
137
+
138
+ output
139
+ end
140
+
105
141
  ##
106
142
  # will initialize a new repo in the current directory. This will output
107
143
  # if it was successful or not.
@@ -56,8 +56,10 @@ module ShopifyCLI
56
56
 
57
57
  attr_accessor :response_query
58
58
 
59
- def authenticate
60
- return if refresh_exchange_tokens || refresh_access_tokens
59
+ def authenticate(spinner: false)
60
+ return if with_spinner(spinner, ctx.message("core.login.spinner.initiating")) do
61
+ attempt_reauthenticate
62
+ end
61
63
 
62
64
  initiate_authentication
63
65
 
@@ -66,7 +68,21 @@ module ShopifyCLI
66
68
  rescue IdentityAuth::Timeout => e
67
69
  ctx.abort(e.message)
68
70
  end
69
- request_exchange_tokens
71
+ with_spinner(spinner, ctx.message("core.login.spinner.finalizing")) do
72
+ request_exchange_tokens
73
+ end
74
+ end
75
+
76
+ def with_spinner(spinner, message, &block)
77
+ result = nil
78
+ if spinner
79
+ CLI::UI::Spinner.spin(message) do
80
+ result = block.call
81
+ end
82
+ else
83
+ result = block.call
84
+ end
85
+ result
70
86
  end
71
87
 
72
88
  def fetch_or_auth_partners_token
@@ -100,10 +116,14 @@ module ShopifyCLI
100
116
  end
101
117
 
102
118
  def reauthenticate
103
- return if refresh_exchange_tokens || refresh_access_tokens
119
+ return if attempt_reauthenticate
104
120
  ctx.abort(ctx.message("core.identity_auth.error.reauthenticate", ShopifyCLI::TOOL_NAME))
105
121
  end
106
122
 
123
+ def attempt_reauthenticate
124
+ refresh_exchange_tokens || refresh_access_tokens
125
+ end
126
+
107
127
  def code_challenge
108
128
  @code_challenge ||= Base64.urlsafe_encode64(
109
129
  OpenSSL::Digest::SHA256.digest(code_verifier),
@@ -18,8 +18,15 @@ module ShopifyCLI
18
18
  missing_node: "Node.js is required to continue. Install Node.js here: https://nodejs.org/en/download.",
19
19
  missing_npm: "npm is required to continue. Install npm here: https://www.npmjs.com/get-npm.",
20
20
  missing_ruby: "Ruby is required to continue. Install Ruby here: https://www.ruby-lang.org/en/downloads.",
21
+ bundle_info_failure: "Error getting version for %s gem",
21
22
  option_parser: {
22
23
  invalid_option: "The option {{command:%s}} is not supported.",
24
+ invalid_option_store_equals: <<~MESSAGE,
25
+ The option {{command:%s}} isn't recognized.
26
+
27
+ Try this:
28
+ {{command:--store=%s}}.
29
+ MESSAGE
23
30
  missing_argument: "The required argument {{command:%s}} is missing.",
24
31
  },
25
32
  },
@@ -257,6 +264,7 @@ module ShopifyCLI
257
264
  {{bold:Options:}}
258
265
  {{cyan:--host=HOST}}: Bypass running tunnel and use custom host. HOST must be HTTPS url.
259
266
  {{cyan:--port=PORT}}: Use custom port.
267
+ {{cyan:--no-update}}: Skips the dashboard URL update step
260
268
  HELP
261
269
  open_info: <<~MESSAGE,
262
270
  {{*}} To install and start using your app, open this URL in your browser:
@@ -277,6 +285,13 @@ module ShopifyCLI
277
285
  },
278
286
  extension: {
279
287
  push: {
288
+ beacon_extension: {
289
+ error: {
290
+ file_read_error: "There was a problem reading %s",
291
+ missing_config_key_error: "Configuration is missing key: %s",
292
+ invalid_config_value_error: "Configuration value is invalid: %s",
293
+ },
294
+ },
280
295
  checkout_ui_extension: {
281
296
  localization: {
282
297
  error: {
@@ -288,6 +303,7 @@ module ShopifyCLI
288
303
  invalid_file_extension: "Invalid locale filename: `%s`; only .json files are allowed.",
289
304
  invalid_locale_code: "Invalid locale filename: `%s`; locale code should be 2 or 3 letters,"\
290
305
  " optionally followed by a two-letter region code, e.g. `fr-CA`.",
306
+ invalid_file_encoding: "Invalid file encoding for `%s`; file encoding should be UTF-8.",
291
307
  single_default_locale: "There must be one and only one locale identified as the default locale,"\
292
308
  " e.g. `en.default.json`",
293
309
  },
@@ -382,6 +398,7 @@ module ShopifyCLI
382
398
  sparse_checkout_not_set: "Sparse checkout set command failed.",
383
399
  pull_failed: "Pull failed.",
384
400
  pull_failed_bad_branch: "Pull failed. Branch %s cannot be found. Check the branch name and try again.",
401
+ merge_failed: "The file %s merge failed.",
385
402
  },
386
403
 
387
404
  cloning: "Cloning %s into %s…",
@@ -432,7 +449,7 @@ module ShopifyCLI
432
449
  login: {
433
450
  help: <<~HELP,
434
451
  Log in to the Shopify CLI by authenticating with a store or partner organization
435
- Usage: {{command:%s login [--store=STORE]}}
452
+ Usage: {{command:%s login [--store STORE]}}
436
453
  HELP
437
454
  invalid_shop: <<~MESSAGE,
438
455
  Invalid store provided (%s). Please provide the store in the following format: my-store.myshopify.com
@@ -440,6 +457,11 @@ module ShopifyCLI
440
457
  shop_prompt: <<~PROMPT,
441
458
  What store are you connecting to? (e.g. my-store.myshopify.com; do {{bold:NOT}} include protocol part, e.g., https://)
442
459
  PROMPT
460
+ spinner: {
461
+ initiating: "Initiating authentication",
462
+ finalizing: "Finalizing authentication",
463
+ loading_organizations: "Loading available partner organizations",
464
+ },
443
465
  },
444
466
 
445
467
  logout: {
@@ -454,7 +476,7 @@ module ShopifyCLI
454
476
  switch: {
455
477
  help: <<~HELP,
456
478
  Switch between development stores in your partner organization
457
- Usage: {{command:%s switch [--store=STORE]}}
479
+ Usage: {{command:%s switch [--store STORE]}}
458
480
  HELP
459
481
  disabled_as_shopify_org: "Can't switch development stores logged in as {{green:Shopify partners org}}",
460
482
  success: "Switched development store to {{green:%s}}",
@@ -567,7 +589,7 @@ module ShopifyCLI
567
589
  HELP
568
590
 
569
591
  error: {
570
- no_shop: "No store found. Please run {{command:%s login --store=STORE}} to login to a specific store",
592
+ no_shop: "No store found. Please run {{command:%s login --store STORE}} to login to a specific store",
571
593
  },
572
594
 
573
595
  customer: {
@@ -707,7 +729,6 @@ module ShopifyCLI
707
729
  updated: "{{v}} Whitelist URLS updated in Partners Dashboard}}",
708
730
  update_error:
709
731
  "{{x}} error: For authentication issues, run {{command:%s logout}} to clear invalid credentials",
710
- update_prompt: "Do you want to update your application url?",
711
732
  },
712
733
  select_org_and_shop: {
713
734
  authentication_issue: "For authentication issues, run {{command:%s logout}} to clear invalid credentials",
@@ -803,7 +824,7 @@ module ShopifyCLI
803
824
  not_logged_in: <<~MESSAGE,
804
825
  It doesn't appear that you're logged in. You must log into a partner organization or a store staff account.
805
826
 
806
- If trying to log into a store staff account, please use {{command:%s login --store=STORE}} to log in.
827
+ If trying to log into a store staff account, please use {{command:%s login --store STORE}} to log in.
807
828
  MESSAGE
808
829
  logged_in_shop_only: <<~MESSAGE,
809
830
  Logged into store {{green:%s}} as staff (no partner organizations available for this login)