shopify-cli 2.7.2 → 2.9.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/.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,
|