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.
- 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
|