shopify-cli 2.15.0 → 2.15.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/stale.yml +7 -2
- data/.vscode/settings.json +1 -2
- data/CHANGELOG.md +70 -19
- data/Gemfile +1 -0
- data/Gemfile.lock +39 -7
- data/Rakefile +48 -0
- data/ext/javy/hashes/javy-arm-macos-v0.3.0.gz.sha256 +1 -0
- data/ext/javy/hashes/javy-x86_64-linux-v0.3.0.gz.sha256 +1 -0
- data/ext/javy/hashes/javy-x86_64-macos-v0.3.0.gz.sha256 +1 -0
- data/ext/javy/hashes/javy-x86_64-windows-v0.3.0.gz.sha256 +1 -0
- data/ext/javy/version +1 -1
- data/ext/shopify-extensions/version +1 -1
- data/lib/project_types/extension/commands/check.rb +6 -1
- data/lib/project_types/extension/forms/questions/ask_template.rb +5 -8
- data/lib/project_types/extension/messages/messages.rb +11 -3
- data/lib/project_types/extension/models/development_server_requirements.rb +14 -7
- data/lib/project_types/extension/models/server_config/root.rb +2 -0
- data/lib/project_types/extension/models/specification_handlers/beacon_extension.rb +57 -0
- data/lib/project_types/extension/models/specification_handlers/beacon_extension_utils/script_config.rb +33 -0
- data/lib/project_types/extension/models/specification_handlers/beacon_extension_utils/script_config_repository.rb +75 -0
- data/lib/project_types/extension/models/specification_handlers/checkout_ui_extension.rb +16 -1
- data/lib/project_types/extension/tasks/configure_options.rb +2 -1
- data/lib/project_types/extension/tasks/convert_server_config.rb +13 -2
- data/lib/project_types/extension/tasks/merge_server_config.rb +5 -2
- data/lib/project_types/script/cli.rb +1 -0
- data/lib/project_types/script/config/extension_points.yml +18 -0
- data/lib/project_types/script/layers/application/create_script.rb +14 -6
- data/lib/project_types/script/layers/infrastructure/errors.rb +17 -0
- data/lib/project_types/script/layers/infrastructure/languages/project_creator.rb +6 -21
- data/lib/project_types/script/layers/infrastructure/script_service.rb +2 -0
- data/lib/project_types/script/layers/infrastructure/sparse_checkout_details.rb +35 -0
- data/lib/project_types/script/messages/messages.rb +3 -0
- data/lib/project_types/script/ui/error_handler.rb +11 -0
- data/lib/project_types/theme/cli.rb +1 -0
- data/lib/project_types/theme/commands/check.rb +4 -1
- data/lib/project_types/theme/commands/open.rb +2 -2
- data/lib/project_types/theme/commands/push.rb +1 -3
- data/lib/project_types/theme/commands/serve.rb +1 -0
- data/lib/project_types/theme/commands/share.rb +56 -0
- data/lib/project_types/theme/messages/messages.rb +71 -11
- data/lib/shopify_cli/changelog.rb +148 -0
- data/lib/shopify_cli/command.rb +7 -0
- data/lib/shopify_cli/command_options/command_serve_options.rb +10 -0
- data/lib/shopify_cli/commands/app/serve.rb +7 -7
- data/lib/shopify_cli/commands/login.rb +5 -2
- data/lib/shopify_cli/context.rb +13 -0
- data/lib/shopify_cli/git.rb +36 -0
- data/lib/shopify_cli/identity_auth.rb +24 -4
- data/lib/shopify_cli/messages/messages.rb +26 -5
- data/lib/shopify_cli/release.rb +194 -0
- data/lib/shopify_cli/sed.rb +19 -0
- data/lib/shopify_cli/services/app/create/rails_service.rb +10 -2
- data/lib/shopify_cli/services/app/serve/node_service.rb +2 -25
- data/lib/shopify_cli/services/app/serve/php_service.rb +2 -25
- data/lib/shopify_cli/services/app/serve/rails_service.rb +8 -28
- data/lib/shopify_cli/services/app/serve/serve_service.rb +57 -0
- data/lib/shopify_cli/services.rb +1 -0
- data/lib/shopify_cli/tasks/update_dashboard_urls.rb +7 -9
- data/lib/shopify_cli/theme/dev_server/hot-reload.js +40 -13
- data/lib/shopify_cli/theme/dev_server/hot_reload/remote_file_reloader.rb +1 -1
- data/lib/shopify_cli/theme/dev_server/hot_reload/sections_index.rb +51 -0
- data/lib/shopify_cli/theme/dev_server/hot_reload.rb +6 -1
- data/lib/shopify_cli/theme/dev_server/local_assets.rb +1 -1
- data/lib/shopify_cli/theme/dev_server/remote_watcher/json_files_update_job.rb +35 -0
- data/lib/shopify_cli/theme/dev_server/remote_watcher.rb +44 -0
- data/lib/shopify_cli/theme/dev_server/watcher.rb +2 -8
- data/lib/shopify_cli/theme/dev_server.rb +18 -5
- data/lib/shopify_cli/theme/file.rb +15 -4
- data/lib/shopify_cli/theme/syncer/checksums.rb +60 -0
- data/lib/shopify_cli/theme/syncer/forms/apply_to_all.rb +39 -0
- data/lib/shopify_cli/theme/syncer/forms/apply_to_all_form.rb +35 -0
- data/lib/shopify_cli/theme/syncer/forms/base_strategy_form.rb +62 -0
- data/lib/shopify_cli/theme/syncer/forms/select_delete_strategy.rb +27 -0
- data/lib/shopify_cli/theme/syncer/forms/select_update_strategy.rb +28 -0
- data/lib/shopify_cli/theme/syncer/ignore_helper.rb +33 -0
- data/lib/shopify_cli/theme/syncer/json_delete_handler.rb +51 -0
- data/lib/shopify_cli/theme/syncer/json_update_handler.rb +82 -0
- data/lib/shopify_cli/theme/syncer/merger.rb +53 -0
- data/lib/shopify_cli/theme/syncer/operation.rb +1 -1
- data/lib/shopify_cli/theme/syncer.rb +79 -63
- data/lib/shopify_cli/theme/theme.rb +26 -4
- data/lib/shopify_cli/theme/theme_admin_api.rb +23 -8
- data/lib/shopify_cli/thread_pool/job.rb +10 -2
- data/lib/shopify_cli/thread_pool.rb +15 -3
- data/lib/shopify_cli/tunnel.rb +9 -0
- data/lib/shopify_cli/version.rb +1 -1
- data/shopify-cli.gemspec +3 -1
- data/vendor/deps/cli-ui/lib/cli/ui/os.rb +8 -0
- metadata +30 -3
@@ -0,0 +1,194 @@
|
|
1
|
+
require "net/http"
|
2
|
+
require "fileutils"
|
3
|
+
require "shopify_cli/sed"
|
4
|
+
require "shopify_cli/changelog"
|
5
|
+
require "octokit"
|
6
|
+
|
7
|
+
module ShopifyCLI
|
8
|
+
class Release
|
9
|
+
def initialize(new_version, github_access_token)
|
10
|
+
@new_version = new_version
|
11
|
+
@changelog = ShopifyCLI::Changelog.new
|
12
|
+
@github = Octokit::Client.new(access_token: github_access_token)
|
13
|
+
end
|
14
|
+
|
15
|
+
def prepare!
|
16
|
+
ensure_updated_main
|
17
|
+
create_release_branch
|
18
|
+
update_changelog
|
19
|
+
update_versions_in_files
|
20
|
+
commit_packaging
|
21
|
+
pr = create_pr
|
22
|
+
system("open #{pr["html_url"]}")
|
23
|
+
end
|
24
|
+
|
25
|
+
def package!
|
26
|
+
ensure_updated_main
|
27
|
+
ensure_correct_gem_version
|
28
|
+
Rake::Task["package"].invoke
|
29
|
+
update_homebrew
|
30
|
+
create_github_release
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
attr_reader :new_version, :changelog, :github
|
36
|
+
|
37
|
+
def ensure_updated_main
|
38
|
+
# We can't be sure what is the correct action to take if changes have been
|
39
|
+
# made but not committed. Ensure the user handles the situation before
|
40
|
+
# moving on.
|
41
|
+
unless %x(git status --porcelain).empty?
|
42
|
+
raise <<~MESSAGE
|
43
|
+
Uncommitted changes have been made to the repository.
|
44
|
+
Please make sure `git status` does not show any changes before continuing.
|
45
|
+
MESSAGE
|
46
|
+
end
|
47
|
+
system_or_fail("git checkout main", "check out main branch")
|
48
|
+
unless system("git pull")
|
49
|
+
raise "git pull failed, cannot be sure there aren't new commits!"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def create_release_branch
|
54
|
+
puts "Checking out release branch"
|
55
|
+
system_or_fail("git checkout -b #{release_branch_name}", "check out release branch")
|
56
|
+
end
|
57
|
+
|
58
|
+
def update_changelog
|
59
|
+
if release_notes("Unreleased").empty?
|
60
|
+
puts "No unreleased CHANGELOG updates found!"
|
61
|
+
else
|
62
|
+
puts "Updating CHANGELOG"
|
63
|
+
changelog.update_version!(new_version)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def update_versions_in_files
|
68
|
+
version_file = File.join(ShopifyCLI::ROOT, "lib/shopify_cli/version.rb")
|
69
|
+
puts "Updating version.rb"
|
70
|
+
ShopifyCLI::Sed.new.replace_inline(version_file, ShopifyCLI::VERSION, new_version)
|
71
|
+
gemfile_lock = File.join(ShopifyCLI::ROOT, "Gemfile.lock")
|
72
|
+
puts "Updating Gemfile.lock"
|
73
|
+
ShopifyCLI::Sed.new.replace_inline(
|
74
|
+
gemfile_lock,
|
75
|
+
"shopify-cli (#{ShopifyCLI::VERSION})",
|
76
|
+
"shopify-cli (#{new_version})",
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
def commit_packaging
|
81
|
+
puts "Committing"
|
82
|
+
system_or_fail("git commit -am 'Packaging for release v#{new_version}'", "commit")
|
83
|
+
system_or_fail("git push -u origin #{release_branch_name}", "push branch")
|
84
|
+
end
|
85
|
+
|
86
|
+
def create_pr
|
87
|
+
repo = "Shopify/shopify-cli"
|
88
|
+
github.create_pull_request(
|
89
|
+
repo,
|
90
|
+
"main",
|
91
|
+
release_branch_name,
|
92
|
+
"Packaging for release v#{new_version}",
|
93
|
+
release_notes(new_version)
|
94
|
+
).tap { |results| puts "Created #{repo} PR ##{results["number"]}" }
|
95
|
+
end
|
96
|
+
|
97
|
+
def ensure_correct_gem_version
|
98
|
+
response = Net::HTTP.get(URI("https://rubygems.org/api/v1/versions/shopify-cli/latest.json"))
|
99
|
+
latest_version = JSON.parse(response)["version"]
|
100
|
+
unless latest_version == new_version
|
101
|
+
raise "Attempted to update to #{new_version}, but latest on RubyGems is #{latest_version}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def update_homebrew
|
106
|
+
ensure_updated_homebrew_repo
|
107
|
+
update_homebrew_repo
|
108
|
+
pr = create_homebrew_pr
|
109
|
+
system("open #{pr["html_url"]}")
|
110
|
+
end
|
111
|
+
|
112
|
+
def ensure_updated_homebrew_repo
|
113
|
+
unless File.exist?(homebrew_path)
|
114
|
+
system_or_fail("/opt/dev/bin/dev clone homebrew-shopify", "clone homebrew-shopify repo")
|
115
|
+
end
|
116
|
+
|
117
|
+
Dir.chdir(homebrew_path) do
|
118
|
+
system_or_fail("git checkout master && git pull", "pull latest homebrew-shopify")
|
119
|
+
system_or_fail("git checkout -b #{homebrew_release_branch}", "check out homebrew branch")
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def update_homebrew_repo
|
124
|
+
source_file = File.join(package_dir, "shopify-cli.rb")
|
125
|
+
FileUtils.copy(source_file, homebrew_path)
|
126
|
+
Dir.chdir(homebrew_path) do
|
127
|
+
system_or_fail("git commit -am '#{homebrew_update_message}'", "commit homebrew update")
|
128
|
+
system_or_fail("git push -u origin #{homebrew_release_branch}", "push homebrew branch")
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def create_homebrew_pr
|
133
|
+
repo = "Shopify/homebrew-shopify"
|
134
|
+
github.create_pull_request(
|
135
|
+
repo,
|
136
|
+
"master",
|
137
|
+
homebrew_release_branch,
|
138
|
+
homebrew_update_message,
|
139
|
+
homebrew_release_notes
|
140
|
+
).tap { |results| puts "Created #{repo} PR ##{results["number"]}" }
|
141
|
+
end
|
142
|
+
|
143
|
+
def create_github_release
|
144
|
+
release = github.create_release(
|
145
|
+
"Shopify/shopify-cli",
|
146
|
+
"v#{new_version}",
|
147
|
+
{
|
148
|
+
name: "Version #{new_version}",
|
149
|
+
body: release_notes(new_version),
|
150
|
+
}
|
151
|
+
)
|
152
|
+
%w(.deb -1.noarch.rpm).each do |suffix|
|
153
|
+
github.upload_asset(
|
154
|
+
release["url"],
|
155
|
+
File.join(package_dir, "shopify-cli-#{new_version}#{suffix}")
|
156
|
+
)
|
157
|
+
end
|
158
|
+
system("open #{release["html_url"]}")
|
159
|
+
end
|
160
|
+
|
161
|
+
def homebrew_path
|
162
|
+
@homebrew_path ||= %x(/opt/dev/bin/dev project-path homebrew-shopify).chomp
|
163
|
+
end
|
164
|
+
|
165
|
+
def homebrew_update_message
|
166
|
+
@homebrew_update_message ||= "Update Shopify CLI to #{new_version}"
|
167
|
+
end
|
168
|
+
|
169
|
+
def package_dir
|
170
|
+
@package_dir ||= File.join(ShopifyCLI::ROOT, "packaging", "builds", new_version)
|
171
|
+
end
|
172
|
+
|
173
|
+
def homebrew_release_branch
|
174
|
+
"release_#{new_version.split(".").join("_")}_of_shopify-cli"
|
175
|
+
end
|
176
|
+
|
177
|
+
def homebrew_release_notes
|
178
|
+
"I'm releasing a new version of the Shopify CLI, " \
|
179
|
+
"[#{new_version}](https://github.com/Shopify/shopify-cli/releases/tag/v#{new_version})"
|
180
|
+
end
|
181
|
+
|
182
|
+
def release_branch_name
|
183
|
+
@release_branch_name ||= "release_#{new_version.split(".").join("_")}"
|
184
|
+
end
|
185
|
+
|
186
|
+
def release_notes(version)
|
187
|
+
changelog.release_notes(version)
|
188
|
+
end
|
189
|
+
|
190
|
+
def system_or_fail(command, action)
|
191
|
+
raise "Failed to #{action}!" unless system(command)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ShopifyCLI
|
2
|
+
class Sed
|
3
|
+
class SedError < StandardError; end
|
4
|
+
|
5
|
+
def replace_inline(filename, pattern, output)
|
6
|
+
command =
|
7
|
+
case CLI::Kit::System.os
|
8
|
+
when :mac
|
9
|
+
"sed -i ''"
|
10
|
+
when :linux
|
11
|
+
"sed -i"
|
12
|
+
else
|
13
|
+
raise "Unrecognized system!"
|
14
|
+
end
|
15
|
+
success = system("#{command} 's/#{pattern}/#{output}/' #{filename}")
|
16
|
+
raise SedError unless success
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -108,8 +108,10 @@ module ShopifyCLI
|
|
108
108
|
end
|
109
109
|
|
110
110
|
def check_ruby
|
111
|
-
ruby_version = Environment.ruby_version(context)
|
112
|
-
return if ruby_version.satisfies?("~>2.5") ||
|
111
|
+
ruby_version = Environment.ruby_version(context: context)
|
112
|
+
return if ruby_version.satisfies?("~>2.5") ||
|
113
|
+
ruby_version.satisfies?("~>3.0.0") ||
|
114
|
+
ruby_version.satisfies?("~>3.1.0")
|
113
115
|
context.abort(context.message("core.app.create.rails.error.invalid_ruby_version"))
|
114
116
|
end
|
115
117
|
|
@@ -205,6 +207,12 @@ module ShopifyCLI
|
|
205
207
|
end
|
206
208
|
|
207
209
|
def set_custom_ua
|
210
|
+
requires_ua_file = Dir.chdir(context.root) do
|
211
|
+
context.ruby_gem_version("shopify_app") < ::Semantic::Version.new("19.0.0")
|
212
|
+
end
|
213
|
+
|
214
|
+
return unless requires_ua_file
|
215
|
+
|
208
216
|
ua_path = File.join("config", "initializers", "user_agent.rb")
|
209
217
|
context.write(ua_path, USER_AGENT_CODE)
|
210
218
|
end
|
@@ -2,32 +2,9 @@ module ShopifyCLI
|
|
2
2
|
module Services
|
3
3
|
module App
|
4
4
|
module Serve
|
5
|
-
class NodeService <
|
6
|
-
attr_accessor :host, :port, :context
|
7
|
-
|
8
|
-
def initialize(host:, port:, context:)
|
9
|
-
@host = host
|
10
|
-
@port = port
|
11
|
-
@context = context
|
12
|
-
super()
|
13
|
-
end
|
14
|
-
|
5
|
+
class NodeService < ServeService
|
15
6
|
def call
|
16
|
-
|
17
|
-
url = host || ShopifyCLI::Tunnel.start(context, port: port)
|
18
|
-
raise ShopifyCLI::Abort,
|
19
|
-
context.message("core.app.serve.error.host_must_be_https") if url.match(/^https/i).nil?
|
20
|
-
project.env.update(context, :host, url)
|
21
|
-
ShopifyCLI::Tasks::UpdateDashboardURLS.call(
|
22
|
-
context,
|
23
|
-
url: url,
|
24
|
-
callback_url: "/auth/callback",
|
25
|
-
)
|
26
|
-
|
27
|
-
if project.env.shop
|
28
|
-
project_url = "#{project.env.host}/auth?shop=#{project.env.shop}"
|
29
|
-
context.puts("\n" + context.message("core.app.serve.open_info", project_url) + "\n")
|
30
|
-
end
|
7
|
+
generate_url
|
31
8
|
|
32
9
|
CLI::UI::Frame.open(context.message("core.app.serve.running_server")) do
|
33
10
|
env = project.env.to_h
|
@@ -2,32 +2,9 @@ module ShopifyCLI
|
|
2
2
|
module Services
|
3
3
|
module App
|
4
4
|
module Serve
|
5
|
-
class PHPService <
|
6
|
-
attr_accessor :host, :port, :context
|
7
|
-
|
8
|
-
def initialize(host:, port:, context:)
|
9
|
-
@host = host
|
10
|
-
@port = port
|
11
|
-
@context = context
|
12
|
-
super()
|
13
|
-
end
|
14
|
-
|
5
|
+
class PHPService < ServeService
|
15
6
|
def call
|
16
|
-
|
17
|
-
url = host || ShopifyCLI::Tunnel.start(context, port: port)
|
18
|
-
raise ShopifyCLI::Abort,
|
19
|
-
context.message("core.app.serve.error.host_must_be_https") if url.match(/^https/i).nil?
|
20
|
-
project.env.update(context, :host, url)
|
21
|
-
ShopifyCLI::Tasks::UpdateDashboardURLS.call(
|
22
|
-
context,
|
23
|
-
url: url,
|
24
|
-
callback_url: "/auth/callback",
|
25
|
-
)
|
26
|
-
|
27
|
-
if project.env.shop
|
28
|
-
project_url = "#{project.env.host}/login?shop=#{project.env.shop}"
|
29
|
-
context.puts("\n" + context.message("core.app.serve.open_info", project_url) + "\n")
|
30
|
-
end
|
7
|
+
generate_url
|
31
8
|
|
32
9
|
CLI::UI::Frame.open(context.message("core.app.serve.running_server")) do
|
33
10
|
if ShopifyCLI::ProcessSupervision.running?(:npm_watch)
|
@@ -2,38 +2,18 @@ module ShopifyCLI
|
|
2
2
|
module Services
|
3
3
|
module App
|
4
4
|
module Serve
|
5
|
-
class RailsService <
|
6
|
-
attr_accessor :host, :port, :context
|
7
|
-
|
8
|
-
def initialize(host:, port:, context:)
|
9
|
-
@host = host
|
10
|
-
@port = port
|
11
|
-
@context = context
|
12
|
-
super()
|
13
|
-
end
|
14
|
-
|
5
|
+
class RailsService < ServeService
|
15
6
|
def call
|
16
|
-
|
17
|
-
url = host || ShopifyCLI::Tunnel.start(context, port: port)
|
18
|
-
raise ShopifyCLI::Abort,
|
19
|
-
context.message("core.app.serve.error.host_must_be_https") if url.match(/^https/i).nil?
|
20
|
-
project.env.update(context, :host, url)
|
21
|
-
ShopifyCLI::Tasks::UpdateDashboardURLS.call(
|
22
|
-
context,
|
23
|
-
url: url,
|
24
|
-
callback_url: "/auth/shopify/callback",
|
25
|
-
)
|
26
|
-
|
27
|
-
if project.env.shop
|
28
|
-
project_url = "#{project.env.host}/login?shop=#{project.env.shop}"
|
29
|
-
context.puts("\n" + context.message("core.app.serve.open_info", project_url) + "\n")
|
30
|
-
end
|
7
|
+
generate_url
|
31
8
|
|
32
9
|
CLI::UI::Frame.open(context.message("core.app.serve.running_server")) do
|
33
|
-
|
34
|
-
env.
|
10
|
+
original_env = JSON.parse(ENV["ORIGINAL_ENV"] || "{}")
|
11
|
+
env = original_env.merge(ShopifyCLI::Project.current.env.to_h)
|
12
|
+
env.delete("HOST") if context.ruby_gem_version("shopify_app") < ::Semantic::Version.new("19.0.0")
|
35
13
|
env["PORT"] = port.to_s
|
36
|
-
env["GEM_PATH"] =
|
14
|
+
env["GEM_PATH"] =
|
15
|
+
[env["GEM_PATH"], Rails::Gem.gem_path(context)].compact
|
16
|
+
.join(CLI::UI::OS.current.path_separator)
|
37
17
|
if context.windows?
|
38
18
|
context.system("ruby bin\\rails server", env: env)
|
39
19
|
else
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module ShopifyCLI
|
2
|
+
module Services
|
3
|
+
module App
|
4
|
+
module Serve
|
5
|
+
class ServeService < BaseService
|
6
|
+
attr_accessor :host, :port, :no_update, :context
|
7
|
+
|
8
|
+
def initialize(host:, port:, no_update:, context:)
|
9
|
+
@host = host
|
10
|
+
@port = port
|
11
|
+
@no_update = no_update
|
12
|
+
@context = context
|
13
|
+
super()
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
raise NotImplementedError
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def generate_url
|
23
|
+
create_tunnel
|
24
|
+
update_url unless no_update
|
25
|
+
show_app_url
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_tunnel
|
29
|
+
url = host || ShopifyCLI::Tunnel.start(context, port: port)
|
30
|
+
raise ShopifyCLI::Abort,
|
31
|
+
context.message("core.app.serve.error.host_must_be_https") if url.match(/^https/i).nil?
|
32
|
+
project.env.update(context, :host, url)
|
33
|
+
end
|
34
|
+
|
35
|
+
def update_url
|
36
|
+
ShopifyCLI::Tasks::UpdateDashboardURLS.call(
|
37
|
+
context,
|
38
|
+
url: project.env.host,
|
39
|
+
callback_url: "/auth/shopify/callback",
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
def show_app_url
|
44
|
+
return unless project.env.shop
|
45
|
+
|
46
|
+
project_url = "#{project.env.host}/login?shop=#{project.env.shop}"
|
47
|
+
context.puts("\n" + context.message("core.app.serve.open_info", project_url) + "\n")
|
48
|
+
end
|
49
|
+
|
50
|
+
def project
|
51
|
+
@project ||= ShopifyCLI::Project.current
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/shopify_cli/services.rb
CHANGED
@@ -5,6 +5,7 @@ module ShopifyCLI
|
|
5
5
|
|
6
6
|
module App
|
7
7
|
module Serve
|
8
|
+
autoload :ServeService, "shopify_cli/services/app/serve/serve_service"
|
8
9
|
autoload :NodeService, "shopify_cli/services/app/serve/node_service"
|
9
10
|
autoload :RailsService, "shopify_cli/services/app/serve/rails_service"
|
10
11
|
autoload :PHPService, "shopify_cli/services/app/serve/php_service"
|
@@ -9,24 +9,22 @@ module ShopifyCLI
|
|
9
9
|
api_key = project.env.api_key
|
10
10
|
result = ShopifyCLI::PartnersAPI.query(ctx, "get_app_urls", apiKey: api_key)
|
11
11
|
app = result["data"]["app"]
|
12
|
-
|
12
|
+
|
13
|
+
return if app["applicationUrl"].match(url)
|
14
|
+
|
13
15
|
constructed_urls = construct_redirect_urls(app["redirectUrlWhitelist"], url, callback_url)
|
14
|
-
return if url == app["applicationUrl"]
|
15
16
|
ShopifyCLI::PartnersAPI.query(@ctx, "update_dashboard_urls", input: {
|
16
|
-
applicationUrl:
|
17
|
-
redirectUrlWhitelist: constructed_urls,
|
17
|
+
applicationUrl: url,
|
18
|
+
redirectUrlWhitelist: constructed_urls,
|
19
|
+
apiKey: api_key,
|
18
20
|
})
|
21
|
+
|
19
22
|
@ctx.puts(@ctx.message("core.tasks.update_dashboard_urls.updated"))
|
20
23
|
rescue
|
21
24
|
@ctx.puts(@ctx.message("core.tasks.update_dashboard_urls.update_error", ShopifyCLI::TOOL_NAME))
|
22
25
|
raise
|
23
26
|
end
|
24
27
|
|
25
|
-
def check_application_url(application_url, new_url)
|
26
|
-
return false if application_url.match(new_url)
|
27
|
-
CLI::UI::Prompt.confirm(@ctx.message("core.tasks.update_dashboard_urls.update_prompt"))
|
28
|
-
end
|
29
|
-
|
30
28
|
def construct_redirect_urls(urls, new_url, callback_url)
|
31
29
|
new_urls = urls.map do |url|
|
32
30
|
if (match = url.match(NGROK_REGEX))
|
@@ -15,17 +15,37 @@
|
|
15
15
|
eventSource.onerror = () => eventSource.close();
|
16
16
|
}
|
17
17
|
|
18
|
+
function sectionNamesByType(type) {
|
19
|
+
const namespace = window.__SHOPIFY_CLI_ENV__;
|
20
|
+
return namespace.section_names_by_type[type] || [];
|
21
|
+
}
|
22
|
+
|
18
23
|
function reloadMode() {
|
19
|
-
|
24
|
+
const namespace = window.__SHOPIFY_CLI_ENV__;
|
20
25
|
return namespace.mode;
|
21
26
|
}
|
22
27
|
|
28
|
+
function querySelectDOMSections(idSuffix) {
|
29
|
+
const elements = document.querySelectorAll(`[id^='shopify-section'][id$='${idSuffix}']`);
|
30
|
+
return Array.from(elements);
|
31
|
+
}
|
32
|
+
|
33
|
+
function fetchDOMSections(name) {
|
34
|
+
const domSections = sectionNamesByType(name).flatMap((n) => querySelectDOMSections(n));
|
35
|
+
|
36
|
+
if (domSections.length > 0) {
|
37
|
+
return domSections;
|
38
|
+
}
|
39
|
+
|
40
|
+
return querySelectDOMSections(name);
|
41
|
+
}
|
42
|
+
|
23
43
|
function isFullPageReloadMode(){
|
24
|
-
return reloadMode() ===
|
44
|
+
return reloadMode() === 'full-page';
|
25
45
|
}
|
26
46
|
|
27
47
|
function isReloadModeActive(){
|
28
|
-
return reloadMode() !==
|
48
|
+
return reloadMode() !== 'off';
|
29
49
|
}
|
30
50
|
|
31
51
|
function isRefreshRequired(files) {
|
@@ -104,26 +124,28 @@
|
|
104
124
|
constructor(filename) {
|
105
125
|
this.filename = filename;
|
106
126
|
this.name = filename.split('/').pop().replace('.liquid', '');
|
107
|
-
this.
|
127
|
+
this.elements = fetchDOMSections(this.name);
|
108
128
|
}
|
109
129
|
|
110
130
|
valid() {
|
111
|
-
return this.filename.startsWith('sections/') && this.
|
131
|
+
return this.filename.startsWith('sections/') && this.elements.length > 0;
|
112
132
|
}
|
113
133
|
|
114
|
-
async
|
115
|
-
|
116
|
-
|
134
|
+
async refreshElement(element) {
|
135
|
+
|
136
|
+
const sectionId = element.id.replace(/^shopify-section-/, '');
|
137
|
+
const url = new URL(window.location.href);
|
138
|
+
|
139
|
+
url.searchParams.append('section_id', sectionId);
|
140
|
+
|
141
|
+
const response = await fetch(url);
|
117
142
|
|
118
143
|
try {
|
119
|
-
const response = await fetch(url);
|
120
144
|
if (response.headers.get('x-templates-from-params') == '1') {
|
121
145
|
const html = await response.text();
|
122
|
-
|
123
|
-
|
124
|
-
console.log(`[HotReload] Reloaded ${this.name} section`);
|
146
|
+
element.outerHTML = html;
|
125
147
|
} else {
|
126
|
-
window.location.reload()
|
148
|
+
window.location.reload();
|
127
149
|
|
128
150
|
console.log(`[HotReload] Hot-reloading not supported, fully reloading ${this.name} section`);
|
129
151
|
}
|
@@ -132,6 +154,11 @@
|
|
132
154
|
console.log(`[HotReload] Failed to reload ${this.name} section: ${e.message}`);
|
133
155
|
}
|
134
156
|
}
|
157
|
+
|
158
|
+
async refresh() {
|
159
|
+
console.log(`[HotReload] Reloaded ${this.name} sections`);
|
160
|
+
this.elements.forEach(this.refreshElement);
|
161
|
+
}
|
135
162
|
}
|
136
163
|
|
137
164
|
if (isReloadModeActive()) {
|
@@ -51,7 +51,7 @@ module ShopifyCLI
|
|
51
51
|
def fetch_asset(file)
|
52
52
|
api_client.get(
|
53
53
|
path: "themes/#{@theme.id}/assets.json",
|
54
|
-
query: URI.encode_www_form("asset[key]" => file.relative_path
|
54
|
+
query: URI.encode_www_form("asset[key]" => file.relative_path),
|
55
55
|
)
|
56
56
|
rescue ShopifyCLI::API::APIRequestNotFoundError
|
57
57
|
[404, {}]
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ShopifyCLI
|
4
|
+
module Theme
|
5
|
+
module DevServer
|
6
|
+
class HotReload
|
7
|
+
class SectionsIndex
|
8
|
+
def initialize(theme)
|
9
|
+
@theme = theme
|
10
|
+
end
|
11
|
+
|
12
|
+
def section_names_by_type
|
13
|
+
index = {}
|
14
|
+
|
15
|
+
files.each do |file|
|
16
|
+
section_hash(file).each do |key, value|
|
17
|
+
name = key
|
18
|
+
type = value&.dig("type")
|
19
|
+
|
20
|
+
next if !name || !type
|
21
|
+
|
22
|
+
index[type] = [] unless index[type]
|
23
|
+
index[type] << name
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
index
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def section_hash(file)
|
33
|
+
content = JSON.parse(file.read)
|
34
|
+
return [] unless content.is_a?(Hash)
|
35
|
+
|
36
|
+
sections = content["sections"]
|
37
|
+
return [] if sections.nil?
|
38
|
+
|
39
|
+
sections
|
40
|
+
rescue JSON::JSONError
|
41
|
+
[]
|
42
|
+
end
|
43
|
+
|
44
|
+
def files
|
45
|
+
@theme.json_files
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "hot_reload/remote_file_reloader"
|
4
|
+
require_relative "hot_reload/sections_index"
|
4
5
|
|
5
6
|
module ShopifyCLI
|
6
7
|
module Theme
|
@@ -13,6 +14,7 @@ module ShopifyCLI
|
|
13
14
|
@mode = mode
|
14
15
|
@streams = SSE::Streams.new
|
15
16
|
@remote_file_reloader = RemoteFileReloader.new(ctx, theme: @theme, streams: @streams)
|
17
|
+
@sections_index = SectionsIndex.new(@theme)
|
16
18
|
@watcher = watcher
|
17
19
|
@watcher.add_observer(self, :notify_streams_of_file_change)
|
18
20
|
@ignore_filter = ignore_filter
|
@@ -78,7 +80,10 @@ module ShopifyCLI
|
|
78
80
|
end
|
79
81
|
|
80
82
|
def params_js
|
81
|
-
env = {
|
83
|
+
env = {
|
84
|
+
mode: @mode,
|
85
|
+
section_names_by_type: @sections_index.section_names_by_type,
|
86
|
+
}
|
82
87
|
<<~JS
|
83
88
|
(() => {
|
84
89
|
window.__SHOPIFY_CLI_ENV__ = #{env.to_json};
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "shopify_cli/thread_pool/job"
|
4
|
+
|
5
|
+
module ShopifyCLI
|
6
|
+
module Theme
|
7
|
+
module DevServer
|
8
|
+
class RemoteWatcher
|
9
|
+
class JsonFilesUpdateJob < ShopifyCLI::ThreadPool::Job
|
10
|
+
def initialize(theme, syncer, interval)
|
11
|
+
super(interval)
|
12
|
+
|
13
|
+
@theme = theme
|
14
|
+
@syncer = syncer
|
15
|
+
end
|
16
|
+
|
17
|
+
def perform!
|
18
|
+
@syncer.fetch_checksums!
|
19
|
+
@syncer.enqueue_get(json_files)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def json_files
|
25
|
+
@theme
|
26
|
+
.json_files
|
27
|
+
.reject { |file| @syncer.pending_updates.include?(file) }
|
28
|
+
.reject { |file| @syncer.broken_file?(file) }
|
29
|
+
.reject { |file| @syncer.ignore_file?(file) }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|