shopify-cli 2.7.1 → 2.8.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 +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
|