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.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +5 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +1 -1
- data/.github/workflows/shopify.yml +2 -1
- data/.rubocop.yml +1 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +44 -1
- data/Gemfile.lock +18 -18
- data/Rakefile +16 -0
- data/bin/shopify +12 -8
- data/dev.yml +1 -1
- data/docs/users/installation.md +1 -44
- 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 +114 -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 +2 -4
- data/lib/project_types/script/commands/create.rb +5 -5
- data/lib/project_types/script/commands/push.rb +4 -6
- data/lib/project_types/script/config/extension_points.yml +0 -10
- data/lib/project_types/script/errors.rb +1 -1
- data/lib/project_types/script/forms/create.rb +7 -20
- data/lib/project_types/script/layers/application/build_script.rb +9 -26
- data/lib/project_types/script/layers/application/connect_app.rb +3 -2
- 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/errors.rb +11 -0
- 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/tool_version_checker.rb +26 -0
- data/lib/project_types/script/layers/infrastructure/languages/typescript_project_creator.rb +22 -10
- data/lib/project_types/script/layers/infrastructure/languages/typescript_task_runner.rb +32 -29
- 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/loaders/project.rb +8 -7
- data/lib/project_types/script/messages/messages.rb +22 -21
- data/lib/project_types/script/ui/error_handler.rb +17 -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 +71 -0
- data/lib/project_types/theme/commands/init.rb +2 -0
- 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 +18 -10
- data/lib/project_types/theme/commands/push.rb +17 -9
- data/lib/project_types/theme/commands/serve.rb +6 -2
- data/lib/project_types/theme/conversions/base_glob.rb +50 -0
- data/lib/project_types/theme/conversions/ignore_glob.rb +15 -0
- data/lib/project_types/theme/conversions/include_glob.rb +15 -0
- data/lib/project_types/theme/forms/select.rb +11 -39
- data/lib/project_types/theme/messages/messages.rb +38 -7
- 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/commands/app/create/node.rb +1 -0
- data/lib/shopify_cli/commands/app/create/php.rb +1 -0
- data/lib/shopify_cli/commands/app/create/rails.rb +1 -0
- data/lib/shopify_cli/commands/app/deploy.rb +1 -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 +6 -0
- data/lib/shopify_cli/exception_reporter.rb +2 -0
- data/lib/shopify_cli/git.rb +9 -1
- data/lib/shopify_cli/messages/messages.rb +21 -1
- data/lib/shopify_cli/packager.rb +1 -1
- data/lib/shopify_cli/result.rb +14 -0
- data/lib/shopify_cli/services/app/create/rails_service.rb +1 -1
- data/lib/shopify_cli/tasks/ensure_git_dependency.rb +14 -0
- data/lib/shopify_cli/tasks.rb +1 -0
- data/lib/shopify_cli/theme/dev_server/hot_reload/remote_file_reloader.rb +5 -5
- data/lib/shopify_cli/theme/dev_server/proxy.rb +14 -2
- 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/include_filter.rb +4 -2
- data/lib/shopify_cli/theme/syncer.rb +40 -36
- data/lib/shopify_cli/theme/theme.rb +16 -27
- data/lib/shopify_cli/theme/theme_admin_api.rb +71 -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 +26 -8
- data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_project_creator.rb +0 -25
- data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_task_runner.rb +0 -98
@@ -5,7 +5,7 @@ module ShopifyCLI
|
|
5
5
|
module EntryPoint
|
6
6
|
class << self
|
7
7
|
def call(args, ctx = Context.new)
|
8
|
-
if ctx.development?
|
8
|
+
if ctx.development? && !ctx.testing?
|
9
9
|
ctx.warn(
|
10
10
|
ctx.message("core.warning.development_version", File.join(ShopifyCLI::ROOT, "bin", ShopifyCLI::TOOL_NAME))
|
11
11
|
)
|
@@ -17,11 +17,7 @@ module ShopifyCLI
|
|
17
17
|
|
18
18
|
def log(name, args, &block) # rubocop:disable Lint/UnusedMethodArgument
|
19
19
|
command, command_name = Commands::Registry.lookup_command(name)
|
20
|
-
|
21
|
-
if command
|
22
|
-
subcommand, subcommand_name = command.subcommand_registry.lookup_command(args.first)
|
23
|
-
final_command << subcommand_name if subcommand
|
24
|
-
end
|
20
|
+
full_command = self.full_command(command, args, resolved_command: [command_name])
|
25
21
|
|
26
22
|
start_time = now_in_milliseconds
|
27
23
|
err = nil
|
@@ -35,9 +31,21 @@ module ShopifyCLI
|
|
35
31
|
# If there's an error, we don't prompt from here and we let the exception
|
36
32
|
# reporter do that.
|
37
33
|
if report?(prompt: err.nil?)
|
38
|
-
send_event(start_time,
|
34
|
+
send_event(start_time, full_command, args - full_command, err&.message)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def full_command(command, args, resolved_command:)
|
40
|
+
resolved_command = resolved_command.dup
|
41
|
+
if command
|
42
|
+
subcommand, subcommand_name = command.subcommand_registry.lookup_command(args.first)
|
43
|
+
resolved_command << subcommand_name if subcommand
|
44
|
+
if subcommand&.subcommand_registry
|
45
|
+
resolved_command = full_command(subcommand, args.drop(1), resolved_command: resolved_command)
|
39
46
|
end
|
40
47
|
end
|
48
|
+
resolved_command
|
41
49
|
end
|
42
50
|
|
43
51
|
private
|
@@ -25,6 +25,12 @@ module ShopifyCLI
|
|
25
25
|
::Semantic::Version.new(out.chomp)
|
26
26
|
end
|
27
27
|
|
28
|
+
def self.npm_version(context: Context.new)
|
29
|
+
out, err, stat = context.capture3("npm", "--version")
|
30
|
+
raise ShopifyCLI::Abort, err unless stat.success?
|
31
|
+
::Semantic::Version.new(out.chomp)
|
32
|
+
end
|
33
|
+
|
28
34
|
def self.interactive=(interactive)
|
29
35
|
@interactive = interactive
|
30
36
|
end
|
data/lib/shopify_cli/git.rb
CHANGED
@@ -4,7 +4,15 @@ module ShopifyCLI
|
|
4
4
|
# git.
|
5
5
|
class Git
|
6
6
|
class << self
|
7
|
-
# Check if Git
|
7
|
+
# Check if Git exists in the environment
|
8
|
+
def exists?(ctx)
|
9
|
+
_output, status = ctx.capture2e("git", "version")
|
10
|
+
status.success?
|
11
|
+
rescue Errno::ENOENT # git is not installed
|
12
|
+
false
|
13
|
+
end
|
14
|
+
|
15
|
+
# Check if the current working directory is a Git repository
|
8
16
|
def available?(ctx)
|
9
17
|
_output, status = ctx.capture2e("git", "status")
|
10
18
|
status.success?
|
@@ -277,6 +277,24 @@ module ShopifyCLI
|
|
277
277
|
HELP
|
278
278
|
},
|
279
279
|
},
|
280
|
+
extension: {
|
281
|
+
push: {
|
282
|
+
checkout_ui_extension: {
|
283
|
+
localization: {
|
284
|
+
error: {
|
285
|
+
bundle_too_large: "Total size of all locale files must be less than %s.",
|
286
|
+
file_empty: "Locale file `%s` is empty.",
|
287
|
+
file_too_large: "Locale file `%s` too large; size must be less than %s.",
|
288
|
+
invalid_file_extension: "Invalid locale filename: `%s`; only .json files are allowed.",
|
289
|
+
invalid_locale_code: "Invalid locale filename: `%s`; locale code should be 2 or 3 letters,"\
|
290
|
+
" optionally followed by a two-letter region code, e.g. `fr-CA`.",
|
291
|
+
single_default_locale: "There must be one and only one locale identified as the default locale,"\
|
292
|
+
" e.g. `en.default.json`",
|
293
|
+
},
|
294
|
+
},
|
295
|
+
},
|
296
|
+
},
|
297
|
+
},
|
280
298
|
error_reporting: {
|
281
299
|
unhandled_error: {
|
282
300
|
message: "{{x}} {{red:An unexpected error occured.}}",
|
@@ -355,6 +373,7 @@ module ShopifyCLI
|
|
355
373
|
error: {
|
356
374
|
directory_exists: "Project directory already exists. Please create a project with a new name.",
|
357
375
|
no_branches_found: "Could not find any git branches",
|
376
|
+
nonexistent: "Git needs to be installed: https://git-scm.com/download",
|
358
377
|
repo_not_initiated:
|
359
378
|
"Git repo is not initiated. Please run {{command:git init}} and make at least one commit.",
|
360
379
|
no_commits_made: "No git commits have been made. Please make at least one commit.",
|
@@ -672,7 +691,8 @@ module ShopifyCLI
|
|
672
691
|
},
|
673
692
|
},
|
674
693
|
ensure_dev_store: {
|
675
|
-
could_not_verify_store: "Couldn't verify your store
|
694
|
+
could_not_verify_store: "Couldn't verify your store. If you don't have a development store set up, "\
|
695
|
+
"please create one in your Partners dashboard and run `shopify app connect`.",
|
676
696
|
convert_to_dev_store: <<~MESSAGE,
|
677
697
|
Do you want to convert %s to a development store?
|
678
698
|
Doing this will allow you to install your app, but the store will become {{bold:transfer-disabled}}.
|
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
|
##
|
@@ -109,7 +109,7 @@ module ShopifyCLI
|
|
109
109
|
|
110
110
|
def check_ruby
|
111
111
|
ruby_version = Rails::Ruby.version(context)
|
112
|
-
return if ruby_version.satisfies?("~>2.5") || ruby_version.satisfies?("~>3.
|
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
|
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "shopify_cli"
|
2
|
+
|
3
|
+
module ShopifyCLI
|
4
|
+
module Tasks
|
5
|
+
class EnsureGitDependency < ShopifyCLI::Task
|
6
|
+
def call(ctx)
|
7
|
+
return if ShopifyCLI::Environment.acceptance_test?
|
8
|
+
unless ShopifyCLI::Git.exists?(ctx)
|
9
|
+
raise ShopifyCLI::Abort, ctx.message("core.git.error.nonexistent")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/shopify_cli/tasks.rb
CHANGED
@@ -34,6 +34,7 @@ module ShopifyCLI
|
|
34
34
|
register :CreateApiClient, :create_api_client, "shopify_cli/tasks/create_api_client"
|
35
35
|
register :EnsureAuthenticated, :ensure_authenticated, "shopify_cli/tasks/ensure_authenticated"
|
36
36
|
register :EnsureEnv, :ensure_env, "shopify_cli/tasks/ensure_env"
|
37
|
+
register :EnsureGitDependency, :ensure_git_dependency, "shopify_cli/tasks/ensure_git_dependency"
|
37
38
|
register :EnsureLoopbackURL, :ensure_loopback_url, "shopify_cli/tasks/ensure_loopback_url"
|
38
39
|
register :EnsureProjectType, :ensure_project_type, "shopify_cli/tasks/ensure_project_type"
|
39
40
|
register :EnsureDevStore, :ensure_dev_store, "shopify_cli/tasks/ensure_dev_store"
|
@@ -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
|
@@ -3,7 +3,6 @@ require "net/http"
|
|
3
3
|
require "stringio"
|
4
4
|
require "time"
|
5
5
|
require "cgi"
|
6
|
-
|
7
6
|
require_relative "proxy/template_param_builder"
|
8
7
|
|
9
8
|
module ShopifyCLI
|
@@ -70,13 +69,26 @@ module ShopifyCLI
|
|
70
69
|
@core_endpoints << env["PATH_INFO"]
|
71
70
|
end
|
72
71
|
|
73
|
-
body = response.body
|
72
|
+
body = patch_body(env, response.body)
|
74
73
|
body = [body] unless body.respond_to?(:each)
|
75
74
|
[response.code, headers, body]
|
76
75
|
end
|
77
76
|
|
78
77
|
private
|
79
78
|
|
79
|
+
def patch_body(env, body)
|
80
|
+
return [""] unless body
|
81
|
+
|
82
|
+
body.gsub(%r{(data-.+=(["']))(http:|https:)?//#{@theme.shop}(.*)(\2)}) do |_|
|
83
|
+
match = Regexp.last_match
|
84
|
+
"#{match[1]}http://#{host(env)}#{match[4]}#{match[5]}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def host(env)
|
89
|
+
env["HTTP_HOST"]
|
90
|
+
end
|
91
|
+
|
80
92
|
def has_body?(headers)
|
81
93
|
headers["Content-Length"] || headers["Transfer-Encoding"]
|
82
94
|
end
|
@@ -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
|
@@ -9,7 +9,8 @@ module ShopifyCLI
|
|
9
9
|
|
10
10
|
attr_reader :globs, :regexes
|
11
11
|
|
12
|
-
def initialize(patterns = [])
|
12
|
+
def initialize(root, patterns = [])
|
13
|
+
@root = Pathname.new(root)
|
13
14
|
@patterns = patterns.nil? ? [] : patterns.compact.reject(&:empty?)
|
14
15
|
|
15
16
|
regexes, globs = patterns_to_regexes_and_globs(@patterns)
|
@@ -22,9 +23,10 @@ module ShopifyCLI
|
|
22
23
|
return true unless present?(@patterns)
|
23
24
|
|
24
25
|
path = path.to_s
|
25
|
-
|
26
26
|
return true if path.empty?
|
27
27
|
|
28
|
+
path = @root.join(path).to_s
|
29
|
+
|
28
30
|
regexes.each do |regex|
|
29
31
|
return true if regex_match?(regex, path)
|
30
32
|
end
|
@@ -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,19 +39,26 @@ 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
|
|
45
|
-
# Checksums of assets with errors.
|
48
|
+
# Checksums of assets with errors.
|
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)
|
@@ -168,7 +172,7 @@ module ShopifyCLI
|
|
168
172
|
# Delete local files not present remotely
|
169
173
|
missing_files = @theme.theme_files
|
170
174
|
.reject { |file| checksums.key?(file.relative_path.to_s) }.uniq
|
171
|
-
.reject { |file|
|
175
|
+
.reject { |file| ignore_file?(file) }
|
172
176
|
missing_files.each do |file|
|
173
177
|
@ctx.debug("rm #{file.relative_path}")
|
174
178
|
file.delete
|
@@ -195,7 +199,7 @@ module ShopifyCLI
|
|
195
199
|
# Already enqueued
|
196
200
|
return if @pending.include?(operation)
|
197
201
|
|
198
|
-
if
|
202
|
+
if ignore_operation?(operation)
|
199
203
|
@ctx.debug("ignore #{operation.file_path}")
|
200
204
|
return
|
201
205
|
end
|
@@ -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
|
|
@@ -253,8 +253,17 @@ module ShopifyCLI
|
|
253
253
|
response
|
254
254
|
end
|
255
255
|
|
256
|
-
def
|
256
|
+
def ignore_operation?(operation)
|
257
257
|
path = operation.file_path
|
258
|
+
ignore_path?(path)
|
259
|
+
end
|
260
|
+
|
261
|
+
def ignore_file?(file)
|
262
|
+
path = file.path
|
263
|
+
ignore_path?(path)
|
264
|
+
end
|
265
|
+
|
266
|
+
def ignore_path?(path)
|
258
267
|
ignored_by_ignore_filter?(path) || ignored_by_include_filter?(path)
|
259
268
|
end
|
260
269
|
|
@@ -263,23 +272,19 @@ module ShopifyCLI
|
|
263
272
|
end
|
264
273
|
|
265
274
|
def ignored_by_include_filter?(path)
|
266
|
-
include_filter && !include_filter.match?(path)
|
275
|
+
!!include_filter && !include_filter.match?(path)
|
267
276
|
end
|
268
277
|
|
269
278
|
def get(file)
|
270
|
-
_status, body, response =
|
271
|
-
@ctx,
|
272
|
-
shop: @theme.shop,
|
279
|
+
_status, body, response = api_client.get(
|
273
280
|
path: "themes/#{@theme.id}/assets.json",
|
274
|
-
method: "GET",
|
275
|
-
api_version: API_VERSION,
|
276
281
|
query: URI.encode_www_form("asset[key]" => file.relative_path.to_s),
|
277
282
|
)
|
278
283
|
|
279
284
|
update_checksums(body)
|
280
285
|
|
281
286
|
attachment = body.dig("asset", "attachment")
|
282
|
-
|
287
|
+
if attachment
|
283
288
|
file.write(Base64.decode64(attachment))
|
284
289
|
else
|
285
290
|
file.write(body.dig("asset", "value"))
|
@@ -289,14 +294,10 @@ module ShopifyCLI
|
|
289
294
|
end
|
290
295
|
|
291
296
|
def delete(file)
|
292
|
-
_status, _body, response =
|
293
|
-
@ctx,
|
294
|
-
shop: @theme.shop,
|
297
|
+
_status, _body, response = api_client.delete(
|
295
298
|
path: "themes/#{@theme.id}/assets.json",
|
296
|
-
method: "DELETE",
|
297
|
-
api_version: API_VERSION,
|
298
299
|
body: JSON.generate(asset: {
|
299
|
-
key: file.relative_path.to_s
|
300
|
+
key: file.relative_path.to_s,
|
300
301
|
})
|
301
302
|
)
|
302
303
|
|
@@ -305,14 +306,17 @@ module ShopifyCLI
|
|
305
306
|
|
306
307
|
def update_checksums(api_response)
|
307
308
|
api_response.values.flatten.each do |asset|
|
308
|
-
|
309
|
+
next unless asset["key"]
|
310
|
+
checksums_mutex.synchronize do
|
309
311
|
@checksums[asset["key"]] = asset["checksum"]
|
310
312
|
end
|
311
313
|
end
|
312
314
|
# Generate .liquid asset files are reported twice in checksum:
|
313
315
|
# once of generated, once for .liquid. We only keep the .liquid, that's the one we have
|
314
316
|
# on disk.
|
315
|
-
|
317
|
+
checksums_mutex.synchronize do
|
318
|
+
@checksums.reject! { |key, _| @checksums.key?("#{key}.liquid") }
|
319
|
+
end
|
316
320
|
end
|
317
321
|
|
318
322
|
def file_has_changed?(file)
|
@@ -323,7 +327,7 @@ module ShopifyCLI
|
|
323
327
|
parsed_body = JSON.parse(exception&.response&.body)
|
324
328
|
message = parsed_body.dig("errors", "asset") || parsed_body["message"] || exception.message
|
325
329
|
# Truncate to first lines
|
326
|
-
[message].flatten.map { |
|
330
|
+
[message].flatten.map { |mess| mess.split("\n", 2).first }
|
327
331
|
rescue JSON::ParserError
|
328
332
|
[exception.message]
|
329
333
|
end
|
@@ -331,7 +335,7 @@ module ShopifyCLI
|
|
331
335
|
def backoff_if_near_limit!(used, limit)
|
332
336
|
if used > limit - @threads.size
|
333
337
|
@ctx.debug("Near API call limit, waiting 2 sec…")
|
334
|
-
@backoff_mutex.synchronize { sleep
|
338
|
+
@backoff_mutex.synchronize { sleep(2) }
|
335
339
|
end
|
336
340
|
end
|
337
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")
|