shopify-cli 2.9.0 → 2.11.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/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://)
|