shopify-cli 1.12.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +1 -1
- data/.github/CONTRIBUTING.md +7 -7
- data/.github/DESIGN.md +3 -3
- data/.github/PULL_REQUEST_TEMPLATE.md +1 -1
- data/.github/workflows/build.yml +1 -1
- data/.gitignore +3 -0
- data/.rubocop.yml +3 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +52 -21
- data/Gemfile +4 -0
- data/Gemfile.lock +32 -0
- data/LICENSE +4 -1
- data/README.md +92 -26
- data/RELEASING.md +31 -7
- data/Rakefile +2 -2
- data/SECURITY.md +1 -1
- data/bin/load_shopify.rb +1 -1
- data/bin/shopify +3 -3
- data/dev.yml +1 -1
- data/docs/app/node/index.md +1 -1
- data/docs/app/rails/index.md +1 -1
- data/docs/core/index.md +1 -1
- data/docs/getting-started/index.md +1 -1
- data/docs/getting-started/install/index.md +1 -1
- data/docs/getting-started/migrate/index.md +1 -1
- data/docs/getting-started/uninstall/index.md +1 -1
- data/docs/getting-started/upgrade/index.md +1 -1
- data/docs/help/start-app/index.md +1 -1
- data/docs/index.md +1 -1
- data/ext/shopify-cli/extconf.rb +17 -5
- data/install.sh +1 -1
- data/lib/docgen/index_template.md.erb +2 -2
- data/lib/graphql/all_orgs_with_extensions.graphql +37 -0
- data/lib/graphql/find_organization.graphql +2 -1
- data/lib/project_types/extension/cli.rb +18 -15
- data/lib/project_types/extension/commands/build.rb +4 -5
- data/lib/project_types/extension/commands/connect.rb +35 -0
- data/lib/project_types/extension/commands/create.rb +12 -16
- data/lib/project_types/extension/commands/extension_command.rb +2 -2
- data/lib/project_types/extension/commands/info.rb +86 -0
- data/lib/project_types/extension/commands/push.rb +8 -7
- data/lib/project_types/extension/commands/register.rb +4 -5
- data/lib/project_types/extension/commands/serve.rb +5 -8
- data/lib/project_types/extension/commands/tunnel.rb +3 -1
- data/lib/project_types/extension/errors.rb +9 -0
- data/lib/project_types/extension/extension_project.rb +5 -0
- data/lib/project_types/extension/features/argo.rb +6 -6
- data/lib/project_types/extension/features/argo_runtime.rb +22 -59
- data/lib/project_types/extension/features/argo_serve.rb +25 -21
- data/lib/project_types/extension/forms/connect.rb +42 -0
- data/lib/project_types/extension/forms/questions/ask_name.rb +14 -6
- data/lib/project_types/extension/forms/questions/ask_registration.rb +51 -0
- data/lib/project_types/extension/messages/messages.rb +75 -11
- data/lib/project_types/extension/models/specification.rb +1 -0
- data/lib/project_types/extension/models/specification_handlers/{checkout_argo_extension.rb → checkout_ui_extension.rb} +3 -1
- data/lib/project_types/extension/models/specification_handlers/default.rb +13 -13
- data/lib/project_types/extension/models/specification_handlers/theme_app_extension.rb +89 -0
- data/lib/project_types/extension/models/specifications.rb +1 -0
- data/lib/project_types/extension/tasks/configure_features.rb +6 -7
- data/lib/project_types/extension/tasks/configure_options.rb +20 -0
- data/lib/project_types/extension/tasks/get_extensions.rb +32 -0
- data/lib/project_types/node/cli.rb +9 -21
- data/lib/project_types/node/commands/connect.rb +8 -2
- data/lib/project_types/node/commands/create.rb +9 -5
- data/lib/project_types/node/commands/deploy.rb +15 -5
- data/lib/project_types/node/commands/deploy/heroku.rb +29 -29
- data/lib/project_types/node/commands/generate.rb +4 -2
- data/lib/project_types/node/commands/open.rb +4 -2
- data/lib/project_types/node/commands/serve.rb +3 -2
- data/lib/project_types/node/commands/tunnel.rb +4 -2
- data/lib/project_types/node/messages/messages.rb +46 -89
- data/lib/project_types/rails/cli.rb +9 -21
- data/lib/project_types/rails/commands/connect.rb +8 -2
- data/lib/project_types/rails/commands/create.rb +10 -6
- data/lib/project_types/rails/commands/deploy.rb +15 -5
- data/lib/project_types/rails/commands/deploy/heroku.rb +84 -82
- data/lib/project_types/rails/commands/generate.rb +15 -5
- data/lib/project_types/rails/commands/generate/webhook.rb +28 -26
- data/lib/project_types/rails/commands/open.rb +4 -2
- data/lib/project_types/rails/commands/serve.rb +3 -2
- data/lib/project_types/rails/commands/tunnel.rb +4 -2
- data/lib/project_types/rails/messages/messages.rb +54 -101
- data/lib/project_types/script/cli.rb +18 -20
- data/lib/project_types/script/commands/create.rb +3 -1
- data/lib/project_types/script/commands/push.rb +12 -5
- data/lib/project_types/script/config/extension_points.yml +0 -3
- data/lib/project_types/script/graphql/app_script_update_or_create.graphql +9 -3
- data/lib/project_types/script/layers/application/create_script.rb +6 -5
- data/lib/project_types/script/layers/application/push_script.rb +2 -1
- data/lib/project_types/script/layers/domain/errors.rb +6 -11
- data/lib/project_types/script/layers/domain/push_package.rb +4 -8
- data/lib/project_types/script/layers/domain/script_json.rb +32 -0
- data/lib/project_types/script/layers/domain/script_project.rb +1 -1
- data/lib/project_types/script/layers/infrastructure/errors.rb +14 -18
- data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_project_creator.rb +105 -0
- data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_task_runner.rb +103 -0
- data/lib/project_types/script/layers/infrastructure/languages/project_creator.rb +26 -0
- data/lib/project_types/script/layers/infrastructure/languages/rust_project_creator.rb +73 -0
- data/lib/project_types/script/layers/infrastructure/languages/rust_task_runner.rb +60 -0
- data/lib/project_types/script/layers/infrastructure/languages/task_runner.rb +21 -0
- data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +2 -4
- data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +45 -34
- data/lib/project_types/script/layers/infrastructure/script_service.rb +37 -16
- data/lib/project_types/script/messages/messages.rb +66 -55
- data/lib/project_types/script/tasks/ensure_env.rb +22 -1
- data/lib/project_types/script/ui/error_handler.rb +32 -32
- data/lib/project_types/theme/cli.rb +16 -27
- data/lib/project_types/theme/commands/check.rb +33 -0
- data/lib/project_types/theme/commands/delete.rb +64 -0
- data/lib/project_types/theme/commands/init.rb +42 -0
- data/lib/project_types/theme/commands/language_server.rb +16 -0
- data/lib/project_types/theme/commands/package.rb +55 -0
- data/lib/project_types/theme/commands/publish.rb +43 -0
- data/lib/project_types/theme/commands/pull.rb +51 -0
- data/lib/project_types/theme/commands/push.rb +58 -32
- data/lib/project_types/theme/commands/serve.rb +8 -16
- data/lib/project_types/theme/forms/confirm_store.rb +15 -0
- data/lib/project_types/theme/forms/select.rb +59 -0
- data/lib/project_types/theme/messages/messages.rb +117 -102
- data/lib/project_types/theme/ui/sync_progress_bar.rb +20 -0
- data/lib/shopify-cli/admin_api.rb +53 -35
- data/lib/shopify-cli/admin_api/populate_resource_command.rb +6 -14
- data/lib/shopify-cli/admin_api/schema.rb +1 -10
- data/lib/shopify-cli/api.rb +29 -14
- data/lib/shopify-cli/command.rb +15 -3
- data/lib/shopify-cli/commands.rb +7 -2
- data/lib/shopify-cli/commands/help.rb +2 -29
- data/lib/shopify-cli/commands/login.rb +95 -0
- data/lib/shopify-cli/commands/logout.rb +24 -8
- data/lib/shopify-cli/commands/populate.rb +23 -0
- data/lib/{project_types/node → shopify-cli}/commands/populate/customer.rb +2 -8
- data/lib/{project_types/node → shopify-cli}/commands/populate/draft_order.rb +2 -2
- data/lib/{project_types/node → shopify-cli}/commands/populate/product.rb +2 -8
- data/lib/shopify-cli/commands/store.rb +15 -0
- data/lib/shopify-cli/commands/switch.rb +39 -0
- data/lib/shopify-cli/commands/system.rb +12 -0
- data/lib/shopify-cli/commands/whoami.rb +28 -0
- data/lib/shopify-cli/connect.rb +32 -0
- data/lib/shopify-cli/context.rb +65 -4
- data/lib/shopify-cli/core/entry_point.rb +3 -22
- data/lib/shopify-cli/db.rb +4 -4
- data/lib/shopify-cli/http_request.rb +16 -0
- data/lib/shopify-cli/identity_auth.rb +282 -0
- data/lib/shopify-cli/{oauth → identity_auth}/servlet.rb +11 -12
- data/lib/shopify-cli/messages/messages.rb +133 -39
- data/lib/shopify-cli/partners_api.rb +21 -41
- data/lib/shopify-cli/partners_api/organizations.rb +8 -0
- data/lib/shopify-cli/project_commands.rb +16 -0
- data/lib/shopify-cli/project_type.rb +0 -31
- data/lib/shopify-cli/resources/env_file.rb +1 -1
- data/lib/shopify-cli/shopifolk.rb +8 -11
- data/lib/shopify-cli/sub_command.rb +1 -0
- data/lib/shopify-cli/tasks.rb +3 -0
- data/lib/shopify-cli/tasks/confirm_store.rb +18 -0
- data/lib/shopify-cli/tasks/create_api_client.rb +2 -2
- data/lib/shopify-cli/tasks/ensure_authenticated.rb +13 -0
- data/lib/shopify-cli/tasks/ensure_loopback_url.rb +1 -1
- data/lib/shopify-cli/tasks/ensure_project_type.rb +12 -0
- data/lib/shopify-cli/tasks/select_org_and_shop.rb +0 -3
- data/lib/shopify-cli/theme/dev_server.rb +98 -0
- data/lib/shopify-cli/theme/dev_server/certificate_manager.rb +79 -0
- data/lib/shopify-cli/theme/dev_server/header_hash.rb +94 -0
- data/lib/shopify-cli/theme/dev_server/hot-reload.js +93 -0
- data/lib/shopify-cli/theme/dev_server/hot_reload.rb +76 -0
- data/lib/shopify-cli/theme/dev_server/local_assets.rb +87 -0
- data/lib/shopify-cli/theme/dev_server/proxy.rb +205 -0
- data/lib/shopify-cli/theme/dev_server/sse.rb +75 -0
- data/lib/shopify-cli/theme/dev_server/watcher.rb +59 -0
- data/lib/shopify-cli/theme/dev_server/web_server.rb +140 -0
- data/lib/shopify-cli/theme/development_theme.rb +69 -0
- data/lib/shopify-cli/theme/file.rb +112 -0
- data/lib/shopify-cli/theme/ignore_filter.rb +109 -0
- data/lib/shopify-cli/theme/mime_type.rb +34 -0
- data/lib/shopify-cli/theme/syncer.rb +328 -0
- data/lib/shopify-cli/theme/theme.rb +204 -0
- data/lib/shopify-cli/version.rb +1 -1
- data/lib/shopify_cli.rb +18 -11
- data/shopify-cli.gemspec +12 -5
- data/shopify.fish +1 -1
- data/shopify.sh +1 -1
- metadata +96 -41
- data/.github/workflows/release.yml +0 -61
- data/lib/project_types/extension/features/argo_serve_options.rb +0 -41
- data/lib/project_types/node/commands/populate.rb +0 -23
- data/lib/project_types/rails/commands/populate.rb +0 -23
- data/lib/project_types/rails/commands/populate/customer.rb +0 -31
- data/lib/project_types/rails/commands/populate/draft_order.rb +0 -28
- data/lib/project_types/rails/commands/populate/product.rb +0 -30
- data/lib/project_types/script/layers/domain/config_ui.rb +0 -16
- data/lib/project_types/script/layers/infrastructure/assemblyscript_project_creator.rb +0 -95
- data/lib/project_types/script/layers/infrastructure/assemblyscript_task_runner.rb +0 -101
- data/lib/project_types/script/layers/infrastructure/project_creator.rb +0 -24
- data/lib/project_types/script/layers/infrastructure/rust_project_creator.rb +0 -71
- data/lib/project_types/script/layers/infrastructure/rust_task_runner.rb +0 -58
- data/lib/project_types/script/layers/infrastructure/task_runner.rb +0 -19
- data/lib/project_types/theme/commands/connect.rb +0 -54
- data/lib/project_types/theme/commands/create.rb +0 -48
- data/lib/project_types/theme/commands/deploy.rb +0 -38
- data/lib/project_types/theme/commands/generate.rb +0 -20
- data/lib/project_types/theme/commands/generate/env.rb +0 -79
- data/lib/project_types/theme/forms/connect.rb +0 -34
- data/lib/project_types/theme/forms/create.rb +0 -22
- data/lib/project_types/theme/tasks/ensure_themekit_installed.rb +0 -78
- data/lib/project_types/theme/themekit.rb +0 -113
- data/lib/shopify-cli/commands/connect.rb +0 -64
- data/lib/shopify-cli/commands/create.rb +0 -50
- data/lib/shopify-cli/oauth.rb +0 -198
@@ -0,0 +1,20 @@
|
|
1
|
+
module Theme
|
2
|
+
module UI
|
3
|
+
class SyncProgressBar
|
4
|
+
def initialize(syncer)
|
5
|
+
@syncer = syncer
|
6
|
+
end
|
7
|
+
|
8
|
+
def progress(method, **args)
|
9
|
+
@syncer.delay_errors!
|
10
|
+
CLI::UI::Progress.progress do |bar|
|
11
|
+
@syncer.public_send(method, **args) do |left, total|
|
12
|
+
bar.tick(set_percent: 1 - left.to_f / total)
|
13
|
+
end
|
14
|
+
bar.tick(set_percent: 1)
|
15
|
+
end
|
16
|
+
@syncer.report_errors!
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -38,9 +38,15 @@ module ShopifyCli
|
|
38
38
|
# ShopifyCli::AdminAPI.query(@ctx, 'all_organizations')
|
39
39
|
#
|
40
40
|
def query(ctx, query_name, shop:, api_version: nil, **variables)
|
41
|
-
|
41
|
+
CLI::Kit::Util.begin do
|
42
42
|
api_client(ctx, api_version, shop).query(query_name, variables: variables)
|
43
|
+
end.retry_after(API::APIRequestUnauthorizedError, retries: 1) do
|
44
|
+
ShopifyCli::IdentityAuth.new(ctx: ctx).reauthenticate
|
43
45
|
end
|
46
|
+
rescue API::APIRequestUnauthorizedError
|
47
|
+
ctx.abort(ctx.message("core.api.error.failed_auth"))
|
48
|
+
rescue API::APIRequestForbiddenError
|
49
|
+
ctx.abort(ctx.message("core.api.error.forbidden", ShopifyCli::TOOL_NAME))
|
44
50
|
end
|
45
51
|
|
46
52
|
##
|
@@ -75,49 +81,47 @@ module ShopifyCli
|
|
75
81
|
# path: 'data.json',
|
76
82
|
# token: 'password')
|
77
83
|
#
|
78
|
-
def rest_request(ctx, shop:, path:, body: nil, method: "GET", api_version: nil, token: nil)
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
+
def rest_request(ctx, shop:, path:, query: nil, body: nil, method: "GET", api_version: nil, token: nil)
|
85
|
+
CLI::Kit::Util.begin do
|
86
|
+
ShopifyCli::DB.set(shopify_exchange_token: token) unless token.nil?
|
87
|
+
url = URI::HTTPS.build(
|
88
|
+
host: shop,
|
89
|
+
path: "/admin/api/#{fetch_api_version(ctx, api_version, shop)}/#{path}",
|
90
|
+
query: query,
|
91
|
+
)
|
92
|
+
resp = api_client(ctx, api_version, shop, path: path).request(url: url.to_s, body: body, method: method)
|
93
|
+
ShopifyCli::DB.set(shopify_exchange_token: nil) unless token.nil?
|
94
|
+
resp
|
95
|
+
end.retry_after(API::APIRequestUnauthorizedError) do
|
96
|
+
ShopifyCli::IdentityAuth.new(ctx: ctx).reauthenticate
|
97
|
+
end
|
84
98
|
end
|
85
99
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
authenticate(ctx, shop)
|
92
|
-
retry
|
100
|
+
def get_shop_or_abort(ctx)
|
101
|
+
ctx.abort(
|
102
|
+
ctx.message("core.populate.error.no_shop", ShopifyCli::TOOL_NAME)
|
103
|
+
) unless ShopifyCli::DB.exists?(:shop)
|
104
|
+
ShopifyCli::DB.get(:shop)
|
93
105
|
end
|
94
106
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
service: "admin",
|
100
|
-
client_id: env.api_key,
|
101
|
-
secret: env.secret,
|
102
|
-
scopes: env.scopes,
|
103
|
-
token_path: "/access_token",
|
104
|
-
options: { "grant_options[]" => "per user" },
|
105
|
-
).authenticate("https://#{shop}/admin/oauth")
|
107
|
+
private
|
108
|
+
|
109
|
+
def authenticate(ctx, _shop)
|
110
|
+
ShopifyCli::IdentityAuth.new(ctx: ctx).authenticate
|
106
111
|
end
|
107
112
|
|
108
113
|
def api_client(ctx, api_version, shop, path: "graphql.json")
|
109
114
|
new(
|
110
115
|
ctx: ctx,
|
111
|
-
|
112
|
-
token: admin_access_token(ctx, shop),
|
116
|
+
token: access_token(ctx, shop),
|
113
117
|
url: "https://#{shop}/admin/api/#{fetch_api_version(ctx, api_version, shop)}/#{path}",
|
114
118
|
)
|
115
119
|
end
|
116
120
|
|
117
|
-
def
|
118
|
-
ShopifyCli::DB.get(:
|
121
|
+
def access_token(ctx, shop)
|
122
|
+
ShopifyCli::DB.get(:shopify_exchange_token) do
|
119
123
|
authenticate(ctx, shop)
|
120
|
-
ShopifyCli::DB.get(:
|
124
|
+
ShopifyCli::DB.get(:shopify_exchange_token)
|
121
125
|
end
|
122
126
|
end
|
123
127
|
|
@@ -125,14 +129,28 @@ module ShopifyCli
|
|
125
129
|
return api_version unless api_version.nil?
|
126
130
|
client = new(
|
127
131
|
ctx: ctx,
|
128
|
-
|
129
|
-
token: admin_access_token(ctx, shop),
|
132
|
+
token: access_token(ctx, shop),
|
130
133
|
url: "https://#{shop}/admin/api/unstable/graphql.json",
|
131
134
|
)
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
+
CLI::Kit::Util.begin do
|
136
|
+
versions = client.query("api_versions")["data"]["publicApiVersions"]
|
137
|
+
latest = versions.find { |version| version["displayName"].include?("Latest") }
|
138
|
+
latest["handle"]
|
139
|
+
end.retry_after(API::APIRequestUnauthorizedError, retries: 1) do
|
140
|
+
ShopifyCli::IdentityAuth.new(ctx: ctx).reauthenticate
|
141
|
+
end
|
142
|
+
rescue API::APIRequestUnauthorizedError
|
143
|
+
ctx.abort(ctx.message("core.api.error.failed_auth"))
|
144
|
+
rescue API::APIRequestForbiddenError
|
145
|
+
ctx.abort(ctx.message("core.api.error.forbidden", ShopifyCli::TOOL_NAME))
|
135
146
|
end
|
136
147
|
end
|
148
|
+
|
149
|
+
def auth_headers(token)
|
150
|
+
{
|
151
|
+
Authorization: "Bearer #{token}",
|
152
|
+
"X-Shopify-Access-Token" => token, # TODO: Remove when we no longer need private apps
|
153
|
+
}
|
154
|
+
end
|
137
155
|
end
|
138
156
|
end
|
@@ -25,12 +25,11 @@ module ShopifyCli
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def call(args, _)
|
28
|
-
return unless Project.current
|
29
|
-
Tasks::EnsureEnv.call(@ctx)
|
30
28
|
@args = args
|
31
29
|
@input = Hash.new
|
32
30
|
@count = DEFAULT_COUNT
|
33
31
|
@help = false
|
32
|
+
@skip_shop_confirmation = false
|
34
33
|
input_options
|
35
34
|
resource_options.parse(@args)
|
36
35
|
|
@@ -41,8 +40,8 @@ module ShopifyCli
|
|
41
40
|
return @ctx.puts(output)
|
42
41
|
end
|
43
42
|
|
44
|
-
|
45
|
-
|
43
|
+
ShopifyCli::Tasks::ConfirmStore.call(@ctx) unless @skip_shop_confirmation
|
44
|
+
@shop = AdminAPI.get_shop_or_abort(@ctx)
|
46
45
|
if @silent
|
47
46
|
spin_group = CLI::UI::SpinGroup.new
|
48
47
|
spin_group.add(@ctx.message("core.populate.populating", @count, camel_case_resource_type)) do |spinner|
|
@@ -89,7 +88,7 @@ module ShopifyCli
|
|
89
88
|
|
90
89
|
opts.on("--silent") { |v| @silent = v }
|
91
90
|
|
92
|
-
opts.on("--shop
|
91
|
+
opts.on("--skip-shop-confirmation") { |v| @skip_shop_confirmation = v }
|
93
92
|
end
|
94
93
|
end
|
95
94
|
|
@@ -130,7 +129,7 @@ module ShopifyCli
|
|
130
129
|
"core.populate.completion_message",
|
131
130
|
@count,
|
132
131
|
"#{camel_case_resource_type}#{plural}",
|
133
|
-
|
132
|
+
@shop,
|
134
133
|
camel_case_resource_type,
|
135
134
|
admin_url,
|
136
135
|
snake_case_resource_type
|
@@ -138,7 +137,7 @@ module ShopifyCli
|
|
138
137
|
end
|
139
138
|
|
140
139
|
def admin_url
|
141
|
-
"https://#{
|
140
|
+
"https://#{@shop}/admin/"
|
142
141
|
end
|
143
142
|
|
144
143
|
def price
|
@@ -147,13 +146,6 @@ module ShopifyCli
|
|
147
146
|
|
148
147
|
private
|
149
148
|
|
150
|
-
def get_shop(ctx)
|
151
|
-
res = ShopifyCli::Tasks::SelectOrgAndShop.call(ctx)
|
152
|
-
domain = res[:shop_domain]
|
153
|
-
Project.current.env.update(ctx, :shop, domain)
|
154
|
-
domain
|
155
|
-
end
|
156
|
-
|
157
149
|
def camel_case_resource_type
|
158
150
|
@camel_case_resource_type ||= self.class.to_s.split("::").last
|
159
151
|
end
|
@@ -6,7 +6,7 @@ module ShopifyCli
|
|
6
6
|
class << self
|
7
7
|
def get(ctx)
|
8
8
|
unless ShopifyCli::DB.exists?(:shopify_admin_schema)
|
9
|
-
shop =
|
9
|
+
shop = AdminAPI.get_shop_or_abort(ctx)
|
10
10
|
schema = AdminAPI.query(ctx, "admin_introspection", shop: shop)
|
11
11
|
ShopifyCli::DB.set(shopify_admin_schema: JSON.dump(schema))
|
12
12
|
end
|
@@ -15,15 +15,6 @@ module ShopifyCli
|
|
15
15
|
# available
|
16
16
|
self[JSON.parse(ShopifyCli::DB.get(:shopify_admin_schema))]
|
17
17
|
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
def get_shop(ctx)
|
22
|
-
res = ShopifyCli::Tasks::SelectOrgAndShop.call(ctx)
|
23
|
-
domain = res[:shop_domain]
|
24
|
-
Project.current.env.update(ctx, :shop, domain)
|
25
|
-
domain
|
26
|
-
end
|
27
18
|
end
|
28
19
|
|
29
20
|
def type(name)
|
data/lib/shopify-cli/api.rb
CHANGED
@@ -9,12 +9,22 @@ module ShopifyCli
|
|
9
9
|
property :auth_header, accepts: String
|
10
10
|
property! :url, accepts: String
|
11
11
|
|
12
|
-
class APIRequestError < StandardError
|
12
|
+
class APIRequestError < StandardError
|
13
|
+
attr_reader :response
|
14
|
+
|
15
|
+
def initialize(message = nil, response: nil)
|
16
|
+
super(message)
|
17
|
+
@response = response
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
13
21
|
class APIRequestNotFoundError < APIRequestError; end
|
14
22
|
class APIRequestClientError < APIRequestError; end
|
15
23
|
class APIRequestUnauthorizedError < APIRequestClientError; end
|
24
|
+
class APIRequestForbiddenError < APIRequestClientError; end
|
16
25
|
class APIRequestUnexpectedError < APIRequestError; end
|
17
26
|
class APIRequestRetriableError < APIRequestError; end
|
27
|
+
class APIRequestTimeoutError < APIRequestRetriableError; end
|
18
28
|
class APIRequestServerError < APIRequestRetriableError; end
|
19
29
|
class APIRequestThrottledError < APIRequestRetriableError; end
|
20
30
|
|
@@ -46,26 +56,33 @@ module ShopifyCli
|
|
46
56
|
headers = default_headers.merge(headers)
|
47
57
|
response = if method == "POST"
|
48
58
|
HttpRequest.post(uri, body, headers)
|
59
|
+
elsif method == "PUT"
|
60
|
+
HttpRequest.put(uri, body, headers)
|
49
61
|
elsif method == "GET"
|
50
62
|
HttpRequest.get(uri, body, headers)
|
63
|
+
elsif method == "DELETE"
|
64
|
+
HttpRequest.delete(uri, body, headers)
|
51
65
|
end
|
52
|
-
|
53
66
|
case response.code.to_i
|
54
67
|
when 200..399
|
55
|
-
[response.code.to_i, JSON.parse(response.body)]
|
68
|
+
[response.code.to_i, JSON.parse(response.body), response]
|
56
69
|
when 401
|
57
|
-
raise APIRequestUnauthorizedError
|
70
|
+
raise APIRequestUnauthorizedError.new("#{response.code}\n#{response.body}", response: response)
|
71
|
+
when 403
|
72
|
+
raise APIRequestForbiddenError.new("#{response.code}\n#{response.body}", response: response)
|
58
73
|
when 404
|
59
|
-
raise APIRequestNotFoundError
|
74
|
+
raise APIRequestNotFoundError.new("#{response.code}\n#{response.body}", response: response)
|
60
75
|
when 429
|
61
|
-
raise APIRequestThrottledError
|
76
|
+
raise APIRequestThrottledError.new("#{response.code}\n#{response.body}", response: response)
|
62
77
|
when 400..499
|
63
|
-
raise APIRequestClientError
|
78
|
+
raise APIRequestClientError.new("#{response.code}\n#{response.body}", response: response)
|
64
79
|
when 500..599
|
65
|
-
raise APIRequestServerError
|
80
|
+
raise APIRequestServerError.new("#{response.code}\n#{response.body}", response: response)
|
66
81
|
else
|
67
|
-
raise APIRequestUnexpectedError
|
82
|
+
raise APIRequestUnexpectedError.new("#{response.code}\n#{response.body}", response: response)
|
68
83
|
end
|
84
|
+
rescue Net::OpenTimeout, Net::ReadTimeout, Net::WriteTimeout, Errno::ETIMEDOUT, Timeout::Error
|
85
|
+
raise APIRequestTimeoutError.new("Timeout")
|
69
86
|
end.retry_after(APIRequestRetriableError, retries: 3) do |e|
|
70
87
|
sleep(1) if e.is_a?(APIRequestThrottledError)
|
71
88
|
end
|
@@ -87,13 +104,11 @@ module ShopifyCli
|
|
87
104
|
|
88
105
|
private
|
89
106
|
|
90
|
-
def current_sha
|
91
|
-
@current_sha ||= Git.sha(dir: ShopifyCli::ROOT)
|
92
|
-
end
|
93
|
-
|
94
107
|
def default_headers
|
95
108
|
{
|
96
|
-
"User-Agent" => "Shopify
|
109
|
+
"User-Agent" => "Shopify CLI; v=#{ShopifyCli::VERSION}",
|
110
|
+
"Sec-CH-UA" => "Shopify CLI; v=#{ShopifyCli::VERSION} sha=#{ShopifyCli.sha}",
|
111
|
+
"Sec-CH-UA-PLATFORM" => ctx.os,
|
97
112
|
}.tap do |headers|
|
98
113
|
headers["X-Shopify-Cli-Employee"] = "1" if Shopifolk.acting_as_shopify_organization?
|
99
114
|
end.merge(auth_headers(token))
|
data/lib/shopify-cli/command.rb
CHANGED
@@ -42,13 +42,16 @@ module ShopifyCli
|
|
42
42
|
)
|
43
43
|
end
|
44
44
|
|
45
|
-
def prerequisite_task(*
|
45
|
+
def prerequisite_task(*tasks_without_args, **tasks_with_args)
|
46
46
|
@prerequisite_tasks ||= []
|
47
|
-
@prerequisite_tasks +=
|
47
|
+
@prerequisite_tasks += tasks_without_args.map { |t| PrerequisiteTask.new(t) }
|
48
|
+
@prerequisite_tasks += tasks_with_args.map { |t, args| PrerequisiteTask.new(t, args) }
|
48
49
|
end
|
49
50
|
|
50
51
|
def run_prerequisites
|
51
|
-
(@prerequisite_tasks || []).each
|
52
|
+
(@prerequisite_tasks || []).each do |task|
|
53
|
+
task_registry[task.name]&.call(@ctx, *task.args)
|
54
|
+
end
|
52
55
|
end
|
53
56
|
|
54
57
|
def task_registry
|
@@ -59,6 +62,15 @@ module ShopifyCli
|
|
59
62
|
help = Commands::Help.new(@ctx)
|
60
63
|
help.call(cmds, nil)
|
61
64
|
end
|
65
|
+
|
66
|
+
class PrerequisiteTask
|
67
|
+
attr_reader :name, :args
|
68
|
+
|
69
|
+
def initialize(name, args = [])
|
70
|
+
@name = name
|
71
|
+
@args = args
|
72
|
+
end
|
73
|
+
end
|
62
74
|
end
|
63
75
|
|
64
76
|
def initialize(ctx = nil)
|
data/lib/shopify-cli/commands.rb
CHANGED
@@ -19,11 +19,16 @@ module ShopifyCli
|
|
19
19
|
end
|
20
20
|
|
21
21
|
register :Config, "config", "shopify-cli/commands/config", true
|
22
|
-
register :Connect, "connect", "shopify-cli/commands/connect", true
|
23
|
-
register :Create, "create", "shopify-cli/commands/create", true
|
24
22
|
register :Help, "help", "shopify-cli/commands/help", true
|
23
|
+
register :Login, "login", "shopify-cli/commands/login", true
|
25
24
|
register :Logout, "logout", "shopify-cli/commands/logout", true
|
25
|
+
register :Populate, "populate", "shopify-cli/commands/populate", true
|
26
|
+
register :Store, "store", "shopify-cli/commands/store", true
|
27
|
+
register :Switch, "switch", "shopify-cli/commands/switch", true
|
26
28
|
register :System, "system", "shopify-cli/commands/system", true
|
27
29
|
register :Version, "version", "shopify-cli/commands/version", true
|
30
|
+
register :Whoami, "whoami", "shopify-cli/commands/whoami", true
|
31
|
+
|
32
|
+
autoload :Connect, "shopify-cli/commands/connect"
|
28
33
|
end
|
29
34
|
end
|
@@ -23,17 +23,9 @@ module ShopifyCli
|
|
23
23
|
preamble = @ctx.message("core.help.preamble", ShopifyCli::TOOL_NAME)
|
24
24
|
@ctx.puts(preamble)
|
25
25
|
|
26
|
-
|
27
|
-
next if name == "help"
|
28
|
-
@ctx.puts("{{command:#{name}}}: #{klass.help}\n")
|
29
|
-
end
|
30
|
-
|
31
|
-
return unless inside_supported_project?
|
26
|
+
available_commands = resolved_commands.select { |_name, c| !c.hidden? }
|
32
27
|
|
33
|
-
|
34
|
-
@ctx.puts("{{bold:Available commands for #{project_type_name} projects:}}\n\n")
|
35
|
-
|
36
|
-
local_commands.each do |name, klass|
|
28
|
+
available_commands.each do |name, klass|
|
37
29
|
next if name == "help"
|
38
30
|
@ctx.puts("{{command:#{name}}}: #{klass.help}\n")
|
39
31
|
end
|
@@ -41,21 +33,6 @@ module ShopifyCli
|
|
41
33
|
|
42
34
|
private
|
43
35
|
|
44
|
-
def project_type_name
|
45
|
-
ProjectType.load_type(Project.current_project_type).project_name
|
46
|
-
end
|
47
|
-
|
48
|
-
def core_commands
|
49
|
-
resolved_commands
|
50
|
-
.select { |_name, c| !c.hidden? }
|
51
|
-
.select { |name, _c| Commands.core_command?(name) }
|
52
|
-
end
|
53
|
-
|
54
|
-
def local_commands
|
55
|
-
resolved_commands
|
56
|
-
.reject { |name, _c| Commands.core_command?(name) }
|
57
|
-
end
|
58
|
-
|
59
36
|
def display_help(klass)
|
60
37
|
output = klass.help
|
61
38
|
if klass.respond_to?(:extended_help)
|
@@ -70,10 +47,6 @@ module ShopifyCli
|
|
70
47
|
.resolved_commands
|
71
48
|
.sort
|
72
49
|
end
|
73
|
-
|
74
|
-
def inside_supported_project?
|
75
|
-
Project.current_project_type && ProjectType.load_type(Project.current_project_type)
|
76
|
-
end
|
77
50
|
end
|
78
51
|
end
|
79
52
|
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require "shopify_cli"
|
2
|
+
|
3
|
+
module ShopifyCli
|
4
|
+
module Commands
|
5
|
+
class Login < ShopifyCli::Command
|
6
|
+
PROTOCOL_REGEX = /^https?\:\/\//
|
7
|
+
PERMANENT_DOMAIN_SUFFIX = /\.myshopify\.(com|io)$/
|
8
|
+
|
9
|
+
options do |parser, flags|
|
10
|
+
parser.on("--store=STORE") { |url| flags[:shop] = url }
|
11
|
+
# backwards compatibility allow 'shop' for now
|
12
|
+
parser.on("--shop=SHOP") { |url| flags[:shop] = url }
|
13
|
+
parser.on("--password=PASSWORD") { |password| flags[:password] = password }
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(*)
|
17
|
+
shop = (options.flags[:shop] || @ctx.getenv("SHOPIFY_SHOP" || nil))
|
18
|
+
ShopifyCli::DB.set(shop: self.class.validate_shop(shop)) unless shop.nil?
|
19
|
+
|
20
|
+
if shop.nil? && Shopifolk.check
|
21
|
+
Shopifolk.reset
|
22
|
+
@ctx.puts(@ctx.message("core.tasks.select_org_and_shop.identified_as_shopify"))
|
23
|
+
message = @ctx.message("core.tasks.select_org_and_shop.first_party")
|
24
|
+
if CLI::UI::Prompt.confirm(message, default: false)
|
25
|
+
Shopifolk.act_as_shopify_organization
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# As password auth will soon be deprecated, we enable only in CI
|
30
|
+
if @ctx.ci? && (password = options.flags[:password] || @ctx.getenv("SHOPIFY_PASSWORD"))
|
31
|
+
ShopifyCli::DB.set(shopify_exchange_token: password)
|
32
|
+
else
|
33
|
+
IdentityAuth.new(ctx: @ctx).authenticate
|
34
|
+
org = select_organization
|
35
|
+
ShopifyCli::DB.set(organization_id: org["id"].to_i) unless org.nil?
|
36
|
+
Whoami.call([], "whoami")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.help
|
41
|
+
ShopifyCli::Context.message("core.login.help", ShopifyCli::TOOL_NAME)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.validate_shop(shop)
|
45
|
+
permanent_domain = shop_to_permanent_domain(shop)
|
46
|
+
@ctx.abort(@ctx.message("core.login.invalid_shop", shop)) unless permanent_domain
|
47
|
+
permanent_domain
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.shop_to_permanent_domain(shop)
|
51
|
+
url = if PROTOCOL_REGEX =~ shop
|
52
|
+
shop
|
53
|
+
elsif shop.include?(".")
|
54
|
+
"https://#{shop}"
|
55
|
+
else
|
56
|
+
"https://#{shop}.myshopify.com"
|
57
|
+
end
|
58
|
+
|
59
|
+
# Make a request to see if it exists or if we get redirected to the permanent domain one
|
60
|
+
uri = URI.parse(url)
|
61
|
+
Net::HTTP.start(uri.host, use_ssl: true) do |http|
|
62
|
+
response = http.request_head("/admin")
|
63
|
+
case response
|
64
|
+
when Net::HTTPSuccess, Net::HTTPSeeOther
|
65
|
+
uri.host
|
66
|
+
when Net::HTTPFound
|
67
|
+
domain = URI.parse(response["location"]).host
|
68
|
+
if PERMANENT_DOMAIN_SUFFIX =~ domain
|
69
|
+
domain
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def select_organization
|
78
|
+
organizations = ShopifyCli::PartnersAPI::Organizations.fetch_all(@ctx)
|
79
|
+
|
80
|
+
if organizations.count == 0
|
81
|
+
nil
|
82
|
+
elsif organizations.count == 1
|
83
|
+
organizations.first
|
84
|
+
else
|
85
|
+
org_id = CLI::UI::Prompt.ask(@ctx.message("core.tasks.select_org_and_shop.organization_select")) do |handler|
|
86
|
+
organizations.each do |o|
|
87
|
+
handler.option(@ctx.message("core.partners_api.org_name_and_id", o["businessName"], o["id"])) { o["id"] }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
organizations.find { |o| o["id"] == org_id }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|