shopify-cli 2.7.3 → 2.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +44 -0
- data/Gemfile.lock +1 -1
- data/RELEASING.md +4 -3
- data/dev.yml +2 -2
- data/ext/javy/javy.rb +8 -9
- 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/gem.rb +1 -2
- data/lib/project_types/script/cli.rb +7 -0
- 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/layers/application/connect_app.rb +15 -3
- data/lib/project_types/script/layers/application/create_script.rb +16 -16
- data/lib/project_types/script/layers/application/extension_points.rb +50 -26
- data/lib/project_types/script/layers/application/push_script.rb +5 -2
- data/lib/project_types/script/layers/domain/errors.rb +3 -2
- data/lib/project_types/script/layers/domain/extension_point.rb +14 -0
- data/lib/project_types/script/layers/domain/script_config.rb +6 -4
- data/lib/project_types/script/layers/infrastructure/errors.rb +38 -23
- data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +49 -28
- data/lib/project_types/script/layers/infrastructure/script_service.rb +22 -5
- 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 +39 -16
- data/lib/project_types/script/ui/error_handler.rb +46 -29
- data/lib/project_types/theme/commands/pull.rb +45 -17
- data/lib/project_types/theme/commands/push.rb +65 -28
- data/lib/project_types/theme/commands/serve.rb +5 -0
- data/lib/project_types/theme/messages/messages.rb +34 -18
- data/lib/shopify_cli/command.rb +6 -0
- data/lib/shopify_cli/commands/login.rb +1 -1
- data/lib/shopify_cli/commands/switch.rb +1 -1
- data/lib/shopify_cli/constants.rb +11 -2
- data/lib/shopify_cli/context.rb +66 -12
- data/lib/shopify_cli/core/executor.rb +4 -4
- data/lib/shopify_cli/environment.rb +50 -20
- data/lib/shopify_cli/form.rb +2 -0
- data/lib/shopify_cli/identity_auth.rb +4 -3
- data/lib/shopify_cli/messages/messages.rb +9 -1
- data/lib/shopify_cli/method_object.rb +21 -9
- 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/result.rb +61 -59
- data/lib/shopify_cli/task.rb +5 -3
- data/lib/shopify_cli/theme/dev_server/cdn/cdn_helper.rb +49 -0
- data/lib/shopify_cli/theme/dev_server/cdn_assets.rb +69 -0
- data/lib/shopify_cli/theme/dev_server/cdn_fonts.rb +8 -28
- data/lib/shopify_cli/theme/dev_server/hot-reload.js +34 -3
- data/lib/shopify_cli/theme/dev_server/hot_reload.rb +18 -2
- data/lib/shopify_cli/theme/dev_server/local_assets.rb +4 -0
- 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/file.rb +2 -2
- data/lib/shopify_cli/theme/filter/path_matcher.rb +38 -0
- data/lib/shopify_cli/theme/ignore_filter.rb +14 -18
- data/lib/shopify_cli/theme/include_filter.rb +43 -0
- data/lib/shopify_cli/theme/syncer.rb +17 -2
- 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/version.rb +1 -1
- data/lib/shopify_cli.rb +6 -1
- data/vendor/deps/cli-kit/lib/cli/kit/error_handler.rb +3 -1
- data/vendor/deps/ruby2_keywords/LICENSE +22 -0
- data/vendor/deps/ruby2_keywords/README.md +67 -0
- data/vendor/deps/ruby2_keywords/Rakefile +54 -0
- data/vendor/deps/ruby2_keywords/lib/ruby2_keywords.rb +57 -0
- data/vendor/deps/ruby2_keywords/ruby2_keywords.gemspec +18 -0
- data/vendor/deps/ruby2_keywords/test/test_keyword.rb +41 -0
- metadata +28 -4
- data/lib/graphql/all_orgs_with_extensions.graphql +0 -37
- data/lib/project_types/extension/tasks/run_extension_command.rb +0 -82
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cgi"
|
4
|
+
|
5
|
+
module ShopifyCLI
|
6
|
+
module Theme
|
7
|
+
module DevServer
|
8
|
+
class Proxy
|
9
|
+
class TemplateParamBuilder
|
10
|
+
def build
|
11
|
+
# Core doesn't support replace_templates
|
12
|
+
return {} if core?(current_path)
|
13
|
+
|
14
|
+
(syncer_templates + request_templates)
|
15
|
+
.select { |file| file.liquid? || file.json? }
|
16
|
+
.uniq(&:relative_path)
|
17
|
+
.map { |file| as_param(file) }
|
18
|
+
.to_h
|
19
|
+
end
|
20
|
+
|
21
|
+
def with_core_endpoints(core_endpoints)
|
22
|
+
@core_endpoints = core_endpoints
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def with_syncer(syncer)
|
27
|
+
@syncer = syncer
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def with_rack_env(rack_env)
|
32
|
+
@rack_env = rack_env
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def with_theme(theme)
|
37
|
+
@theme = theme
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def as_param(file)
|
44
|
+
["replace_templates[#{file.relative_path}]", file.read]
|
45
|
+
end
|
46
|
+
|
47
|
+
def syncer_templates
|
48
|
+
@syncer&.pending_updates || []
|
49
|
+
end
|
50
|
+
|
51
|
+
def request_templates
|
52
|
+
cookie_sections
|
53
|
+
.map { |section| @theme[section] unless @theme.nil? }
|
54
|
+
.compact
|
55
|
+
end
|
56
|
+
|
57
|
+
def cookie_sections
|
58
|
+
CGI::Cookie.parse(cookie)["hot_reload_sections"].join.split(",") || []
|
59
|
+
end
|
60
|
+
|
61
|
+
def core?(path)
|
62
|
+
core_endpoints.include?(path)
|
63
|
+
end
|
64
|
+
|
65
|
+
def current_path
|
66
|
+
rack_env["PATH_INFO"]
|
67
|
+
end
|
68
|
+
|
69
|
+
def cookie
|
70
|
+
rack_env["HTTP_COOKIE"]
|
71
|
+
end
|
72
|
+
|
73
|
+
def core_endpoints
|
74
|
+
@core_endpoints || []
|
75
|
+
end
|
76
|
+
|
77
|
+
def rack_env
|
78
|
+
@rack_env || {}
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -2,6 +2,9 @@
|
|
2
2
|
require "net/http"
|
3
3
|
require "stringio"
|
4
4
|
require "time"
|
5
|
+
require "cgi"
|
6
|
+
|
7
|
+
require_relative "proxy/template_param_builder"
|
5
8
|
|
6
9
|
module ShopifyCLI
|
7
10
|
module Theme
|
@@ -15,6 +18,7 @@ module ShopifyCLI
|
|
15
18
|
"trailer",
|
16
19
|
"transfer-encoding",
|
17
20
|
"upgrade",
|
21
|
+
"content-security-policy",
|
18
22
|
]
|
19
23
|
|
20
24
|
class Proxy
|
@@ -112,21 +116,12 @@ module ShopifyCLI
|
|
112
116
|
end
|
113
117
|
|
114
118
|
def build_replace_templates_param(env)
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
# Only replace Liquid or JSON files
|
122
|
-
file.liquid? || file.json?
|
123
|
-
end
|
124
|
-
|
125
|
-
pending_templates.each do |path|
|
126
|
-
params["replace_templates[#{path.relative_path}]"] = path.read
|
127
|
-
end
|
128
|
-
|
129
|
-
params
|
119
|
+
TemplateParamBuilder.new
|
120
|
+
.with_core_endpoints(@core_endpoints)
|
121
|
+
.with_syncer(@syncer)
|
122
|
+
.with_theme(@theme)
|
123
|
+
.with_rack_env(env)
|
124
|
+
.build
|
130
125
|
end
|
131
126
|
|
132
127
|
def add_session_cookie(cookie_header)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ShopifyCLI
|
4
|
+
module Theme
|
5
|
+
module DevServer
|
6
|
+
class ReloadMode
|
7
|
+
MODES = [:"hot-reload", :"full-page", :off]
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def default
|
11
|
+
:"hot-reload"
|
12
|
+
end
|
13
|
+
|
14
|
+
def get!(mode)
|
15
|
+
MODES.find { |m| m == mode.to_sym } || raise_error(mode)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def raise_error(invalid_mode)
|
21
|
+
error_message = ShopifyCLI::Context.message("theme.serve.reload_mode_is_not_valid", invalid_mode)
|
22
|
+
help_message = ShopifyCLI::Context.message("theme.serve.try_a_valid_reload_mode", valid_modes)
|
23
|
+
|
24
|
+
ShopifyCLI::Context.abort(error_message, help_message)
|
25
|
+
end
|
26
|
+
|
27
|
+
def valid_modes
|
28
|
+
MODES.map { |v| "`#{v}`" }.join(", ")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -3,9 +3,11 @@ require_relative "development_theme"
|
|
3
3
|
require_relative "ignore_filter"
|
4
4
|
require_relative "syncer"
|
5
5
|
|
6
|
+
require_relative "dev_server/cdn_assets"
|
6
7
|
require_relative "dev_server/cdn_fonts"
|
7
8
|
require_relative "dev_server/hot_reload"
|
8
9
|
require_relative "dev_server/header_hash"
|
10
|
+
require_relative "dev_server/reload_mode"
|
9
11
|
require_relative "dev_server/local_assets"
|
10
12
|
require_relative "dev_server/proxy"
|
11
13
|
require_relative "dev_server/sse"
|
@@ -25,7 +27,7 @@ module ShopifyCLI
|
|
25
27
|
class << self
|
26
28
|
attr_accessor :ctx
|
27
29
|
|
28
|
-
def start(ctx, root, host: "127.0.0.1", port: 9292, poll: false)
|
30
|
+
def start(ctx, root, host: "127.0.0.1", port: 9292, poll: false, mode: ReloadMode.default)
|
29
31
|
@ctx = ctx
|
30
32
|
theme = DevelopmentTheme.new(ctx, root: root)
|
31
33
|
ignore_filter = IgnoreFilter.from_path(root)
|
@@ -36,7 +38,8 @@ module ShopifyCLI
|
|
36
38
|
@app = Proxy.new(ctx, theme: theme, syncer: @syncer)
|
37
39
|
@app = CdnFonts.new(@app, theme: theme)
|
38
40
|
@app = LocalAssets.new(ctx, @app, theme: theme)
|
39
|
-
@app =
|
41
|
+
@app = CdnAssets.new(@app, theme: theme)
|
42
|
+
@app = HotReload.new(ctx, @app, theme: theme, watcher: watcher, mode: mode, ignore_filter: ignore_filter)
|
40
43
|
stopped = false
|
41
44
|
address = "http://#{host}:#{port}"
|
42
45
|
|
@@ -83,7 +86,9 @@ module ShopifyCLI
|
|
83
86
|
ShopifyCLI::API::APIRequestUnauthorizedError
|
84
87
|
raise ShopifyCLI::Abort, @ctx.message("theme.serve.ensure_user", theme.shop)
|
85
88
|
rescue Errno::EADDRINUSE
|
86
|
-
|
89
|
+
error_message = @ctx.message("theme.serve.address_already_in_use", address)
|
90
|
+
help_message = @ctx.message("theme.serve.try_port_option")
|
91
|
+
@ctx.abort(error_message, help_message)
|
87
92
|
rescue Errno::EADDRNOTAVAIL
|
88
93
|
raise AddressBindingError, "Error binding to the address #{host}."
|
89
94
|
end
|
@@ -94,24 +99,6 @@ module ShopifyCLI
|
|
94
99
|
@syncer.shutdown
|
95
100
|
WebServer.shutdown
|
96
101
|
end
|
97
|
-
|
98
|
-
private
|
99
|
-
|
100
|
-
def abort_address_already_in_use(address)
|
101
|
-
open_frame(@ctx.message("theme.serve.already_in_use_error"), color: :red) do
|
102
|
-
@ctx.puts(@ctx.message("theme.serve.address_already_in_use", address))
|
103
|
-
end
|
104
|
-
|
105
|
-
open_frame(@ctx.message("theme.serve.try_this"), color: :green) do
|
106
|
-
@ctx.puts(@ctx.message("theme.serve.try_port_option"))
|
107
|
-
end
|
108
|
-
|
109
|
-
raise ShopifyCLI::AbortSilent
|
110
|
-
end
|
111
|
-
|
112
|
-
def open_frame(title, color:, &block)
|
113
|
-
CLI::UI::Frame.open(title, color: CLI::UI.resolve_color(color), timing: false, &block)
|
114
|
-
end
|
115
102
|
end
|
116
103
|
end
|
117
104
|
end
|
@@ -18,7 +18,7 @@ module ShopifyCLI
|
|
18
18
|
|
19
19
|
def read
|
20
20
|
if text?
|
21
|
-
path.read
|
21
|
+
path.read(universal_newline: true)
|
22
22
|
else
|
23
23
|
path.read(mode: "rb")
|
24
24
|
end
|
@@ -27,7 +27,7 @@ module ShopifyCLI
|
|
27
27
|
def write(content)
|
28
28
|
path.parent.mkpath unless path.parent.directory?
|
29
29
|
if text?
|
30
|
-
path.write(content)
|
30
|
+
path.write(content, universal_newline: true)
|
31
31
|
else
|
32
32
|
path.write(content, 0, mode: "wb")
|
33
33
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ShopifyCLI
|
4
|
+
module Theme
|
5
|
+
module Filter
|
6
|
+
module PathMatcher
|
7
|
+
def regex_match?(regex, path)
|
8
|
+
regex.match?(path)
|
9
|
+
rescue StandardError
|
10
|
+
false
|
11
|
+
end
|
12
|
+
|
13
|
+
def glob_match?(glob, path)
|
14
|
+
!!::File.fnmatch?(glob, path)
|
15
|
+
end
|
16
|
+
|
17
|
+
def regex?(pattern)
|
18
|
+
pattern.start_with?("/") && pattern.end_with?("/")
|
19
|
+
end
|
20
|
+
|
21
|
+
def as_regex(pattern)
|
22
|
+
Regexp.new(pattern.gsub(%r{^\/|\/$}, ""))
|
23
|
+
end
|
24
|
+
|
25
|
+
def as_glob(pattern)
|
26
|
+
# if specifying a directory, match everything below it
|
27
|
+
pattern += "*" if pattern.end_with?("/")
|
28
|
+
|
29
|
+
# The pattern will be scoped to root directory, so it should match anything
|
30
|
+
# within that space
|
31
|
+
pattern.prepend("*") unless pattern.start_with?("*")
|
32
|
+
|
33
|
+
pattern
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -1,8 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "filter/path_matcher"
|
4
|
+
|
3
5
|
module ShopifyCLI
|
4
6
|
module Theme
|
5
7
|
class IgnoreFilter
|
8
|
+
include Filter::PathMatcher
|
9
|
+
|
6
10
|
FILE = ".shopifyignore"
|
7
11
|
|
8
12
|
DEFAULT_REGEXES = [
|
@@ -72,11 +76,11 @@ module ShopifyCLI
|
|
72
76
|
return true if path.empty?
|
73
77
|
|
74
78
|
regexes.each do |regex|
|
75
|
-
return true if regex
|
79
|
+
return true if regex_match?(regex, path)
|
76
80
|
end
|
77
81
|
|
78
82
|
globs.each do |glob|
|
79
|
-
return true if
|
83
|
+
return true if glob_match?(glob, path)
|
80
84
|
end
|
81
85
|
|
82
86
|
false
|
@@ -91,24 +95,16 @@ module ShopifyCLI
|
|
91
95
|
new_regexes = DEFAULT_REGEXES.dup
|
92
96
|
new_globs = DEFAULT_GLOBS.dup
|
93
97
|
|
94
|
-
patterns
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
98
|
+
patterns
|
99
|
+
.map(&:strip)
|
100
|
+
.each do |pattern|
|
101
|
+
if regex?(pattern)
|
102
|
+
new_regexes << as_regex(pattern)
|
103
|
+
else
|
104
|
+
new_globs << as_glob(pattern)
|
105
|
+
end
|
100
106
|
end
|
101
107
|
|
102
|
-
# if specifying a directory, match everything below it
|
103
|
-
pattern += "*" if pattern.end_with?("/")
|
104
|
-
|
105
|
-
# The pattern will be scoped to root directory, so it should match anything
|
106
|
-
# within that space
|
107
|
-
pattern.prepend("*") unless pattern.start_with?("*")
|
108
|
-
|
109
|
-
new_globs << pattern
|
110
|
-
end
|
111
|
-
|
112
108
|
[new_regexes, new_globs]
|
113
109
|
end
|
114
110
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "filter/path_matcher"
|
4
|
+
|
5
|
+
module ShopifyCLI
|
6
|
+
module Theme
|
7
|
+
class IncludeFilter
|
8
|
+
include Filter::PathMatcher
|
9
|
+
|
10
|
+
def initialize(pattern = nil)
|
11
|
+
@pattern = pattern
|
12
|
+
end
|
13
|
+
|
14
|
+
def match?(path)
|
15
|
+
return true unless present?(@pattern)
|
16
|
+
|
17
|
+
if regex_pattern?
|
18
|
+
regex_match?(regex_pattern, path)
|
19
|
+
else
|
20
|
+
glob_match?(glob_pattern, path)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def present?(pattern)
|
27
|
+
!pattern.nil? && !pattern.empty?
|
28
|
+
end
|
29
|
+
|
30
|
+
def regex_pattern?
|
31
|
+
@is_regex_pattern ||= regex?(@pattern)
|
32
|
+
end
|
33
|
+
|
34
|
+
def regex_pattern
|
35
|
+
@regex_pattern ||= as_regex(@pattern)
|
36
|
+
end
|
37
|
+
|
38
|
+
def glob_pattern
|
39
|
+
@glob_pattern ||= as_glob(@pattern)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -16,13 +16,15 @@ module ShopifyCLI
|
|
16
16
|
API_VERSION = "unstable"
|
17
17
|
|
18
18
|
attr_reader :checksums
|
19
|
+
attr_accessor :include_filter
|
19
20
|
attr_accessor :ignore_filter
|
20
21
|
|
21
22
|
def_delegators :@error_reporter, :has_any_error?
|
22
23
|
|
23
|
-
def initialize(ctx, theme:, ignore_filter: nil)
|
24
|
+
def initialize(ctx, theme:, include_filter: nil, ignore_filter: nil)
|
24
25
|
@ctx = ctx
|
25
26
|
@theme = theme
|
27
|
+
@include_filter = include_filter
|
26
28
|
@ignore_filter = ignore_filter
|
27
29
|
@error_reporter = ErrorReporter.new(ctx)
|
28
30
|
@standard_reporter = StandardReporter.new(ctx)
|
@@ -193,7 +195,7 @@ module ShopifyCLI
|
|
193
195
|
# Already enqueued
|
194
196
|
return if @pending.include?(operation)
|
195
197
|
|
196
|
-
if
|
198
|
+
if ignore?(operation)
|
197
199
|
@ctx.debug("ignore #{operation.file_path}")
|
198
200
|
return
|
199
201
|
end
|
@@ -251,6 +253,19 @@ module ShopifyCLI
|
|
251
253
|
response
|
252
254
|
end
|
253
255
|
|
256
|
+
def ignore?(operation)
|
257
|
+
path = operation.file_path
|
258
|
+
ignored_by_ignore_filter?(path) || ignored_by_include_filter?(path)
|
259
|
+
end
|
260
|
+
|
261
|
+
def ignored_by_ignore_filter?(path)
|
262
|
+
ignore_filter&.ignore?(path)
|
263
|
+
end
|
264
|
+
|
265
|
+
def ignored_by_include_filter?(path)
|
266
|
+
include_filter && !include_filter.match?(path)
|
267
|
+
end
|
268
|
+
|
254
269
|
def get(file)
|
255
270
|
_status, body, response = ShopifyCLI::AdminAPI.rest_request(
|
256
271
|
@ctx,
|
@@ -173,15 +173,37 @@ module ShopifyCLI
|
|
173
173
|
end
|
174
174
|
|
175
175
|
def live(ctx, root: nil)
|
176
|
-
|
176
|
+
find(ctx, root) { |attrs| attrs["role"] == "main" }
|
177
|
+
end
|
177
178
|
|
178
|
-
|
179
|
-
|
180
|
-
|
179
|
+
def development(ctx, root: nil)
|
180
|
+
find(ctx, root) { |attrs| attrs["role"] == "development" }
|
181
|
+
end
|
182
|
+
|
183
|
+
# Finds a Theme by its identifier
|
184
|
+
#
|
185
|
+
# #### Parameters
|
186
|
+
# * `ctx` - current running context of your command
|
187
|
+
# * `root` - theme root
|
188
|
+
# * `identifier` - theme ID or theme name
|
189
|
+
def find_by_identifier(ctx, root: nil, identifier:)
|
190
|
+
find(ctx, root) do |attrs|
|
191
|
+
attrs.slice("name", "id").values.map(&:to_s).include?(identifier)
|
192
|
+
end
|
181
193
|
end
|
182
194
|
|
183
195
|
private
|
184
196
|
|
197
|
+
def find(ctx, root, &block)
|
198
|
+
_status, body = fetch_themes(ctx)
|
199
|
+
|
200
|
+
body["themes"]
|
201
|
+
.find(&block)
|
202
|
+
.tap do |attrs|
|
203
|
+
break new(ctx, root: root, **allowed_attrs(attrs)) if attrs
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
185
207
|
def allowed_attrs(attrs)
|
186
208
|
attrs.slice("id", "name", "role").transform_keys(&:to_sym)
|
187
209
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ShopifyCLI
|
4
|
+
class ThreadPool
|
5
|
+
class Job
|
6
|
+
attr_reader :error
|
7
|
+
|
8
|
+
def perform!
|
9
|
+
raise "`#{self.class.name}#perform!` must be defined"
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
perform!
|
14
|
+
rescue StandardError => error
|
15
|
+
@error = error
|
16
|
+
end
|
17
|
+
|
18
|
+
def success?
|
19
|
+
!@error
|
20
|
+
end
|
21
|
+
|
22
|
+
def error?
|
23
|
+
!!@error
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ShopifyCLI
|
4
|
+
class ThreadPool
|
5
|
+
attr_reader :errors
|
6
|
+
|
7
|
+
def initialize(pool_size: 10)
|
8
|
+
@jobs = Queue.new
|
9
|
+
@pool = Array.new(pool_size) { spawn_thread }
|
10
|
+
end
|
11
|
+
|
12
|
+
def schedule(job)
|
13
|
+
@jobs << job
|
14
|
+
end
|
15
|
+
|
16
|
+
def shutdown
|
17
|
+
@pool.size.times do
|
18
|
+
schedule(-> { throw(:stop_thread) })
|
19
|
+
end
|
20
|
+
@pool.map(&:join)
|
21
|
+
ensure
|
22
|
+
@jobs.close
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def spawn_thread
|
28
|
+
Thread.new do
|
29
|
+
catch(:stop_thread) do
|
30
|
+
loop do
|
31
|
+
@jobs.pop.call
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/shopify_cli/version.rb
CHANGED
data/lib/shopify_cli.rb
CHANGED
@@ -15,12 +15,13 @@ ENV["PATH"] = ENV["PATH"].split(":").select { |p| p.start_with?("/", "~") }.join
|
|
15
15
|
vendor_path = File.expand_path("../../vendor/lib", __FILE__)
|
16
16
|
$LOAD_PATH.unshift(vendor_path) unless $LOAD_PATH.include?(vendor_path)
|
17
17
|
|
18
|
-
deps = %w(cli-ui cli-kit smart_properties webrick)
|
18
|
+
deps = %w(cli-ui cli-kit smart_properties ruby2_keywords webrick)
|
19
19
|
deps.each do |dep|
|
20
20
|
vendor_path = File.expand_path("../../vendor/deps/#{dep}/lib", __FILE__)
|
21
21
|
$LOAD_PATH.unshift(vendor_path) unless $LOAD_PATH.include?(vendor_path)
|
22
22
|
end
|
23
23
|
|
24
|
+
require "ruby2_keywords"
|
24
25
|
require "cli/ui"
|
25
26
|
require "cli/kit"
|
26
27
|
require "smart_properties"
|
@@ -139,6 +140,10 @@ module ShopifyCLI
|
|
139
140
|
require "shopify_cli/messages/messages"
|
140
141
|
Context.load_messages(ShopifyCLI::Messages::MESSAGES)
|
141
142
|
|
143
|
+
# cli-ui utilities for capturing the output close the stream while capturing.
|
144
|
+
# By setting the value here we persist the tty value for the whole lifetime of the process.
|
145
|
+
Environment.interactive = $stdin.tty?
|
146
|
+
|
142
147
|
def self.cache_dir
|
143
148
|
cache_dir = if Environment.test?
|
144
149
|
TEMP_DIR
|
@@ -114,7 +114,9 @@ module CLI
|
|
114
114
|
end
|
115
115
|
|
116
116
|
def print_error_message(e)
|
117
|
-
|
117
|
+
CLI::UI::Frame.open("Error", color: :red, timing: false) do
|
118
|
+
$stderr.puts(format_error_message(e.message))
|
119
|
+
end
|
118
120
|
end
|
119
121
|
end
|
120
122
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright 2019-2020 Nobuyoshi Nakada, Yusuke Endoh
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
4
|
+
modification, are permitted provided that the following conditions are met:
|
5
|
+
|
6
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
7
|
+
list of conditions and the following disclaimer.
|
8
|
+
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
this list of conditions and the following disclaimer in the documentation
|
11
|
+
and/or other materials provided with the distribution.
|
12
|
+
|
13
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
14
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
15
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
16
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
17
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
18
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
19
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
20
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
21
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
22
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# ruby2_keywords
|
2
|
+
|
3
|
+
Provides empty `Module#ruby2_keywords` method, for the forward
|
4
|
+
source-level compatibility against ruby2.7 and ruby3.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'ruby2_keywords'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install ruby2_keywords
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
For class/module instance methods:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
require 'ruby2_keywords'
|
28
|
+
|
29
|
+
module YourModule
|
30
|
+
ruby2_keywords def delegating_method(*args)
|
31
|
+
other_method(*args)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
```
|
35
|
+
|
36
|
+
For global methods:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
require 'ruby2_keywords'
|
40
|
+
|
41
|
+
ruby2_keywords def oldstyle_keywords(options = {})
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
You can do the same for a method defined by `Module#define_method`:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
define_method :delegating_method do |*args, &block|
|
49
|
+
other_method(*args, &block)
|
50
|
+
end
|
51
|
+
ruby2_keywords :delegating_method
|
52
|
+
```
|
53
|
+
|
54
|
+
## Contributing
|
55
|
+
|
56
|
+
Bug reports and pull requests are welcome on [GitHub] or
|
57
|
+
[Ruby Issue Tracking System].
|
58
|
+
|
59
|
+
## License
|
60
|
+
|
61
|
+
The gem is available as open source under the terms of the
|
62
|
+
[Ruby License] or the [2-Clause BSD License].
|
63
|
+
|
64
|
+
[GitHub]: https://github.com/ruby/ruby2_keywords/
|
65
|
+
[Ruby Issue Tracking System]: https://bugs.ruby-lang.org
|
66
|
+
[Ruby License]: https://www.ruby-lang.org/en/about/license.txt
|
67
|
+
[2-Clause BSD License]: https://opensource.org/licenses/BSD-2-Clause
|