shopify-cli 2.15.1 → 2.15.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.vscode/settings.json +1 -2
- data/CHANGELOG.md +68 -20
- data/Gemfile.lock +1 -1
- data/Rakefile +21 -0
- data/ext/javy/hashes/javy-arm-macos-v0.3.0.gz.sha256 +1 -0
- data/ext/javy/hashes/javy-x86_64-linux-v0.3.0.gz.sha256 +1 -0
- data/ext/javy/hashes/javy-x86_64-macos-v0.3.0.gz.sha256 +1 -0
- data/ext/javy/hashes/javy-x86_64-windows-v0.3.0.gz.sha256 +1 -0
- data/ext/javy/version +1 -1
- data/ext/shopify-extensions/version +1 -1
- data/lib/project_types/extension/cli.rb +4 -0
- data/lib/project_types/extension/commands/check.rb +6 -1
- data/lib/project_types/extension/forms/questions/ask_template.rb +1 -2
- data/lib/project_types/extension/messages/messages.rb +1 -3
- data/lib/project_types/extension/models/development_server_requirements.rb +1 -0
- data/lib/project_types/extension/models/specification_handlers/beacon_extension.rb +57 -0
- data/lib/project_types/extension/models/specification_handlers/beacon_extension_utils/script_config.rb +33 -0
- data/lib/project_types/extension/models/specification_handlers/beacon_extension_utils/script_config_repository.rb +75 -0
- data/lib/project_types/extension/models/specification_handlers/checkout_ui_extension.rb +16 -1
- data/lib/project_types/extension/models/specification_handlers/theme_app_extension.rb +4 -1
- data/lib/project_types/extension/tasks/configure_options.rb +2 -1
- data/lib/project_types/extension/tasks/convert_server_config.rb +13 -2
- data/lib/project_types/extension/tasks/merge_server_config.rb +5 -2
- data/lib/project_types/script/cli.rb +1 -0
- data/lib/project_types/script/layers/application/create_script.rb +14 -6
- data/lib/project_types/script/layers/infrastructure/errors.rb +17 -0
- data/lib/project_types/script/layers/infrastructure/languages/project_creator.rb +6 -21
- data/lib/project_types/script/layers/infrastructure/script_service.rb +2 -0
- data/lib/project_types/script/layers/infrastructure/sparse_checkout_details.rb +35 -0
- data/lib/project_types/script/messages/messages.rb +3 -0
- data/lib/project_types/script/ui/error_handler.rb +11 -0
- data/lib/project_types/theme/cli.rb +1 -0
- data/lib/project_types/theme/commands/check.rb +4 -1
- data/lib/project_types/theme/commands/open.rb +2 -2
- data/lib/project_types/theme/commands/push.rb +1 -3
- data/lib/project_types/theme/commands/serve.rb +1 -0
- data/lib/project_types/theme/commands/share.rb +56 -0
- data/lib/project_types/theme/messages/messages.rb +64 -11
- data/lib/shopify_cli/changelog.rb +97 -25
- data/lib/shopify_cli/command_options/command_serve_options.rb +10 -0
- data/lib/shopify_cli/commands/app/serve.rb +7 -7
- data/lib/shopify_cli/commands/login.rb +5 -2
- data/lib/shopify_cli/context.rb +13 -0
- data/lib/shopify_cli/git.rb +36 -0
- data/lib/shopify_cli/identity_auth.rb +24 -4
- data/lib/shopify_cli/messages/messages.rb +22 -11
- data/lib/shopify_cli/release.rb +120 -20
- data/lib/shopify_cli/services/app/create/rails_service.rb +9 -1
- data/lib/shopify_cli/services/app/serve/node_service.rb +2 -25
- data/lib/shopify_cli/services/app/serve/php_service.rb +2 -25
- data/lib/shopify_cli/services/app/serve/rails_service.rb +8 -28
- data/lib/shopify_cli/services/app/serve/serve_service.rb +57 -0
- data/lib/shopify_cli/services.rb +1 -0
- data/lib/shopify_cli/tasks/update_dashboard_urls.rb +7 -9
- data/lib/shopify_cli/theme/dev_server/hot-reload.js +40 -13
- data/lib/shopify_cli/theme/dev_server/hot_reload/remote_file_reloader.rb +1 -1
- data/lib/shopify_cli/theme/dev_server/hot_reload/sections_index.rb +51 -0
- data/lib/shopify_cli/theme/dev_server/hot_reload.rb +6 -1
- data/lib/shopify_cli/theme/dev_server/local_assets.rb +1 -1
- data/lib/shopify_cli/theme/dev_server/remote_watcher/json_files_update_job.rb +35 -0
- data/lib/shopify_cli/theme/dev_server/remote_watcher.rb +44 -0
- data/lib/shopify_cli/theme/dev_server/watcher.rb +2 -8
- data/lib/shopify_cli/theme/dev_server.rb +18 -5
- data/lib/shopify_cli/theme/file.rb +15 -4
- data/lib/shopify_cli/theme/syncer/checksums.rb +60 -0
- data/lib/shopify_cli/theme/syncer/forms/apply_to_all.rb +39 -0
- data/lib/shopify_cli/theme/syncer/forms/apply_to_all_form.rb +35 -0
- data/lib/shopify_cli/theme/syncer/forms/base_strategy_form.rb +62 -0
- data/lib/shopify_cli/theme/syncer/forms/select_delete_strategy.rb +27 -0
- data/lib/shopify_cli/theme/syncer/forms/select_update_strategy.rb +28 -0
- data/lib/shopify_cli/theme/syncer/ignore_helper.rb +33 -0
- data/lib/shopify_cli/theme/syncer/json_delete_handler.rb +51 -0
- data/lib/shopify_cli/theme/syncer/json_update_handler.rb +82 -0
- data/lib/shopify_cli/theme/syncer/merger.rb +53 -0
- data/lib/shopify_cli/theme/syncer/operation.rb +1 -1
- data/lib/shopify_cli/theme/syncer.rb +79 -63
- data/lib/shopify_cli/theme/theme.rb +21 -7
- data/lib/shopify_cli/theme/theme_admin_api.rb +23 -8
- data/lib/shopify_cli/thread_pool/job.rb +10 -2
- data/lib/shopify_cli/thread_pool.rb +15 -3
- data/lib/shopify_cli/tunnel.rb +3 -13
- data/lib/shopify_cli/version.rb +1 -1
- data/vendor/deps/cli-ui/lib/cli/ui/os.rb +8 -0
- metadata +25 -2
@@ -23,19 +23,23 @@ module Script
|
|
23
23
|
)
|
24
24
|
|
25
25
|
# remove the need to pass the whole extension-point object to the infra layer
|
26
|
-
sparse_checkout_repo = extension_point.libraries.for(language).repo
|
27
26
|
type = extension_point.dasherize_type
|
28
27
|
domain = extension_point.domain
|
29
28
|
|
29
|
+
sparse_checkout_details = Infrastructure::SparseCheckoutDetails.new(
|
30
|
+
repo: extension_point.libraries.for(language).repo,
|
31
|
+
branch: sparse_checkout_branch,
|
32
|
+
path: "#{domain}/#{language}/#{type}/default",
|
33
|
+
input_queries_enabled: input_queries_enabled?,
|
34
|
+
)
|
35
|
+
|
30
36
|
project_creator = Infrastructure::Languages::ProjectCreator.for(
|
31
37
|
ctx: ctx,
|
32
38
|
language: language,
|
33
39
|
type: type,
|
34
40
|
project_name: title,
|
35
41
|
path_to_project: project.id,
|
36
|
-
|
37
|
-
sparse_checkout_branch: sparse_checkout_branch,
|
38
|
-
sparse_checkout_set_path: "#{domain}/#{language}/#{type}/default"
|
42
|
+
sparse_checkout_details: sparse_checkout_details,
|
39
43
|
)
|
40
44
|
|
41
45
|
install_dependencies(ctx, language, title, project_creator)
|
@@ -49,12 +53,12 @@ module Script
|
|
49
53
|
task_runner = Infrastructure::Languages::TaskRunner.for(ctx, language)
|
50
54
|
CLI::UI::Frame.open(ctx.message(
|
51
55
|
"core.git.pulling_from_to",
|
52
|
-
project_creator.
|
56
|
+
project_creator.sparse_checkout_details.repo,
|
53
57
|
title,
|
54
58
|
)) do
|
55
59
|
UI::StrictSpinner.spin(ctx.message(
|
56
60
|
"core.git.pulling",
|
57
|
-
project_creator.
|
61
|
+
project_creator.sparse_checkout_details.repo,
|
58
62
|
title,
|
59
63
|
)) do |spinner|
|
60
64
|
project_creator.setup_dependencies
|
@@ -75,6 +79,10 @@ module Script
|
|
75
79
|
ensure
|
76
80
|
script_project_repo.change_to_initial_directory
|
77
81
|
end
|
82
|
+
|
83
|
+
def input_queries_enabled?
|
84
|
+
ShopifyCLI::Feature.enabled?(:scripts_beta_input_queries)
|
85
|
+
end
|
78
86
|
end
|
79
87
|
end
|
80
88
|
end
|
@@ -180,6 +180,23 @@ module Script
|
|
180
180
|
@max_size = max_size
|
181
181
|
end
|
182
182
|
end
|
183
|
+
|
184
|
+
class InvalidInputQueryErrors < ScriptProjectError
|
185
|
+
attr_reader :messages
|
186
|
+
|
187
|
+
def initialize(messages)
|
188
|
+
@messages = messages
|
189
|
+
super()
|
190
|
+
end
|
191
|
+
|
192
|
+
def input_query_path
|
193
|
+
ScriptProjectRepository::INPUT_QUERY_PATH
|
194
|
+
end
|
195
|
+
|
196
|
+
def message
|
197
|
+
messages.join("\n")
|
198
|
+
end
|
199
|
+
end
|
183
200
|
end
|
184
201
|
end
|
185
202
|
end
|
@@ -10,9 +10,7 @@ module Script
|
|
10
10
|
property! :type, accepts: String
|
11
11
|
property! :project_name, accepts: String
|
12
12
|
property! :path_to_project, accepts: String
|
13
|
-
property! :
|
14
|
-
property! :sparse_checkout_branch, accepts: String
|
15
|
-
property! :sparse_checkout_set_path, accepts: String
|
13
|
+
property! :sparse_checkout_details, accepts: SparseCheckoutDetails
|
16
14
|
|
17
15
|
def self.for(
|
18
16
|
ctx:,
|
@@ -20,9 +18,7 @@ module Script
|
|
20
18
|
type:,
|
21
19
|
project_name:,
|
22
20
|
path_to_project:,
|
23
|
-
|
24
|
-
sparse_checkout_branch:,
|
25
|
-
sparse_checkout_set_path:
|
21
|
+
sparse_checkout_details:
|
26
22
|
)
|
27
23
|
|
28
24
|
project_creators = {
|
@@ -36,33 +32,22 @@ module Script
|
|
36
32
|
type: type,
|
37
33
|
project_name: project_name,
|
38
34
|
path_to_project: path_to_project,
|
39
|
-
|
40
|
-
sparse_checkout_branch: sparse_checkout_branch,
|
41
|
-
sparse_checkout_set_path: sparse_checkout_set_path
|
35
|
+
sparse_checkout_details: sparse_checkout_details,
|
42
36
|
)
|
43
37
|
end
|
44
38
|
|
45
39
|
# the sparse checkout process is common to all script types
|
46
40
|
def setup_dependencies
|
47
|
-
|
41
|
+
sparse_checkout_details.setup(ctx)
|
48
42
|
clean
|
49
43
|
end
|
50
44
|
|
51
45
|
private
|
52
46
|
|
53
|
-
def setup_sparse_checkout
|
54
|
-
ShopifyCLI::Git.sparse_checkout(
|
55
|
-
sparse_checkout_repo,
|
56
|
-
sparse_checkout_set_path,
|
57
|
-
sparse_checkout_branch,
|
58
|
-
ctx
|
59
|
-
)
|
60
|
-
end
|
61
|
-
|
62
47
|
def clean
|
63
|
-
source = File.join(path_to_project,
|
48
|
+
source = File.join(path_to_project, sparse_checkout_details.path, ".")
|
64
49
|
FileUtils.cp_r(source, path_to_project)
|
65
|
-
ctx.rm_rf(
|
50
|
+
ctx.rm_rf(sparse_checkout_details.path.split("/")[0])
|
66
51
|
ctx.rm_rf(".git")
|
67
52
|
end
|
68
53
|
|
@@ -83,6 +83,8 @@ module Script
|
|
83
83
|
valid_types: e["message"],
|
84
84
|
filename: script_config.filename,
|
85
85
|
)
|
86
|
+
elsif (errors = user_errors.filter { |err| err["tag"] == "input_query_validation_error" }).any?
|
87
|
+
raise Errors::InvalidInputQueryErrors, errors.map { |err| err["message"] }
|
86
88
|
elsif user_errors.find { |err| %w(not_use_msgpack_error schema_version_argument_error).include?(err["tag"]) }
|
87
89
|
raise Domain::Errors::MetadataValidationError
|
88
90
|
else
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Script
|
4
|
+
module Layers
|
5
|
+
module Infrastructure
|
6
|
+
class SparseCheckoutDetails
|
7
|
+
include SmartProperties
|
8
|
+
property! :repo, accepts: String
|
9
|
+
property! :branch, accepts: String
|
10
|
+
property! :path, accepts: String
|
11
|
+
property! :input_queries_enabled, accepts: [true, false]
|
12
|
+
|
13
|
+
def ==(other)
|
14
|
+
self.class == other.class &&
|
15
|
+
self.class.properties.all? { |name, _| self[name] == other[name] }
|
16
|
+
end
|
17
|
+
|
18
|
+
def setup(ctx)
|
19
|
+
ShopifyCLI::Git.sparse_checkout(repo, patterns_to_checkout, branch, ctx)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def patterns_to_checkout
|
25
|
+
paths = [path]
|
26
|
+
unless input_queries_enabled
|
27
|
+
paths << "!#{path}/#{ScriptProjectRepository::INPUT_QUERY_PATH}"
|
28
|
+
paths << "!#{path}/schema.graphql"
|
29
|
+
end
|
30
|
+
paths.join(" ")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -86,6 +86,9 @@ module Script
|
|
86
86
|
"type(s): %{valid_types}.",
|
87
87
|
configuration_schema_field_invalid_value_error_help: "Change the value of the type.",
|
88
88
|
|
89
|
+
input_query_error_cause: "Input query is invalid:\n%{messages}\n\n",
|
90
|
+
input_query_error_help: "Fix the query in the `%{input_query_path}` file.",
|
91
|
+
|
89
92
|
script_not_found_cause: "Can't find script %s for Script API %s",
|
90
93
|
|
91
94
|
system_call_failure_cause: "Something went wrong while running: {{command:%{cmd}}}.",
|
@@ -216,6 +216,17 @@ module Script
|
|
216
216
|
"script.error.configuration_schema_field_invalid_value_error_help"
|
217
217
|
),
|
218
218
|
}
|
219
|
+
when Layers::Infrastructure::Errors::InvalidInputQueryErrors
|
220
|
+
{
|
221
|
+
cause_of_error: ShopifyCLI::Context.message(
|
222
|
+
"script.error.input_query_error_cause",
|
223
|
+
messages: e.messages.map { |message| " {{x}} #{message}." }.join("\n"),
|
224
|
+
),
|
225
|
+
help_suggestion: ShopifyCLI::Context.message(
|
226
|
+
"script.error.input_query_error_help",
|
227
|
+
input_query_path: e.input_query_path,
|
228
|
+
),
|
229
|
+
}
|
219
230
|
when Layers::Infrastructure::Errors::DependencyInstallError
|
220
231
|
{
|
221
232
|
cause_of_error: ShopifyCLI::Context.message("script.error.dependency_install_cause"),
|
@@ -16,6 +16,7 @@ module Theme
|
|
16
16
|
subcommand :Package, "package", Project.project_filepath("commands/package")
|
17
17
|
subcommand :Open, "open", Project.project_filepath("commands/open")
|
18
18
|
subcommand :List, "list", Project.project_filepath("commands/list")
|
19
|
+
subcommand :Share, "share", Project.project_filepath("commands/share")
|
19
20
|
subcommand :LanguageServer, "language-server", Project.project_filepath("commands/language_server")
|
20
21
|
end
|
21
22
|
ShopifyCLI::Commands.register("Theme::Command", "theme")
|
@@ -24,7 +24,10 @@ module Theme
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def call(*)
|
27
|
-
@theme_check.run
|
27
|
+
@theme_check.run!
|
28
|
+
rescue ThemeCheck::Cli::Abort, ThemeCheck::ThemeCheckError => e
|
29
|
+
raise ShopifyCLI::Abort,
|
30
|
+
ShopifyCLI::Context.message("theme.check.error", e.full_message)
|
28
31
|
end
|
29
32
|
|
30
33
|
def self.help
|
@@ -17,8 +17,8 @@ module Theme
|
|
17
17
|
def call(_args, _name)
|
18
18
|
theme = find_theme(**options.flags)
|
19
19
|
|
20
|
-
@ctx.puts(@ctx.message("theme.open.details", theme.name, theme.editor_url))
|
21
|
-
@ctx.
|
20
|
+
@ctx.puts(@ctx.message("theme.open.details", theme.name, theme.preview_url, theme.editor_url))
|
21
|
+
@ctx.open_browser_url!(theme.preview_url)
|
22
22
|
end
|
23
23
|
|
24
24
|
def self.help
|
@@ -104,9 +104,7 @@ module Theme
|
|
104
104
|
|
105
105
|
if unpublished
|
106
106
|
name = theme || ask_theme_name
|
107
|
-
|
108
|
-
new_theme.create
|
109
|
-
return new_theme
|
107
|
+
return ShopifyCLI::Theme::Theme.create_unpublished(@ctx, root: root, name: name)
|
110
108
|
end
|
111
109
|
|
112
110
|
if theme
|
@@ -16,6 +16,7 @@ module Theme
|
|
16
16
|
parser.on("--port=PORT") { |port| flags[:port] = port.to_i }
|
17
17
|
parser.on("--poll") { flags[:poll] = true }
|
18
18
|
parser.on("--live-reload=MODE") { |mode| flags[:mode] = as_reload_mode(mode) }
|
19
|
+
parser.on("--theme-editor-sync") { flags[:editor_sync] = true }
|
19
20
|
end
|
20
21
|
|
21
22
|
def call(_args, name)
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "shopify_cli/theme/theme"
|
4
|
+
require "shopify_cli/theme/syncer"
|
5
|
+
require "project_types/theme/commands/common/root_helper"
|
6
|
+
|
7
|
+
module Theme
|
8
|
+
class Command
|
9
|
+
class Share < ShopifyCLI::Command::SubCommand
|
10
|
+
include Common::RootHelper
|
11
|
+
|
12
|
+
recommend_default_ruby_range
|
13
|
+
|
14
|
+
def call(_args, name)
|
15
|
+
root = root_value(options, name)
|
16
|
+
theme = create_theme(root)
|
17
|
+
|
18
|
+
upload(theme)
|
19
|
+
|
20
|
+
@ctx.done(done_message(theme))
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.help
|
24
|
+
tool = ShopifyCLI::TOOL_NAME
|
25
|
+
@ctx.message("theme.share.help", tool, tool)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def create_theme(root)
|
31
|
+
ShopifyCLI::Theme::Theme.create_unpublished(@ctx, root: root)
|
32
|
+
end
|
33
|
+
|
34
|
+
def upload(theme)
|
35
|
+
syncer = ShopifyCLI::Theme::Syncer.new(@ctx, theme: theme)
|
36
|
+
syncer.start_threads
|
37
|
+
|
38
|
+
CLI::UI::Frame.open(upload_message(theme)) do
|
39
|
+
UI::SyncProgressBar.new(syncer).progress(:upload_theme!)
|
40
|
+
end
|
41
|
+
|
42
|
+
raise ShopifyCLI::AbortSilent if syncer.has_any_error?
|
43
|
+
ensure
|
44
|
+
syncer.shutdown
|
45
|
+
end
|
46
|
+
|
47
|
+
def upload_message(theme)
|
48
|
+
@ctx.message("theme.share.upload", theme.name, theme.id, theme.shop)
|
49
|
+
end
|
50
|
+
|
51
|
+
def done_message(theme)
|
52
|
+
@ctx.message("theme.share.done", theme.name, theme.preview_url, theme.editor_url)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -8,8 +8,9 @@ module Theme
|
|
8
8
|
Usage: {{command:%1$s theme [ %2$s ]}}
|
9
9
|
HELP
|
10
10
|
ensure_user_error: "You are not authorized to edit themes on %s.",
|
11
|
-
ensure_user_try_this:
|
12
|
-
|
11
|
+
ensure_user_try_this: <<~ENSURE_USER,
|
12
|
+
Check if your user is activated, has permission to edit themes at the store, and try to re-login.
|
13
|
+
ENSURE_USER
|
13
14
|
init: {
|
14
15
|
help: <<~HELP,
|
15
16
|
{{command:%s theme init}}: Clones a Git repository to use as a starting point for building a new theme.
|
@@ -101,13 +102,14 @@ module Theme
|
|
101
102
|
Usage: {{command:%s theme serve [ ROOT ]}}
|
102
103
|
|
103
104
|
Options:
|
104
|
-
{{command:--port=PORT}}
|
105
|
-
{{command:--poll}}
|
106
|
-
{{command:--host=HOST}}
|
107
|
-
{{command:--
|
108
|
-
|
109
|
-
|
110
|
-
|
105
|
+
{{command:--port=PORT}} Local port to serve theme preview from.
|
106
|
+
{{command:--poll}} Force polling to detect file changes.
|
107
|
+
{{command:--host=HOST}} Set which network interface the web server listens on. The default value is 127.0.0.1.
|
108
|
+
{{command:--theme-editor-sync}} Synchronize Theme Editor updates in the local theme files.
|
109
|
+
{{command:--live-reload=MODE}} The live reload mode switches the server behavior when a file is modified:
|
110
|
+
- {{command:hot-reload}} Hot reloads local changes to CSS and sections (default)
|
111
|
+
- {{command:full-page}} Always refreshes the entire page
|
112
|
+
- {{command:off}} Deactivate live reload
|
111
113
|
HELP
|
112
114
|
reload_mode_is_not_valid: "The live reload mode `%s` is not valid.",
|
113
115
|
try_a_valid_reload_mode: "Try a valid live reload mode: %s.",
|
@@ -121,6 +123,36 @@ module Theme
|
|
121
123
|
fixed: "Fixed",
|
122
124
|
},
|
123
125
|
},
|
126
|
+
syncer: {
|
127
|
+
forms: {
|
128
|
+
apply_to_all: {
|
129
|
+
title: "Would like apply this to all the other %s files?",
|
130
|
+
yes: "Yes",
|
131
|
+
no: "No",
|
132
|
+
},
|
133
|
+
update_strategy: {
|
134
|
+
title_context: <<~TITLE,
|
135
|
+
|
136
|
+
The local file {{command:%s}} is different from the remote version in the development theme."
|
137
|
+
TITLE
|
138
|
+
title_question: "What would you like to do?",
|
139
|
+
keep_remote: "Keep the remote version",
|
140
|
+
keep_local: "Keep the local version",
|
141
|
+
union_merge: "Merge files (it may break the local file)",
|
142
|
+
exit: "Exit",
|
143
|
+
},
|
144
|
+
delete_strategy: {
|
145
|
+
title_context: <<~TITLE,
|
146
|
+
|
147
|
+
The local file {{command:%s}} has been recently removed, but it's present on your remote development theme.",
|
148
|
+
TITLE
|
149
|
+
title_question: "What would you like to do?",
|
150
|
+
delete: "Delete permanently",
|
151
|
+
restore: "Restore with the remote version",
|
152
|
+
exit: "Exit",
|
153
|
+
},
|
154
|
+
},
|
155
|
+
},
|
124
156
|
error: {
|
125
157
|
address_binding_error: "Couldn't bind to localhost."\
|
126
158
|
" To serve your theme, set a different address with {{command:%s theme serve --host=<address>}}",
|
@@ -137,9 +169,10 @@ module Theme
|
|
137
169
|
Serving %s
|
138
170
|
|
139
171
|
SERVING
|
172
|
+
download_changes: ", and use 'theme pull' to get the changes",
|
140
173
|
customize_or_preview: <<~CUSTOMIZE_OR_PREVIEW,
|
141
174
|
|
142
|
-
Customize this theme in the Theme Editor:
|
175
|
+
Customize this theme in the Theme Editor%s:
|
143
176
|
{{green:%s}}
|
144
177
|
|
145
178
|
Share this theme preview:
|
@@ -149,7 +182,7 @@ module Theme
|
|
149
182
|
CUSTOMIZE_OR_PREVIEW
|
150
183
|
ensure_user: <<~ENSURE_USER,
|
151
184
|
You are not authorized to edit themes on %s.
|
152
|
-
|
185
|
+
Check if your user is activated, has permission to edit themes at the store, and try to re-login.
|
153
186
|
ENSURE_USER
|
154
187
|
address_already_in_use: "The address \"%s\" is already in use.",
|
155
188
|
try_port_option: "Use the --port=PORT option to serve the theme in a different port.",
|
@@ -159,6 +192,7 @@ module Theme
|
|
159
192
|
Check your theme for errors, suggestions, and best practices.
|
160
193
|
Usage: {{command:%s check}}
|
161
194
|
HELP
|
195
|
+
error: "Theme check failed with error:\n%s",
|
162
196
|
},
|
163
197
|
delete: {
|
164
198
|
help: <<~HELP,
|
@@ -230,6 +264,9 @@ module Theme
|
|
230
264
|
details: <<~DETAILS,
|
231
265
|
{{*}} {{bold:%s}}
|
232
266
|
|
267
|
+
Preview your theme:
|
268
|
+
{{green:%s}}
|
269
|
+
|
233
270
|
Customize your theme in the Theme Editor:
|
234
271
|
{{green:%s}}
|
235
272
|
|
@@ -253,6 +290,22 @@ module Theme
|
|
253
290
|
Usage: {{command:%s theme list}}
|
254
291
|
HELP
|
255
292
|
},
|
293
|
+
share: {
|
294
|
+
help: <<~HELP,
|
295
|
+
{{command:%s theme share}}: Creates a shareable, unpublished, and new theme on your theme library with a randomized name.
|
296
|
+
Works like an alias to {{command:theme push -u -t=RANDOMIZED_NAME}}.
|
297
|
+
|
298
|
+
Usage: {{command:%s theme share [ ROOT ]}}
|
299
|
+
HELP
|
300
|
+
done: <<~DONE,
|
301
|
+
{{green:The {{bold:%s}} theme was pushed successfully}}
|
302
|
+
|
303
|
+
{{info:Share your theme preview:}}
|
304
|
+
{{underline:%s}}
|
305
|
+
|
306
|
+
DONE
|
307
|
+
upload: "Pushing theme files to %s (#%s) on %s",
|
308
|
+
},
|
256
309
|
},
|
257
310
|
}.freeze
|
258
311
|
end
|
@@ -1,23 +1,32 @@
|
|
1
|
+
require "date"
|
1
2
|
require "shopify_cli/sed"
|
3
|
+
require "octokit"
|
2
4
|
|
3
5
|
module ShopifyCLI
|
4
6
|
class Changelog
|
5
7
|
CHANGELOG_FILE = File.join(ShopifyCLI::ROOT, "CHANGELOG.md")
|
8
|
+
CHANGE_CATEGORIES = %w(Added Changed Deprecated Removed Fixed Security)
|
6
9
|
|
7
10
|
def initialize
|
8
11
|
load(File.read(CHANGELOG_FILE))
|
9
12
|
end
|
10
13
|
|
11
14
|
def update_version!(new_version)
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
changes[new_version] = changes["Unreleased"]
|
16
|
+
changes[new_version][:date] = Date.today.iso8601
|
17
|
+
changes["Unreleased"] = { changes: [], date: nil }
|
18
|
+
save!
|
19
|
+
end
|
20
|
+
|
21
|
+
def update!
|
22
|
+
pr = pr_for_current_branch
|
23
|
+
category = CLI::UI::Prompt.ask("What type of change?", options: CHANGE_CATEGORIES)
|
24
|
+
add_change(category, { pr_id: pr.number, desc: pr.title })
|
25
|
+
save!
|
17
26
|
end
|
18
27
|
|
19
28
|
def release_notes(version)
|
20
|
-
changes[version].map do |change_category, changes|
|
29
|
+
changes[version][:changes].map do |change_category, changes|
|
21
30
|
<<~CHANGES
|
22
31
|
### #{change_category}
|
23
32
|
#{changes.map { |change| entry(**change) }.join("\n")}
|
@@ -25,17 +34,61 @@ module ShopifyCLI
|
|
25
34
|
end.join("\n")
|
26
35
|
end
|
27
36
|
|
37
|
+
def add_change(category, change)
|
38
|
+
changes["Unreleased"][:changes][category] << change
|
39
|
+
end
|
40
|
+
|
28
41
|
def entry(pr_id:, desc:)
|
29
42
|
"* [##{pr_id}](https://github.com/Shopify/shopify-cli/pull/#{pr_id}): #{desc}"
|
30
43
|
end
|
31
44
|
|
45
|
+
def full_contents
|
46
|
+
sorted_changes = changes.each_key.sort_by do |change|
|
47
|
+
if change == "Unreleased"
|
48
|
+
[Float::INFINITY] * 3 # end of the list
|
49
|
+
else
|
50
|
+
major, minor, patch = change.split(".").map(&:to_i)
|
51
|
+
[major, minor, patch]
|
52
|
+
end
|
53
|
+
end.reverse
|
54
|
+
[
|
55
|
+
heading,
|
56
|
+
*sorted_changes.each.map { |version| release_notes_with_header(version) }.join,
|
57
|
+
remainder,
|
58
|
+
].map { |section| section.chomp << "\n" }.join
|
59
|
+
end
|
60
|
+
|
61
|
+
def save!
|
62
|
+
File.write(CHANGELOG_FILE, full_contents)
|
63
|
+
end
|
64
|
+
|
32
65
|
private
|
33
66
|
|
67
|
+
attr_reader :heading, :remainder
|
68
|
+
|
69
|
+
def release_notes_with_header(version)
|
70
|
+
header_line =
|
71
|
+
if version == "Unreleased"
|
72
|
+
"[Unreleased]"
|
73
|
+
else
|
74
|
+
date = changes[version][:date]
|
75
|
+
"Version #{version}#{" - #{date}" if date}"
|
76
|
+
end
|
77
|
+
|
78
|
+
[
|
79
|
+
"## #{header_line}",
|
80
|
+
release_notes(version),
|
81
|
+
].reject(&:empty?).map { |section| section.chomp << "\n\n" }.join
|
82
|
+
end
|
83
|
+
|
34
84
|
def changes
|
35
85
|
@changes ||= Hash.new do |h, k|
|
36
|
-
h[k] =
|
37
|
-
|
38
|
-
|
86
|
+
h[k] = {
|
87
|
+
date: nil,
|
88
|
+
changes: Hash.new do |h2, k2|
|
89
|
+
h2[k2] = []
|
90
|
+
end,
|
91
|
+
}
|
39
92
|
end
|
40
93
|
end
|
41
94
|
|
@@ -43,34 +96,53 @@ module ShopifyCLI
|
|
43
96
|
state = :initial
|
44
97
|
change_category = nil
|
45
98
|
current_version = nil
|
99
|
+
@heading = ""
|
46
100
|
@remainder = ""
|
47
101
|
log.each_line do |line|
|
48
102
|
case state
|
49
103
|
when :initial
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
104
|
+
if line.chomp == "\#\# [Unreleased]"
|
105
|
+
state = :unreleased
|
106
|
+
current_version = "Unreleased"
|
107
|
+
# Ensure Unreleased changeset exists even if no changes have happened yet
|
108
|
+
changes["Unreleased"]
|
109
|
+
else
|
110
|
+
@heading << line
|
111
|
+
end
|
112
|
+
when :unreleased, :prior_versions
|
54
113
|
if /\A\#\#\# (?<category>\w+)/ =~ line
|
55
114
|
change_category = category
|
56
|
-
elsif %r{\A\* \[\#(?<
|
57
|
-
changes[current_version][change_category] << { pr_id:
|
58
|
-
elsif /\A\#\# Version (?<version>\d+\.\d+\.\d+)
|
115
|
+
elsif %r{\A\* \[\#(?<id>\d+)\]\(https://github.com/Shopify/shopify-cli/pull/\k<id>\): (?<desc>.+)\n} =~ line
|
116
|
+
changes[current_version][:changes][change_category] << { pr_id: id, desc: desc }
|
117
|
+
elsif /\A\#\# Version (?<version>\d+\.\d+\.\d+)( - (?<date>\d{4}-\d{2}-\d{2}))?/ =~ line
|
59
118
|
current_version = version
|
60
|
-
state =
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
119
|
+
state = :prior_versions
|
120
|
+
major, minor, _patch = current_version.split(".")
|
121
|
+
if major.to_i <= 2 && minor.to_i < 7
|
122
|
+
# Changelog starts to become irregular in 2.6.x
|
123
|
+
state = :finished
|
124
|
+
end
|
125
|
+
changes[current_version][:date] = date unless state == :finished
|
67
126
|
elsif !line.match?(/\s*\n/)
|
68
127
|
raise "Unrecognized line: #{line.inspect}"
|
69
128
|
end
|
70
|
-
when :finished
|
71
|
-
@remainder << line
|
72
129
|
end
|
130
|
+
@remainder << line if state == :finished
|
73
131
|
end
|
74
132
|
end
|
133
|
+
|
134
|
+
def pr_for_current_branch
|
135
|
+
current_branch = %x(git branch --show-current).chomp
|
136
|
+
search_term = "repo:Shopify/shopify-cli is:pr is:open head:#{current_branch}"
|
137
|
+
results = Octokit::Client.new.search_issues(search_term)
|
138
|
+
case results.total_count
|
139
|
+
when 0
|
140
|
+
raise "PR not opened yet!"
|
141
|
+
when (2..)
|
142
|
+
raise "Multiple open PRs, not sure which one to use for changelog!"
|
143
|
+
end
|
144
|
+
|
145
|
+
results.items.first
|
146
|
+
end
|
75
147
|
end
|
76
148
|
end
|
@@ -20,6 +20,10 @@ module ShopifyCLI
|
|
20
20
|
end
|
21
21
|
host
|
22
22
|
end
|
23
|
+
|
24
|
+
def no_update
|
25
|
+
options.flags[:no_update] || false
|
26
|
+
end
|
23
27
|
end
|
24
28
|
end
|
25
29
|
|
@@ -37,6 +41,12 @@ module ShopifyCLI
|
|
37
41
|
parser.on("--port=PORT") { |port| flags[:port] = port }
|
38
42
|
end
|
39
43
|
end
|
44
|
+
|
45
|
+
def parse_no_update_option
|
46
|
+
options do |parser, flags|
|
47
|
+
parser.on("--no-update") { flags[:no_update] = true }
|
48
|
+
end
|
49
|
+
end
|
40
50
|
end
|
41
51
|
end
|
42
52
|
end
|