shopify-cli 2.11.0 → 2.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +29 -1
- data/Gemfile.lock +1 -1
- data/bin/shopify +13 -0
- data/docs/users/installation.md +1 -44
- data/lib/project_types/extension/commands/build.rb +0 -3
- data/lib/project_types/extension/commands/check.rb +0 -1
- data/lib/project_types/extension/commands/create.rb +0 -1
- data/lib/project_types/extension/commands/push.rb +13 -1
- data/lib/project_types/extension/commands/serve.rb +0 -1
- data/lib/project_types/extension/loaders/project.rb +28 -8
- data/lib/project_types/extension/messages/messages.rb +10 -2
- data/lib/project_types/extension/models/specification_handlers/checkout_ui_extension.rb +114 -0
- data/lib/project_types/extension/models/specification_handlers/theme_app_extension.rb +7 -1
- data/lib/project_types/script/cli.rb +2 -0
- data/lib/project_types/script/commands/create.rb +2 -2
- data/lib/project_types/script/commands/push.rb +4 -6
- data/lib/project_types/script/config/extension_points.yml +0 -4
- data/lib/project_types/script/forms/create.rb +1 -14
- data/lib/project_types/script/layers/application/connect_app.rb +3 -2
- data/lib/project_types/script/layers/application/create_script.rb +1 -1
- data/lib/project_types/script/layers/application/push_script.rb +1 -1
- data/lib/project_types/script/layers/infrastructure/errors.rb +11 -0
- data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_project_creator.rb +2 -6
- data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_task_runner.rb +30 -26
- data/lib/project_types/script/layers/infrastructure/languages/task_runner.rb +35 -9
- 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 +3 -6
- data/lib/project_types/script/layers/infrastructure/languages/typescript_task_runner.rb +31 -27
- data/lib/project_types/script/layers/infrastructure/languages/wasm_task_runner.rb +3 -7
- data/lib/project_types/script/loaders/project.rb +8 -7
- data/lib/project_types/script/messages/messages.rb +14 -13
- data/lib/project_types/script/ui/error_handler.rb +13 -0
- data/lib/project_types/theme/commands/check.rb +0 -1
- data/lib/project_types/theme/commands/common/root_helper.rb +65 -0
- data/lib/project_types/theme/commands/delete.rb +0 -1
- data/lib/project_types/theme/commands/init.rb +2 -1
- data/lib/project_types/theme/commands/language_server.rb +0 -1
- data/lib/project_types/theme/commands/package.rb +0 -1
- data/lib/project_types/theme/commands/publish.rb +0 -1
- data/lib/project_types/theme/commands/pull.rb +18 -9
- data/lib/project_types/theme/commands/push.rb +16 -11
- data/lib/project_types/theme/commands/serve.rb +6 -3
- 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/messages/messages.rb +5 -5
- data/lib/shopify_cli/command.rb +11 -3
- 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 +2 -1
- data/lib/shopify_cli/commands/app/create.rb +0 -3
- data/lib/shopify_cli/commands/app/deploy.rb +1 -1
- data/lib/shopify_cli/commands/app/serve.rb +0 -1
- data/lib/shopify_cli/constants.rb +2 -2
- data/lib/shopify_cli/environment.rb +45 -45
- data/lib/shopify_cli/git.rb +9 -1
- data/lib/shopify_cli/messages/messages.rb +23 -2
- 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 +63 -0
- data/lib/shopify_cli/theme/dev_server/hot_reload.rb +22 -6
- data/lib/shopify_cli/theme/dev_server/proxy.rb +18 -7
- data/lib/shopify_cli/theme/dev_server.rb +1 -3
- data/lib/shopify_cli/theme/development_theme.rb +11 -0
- data/lib/shopify_cli/theme/file.rb +4 -0
- data/lib/shopify_cli/theme/include_filter.rb +4 -2
- data/lib/shopify_cli/theme/syncer.rb +13 -4
- data/lib/shopify_cli/theme/theme.rb +0 -4
- data/lib/shopify_cli/version.rb +1 -1
- metadata +9 -2
@@ -4,14 +4,21 @@ require "shopify_cli/theme/development_theme"
|
|
4
4
|
require "shopify_cli/theme/ignore_filter"
|
5
5
|
require "shopify_cli/theme/include_filter"
|
6
6
|
require "shopify_cli/theme/syncer"
|
7
|
+
require "project_types/theme/commands/common/root_helper"
|
8
|
+
require "project_types/theme/conversions/include_glob"
|
9
|
+
require "project_types/theme/conversions/ignore_glob"
|
7
10
|
|
8
11
|
module Theme
|
9
12
|
class Command
|
10
13
|
class Push < ShopifyCLI::Command::SubCommand
|
11
|
-
|
14
|
+
include Common::RootHelper
|
15
|
+
|
12
16
|
recommend_default_ruby_range
|
13
17
|
|
14
18
|
options do |parser, flags|
|
19
|
+
Conversions::IncludeGlob.register(parser)
|
20
|
+
Conversions::IgnoreGlob.register(parser)
|
21
|
+
|
15
22
|
parser.on("-n", "--nodelete") { flags[:nodelete] = true }
|
16
23
|
parser.on("-i", "--themeid=ID") { |theme_id| flags[:theme_id] = theme_id }
|
17
24
|
parser.on("-t", "--theme=NAME_OR_ID") { |theme| flags[:theme] = theme }
|
@@ -21,18 +28,18 @@ module Theme
|
|
21
28
|
parser.on("-j", "--json") { flags[:json] = true }
|
22
29
|
parser.on("-a", "--allow-live") { flags[:allow_live] = true }
|
23
30
|
parser.on("-p", "--publish") { flags[:publish] = true }
|
24
|
-
parser.on("-o", "--only=PATTERN") do |pattern|
|
31
|
+
parser.on("-o", "--only=PATTERN", Conversions::IncludeGlob) do |pattern|
|
25
32
|
flags[:includes] ||= []
|
26
|
-
flags[:includes]
|
33
|
+
flags[:includes] += pattern
|
27
34
|
end
|
28
|
-
parser.on("-x", "--ignore=PATTERN") do |pattern|
|
35
|
+
parser.on("-x", "--ignore=PATTERN", Conversions::IgnoreGlob) do |pattern|
|
29
36
|
flags[:ignores] ||= []
|
30
|
-
flags[:ignores]
|
37
|
+
flags[:ignores] += pattern
|
31
38
|
end
|
32
39
|
end
|
33
40
|
|
34
|
-
def call(
|
35
|
-
root =
|
41
|
+
def call(_args, name)
|
42
|
+
root = root_value(options, name)
|
36
43
|
delete = !options.flags[:nodelete]
|
37
44
|
theme = find_theme(root, **options.flags)
|
38
45
|
return if theme.nil?
|
@@ -43,7 +50,7 @@ module Theme
|
|
43
50
|
return unless CLI::UI::Prompt.confirm(question)
|
44
51
|
end
|
45
52
|
|
46
|
-
include_filter = ShopifyCLI::Theme::IncludeFilter.new(options.flags[:includes])
|
53
|
+
include_filter = ShopifyCLI::Theme::IncludeFilter.new(root, options.flags[:includes])
|
47
54
|
ignore_filter = ShopifyCLI::Theme::IgnoreFilter.from_path(root)
|
48
55
|
ignore_filter.add_patterns(options.flags[:ignores]) if options.flags[:ignores]
|
49
56
|
|
@@ -92,9 +99,7 @@ module Theme
|
|
92
99
|
end
|
93
100
|
|
94
101
|
if development
|
95
|
-
|
96
|
-
new_theme.ensure_exists!
|
97
|
-
return new_theme
|
102
|
+
return ShopifyCLI::Theme::DevelopmentTheme.find_or_create!(@ctx, root: root)
|
98
103
|
end
|
99
104
|
|
100
105
|
if unpublished
|
@@ -1,10 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require "shopify_cli/theme/dev_server"
|
3
|
+
require "project_types/theme/commands/common/root_helper"
|
3
4
|
|
4
5
|
module Theme
|
5
6
|
class Command
|
6
7
|
class Serve < ShopifyCLI::Command::SubCommand
|
7
|
-
|
8
|
+
include Common::RootHelper
|
9
|
+
|
8
10
|
recommend_default_ruby_range
|
9
11
|
|
10
12
|
DEFAULT_HTTP_HOST = "127.0.0.1"
|
@@ -16,10 +18,11 @@ module Theme
|
|
16
18
|
parser.on("--live-reload=MODE") { |mode| flags[:mode] = as_reload_mode(mode) }
|
17
19
|
end
|
18
20
|
|
19
|
-
def call(
|
21
|
+
def call(_args, name)
|
22
|
+
root = root_value(options, name)
|
20
23
|
flags = options.flags.dup
|
21
24
|
host = flags[:host] || DEFAULT_HTTP_HOST
|
22
|
-
ShopifyCLI::Theme::DevServer.start(@ctx,
|
25
|
+
ShopifyCLI::Theme::DevServer.start(@ctx, root, host: host, **flags) do |syncer|
|
23
26
|
UI::SyncProgressBar.new(syncer).progress(:upload_theme!, delay_low_priority_files: true)
|
24
27
|
end
|
25
28
|
rescue ShopifyCLI::Theme::DevServer::AddressBindingError
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Theme
|
4
|
+
module Conversions
|
5
|
+
class BaseGlob
|
6
|
+
class << self
|
7
|
+
def register(parser)
|
8
|
+
parser.accept(self) { |_val| convert(parser) }
|
9
|
+
end
|
10
|
+
|
11
|
+
def convert(parser)
|
12
|
+
argv = parser.default_argv
|
13
|
+
option_index = argv.index { |v| options.include?(v) }
|
14
|
+
|
15
|
+
return [] if option_index.nil?
|
16
|
+
|
17
|
+
start_index = option_index + 1
|
18
|
+
option_by_key = options_map(parser)
|
19
|
+
values = []
|
20
|
+
|
21
|
+
argv[start_index..-1].each do |value|
|
22
|
+
return values unless option_by_key[value].nil?
|
23
|
+
values << value
|
24
|
+
end
|
25
|
+
|
26
|
+
values
|
27
|
+
end
|
28
|
+
|
29
|
+
def options
|
30
|
+
raise "`#{self.class.name}#options` must be defined"
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def options_map(parser)
|
36
|
+
map = {}
|
37
|
+
parser.top.list.each do |option|
|
38
|
+
map[option.short.first] = option
|
39
|
+
map[option.long.first] = option
|
40
|
+
end
|
41
|
+
map
|
42
|
+
end
|
43
|
+
|
44
|
+
def parameter?(value)
|
45
|
+
value.start_with?("-")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -65,8 +65,8 @@ module Theme
|
|
65
65
|
{{command:-j, --json}} Output JSON instead of a UI.
|
66
66
|
{{command:-a, --allow-live}} Allow push to a live theme.
|
67
67
|
{{command:-p, --publish}} Publish as the live theme after uploading.
|
68
|
-
{{command:-o, --only}} Upload only the specified files.
|
69
|
-
{{command:-x, --ignore}} Skip uploading the specified files.
|
68
|
+
{{command:-o, --only}} Upload only the specified files (Multiple flags allowed).
|
69
|
+
{{command:-x, --ignore}} Skip uploading the specified files (Multiple flags allowed).
|
70
70
|
|
71
71
|
Run without options to select theme from a list.
|
72
72
|
HELP
|
@@ -96,7 +96,7 @@ module Theme
|
|
96
96
|
help: <<~HELP,
|
97
97
|
Uploads the current theme as a development theme to the connected store, then prints theme editor and preview URLs to your terminal. While running, changes will push to the store in real time.
|
98
98
|
|
99
|
-
Usage: {{command:%s theme serve}}
|
99
|
+
Usage: {{command:%s theme serve [ ROOT ]}}
|
100
100
|
|
101
101
|
Options:
|
102
102
|
{{command:--port=PORT}} Local port to serve theme preview from.
|
@@ -202,8 +202,8 @@ module Theme
|
|
202
202
|
{{command:-l, --live}} Pull theme files from your remote live theme.
|
203
203
|
{{command:-d, --development}} Pull theme files from your remote development theme.
|
204
204
|
{{command:-n, --nodelete}} Runs the pull command without deleting local files.
|
205
|
-
{{command:-o, --only}} Download only the specified files.
|
206
|
-
{{command:-x, --ignore}} Skip downloading the specified files.
|
205
|
+
{{command:-o, --only}} Download only the specified files (Multiple flags allowed).
|
206
|
+
{{command:-x, --ignore}} Skip downloading the specified files (Multiple flags allowed).
|
207
207
|
|
208
208
|
Run without options to select theme from a list.
|
209
209
|
HELP
|
data/lib/shopify_cli/command.rb
CHANGED
@@ -100,14 +100,22 @@ module ShopifyCLI
|
|
100
100
|
end
|
101
101
|
|
102
102
|
def check_node_version
|
103
|
+
return unless @compatible_node_range
|
104
|
+
|
105
|
+
context = Context.new
|
106
|
+
if context.which("node").nil?
|
107
|
+
raise ShopifyCLI::Abort, context.message("core.errors.missing_node")
|
108
|
+
end
|
109
|
+
|
103
110
|
check_version(
|
104
111
|
Environment.node_version,
|
105
112
|
range: @compatible_node_range,
|
106
|
-
runtime: "Node"
|
113
|
+
runtime: "Node",
|
114
|
+
context: context
|
107
115
|
)
|
108
116
|
end
|
109
117
|
|
110
|
-
def check_version(version, range:, runtime:)
|
118
|
+
def check_version(version, range:, runtime:, context: Context.new)
|
111
119
|
return if Environment.test?
|
112
120
|
return if range.nil?
|
113
121
|
|
@@ -116,7 +124,7 @@ module ShopifyCLI
|
|
116
124
|
is_lower_than_top = version_without_pre_nor_build < Utilities.version_dropping_pre_and_build(range.to)
|
117
125
|
return if is_higher_than_bottom && is_lower_than_top
|
118
126
|
|
119
|
-
|
127
|
+
context.warn("Your environment #{runtime} version, #{version},"\
|
120
128
|
" is outside of the range supported by the CLI,"\
|
121
129
|
" #{range.from}..<#{range.to},"\
|
122
130
|
" and might cause incompatibility issues.")
|
@@ -4,9 +4,10 @@ module ShopifyCLI
|
|
4
4
|
class Create
|
5
5
|
class Rails < ShopifyCLI::Command::AppSubCommand
|
6
6
|
prerequisite_task :ensure_authenticated
|
7
|
+
prerequisite_task :ensure_git_dependency
|
7
8
|
|
8
|
-
recommend_default_node_range
|
9
9
|
recommend_default_ruby_range
|
10
|
+
recommend_default_node_range
|
10
11
|
|
11
12
|
options do |parser, flags|
|
12
13
|
parser.on("--name=NAME") { |t| flags[:name] = t }
|
@@ -6,9 +6,6 @@ module ShopifyCLI
|
|
6
6
|
subcommand :PHP, "php", "shopify_cli/commands/app/create/php"
|
7
7
|
subcommand :Node, "node", "shopify_cli/commands/app/create/node"
|
8
8
|
|
9
|
-
recommend_default_node_range
|
10
|
-
recommend_default_ruby_range
|
11
|
-
|
12
9
|
def call(_args, _command_name)
|
13
10
|
@ctx.puts(self.class.help)
|
14
11
|
end
|
@@ -3,8 +3,8 @@ module ShopifyCLI
|
|
3
3
|
class App
|
4
4
|
class Deploy < ShopifyCLI::Command::AppSubCommand
|
5
5
|
subcommand :Heroku, "heroku", "shopify_cli/commands/app/deploy/heroku"
|
6
|
+
prerequisite_task :ensure_git_dependency
|
6
7
|
|
7
|
-
recommend_default_node_range
|
8
8
|
recommend_default_ruby_range
|
9
9
|
|
10
10
|
def call(args, _name)
|
@@ -38,7 +38,7 @@ module ShopifyCLI
|
|
38
38
|
|
39
39
|
# When true the CLI points to spin instances of services
|
40
40
|
SPIN = "SPIN"
|
41
|
-
|
41
|
+
SPIN_INSTANCE = "SPIN_INSTANCE"
|
42
42
|
SPIN_WORKSPACE = "SPIN_WORKSPACE"
|
43
43
|
SPIN_NAMESPACE = "SPIN_NAMESPACE"
|
44
44
|
SPIN_HOST = "SPIN_HOST"
|
@@ -65,7 +65,7 @@ module ShopifyCLI
|
|
65
65
|
end
|
66
66
|
|
67
67
|
module Node
|
68
|
-
FROM = "
|
68
|
+
FROM = "14.5.0"
|
69
69
|
TO = "17.0.0"
|
70
70
|
end
|
71
71
|
end
|
@@ -5,6 +5,11 @@ module ShopifyCLI
|
|
5
5
|
# the environment in which the CLI runs
|
6
6
|
module Environment
|
7
7
|
TRUTHY_ENV_VARIABLE_VALUES = ["1", "true", "TRUE", "yes", "YES"]
|
8
|
+
SPIN_OVERRIDE_ENV_NAMES = [
|
9
|
+
Constants::EnvironmentVariables::SPIN_WORKSPACE,
|
10
|
+
Constants::EnvironmentVariables::SPIN_NAMESPACE,
|
11
|
+
Constants::EnvironmentVariables::SPIN_HOST,
|
12
|
+
]
|
8
13
|
|
9
14
|
def self.ruby_version(context: Context.new)
|
10
15
|
out, err, stat = context.capture3('ruby -e "puts RUBY_VERSION"')
|
@@ -20,6 +25,12 @@ module ShopifyCLI
|
|
20
25
|
::Semantic::Version.new(out.chomp)
|
21
26
|
end
|
22
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
|
+
|
23
34
|
def self.interactive=(interactive)
|
24
35
|
@interactive = interactive
|
25
36
|
end
|
@@ -87,6 +98,20 @@ module ShopifyCLI
|
|
87
98
|
end
|
88
99
|
end
|
89
100
|
|
101
|
+
def self.spin_url_override(env_variables: ENV)
|
102
|
+
tokens = SPIN_OVERRIDE_ENV_NAMES.map do |name|
|
103
|
+
env_variables[name]
|
104
|
+
end
|
105
|
+
|
106
|
+
return if tokens.all?(&:nil?)
|
107
|
+
|
108
|
+
if tokens.any?(&:nil?)
|
109
|
+
raise "To manually target a spin instance, you must set #{SPIN_OVERRIDE_ENV_NAMES}"
|
110
|
+
else
|
111
|
+
tokens.join(".")
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
90
115
|
def self.use_spin?(env_variables: ENV)
|
91
116
|
env_variable_truthy?(
|
92
117
|
Constants::EnvironmentVariables::SPIN,
|
@@ -97,34 +122,31 @@ module ShopifyCLI
|
|
97
122
|
)
|
98
123
|
end
|
99
124
|
|
100
|
-
def self.infer_spin?(env_variables: ENV)
|
101
|
-
env_variable_truthy?(
|
102
|
-
Constants::EnvironmentVariables::INFER_SPIN,
|
103
|
-
env_variables: env_variables
|
104
|
-
)
|
105
|
-
end
|
106
|
-
|
107
125
|
def self.spin_url(env_variables: ENV)
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
%x(spin show -o fqdn 2> /dev/null).strip
|
116
|
-
else
|
117
|
-
# spin-legacy
|
118
|
-
%x(spin info fqdn 2> /dev/null).strip
|
119
|
-
end
|
126
|
+
override = spin_url_override(env_variables: env_variables)
|
127
|
+
return override unless override.nil?
|
128
|
+
|
129
|
+
spin_response = if env_variables.key?(
|
130
|
+
Constants::EnvironmentVariables::SPIN_INSTANCE
|
131
|
+
)
|
132
|
+
spin_show
|
120
133
|
else
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
134
|
+
spin_show(latest: true)
|
135
|
+
end
|
136
|
+
|
137
|
+
begin
|
138
|
+
instance = JSON.parse(spin_response)
|
139
|
+
raise "Missing key 'fqdn' from spin show. Actual response: #{instance}" unless instance.include?("fqdn")
|
140
|
+
instance["fqdn"]
|
141
|
+
rescue => e
|
142
|
+
raise "Failed to infer spin environment from spin show response #{spin_response}: #{e}"
|
125
143
|
end
|
126
144
|
end
|
127
145
|
|
146
|
+
def self.spin_show(latest: false)
|
147
|
+
latest ? %x(spin show --latest --json) : %x(spin show --json)
|
148
|
+
end
|
149
|
+
|
128
150
|
def self.send_monorail_events?(env_variables: ENV)
|
129
151
|
env_variable_truthy?(
|
130
152
|
Constants::EnvironmentVariables::MONORAIL_REAL_EVENTS,
|
@@ -139,27 +161,5 @@ module ShopifyCLI
|
|
139
161
|
def self.env_variable_truthy?(variable_name, env_variables: ENV)
|
140
162
|
TRUTHY_ENV_VARIABLE_VALUES.include?(env_variables[variable_name.to_s])
|
141
163
|
end
|
142
|
-
|
143
|
-
def self.spin_workspace(env_variables: ENV)
|
144
|
-
env_value = env_variables[Constants::EnvironmentVariables::SPIN_WORKSPACE]
|
145
|
-
return env_value unless env_value.nil?
|
146
|
-
|
147
|
-
if env_value.nil?
|
148
|
-
raise "No value set for #{Constants::EnvironmentVariables::SPIN_WORKSPACE}"
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
def self.spin_namespace(env_variables: ENV)
|
153
|
-
env_value = env_variables[Constants::EnvironmentVariables::SPIN_NAMESPACE]
|
154
|
-
return env_value unless env_value.nil?
|
155
|
-
|
156
|
-
if env_value.nil?
|
157
|
-
raise "No value set for #{Constants::EnvironmentVariables::SPIN_NAMESPACE}"
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
def self.spin_host(env_variables: ENV)
|
162
|
-
env_variables[Constants::EnvironmentVariables::SPIN_HOST] || "us.spin.dev"
|
163
|
-
end
|
164
164
|
end
|
165
165
|
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?
|
@@ -15,6 +15,7 @@ module ShopifyCLI
|
|
15
15
|
},
|
16
16
|
core: {
|
17
17
|
errors: {
|
18
|
+
missing_node: "Node is required to continue. Install node here: https://nodejs.org/en/download.",
|
18
19
|
option_parser: {
|
19
20
|
invalid_option: "The option {{command:%s}} is not supported.",
|
20
21
|
missing_argument: "The required argument {{command:%s}} is missing.",
|
@@ -41,7 +42,7 @@ module ShopifyCLI
|
|
41
42
|
invalid_type: "The type %s is not supported. The only supported types are"\
|
42
43
|
" {{command:[ rails | node | php ]}}",
|
43
44
|
help: <<~HELP,
|
44
|
-
{{command:%s app create}}: Creates a
|
45
|
+
{{command:%s app create}}: Creates a new project in a subdirectory.
|
45
46
|
Usage: {{command:%s app create [ rails | node | php ]}}
|
46
47
|
HELP
|
47
48
|
rails: {
|
@@ -276,6 +277,24 @@ module ShopifyCLI
|
|
276
277
|
HELP
|
277
278
|
},
|
278
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
|
+
},
|
279
298
|
error_reporting: {
|
280
299
|
unhandled_error: {
|
281
300
|
message: "{{x}} {{red:An unexpected error occured.}}",
|
@@ -354,6 +373,7 @@ module ShopifyCLI
|
|
354
373
|
error: {
|
355
374
|
directory_exists: "Project directory already exists. Please create a project with a new name.",
|
356
375
|
no_branches_found: "Could not find any git branches",
|
376
|
+
nonexistent: "Git needs to be installed: https://git-scm.com/download",
|
357
377
|
repo_not_initiated:
|
358
378
|
"Git repo is not initiated. Please run {{command:git init}} and make at least one commit.",
|
359
379
|
no_commits_made: "No git commits have been made. Please make at least one commit.",
|
@@ -671,7 +691,8 @@ module ShopifyCLI
|
|
671
691
|
},
|
672
692
|
},
|
673
693
|
ensure_dev_store: {
|
674
|
-
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`.",
|
675
696
|
convert_to_dev_store: <<~MESSAGE,
|
676
697
|
Do you want to convert %s to a development store?
|
677
698
|
Doing this will allow you to install your app, but the store will become {{bold:transfer-disabled}}.
|
@@ -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"
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ShopifyCLI
|
4
|
+
module Theme
|
5
|
+
module DevServer
|
6
|
+
class HotReload
|
7
|
+
class RemoteFileReloader
|
8
|
+
def initialize(ctx, theme:, streams:)
|
9
|
+
@ctx = ctx
|
10
|
+
@theme = theme
|
11
|
+
@streams = streams
|
12
|
+
end
|
13
|
+
|
14
|
+
def reload(file)
|
15
|
+
retries = 6
|
16
|
+
|
17
|
+
until retries.zero?
|
18
|
+
retries -= 1
|
19
|
+
|
20
|
+
_status, body = fetch_asset(file)
|
21
|
+
retries = 0 if updated_file?(body, file)
|
22
|
+
|
23
|
+
wait
|
24
|
+
end
|
25
|
+
|
26
|
+
notify(file)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def updated_file?(body, file)
|
32
|
+
remote_checksum = body.dig("asset", "checksum")
|
33
|
+
local_checksum = file.checksum
|
34
|
+
|
35
|
+
remote_checksum == local_checksum
|
36
|
+
end
|
37
|
+
|
38
|
+
def notify(file)
|
39
|
+
@streams.broadcast(JSON.generate(modified: [file]))
|
40
|
+
@ctx.debug("[RemoteFileReloader] Modified #{file}")
|
41
|
+
end
|
42
|
+
|
43
|
+
def wait
|
44
|
+
sleep(1)
|
45
|
+
end
|
46
|
+
|
47
|
+
def fetch_asset(file)
|
48
|
+
ShopifyCLI::AdminAPI.rest_request(
|
49
|
+
@ctx,
|
50
|
+
shop: @theme.shop,
|
51
|
+
path: "themes/#{@theme.id}/assets.json",
|
52
|
+
method: "GET",
|
53
|
+
api_version: "unstable",
|
54
|
+
query: URI.encode_www_form("asset[key]" => file.relative_path.to_s),
|
55
|
+
)
|
56
|
+
rescue ShopifyCLI::API::APIRequestNotFoundError
|
57
|
+
[404, {}]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "hot_reload/remote_file_reloader"
|
4
|
+
|
3
5
|
module ShopifyCLI
|
4
6
|
module Theme
|
5
7
|
module DevServer
|
@@ -10,6 +12,7 @@ module ShopifyCLI
|
|
10
12
|
@theme = theme
|
11
13
|
@mode = mode
|
12
14
|
@streams = SSE::Streams.new
|
15
|
+
@remote_file_reloader = RemoteFileReloader.new(ctx, theme: @theme, streams: @streams)
|
13
16
|
@watcher = watcher
|
14
17
|
@watcher.add_observer(self, :notify_streams_of_file_change)
|
15
18
|
@ignore_filter = ignore_filter
|
@@ -32,17 +35,30 @@ module ShopifyCLI
|
|
32
35
|
end
|
33
36
|
|
34
37
|
def notify_streams_of_file_change(modified, added, _removed)
|
35
|
-
files = (modified + added)
|
36
|
-
.
|
38
|
+
files = (modified + added)
|
39
|
+
.reject { |file| @ignore_filter&.ignore?(file) }
|
40
|
+
.map { |file| @theme[file] }
|
37
41
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
+
files -= liquid_css_files = files.select(&:liquid_css?)
|
43
|
+
|
44
|
+
hot_reload(files) unless files.empty?
|
45
|
+
remote_reload(liquid_css_files)
|
42
46
|
end
|
43
47
|
|
44
48
|
private
|
45
49
|
|
50
|
+
def hot_reload(files)
|
51
|
+
paths = files.map(&:relative_path)
|
52
|
+
@streams.broadcast(JSON.generate(modified: paths))
|
53
|
+
@ctx.debug("[HotReload] Modified #{paths.join(", ")}")
|
54
|
+
end
|
55
|
+
|
56
|
+
def remote_reload(files)
|
57
|
+
files.each do |file|
|
58
|
+
@remote_file_reloader.reload(file)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
46
62
|
def request_is_html?(headers)
|
47
63
|
headers["content-type"]&.start_with?("text/html")
|
48
64
|
end
|