shopify-cli 2.7.1 → 2.8.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 +43 -0
- data/Codespace.dockerfile +2 -2
- data/Gemfile.lock +4 -4
- 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 +30 -12
- 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 -15
- data/lib/project_types/extension/commands/create.rb +3 -6
- 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 -2
- data/lib/project_types/extension/models/app.rb +1 -1
- data/lib/project_types/extension/models/development_server.rb +2 -2
- 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 +2 -4
- 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 +1 -3
- data/lib/project_types/script/commands/javy.rb +0 -2
- data/lib/project_types/script/commands/push.rb +2 -1
- data/lib/project_types/script/config/extension_points.yml +10 -28
- data/lib/project_types/script/forms/ask_app.rb +32 -0
- data/lib/project_types/script/forms/ask_org.rb +30 -0
- data/lib/project_types/script/forms/ask_script_uuid.rb +22 -0
- data/lib/project_types/script/forms/run_against_shopify_org.rb +14 -0
- data/lib/project_types/script/graphql/app_script_set.graphql +2 -2
- data/lib/project_types/script/layers/application/build_script.rb +0 -1
- data/lib/project_types/script/layers/application/connect_app.rb +79 -0
- data/lib/project_types/script/layers/application/create_script.rb +17 -17
- data/lib/project_types/script/layers/application/push_script.rb +1 -1
- data/lib/project_types/script/layers/domain/errors.rb +1 -4
- 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 +5 -1
- data/lib/project_types/script/layers/infrastructure/errors.rb +28 -6
- data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_task_runner.rb +0 -4
- data/lib/project_types/script/layers/infrastructure/languages/typescript_task_runner.rb +0 -4
- 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/messages/messages.rb +20 -5
- data/lib/project_types/script/ui/error_handler.rb +30 -20
- data/lib/project_types/theme/commands/pull.rb +3 -0
- data/lib/project_types/theme/commands/push.rb +7 -1
- data/lib/project_types/theme/commands/serve.rb +1 -1
- data/lib/project_types/theme/messages/messages.rb +10 -0
- data/lib/project_types/theme/ui/sync_progress_bar.rb +2 -2
- data/lib/shopify_cli/command/project_command.rb +20 -7
- data/lib/shopify_cli/command.rb +6 -0
- data/lib/shopify_cli/commands/app/create/node.rb +1 -3
- data/lib/shopify_cli/commands/app/create/rails.rb +1 -3
- data/lib/shopify_cli/commands/login.rb +1 -1
- data/lib/shopify_cli/commands/switch.rb +1 -1
- data/lib/shopify_cli/constants.rb +7 -0
- data/lib/shopify_cli/context.rb +11 -1
- data/lib/shopify_cli/environment.rb +4 -0
- data/lib/shopify_cli/form.rb +2 -0
- data/lib/shopify_cli/git.rb +2 -0
- data/lib/shopify_cli/identity_auth.rb +18 -0
- data/lib/shopify_cli/messages/messages.rb +8 -1
- 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 +2 -8
- data/lib/shopify_cli/project.rb +8 -7
- data/lib/shopify_cli/resources/env_file.rb +13 -5
- data/lib/shopify_cli/services/app/create/rails_service.rb +1 -1
- data/lib/shopify_cli/services/app/serve/node_service.rb +1 -1
- data/lib/shopify_cli/services/app/serve/rails_service.rb +1 -1
- data/lib/shopify_cli/tasks/ensure_authenticated.rb +9 -3
- data/lib/shopify_cli/theme/dev_server/cdn_fonts.rb +73 -0
- data/lib/shopify_cli/theme/dev_server/hot-reload.js +38 -9
- data/lib/shopify_cli/theme/dev_server/proxy/template_param_builder.rb +84 -0
- data/lib/shopify_cli/theme/dev_server/proxy.rb +9 -15
- data/lib/shopify_cli/theme/dev_server.rb +6 -4
- data/lib/shopify_cli/theme/syncer/error_reporter.rb +45 -0
- data/lib/shopify_cli/theme/syncer/operation.rb +56 -0
- data/lib/shopify_cli/theme/syncer/standard_reporter.rb +32 -0
- data/lib/shopify_cli/theme/syncer.rb +40 -39
- data/lib/shopify_cli/theme/theme.rb +31 -19
- 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 +9 -10
- data/lib/shopify_cli/version.rb +1 -1
- data/shopify-cli.gemspec +1 -1
- data/vendor/deps/cli-kit/lib/cli/kit/error_handler.rb +3 -1
- metadata +31 -8
- data/lib/graphql/all_orgs_with_extensions.graphql +0 -37
- data/lib/project_types/extension/tasks/run_extension_command.rb +0 -82
- data/lib/project_types/script/tasks/ensure_env.rb +0 -106
@@ -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("-l", "--live") { flags[:live] = true }
|
13
14
|
parser.on("-d", "--development") { flags[:development] = true }
|
14
15
|
parser.on("-u", "--unpublished") { flags[:unpublished] = true }
|
15
16
|
parser.on("-j", "--json") { flags[:json] = true }
|
@@ -27,6 +28,8 @@ module Theme
|
|
27
28
|
|
28
29
|
theme = if (theme_id = options.flags[:theme_id])
|
29
30
|
ShopifyCLI::Theme::Theme.new(@ctx, root: root, id: theme_id)
|
31
|
+
elsif options.flags[:live]
|
32
|
+
ShopifyCLI::Theme::Theme.live(@ctx, root: root)
|
30
33
|
elsif options.flags[:development]
|
31
34
|
theme = ShopifyCLI::Theme::DevelopmentTheme.new(@ctx, root: root)
|
32
35
|
theme.ensure_exists!
|
@@ -48,7 +51,9 @@ module Theme
|
|
48
51
|
end
|
49
52
|
|
50
53
|
if theme.live? && !options.flags[:allow_live]
|
51
|
-
|
54
|
+
question = @ctx.message("theme.push.live")
|
55
|
+
question += @ctx.message("theme.push.theme", theme.name, theme.id) if options.flags[:live]
|
56
|
+
return unless CLI::UI::Prompt.confirm(question)
|
52
57
|
end
|
53
58
|
|
54
59
|
ignore_filter = ShopifyCLI::Theme::IgnoreFilter.from_path(root)
|
@@ -72,6 +77,7 @@ module Theme
|
|
72
77
|
@ctx.done(@ctx.message("theme.push.done", theme.preview_url, theme.editor_url))
|
73
78
|
end
|
74
79
|
end
|
80
|
+
raise ShopifyCLI::AbortSilent if syncer.has_any_error?
|
75
81
|
rescue ShopifyCLI::API::APIRequestNotFoundError
|
76
82
|
@ctx.abort(@ctx.message("theme.push.theme_not_found", theme.id))
|
77
83
|
ensure
|
@@ -15,7 +15,7 @@ module Theme
|
|
15
15
|
def call(*)
|
16
16
|
flags = options.flags.dup
|
17
17
|
host = flags[:host] || DEFAULT_HTTP_HOST
|
18
|
-
ShopifyCLI::Theme::DevServer.start(@ctx, ".",
|
18
|
+
ShopifyCLI::Theme::DevServer.start(@ctx, ".", host: host, **flags) do |syncer|
|
19
19
|
UI::SyncProgressBar.new(syncer).progress(:upload_theme!, delay_low_priority_files: true)
|
20
20
|
end
|
21
21
|
rescue ShopifyCLI::Theme::DevServer::AddressBindingError
|
@@ -58,6 +58,7 @@ module Theme
|
|
58
58
|
|
59
59
|
Options:
|
60
60
|
{{command:-i, --themeid=THEMEID}} Theme ID. Must be an existing theme on your store.
|
61
|
+
{{command:-l, --live}} Push to your remote live theme, and update your live store.
|
61
62
|
{{command:-d, --development}} Push to your remote development theme, and create it if needed.
|
62
63
|
{{command:-u, --unpublished}} Create a new unpublished theme and push to it.
|
63
64
|
{{command:-n, --nodelete}} Runs the push command without deleting remote files from Shopify.
|
@@ -73,6 +74,7 @@ module Theme
|
|
73
74
|
push: "Pushing theme files to Shopify",
|
74
75
|
select: "Select theme to push to",
|
75
76
|
live: "Are you sure you want to push to your live theme?",
|
77
|
+
theme: "\n Theme: {{blue:%s #%s}} {{green:[live]}}",
|
76
78
|
theme_not_found: "Theme #%s doesn't exist",
|
77
79
|
done: <<~DONE,
|
78
80
|
{{green:Your theme was pushed successfully}}
|
@@ -99,6 +101,13 @@ module Theme
|
|
99
101
|
viewing_theme: "Viewing theme…",
|
100
102
|
syncing_theme: "Syncing theme #%s on %s",
|
101
103
|
open_fail: "Couldn't open the theme",
|
104
|
+
operation: {
|
105
|
+
status: {
|
106
|
+
error: "ERROR",
|
107
|
+
synced: "Synced",
|
108
|
+
fixed: "Fixed",
|
109
|
+
},
|
110
|
+
},
|
102
111
|
error: {
|
103
112
|
address_binding_error: "Couldn't bind to localhost."\
|
104
113
|
" To serve your theme, set a different address with {{command:%s theme serve --host=<address>}}",
|
@@ -181,6 +190,7 @@ module Theme
|
|
181
190
|
|
182
191
|
Options:
|
183
192
|
{{command:-i, --themeid=THEMEID}} The Theme ID. Must be an existing theme on your store.
|
193
|
+
{{command:-l, --live}} Pull theme files from your remote live theme.
|
184
194
|
{{command:-n, --nodelete}} Runs the pull command without deleting local files.
|
185
195
|
|
186
196
|
Run without options to select theme from a list.
|
@@ -6,14 +6,14 @@ module Theme
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def progress(method, **args)
|
9
|
-
@syncer.
|
9
|
+
@syncer.lock_io!
|
10
10
|
CLI::UI::Progress.progress do |bar|
|
11
11
|
@syncer.public_send(method, **args) do |left, total|
|
12
12
|
bar.tick(set_percent: 1 - left.to_f / total)
|
13
13
|
end
|
14
14
|
bar.tick(set_percent: 1)
|
15
15
|
end
|
16
|
-
@syncer.
|
16
|
+
@syncer.unlock_io!
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
@@ -5,13 +5,26 @@ module ShopifyCLI
|
|
5
5
|
@ctx.puts(self.class.help)
|
6
6
|
end
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
8
|
+
class << self
|
9
|
+
def help
|
10
|
+
project_type = name.split("::")[0].downcase
|
11
|
+
ShopifyCLI::Context.message(
|
12
|
+
"#{project_type}.help",
|
13
|
+
ShopifyCLI::TOOL_NAME,
|
14
|
+
available_subcommands
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def available_subcommands
|
21
|
+
subcommand_registry
|
22
|
+
.resolved_commands
|
23
|
+
.reject { |_name, command| command.hidden? }
|
24
|
+
.keys
|
25
|
+
.sort
|
26
|
+
.join(" | ")
|
27
|
+
end
|
15
28
|
end
|
16
29
|
end
|
17
30
|
end
|
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)
|
@@ -3,9 +3,7 @@ module ShopifyCLI
|
|
3
3
|
class App
|
4
4
|
class Create
|
5
5
|
class Node < ShopifyCLI::Command::AppSubCommand
|
6
|
-
|
7
|
-
prerequisite_task :ensure_authenticated
|
8
|
-
end
|
6
|
+
prerequisite_task :ensure_authenticated
|
9
7
|
|
10
8
|
options do |parser, flags|
|
11
9
|
parser.on("--name=NAME") { |t| flags[:name] = t }
|
@@ -3,9 +3,7 @@ module ShopifyCLI
|
|
3
3
|
class App
|
4
4
|
class Create
|
5
5
|
class Rails < ShopifyCLI::Command::AppSubCommand
|
6
|
-
|
7
|
-
prerequisite_task :ensure_authenticated
|
8
|
-
end
|
6
|
+
prerequisite_task :ensure_authenticated
|
9
7
|
|
10
8
|
options do |parser, flags|
|
11
9
|
parser.on("--name=NAME") { |t| flags[:name] = t }
|
@@ -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 }
|
@@ -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
|
@@ -46,6 +46,9 @@ module ShopifyCLI
|
|
46
46
|
ACCEPTANCE_TEST = "SHOPIFY_CLI_ACCEPTANCE_TEST"
|
47
47
|
DEVELOPMENT = "SHOPIFY_CLI_DEVELOPMENT"
|
48
48
|
|
49
|
+
# Authentication
|
50
|
+
AUTH_TOKEN = "SHOPIFY_CLI_AUTH_TOKEN"
|
51
|
+
|
49
52
|
# Monorail
|
50
53
|
MONORAIL_REAL_EVENTS = "MONORAIL_REAL_EVENTS"
|
51
54
|
end
|
@@ -58,5 +61,9 @@ module ShopifyCLI
|
|
58
61
|
module Links
|
59
62
|
NEW_ISSUE = "https://github.com/Shopify/shopify-cli/issues/new"
|
60
63
|
end
|
64
|
+
|
65
|
+
module Extension
|
66
|
+
DEFAULT_PORT = 39351
|
67
|
+
end
|
61
68
|
end
|
62
69
|
end
|
data/lib/shopify_cli/context.rb
CHANGED
@@ -61,6 +61,7 @@ module ShopifyCLI
|
|
61
61
|
# will return which operating system that the cli is running on [:mac, :linux]
|
62
62
|
def os
|
63
63
|
host = uname
|
64
|
+
return :mac_m1 if /arm64-apple-darwin/i.match(host)
|
64
65
|
return :mac if /darwin/i.match(host)
|
65
66
|
return :windows if /mswin|mingw|cygwin/i.match(host)
|
66
67
|
return :linux if /linux|bsd/i.match(host)
|
@@ -89,7 +90,7 @@ module ShopifyCLI
|
|
89
90
|
|
90
91
|
# will return true if being launched from a tty
|
91
92
|
def tty?
|
92
|
-
$stdin.tty?
|
93
|
+
!testing? && $stdin.tty?
|
93
94
|
end
|
94
95
|
|
95
96
|
# will return true if the cli is being run from an installation, and not a
|
@@ -357,6 +358,15 @@ module ShopifyCLI
|
|
357
358
|
Kernel.puts(CLI::UI.fmt(*args))
|
358
359
|
end
|
359
360
|
|
361
|
+
# a wrapper around $stderr.puts to allow for easy formatting
|
362
|
+
#
|
363
|
+
# #### Parameters
|
364
|
+
# * `text` - a string message to output
|
365
|
+
#
|
366
|
+
def error(text)
|
367
|
+
$stderr.puts(CLI::UI.fmt(text))
|
368
|
+
end
|
369
|
+
|
360
370
|
# a wrapper around Kernel.warn to allow for easy formatting
|
361
371
|
#
|
362
372
|
# #### Parameters
|
@@ -86,6 +86,10 @@ module ShopifyCLI
|
|
86
86
|
)
|
87
87
|
end
|
88
88
|
|
89
|
+
def self.auth_token(env_variables: ENV)
|
90
|
+
env_variables[Constants::EnvironmentVariables::AUTH_TOKEN]
|
91
|
+
end
|
92
|
+
|
89
93
|
def self.env_variable_truthy?(variable_name, env_variables: ENV)
|
90
94
|
TRUTHY_ENV_VARIABLE_VALUES.include?(env_variables[variable_name.to_s])
|
91
95
|
end
|
data/lib/shopify_cli/form.rb
CHANGED
data/lib/shopify_cli/git.rb
CHANGED
@@ -68,6 +68,24 @@ module ShopifyCLI
|
|
68
68
|
request_exchange_tokens
|
69
69
|
end
|
70
70
|
|
71
|
+
def self.fetch_or_auth_partners_token(ctx:)
|
72
|
+
env_var_auth_token = Environment.auth_token
|
73
|
+
return env_var_auth_token if env_var_auth_token
|
74
|
+
|
75
|
+
ShopifyCLI::DB.get(:partners_exchange_token) do
|
76
|
+
IdentityAuth.new(ctx: ctx).authenticate
|
77
|
+
ShopifyCLI::DB.get(:partners_exchange_token)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.environment_auth_token?
|
82
|
+
!!Environment.auth_token
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.authenticated?
|
86
|
+
environment_auth_token? || IDENTITY_ACCESS_TOKENS.all? { |key| ShopifyCLI::DB.exists?(key) }
|
87
|
+
end
|
88
|
+
|
71
89
|
def reauthenticate
|
72
90
|
return if refresh_exchange_tokens || refresh_access_tokens
|
73
91
|
ctx.abort(ctx.message("core.identity_auth.error.reauthenticate", ShopifyCLI::TOOL_NAME))
|
@@ -14,6 +14,12 @@ module ShopifyCLI
|
|
14
14
|
},
|
15
15
|
},
|
16
16
|
core: {
|
17
|
+
errors: {
|
18
|
+
option_parser: {
|
19
|
+
invalid_option: "The option {{command:%s}} is not supported.",
|
20
|
+
missing_argument: "The required argument {{command:%s}} is missing.",
|
21
|
+
},
|
22
|
+
},
|
17
23
|
app: {
|
18
24
|
help: <<~HELP,
|
19
25
|
Suite of commands for developing apps. See {{command:%1$s app <command> --help}} for usage of each command.
|
@@ -457,6 +463,7 @@ module ShopifyCLI
|
|
457
463
|
not_authenticated: "Failed to authenticate",
|
458
464
|
},
|
459
465
|
login_prompt: "Please ensure you've logged in with {{command:%s login}} and try again",
|
466
|
+
token_authentication: "%s environment variable. We'll authenticate using its value as a token.",
|
460
467
|
},
|
461
468
|
|
462
469
|
options: {
|
@@ -718,7 +725,7 @@ module ShopifyCLI
|
|
718
725
|
signup_suggestion: <<~MESSAGE,
|
719
726
|
{{*}} To avoid tunnels that timeout, it is recommended to signup for a free ngrok
|
720
727
|
account at {{underline:https://ngrok.com/signup}}. After you signup, install your
|
721
|
-
personalized authorization token using {{command:%s
|
728
|
+
personalized authorization token using {{command:%s app tunnel auth <token>}}.
|
722
729
|
MESSAGE
|
723
730
|
start: "{{v}} ngrok tunnel running at {{underline:%s}}",
|
724
731
|
start_with_account: "{{v}} ngrok tunnel running at {{underline:%s}}, with account %s",
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "shopify_cli/thread_pool/job"
|
4
|
+
|
5
|
+
module ShopifyCLI
|
6
|
+
class PartnersAPI
|
7
|
+
class AppExtensions
|
8
|
+
class Job < ShopifyCLI::ThreadPool::Job
|
9
|
+
attr_reader :result
|
10
|
+
|
11
|
+
def initialize(ctx, app, type)
|
12
|
+
super()
|
13
|
+
@ctx = ctx
|
14
|
+
@app = app
|
15
|
+
@api_key = @app["apiKey"]
|
16
|
+
@type = type
|
17
|
+
end
|
18
|
+
|
19
|
+
def perform!
|
20
|
+
resp = PartnersAPI.query(@ctx, "get_extension_registrations", **params)
|
21
|
+
@result = resp&.dig("data", "app") || {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def patch_app_with_extensions!
|
25
|
+
@app.merge!(result)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def params
|
31
|
+
{ api_key: @api_key, type: @type }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "shopify_cli/thread_pool"
|
4
|
+
|
5
|
+
require_relative "app_extensions/job"
|
6
|
+
|
7
|
+
module ShopifyCLI
|
8
|
+
class PartnersAPI
|
9
|
+
class AppExtensions
|
10
|
+
class << self
|
11
|
+
def fetch_apps_extensions(ctx, orgs, type)
|
12
|
+
jobs = apps(orgs).map { |app| AppExtensions::Job.new(ctx, app, type) }
|
13
|
+
|
14
|
+
consume_jobs!(jobs)
|
15
|
+
patch_apps_with_extensions!(jobs)
|
16
|
+
|
17
|
+
orgs
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def apps(orgs)
|
23
|
+
orgs.flat_map { |org| org["apps"] }
|
24
|
+
end
|
25
|
+
|
26
|
+
def consume_jobs!(jobs)
|
27
|
+
thread_pool = ShopifyCLI::ThreadPool.new
|
28
|
+
jobs.each do |job|
|
29
|
+
thread_pool.schedule(job)
|
30
|
+
end
|
31
|
+
thread_pool.shutdown
|
32
|
+
|
33
|
+
raise_if_any_error(jobs)
|
34
|
+
end
|
35
|
+
|
36
|
+
def patch_apps_with_extensions!(jobs)
|
37
|
+
jobs.each(&:patch_app_with_extensions!)
|
38
|
+
end
|
39
|
+
|
40
|
+
def raise_if_any_error(jobs)
|
41
|
+
jobs.find(&:error?).tap { |job| raise job.error if job }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -28,11 +28,8 @@ module ShopifyCLI
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def fetch_with_extensions(ctx, type)
|
31
|
-
|
32
|
-
(
|
33
|
-
org["apps"] = (org.dig("apps", "nodes") || [])
|
34
|
-
org
|
35
|
-
end
|
31
|
+
orgs = fetch_with_app(ctx)
|
32
|
+
AppExtensions.fetch_apps_extensions(ctx, orgs, type)
|
36
33
|
end
|
37
34
|
end
|
38
35
|
end
|
@@ -7,6 +7,7 @@ module ShopifyCLI
|
|
7
7
|
#
|
8
8
|
class PartnersAPI < API
|
9
9
|
autoload :Organizations, "shopify_cli/partners_api/organizations"
|
10
|
+
autoload :AppExtensions, "shopify_cli/partners_api/app_extensions"
|
10
11
|
|
11
12
|
class << self
|
12
13
|
##
|
@@ -61,18 +62,11 @@ module ShopifyCLI
|
|
61
62
|
def api_client(ctx)
|
62
63
|
new(
|
63
64
|
ctx: ctx,
|
64
|
-
token:
|
65
|
+
token: IdentityAuth.fetch_or_auth_partners_token(ctx: ctx),
|
65
66
|
url: "https://#{Environment.partners_domain}/api/cli/graphql",
|
66
67
|
)
|
67
68
|
end
|
68
69
|
|
69
|
-
def access_token(ctx)
|
70
|
-
ShopifyCLI::DB.get(:partners_exchange_token) do
|
71
|
-
IdentityAuth.new(ctx: ctx).authenticate
|
72
|
-
ShopifyCLI::DB.get(:partners_exchange_token)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
70
|
def auth_failure_info(ctx, error)
|
77
71
|
if error.response
|
78
72
|
headers = %w(www-authenticate x-request-id)
|
data/lib/shopify_cli/project.rb
CHANGED
@@ -107,13 +107,6 @@ module ShopifyCLI
|
|
107
107
|
@dir = nil
|
108
108
|
end
|
109
109
|
|
110
|
-
private
|
111
|
-
|
112
|
-
def directory(dir)
|
113
|
-
@dir ||= Hash.new { |h, k| h[k] = __directory(k) }
|
114
|
-
@dir[dir]
|
115
|
-
end
|
116
|
-
|
117
110
|
def at(dir)
|
118
111
|
proj_dir = directory(dir)
|
119
112
|
unless proj_dir
|
@@ -123,6 +116,13 @@ module ShopifyCLI
|
|
123
116
|
@at[proj_dir]
|
124
117
|
end
|
125
118
|
|
119
|
+
private
|
120
|
+
|
121
|
+
def directory(dir)
|
122
|
+
@dir ||= Hash.new { |h, k| h[k] = __directory(k) }
|
123
|
+
@dir[dir]
|
124
|
+
end
|
125
|
+
|
126
126
|
def __directory(curr)
|
127
127
|
loop do
|
128
128
|
return nil if curr == "/" || /^[A-Z]:\/$/.match?(curr)
|
@@ -134,6 +134,7 @@ module ShopifyCLI
|
|
134
134
|
end
|
135
135
|
|
136
136
|
property :directory # :nodoc:
|
137
|
+
property :env # :nodoc:
|
137
138
|
|
138
139
|
##
|
139
140
|
# will read, parse and return the envfile for the project
|
@@ -14,11 +14,15 @@ module ShopifyCLI
|
|
14
14
|
}
|
15
15
|
|
16
16
|
class << self
|
17
|
-
def read(_directory = Dir.pwd)
|
18
|
-
input = parse_external_env
|
17
|
+
def read(_directory = Dir.pwd, overrides: {})
|
18
|
+
input = parse_external_env(overrides: overrides)
|
19
19
|
new(input)
|
20
20
|
end
|
21
21
|
|
22
|
+
def from_hash(hash)
|
23
|
+
new(env_input(hash))
|
24
|
+
end
|
25
|
+
|
22
26
|
def parse(directory)
|
23
27
|
File.read(File.join(directory, FILENAME))
|
24
28
|
.gsub("\r\n", "\n").split("\n").each_with_object({}) do |line, output|
|
@@ -37,10 +41,14 @@ module ShopifyCLI
|
|
37
41
|
end
|
38
42
|
end
|
39
43
|
|
40
|
-
def parse_external_env(directory = Dir.pwd)
|
44
|
+
def parse_external_env(directory = Dir.pwd, overrides: {})
|
45
|
+
env_input(parse(directory), overrides: overrides)
|
46
|
+
end
|
47
|
+
|
48
|
+
def env_input(parsed_source, overrides: {})
|
41
49
|
env_details = {}
|
42
50
|
extra = {}
|
43
|
-
|
51
|
+
parsed_source.merge(overrides).each do |key, value|
|
44
52
|
if KEY_MAP[key]
|
45
53
|
env_details[KEY_MAP[key]] = value
|
46
54
|
else
|
@@ -53,7 +61,7 @@ module ShopifyCLI
|
|
53
61
|
end
|
54
62
|
|
55
63
|
property :api_key, required: true
|
56
|
-
property :secret
|
64
|
+
property :secret
|
57
65
|
property :shop
|
58
66
|
property :scopes
|
59
67
|
property :host
|
@@ -159,10 +159,10 @@ module ShopifyCLI
|
|
159
159
|
|
160
160
|
CLI::UI::Frame.open(context.message("core.app.create.rails.generating_app", name)) do
|
161
161
|
new_command = %w(rails new)
|
162
|
+
new_command << name
|
162
163
|
new_command += DEFAULT_RAILS_FLAGS
|
163
164
|
new_command << "--database=#{db}"
|
164
165
|
new_command += rails_opts.split unless rails_opts.nil?
|
165
|
-
new_command << name
|
166
166
|
|
167
167
|
syscall(new_command)
|
168
168
|
end
|
@@ -14,7 +14,7 @@ module ShopifyCLI
|
|
14
14
|
|
15
15
|
def call
|
16
16
|
project = ShopifyCLI::Project.current
|
17
|
-
url = host || ShopifyCLI::Tunnel.start(context)
|
17
|
+
url = host || ShopifyCLI::Tunnel.start(context, port: port)
|
18
18
|
raise ShopifyCLI::Abort,
|
19
19
|
context.message("core.app.serve.error.host_must_be_https") if url.match(/^https/i).nil?
|
20
20
|
project.env.update(context, :host, url)
|
@@ -14,7 +14,7 @@ module ShopifyCLI
|
|
14
14
|
|
15
15
|
def call
|
16
16
|
project = ShopifyCLI::Project.current
|
17
|
-
url = host || ShopifyCLI::Tunnel.start(context)
|
17
|
+
url = host || ShopifyCLI::Tunnel.start(context, port: port)
|
18
18
|
raise ShopifyCLI::Abort,
|
19
19
|
context.message("core.app.serve.error.host_must_be_https") if url.match(/^https/i).nil?
|
20
20
|
project.env.update(context, :host, url)
|
@@ -4,9 +4,15 @@ module ShopifyCLI
|
|
4
4
|
module Tasks
|
5
5
|
class EnsureAuthenticated < ShopifyCLI::Task
|
6
6
|
def call(ctx)
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
return if ShopifyCLI::Environment.acceptance_test?
|
8
|
+
unless ShopifyCLI::IdentityAuth.authenticated?
|
9
|
+
raise ShopifyCLI::Abort,
|
10
|
+
ctx.message("core.identity_auth.login_prompt", ShopifyCLI::TOOL_NAME)
|
11
|
+
end
|
12
|
+
if ShopifyCLI::IdentityAuth.environment_auth_token?
|
13
|
+
ctx.puts(ctx.message("core.identity_auth.token_authentication",
|
14
|
+
ShopifyCLI::Constants::EnvironmentVariables::AUTH_TOKEN))
|
15
|
+
end
|
10
16
|
end
|
11
17
|
end
|
12
18
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ShopifyCLI
|
4
|
+
module Theme
|
5
|
+
module DevServer
|
6
|
+
class CdnFonts
|
7
|
+
FONTS_PATH = "/fonts"
|
8
|
+
FONTS_CDN = "https://fonts.shopifycdn.com"
|
9
|
+
FONTS_REGEX = %r{#{FONTS_CDN}}
|
10
|
+
|
11
|
+
def initialize(app, theme:)
|
12
|
+
@app = app
|
13
|
+
@theme = theme
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(env)
|
17
|
+
path = env["PATH_INFO"]
|
18
|
+
|
19
|
+
# Serve from fonts CDN
|
20
|
+
return serve_font(env) if path.start_with?(FONTS_PATH)
|
21
|
+
|
22
|
+
# Proxy the request, and replace the URLs in the response
|
23
|
+
status, headers, body = @app.call(env)
|
24
|
+
body = replace_font_urls(body)
|
25
|
+
[status, headers, body]
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def serve_font(env)
|
31
|
+
parameters = %w(PATH_INFO QUERY_STRING REQUEST_METHOD rack.input)
|
32
|
+
path, query, method, body_stream = *env.slice(*parameters).values
|
33
|
+
|
34
|
+
uri = fonts_cdn_uri(path, query)
|
35
|
+
|
36
|
+
response = Net::HTTP.start(uri.host, 443, use_ssl: true) do |http|
|
37
|
+
req_class = Net::HTTP.const_get(method.capitalize)
|
38
|
+
req = req_class.new(uri)
|
39
|
+
req.initialize_http_header(fonts_cdn_headers)
|
40
|
+
req.body_stream = body_stream
|
41
|
+
http.request(req)
|
42
|
+
end
|
43
|
+
|
44
|
+
[
|
45
|
+
response.code.to_s,
|
46
|
+
{
|
47
|
+
"Content-Type" => response.content_type,
|
48
|
+
"Content-Length" => response.content_length.to_s,
|
49
|
+
},
|
50
|
+
[response.body],
|
51
|
+
]
|
52
|
+
end
|
53
|
+
|
54
|
+
def fonts_cdn_headers
|
55
|
+
{
|
56
|
+
"Referer" => "https://#{@theme.shop}",
|
57
|
+
"Transfer-Encoding" => "chunked",
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def fonts_cdn_uri(path, query)
|
62
|
+
uri = URI.join("#{FONTS_CDN}/", path.gsub(%r{^#{FONTS_PATH}\/}, ""))
|
63
|
+
uri.query = query.split("&").last
|
64
|
+
uri
|
65
|
+
end
|
66
|
+
|
67
|
+
def replace_font_urls(body)
|
68
|
+
[body.join.gsub(FONTS_REGEX, FONTS_PATH)]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|