shopify-cli 2.9.0 → 2.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE/bug_report.yaml +117 -0
- data/.github/ISSUE_TEMPLATE/enhancement.yaml +38 -0
- data/.github/ISSUE_TEMPLATE/feature.yaml +47 -0
- data/.github/ISSUE_TEMPLATE.md +18 -0
- data/CHANGELOG.md +38 -5
- data/Gemfile.lock +1 -1
- data/dev.yml +3 -0
- data/lib/project_types/extension/commands/build.rb +3 -0
- data/lib/project_types/extension/commands/check.rb +3 -0
- data/lib/project_types/extension/commands/create.rb +3 -0
- data/lib/project_types/extension/commands/push.rb +3 -0
- data/lib/project_types/extension/commands/serve.rb +3 -0
- data/lib/project_types/extension/models/specification_handlers/default.rb +1 -1
- data/lib/project_types/extension/tasks/convert_server_config.rb +3 -1
- data/lib/project_types/script/cli.rb +5 -0
- data/lib/project_types/script/commands/connect.rb +3 -1
- data/lib/project_types/script/commands/create.rb +2 -0
- data/lib/project_types/script/commands/push.rb +6 -0
- data/lib/project_types/script/config/extension_points.yml +12 -0
- data/lib/project_types/script/graphql/app_script_set.graphql +2 -0
- data/lib/project_types/script/graphql/module_upload_url_generate.graphql +5 -1
- data/lib/project_types/script/layers/application/build_script.rb +6 -3
- data/lib/project_types/script/layers/application/project_dependencies.rb +1 -1
- data/lib/project_types/script/layers/application/push_script.rb +38 -30
- data/lib/project_types/script/layers/domain/errors.rb +10 -3
- data/lib/project_types/script/layers/domain/extension_point.rb +2 -2
- data/lib/project_types/script/layers/domain/push_package.rb +0 -3
- data/lib/project_types/script/layers/domain/script_config.rb +6 -4
- data/lib/project_types/script/layers/domain/script_project.rb +1 -0
- data/lib/project_types/script/layers/infrastructure/errors.rb +47 -24
- data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_task_runner.rb +2 -12
- data/lib/project_types/script/layers/infrastructure/languages/project_creator.rb +1 -0
- data/lib/project_types/script/layers/infrastructure/languages/task_runner.rb +1 -0
- data/lib/project_types/script/layers/infrastructure/languages/typescript_task_runner.rb +2 -12
- data/lib/project_types/script/layers/infrastructure/languages/wasm_project_creator.rb +15 -0
- data/lib/project_types/script/layers/infrastructure/languages/wasm_task_runner.rb +36 -0
- data/lib/project_types/script/layers/infrastructure/metadata_repository.rb +18 -0
- data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +7 -8
- data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +45 -54
- data/lib/project_types/script/layers/infrastructure/script_service.rb +35 -12
- data/lib/project_types/script/layers/infrastructure/script_uploader.rb +22 -9
- data/lib/project_types/script/loaders/project.rb +2 -1
- data/lib/project_types/script/messages/messages.rb +94 -88
- data/lib/project_types/script/ui/error_handler.rb +75 -38
- data/lib/project_types/theme/commands/check.rb +3 -0
- data/lib/project_types/theme/commands/delete.rb +3 -0
- data/lib/project_types/theme/commands/init.rb +3 -0
- data/lib/project_types/theme/commands/language_server.rb +3 -0
- data/lib/project_types/theme/commands/package.rb +3 -0
- data/lib/project_types/theme/commands/publish.rb +3 -0
- data/lib/project_types/theme/commands/pull.rb +12 -1
- data/lib/project_types/theme/commands/push.rb +12 -1
- data/lib/project_types/theme/commands/serve.rb +3 -0
- data/lib/project_types/theme/messages/messages.rb +4 -0
- data/lib/shopify_cli/command/sub_command.rb +2 -0
- data/lib/shopify_cli/command.rb +66 -0
- data/lib/shopify_cli/commands/app/create/node.rb +3 -0
- data/lib/shopify_cli/commands/app/create/rails.rb +3 -0
- data/lib/shopify_cli/commands/app/create.rb +3 -0
- data/lib/shopify_cli/commands/app/deploy.rb +3 -0
- data/lib/shopify_cli/commands/app/serve.rb +3 -0
- data/lib/shopify_cli/commands/login.rb +4 -10
- data/lib/shopify_cli/constants.rb +18 -2
- data/lib/shopify_cli/core/executor.rb +4 -4
- data/lib/shopify_cli/environment.rb +61 -16
- data/lib/shopify_cli/exception_reporter.rb +9 -0
- data/lib/shopify_cli/github/issue_url_generator.rb +19 -8
- data/lib/shopify_cli/identity_auth/env_auth_token.rb +34 -0
- data/lib/shopify_cli/identity_auth.rb +36 -18
- data/lib/shopify_cli/messages/messages.rb +2 -2
- data/lib/shopify_cli/method_object.rb +21 -9
- data/lib/shopify_cli/partners_api.rb +7 -2
- data/lib/shopify_cli/result.rb +61 -59
- data/lib/shopify_cli/services/app/create/rails_service.rb +37 -13
- data/lib/shopify_cli/task.rb +5 -3
- 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 +65 -0
- data/lib/shopify_cli/theme/syncer.rb +17 -2
- data/lib/shopify_cli/utilities.rb +7 -0
- data/lib/shopify_cli/version.rb +1 -1
- data/lib/shopify_cli.rb +3 -1
- data/vendor/deps/cli-kit/lib/cli/kit/system.rb +11 -6
- data/vendor/deps/cli-kit/lib/cli/kit/util.rb +5 -1
- data/vendor/deps/cli-ui/lib/cli/ui/os.rb +6 -4
- 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
- data/vendor/lib/semantic/version.rb +0 -1
- metadata +18 -3
- data/lib/project_types/rails/commands/create.rb +0 -210
@@ -2,11 +2,15 @@
|
|
2
2
|
require "shopify_cli/theme/theme"
|
3
3
|
require "shopify_cli/theme/development_theme"
|
4
4
|
require "shopify_cli/theme/ignore_filter"
|
5
|
+
require "shopify_cli/theme/include_filter"
|
5
6
|
require "shopify_cli/theme/syncer"
|
6
7
|
|
7
8
|
module Theme
|
8
9
|
class Command
|
9
10
|
class Push < ShopifyCLI::Command::SubCommand
|
11
|
+
recommend_default_node_range
|
12
|
+
recommend_default_ruby_range
|
13
|
+
|
10
14
|
options do |parser, flags|
|
11
15
|
parser.on("-n", "--nodelete") { flags[:nodelete] = true }
|
12
16
|
parser.on("-i", "--themeid=ID") { |theme_id| flags[:theme_id] = theme_id }
|
@@ -17,6 +21,10 @@ module Theme
|
|
17
21
|
parser.on("-j", "--json") { flags[:json] = true }
|
18
22
|
parser.on("-a", "--allow-live") { flags[:allow_live] = true }
|
19
23
|
parser.on("-p", "--publish") { flags[:publish] = true }
|
24
|
+
parser.on("-o", "--only=PATTERN") do |pattern|
|
25
|
+
flags[:includes] ||= []
|
26
|
+
flags[:includes] << pattern
|
27
|
+
end
|
20
28
|
parser.on("-x", "--ignore=PATTERN") do |pattern|
|
21
29
|
flags[:ignores] ||= []
|
22
30
|
flags[:ignores] << pattern
|
@@ -35,10 +43,13 @@ module Theme
|
|
35
43
|
return unless CLI::UI::Prompt.confirm(question)
|
36
44
|
end
|
37
45
|
|
46
|
+
include_filter = ShopifyCLI::Theme::IncludeFilter.new(options.flags[:includes])
|
38
47
|
ignore_filter = ShopifyCLI::Theme::IgnoreFilter.from_path(root)
|
39
48
|
ignore_filter.add_patterns(options.flags[:ignores]) if options.flags[:ignores]
|
40
49
|
|
41
|
-
syncer = ShopifyCLI::Theme::Syncer.new(@ctx, theme: theme,
|
50
|
+
syncer = ShopifyCLI::Theme::Syncer.new(@ctx, theme: theme,
|
51
|
+
include_filter: include_filter,
|
52
|
+
ignore_filter: ignore_filter)
|
42
53
|
begin
|
43
54
|
syncer.start_threads
|
44
55
|
if options.flags[:json]
|
@@ -65,6 +65,8 @@ module Theme
|
|
65
65
|
{{command:-j, --json}} Output JSON instead of a UI.
|
66
66
|
{{command:-a, --allow-live}} Allow push to a live theme.
|
67
67
|
{{command:-p, --publish}} Publish as the live theme after uploading.
|
68
|
+
{{command:-o, --only}} Upload only the specified files.
|
69
|
+
{{command:-x, --ignore}} Skip uploading the specified files.
|
68
70
|
|
69
71
|
Run without options to select theme from a list.
|
70
72
|
HELP
|
@@ -200,6 +202,8 @@ module Theme
|
|
200
202
|
{{command:-l, --live}} Pull theme files from your remote live theme.
|
201
203
|
{{command:-d, --development}} Pull theme files from your remote development theme.
|
202
204
|
{{command:-n, --nodelete}} Runs the pull command without deleting local files.
|
205
|
+
{{command:-o, --only}} Download only the specified files.
|
206
|
+
{{command:-x, --ignore}} Skip downloading the specified files.
|
203
207
|
|
204
208
|
Run without options to select theme from a list.
|
205
209
|
HELP
|
data/lib/shopify_cli/command.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require "shopify_cli"
|
3
|
+
require "semantic/semantic"
|
3
4
|
|
4
5
|
module ShopifyCLI
|
5
6
|
class Command < CLI::Kit::BaseCommand
|
@@ -7,6 +8,8 @@ module ShopifyCLI
|
|
7
8
|
autoload :AppSubCommand, "shopify_cli/command/app_sub_command"
|
8
9
|
autoload :ProjectCommand, "shopify_cli/command/project_command"
|
9
10
|
|
11
|
+
VersionRange = Struct.new(:from, :to, keyword_init: true)
|
12
|
+
|
10
13
|
extend Feature::Set
|
11
14
|
|
12
15
|
attr_writer :ctx
|
@@ -26,6 +29,8 @@ module ShopifyCLI
|
|
26
29
|
cmd = new(@ctx)
|
27
30
|
cmd.options.parse(@_options, args)
|
28
31
|
return call_help(command_name) if cmd.options.help
|
32
|
+
check_ruby_version
|
33
|
+
check_node_version
|
29
34
|
run_prerequisites
|
30
35
|
cmd.call(args, command_name)
|
31
36
|
end
|
@@ -58,6 +63,67 @@ module ShopifyCLI
|
|
58
63
|
)
|
59
64
|
end
|
60
65
|
|
66
|
+
def recommend_ruby(from:, to:)
|
67
|
+
@compatible_ruby_range = VersionRange.new(
|
68
|
+
from: Semantic::Version.new(from),
|
69
|
+
to: Semantic::Version.new(to)
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
def recommend_default_ruby_range
|
74
|
+
recommend_ruby(
|
75
|
+
from: Constants::SupportedVersions::Ruby::FROM,
|
76
|
+
to: Constants::SupportedVersions::Ruby::TO
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
def check_ruby_version
|
81
|
+
check_version(
|
82
|
+
Environment.ruby_version,
|
83
|
+
range: @compatible_ruby_range,
|
84
|
+
runtime: "Ruby"
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
def recommend_node(from:, to:)
|
89
|
+
@compatible_node_range = VersionRange.new(
|
90
|
+
from: Semantic::Version.new(from),
|
91
|
+
to: Semantic::Version.new(to)
|
92
|
+
)
|
93
|
+
end
|
94
|
+
|
95
|
+
def recommend_default_node_range
|
96
|
+
recommend_node(
|
97
|
+
from: Constants::SupportedVersions::Node::FROM,
|
98
|
+
to: Constants::SupportedVersions::Node::TO
|
99
|
+
)
|
100
|
+
end
|
101
|
+
|
102
|
+
def check_node_version
|
103
|
+
check_version(
|
104
|
+
Environment.node_version,
|
105
|
+
range: @compatible_node_range,
|
106
|
+
runtime: "Node"
|
107
|
+
)
|
108
|
+
end
|
109
|
+
|
110
|
+
def check_version(version, range:, runtime:)
|
111
|
+
return if Environment.test?
|
112
|
+
return if range.nil?
|
113
|
+
|
114
|
+
version_without_pre_nor_build = Utilities.version_dropping_pre_and_build(version)
|
115
|
+
is_higher_than_bottom = version_without_pre_nor_build >= Utilities.version_dropping_pre_and_build(range.from)
|
116
|
+
is_lower_than_top = version_without_pre_nor_build < Utilities.version_dropping_pre_and_build(range.to)
|
117
|
+
return if is_higher_than_bottom && is_lower_than_top
|
118
|
+
|
119
|
+
Context.new.warn("Your environment #{runtime} version, #{version},"\
|
120
|
+
" is outside of the range supported by the CLI,"\
|
121
|
+
" #{range.from}..<#{range.to},"\
|
122
|
+
" and might cause incompatibility issues.")
|
123
|
+
rescue StandardError => error
|
124
|
+
ExceptionReporter.report_error_silently(error)
|
125
|
+
end
|
126
|
+
|
61
127
|
def prerequisite_task(*tasks_without_args, **tasks_with_args)
|
62
128
|
@prerequisite_tasks ||= []
|
63
129
|
@prerequisite_tasks += tasks_without_args.map { |t| PrerequisiteTask.new(t) }
|
@@ -5,6 +5,9 @@ module ShopifyCLI
|
|
5
5
|
class Node < ShopifyCLI::Command::AppSubCommand
|
6
6
|
prerequisite_task :ensure_authenticated
|
7
7
|
|
8
|
+
recommend_default_node_range
|
9
|
+
recommend_default_ruby_range
|
10
|
+
|
8
11
|
options do |parser, flags|
|
9
12
|
parser.on("--name=NAME") { |t| flags[:name] = t }
|
10
13
|
parser.on("--organization-id=ID") { |id| flags[:organization_id] = id }
|
@@ -5,6 +5,9 @@ module ShopifyCLI
|
|
5
5
|
class Rails < ShopifyCLI::Command::AppSubCommand
|
6
6
|
prerequisite_task :ensure_authenticated
|
7
7
|
|
8
|
+
recommend_default_node_range
|
9
|
+
recommend_default_ruby_range
|
10
|
+
|
8
11
|
options do |parser, flags|
|
9
12
|
parser.on("--name=NAME") { |t| flags[:name] = t }
|
10
13
|
parser.on("--organization-id=ID") { |id| flags[:organization_id] = id }
|
@@ -6,6 +6,9 @@ module ShopifyCLI
|
|
6
6
|
subcommand :PHP, "php", "shopify_cli/commands/app/create/php"
|
7
7
|
subcommand :Node, "node", "shopify_cli/commands/app/create/node"
|
8
8
|
|
9
|
+
recommend_default_node_range
|
10
|
+
recommend_default_ruby_range
|
11
|
+
|
9
12
|
def call(_args, _command_name)
|
10
13
|
@ctx.puts(self.class.help)
|
11
14
|
end
|
@@ -4,6 +4,9 @@ module ShopifyCLI
|
|
4
4
|
class Deploy < ShopifyCLI::Command::AppSubCommand
|
5
5
|
subcommand :Heroku, "heroku", "shopify_cli/commands/app/deploy/heroku"
|
6
6
|
|
7
|
+
recommend_default_node_range
|
8
|
+
recommend_default_ruby_range
|
9
|
+
|
7
10
|
def call(args, _name)
|
8
11
|
platform = args.shift
|
9
12
|
case platform
|
@@ -15,6 +15,8 @@ module ShopifyCLI
|
|
15
15
|
|
16
16
|
def call(*)
|
17
17
|
shop = (options.flags[:shop] || @ctx.getenv("SHOPIFY_SHOP" || nil))
|
18
|
+
ShopifyCLI::DB.set(shop: self.class.validate_shop(shop, context: @ctx)) unless shop.nil?
|
19
|
+
|
18
20
|
if shop.nil? && Shopifolk.check
|
19
21
|
Shopifolk.reset
|
20
22
|
@ctx.puts(@ctx.message("core.tasks.select_org_and_shop.identified_as_shopify"))
|
@@ -31,25 +33,17 @@ module ShopifyCLI
|
|
31
33
|
IdentityAuth.new(ctx: @ctx).authenticate
|
32
34
|
org = select_organization
|
33
35
|
ShopifyCLI::DB.set(organization_id: org["id"].to_i) unless org.nil?
|
34
|
-
|
36
|
+
Whoami.call([], "whoami")
|
35
37
|
end
|
36
|
-
# validate that shop belongs to organization
|
37
|
-
ShopifyCLI::DB.set(shop: self.class.validate_shop(shop: shop, org: org, context: @ctx)) unless shop.nil?
|
38
|
-
Whoami.call([], "whoami")
|
39
38
|
end
|
40
39
|
|
41
40
|
def self.help
|
42
41
|
ShopifyCLI::Context.message("core.login.help", ShopifyCLI::TOOL_NAME)
|
43
42
|
end
|
44
43
|
|
45
|
-
def self.validate_shop(shop
|
44
|
+
def self.validate_shop(shop, context:)
|
46
45
|
permanent_domain = shop_to_permanent_domain(shop)
|
47
46
|
context.abort(context.message("core.login.invalid_shop", shop)) unless permanent_domain
|
48
|
-
if org
|
49
|
-
stores_owned = org["stores"]
|
50
|
-
is_verified = stores_owned.any? { |store| store["shopDomain"] == permanent_domain }
|
51
|
-
context.abort(context.message("core.login.invalid_shop", shop)) unless is_verified
|
52
|
-
end
|
53
47
|
permanent_domain
|
54
48
|
end
|
55
49
|
|
@@ -36,12 +36,16 @@ module ShopifyCLI
|
|
36
36
|
# the partners dashboard and identity.
|
37
37
|
LOCAL_PARTNERS = "SHOPIFY_APP_CLI_LOCAL_PARTNERS"
|
38
38
|
|
39
|
-
# When true the CLI points to
|
40
|
-
|
39
|
+
# When true the CLI points to spin instances of services
|
40
|
+
SPIN = "SPIN"
|
41
|
+
INFER_SPIN = "INFER_SPIN"
|
41
42
|
SPIN_WORKSPACE = "SPIN_WORKSPACE"
|
42
43
|
SPIN_NAMESPACE = "SPIN_NAMESPACE"
|
43
44
|
SPIN_HOST = "SPIN_HOST"
|
44
45
|
|
46
|
+
# Deprecated, equivalent to using SPIN=1
|
47
|
+
SPIN_PARTNERS = "SHOPIFY_APP_CLI_SPIN_PARTNERS"
|
48
|
+
|
45
49
|
# Environments
|
46
50
|
TEST = "SHOPIFY_CLI_TEST"
|
47
51
|
ACCEPTANCE_TEST = "SHOPIFY_CLI_ACCEPTANCE_TEST"
|
@@ -54,6 +58,18 @@ module ShopifyCLI
|
|
54
58
|
MONORAIL_REAL_EVENTS = "MONORAIL_REAL_EVENTS"
|
55
59
|
end
|
56
60
|
|
61
|
+
module SupportedVersions
|
62
|
+
module Ruby
|
63
|
+
FROM = "2.6.6"
|
64
|
+
TO = "3.1.0"
|
65
|
+
end
|
66
|
+
|
67
|
+
module Node
|
68
|
+
FROM = "12.0.0"
|
69
|
+
TO = "17.0.0"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
57
73
|
module Identity
|
58
74
|
CLIENT_ID_DEV = "e5380e02-312a-7408-5718-e07017e9cf52"
|
59
75
|
CLIENT_ID = "fbdb2649-e327-4907-8f67-908d24cfd7e3"
|
@@ -3,10 +3,10 @@ require "shopify_cli"
|
|
3
3
|
module ShopifyCLI
|
4
4
|
module Core
|
5
5
|
class Executor < CLI::Kit::Executor
|
6
|
-
def initialize(ctx, task_registry, *args
|
7
|
-
@ctx = ctx ||
|
8
|
-
@task_registry = task_registry ||
|
9
|
-
super(*args
|
6
|
+
ruby2_keywords def initialize(ctx, task_registry, *args)
|
7
|
+
@ctx = ctx || ShopifyCli::Context.new
|
8
|
+
@task_registry = task_registry || ShopifyCli::Tasks::TaskRegistry.new
|
9
|
+
super(*args)
|
10
10
|
end
|
11
11
|
|
12
12
|
def call(command, command_name, args)
|
@@ -1,9 +1,25 @@
|
|
1
|
+
require "semantic/semantic"
|
2
|
+
|
1
3
|
module ShopifyCLI
|
2
4
|
# The environment module provides an interface to get information from
|
3
5
|
# the environment in which the CLI runs
|
4
6
|
module Environment
|
5
7
|
TRUTHY_ENV_VARIABLE_VALUES = ["1", "true", "TRUE", "yes", "YES"]
|
6
8
|
|
9
|
+
def self.ruby_version(context: Context.new)
|
10
|
+
out, err, stat = context.capture3('ruby -e "puts RUBY_VERSION"')
|
11
|
+
raise ShopifyCLI::Abort, err unless stat.success?
|
12
|
+
out = out.gsub('"', "")
|
13
|
+
::Semantic::Version.new(out.chomp)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.node_version(context: Context.new)
|
17
|
+
out, err, stat = context.capture3("node", "--version")
|
18
|
+
raise ShopifyCLI::Abort, err unless stat.success?
|
19
|
+
out = out.gsub("v", "")
|
20
|
+
::Semantic::Version.new(out.chomp)
|
21
|
+
end
|
22
|
+
|
7
23
|
def self.interactive=(interactive)
|
8
24
|
@interactive = interactive
|
9
25
|
end
|
@@ -61,17 +77,10 @@ module ShopifyCLI
|
|
61
77
|
)
|
62
78
|
end
|
63
79
|
|
64
|
-
def self.use_spin_partners_instance?(env_variables: ENV)
|
65
|
-
env_variable_truthy?(
|
66
|
-
Constants::EnvironmentVariables::SPIN_PARTNERS,
|
67
|
-
env_variables: env_variables
|
68
|
-
)
|
69
|
-
end
|
70
|
-
|
71
80
|
def self.partners_domain(env_variables: ENV)
|
72
81
|
if use_local_partners_instance?(env_variables: env_variables)
|
73
82
|
"partners.myshopify.io"
|
74
|
-
elsif
|
83
|
+
elsif use_spin?(env_variables: env_variables)
|
75
84
|
"partners.#{spin_url(env_variables: env_variables)}"
|
76
85
|
else
|
77
86
|
"partners.shopify.com"
|
@@ -79,15 +88,41 @@ module ShopifyCLI
|
|
79
88
|
end
|
80
89
|
|
81
90
|
def self.use_spin?(env_variables: ENV)
|
82
|
-
|
83
|
-
|
91
|
+
env_variable_truthy?(
|
92
|
+
Constants::EnvironmentVariables::SPIN,
|
93
|
+
env_variables: env_variables
|
94
|
+
) || env_variable_truthy?(
|
95
|
+
Constants::EnvironmentVariables::SPIN_PARTNERS,
|
96
|
+
env_variables: env_variables
|
97
|
+
)
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.infer_spin?(env_variables: ENV)
|
101
|
+
env_variable_truthy?(
|
102
|
+
Constants::EnvironmentVariables::INFER_SPIN,
|
103
|
+
env_variables: env_variables
|
104
|
+
)
|
84
105
|
end
|
85
106
|
|
86
107
|
def self.spin_url(env_variables: ENV)
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
108
|
+
if infer_spin?(env_variables: env_variables)
|
109
|
+
# TODO: Remove version check and delete spin-legacy branch
|
110
|
+
# once spin2 becomes the installed "spin" binary by default
|
111
|
+
spin_version = %x(spin version 2> /dev/null).strip
|
112
|
+
if spin_version.start_with?("spin-")
|
113
|
+
# spin2
|
114
|
+
raise ShopifyCLI:: Abort, "SPIN_INSTANCE must be specified" unless ENV.key?("SPIN_INSTANCE")
|
115
|
+
%x(spin show -o fqdn 2> /dev/null).strip
|
116
|
+
else
|
117
|
+
# spin-legacy
|
118
|
+
%x(spin info fqdn 2> /dev/null).strip
|
119
|
+
end
|
120
|
+
else
|
121
|
+
spin_workspace = spin_workspace(env_variables: env_variables)
|
122
|
+
spin_namespace = spin_namespace(env_variables: env_variables)
|
123
|
+
spin_host = spin_host(env_variables: env_variables)
|
124
|
+
"#{spin_workspace}.#{spin_namespace}.#{spin_host}"
|
125
|
+
end
|
91
126
|
end
|
92
127
|
|
93
128
|
def self.send_monorail_events?(env_variables: ENV)
|
@@ -106,11 +141,21 @@ module ShopifyCLI
|
|
106
141
|
end
|
107
142
|
|
108
143
|
def self.spin_workspace(env_variables: ENV)
|
109
|
-
env_variables[Constants::EnvironmentVariables::SPIN_WORKSPACE]
|
144
|
+
env_value = env_variables[Constants::EnvironmentVariables::SPIN_WORKSPACE]
|
145
|
+
return env_value unless env_value.nil?
|
146
|
+
|
147
|
+
if env_value.nil?
|
148
|
+
raise "No value set for #{Constants::EnvironmentVariables::SPIN_WORKSPACE}"
|
149
|
+
end
|
110
150
|
end
|
111
151
|
|
112
152
|
def self.spin_namespace(env_variables: ENV)
|
113
|
-
env_variables[Constants::EnvironmentVariables::SPIN_NAMESPACE]
|
153
|
+
env_value = env_variables[Constants::EnvironmentVariables::SPIN_NAMESPACE]
|
154
|
+
return env_value unless env_value.nil?
|
155
|
+
|
156
|
+
if env_value.nil?
|
157
|
+
raise "No value set for #{Constants::EnvironmentVariables::SPIN_NAMESPACE}"
|
158
|
+
end
|
114
159
|
end
|
115
160
|
|
116
161
|
def self.spin_host(env_variables: ENV)
|
@@ -1,5 +1,10 @@
|
|
1
1
|
module ShopifyCLI
|
2
2
|
module ExceptionReporter
|
3
|
+
def self.report_error_silently(error)
|
4
|
+
return unless ReportingConfigurationController.reporting_enabled?
|
5
|
+
report_to_bugsnag(error: error)
|
6
|
+
end
|
7
|
+
|
3
8
|
def self.report(error, _logs = nil, _api_key = nil, custom_metadata = {})
|
4
9
|
context = ShopifyCLI::Context.new
|
5
10
|
unless ShopifyCLI::Environment.development?
|
@@ -19,6 +24,10 @@ module ShopifyCLI
|
|
19
24
|
return unless reportable_error?(error)
|
20
25
|
|
21
26
|
return unless report?(context: context)
|
27
|
+
report_to_bugsnag(error: error, custom_metadata: custom_metadata)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.report_to_bugsnag(error:, custom_metadata: {})
|
22
31
|
ENV["BUGSNAG_DISABLE_AUTOCONFIGURE"] = "1"
|
23
32
|
require "bugsnag"
|
24
33
|
|
@@ -2,17 +2,28 @@ module ShopifyCLI
|
|
2
2
|
module GitHub
|
3
3
|
module IssueURLGenerator
|
4
4
|
def self.error_url(error)
|
5
|
-
title = "#{error.class}: #{error.message}"
|
5
|
+
title = "[Bug]: #{error.class}: #{error.message}"
|
6
6
|
labels = "type:bug"
|
7
|
-
content = File.read(File.join(ShopifyCLI::ROOT, ".github/ISSUE_TEMPLATE.md"))
|
8
7
|
|
9
8
|
# take at most 5 lines from backtrace
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
9
|
+
stacktrace_text =
|
10
|
+
if error.backtrace # Sometimes errors seem to appear without backtrace, see https://github.com/Shopify/shopify-cli/issues/1972#issuecomment-1028013630
|
11
|
+
stacktrace = error.backtrace.length < 5 ? error.backtrace : error.backtrace[0..4]
|
12
|
+
stacktrace.join("\n").to_s
|
13
|
+
else
|
14
|
+
""
|
15
|
+
end
|
16
|
+
query = URI.encode_www_form({
|
17
|
+
title: title,
|
18
|
+
labels: labels,
|
19
|
+
template: "bug_report.yaml",
|
20
|
+
stack_trace: stacktrace_text,
|
21
|
+
os: RUBY_PLATFORM,
|
22
|
+
cli_version: ShopifyCLI::VERSION,
|
23
|
+
ruby_version: "#{RUBY_VERSION}p#{RUBY_PATCHLEVEL}",
|
24
|
+
shell: ENV["SHELL"],
|
25
|
+
})
|
26
|
+
"#{ShopifyCLI::Constants::Links::NEW_ISSUE}?#{query}"
|
16
27
|
end
|
17
28
|
end
|
18
29
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module ShopifyCLI
|
2
|
+
class IdentityAuth
|
3
|
+
class EnvAuthToken
|
4
|
+
Token = Struct.new(:token, :expires_at, keyword_init: true)
|
5
|
+
|
6
|
+
class << self
|
7
|
+
attr_accessor :exchanged_partners_token
|
8
|
+
|
9
|
+
def partners_token_present?
|
10
|
+
Environment.auth_token
|
11
|
+
end
|
12
|
+
|
13
|
+
def fetch_exchanged_partners_token
|
14
|
+
current_time = Time.now.to_i
|
15
|
+
|
16
|
+
# If we have an in-memory token that hasn't expired yet, we reuse it.
|
17
|
+
if exchanged_partners_token && current_time < exchanged_partners_token.expires_at.to_i
|
18
|
+
return exchanged_partners_token.token
|
19
|
+
end
|
20
|
+
|
21
|
+
new_exchanged_token = yield(Environment.auth_token)
|
22
|
+
token = new_exchanged_token["access_token"]
|
23
|
+
expires_in = new_exchanged_token["expires_in"].to_i
|
24
|
+
expires_at = Time.at(current_time + expires_in)
|
25
|
+
|
26
|
+
token = Token.new(token: token, expires_at: expires_at)
|
27
|
+
|
28
|
+
self.exchanged_partners_token = token
|
29
|
+
token.token
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -13,6 +13,7 @@ module ShopifyCLI
|
|
13
13
|
include SmartProperties
|
14
14
|
|
15
15
|
autoload :Servlet, "shopify_cli/identity_auth/servlet"
|
16
|
+
autoload :EnvAuthToken, "shopify_cli/identity_auth/env_auth_token"
|
16
17
|
|
17
18
|
class Error < StandardError; end
|
18
19
|
class Timeout < StandardError; end
|
@@ -68,9 +69,12 @@ module ShopifyCLI
|
|
68
69
|
request_exchange_tokens
|
69
70
|
end
|
70
71
|
|
71
|
-
def
|
72
|
-
|
73
|
-
|
72
|
+
def fetch_or_auth_partners_token
|
73
|
+
if EnvAuthToken.partners_token_present?
|
74
|
+
return EnvAuthToken.fetch_exchanged_partners_token do |env_token|
|
75
|
+
exchange_partners_auth_token(env_token)
|
76
|
+
end
|
77
|
+
end
|
74
78
|
|
75
79
|
ShopifyCLI::DB.get(:partners_exchange_token) do
|
76
80
|
IdentityAuth.new(ctx: ctx).authenticate
|
@@ -78,6 +82,15 @@ module ShopifyCLI
|
|
78
82
|
end
|
79
83
|
end
|
80
84
|
|
85
|
+
def exchange_partners_auth_token(subject_token)
|
86
|
+
application = "partners"
|
87
|
+
request_exchange_token(
|
88
|
+
audience: client_id_for_application(application),
|
89
|
+
scopes: APPLICATION_SCOPES[application],
|
90
|
+
subject_token: subject_token,
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
81
94
|
def self.environment_auth_token?
|
82
95
|
!!Environment.auth_token
|
83
96
|
end
|
@@ -195,30 +208,35 @@ module ShopifyCLI
|
|
195
208
|
|
196
209
|
def request_exchange_tokens
|
197
210
|
APPLICATION_SCOPES.each do |key, scopes|
|
198
|
-
|
211
|
+
request_and_save_exchange_token(key, client_id_for_application(key), scopes)
|
199
212
|
end
|
200
213
|
end
|
201
214
|
|
202
|
-
def
|
215
|
+
def request_and_save_exchange_token(name, audience, additional_scopes)
|
203
216
|
return if name == "shopify" && !store.exists?(:shop)
|
217
|
+
access_token = request_exchange_token(
|
218
|
+
audience: audience,
|
219
|
+
scopes: scopes(additional_scopes),
|
220
|
+
subject_token: store.get(:identity_access_token),
|
221
|
+
destination: name == "shopify" ? "https://#{store.get(:shop)}/admin" : nil
|
222
|
+
)["access_token"]
|
223
|
+
store.set("#{name}_exchange_token".to_sym => access_token)
|
224
|
+
ctx.debug("#{name}_exchange_token: " + access_token)
|
225
|
+
end
|
204
226
|
|
227
|
+
def request_exchange_token(audience:, scopes:, subject_token:, destination: nil)
|
205
228
|
params = {
|
206
229
|
grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
|
207
230
|
requested_token_type: "urn:ietf:params:oauth:token-type:access_token",
|
208
231
|
subject_token_type: "urn:ietf:params:oauth:token-type:access_token",
|
209
232
|
client_id: client_id,
|
210
233
|
audience: audience,
|
211
|
-
scope: scopes
|
212
|
-
subject_token:
|
213
|
-
|
214
|
-
|
215
|
-
result[:destination] = "https://#{store.get(:shop)}/admin"
|
216
|
-
end
|
217
|
-
end
|
234
|
+
scope: scopes,
|
235
|
+
subject_token: subject_token,
|
236
|
+
destination: destination,
|
237
|
+
}.compact
|
218
238
|
# ctx.debug(params)
|
219
|
-
|
220
|
-
store.set("#{name}_exchange_token".to_sym => resp["access_token"])
|
221
|
-
ctx.debug("#{name}_exchange_token: " + resp["access_token"])
|
239
|
+
post_token_request(params)
|
222
240
|
end
|
223
241
|
|
224
242
|
def post_token_request(params)
|
@@ -256,7 +274,7 @@ module ShopifyCLI
|
|
256
274
|
def auth_url
|
257
275
|
if Environment.use_local_partners_instance?
|
258
276
|
"https://identity.myshopify.io/oauth"
|
259
|
-
elsif Environment.
|
277
|
+
elsif Environment.use_spin?
|
260
278
|
"https://identity.#{Environment.spin_url}/oauth"
|
261
279
|
else
|
262
280
|
"https://accounts.shopify.com/oauth"
|
@@ -264,7 +282,7 @@ module ShopifyCLI
|
|
264
282
|
end
|
265
283
|
|
266
284
|
def client_id_for_application(application_name)
|
267
|
-
client_ids = if Environment.use_local_partners_instance? || Environment.
|
285
|
+
client_ids = if Environment.use_local_partners_instance? || Environment.use_spin?
|
268
286
|
DEV_APPLICATION_CLIENT_IDS
|
269
287
|
else
|
270
288
|
APPLICATION_CLIENT_IDS
|
@@ -280,7 +298,7 @@ module ShopifyCLI
|
|
280
298
|
end
|
281
299
|
|
282
300
|
def client_id
|
283
|
-
if Environment.use_local_partners_instance? || Environment.
|
301
|
+
if Environment.use_local_partners_instance? || Environment.use_spin?
|
284
302
|
Constants::Identity::CLIENT_ID_DEV
|
285
303
|
else
|
286
304
|
# In the future we might want to use Identity's dynamic
|
@@ -53,7 +53,7 @@ module ShopifyCLI
|
|
53
53
|
{{command:--organization-id=ID}} Partner organization ID. Must be an existing organization.
|
54
54
|
{{command:--store-domain=MYSHOPIFYDOMAIN }} Development store URL. Must be an existing development store.
|
55
55
|
{{command:--db=DB}} Database type. Must be one of: mysql, postgresql, sqlite3, oracle, frontbase, ibm_db, sqlserver, jdbcmysql, jdbcsqlite3, jdbcpostgresql, jdbc.
|
56
|
-
{{command:--rails-opts=RAILSOPTS}} Additional options. Must be string containing one or more valid Rails options, separated by spaces.
|
56
|
+
{{command:--rails-opts=RAILSOPTS}} Additional options. Must be a string containing one or more valid Rails options, separated by spaces.
|
57
57
|
HELP
|
58
58
|
|
59
59
|
error: {
|
@@ -415,7 +415,7 @@ module ShopifyCLI
|
|
415
415
|
Usage: {{command:%s login [--store=STORE]}}
|
416
416
|
HELP
|
417
417
|
invalid_shop: <<~MESSAGE,
|
418
|
-
Invalid store provided (%s). Please
|
418
|
+
Invalid store provided (%s). Please provide the store in the following format: my-store.myshopify.com
|
419
419
|
MESSAGE
|
420
420
|
shop_prompt: <<~PROMPT,
|
421
421
|
What store are you connecting to? (e.g. my-store.myshopify.com; do {{bold:NOT}} include protocol part, e.g., https://)
|