shopify-cli 2.11.0 → 2.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/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
|