shopify-cli 2.7.2 → 2.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +2 -2
- data/.github/workflows/shopify.yml +1 -1
- data/.gitignore +1 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +52 -0
- data/Codespace.dockerfile +2 -2
- data/Gemfile.lock +4 -4
- data/RELEASING.md +4 -3
- data/Tests.dockerfile +2 -2
- data/dev.yml +3 -3
- data/ext/javy/hashes/javy-arm-macos-v0.1.0.gz.sha256 +1 -0
- data/ext/javy/hashes/javy-x86_64-linux-v0.1.0.gz.sha256 +1 -0
- data/ext/javy/hashes/javy-x86_64-macos-v0.1.0.gz.sha256 +1 -0
- data/ext/javy/hashes/javy-x86_64-windows-v0.1.0.gz.sha256 +1 -0
- data/ext/javy/javy.rb +31 -13
- data/lib/graphql/get_extension_registrations.graphql +27 -0
- data/lib/project_types/extension/cli.rb +27 -2
- data/lib/project_types/extension/commands/build.rb +10 -10
- data/lib/project_types/extension/commands/create.rb +2 -3
- data/lib/project_types/extension/commands/push.rb +36 -8
- data/lib/project_types/extension/extension_project.rb +1 -1
- data/lib/project_types/extension/features/argo_serve.rb +6 -5
- data/lib/project_types/extension/forms/questions/ask_registration.rb +6 -2
- data/lib/project_types/extension/loaders/project.rb +29 -0
- data/lib/project_types/extension/loaders/specification_handler.rb +22 -0
- data/lib/project_types/extension/messages/messages.rb +4 -0
- data/lib/project_types/extension/models/app.rb +1 -1
- data/lib/project_types/extension/models/development_server.rb +2 -4
- data/lib/project_types/extension/models/specification_handlers/default.rb +4 -0
- data/lib/project_types/extension/tasks/convert_server_config.rb +3 -1
- data/lib/project_types/extension/tasks/execute_commands/base.rb +13 -0
- data/lib/project_types/extension/tasks/execute_commands/build.rb +29 -0
- data/lib/project_types/extension/tasks/execute_commands/create.rb +33 -0
- data/lib/project_types/extension/tasks/execute_commands/serve.rb +35 -0
- data/lib/project_types/extension/tasks/merge_server_config.rb +33 -22
- data/lib/project_types/rails/commands/create.rb +1 -1
- data/lib/project_types/rails/gem.rb +1 -2
- data/lib/project_types/script/cli.rb +8 -1
- data/lib/project_types/script/commands/connect.rb +19 -0
- data/lib/project_types/script/commands/create.rb +8 -2
- data/lib/project_types/script/commands/push.rb +35 -12
- data/lib/project_types/script/config/extension_points.yml +10 -2
- data/lib/project_types/script/graphql/app_script_set.graphql +2 -2
- data/lib/project_types/script/layers/application/connect_app.rb +15 -3
- data/lib/project_types/script/layers/application/create_script.rb +17 -17
- data/lib/project_types/script/layers/application/extension_points.rb +50 -26
- data/lib/project_types/script/layers/application/push_script.rb +6 -3
- data/lib/project_types/script/layers/domain/errors.rb +1 -4
- data/lib/project_types/script/layers/domain/extension_point.rb +14 -0
- data/lib/project_types/script/layers/domain/push_package.rb +3 -3
- data/lib/project_types/script/layers/domain/{script_json.rb → script_config.rb} +2 -2
- data/lib/project_types/script/layers/domain/script_project.rb +1 -1
- data/lib/project_types/script/layers/infrastructure/errors.rb +30 -5
- data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +2 -2
- data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +125 -27
- data/lib/project_types/script/layers/infrastructure/script_service.rb +11 -11
- data/lib/project_types/script/loaders/project.rb +44 -0
- data/lib/project_types/script/loaders/specification_handler.rb +22 -0
- data/lib/project_types/script/messages/messages.rb +34 -3
- data/lib/project_types/script/ui/error_handler.rb +35 -15
- data/lib/project_types/theme/commands/pull.rb +39 -16
- data/lib/project_types/theme/commands/push.rb +60 -29
- data/lib/project_types/theme/commands/serve.rb +5 -0
- data/lib/project_types/theme/messages/messages.rb +30 -18
- data/lib/shopify_cli/command.rb +6 -0
- data/lib/shopify_cli/commands/login.rb +11 -5
- data/lib/shopify_cli/commands/switch.rb +1 -1
- data/lib/shopify_cli/constants.rb +5 -0
- data/lib/shopify_cli/context.rb +66 -11
- data/lib/shopify_cli/environment.rb +15 -4
- data/lib/shopify_cli/form.rb +2 -0
- data/lib/shopify_cli/git.rb +2 -0
- data/lib/shopify_cli/identity_auth.rb +1 -0
- data/lib/shopify_cli/messages/messages.rb +10 -2
- data/lib/shopify_cli/partners_api/app_extensions/job.rb +36 -0
- data/lib/shopify_cli/partners_api/app_extensions.rb +46 -0
- data/lib/shopify_cli/partners_api/organizations.rb +2 -5
- data/lib/shopify_cli/partners_api.rb +1 -0
- data/lib/shopify_cli/project.rb +8 -7
- data/lib/shopify_cli/resources/env_file.rb +18 -6
- data/lib/shopify_cli/services/app/create/rails_service.rb +1 -1
- data/lib/shopify_cli/theme/dev_server/cdn_fonts.rb +73 -0
- data/lib/shopify_cli/theme/dev_server/hot-reload.js +57 -10
- data/lib/shopify_cli/theme/dev_server/hot_reload.rb +18 -2
- data/lib/shopify_cli/theme/dev_server/proxy/template_param_builder.rb +84 -0
- data/lib/shopify_cli/theme/dev_server/proxy.rb +10 -15
- data/lib/shopify_cli/theme/dev_server/reload_mode.rb +34 -0
- data/lib/shopify_cli/theme/dev_server.rb +8 -21
- data/lib/shopify_cli/theme/theme.rb +26 -4
- data/lib/shopify_cli/thread_pool/job.rb +27 -0
- data/lib/shopify_cli/thread_pool.rb +37 -0
- data/lib/shopify_cli/tunnel.rb +1 -0
- data/lib/shopify_cli/version.rb +1 -1
- data/lib/shopify_cli.rb +4 -0
- data/shopify-cli.gemspec +1 -1
- data/vendor/deps/cli-kit/lib/cli/kit/error_handler.rb +3 -1
- metadata +26 -7
- data/lib/graphql/all_orgs_with_extensions.graphql +0 -37
- data/lib/project_types/extension/tasks/run_extension_command.rb +0 -82
@@ -103,32 +103,52 @@ module Script
|
|
103
103
|
cause_of_error: ShopifyCLI::Context.message("script.error.metadata_not_found_cause"),
|
104
104
|
help_suggestion: ShopifyCLI::Context.message("script.error.metadata_not_found_help"),
|
105
105
|
}
|
106
|
-
when Layers::
|
106
|
+
when Layers::Infrastructure::Errors::BuildError
|
107
107
|
{
|
108
|
-
cause_of_error: ShopifyCLI::Context.message("script.error.
|
109
|
-
help_suggestion: ShopifyCLI::Context.message("script.error.
|
108
|
+
cause_of_error: ShopifyCLI::Context.message("script.error.build_error_cause"),
|
109
|
+
help_suggestion: ShopifyCLI::Context.message("script.error.build_error_help"),
|
110
110
|
}
|
111
|
-
when Layers::
|
111
|
+
when Layers::Infrastructure::Errors::InvalidScriptConfigYmlDefinitionError
|
112
|
+
{
|
113
|
+
cause_of_error: ShopifyCLI::Context.message("script.error.invalid_script_config_yml_definition_cause"),
|
114
|
+
help_suggestion: ShopifyCLI::Context.message("script.error.invalid_script_config_yml_definition_help"),
|
115
|
+
}
|
116
|
+
when Layers::Infrastructure::Errors::InvalidScriptJsonDefinitionError
|
112
117
|
{
|
113
118
|
cause_of_error: ShopifyCLI::Context.message("script.error.invalid_script_json_definition_cause"),
|
114
119
|
help_suggestion: ShopifyCLI::Context.message("script.error.invalid_script_json_definition_help"),
|
115
120
|
}
|
116
|
-
when Layers::
|
121
|
+
when Layers::Infrastructure::Errors::MissingScriptConfigYmlFieldError
|
117
122
|
{
|
118
|
-
cause_of_error: ShopifyCLI::Context.message("script.error.
|
119
|
-
help_suggestion: ShopifyCLI::Context.message("script.error.
|
123
|
+
cause_of_error: ShopifyCLI::Context.message("script.error.missing_script_config_yml_field_cause", e.field),
|
124
|
+
help_suggestion: ShopifyCLI::Context.message("script.error.missing_script_config_yml_field_help"),
|
120
125
|
}
|
121
|
-
when Layers::Infrastructure::Errors::
|
126
|
+
when Layers::Infrastructure::Errors::MissingScriptConfigYmlFieldError
|
122
127
|
{
|
123
|
-
cause_of_error: ShopifyCLI::Context.message("script.error.
|
124
|
-
help_suggestion: ShopifyCLI::Context.message("script.error.
|
128
|
+
cause_of_error: ShopifyCLI::Context.message("script.error.missing_script_config_yml_field_cause", e.field),
|
129
|
+
help_suggestion: ShopifyCLI::Context.message("script.error.missing_script_config_yml_field_help"),
|
130
|
+
}
|
131
|
+
when Layers::Infrastructure::Errors::MissingScriptJsonFieldError
|
132
|
+
{
|
133
|
+
cause_of_error: ShopifyCLI::Context.message("script.error.missing_script_json_field_cause", e.field),
|
134
|
+
help_suggestion: ShopifyCLI::Context.message("script.error.missing_script_json_field_help"),
|
125
135
|
}
|
126
|
-
when Layers::Infrastructure::Errors::
|
136
|
+
when Layers::Infrastructure::Errors::NoScriptConfigYmlFileError
|
137
|
+
{
|
138
|
+
cause_of_error: ShopifyCLI::Context.message("script.error.no_script_config_yml_file_cause"),
|
139
|
+
help_suggestion: ShopifyCLI::Context.message("script.error.no_script_config_yml_file_help"),
|
140
|
+
}
|
141
|
+
when Layers::Infrastructure::Errors::ScriptConfigSyntaxError
|
127
142
|
{
|
128
143
|
cause_of_error: ShopifyCLI::Context.message("script.error.configuration_syntax_error_cause"),
|
129
144
|
help_suggestion: ShopifyCLI::Context.message("script.error.configuration_syntax_error_help"),
|
130
145
|
}
|
131
|
-
when Layers::Infrastructure::Errors::
|
146
|
+
when Layers::Infrastructure::Errors::ScriptEnvAppNotConnectedError
|
147
|
+
{
|
148
|
+
cause_of_error: ShopifyCLI::Context.message("script.error.app_not_connected_cause"),
|
149
|
+
help_suggestion: ShopifyCLI::Context.message("script.error.app_not_connected_help"),
|
150
|
+
}
|
151
|
+
when Layers::Infrastructure::Errors::ScriptConfigMissingKeysError
|
132
152
|
{
|
133
153
|
cause_of_error: ShopifyCLI::Context.message(
|
134
154
|
"script.error.configuration_missing_keys_error_cause",
|
@@ -136,7 +156,7 @@ module Script
|
|
136
156
|
),
|
137
157
|
help_suggestion: ShopifyCLI::Context.message("script.error.configuration_missing_keys_error_help"),
|
138
158
|
}
|
139
|
-
when Layers::Infrastructure::Errors::
|
159
|
+
when Layers::Infrastructure::Errors::ScriptConfigInvalidValueError
|
140
160
|
{
|
141
161
|
cause_of_error: ShopifyCLI::Context.message(
|
142
162
|
"script.error.configuration_invalid_value_error_cause",
|
@@ -144,7 +164,7 @@ module Script
|
|
144
164
|
),
|
145
165
|
help_suggestion: ShopifyCLI::Context.message("script.error.configuration_invalid_value_error_help"),
|
146
166
|
}
|
147
|
-
when Layers::Infrastructure::Errors::
|
167
|
+
when Layers::Infrastructure::Errors::ScriptConfigFieldsMissingKeysError
|
148
168
|
{
|
149
169
|
cause_of_error: ShopifyCLI::Context.message(
|
150
170
|
"script.error.configuration_schema_field_missing_keys_error_cause",
|
@@ -154,7 +174,7 @@ module Script
|
|
154
174
|
"script.error.configuration_definition_schema_field_missing_keys_error_help"
|
155
175
|
),
|
156
176
|
}
|
157
|
-
when Layers::Infrastructure::Errors::
|
177
|
+
when Layers::Infrastructure::Errors::ScriptConfigFieldsInvalidValueError
|
158
178
|
{
|
159
179
|
cause_of_error: ShopifyCLI::Context.message(
|
160
180
|
"script.error.configuration_schema_field_invalid_value_error_cause",
|
@@ -9,7 +9,9 @@ module Theme
|
|
9
9
|
options do |parser, flags|
|
10
10
|
parser.on("-n", "--nodelete") { flags[:nodelete] = true }
|
11
11
|
parser.on("-i", "--themeid=ID") { |theme_id| flags[:theme_id] = theme_id }
|
12
|
+
parser.on("-t", "--theme=NAME_OR_ID") { |theme| flags[:theme] = theme }
|
12
13
|
parser.on("-l", "--live") { flags[:live] = true }
|
14
|
+
parser.on("-d", "--development") { flags[:development] = true }
|
13
15
|
parser.on("-x", "--ignore=PATTERN") do |pattern|
|
14
16
|
flags[:ignores] ||= []
|
15
17
|
flags[:ignores] << pattern
|
@@ -19,21 +21,8 @@ module Theme
|
|
19
21
|
def call(args, _name)
|
20
22
|
root = args.first || "."
|
21
23
|
delete = !options.flags[:nodelete]
|
22
|
-
|
23
|
-
|
24
|
-
ShopifyCLI::Theme::Theme.new(@ctx, root: root, id: theme_id)
|
25
|
-
elsif options.flags[:live]
|
26
|
-
ShopifyCLI::Theme::Theme.live(@ctx, root: root)
|
27
|
-
else
|
28
|
-
form = Forms::Select.ask(
|
29
|
-
@ctx,
|
30
|
-
[],
|
31
|
-
title: @ctx.message("theme.pull.select"),
|
32
|
-
root: root,
|
33
|
-
)
|
34
|
-
return unless form
|
35
|
-
form.theme
|
36
|
-
end
|
24
|
+
theme = find_theme(root, **options.flags)
|
25
|
+
return if theme.nil?
|
37
26
|
|
38
27
|
ignore_filter = ShopifyCLI::Theme::IgnoreFilter.from_path(root)
|
39
28
|
ignore_filter.add_patterns(options.flags[:ignores]) if options.flags[:ignores]
|
@@ -46,7 +35,7 @@ module Theme
|
|
46
35
|
end
|
47
36
|
@ctx.done(@ctx.message("theme.pull.done"))
|
48
37
|
rescue ShopifyCLI::API::APIRequestNotFoundError
|
49
|
-
@ctx.abort(@ctx.message("theme.pull.theme_not_found", theme.id))
|
38
|
+
@ctx.abort(@ctx.message("theme.pull.theme_not_found", "##{theme.id}"))
|
50
39
|
ensure
|
51
40
|
syncer.shutdown
|
52
41
|
end
|
@@ -55,6 +44,40 @@ module Theme
|
|
55
44
|
def self.help
|
56
45
|
ShopifyCLI::Context.message("theme.pull.help", ShopifyCLI::TOOL_NAME, ShopifyCLI::TOOL_NAME)
|
57
46
|
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def find_theme(root, theme_id: nil, theme: nil, live: nil, development: nil, **_args)
|
51
|
+
if theme_id
|
52
|
+
@ctx.warn(@ctx.message("theme.pull.deprecated_themeid"))
|
53
|
+
return ShopifyCLI::Theme::Theme.new(@ctx, root: root, id: theme_id)
|
54
|
+
end
|
55
|
+
|
56
|
+
if theme
|
57
|
+
selected_theme = ShopifyCLI::Theme::Theme.find_by_identifier(@ctx, root: root, identifier: theme)
|
58
|
+
return selected_theme || @ctx.abort(@ctx.message("theme.pull.theme_not_found", theme))
|
59
|
+
end
|
60
|
+
|
61
|
+
if live
|
62
|
+
return ShopifyCLI::Theme::Theme.live(@ctx, root: root)
|
63
|
+
end
|
64
|
+
|
65
|
+
if development
|
66
|
+
return ShopifyCLI::Theme::Theme.development(@ctx, root: root)
|
67
|
+
end
|
68
|
+
|
69
|
+
select_theme(root)
|
70
|
+
end
|
71
|
+
|
72
|
+
def select_theme(root)
|
73
|
+
form = Forms::Select.ask(
|
74
|
+
@ctx,
|
75
|
+
[],
|
76
|
+
title: @ctx.message("theme.pull.select"),
|
77
|
+
root: root,
|
78
|
+
)
|
79
|
+
form&.theme
|
80
|
+
end
|
58
81
|
end
|
59
82
|
end
|
60
83
|
end
|
@@ -10,6 +10,7 @@ module Theme
|
|
10
10
|
options do |parser, flags|
|
11
11
|
parser.on("-n", "--nodelete") { flags[:nodelete] = true }
|
12
12
|
parser.on("-i", "--themeid=ID") { |theme_id| flags[:theme_id] = theme_id }
|
13
|
+
parser.on("-t", "--theme=NAME_OR_ID") { |theme| flags[:theme] = theme }
|
13
14
|
parser.on("-l", "--live") { flags[:live] = true }
|
14
15
|
parser.on("-d", "--development") { flags[:development] = true }
|
15
16
|
parser.on("-u", "--unpublished") { flags[:unpublished] = true }
|
@@ -25,34 +26,13 @@ module Theme
|
|
25
26
|
def call(args, _name)
|
26
27
|
root = args.first || "."
|
27
28
|
delete = !options.flags[:nodelete]
|
29
|
+
theme = find_theme(root, **options.flags)
|
30
|
+
return if theme.nil?
|
28
31
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
elsif options.flags[:development]
|
34
|
-
theme = ShopifyCLI::Theme::DevelopmentTheme.new(@ctx, root: root)
|
35
|
-
theme.ensure_exists!
|
36
|
-
theme
|
37
|
-
elsif options.flags[:unpublished]
|
38
|
-
name = CLI::UI::Prompt.ask(@ctx.message("theme.push.name"), allow_empty: false)
|
39
|
-
theme = ShopifyCLI::Theme::Theme.new(@ctx, root: root, name: name, role: "unpublished")
|
40
|
-
theme.create
|
41
|
-
theme
|
42
|
-
else
|
43
|
-
form = Forms::Select.ask(
|
44
|
-
@ctx,
|
45
|
-
[],
|
46
|
-
title: @ctx.message("theme.push.select"),
|
47
|
-
root: root,
|
48
|
-
)
|
49
|
-
return unless form
|
50
|
-
form.theme
|
51
|
-
end
|
52
|
-
|
53
|
-
is_confirm_required = !options.flags[:allow_live] && !options.flags[:live]
|
54
|
-
if theme.live? && is_confirm_required
|
55
|
-
return unless CLI::UI::Prompt.confirm(@ctx.message("theme.push.live"))
|
32
|
+
if theme.live? && !options.flags[:allow_live]
|
33
|
+
question = @ctx.message("theme.push.live")
|
34
|
+
question += @ctx.message("theme.push.theme", theme.name, theme.id) if options.flags[:live]
|
35
|
+
return unless CLI::UI::Prompt.confirm(question)
|
56
36
|
end
|
57
37
|
|
58
38
|
ignore_filter = ShopifyCLI::Theme::IgnoreFilter.from_path(root)
|
@@ -77,16 +57,67 @@ module Theme
|
|
77
57
|
end
|
78
58
|
end
|
79
59
|
raise ShopifyCLI::AbortSilent if syncer.has_any_error?
|
80
|
-
rescue ShopifyCLI::API::APIRequestNotFoundError
|
81
|
-
@ctx.abort(@ctx.message("theme.push.theme_not_found", theme.id))
|
82
60
|
ensure
|
83
61
|
syncer.shutdown
|
84
62
|
end
|
63
|
+
rescue ShopifyCLI::API::APIRequestNotFoundError
|
64
|
+
@ctx.abort(@ctx.message("theme.push.theme_not_found", "##{theme.id}"))
|
85
65
|
end
|
86
66
|
|
87
67
|
def self.help
|
88
68
|
ShopifyCLI::Context.message("theme.push.help", ShopifyCLI::TOOL_NAME, ShopifyCLI::TOOL_NAME)
|
89
69
|
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def find_theme(root, theme_id: nil, theme: nil, live: nil, development: nil, unpublished: nil, **_args)
|
74
|
+
if theme_id
|
75
|
+
@ctx.warn(@ctx.message("theme.push.deprecated_themeid"))
|
76
|
+
return ShopifyCLI::Theme::Theme.new(@ctx, root: root, id: theme_id)
|
77
|
+
end
|
78
|
+
|
79
|
+
if live
|
80
|
+
return ShopifyCLI::Theme::Theme.live(@ctx, root: root)
|
81
|
+
end
|
82
|
+
|
83
|
+
if development
|
84
|
+
new_theme = ShopifyCLI::Theme::DevelopmentTheme.new(@ctx, root: root)
|
85
|
+
new_theme.ensure_exists!
|
86
|
+
return new_theme
|
87
|
+
end
|
88
|
+
|
89
|
+
if unpublished
|
90
|
+
name = theme || ask_theme_name
|
91
|
+
new_theme = ShopifyCLI::Theme::Theme.new(@ctx, root: root, name: name, role: "unpublished")
|
92
|
+
new_theme.create
|
93
|
+
return new_theme
|
94
|
+
end
|
95
|
+
|
96
|
+
if theme
|
97
|
+
selected_theme = ShopifyCLI::Theme::Theme.find_by_identifier(@ctx, root: root, identifier: theme)
|
98
|
+
return selected_theme || @ctx.abort(@ctx.message("theme.push.theme_not_found", theme))
|
99
|
+
end
|
100
|
+
|
101
|
+
select_theme(root)
|
102
|
+
end
|
103
|
+
|
104
|
+
def ask_theme_name
|
105
|
+
CLI::UI::Prompt.ask(@ctx.message("theme.push.name"), allow_empty: false)
|
106
|
+
end
|
107
|
+
|
108
|
+
def select_theme(root)
|
109
|
+
form = Forms::Select.ask(
|
110
|
+
@ctx,
|
111
|
+
[],
|
112
|
+
title: @ctx.message("theme.push.select"),
|
113
|
+
root: root,
|
114
|
+
)
|
115
|
+
form&.theme
|
116
|
+
end
|
117
|
+
|
118
|
+
def themes(root)
|
119
|
+
ShopifyCLI::Theme::Theme.all(@ctx, root: root)
|
120
|
+
end
|
90
121
|
end
|
91
122
|
end
|
92
123
|
end
|
@@ -10,6 +10,7 @@ module Theme
|
|
10
10
|
parser.on("--host=HOST") { |host| flags[:host] = host.to_s }
|
11
11
|
parser.on("--port=PORT") { |port| flags[:port] = port.to_i }
|
12
12
|
parser.on("--poll") { flags[:poll] = true }
|
13
|
+
parser.on("--live-reload=MODE") { |mode| flags[:mode] = as_reload_mode(mode) }
|
13
14
|
end
|
14
15
|
|
15
16
|
def call(*)
|
@@ -23,6 +24,10 @@ module Theme
|
|
23
24
|
ShopifyCLI::Context.message("theme.serve.error.address_binding_error", ShopifyCLI::TOOL_NAME)
|
24
25
|
end
|
25
26
|
|
27
|
+
def self.as_reload_mode(mode)
|
28
|
+
ShopifyCLI::Theme::DevServer::ReloadMode.get!(mode)
|
29
|
+
end
|
30
|
+
|
26
31
|
def self.help
|
27
32
|
ShopifyCLI::Context.message("theme.serve.help", ShopifyCLI::TOOL_NAME)
|
28
33
|
end
|
@@ -57,14 +57,14 @@ module Theme
|
|
57
57
|
Usage: {{command:%s theme push [ ROOT ]}}
|
58
58
|
|
59
59
|
Options:
|
60
|
-
{{command:-
|
61
|
-
{{command:-l, --live}}
|
62
|
-
{{command:-d, --development}}
|
63
|
-
{{command:-u, --unpublished}}
|
64
|
-
{{command:-n, --nodelete}}
|
65
|
-
{{command:-j, --json}}
|
66
|
-
{{command:-a, --allow-live}}
|
67
|
-
{{command:-p, --publish}}
|
60
|
+
{{command:-t, --theme=NAME_OR_ID}} Theme ID or name of the remote theme.
|
61
|
+
{{command:-l, --live}} Push to your remote live theme, and update your live store.
|
62
|
+
{{command:-d, --development}} Push to your remote development theme, and create it if needed.
|
63
|
+
{{command:-u, --unpublished}} Create a new unpublished theme and push to it.
|
64
|
+
{{command:-n, --nodelete}} Runs the push command without deleting remote files from Shopify.
|
65
|
+
{{command:-j, --json}} Output JSON instead of a UI.
|
66
|
+
{{command:-a, --allow-live}} Allow push to a live theme.
|
67
|
+
{{command:-p, --publish}} Publish as the live theme after uploading.
|
68
68
|
|
69
69
|
Run without options to select theme from a list.
|
70
70
|
HELP
|
@@ -74,7 +74,11 @@ module Theme
|
|
74
74
|
push: "Pushing theme files to Shopify",
|
75
75
|
select: "Select theme to push to",
|
76
76
|
live: "Are you sure you want to push to your live theme?",
|
77
|
-
|
77
|
+
theme: "\n Theme: {{blue:%s #%s}} {{green:[live]}}",
|
78
|
+
deprecated_themeid: <<~WARN,
|
79
|
+
{{warning:The {{command:-i, --themeid}} flag is deprecated. Use {{command:-t, --theme}} instead.}}
|
80
|
+
WARN
|
81
|
+
theme_not_found: "Theme \"%s\" doesn't exist",
|
78
82
|
done: <<~DONE,
|
79
83
|
{{green:Your theme was pushed successfully}}
|
80
84
|
|
@@ -93,10 +97,16 @@ module Theme
|
|
93
97
|
Usage: {{command:%s theme serve}}
|
94
98
|
|
95
99
|
Options:
|
96
|
-
{{command:--port=PORT}}
|
97
|
-
{{command:--poll}}
|
98
|
-
{{command:--host=HOST}}
|
100
|
+
{{command:--port=PORT}} Local port to serve theme preview from.
|
101
|
+
{{command:--poll}} Force polling to detect file changes.
|
102
|
+
{{command:--host=HOST}} Set which network interface the web server listens on. The default value is 127.0.0.1.
|
103
|
+
{{command:--live-reload=MODE}} The live reload mode switches the server behavior when a file is modified:
|
104
|
+
- {{command:hot-reload}} Hot reloads local changes to CSS and sections (default)
|
105
|
+
- {{command:full-page}} Always refreshes the entire page
|
106
|
+
- {{command:off}} Deactivate live reload
|
99
107
|
HELP
|
108
|
+
reload_mode_is_not_valid: "The live reload mode `%s` is not valid.",
|
109
|
+
try_a_valid_reload_mode: "Try a valid live reload mode: %s.",
|
100
110
|
viewing_theme: "Viewing theme…",
|
101
111
|
syncing_theme: "Syncing theme #%s on %s",
|
102
112
|
open_fail: "Couldn't open the theme",
|
@@ -130,9 +140,7 @@ module Theme
|
|
130
140
|
You are not authorized to edit themes on %s.
|
131
141
|
Make sure you are a user of that store, and allowed to edit themes.
|
132
142
|
ENSURE_USER
|
133
|
-
already_in_use_error: "Error",
|
134
143
|
address_already_in_use: "The address \"%s\" is already in use.",
|
135
|
-
try_this: "Try this",
|
136
144
|
try_port_option: "Use the --port=PORT option to serve the theme in a different port.",
|
137
145
|
},
|
138
146
|
check: {
|
@@ -188,16 +196,20 @@ module Theme
|
|
188
196
|
Usage: {{command:%s theme pull [ ROOT ]}}
|
189
197
|
|
190
198
|
Options:
|
191
|
-
{{command:-
|
192
|
-
{{command:-l, --live}}
|
193
|
-
{{command:-
|
199
|
+
{{command:-t, --theme=NAME_OR_ID}} Theme ID or name of the remote theme.
|
200
|
+
{{command:-l, --live}} Pull theme files from your remote live theme.
|
201
|
+
{{command:-d, --development}} Pull theme files from your remote development theme.
|
202
|
+
{{command:-n, --nodelete}} Runs the pull command without deleting local files.
|
194
203
|
|
195
204
|
Run without options to select theme from a list.
|
196
205
|
HELP
|
197
206
|
select: "Select a theme to pull from",
|
198
207
|
pulling: "Pulling theme files from %s (#%s) on %s",
|
199
208
|
done: "Theme pulled successfully",
|
200
|
-
|
209
|
+
deprecated_themeid: <<~WARN,
|
210
|
+
{{warning:The {{command:-i, --themeid}} flag is deprecated. Use {{command:-t, --theme}} instead.}}
|
211
|
+
WARN
|
212
|
+
theme_not_found: "Theme \"%s\" doesn't exist",
|
201
213
|
},
|
202
214
|
},
|
203
215
|
}.freeze
|
data/lib/shopify_cli/command.rb
CHANGED
@@ -29,6 +29,12 @@ module ShopifyCLI
|
|
29
29
|
run_prerequisites
|
30
30
|
cmd.call(args, command_name)
|
31
31
|
end
|
32
|
+
rescue OptionParser::InvalidOption => error
|
33
|
+
arg = error.args.first
|
34
|
+
raise ShopifyCLI::Abort, @ctx.message("core.errors.option_parser.invalid_option", arg)
|
35
|
+
rescue OptionParser::MissingArgument => error
|
36
|
+
arg = error.args.first
|
37
|
+
raise ShopifyCLI::Abort, @ctx.message("core.errors.option_parser.missing_argument", arg)
|
32
38
|
end
|
33
39
|
|
34
40
|
def options(&block)
|
@@ -7,7 +7,7 @@ module ShopifyCLI
|
|
7
7
|
PERMANENT_DOMAIN_SUFFIX = /\.myshopify\.(com|io)$/
|
8
8
|
|
9
9
|
options do |parser, flags|
|
10
|
-
parser.on("--store=STORE") { |url| flags[:shop] = url }
|
10
|
+
parser.on("-s", "--store=STORE") { |url| flags[:shop] = url }
|
11
11
|
# backwards compatibility allow 'shop' for now
|
12
12
|
parser.on("--shop=SHOP") { |url| flags[:shop] = url }
|
13
13
|
parser.on("--password=PASSWORD") { |password| flags[:password] = password }
|
@@ -15,8 +15,6 @@ module ShopifyCLI
|
|
15
15
|
|
16
16
|
def call(*)
|
17
17
|
shop = (options.flags[:shop] || @ctx.getenv("SHOPIFY_SHOP" || nil))
|
18
|
-
ShopifyCLI::DB.set(shop: self.class.validate_shop(shop, context: @ctx)) unless shop.nil?
|
19
|
-
|
20
18
|
if shop.nil? && Shopifolk.check
|
21
19
|
Shopifolk.reset
|
22
20
|
@ctx.puts(@ctx.message("core.tasks.select_org_and_shop.identified_as_shopify"))
|
@@ -33,17 +31,25 @@ module ShopifyCLI
|
|
33
31
|
IdentityAuth.new(ctx: @ctx).authenticate
|
34
32
|
org = select_organization
|
35
33
|
ShopifyCLI::DB.set(organization_id: org["id"].to_i) unless org.nil?
|
36
|
-
|
34
|
+
|
37
35
|
end
|
36
|
+
# validate that shop belongs to organization
|
37
|
+
ShopifyCLI::DB.set(shop: self.class.validate_shop(shop: shop, org: org, context: @ctx)) unless shop.nil?
|
38
|
+
Whoami.call([], "whoami")
|
38
39
|
end
|
39
40
|
|
40
41
|
def self.help
|
41
42
|
ShopifyCLI::Context.message("core.login.help", ShopifyCLI::TOOL_NAME)
|
42
43
|
end
|
43
44
|
|
44
|
-
def self.validate_shop(shop
|
45
|
+
def self.validate_shop(shop:, org:, context:)
|
45
46
|
permanent_domain = shop_to_permanent_domain(shop)
|
46
47
|
context.abort(context.message("core.login.invalid_shop", shop)) unless permanent_domain
|
48
|
+
if org
|
49
|
+
stores_owned = org["stores"]
|
50
|
+
is_verified = stores_owned.any? { |store| store["shopDomain"] == permanent_domain }
|
51
|
+
context.abort(context.message("core.login.invalid_shop", shop)) unless is_verified
|
52
|
+
end
|
47
53
|
permanent_domain
|
48
54
|
end
|
49
55
|
|
@@ -4,7 +4,7 @@ module ShopifyCLI
|
|
4
4
|
module Commands
|
5
5
|
class Switch < ShopifyCLI::Command
|
6
6
|
options do |parser, flags|
|
7
|
-
parser.on("--store=STORE") { |url| flags[:shop] = url }
|
7
|
+
parser.on("-s", "--store=STORE") { |url| flags[:shop] = url }
|
8
8
|
# backwards compatibility allow 'shop' for now
|
9
9
|
parser.on("--shop=SHOP") { |url| flags[:shop] = url }
|
10
10
|
end
|
@@ -30,6 +30,7 @@ module ShopifyCLI
|
|
30
30
|
|
31
31
|
module EnvironmentVariables
|
32
32
|
STACKTRACE = "SHOPIFY_CLI_STACKTRACE"
|
33
|
+
TTY = "SHOPIFY_CLI_TTY"
|
33
34
|
|
34
35
|
# When true the CLI points to a local instance of
|
35
36
|
# the partners dashboard and identity.
|
@@ -61,5 +62,9 @@ module ShopifyCLI
|
|
61
62
|
module Links
|
62
63
|
NEW_ISSUE = "https://github.com/Shopify/shopify-cli/issues/new"
|
63
64
|
end
|
65
|
+
|
66
|
+
module Extension
|
67
|
+
DEFAULT_PORT = 39351
|
68
|
+
end
|
64
69
|
end
|
65
70
|
end
|
data/lib/shopify_cli/context.rb
CHANGED
@@ -44,6 +44,56 @@ module ShopifyCLI
|
|
44
44
|
str = Context.messages.dig(*key_parts)
|
45
45
|
str ? str % params : key
|
46
46
|
end
|
47
|
+
|
48
|
+
# a wrapper around Kernel.puts to allow for easy formatting
|
49
|
+
#
|
50
|
+
# #### Parameters
|
51
|
+
# * `text` - a string message to output
|
52
|
+
def puts(*args)
|
53
|
+
Kernel.puts(CLI::UI.fmt(*args))
|
54
|
+
end
|
55
|
+
|
56
|
+
# aborts the current running command and outputs an error message:
|
57
|
+
# - when the `help_message` is not provided, the error message appears in
|
58
|
+
# a red frame, prefixed by an ✗ icon
|
59
|
+
# - when the `help_message` is provided, the error message appears in a
|
60
|
+
# red frame, and the help message appears in a green frame
|
61
|
+
#
|
62
|
+
# #### Parameters
|
63
|
+
# * `error_message` - an error message to output
|
64
|
+
# * `help_message` - an optional help message
|
65
|
+
#
|
66
|
+
# #### Example
|
67
|
+
#
|
68
|
+
# ShopifyCLI::Context.abort("Execution error")
|
69
|
+
# # Output:
|
70
|
+
# # ┏━━ Error ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
71
|
+
# # ┃ ✗ Execution error
|
72
|
+
# # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
73
|
+
#
|
74
|
+
# ShopifyCLI::Context.abort("Execution error", "export EXECUTION=1")
|
75
|
+
# # Output:
|
76
|
+
# # ┏━━ Error ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
77
|
+
# # ┃ Execution error
|
78
|
+
# # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
79
|
+
# # ┏━━ Try this ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
80
|
+
# # ┃ export EXECUTION=1
|
81
|
+
# # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
82
|
+
#
|
83
|
+
def abort(error_message, help_message = nil)
|
84
|
+
raise ShopifyCLI::Abort, "{{x}} #{error_message}" if help_message.nil?
|
85
|
+
|
86
|
+
frame(message("core.error"), color: :red) { self.puts(error_message) }
|
87
|
+
frame(message("core.try_this"), color: :green) { self.puts(help_message) }
|
88
|
+
|
89
|
+
raise ShopifyCLI::AbortSilent
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def frame(title, color:, &block)
|
95
|
+
CLI::UI::Frame.open(title, color: CLI::UI.resolve_color(color), timing: false, &block)
|
96
|
+
end
|
47
97
|
end
|
48
98
|
|
49
99
|
# is the directory root that the current command is running in. If you want to
|
@@ -61,13 +111,19 @@ module ShopifyCLI
|
|
61
111
|
# will return which operating system that the cli is running on [:mac, :linux]
|
62
112
|
def os
|
63
113
|
host = uname
|
114
|
+
return :mac_m1 if /arm64.*darwin/i.match(host)
|
64
115
|
return :mac if /darwin/i.match(host)
|
65
116
|
return :windows if /mswin|mingw|cygwin/i.match(host)
|
66
117
|
return :linux if /linux|bsd/i.match(host)
|
67
118
|
:unknown
|
68
119
|
end
|
69
120
|
|
70
|
-
# will return true if the cli is running on an
|
121
|
+
# will return true if the cli is running on an ARM Apple computer.
|
122
|
+
def mac_m1?
|
123
|
+
os == :mac_m1
|
124
|
+
end
|
125
|
+
|
126
|
+
# will return true if the cli is running on a Intel x86 Apple computer.
|
71
127
|
def mac?
|
72
128
|
os == :mac
|
73
129
|
end
|
@@ -89,7 +145,7 @@ module ShopifyCLI
|
|
89
145
|
|
90
146
|
# will return true if being launched from a tty
|
91
147
|
def tty?
|
92
|
-
$stdin.tty?
|
148
|
+
$stdin.tty?
|
93
149
|
end
|
94
150
|
|
95
151
|
# will return true if the cli is being run from an installation, and not a
|
@@ -328,7 +384,7 @@ module ShopifyCLI
|
|
328
384
|
system("xdg-open", uri.to_s)
|
329
385
|
elsif windows?
|
330
386
|
system("start \"\" \"#{uri}\"")
|
331
|
-
elsif mac?
|
387
|
+
elsif mac? || mac_m1?
|
332
388
|
system("open", uri.to_s)
|
333
389
|
else
|
334
390
|
open_url!(uri)
|
@@ -348,13 +404,13 @@ module ShopifyCLI
|
|
348
404
|
puts "{{yellow:*}} #{text}"
|
349
405
|
end
|
350
406
|
|
351
|
-
#
|
407
|
+
# proxy call to Context.puts.
|
352
408
|
#
|
353
409
|
# #### Parameters
|
354
410
|
# * `text` - a string message to output
|
355
411
|
#
|
356
412
|
def puts(*args)
|
357
|
-
|
413
|
+
Context.puts(*args)
|
358
414
|
end
|
359
415
|
|
360
416
|
# a wrapper around $stderr.puts to allow for easy formatting
|
@@ -384,14 +440,13 @@ module ShopifyCLI
|
|
384
440
|
puts("{{v}} #{text}")
|
385
441
|
end
|
386
442
|
|
387
|
-
#
|
388
|
-
# by a red x
|
443
|
+
# proxy call to Context.abort.
|
389
444
|
#
|
390
445
|
# #### Parameters
|
391
|
-
# * `
|
392
|
-
#
|
393
|
-
def abort(
|
394
|
-
|
446
|
+
# * `error_message` - an error message to output
|
447
|
+
# * `help_message` - an optional help message
|
448
|
+
def abort(error_message, help_message = nil)
|
449
|
+
Context.abort(error_message, help_message)
|
395
450
|
end
|
396
451
|
|
397
452
|
# outputs a message, prefixed by a red `DEBUG` tag. This will only output to
|
@@ -4,6 +4,21 @@ module ShopifyCLI
|
|
4
4
|
module Environment
|
5
5
|
TRUTHY_ENV_VARIABLE_VALUES = ["1", "true", "TRUE", "yes", "YES"]
|
6
6
|
|
7
|
+
def self.interactive=(interactive)
|
8
|
+
@interactive = interactive
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.interactive?(env_variables: ENV)
|
12
|
+
if env_variables.key?(Constants::EnvironmentVariables::TTY)
|
13
|
+
env_variable_truthy?(
|
14
|
+
Constants::EnvironmentVariables::TTY,
|
15
|
+
env_variables: env_variables
|
16
|
+
)
|
17
|
+
else
|
18
|
+
@interactive ||= STDIN.tty?
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
7
22
|
def self.development?(env_variables: ENV)
|
8
23
|
env_variable_truthy?(
|
9
24
|
Constants::EnvironmentVariables::DEVELOPMENT,
|
@@ -11,10 +26,6 @@ module ShopifyCLI
|
|
11
26
|
)
|
12
27
|
end
|
13
28
|
|
14
|
-
def self.interactive?
|
15
|
-
ShopifyCLI::Context.new.tty?
|
16
|
-
end
|
17
|
-
|
18
29
|
def self.use_local_partners_instance?(env_variables: ENV)
|
19
30
|
env_variable_truthy?(
|
20
31
|
Constants::EnvironmentVariables::LOCAL_PARTNERS,
|