shopify-cli 2.12.0 → 2.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +5 -0
- data/.github/CONTRIBUTING.md +1 -1
- data/.github/PULL_REQUEST_TEMPLATE.md +1 -1
- data/.github/workflows/shopify.yml +2 -1
- data/.github/workflows/stale.yml +41 -0
- data/.rubocop.yml +1 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +36 -0
- data/Gemfile.lock +18 -18
- data/Rakefile +16 -0
- data/bin/shopify +4 -4
- data/dev.yml +1 -1
- data/ext/javy/hashes/javy-arm-macos-v0.2.0.gz.sha256 +1 -0
- data/ext/javy/hashes/javy-arm-macos-v0.2.1.gz.sha256 +1 -0
- data/ext/javy/hashes/javy-x86_64-linux-v0.2.0.gz.sha256 +1 -0
- data/ext/javy/hashes/javy-x86_64-linux-v0.2.1.gz.sha256 +1 -0
- data/ext/javy/hashes/javy-x86_64-macos-v0.2.0.gz.sha256 +1 -0
- data/ext/javy/hashes/javy-x86_64-macos-v0.2.1.gz.sha256 +1 -0
- data/ext/javy/hashes/javy-x86_64-windows-v0.2.0.gz.sha256 +1 -0
- data/ext/javy/hashes/javy-x86_64-windows-v0.2.1.gz.sha256 +1 -0
- data/ext/javy/version +1 -1
- data/lib/project_types/extension/features/argo_setup_steps.rb +4 -6
- data/lib/project_types/extension/models/npm_package.rb +19 -1
- data/lib/project_types/extension/models/server_config/development_renderer.rb +4 -3
- data/lib/project_types/extension/models/specification_handlers/checkout_ui_extension.rb +13 -0
- data/lib/project_types/extension/tasks/configure_features.rb +15 -2
- data/lib/project_types/extension/tasks/convert_server_config.rb +2 -1
- data/lib/project_types/script/cli.rb +0 -4
- data/lib/project_types/script/commands/create.rb +4 -4
- data/lib/project_types/script/config/extension_points.yml +0 -6
- data/lib/project_types/script/errors.rb +1 -1
- data/lib/project_types/script/forms/create.rb +7 -7
- data/lib/project_types/script/layers/application/build_script.rb +9 -26
- data/lib/project_types/script/layers/application/create_script.rb +9 -10
- data/lib/project_types/script/layers/application/project_dependencies.rb +12 -14
- data/lib/project_types/script/layers/application/push_script.rb +14 -10
- data/lib/project_types/script/layers/domain/errors.rb +3 -3
- data/lib/project_types/script/layers/domain/push_package.rb +6 -0
- data/lib/project_types/script/layers/domain/script_config.rb +2 -4
- data/lib/project_types/script/layers/domain/script_project.rb +3 -2
- data/lib/project_types/script/layers/infrastructure/languages/project_creator.rb +0 -16
- data/lib/project_types/script/layers/infrastructure/languages/task_runner.rb +0 -1
- data/lib/project_types/script/layers/infrastructure/languages/typescript_project_creator.rb +19 -4
- data/lib/project_types/script/layers/infrastructure/languages/typescript_task_runner.rb +2 -10
- data/lib/project_types/script/layers/infrastructure/languages/wasm_project_creator.rb +0 -3
- data/lib/project_types/script/layers/infrastructure/languages/wasm_task_runner.rb +1 -1
- data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +3 -21
- data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +14 -26
- data/lib/project_types/script/layers/infrastructure/script_service.rb +4 -2
- data/lib/project_types/script/messages/messages.rb +9 -9
- data/lib/project_types/script/ui/error_handler.rb +4 -4
- data/lib/project_types/script/ui/strict_spinner.rb +4 -6
- data/lib/project_types/theme/cli.rb +2 -0
- data/lib/project_types/theme/commands/common/root_helper.rb +11 -5
- data/lib/project_types/theme/commands/list.rb +34 -0
- data/lib/project_types/theme/commands/open.rb +65 -0
- data/lib/project_types/theme/commands/package.rb +1 -0
- data/lib/project_types/theme/commands/pull.rb +4 -4
- data/lib/project_types/theme/commands/push.rb +4 -4
- data/lib/project_types/theme/conversions/base_glob.rb +20 -5
- data/lib/project_types/theme/forms/select.rb +11 -39
- data/lib/project_types/theme/messages/messages.rb +33 -2
- data/lib/project_types/theme/presenters/theme_presenter.rb +48 -0
- data/lib/project_types/theme/presenters/themes_presenter.rb +32 -0
- data/lib/shopify_cli/api.rb +1 -1
- data/lib/shopify_cli/command.rb +1 -7
- data/lib/shopify_cli/commands/app/deploy.rb +0 -1
- data/lib/shopify_cli/constants.rb +2 -2
- data/lib/shopify_cli/context.rb +13 -15
- data/lib/shopify_cli/core/entry_point.rb +1 -1
- data/lib/shopify_cli/core/monorail.rb +14 -6
- data/lib/shopify_cli/environment.rb +19 -11
- data/lib/shopify_cli/exception_reporter.rb +2 -0
- data/lib/shopify_cli/messages/messages.rb +5 -5
- data/lib/shopify_cli/packager.rb +1 -1
- data/lib/shopify_cli/result.rb +14 -0
- data/lib/shopify_cli/services/app/create/node_service.rb +2 -14
- data/lib/shopify_cli/services/app/create/php_service.rb +1 -6
- data/lib/shopify_cli/services/app/create/rails_service.rb +5 -13
- data/lib/shopify_cli/theme/dev_server/hot_reload/remote_file_reloader.rb +5 -5
- data/lib/shopify_cli/theme/dev_server/watcher.rb +10 -2
- data/lib/shopify_cli/theme/development_theme.rb +2 -5
- data/lib/shopify_cli/theme/syncer.rb +27 -32
- data/lib/shopify_cli/theme/theme.rb +16 -27
- data/lib/shopify_cli/theme/theme_admin_api.rb +72 -0
- data/lib/shopify_cli/transform_data_structure.rb +3 -2
- data/lib/shopify_cli/version.rb +1 -1
- data/shipit.yml +3 -0
- data/shopify-cli.gemspec +9 -2
- data/shopify-dev +9 -11
- metadata +21 -9
- data/lib/project_types/rails/ruby.rb +0 -17
- data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_project_creator.rb +0 -21
- data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_task_runner.rb +0 -109
|
@@ -15,7 +15,9 @@ module ShopifyCLI
|
|
|
15
15
|
},
|
|
16
16
|
core: {
|
|
17
17
|
errors: {
|
|
18
|
-
missing_node: "Node is required to continue. Install
|
|
18
|
+
missing_node: "Node.js is required to continue. Install Node.js here: https://nodejs.org/en/download.",
|
|
19
|
+
missing_npm: "npm is required to continue. Install npm here: https://www.npmjs.com/get-npm.",
|
|
20
|
+
missing_ruby: "Ruby is required to continue. Install Ruby here: https://www.ruby-lang.org/en/downloads.",
|
|
19
21
|
option_parser: {
|
|
20
22
|
invalid_option: "The option {{command:%s}} is not supported.",
|
|
21
23
|
missing_argument: "The required argument {{command:%s}} is missing.",
|
|
@@ -95,8 +97,6 @@ module ShopifyCLI
|
|
|
95
97
|
HELP
|
|
96
98
|
error: {
|
|
97
99
|
node_required: "node is required to create an app project. Download at https://nodejs.org/en/download.",
|
|
98
|
-
node_version_failure: "Failed to get the current node version. Please make sure it is installed as " \
|
|
99
|
-
"per the instructions at https://nodejs.org/en.",
|
|
100
100
|
npm_required: "npm is required to create an app project. Download at https://www.npmjs.com/get-npm.",
|
|
101
101
|
npm_version_failure: "Failed to get the current npm version. Please make sure it is installed as per " \
|
|
102
102
|
"the instructions at https://www.npmjs.com/get-npm.",
|
|
@@ -131,8 +131,6 @@ module ShopifyCLI
|
|
|
131
131
|
{{underline:https://getcomposer.org/download/}}
|
|
132
132
|
COMPOSER
|
|
133
133
|
npm_required: "npm is required to create an app project. Download at https://www.npmjs.com/get-npm.",
|
|
134
|
-
npm_version_failure: "Failed to get the current npm version. Please make sure it is installed as per " \
|
|
135
|
-
"the instructions at https://www.npmjs.com/get-npm.",
|
|
136
134
|
app_setup: "Failed to set up the app",
|
|
137
135
|
},
|
|
138
136
|
|
|
@@ -283,6 +281,8 @@ module ShopifyCLI
|
|
|
283
281
|
localization: {
|
|
284
282
|
error: {
|
|
285
283
|
bundle_too_large: "Total size of all locale files must be less than %s.",
|
|
284
|
+
duplicate_locale_code: "Duplicate locale found: `%s`; locale codes"\
|
|
285
|
+
" should be unique and are case insensitive.",
|
|
286
286
|
file_empty: "Locale file `%s` is empty.",
|
|
287
287
|
file_too_large: "Locale file `%s` too large; size must be less than %s.",
|
|
288
288
|
invalid_file_extension: "Invalid locale filename: `%s`; only .json files are allowed.",
|
data/lib/shopify_cli/packager.rb
CHANGED
|
@@ -81,7 +81,7 @@ module ShopifyCLI
|
|
|
81
81
|
puts "Grabbing sha256 checksum from Rubygems.org"
|
|
82
82
|
require "digest/sha2"
|
|
83
83
|
require "open-uri"
|
|
84
|
-
gem_checksum = open("https://rubygems.org/downloads/shopify-cli-#{ShopifyCLI::VERSION}.gem") do |io|
|
|
84
|
+
gem_checksum = URI.open("https://rubygems.org/downloads/shopify-cli-#{ShopifyCLI::VERSION}.gem") do |io|
|
|
85
85
|
Digest::SHA256.new.hexdigest(io.read)
|
|
86
86
|
end
|
|
87
87
|
|
data/lib/shopify_cli/result.rb
CHANGED
|
@@ -181,6 +181,13 @@ module ShopifyCLI
|
|
|
181
181
|
self
|
|
182
182
|
end
|
|
183
183
|
|
|
184
|
+
##
|
|
185
|
+
# returns the value this success represents
|
|
186
|
+
#
|
|
187
|
+
def unwrap!
|
|
188
|
+
value
|
|
189
|
+
end
|
|
190
|
+
|
|
184
191
|
##
|
|
185
192
|
# returns the success value and ignores the fallback value that was either
|
|
186
193
|
# provided as a method argument or by passing a block. However, the caller
|
|
@@ -339,6 +346,13 @@ module ShopifyCLI
|
|
|
339
346
|
raise ArgumentError, "expected either a fallback value or a block" unless (args.length == 1) ^ block
|
|
340
347
|
block ? block.call(@error) : args.pop
|
|
341
348
|
end
|
|
349
|
+
|
|
350
|
+
##
|
|
351
|
+
# raises the error this failure represents
|
|
352
|
+
#
|
|
353
|
+
def unwrap!
|
|
354
|
+
raise error
|
|
355
|
+
end
|
|
342
356
|
end
|
|
343
357
|
|
|
344
358
|
##
|
|
@@ -88,24 +88,12 @@ module ShopifyCLI
|
|
|
88
88
|
end
|
|
89
89
|
|
|
90
90
|
def check_node
|
|
91
|
-
|
|
92
|
-
context.abort(context.message("core.app.create.node.error.node_required")) if cmd_path.nil?
|
|
93
|
-
|
|
94
|
-
version, stat = context.capture2e("node", "-v")
|
|
95
|
-
unless stat.success?
|
|
96
|
-
context.abort(context.message("core.app.create.node.error.node_version_failure"))
|
|
97
|
-
end
|
|
98
|
-
|
|
91
|
+
version = ShopifyCLI::Environment.node_version(context: context)
|
|
99
92
|
context.done(context.message("core.app.create.node.node_version", version))
|
|
100
93
|
end
|
|
101
94
|
|
|
102
95
|
def check_npm
|
|
103
|
-
|
|
104
|
-
context.abort(context.message("core.app.create.node.error.npm_required")) if cmd_path.nil?
|
|
105
|
-
|
|
106
|
-
version, stat = context.capture2e("npm", "-v")
|
|
107
|
-
context.abort(context.message("core.app.create.node.error.npm_version_failure")) unless stat.success?
|
|
108
|
-
|
|
96
|
+
version = ShopifyCLI::Environment.npm_version(context: context)
|
|
109
97
|
context.done(context.message("core.app.create.node.npm_version", version))
|
|
110
98
|
end
|
|
111
99
|
|
|
@@ -69,12 +69,7 @@ module ShopifyCLI
|
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
def check_npm
|
|
72
|
-
|
|
73
|
-
context.abort(context.message("core.app.create.php.error.npm_required")) if cmd_path.nil?
|
|
74
|
-
|
|
75
|
-
version, stat = context.capture2e("npm", "-v")
|
|
76
|
-
context.abort(context.message("core.app.create.php.error.npm_version_failure")) unless stat.success?
|
|
77
|
-
|
|
72
|
+
version = ShopifyCLI::Environment.npm_version(context: context)
|
|
78
73
|
context.done(context.message("core.app.create.php.npm_version", version))
|
|
79
74
|
end
|
|
80
75
|
|
|
@@ -108,8 +108,8 @@ module ShopifyCLI
|
|
|
108
108
|
end
|
|
109
109
|
|
|
110
110
|
def check_ruby
|
|
111
|
-
ruby_version =
|
|
112
|
-
return if ruby_version.satisfies?("~>2.5") || ruby_version.satisfies?("~>3.
|
|
111
|
+
ruby_version = Environment.ruby_version(context)
|
|
112
|
+
return if ruby_version.satisfies?("~>2.5") || ruby_version.satisfies?("~>3.1.0")
|
|
113
113
|
context.abort(context.message("core.app.create.rails.error.invalid_ruby_version"))
|
|
114
114
|
end
|
|
115
115
|
|
|
@@ -219,18 +219,10 @@ module ShopifyCLI
|
|
|
219
219
|
end
|
|
220
220
|
|
|
221
221
|
def install_webpacker?
|
|
222
|
-
rails_version
|
|
223
|
-
|
|
224
|
-
end
|
|
225
|
-
|
|
226
|
-
def rails_version
|
|
227
|
-
output, status = context.capture2e("rails", "--version")
|
|
228
|
-
unless status.success?
|
|
229
|
-
context.abort(context.message("core.app.create.rails.error.install_failure", "rails"))
|
|
230
|
-
end
|
|
222
|
+
rails_version = Environment.rails_version(context: context)
|
|
223
|
+
webpacker_config = File.exist?(File.join(context.root, "config/webpacker.yml"))
|
|
231
224
|
|
|
232
|
-
|
|
233
|
-
::Semantic::Version.new(version)
|
|
225
|
+
rails_version < ::Semantic::Version.new("7.0.0") && !webpacker_config
|
|
234
226
|
end
|
|
235
227
|
end
|
|
236
228
|
end
|
|
@@ -28,6 +28,10 @@ module ShopifyCLI
|
|
|
28
28
|
|
|
29
29
|
private
|
|
30
30
|
|
|
31
|
+
def api_client
|
|
32
|
+
@api_client ||= ThemeAdminAPI.new(@ctx, @theme.shop)
|
|
33
|
+
end
|
|
34
|
+
|
|
31
35
|
def updated_file?(body, file)
|
|
32
36
|
remote_checksum = body.dig("asset", "checksum")
|
|
33
37
|
local_checksum = file.checksum
|
|
@@ -45,12 +49,8 @@ module ShopifyCLI
|
|
|
45
49
|
end
|
|
46
50
|
|
|
47
51
|
def fetch_asset(file)
|
|
48
|
-
|
|
49
|
-
@ctx,
|
|
50
|
-
shop: @theme.shop,
|
|
52
|
+
api_client.get(
|
|
51
53
|
path: "themes/#{@theme.id}/assets.json",
|
|
52
|
-
method: "GET",
|
|
53
|
-
api_version: "unstable",
|
|
54
54
|
query: URI.encode_www_form("asset[key]" => file.relative_path.to_s),
|
|
55
55
|
)
|
|
56
56
|
rescue ShopifyCLI::API::APIRequestNotFoundError
|
|
@@ -45,13 +45,21 @@ module ShopifyCLI
|
|
|
45
45
|
def filter_theme_files(files)
|
|
46
46
|
files
|
|
47
47
|
.select { |file| @theme.theme_file?(file) }
|
|
48
|
-
.
|
|
48
|
+
.map { |file| @theme[file] }
|
|
49
|
+
.reject { |file| ignore_file?(file) }
|
|
49
50
|
end
|
|
50
51
|
|
|
51
52
|
def filter_remote_files(files)
|
|
52
53
|
files
|
|
53
54
|
.select { |file| @syncer.remote_file?(file) }
|
|
54
|
-
.
|
|
55
|
+
.map { |file| @theme[file] }
|
|
56
|
+
.reject { |file| ignore_file?(file) }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def ignore_file?(file)
|
|
62
|
+
@ignore_filter&.ignore?(file.relative_path.to_s)
|
|
55
63
|
end
|
|
56
64
|
end
|
|
57
65
|
end
|
|
@@ -45,11 +45,8 @@ module ShopifyCLI
|
|
|
45
45
|
def exists?
|
|
46
46
|
return false unless id
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
shop: shop,
|
|
51
|
-
path: "themes/#{id}.json",
|
|
52
|
-
api_version: "unstable",
|
|
48
|
+
api_client.get(
|
|
49
|
+
path: "themes/#{id}.json"
|
|
53
50
|
)
|
|
54
51
|
rescue ShopifyCLI::API::APIRequestNotFoundError
|
|
55
52
|
false
|
|
@@ -7,15 +7,15 @@ require "forwardable"
|
|
|
7
7
|
require_relative "syncer/error_reporter"
|
|
8
8
|
require_relative "syncer/standard_reporter"
|
|
9
9
|
require_relative "syncer/operation"
|
|
10
|
+
require_relative "theme_admin_api"
|
|
10
11
|
|
|
11
12
|
module ShopifyCLI
|
|
12
13
|
module Theme
|
|
13
14
|
class Syncer
|
|
14
15
|
extend Forwardable
|
|
15
16
|
|
|
16
|
-
API_VERSION = "unstable"
|
|
17
|
-
|
|
18
17
|
attr_reader :checksums
|
|
18
|
+
attr_reader :checksums_mutex
|
|
19
19
|
attr_accessor :include_filter
|
|
20
20
|
attr_accessor :ignore_filter
|
|
21
21
|
|
|
@@ -39,6 +39,9 @@ module ShopifyCLI
|
|
|
39
39
|
# Mutex used to pause all threads when backing-off when hitting API rate limits
|
|
40
40
|
@backoff_mutex = Mutex.new
|
|
41
41
|
|
|
42
|
+
# Mutex used to coordinate changes in the checksums (shared accross all threads)
|
|
43
|
+
@checksums_mutex = Mutex.new
|
|
44
|
+
|
|
42
45
|
# Latest theme assets checksums. Updated on each upload.
|
|
43
46
|
@checksums = {}
|
|
44
47
|
|
|
@@ -46,12 +49,16 @@ module ShopifyCLI
|
|
|
46
49
|
@error_checksums = []
|
|
47
50
|
end
|
|
48
51
|
|
|
52
|
+
def api_client
|
|
53
|
+
@api_client ||= ThemeAdminAPI.new(@ctx, @theme.shop)
|
|
54
|
+
end
|
|
55
|
+
|
|
49
56
|
def lock_io!
|
|
50
|
-
@reporters.each
|
|
57
|
+
@reporters.each(&:disable!)
|
|
51
58
|
end
|
|
52
59
|
|
|
53
60
|
def unlock_io!
|
|
54
|
-
@reporters.each
|
|
61
|
+
@reporters.each(&:enable!)
|
|
55
62
|
end
|
|
56
63
|
|
|
57
64
|
def enqueue_updates(files)
|
|
@@ -96,11 +103,8 @@ module ShopifyCLI
|
|
|
96
103
|
end
|
|
97
104
|
|
|
98
105
|
def fetch_checksums!
|
|
99
|
-
_status, response =
|
|
100
|
-
@
|
|
101
|
-
shop: @theme.shop,
|
|
102
|
-
path: "themes/#{@theme.id}/assets.json",
|
|
103
|
-
api_version: API_VERSION,
|
|
106
|
+
_status, response = api_client.get(
|
|
107
|
+
path: "themes/#{@theme.id}/assets.json"
|
|
104
108
|
)
|
|
105
109
|
update_checksums(response)
|
|
106
110
|
end
|
|
@@ -118,7 +122,7 @@ module ShopifyCLI
|
|
|
118
122
|
operation = @queue.pop
|
|
119
123
|
break if operation.nil? # shutdown was called
|
|
120
124
|
perform(operation)
|
|
121
|
-
rescue Exception => e
|
|
125
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
122
126
|
error_suffix = ": #{e}"
|
|
123
127
|
error_suffix += + "\n\t#{e.backtrace.join("\n\t")}" if @ctx.debug?
|
|
124
128
|
report_error(operation, error_suffix)
|
|
@@ -239,12 +243,8 @@ module ShopifyCLI
|
|
|
239
243
|
asset[:attachment] = Base64.encode64(file.read)
|
|
240
244
|
end
|
|
241
245
|
|
|
242
|
-
_status, body, response =
|
|
243
|
-
@ctx,
|
|
244
|
-
shop: @theme.shop,
|
|
246
|
+
_status, body, response = api_client.put(
|
|
245
247
|
path: "themes/#{@theme.id}/assets.json",
|
|
246
|
-
method: "PUT",
|
|
247
|
-
api_version: API_VERSION,
|
|
248
248
|
body: JSON.generate(asset: asset)
|
|
249
249
|
)
|
|
250
250
|
|
|
@@ -272,23 +272,19 @@ module ShopifyCLI
|
|
|
272
272
|
end
|
|
273
273
|
|
|
274
274
|
def ignored_by_include_filter?(path)
|
|
275
|
-
include_filter && !include_filter.match?(path)
|
|
275
|
+
!!include_filter && !include_filter.match?(path)
|
|
276
276
|
end
|
|
277
277
|
|
|
278
278
|
def get(file)
|
|
279
|
-
_status, body, response =
|
|
280
|
-
@ctx,
|
|
281
|
-
shop: @theme.shop,
|
|
279
|
+
_status, body, response = api_client.get(
|
|
282
280
|
path: "themes/#{@theme.id}/assets.json",
|
|
283
|
-
method: "GET",
|
|
284
|
-
api_version: API_VERSION,
|
|
285
281
|
query: URI.encode_www_form("asset[key]" => file.relative_path.to_s),
|
|
286
282
|
)
|
|
287
283
|
|
|
288
284
|
update_checksums(body)
|
|
289
285
|
|
|
290
286
|
attachment = body.dig("asset", "attachment")
|
|
291
|
-
|
|
287
|
+
if attachment
|
|
292
288
|
file.write(Base64.decode64(attachment))
|
|
293
289
|
else
|
|
294
290
|
file.write(body.dig("asset", "value"))
|
|
@@ -298,14 +294,10 @@ module ShopifyCLI
|
|
|
298
294
|
end
|
|
299
295
|
|
|
300
296
|
def delete(file)
|
|
301
|
-
_status, _body, response =
|
|
302
|
-
@ctx,
|
|
303
|
-
shop: @theme.shop,
|
|
297
|
+
_status, _body, response = api_client.delete(
|
|
304
298
|
path: "themes/#{@theme.id}/assets.json",
|
|
305
|
-
method: "DELETE",
|
|
306
|
-
api_version: API_VERSION,
|
|
307
299
|
body: JSON.generate(asset: {
|
|
308
|
-
key: file.relative_path.to_s
|
|
300
|
+
key: file.relative_path.to_s,
|
|
309
301
|
})
|
|
310
302
|
)
|
|
311
303
|
|
|
@@ -314,14 +306,17 @@ module ShopifyCLI
|
|
|
314
306
|
|
|
315
307
|
def update_checksums(api_response)
|
|
316
308
|
api_response.values.flatten.each do |asset|
|
|
317
|
-
|
|
309
|
+
next unless asset["key"]
|
|
310
|
+
checksums_mutex.synchronize do
|
|
318
311
|
@checksums[asset["key"]] = asset["checksum"]
|
|
319
312
|
end
|
|
320
313
|
end
|
|
321
314
|
# Generate .liquid asset files are reported twice in checksum:
|
|
322
315
|
# once of generated, once for .liquid. We only keep the .liquid, that's the one we have
|
|
323
316
|
# on disk.
|
|
324
|
-
|
|
317
|
+
checksums_mutex.synchronize do
|
|
318
|
+
@checksums.reject! { |key, _| @checksums.key?("#{key}.liquid") }
|
|
319
|
+
end
|
|
325
320
|
end
|
|
326
321
|
|
|
327
322
|
def file_has_changed?(file)
|
|
@@ -332,7 +327,7 @@ module ShopifyCLI
|
|
|
332
327
|
parsed_body = JSON.parse(exception&.response&.body)
|
|
333
328
|
message = parsed_body.dig("errors", "asset") || parsed_body["message"] || exception.message
|
|
334
329
|
# Truncate to first lines
|
|
335
|
-
[message].flatten.map { |
|
|
330
|
+
[message].flatten.map { |mess| mess.split("\n", 2).first }
|
|
336
331
|
rescue JSON::ParserError
|
|
337
332
|
[exception.message]
|
|
338
333
|
end
|
|
@@ -340,7 +335,7 @@ module ShopifyCLI
|
|
|
340
335
|
def backoff_if_near_limit!(used, limit)
|
|
341
336
|
if used > limit - @threads.size
|
|
342
337
|
@ctx.debug("Near API call limit, waiting 2 sec…")
|
|
343
|
-
@backoff_mutex.synchronize { sleep
|
|
338
|
+
@backoff_mutex.synchronize { sleep(2) }
|
|
344
339
|
end
|
|
345
340
|
end
|
|
346
341
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
require_relative "file"
|
|
3
|
+
require_relative "theme_admin_api"
|
|
3
4
|
|
|
4
5
|
require "pathname"
|
|
5
6
|
require "time"
|
|
@@ -59,7 +60,7 @@ module ShopifyCLI
|
|
|
59
60
|
end
|
|
60
61
|
|
|
61
62
|
def shop
|
|
62
|
-
|
|
63
|
+
api_client.get_shop_or_abort
|
|
63
64
|
end
|
|
64
65
|
|
|
65
66
|
def editor_url
|
|
@@ -101,9 +102,7 @@ module ShopifyCLI
|
|
|
101
102
|
def create
|
|
102
103
|
raise InvalidThemeRole, "Can't create live theme. Use publish." if live?
|
|
103
104
|
|
|
104
|
-
_status, body =
|
|
105
|
-
@ctx,
|
|
106
|
-
shop: shop,
|
|
105
|
+
_status, body = api_client.post(
|
|
107
106
|
path: "themes.json",
|
|
108
107
|
body: JSON.generate({
|
|
109
108
|
theme: {
|
|
@@ -111,31 +110,21 @@ module ShopifyCLI
|
|
|
111
110
|
role: role,
|
|
112
111
|
},
|
|
113
112
|
}),
|
|
114
|
-
method: "POST",
|
|
115
|
-
api_version: "unstable",
|
|
116
113
|
)
|
|
117
114
|
|
|
118
115
|
@id = body["theme"]["id"]
|
|
119
116
|
end
|
|
120
117
|
|
|
121
118
|
def delete
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
shop: shop,
|
|
125
|
-
method: "DELETE",
|
|
126
|
-
path: "themes/#{id}.json",
|
|
127
|
-
api_version: "unstable",
|
|
119
|
+
api_client.delete(
|
|
120
|
+
path: "themes/#{id}.json"
|
|
128
121
|
)
|
|
129
122
|
end
|
|
130
123
|
|
|
131
124
|
def publish
|
|
132
125
|
return if live?
|
|
133
|
-
|
|
134
|
-
@ctx,
|
|
135
|
-
shop: shop,
|
|
136
|
-
method: "PUT",
|
|
126
|
+
api_client.put(
|
|
137
127
|
path: "themes/#{id}.json",
|
|
138
|
-
api_version: "unstable",
|
|
139
128
|
body: JSON.generate(theme: {
|
|
140
129
|
role: "main",
|
|
141
130
|
})
|
|
@@ -205,23 +194,23 @@ module ShopifyCLI
|
|
|
205
194
|
end
|
|
206
195
|
|
|
207
196
|
def fetch_themes(ctx)
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
path: "themes.json"
|
|
212
|
-
api_version: "unstable",
|
|
197
|
+
api_client = ThemeAdminAPI.new(ctx)
|
|
198
|
+
|
|
199
|
+
api_client.get(
|
|
200
|
+
path: "themes.json"
|
|
213
201
|
)
|
|
214
202
|
end
|
|
215
203
|
end
|
|
216
204
|
|
|
217
205
|
private
|
|
218
206
|
|
|
207
|
+
def api_client
|
|
208
|
+
@api_client ||= ThemeAdminAPI.new(@ctx)
|
|
209
|
+
end
|
|
210
|
+
|
|
219
211
|
def load_info_from_api
|
|
220
|
-
_status, body =
|
|
221
|
-
|
|
222
|
-
shop: shop,
|
|
223
|
-
path: "themes/#{id}.json",
|
|
224
|
-
api_version: "unstable",
|
|
212
|
+
_status, body = api_client.get(
|
|
213
|
+
path: "themes/#{id}.json"
|
|
225
214
|
)
|
|
226
215
|
|
|
227
216
|
@name = body.dig("theme", "name")
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
module ShopifyCLI
|
|
2
|
+
module Theme
|
|
3
|
+
class ThemeAdminAPI
|
|
4
|
+
API_VERSION = "unstable"
|
|
5
|
+
|
|
6
|
+
attr_reader :shop
|
|
7
|
+
|
|
8
|
+
def initialize(ctx, shop = nil)
|
|
9
|
+
@ctx = ctx
|
|
10
|
+
@shop = shop || get_shop_or_abort
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def get(path:, **args)
|
|
14
|
+
rest_request(method: "GET", path: path, **args)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def put(path:, **args)
|
|
18
|
+
rest_request(method: "PUT", path: path, **args)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def post(path:, **args)
|
|
22
|
+
rest_request(method: "POST", path: path, **args)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def delete(path:, **args)
|
|
26
|
+
rest_request(method: "DELETE", path: path, **args)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def get_shop_or_abort # rubocop:disable Naming/AccessorMethodName
|
|
30
|
+
ShopifyCLI::AdminAPI.get_shop_or_abort(@ctx)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def rest_request(**args)
|
|
36
|
+
ShopifyCLI::AdminAPI.rest_request(
|
|
37
|
+
@ctx,
|
|
38
|
+
shop: @shop,
|
|
39
|
+
api_version: API_VERSION,
|
|
40
|
+
**args.compact
|
|
41
|
+
)
|
|
42
|
+
rescue ShopifyCLI::API::APIRequestForbiddenError,
|
|
43
|
+
ShopifyCLI::API::APIRequestUnauthorizedError => error
|
|
44
|
+
# The Admin API returns 403 Forbidden responses on different
|
|
45
|
+
# scenarios:
|
|
46
|
+
#
|
|
47
|
+
# * when a user doesn't have permissions for a request:
|
|
48
|
+
# <APIRequestForbiddenError: 403 {}>
|
|
49
|
+
#
|
|
50
|
+
# * when an asset operation cannot be performed:
|
|
51
|
+
# <APIRequestForbiddenError: 403 {"message":"templates/gift_card.liquid could not be deleted"}>
|
|
52
|
+
if empty_response_error?(error)
|
|
53
|
+
return handle_permissions_error
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
raise error
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def handle_permissions_error
|
|
60
|
+
ensure_user_error = @ctx.message("theme.ensure_user_error", shop)
|
|
61
|
+
ensure_user_try_this = @ctx.message("theme.ensure_user_try_this")
|
|
62
|
+
|
|
63
|
+
@ctx.abort(ensure_user_error, ensure_user_try_this)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def empty_response_error?(error)
|
|
67
|
+
error_message = error&.response&.body.to_s
|
|
68
|
+
error_message.empty?
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -40,6 +40,7 @@ module ShopifyCLI
|
|
|
40
40
|
|
|
41
41
|
property! :underscore_keys, accepts: [true, false], default: false, reader: :underscore_keys?
|
|
42
42
|
property! :symbolize_keys, accepts: [true, false], default: false, reader: :symbolize_keys?
|
|
43
|
+
property! :shallow, accepts: [true, false], default: false, reader: :shallow?
|
|
43
44
|
property! :associative_array_container,
|
|
44
45
|
accepts: ->(c) { c.respond_to?(:new) && c.method_defined?(:[]=) },
|
|
45
46
|
default: -> { Hash }
|
|
@@ -47,10 +48,10 @@ module ShopifyCLI
|
|
|
47
48
|
def call(object)
|
|
48
49
|
case object
|
|
49
50
|
when Array
|
|
50
|
-
object.map(&self).map(&:value)
|
|
51
|
+
shallow? ? object.dup : object.map(&self).map(&:value)
|
|
51
52
|
when Hash
|
|
52
53
|
object.each.with_object(associative_array_container.new) do |(key, value), result|
|
|
53
|
-
result[transform_key(key)] = call(value).value
|
|
54
|
+
result[transform_key(key)] = shallow? ? value : call(value).value
|
|
54
55
|
end
|
|
55
56
|
else
|
|
56
57
|
ShopifyCLI::Result.success(object)
|
data/lib/shopify_cli/version.rb
CHANGED
data/shipit.yml
ADDED
data/shopify-cli.gemspec
CHANGED
|
@@ -35,11 +35,18 @@ Gem::Specification.new do |spec|
|
|
|
35
35
|
spec.require_paths = ["lib", "vendor"]
|
|
36
36
|
spec.executables << "shopify"
|
|
37
37
|
|
|
38
|
-
spec.add_development_dependency("bundler", "~> 2.
|
|
38
|
+
spec.add_development_dependency("bundler", "~> 2.3.8")
|
|
39
39
|
spec.add_development_dependency("rake", "~> 12.3", ">= 12.3.3")
|
|
40
40
|
spec.add_development_dependency("minitest", "~> 5.0")
|
|
41
41
|
|
|
42
42
|
spec.add_dependency("bugsnag", "~> 6.22")
|
|
43
43
|
spec.add_dependency("listen", "~> 3.7.0")
|
|
44
|
-
|
|
44
|
+
|
|
45
|
+
# We prefer being more strict here with the version range to have a more deterministic build.
|
|
46
|
+
# The added benefit is that, if the user upgrades the CLI, and we have "~> 1.10.1" version range,
|
|
47
|
+
# they will get a theme-check update.
|
|
48
|
+
# Whereas if we were to have "~> 1.9", that version would still be satisfied and thus not upgraded.
|
|
49
|
+
# Both shopify-cli and theme-check gems are owned and developed by Shopify.
|
|
50
|
+
# These gems are currently being actively developed and it's easiest to update them together.
|
|
51
|
+
spec.add_dependency("theme-check", "~> 1.10.1")
|
|
45
52
|
end
|
data/shopify-dev
CHANGED
|
@@ -3,16 +3,14 @@
|
|
|
3
3
|
require_relative "./bin/load_shopify"
|
|
4
4
|
|
|
5
5
|
exit(proc do
|
|
6
|
-
|
|
7
|
-
ShopifyCLI::
|
|
8
|
-
ShopifyCLI::Core::EntryPoint.call(ARGV.dup)
|
|
9
|
-
end
|
|
10
|
-
rescue StandardError => error
|
|
11
|
-
ShopifyCLI::ErrorHandler.exception = error
|
|
12
|
-
if ShopifyCLI::Environment.print_stacktrace?
|
|
13
|
-
raise error
|
|
14
|
-
else
|
|
15
|
-
1
|
|
16
|
-
end
|
|
6
|
+
ShopifyCLI::ErrorHandler.call do
|
|
7
|
+
ShopifyCLI::Core::EntryPoint.call(ARGV.dup)
|
|
17
8
|
end
|
|
9
|
+
rescue StandardError => error
|
|
10
|
+
ShopifyCLI::ErrorHandler.exception = error
|
|
11
|
+
if ShopifyCLI::Environment.print_stacktrace?
|
|
12
|
+
raise error
|
|
13
|
+
else
|
|
14
|
+
1
|
|
15
|
+
end
|
|
18
16
|
end.call)
|