shopify-cli 0.9.2 → 1.0.3
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/CHANGELOG.md +24 -3
- data/docs/_config.yml +3 -0
- data/docs/_data/nav.yml +9 -0
- data/docs/getting-started/index.md +5 -40
- data/docs/getting-started/install/index.md +75 -0
- data/docs/getting-started/migrate/index.md +96 -0
- data/docs/getting-started/uninstall/index.md +37 -0
- data/docs/getting-started/upgrade/index.md +37 -0
- data/docs/index.md +5 -6
- data/lib/project_types/extension/cli.rb +3 -2
- data/lib/project_types/extension/commands/build.rb +1 -1
- data/lib/project_types/extension/commands/tunnel.rb +1 -1
- data/lib/project_types/extension/forms/register.rb +2 -3
- data/lib/project_types/extension/graphql/get_app_by_api_key.graphql +9 -0
- data/lib/project_types/extension/tasks/converters/app_converter.rb +27 -0
- data/lib/project_types/extension/tasks/get_app.rb +22 -0
- data/lib/project_types/extension/tasks/get_apps.rb +1 -6
- data/lib/project_types/node/forms/create.rb +3 -54
- data/lib/project_types/node/messages/messages.rb +3 -14
- data/lib/project_types/rails/cli.rb +0 -1
- data/lib/project_types/rails/forms/create.rb +3 -52
- data/lib/project_types/rails/messages/messages.rb +2 -13
- data/lib/project_types/script/cli.rb +5 -5
- data/lib/project_types/script/commands/create.rb +5 -4
- data/lib/project_types/script/commands/disable.rb +4 -14
- data/lib/project_types/script/commands/enable.rb +35 -11
- data/lib/project_types/script/commands/push.rb +9 -9
- data/lib/project_types/script/config/extension_points.yml +9 -3
- data/lib/project_types/script/errors.rb +1 -0
- data/lib/project_types/script/forms/create.rb +8 -4
- data/lib/project_types/script/forms/script_form.rb +5 -2
- data/lib/project_types/script/layers/application/create_script.rb +14 -20
- data/lib/project_types/script/layers/application/disable_script.rb +9 -7
- data/lib/project_types/script/layers/application/enable_script.rb +11 -9
- data/lib/project_types/script/layers/application/project_dependencies.rb +0 -5
- data/lib/project_types/script/layers/application/push_script.rb +6 -4
- data/lib/project_types/script/layers/infrastructure/assemblyscript_project_creator.rb +106 -0
- data/lib/project_types/script/layers/infrastructure/assemblyscript_task_runner.rb +2 -2
- data/lib/project_types/script/layers/infrastructure/errors.rb +2 -1
- data/lib/project_types/script/layers/infrastructure/project_creator.rb +23 -0
- data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +1 -1
- data/lib/project_types/script/layers/infrastructure/script_repository.rb +1 -34
- data/lib/project_types/script/layers/infrastructure/script_service.rb +2 -0
- data/lib/project_types/script/messages/messages.rb +21 -25
- data/lib/project_types/script/script_project.rb +8 -4
- data/lib/project_types/script/templates/ts/as-pect.config.js +6 -0
- data/lib/project_types/script/templates/ts/as-pect.d.ts +1 -0
- data/lib/project_types/script/ui/error_handler.rb +9 -0
- data/lib/project_types/script/ui/printing_spinner.rb +75 -0
- data/lib/shopify-cli/admin_api.rb +1 -2
- data/lib/shopify-cli/admin_api/populate_resource_command.rb +10 -1
- data/lib/shopify-cli/admin_api/schema.rb +20 -8
- data/lib/shopify-cli/command.rb +2 -5
- data/lib/shopify-cli/commands.rb +1 -0
- data/lib/shopify-cli/commands/config.rb +44 -0
- data/lib/shopify-cli/commands/connect.rb +17 -10
- data/lib/shopify-cli/commands/create.rb +1 -1
- data/lib/shopify-cli/commands/help.rb +1 -1
- data/lib/shopify-cli/commands/system.rb +1 -1
- data/lib/shopify-cli/context.rb +10 -1
- data/lib/shopify-cli/core.rb +0 -1
- data/lib/shopify-cli/core/entry_point.rb +6 -0
- data/lib/shopify-cli/core/finalize.rb +13 -0
- data/lib/shopify-cli/feature.rb +97 -0
- data/lib/shopify-cli/messages/messages.rb +45 -2
- data/lib/shopify-cli/partners_api/organizations.rb +7 -7
- data/lib/shopify-cli/project_type.rb +2 -5
- data/lib/shopify-cli/tasks.rb +1 -0
- data/lib/shopify-cli/tasks/ensure_env.rb +0 -1
- data/lib/shopify-cli/tasks/select_org_and_shop.rb +77 -0
- data/lib/shopify-cli/tasks/update_dashboard_urls.rb +4 -3
- data/lib/shopify-cli/tunnel.rb +66 -10
- data/lib/shopify-cli/version.rb +1 -1
- data/lib/shopify_cli.rb +1 -0
- data/vendor/deps/cli-ui/REVISION +1 -1
- data/vendor/deps/cli-ui/lib/cli/ui.rb +52 -11
- data/vendor/deps/cli-ui/lib/cli/ui/color.rb +11 -7
- data/vendor/deps/cli-ui/lib/cli/ui/formatter.rb +34 -21
- data/vendor/deps/cli-ui/lib/cli/ui/frame.rb +107 -149
- data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_stack.rb +99 -0
- data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style.rb +119 -0
- data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style/box.rb +158 -0
- data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style/bracket.rb +112 -0
- data/vendor/deps/cli-ui/lib/cli/ui/glyph.rb +9 -15
- data/vendor/deps/cli-ui/lib/cli/ui/printer.rb +47 -0
- data/vendor/deps/cli-ui/lib/cli/ui/progress.rb +9 -7
- data/vendor/deps/cli-ui/lib/cli/ui/prompt.rb +39 -14
- data/vendor/deps/cli-ui/lib/cli/ui/prompt/interactive_options.rb +62 -44
- data/vendor/deps/cli-ui/lib/cli/ui/prompt/options_handler.rb +7 -2
- data/vendor/deps/cli-ui/lib/cli/ui/spinner.rb +23 -3
- data/vendor/deps/cli-ui/lib/cli/ui/spinner/spin_group.rb +34 -10
- data/vendor/deps/cli-ui/lib/cli/ui/stdout_router.rb +12 -7
- data/vendor/deps/cli-ui/lib/cli/ui/terminal.rb +26 -16
- data/vendor/deps/cli-ui/lib/cli/ui/truncater.rb +3 -3
- data/vendor/deps/cli-ui/lib/cli/ui/widgets.rb +75 -0
- data/vendor/deps/cli-ui/lib/cli/ui/widgets/base.rb +27 -0
- data/vendor/deps/cli-ui/lib/cli/ui/widgets/status.rb +61 -0
- metadata +25 -9
- data/lib/project_types/extension/features/tunnel_url.rb +0 -20
- data/lib/project_types/script/forms/enable.rb +0 -24
- data/lib/project_types/script/forms/push.rb +0 -19
- data/lib/project_types/script/layers/infrastructure/assemblyscript_dependency_manager.rb +0 -51
- data/lib/project_types/script/layers/infrastructure/dependency_manager.rb +0 -36
- data/lib/project_types/script/layers/infrastructure/test_suite_repository.rb +0 -62
- data/vendor/deps/cli-ui/lib/cli/ui/box.rb +0 -15
data/lib/shopify-cli/context.rb
CHANGED
|
@@ -146,10 +146,19 @@ module ShopifyCli
|
|
|
146
146
|
# #### Parameters
|
|
147
147
|
# * `path` - the file path to a directory, relative to the context root to remove from the FS
|
|
148
148
|
#
|
|
149
|
-
def
|
|
149
|
+
def dir_exist?(path)
|
|
150
150
|
Dir.exist?(ctx_path(path))
|
|
151
151
|
end
|
|
152
152
|
|
|
153
|
+
# checks if a file exists, the filepath is relative to the command root unless absolute
|
|
154
|
+
#
|
|
155
|
+
# #### Parameters
|
|
156
|
+
# * `path` - the file path to a file, relative to the context root to remove from the FS
|
|
157
|
+
#
|
|
158
|
+
def file_exist?(path)
|
|
159
|
+
File.exist?(ctx_path(path))
|
|
160
|
+
end
|
|
161
|
+
|
|
153
162
|
# will recursively copy a directory from the FS, the filepath is relative to the command
|
|
154
163
|
# root unless absolute
|
|
155
164
|
#
|
data/lib/shopify-cli/core.rb
CHANGED
|
@@ -11,6 +11,12 @@ module ShopifyCli
|
|
|
11
11
|
IO.open(9) { is_shell_shim = true }
|
|
12
12
|
rescue Errno::EBADF
|
|
13
13
|
# This is expected if the descriptor doesn't exist
|
|
14
|
+
rescue ArgumentError => e
|
|
15
|
+
# This can happen on RVM, because it can use fd 9 itself and block access to it. That only happens if the fd
|
|
16
|
+
# did not exist beforehand, so that means there was no fd 9 before Ruby started.
|
|
17
|
+
unless e.message == 'The given fd is not accessible because RubyVM reserves it'
|
|
18
|
+
raise e
|
|
19
|
+
end
|
|
14
20
|
end
|
|
15
21
|
|
|
16
22
|
if !ctx.testing? && is_shell_shim
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module ShopifyCli
|
|
2
|
+
module Core
|
|
3
|
+
# This class is just a dummy to make sure that we don't trigger warnings on the first time the updated code runs.
|
|
4
|
+
# The old code would try to call the Finalizer after it is done updating, which would then trigger an autoload of
|
|
5
|
+
# this class and fail.
|
|
6
|
+
module Finalize
|
|
7
|
+
class << self
|
|
8
|
+
def deliver!
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
module ShopifyCli
|
|
2
|
+
##
|
|
3
|
+
# ShopifyCli::Feature contains the logic to hide and show features across the CLI
|
|
4
|
+
# These features can be either commands or project types currently.
|
|
5
|
+
#
|
|
6
|
+
# Feature flags will persist between runs so if the flag is enabled or disabled,
|
|
7
|
+
# it will still be in that same state on the next cli invocation.
|
|
8
|
+
class Feature
|
|
9
|
+
SECTION = 'features'
|
|
10
|
+
|
|
11
|
+
##
|
|
12
|
+
# ShopifyCli::Feature::Set is included on commands and projects to allow you to hide
|
|
13
|
+
# and enable projects and commands based on feature flags.
|
|
14
|
+
module Set
|
|
15
|
+
##
|
|
16
|
+
# will hide a feature, either a project_type or a command
|
|
17
|
+
#
|
|
18
|
+
# #### Parameters
|
|
19
|
+
#
|
|
20
|
+
# * `feature_set` - either a single, or array of symbols that represent feature sets
|
|
21
|
+
#
|
|
22
|
+
# #### Example
|
|
23
|
+
#
|
|
24
|
+
# module ShopifyCli
|
|
25
|
+
# module Commands
|
|
26
|
+
# class Config < ShopifyCli::Command
|
|
27
|
+
# hidden_feature(feature_set: :basic)
|
|
28
|
+
# ....
|
|
29
|
+
#
|
|
30
|
+
def hidden_feature(feature_set: [])
|
|
31
|
+
@feature_hidden = true
|
|
32
|
+
@hidden_feature_set = Array(feature_set).compact
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
##
|
|
36
|
+
# will return if the feature has been hidden or not
|
|
37
|
+
#
|
|
38
|
+
# #### Returns
|
|
39
|
+
#
|
|
40
|
+
# * `is_hidden` - returns true if the feature has been hidden and false otherwise
|
|
41
|
+
#
|
|
42
|
+
# #### Example
|
|
43
|
+
#
|
|
44
|
+
# ShopifyCli::Commands::Config.hidden?
|
|
45
|
+
#
|
|
46
|
+
def hidden?
|
|
47
|
+
enabled = (@hidden_feature_set || []).any? do |feature|
|
|
48
|
+
Feature.enabled?(feature)
|
|
49
|
+
end
|
|
50
|
+
@feature_hidden && !enabled
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
class << self
|
|
55
|
+
##
|
|
56
|
+
# will enable a feature in the CLI.
|
|
57
|
+
#
|
|
58
|
+
# #### Parameters
|
|
59
|
+
#
|
|
60
|
+
# * `feature` - a symbol representing the flag to be enabled
|
|
61
|
+
def enable(feature)
|
|
62
|
+
set(feature, true)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
##
|
|
66
|
+
# will disable a feature in the CLI.
|
|
67
|
+
#
|
|
68
|
+
# #### Parameters
|
|
69
|
+
#
|
|
70
|
+
# * `feature` - a symbol representing the flag to be disabled
|
|
71
|
+
def disable(feature)
|
|
72
|
+
set(feature, false)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
##
|
|
76
|
+
# will check if the feature has been enabled
|
|
77
|
+
#
|
|
78
|
+
# #### Parameters
|
|
79
|
+
#
|
|
80
|
+
# * `feature` - a symbol representing a flag that the status should be requested
|
|
81
|
+
#
|
|
82
|
+
# #### Returns
|
|
83
|
+
#
|
|
84
|
+
# * `is_enabled` - will be true if the feature has been enabled.
|
|
85
|
+
def enabled?(feature)
|
|
86
|
+
return false if feature.nil?
|
|
87
|
+
ShopifyCli::Config.get_bool(SECTION, feature.to_s)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def set(feature, value)
|
|
93
|
+
ShopifyCli::Config.set(SECTION, feature.to_s, value)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -24,6 +24,15 @@ module ShopifyCli
|
|
|
24
24
|
MESSAGE
|
|
25
25
|
development_store_select: "Which development store would you like to use?",
|
|
26
26
|
cli_yml_saved: ".shopify-cli.yml saved to project root",
|
|
27
|
+
|
|
28
|
+
no_apps: 'You have no apps to connect to, creating a new app.',
|
|
29
|
+
app_name: "App name",
|
|
30
|
+
app_type: {
|
|
31
|
+
select: "What type of app are you building?",
|
|
32
|
+
select_public: "Public: An app built for a wide merchant audience.",
|
|
33
|
+
select_custom: "Custom: An app custom built for a single client.",
|
|
34
|
+
selected: "App type {{green:%s}}",
|
|
35
|
+
},
|
|
27
36
|
},
|
|
28
37
|
|
|
29
38
|
context: {
|
|
@@ -52,6 +61,19 @@ module ShopifyCli
|
|
|
52
61
|
saved: "%s saved to project root",
|
|
53
62
|
},
|
|
54
63
|
|
|
64
|
+
config: {
|
|
65
|
+
help: <<~HELP,
|
|
66
|
+
Change configuration of how the CLI operates
|
|
67
|
+
Usage: {{command:%s config [ feature ] [ feature_name ] }}
|
|
68
|
+
HELP
|
|
69
|
+
feature: {
|
|
70
|
+
enabled: "{{v}} feature {{green:%s}} was enabled",
|
|
71
|
+
disabled: "{{v}} feature {{green:%s}} was disabled",
|
|
72
|
+
is_enabled: "{{v}} feature {{green:%s}} is enabled",
|
|
73
|
+
is_disabled: "{{v}} feature {{green:%s}} is disabled",
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
|
|
55
77
|
git: {
|
|
56
78
|
error: {
|
|
57
79
|
directory_exists: "Project directory already exists. Please create a project with a new name.",
|
|
@@ -251,6 +273,20 @@ module ShopifyCli
|
|
|
251
273
|
"{{x}} error: For authentication issues, run {{command:%s logout}} to clear invalid credentials",
|
|
252
274
|
update_prompt: "Do you want to update your application url?",
|
|
253
275
|
},
|
|
276
|
+
select_org_and_shop: {
|
|
277
|
+
authentication_issue: "For authentication issues, run {{command:%s logout}} to clear invalid credentials",
|
|
278
|
+
create_store: "Visit {{underline:https://partners.shopify.com/%s/stores}} to create one",
|
|
279
|
+
development_store: "Using development store {{green:%s}}",
|
|
280
|
+
development_store_select: "Select a development store",
|
|
281
|
+
error: {
|
|
282
|
+
no_development_stores: "{{x}} No Development Stores available.",
|
|
283
|
+
no_organizations: "No partner organizations available.",
|
|
284
|
+
organization_not_found: "Cannot find a partner organization with that ID",
|
|
285
|
+
partners_notice: "Please visit https://partners.shopify.com/ to create a partners account",
|
|
286
|
+
},
|
|
287
|
+
organization: "Partner organization {{green:%s (%s)}}",
|
|
288
|
+
organization_select: "Select partner organization",
|
|
289
|
+
},
|
|
254
290
|
},
|
|
255
291
|
|
|
256
292
|
tunnel: {
|
|
@@ -259,10 +295,17 @@ module ShopifyCli
|
|
|
259
295
|
url_fetch_failure: "Unable to fetch external url",
|
|
260
296
|
},
|
|
261
297
|
|
|
262
|
-
stopped: "{{green:x}} ngrok tunnel stopped",
|
|
263
298
|
not_running: "{{green:x}} ngrok tunnel not running",
|
|
264
|
-
|
|
299
|
+
signup_suggestion: <<~MESSAGE,
|
|
300
|
+
{{*}} To avoid tunnels that timeout, it is recommended to signup for a free ngrok
|
|
301
|
+
account at {{underline:https://ngrok.com/signup}}. After you signup, install your
|
|
302
|
+
personalized authorization token using {{command:%s tunnel auth <token>}}.
|
|
303
|
+
MESSAGE
|
|
265
304
|
start: "{{v}} ngrok tunnel running at {{underline:%s}}",
|
|
305
|
+
start_with_account: "{{v}} ngrok tunnel running at {{underline:%s}}, with account %s",
|
|
306
|
+
stopped: "{{green:x}} ngrok tunnel stopped",
|
|
307
|
+
timed_out: "{{x}} ngrok tunnel has timed out, restarting ...",
|
|
308
|
+
will_timeout: "{{*}} This tunnel will timeout in {{red:%s}}",
|
|
266
309
|
},
|
|
267
310
|
|
|
268
311
|
version: {
|
|
@@ -4,25 +4,25 @@ module ShopifyCli
|
|
|
4
4
|
class << self
|
|
5
5
|
def fetch_all(ctx)
|
|
6
6
|
resp = PartnersAPI.query(ctx, 'all_organizations')
|
|
7
|
-
resp
|
|
8
|
-
org['stores'] = org
|
|
7
|
+
(resp.dig('data', 'organizations', 'nodes') || []).map do |org|
|
|
8
|
+
org['stores'] = (org.dig('stores', 'nodes') || [])
|
|
9
9
|
org
|
|
10
10
|
end
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def fetch(ctx, id:)
|
|
14
14
|
resp = PartnersAPI.query(ctx, 'find_organization', id: id)
|
|
15
|
-
org = resp
|
|
15
|
+
org = resp.dig('data', 'organizations', 'nodes').first
|
|
16
16
|
return nil if org.nil?
|
|
17
|
-
org['stores'] = org
|
|
17
|
+
org['stores'] = (org.dig('stores', 'nodes') || [])
|
|
18
18
|
org
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def fetch_with_app(ctx)
|
|
22
22
|
resp = PartnersAPI.query(ctx, 'all_orgs_with_apps')
|
|
23
|
-
resp
|
|
24
|
-
org['stores'] = org
|
|
25
|
-
org['apps'] = org
|
|
23
|
+
(resp.dig('data', 'organizations', 'nodes') || []).map do |org|
|
|
24
|
+
org['stores'] = (org.dig('stores', 'nodes') || [])
|
|
25
|
+
org['apps'] = (org.dig('apps', 'nodes') || [])
|
|
26
26
|
org
|
|
27
27
|
end
|
|
28
28
|
end
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
module ShopifyCli
|
|
2
2
|
class ProjectType
|
|
3
|
+
extend Feature::Set
|
|
4
|
+
|
|
3
5
|
class << self
|
|
4
6
|
attr_accessor :project_type,
|
|
5
7
|
:project_name,
|
|
6
8
|
:project_creator_command_class,
|
|
7
9
|
:project_load_shallow
|
|
8
|
-
attr_reader :hidden
|
|
9
10
|
|
|
10
11
|
def repository
|
|
11
12
|
@repository ||= []
|
|
@@ -53,10 +54,6 @@ module ShopifyCli
|
|
|
53
54
|
const_get(@project_creator_command_class)
|
|
54
55
|
end
|
|
55
56
|
|
|
56
|
-
def hidden_project_type
|
|
57
|
-
@hidden = true
|
|
58
|
-
end
|
|
59
|
-
|
|
60
57
|
def register_command(const, cmd)
|
|
61
58
|
return if project_load_shallow
|
|
62
59
|
Context.new.abort(
|
data/lib/shopify-cli/tasks.rb
CHANGED
|
@@ -27,6 +27,7 @@ module ShopifyCli
|
|
|
27
27
|
register :EnsureEnv, :ensure_env, 'shopify-cli/tasks/ensure_env'
|
|
28
28
|
register :EnsureLoopbackURL, :ensure_loopback_url, 'shopify-cli/tasks/ensure_loopback_url'
|
|
29
29
|
register :EnsureDevStore, :ensure_dev_store, 'shopify-cli/tasks/ensure_dev_store'
|
|
30
|
+
register :SelectOrgAndShop, :select_org_and_shop, 'shopify-cli/tasks/select_org_and_shop'
|
|
30
31
|
register :UpdateDashboardURLS, :update_dashboard_urls, 'shopify-cli/tasks/update_dashboard_urls'
|
|
31
32
|
end
|
|
32
33
|
end
|
|
@@ -14,7 +14,6 @@ module ShopifyCli
|
|
|
14
14
|
api_key = CLI::UI.ask(@ctx.message('core.tasks.ensure_env.api_key_question'))
|
|
15
15
|
api_secret = CLI::UI.ask(@ctx.message('core.tasks.ensure_env.api_secret_key_question'))
|
|
16
16
|
shop = CLI::UI.ask(@ctx.message('core.tasks.ensure_env.development_store_question'))
|
|
17
|
-
|
|
18
17
|
shop.gsub!(/https?\:\/\//, '')
|
|
19
18
|
|
|
20
19
|
env = Resources::EnvFile.new(
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
require 'shopify_cli'
|
|
2
|
+
|
|
3
|
+
module ShopifyCli
|
|
4
|
+
module Tasks
|
|
5
|
+
class SelectOrgAndShop < ShopifyCli::Task
|
|
6
|
+
attr_reader :ctx
|
|
7
|
+
|
|
8
|
+
def call(ctx, organization_id: nil, shop_domain: nil)
|
|
9
|
+
@ctx = ctx
|
|
10
|
+
return response(organization_id.to_i, shop_domain) unless organization_id.nil? || shop_domain.nil?
|
|
11
|
+
org = get_organization(organization_id)
|
|
12
|
+
shop_domain ||= get_shop_domain(org)
|
|
13
|
+
response(org["id"].to_i, shop_domain)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def response(organization_id, shop_domain)
|
|
19
|
+
{
|
|
20
|
+
organization_id: organization_id,
|
|
21
|
+
shop_domain: shop_domain,
|
|
22
|
+
}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def organizations
|
|
26
|
+
@organizations ||= ShopifyCli::PartnersAPI::Organizations.fetch_all(ctx)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def get_organization(organization_id)
|
|
30
|
+
@organization ||= if !organization_id.nil?
|
|
31
|
+
org = ShopifyCli::PartnersAPI::Organizations.fetch(ctx, id: organization_id)
|
|
32
|
+
if org.nil?
|
|
33
|
+
ctx.puts(ctx.message('core.tasks.select_org_and_shop.error.authentication_issue', ShopifyCli::TOOL_NAME))
|
|
34
|
+
ctx.abort(ctx.message('core.tasks.select_org_and_shop.error.organization_not_found'))
|
|
35
|
+
end
|
|
36
|
+
org
|
|
37
|
+
elsif organizations.count == 0
|
|
38
|
+
ctx.puts(ctx.message('core.tasks.select_org_and_shop.error.partners_notice'))
|
|
39
|
+
ctx.puts(ctx.message('core.tasks.select_org_and_shop.authentication_issue', ShopifyCli::TOOL_NAME))
|
|
40
|
+
ctx.abort(ctx.message('core.tasks.select_org_and_shop.error.no_organizations'))
|
|
41
|
+
elsif organizations.count == 1
|
|
42
|
+
org = organizations.first
|
|
43
|
+
ctx.puts(ctx.message('core.tasks.select_org_and_shop.organization', org['businessName'], org['id']))
|
|
44
|
+
org
|
|
45
|
+
else
|
|
46
|
+
org_id = CLI::UI::Prompt.ask(ctx.message('core.tasks.select_org_and_shop.organization_select')) do |handler|
|
|
47
|
+
organizations.each do |o|
|
|
48
|
+
handler.option(ctx.message('core.partners_api.org_name_and_id', o['businessName'], o['id'])) { o['id'] }
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
organizations.find { |o| o['id'] == org_id }
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def get_shop_domain(organization)
|
|
56
|
+
valid_stores = organization['stores'].select do |store|
|
|
57
|
+
store['transferDisabled'] == true || store['convertableToPartnerTest'] == true
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
if valid_stores.count == 0
|
|
61
|
+
ctx.puts(ctx.message('core.tasks.select_org_and_shop.error.no_development_stores'))
|
|
62
|
+
ctx.puts(ctx.message('core.tasks.select_org_and_shop.create_store', organization['id']))
|
|
63
|
+
ctx.puts(ctx.message('core.tasks.select_org_and_shop.authentication_issue', ShopifyCli::TOOL_NAME))
|
|
64
|
+
elsif valid_stores.count == 1
|
|
65
|
+
domain = valid_stores.first['shopDomain']
|
|
66
|
+
ctx.puts(ctx.message('core.tasks.select_org_and_shop.development_store', domain))
|
|
67
|
+
domain
|
|
68
|
+
else
|
|
69
|
+
CLI::UI::Prompt.ask(
|
|
70
|
+
ctx.message('core.tasks.select_org_and_shop.development_store_select'),
|
|
71
|
+
options: valid_stores.map { |s| s['shopDomain'] }
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -28,16 +28,17 @@ module ShopifyCli
|
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
def construct_redirect_urls(urls, new_url, callback_url)
|
|
31
|
-
urls.map do |url|
|
|
31
|
+
new_urls = urls.map do |url|
|
|
32
32
|
if (match = url.match(NGROK_REGEX))
|
|
33
33
|
"#{new_url}#{match[2]}"
|
|
34
34
|
else
|
|
35
35
|
url
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
|
-
if
|
|
39
|
-
|
|
38
|
+
if new_urls.grep(/#{new_url}#{callback_url}/).empty?
|
|
39
|
+
new_urls.push("#{new_url}#{callback_url}")
|
|
40
40
|
end
|
|
41
|
+
new_urls.uniq
|
|
41
42
|
end
|
|
42
43
|
end
|
|
43
44
|
end
|
data/lib/shopify-cli/tunnel.rb
CHANGED
|
@@ -11,7 +11,7 @@ module ShopifyCli
|
|
|
11
11
|
class Tunnel
|
|
12
12
|
extend SingleForwardable
|
|
13
13
|
|
|
14
|
-
def_delegators :new, :start, :stop, :auth
|
|
14
|
+
def_delegators :new, :start, :stop, :auth, :stats, :urls
|
|
15
15
|
|
|
16
16
|
class FetchUrlError < RuntimeError; end
|
|
17
17
|
class NgrokError < RuntimeError; end
|
|
@@ -23,6 +23,10 @@ module ShopifyCli
|
|
|
23
23
|
linux: 'https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip',
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
NGROK_TUNNELS_URI = URI.parse('http://localhost:4040/api/tunnels')
|
|
27
|
+
TUNNELS_FIELD = 'tunnels'
|
|
28
|
+
PUBLIC_URL_FIELD = 'public_url'
|
|
29
|
+
|
|
26
30
|
##
|
|
27
31
|
# will find and stop a running tunnel process. It will also output if the
|
|
28
32
|
# operation was successful or not
|
|
@@ -58,14 +62,19 @@ module ShopifyCli
|
|
|
58
62
|
#
|
|
59
63
|
def start(ctx, port: PORT)
|
|
60
64
|
install(ctx)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
ctx.puts(ctx.message('core.tunnel.start_with_account', log.url, log.account))
|
|
65
|
+
url, account, seconds_remaining = start_ngrok(ctx, port)
|
|
66
|
+
if account
|
|
67
|
+
ctx.puts(ctx.message('core.tunnel.start_with_account', url, account))
|
|
65
68
|
else
|
|
66
|
-
|
|
69
|
+
if seconds_remaining <= 0
|
|
70
|
+
ctx.puts(ctx.message('core.tunnel.timed_out'))
|
|
71
|
+
url, _account, seconds_remaining = restart_ngrok(ctx, port)
|
|
72
|
+
end
|
|
73
|
+
ctx.puts(ctx.message('core.tunnel.start', url))
|
|
74
|
+
ctx.puts(ctx.message('core.tunnel.will_timeout', seconds_to_hm(seconds_remaining)))
|
|
75
|
+
ctx.puts(ctx.message('core.tunnel.signup_suggestion', ShopifyCli::TOOL_NAME))
|
|
67
76
|
end
|
|
68
|
-
|
|
77
|
+
url
|
|
69
78
|
end
|
|
70
79
|
|
|
71
80
|
##
|
|
@@ -82,6 +91,34 @@ module ShopifyCli
|
|
|
82
91
|
ctx.system(File.join(ShopifyCli::CACHE_DIR, 'ngrok'), 'authtoken', token)
|
|
83
92
|
end
|
|
84
93
|
|
|
94
|
+
##
|
|
95
|
+
# will return the statistics of the current running tunnels
|
|
96
|
+
#
|
|
97
|
+
# #### Returns
|
|
98
|
+
#
|
|
99
|
+
# * `stats` - the hash of running statistics returning from the ngrok api
|
|
100
|
+
#
|
|
101
|
+
def stats
|
|
102
|
+
response = Net::HTTP.get_response(NGROK_TUNNELS_URI)
|
|
103
|
+
JSON.parse(response.body)
|
|
104
|
+
rescue
|
|
105
|
+
{}
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
##
|
|
109
|
+
# will return the urls of the current running tunnels
|
|
110
|
+
#
|
|
111
|
+
# #### Returns
|
|
112
|
+
#
|
|
113
|
+
# * `stats` - the array of urls
|
|
114
|
+
#
|
|
115
|
+
def urls
|
|
116
|
+
tunnels = stats.dig(TUNNELS_FIELD)
|
|
117
|
+
tunnels.map { |tunnel| tunnel.dig(PUBLIC_URL_FIELD) }
|
|
118
|
+
rescue
|
|
119
|
+
[]
|
|
120
|
+
end
|
|
121
|
+
|
|
85
122
|
private
|
|
86
123
|
|
|
87
124
|
def install(ctx)
|
|
@@ -106,13 +143,30 @@ module ShopifyCli
|
|
|
106
143
|
end
|
|
107
144
|
|
|
108
145
|
def ngrok_command(port)
|
|
109
|
-
"exec #{File.join(ShopifyCli::CACHE_DIR, 'ngrok')} http -log=stdout -log-level=debug #{port}"
|
|
146
|
+
"exec #{File.join(ShopifyCli::CACHE_DIR, 'ngrok')} http -inspect=false -log=stdout -log-level=debug #{port}"
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def seconds_to_hm(seconds)
|
|
150
|
+
format("%d hours %d minutes", seconds / 3600, seconds / 60 % 60)
|
|
110
151
|
end
|
|
111
152
|
|
|
153
|
+
def start_ngrok(ctx, port)
|
|
154
|
+
process = ShopifyCli::ProcessSupervision.start(:ngrok, ngrok_command(port))
|
|
155
|
+
log = fetch_url(ctx, process.log_path)
|
|
156
|
+
seconds_remaining = (process.time.to_i + log.timeout) - Time.now.to_i
|
|
157
|
+
[log.url, log.account, seconds_remaining]
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def restart_ngrok(ctx, port)
|
|
161
|
+
unless ShopifyCli::ProcessSupervision.stop(:ngrok)
|
|
162
|
+
ctx.abort(ctx.message('core.tunnel.error.stop'))
|
|
163
|
+
end
|
|
164
|
+
start_ngrok(ctx, port)
|
|
165
|
+
end
|
|
112
166
|
class LogParser # :nodoc:
|
|
113
167
|
TIMEOUT = 10
|
|
114
168
|
|
|
115
|
-
attr_reader :url, :account
|
|
169
|
+
attr_reader :url, :account, :timeout
|
|
116
170
|
|
|
117
171
|
def initialize(log_path)
|
|
118
172
|
@log_path = log_path
|
|
@@ -141,7 +195,9 @@ module ShopifyCli
|
|
|
141
195
|
end
|
|
142
196
|
|
|
143
197
|
def parse_account
|
|
144
|
-
|
|
198
|
+
account, timeout, _ = @log.match(/AccountName:([\w\s]*) SessionDuration:([\d]+) PlanName/)&.captures
|
|
199
|
+
@account = account&.empty? ? nil : account
|
|
200
|
+
@timeout = timeout&.empty? ? 0 : timeout.to_i
|
|
145
201
|
end
|
|
146
202
|
|
|
147
203
|
def error
|