shopify-cli 1.11.0 → 2.0.0
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 +48 -20
- data/Gemfile +4 -0
- data/Gemfile.lock +32 -0
- data/LICENSE +4 -1
- data/README.md +92 -26
- data/RELEASING.md +29 -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 -38
- data/lib/project_types/extension/features/argo_serve.rb +25 -20
- 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 +21 -6
- data/lib/project_types/extension/models/specification_handlers/theme_app_extension.rb +86 -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 +20 -14
- 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 +15 -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/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 +7 -17
- 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 +110 -106
- 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 +10 -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 +95 -41
- data/.github/workflows/release.yml +0 -61
- data/lib/project_types/extension/features/argo_serve_options.rb +0 -40
- 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,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Extension
|
4
|
+
module Forms
|
5
|
+
class Connect < ShopifyCli::Form
|
6
|
+
attr_reader :registration, :app
|
7
|
+
|
8
|
+
flag_arguments :type
|
9
|
+
|
10
|
+
class ExtensionProjectDetails
|
11
|
+
include SmartProperties
|
12
|
+
|
13
|
+
property :registration, accepts: Models::Registration
|
14
|
+
property :app, accepts: Models::App
|
15
|
+
|
16
|
+
def complete?
|
17
|
+
!!(registration && app)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def ask
|
22
|
+
ShopifyCli::Result.wrap(ExtensionProjectDetails.new)
|
23
|
+
.then(&Questions::AskRegistration.new(ctx: ctx, type: type))
|
24
|
+
.unwrap { |e| raise e }
|
25
|
+
.tap do |project_details|
|
26
|
+
ctx.abort(ctx.message("connect.incomplete_configuration")) unless project_details.complete?
|
27
|
+
|
28
|
+
self.registration = project_details.registration
|
29
|
+
self.app = project_details.app
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def directory_name
|
34
|
+
name.strip.gsub(/( )/, "_").downcase
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
attr_writer :registration, :app
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -11,17 +11,25 @@ module Extension
|
|
11
11
|
default: -> { CLI::UI::Prompt.method(:ask) }
|
12
12
|
|
13
13
|
def call(project_details)
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
if theme_app_extension?(project_details)
|
15
|
+
project_details.name = name || "theme-app-extension"
|
16
|
+
else
|
17
|
+
project_details.name = ask_with_reprompt(
|
18
|
+
initial_value: name,
|
19
|
+
break_condition: -> (current_name) { Models::Registration.valid_title?(current_name) },
|
20
|
+
prompt_message: ctx.message("create.ask_name"),
|
21
|
+
reprompt_message: ctx.message("create.invalid_name", Models::Registration::MAX_TITLE_LENGTH)
|
22
|
+
)
|
23
|
+
end
|
20
24
|
project_details
|
21
25
|
end
|
22
26
|
|
23
27
|
private
|
24
28
|
|
29
|
+
def theme_app_extension?(project_details)
|
30
|
+
project_details&.type&.identifier == "THEME_APP_EXTENSION"
|
31
|
+
end
|
32
|
+
|
25
33
|
def ask_with_reprompt(initial_value:, break_condition:, prompt_message:, reprompt_message:)
|
26
34
|
value = initial_value
|
27
35
|
reprompt = false
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Extension
|
4
|
+
module Forms
|
5
|
+
module Questions
|
6
|
+
class AskRegistration
|
7
|
+
include ShopifyCli::MethodObject
|
8
|
+
|
9
|
+
property! :ctx
|
10
|
+
property! :type
|
11
|
+
property! :prompt,
|
12
|
+
converts: :to_proc,
|
13
|
+
default: -> { CLI::UI::Prompt.method(:ask) }
|
14
|
+
|
15
|
+
def call(project_details)
|
16
|
+
project_details.tap(&method(:prompt_for_registration))
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def prompt_for_registration(project_details)
|
22
|
+
apps_and_registrations = load_registrations(type)
|
23
|
+
app, registration = choose_interactively(apps_and_registrations)
|
24
|
+
project_details.app = app
|
25
|
+
project_details.registration = registration
|
26
|
+
end
|
27
|
+
|
28
|
+
def choose_interactively(apps_and_registrations)
|
29
|
+
prompt.call(ctx.message("connect.ask_registration")) do |handler|
|
30
|
+
apps_and_registrations.each do |(app, extension)|
|
31
|
+
handler.option("#{app.title} by #{app.business_name}: #{extension.title}") { [app, extension] }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def load_registrations(type)
|
37
|
+
ctx.puts(@ctx.message("connect.loading_extensions"))
|
38
|
+
registrations = Tasks::GetExtensions.call(context: ctx, type: type)
|
39
|
+
|
40
|
+
registrations.empty? ? abort_no_registrations : registrations
|
41
|
+
end
|
42
|
+
|
43
|
+
def abort_no_registrations
|
44
|
+
ctx.puts(@ctx.message("connect.no_extensions", type))
|
45
|
+
ctx.puts(@ctx.message("connect.learn_about_extensions"))
|
46
|
+
raise ShopifyCli::AbortSilent
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -4,7 +4,21 @@ require "shopify_cli"
|
|
4
4
|
module Extension
|
5
5
|
module Messages
|
6
6
|
MESSAGES = {
|
7
|
+
extension: {
|
8
|
+
help: <<~HELP,
|
9
|
+
Suite of commands for developing app extensions. See {{command:%1$s extension <command> --help}} for usage of each command.
|
10
|
+
Usage: {{command:%1$s extension [ %2$s ]}}
|
11
|
+
HELP
|
12
|
+
},
|
7
13
|
create: {
|
14
|
+
help: <<~HELP,
|
15
|
+
Create a new app extension.
|
16
|
+
Usage: {{command:%s extension create <name>}}
|
17
|
+
Options:
|
18
|
+
{{command:--type=TYPE}} The type of extension you would like to create.
|
19
|
+
{{command:--name=NAME}} The name of your extension (50 characters).
|
20
|
+
{{command:--api-key=KEY}} The API key of your app.
|
21
|
+
HELP
|
8
22
|
ask_name: "Extension name",
|
9
23
|
invalid_name: "Extension name must be under %s characters",
|
10
24
|
ask_type: "What type of extension are you creating?",
|
@@ -13,13 +27,13 @@ module Extension
|
|
13
27
|
ready_to_start: <<~MESSAGE,
|
14
28
|
{{v}} A new folder was generated at {{green:./%s}}.
|
15
29
|
{{*}} You’re ready to start building {{green:%s}}!
|
16
|
-
Navigate to the new folder, then run {{command:shopify serve}} to start a local server.
|
30
|
+
Navigate to the new folder, then run {{command:shopify extension serve}} to start a local server.
|
17
31
|
MESSAGE
|
18
32
|
learn_more: <<~MESSAGE,
|
19
33
|
{{*}} Once you're ready to version and publish your extension,
|
20
|
-
run {{command:shopify register}} to register this extension with one of your apps.
|
34
|
+
run {{command:shopify extension register}} to register this extension with one of your apps.
|
21
35
|
MESSAGE
|
22
|
-
try_again: "{{*}} Fix the errors and run {{command:shopify create
|
36
|
+
try_again: "{{*}} Fix the errors and run {{command:shopify extension create}} again.",
|
23
37
|
errors: {
|
24
38
|
directory_exists: "Directory ‘%s’ already exists. Please remove it or choose a new name for your project.",
|
25
39
|
},
|
@@ -28,15 +42,39 @@ module Extension
|
|
28
42
|
ask_app: "Which app would you like to register this extension with?",
|
29
43
|
no_apps: "{{x}} You don’t have any apps.",
|
30
44
|
learn_about_apps: "{{*}} Learn more about building apps at <https://shopify.dev/concepts/apps>, " \
|
31
|
-
"or try creating a new app using {{command:shopify create}}.",
|
45
|
+
"or try creating a new app using {{command:shopify [node|rails] create}}.",
|
32
46
|
loading_apps: "Loading your apps...",
|
33
47
|
no_available_extensions: "{{x}} There are no available extensions for this app.",
|
34
48
|
},
|
49
|
+
connect: {
|
50
|
+
connected: "Project now connected to {{green:%s: %s}}",
|
51
|
+
incomplete_configuration: "Cannot connect extension due to missing configuration information",
|
52
|
+
invalid_api_key: "The API key %s does not match any of your apps.",
|
53
|
+
ask_registration: "Which extension would you like to connect to?",
|
54
|
+
loading_extensions: "Loading your extensions...",
|
55
|
+
no_extensions: "{{x}} You don't have any extensions of type %s",
|
56
|
+
learn_about_extensions: "{{*}} Learn more about building extensions at <https://shopify.dev/concepts/apps>, " \
|
57
|
+
"or try creating a new extension using {{command:shopify extension create}}.",
|
58
|
+
help: <<~HELP,
|
59
|
+
{{command:%s extension connect}}: Connects an existing extension to Shopify CLI. Creates a config file.
|
60
|
+
Usage: {{command:%s extension connect}}
|
61
|
+
HELP
|
62
|
+
},
|
35
63
|
build: {
|
64
|
+
help: <<~HELP,
|
65
|
+
Build your extension to prepare for deployment.
|
66
|
+
Usage: {{command:%s extension build}}
|
67
|
+
HELP
|
36
68
|
frame_title: "Building extension with: %s...",
|
37
69
|
build_failure_message: "Failed to build extension code.",
|
38
70
|
},
|
39
71
|
register: {
|
72
|
+
help: <<~HELP,
|
73
|
+
Register your local extension to a Shopify app
|
74
|
+
Usage: {{command:%s extension register}}
|
75
|
+
Options:
|
76
|
+
{{command:--api-key=API_KEY}} The API key used to register an app with the extension. This can be found on the app page on Partners Dashboard.
|
77
|
+
HELP
|
40
78
|
frame_title: "Registering Extension",
|
41
79
|
waiting_text: "Registering with Shopify...",
|
42
80
|
already_registered: "Extension is already registered.",
|
@@ -44,17 +82,28 @@ module Extension
|
|
44
82
|
confirm_question: "Would you like to register this extension? (y/n)",
|
45
83
|
confirm_abort: "Extension was not registered.",
|
46
84
|
success: "{{v}} Registered {{green:%s}}.",
|
47
|
-
success_info: "{{*}} Run {{command:shopify push}} to push your extension to Shopify.",
|
85
|
+
success_info: "{{*}} Run {{command:shopify extension push}} to push your extension to Shopify.",
|
48
86
|
},
|
49
87
|
push: {
|
88
|
+
help: <<~HELP,
|
89
|
+
Push the current extension to Shopify.
|
90
|
+
Usage: {{command:%s extension push}}
|
91
|
+
HELP
|
50
92
|
frame_title: "Pushing your extension to Shopify",
|
51
93
|
waiting_text: "Pushing code to Shopify...",
|
52
94
|
pushed_with_errors: "{{x}} Code pushed to Shopify with errors on %s.",
|
53
|
-
push_with_errors_info: "{{*}} Fix these errors and run {{command:shopify push}} to
|
95
|
+
push_with_errors_info: "{{*}} Fix these errors and run {{command:shopify extension push}} to " \
|
96
|
+
"revalidate your extension.",
|
54
97
|
success_confirmation: "{{v}} Pushed {{green:%s}} to a draft on %s.",
|
55
98
|
success_info: "{{*}} Visit %s to version and publish your extension.",
|
56
99
|
},
|
57
100
|
serve: {
|
101
|
+
help: <<~HELP,
|
102
|
+
Serve your extension in a local simulator for development.
|
103
|
+
Usage: {{command:%s extension serve}}
|
104
|
+
Options:
|
105
|
+
{{command:--tunnel=TUNNEL}} Establish an ngrok tunnel (default: false)
|
106
|
+
HELP
|
58
107
|
frame_title: "Serving extension...",
|
59
108
|
no_available_ports_found: "No available ports found to run extension.",
|
60
109
|
serve_failure_message: "Failed to run extension code.",
|
@@ -69,25 +118,25 @@ module Extension
|
|
69
118
|
tunnel_running_at: "Tunnel running at: {{underline:%s}}",
|
70
119
|
help: <<~HELP,
|
71
120
|
Start or stop an http tunnel to your local development extension using ngrok.
|
72
|
-
Usage: {{command:%s tunnel [ auth | start | stop | status ]}}
|
121
|
+
Usage: {{command:%s extension tunnel [ auth | start | stop | status ]}}
|
73
122
|
HELP
|
74
123
|
extended_help: <<~HELP,
|
75
124
|
{{bold:Subcommands:}}
|
76
125
|
|
77
126
|
{{cyan:auth}}: Writes an ngrok auth token to ~/.ngrok2/ngrok.yml to connect with an ngrok account.
|
78
127
|
Visit https://dashboard.ngrok.com/signup to sign up.
|
79
|
-
Usage: {{command:%1$s tunnel auth <token>}}
|
128
|
+
Usage: {{command:%1$s extension tunnel auth <token>}}
|
80
129
|
|
81
130
|
{{cyan:start}}: Starts an ngrok tunnel, will print the URL for an existing tunnel if already running.
|
82
|
-
Usage: {{command:%1$s tunnel start}}
|
131
|
+
Usage: {{command:%1$s extension tunnel start}}
|
83
132
|
Options:
|
84
133
|
{{command:--port=PORT}} Forward the ngrok subdomain to local port PORT. Defaults to %2$s.
|
85
134
|
|
86
135
|
{{cyan:stop}}: Stops the ngrok tunnel.
|
87
|
-
Usage: {{command:%1$s tunnel stop}}
|
136
|
+
Usage: {{command:%1$s extension tunnel stop}}
|
88
137
|
|
89
138
|
{{cyan:status}}: Output the current status of the ngrok tunnel.
|
90
|
-
Usage: {{command:%1$s tunnel status}}
|
139
|
+
Usage: {{command:%1$s extension tunnel status}}
|
91
140
|
HELP
|
92
141
|
},
|
93
142
|
features: {
|
@@ -134,6 +183,21 @@ module Extension
|
|
134
183
|
checkout_post_purchase: {
|
135
184
|
name: "Checkout Post Purchase",
|
136
185
|
},
|
186
|
+
theme_app_extension: {
|
187
|
+
name: "Theme App Extension",
|
188
|
+
tagline: "(limit 1 per app)",
|
189
|
+
overrides: {
|
190
|
+
register: {
|
191
|
+
confirm_info: "You can only create one %s extension per app, which can’t be undone.",
|
192
|
+
},
|
193
|
+
create: {
|
194
|
+
ready_to_start: <<~MESSAGE,
|
195
|
+
{{v}} A new folder was generated at {{green:./%s}}.
|
196
|
+
{{*}} You’re ready to start building {{green:%s}}!
|
197
|
+
MESSAGE
|
198
|
+
},
|
199
|
+
},
|
200
|
+
},
|
137
201
|
}
|
138
202
|
end
|
139
203
|
end
|
@@ -17,6 +17,7 @@ module Extension
|
|
17
17
|
|
18
18
|
def self.build(feature_set_attributes)
|
19
19
|
feature_set_attributes.each_with_object(OpenStruct.new) do |(identifier, feature_attributes), feature_set|
|
20
|
+
next if feature_attributes.nil?
|
20
21
|
feature_set[identifier] = ShopifyCli::ResolveConstant
|
21
22
|
.call(identifier, namespace: Features)
|
22
23
|
.rescue { OpenStruct }
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module Extension
|
4
4
|
module Models
|
5
5
|
module SpecificationHandlers
|
6
|
-
class
|
6
|
+
class CheckoutUiExtension < Default
|
7
7
|
PERMITTED_CONFIG_KEYS = [:metafields, :extension_points]
|
8
8
|
|
9
9
|
def config(context)
|
@@ -13,6 +13,8 @@ module Extension
|
|
13
13
|
}
|
14
14
|
end
|
15
15
|
end
|
16
|
+
|
17
|
+
CheckoutArgoExtension = CheckoutUiExtension
|
16
18
|
end
|
17
19
|
end
|
18
20
|
end
|
@@ -30,7 +30,7 @@ module Extension
|
|
30
30
|
argo.config(context)
|
31
31
|
end
|
32
32
|
|
33
|
-
def create(directory_name, context)
|
33
|
+
def create(directory_name, context, **_args)
|
34
34
|
argo.create(directory_name, identifier, context)
|
35
35
|
end
|
36
36
|
|
@@ -43,16 +43,21 @@ module Extension
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def choose_port?(context)
|
46
|
-
argo_runtime(context).
|
46
|
+
argo_runtime(context).supports?(:port)
|
47
47
|
end
|
48
48
|
|
49
49
|
def establish_tunnel?(context)
|
50
|
-
argo_runtime(context).
|
50
|
+
argo_runtime(context).supports?(:public_url)
|
51
51
|
end
|
52
52
|
|
53
53
|
def serve(context:, port:, tunnel_url:)
|
54
|
-
Features::ArgoServe.new(
|
55
|
-
|
54
|
+
Features::ArgoServe.new(
|
55
|
+
specification_handler: self,
|
56
|
+
argo_runtime: argo_runtime(context),
|
57
|
+
context: context,
|
58
|
+
port: port,
|
59
|
+
tunnel_url: tunnel_url,
|
60
|
+
).call
|
56
61
|
end
|
57
62
|
|
58
63
|
def renderer_package(context)
|
@@ -62,7 +67,7 @@ module Extension
|
|
62
67
|
def argo_runtime(context)
|
63
68
|
@argo_runtime ||= Features::ArgoRuntime.new(
|
64
69
|
renderer: renderer_package(context),
|
65
|
-
cli: cli_package(context)
|
70
|
+
cli: cli_package(context),
|
66
71
|
)
|
67
72
|
end
|
68
73
|
|
@@ -75,6 +80,16 @@ module Extension
|
|
75
80
|
.unwrap { |_e| context.abort(context.message("errors.package_not_found", cli_package_name)) }
|
76
81
|
end
|
77
82
|
|
83
|
+
def message_for_extension(key, *params)
|
84
|
+
override_key = "overrides.#{key}"
|
85
|
+
key_parts = override_key.split(".").map(&:to_sym)
|
86
|
+
if (str = messages.dig(*key_parts))
|
87
|
+
str % params
|
88
|
+
else
|
89
|
+
ShopifyCli::Context.message(key, *params)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
78
93
|
protected
|
79
94
|
|
80
95
|
def argo
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "base64"
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
module Extension
|
6
|
+
module Models
|
7
|
+
module SpecificationHandlers
|
8
|
+
class ThemeAppExtension < Default
|
9
|
+
SUPPORTED_BUCKETS = %w(assets blocks snippets)
|
10
|
+
BUNDLE_SIZE_LIMIT = 10 * 1024 * 1024 # 10MB
|
11
|
+
LIQUID_SIZE_LIMIT = 100 * 1024 # 100kb
|
12
|
+
SUPPORTED_ASSET_EXTS = %w(.jpg .js .css .png .svg)
|
13
|
+
|
14
|
+
def create(directory_name, context, getting_started: false)
|
15
|
+
context.root = File.join(context.root, directory_name)
|
16
|
+
|
17
|
+
if getting_started
|
18
|
+
ShopifyCli::Git.clone("https://github.com/Shopify/theme-extension-getting-started", context.root)
|
19
|
+
context.rm_r(".git")
|
20
|
+
else
|
21
|
+
FileUtils.makedirs(SUPPORTED_BUCKETS.map { |b| File.join(context.root, b) })
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def config(context)
|
26
|
+
current_size = 0
|
27
|
+
current_liquid_size = 0
|
28
|
+
Dir.chdir(context.root) do
|
29
|
+
Dir["**/*"].select { |filename| File.file?(filename) && validate(filename) }
|
30
|
+
.map do |filename|
|
31
|
+
dirname = File.dirname(filename)
|
32
|
+
if dirname == "assets"
|
33
|
+
# Assets should be read as binary data, since they could be images
|
34
|
+
mode = "rb"
|
35
|
+
encoding = "BINARY"
|
36
|
+
else
|
37
|
+
# Other assets should be treated as UTF-8 encoded text
|
38
|
+
mode = "rt"
|
39
|
+
encoding = "UTF-8"
|
40
|
+
current_liquid_size += File.size(filename)
|
41
|
+
end
|
42
|
+
current_size += File.size(filename)
|
43
|
+
if current_size > BUNDLE_SIZE_LIMIT
|
44
|
+
raise Extension::Errors::BundleTooLargeError,
|
45
|
+
"Total size of all files must be less than #{CLI::Kit::Util.to_filesize(BUNDLE_SIZE_LIMIT)}"
|
46
|
+
end
|
47
|
+
if current_liquid_size > LIQUID_SIZE_LIMIT
|
48
|
+
raise Extension::Errors::BundleTooLargeError,
|
49
|
+
"Total size of all liquid must be less than #{CLI::Kit::Util.to_filesize(LIQUID_SIZE_LIMIT)}"
|
50
|
+
end
|
51
|
+
[filename, Base64.encode64(File.read(filename, mode: mode, encoding: encoding))]
|
52
|
+
end
|
53
|
+
.yield_self do |encoded_files_by_name|
|
54
|
+
{ "theme_extension" => { "files" => encoded_files_by_name.to_h } }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def name
|
60
|
+
"Theme App Extension"
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def validate(filename)
|
66
|
+
dirname = File.dirname(filename)
|
67
|
+
unless SUPPORTED_BUCKETS.include?(dirname)
|
68
|
+
raise Extension::Errors::InvalidFilenameError, "Invalid directory: #{dirname}"
|
69
|
+
end
|
70
|
+
|
71
|
+
ext = File.extname(filename)
|
72
|
+
if dirname == "assets"
|
73
|
+
unless SUPPORTED_ASSET_EXTS.include?(ext)
|
74
|
+
raise Extension::Errors::InvalidFilenameError,
|
75
|
+
"Invalid filename: #{filename}; #{ext} is not supported"
|
76
|
+
end
|
77
|
+
elsif ext != ".liquid"
|
78
|
+
raise Extension::Errors::InvalidFilenameError,
|
79
|
+
"Invalid filename: #{filename}; Only .liquid allowed in #{dirname}"
|
80
|
+
end
|
81
|
+
true
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|