shopify-cli 0.9.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 +7 -0
- data/.github/CODEOWNERS +1 -0
- data/.github/CODE_OF_CONDUCT.md +73 -0
- data/.github/CONTRIBUTING.md +51 -0
- data/.github/DESIGN.md +153 -0
- data/.github/ISSUE_TEMPLATE.md +38 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +22 -0
- data/.github/probots.yml +3 -0
- data/.gitignore +19 -0
- data/.rubocop.yml +47 -0
- data/.ruby-version +1 -0
- data/.travis.yml +12 -0
- data/Gemfile +22 -0
- data/Gemfile.lock +77 -0
- data/LICENSE.md +7 -0
- data/README.md +13 -0
- data/Rakefile +101 -0
- data/SECURITY.md +59 -0
- data/Vagrantfile +17 -0
- data/bin/load_shopify.rb +20 -0
- data/bin/shopify +32 -0
- data/dev.yml +17 -0
- data/docs/Gemfile +5 -0
- data/docs/Gemfile.lock +248 -0
- data/docs/_config.yml +16 -0
- data/docs/_data/nav.yml +26 -0
- data/docs/_includes/footer.html +15 -0
- data/docs/_includes/head.html +19 -0
- data/docs/_includes/sidebar_nav.html +22 -0
- data/docs/_includes/toc.html +112 -0
- data/docs/_layouts/default.html +79 -0
- data/docs/app/node/commands/index.md +82 -0
- data/docs/app/node/index.md +35 -0
- data/docs/app/rails/commands/index.md +80 -0
- data/docs/app/rails/index.md +36 -0
- data/docs/core/index.md +70 -0
- data/docs/css/docs.css +157 -0
- data/docs/getting-started/index.md +61 -0
- data/docs/help/start-app/index.md +6 -0
- data/docs/images/header.png +0 -0
- data/docs/index.md +27 -0
- data/docs/installing-ruby.md +28 -0
- data/ext/shopify-cli/extconf.rb +27 -0
- data/install.sh +7 -0
- data/lib/docgen/class_template.md.erb +81 -0
- data/lib/docgen/index_template.md.erb +5 -0
- data/lib/docgen/markdown.rb +101 -0
- data/lib/graphql/admin_introspection.graphql +87 -0
- data/lib/graphql/all_organizations.graphql +19 -0
- data/lib/graphql/all_orgs_with_apps.graphql +30 -0
- data/lib/graphql/api_versions.graphql +6 -0
- data/lib/graphql/convert_dev_to_test_store.graphql +10 -0
- data/lib/graphql/create_app.graphql +20 -0
- data/lib/graphql/create_customer.graphql +9 -0
- data/lib/graphql/create_draft_order.graphql +8 -0
- data/lib/graphql/create_product.graphql +9 -0
- data/lib/graphql/extension_create.graphql +21 -0
- data/lib/graphql/extension_update_draft.graphql +18 -0
- data/lib/graphql/find_organization.graphql +17 -0
- data/lib/graphql/get_app_urls.graphql +6 -0
- data/lib/graphql/update_dashboard_urls.graphql +8 -0
- data/lib/project_types/extension/cli.rb +71 -0
- data/lib/project_types/extension/commands/build.rb +29 -0
- data/lib/project_types/extension/commands/create.rb +49 -0
- data/lib/project_types/extension/commands/extension_command.rb +22 -0
- data/lib/project_types/extension/commands/push.rb +69 -0
- data/lib/project_types/extension/commands/register.rb +78 -0
- data/lib/project_types/extension/commands/serve.rb +24 -0
- data/lib/project_types/extension/commands/tunnel.rb +69 -0
- data/lib/project_types/extension/extension_project.rb +85 -0
- data/lib/project_types/extension/extension_project_keys.rb +10 -0
- data/lib/project_types/extension/features/argo.rb +48 -0
- data/lib/project_types/extension/features/argo_dependencies.rb +28 -0
- data/lib/project_types/extension/features/argo_setup.rb +54 -0
- data/lib/project_types/extension/features/argo_setup_step.rb +31 -0
- data/lib/project_types/extension/features/argo_setup_steps.rb +53 -0
- data/lib/project_types/extension/features/tunnel_url.rb +20 -0
- data/lib/project_types/extension/forms/create.rb +52 -0
- data/lib/project_types/extension/forms/register.rb +48 -0
- data/lib/project_types/extension/messages/message_loading.rb +37 -0
- data/lib/project_types/extension/messages/messages.rb +126 -0
- data/lib/project_types/extension/models/app.rb +14 -0
- data/lib/project_types/extension/models/registration.rb +19 -0
- data/lib/project_types/extension/models/type.rb +76 -0
- data/lib/project_types/extension/models/types/checkout_post_purchase.rb +20 -0
- data/lib/project_types/extension/models/types/subscription_management.rb +20 -0
- data/lib/project_types/extension/models/validation_error.rb +17 -0
- data/lib/project_types/extension/models/version.rb +15 -0
- data/lib/project_types/extension/tasks/converters/registration_converter.rb +26 -0
- data/lib/project_types/extension/tasks/converters/validation_error_converter.rb +25 -0
- data/lib/project_types/extension/tasks/converters/version_converter.rb +28 -0
- data/lib/project_types/extension/tasks/create_extension.rb +31 -0
- data/lib/project_types/extension/tasks/get_apps.rb +34 -0
- data/lib/project_types/extension/tasks/update_draft.rb +29 -0
- data/lib/project_types/extension/tasks/user_errors.rb +45 -0
- data/lib/project_types/node/cli.rb +37 -0
- data/lib/project_types/node/commands/create.rb +117 -0
- data/lib/project_types/node/commands/deploy.rb +22 -0
- data/lib/project_types/node/commands/deploy/heroku.rb +91 -0
- data/lib/project_types/node/commands/generate.rb +51 -0
- data/lib/project_types/node/commands/generate/billing.rb +37 -0
- data/lib/project_types/node/commands/generate/page.rb +55 -0
- data/lib/project_types/node/commands/generate/webhook.rb +33 -0
- data/lib/project_types/node/commands/open.rb +16 -0
- data/lib/project_types/node/commands/populate.rb +23 -0
- data/lib/project_types/node/commands/populate/customer.rb +31 -0
- data/lib/project_types/node/commands/populate/draft_order.rb +28 -0
- data/lib/project_types/node/commands/populate/product.rb +30 -0
- data/lib/project_types/node/commands/serve.rb +45 -0
- data/lib/project_types/node/commands/tunnel.rb +39 -0
- data/lib/project_types/node/forms/create.rb +87 -0
- data/lib/project_types/node/messages/messages.rb +260 -0
- data/lib/project_types/rails/cli.rb +41 -0
- data/lib/project_types/rails/commands/create.rb +126 -0
- data/lib/project_types/rails/commands/deploy.rb +22 -0
- data/lib/project_types/rails/commands/deploy/heroku.rb +113 -0
- data/lib/project_types/rails/commands/generate.rb +49 -0
- data/lib/project_types/rails/commands/generate/webhook.rb +39 -0
- data/lib/project_types/rails/commands/open.rb +16 -0
- data/lib/project_types/rails/commands/populate.rb +23 -0
- data/lib/project_types/rails/commands/populate/customer.rb +31 -0
- data/lib/project_types/rails/commands/populate/draft_order.rb +28 -0
- data/lib/project_types/rails/commands/populate/product.rb +30 -0
- data/lib/project_types/rails/commands/serve.rb +47 -0
- data/lib/project_types/rails/commands/tunnel.rb +39 -0
- data/lib/project_types/rails/forms/create.rb +116 -0
- data/lib/project_types/rails/gem.rb +56 -0
- data/lib/project_types/rails/messages/messages.rb +283 -0
- data/lib/project_types/rails/ruby.rb +17 -0
- data/lib/project_types/script/cli.rb +76 -0
- data/lib/project_types/script/commands/create.rb +45 -0
- data/lib/project_types/script/commands/disable.rb +36 -0
- data/lib/project_types/script/commands/enable.rb +46 -0
- data/lib/project_types/script/commands/push.rb +39 -0
- data/lib/project_types/script/config/extension_points.yml +18 -0
- data/lib/project_types/script/errors.rb +16 -0
- data/lib/project_types/script/forms/create.rb +29 -0
- data/lib/project_types/script/forms/enable.rb +24 -0
- data/lib/project_types/script/forms/push.rb +19 -0
- data/lib/project_types/script/forms/script_form.rb +66 -0
- data/lib/project_types/script/graphql/app_script_update_or_create.graphql +27 -0
- data/lib/project_types/script/graphql/script_service_proxy.graphql +8 -0
- data/lib/project_types/script/graphql/shop_script_delete.graphql +14 -0
- data/lib/project_types/script/graphql/shop_script_update_or_create.graphql +28 -0
- data/lib/project_types/script/layers/application/build_script.rb +43 -0
- data/lib/project_types/script/layers/application/create_script.rb +47 -0
- data/lib/project_types/script/layers/application/disable_script.rb +19 -0
- data/lib/project_types/script/layers/application/enable_script.rb +21 -0
- data/lib/project_types/script/layers/application/extension_points.rb +17 -0
- data/lib/project_types/script/layers/application/project_dependencies.rb +34 -0
- data/lib/project_types/script/layers/application/push_script.rb +30 -0
- data/lib/project_types/script/layers/domain/errors.rb +25 -0
- data/lib/project_types/script/layers/domain/extension_point.rb +29 -0
- data/lib/project_types/script/layers/domain/push_package.rb +29 -0
- data/lib/project_types/script/layers/domain/script.rb +18 -0
- data/lib/project_types/script/layers/infrastructure/assemblyscript_dependency_manager.rb +73 -0
- data/lib/project_types/script/layers/infrastructure/assemblyscript_tsconfig.rb +38 -0
- data/lib/project_types/script/layers/infrastructure/assemblyscript_wasm_builder.rb +39 -0
- data/lib/project_types/script/layers/infrastructure/dependency_manager.rb +36 -0
- data/lib/project_types/script/layers/infrastructure/errors.rb +38 -0
- data/lib/project_types/script/layers/infrastructure/extension_point_repository.rb +31 -0
- data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +47 -0
- data/lib/project_types/script/layers/infrastructure/script_builder.rb +34 -0
- data/lib/project_types/script/layers/infrastructure/script_repository.rb +89 -0
- data/lib/project_types/script/layers/infrastructure/script_service.rb +165 -0
- data/lib/project_types/script/layers/infrastructure/test_suite_repository.rb +59 -0
- data/lib/project_types/script/messages/messages.rb +204 -0
- data/lib/project_types/script/script_project.rb +37 -0
- data/lib/project_types/script/templates/ts/as-pect.config.js +21 -0
- data/lib/project_types/script/ui/error_handler.rb +136 -0
- data/lib/project_types/script/ui/strict_spinner.rb +22 -0
- data/lib/rubygems_plugin.rb +18 -0
- data/lib/shopify-cli/admin_api.rb +99 -0
- data/lib/shopify-cli/admin_api/populate_resource_command.rb +165 -0
- data/lib/shopify-cli/admin_api/schema.rb +32 -0
- data/lib/shopify-cli/api.rb +104 -0
- data/lib/shopify-cli/command.rb +67 -0
- data/lib/shopify-cli/commands.rb +28 -0
- data/lib/shopify-cli/commands/connect.rb +108 -0
- data/lib/shopify-cli/commands/create.rb +50 -0
- data/lib/shopify-cli/commands/help.rb +79 -0
- data/lib/shopify-cli/commands/logout.rb +23 -0
- data/lib/shopify-cli/commands/system.rb +135 -0
- data/lib/shopify-cli/commands/version.rb +15 -0
- data/lib/shopify-cli/context.rb +372 -0
- data/lib/shopify-cli/core.rb +9 -0
- data/lib/shopify-cli/core/entry_point.rb +40 -0
- data/lib/shopify-cli/core/executor.rb +21 -0
- data/lib/shopify-cli/core/help_resolver.rb +20 -0
- data/lib/shopify-cli/core/monorail.rb +118 -0
- data/lib/shopify-cli/db.rb +114 -0
- data/lib/shopify-cli/form.rb +40 -0
- data/lib/shopify-cli/git.rb +141 -0
- data/lib/shopify-cli/helpers.rb +5 -0
- data/lib/shopify-cli/helpers/haikunator.rb +92 -0
- data/lib/shopify-cli/heroku.rb +97 -0
- data/lib/shopify-cli/js_deps.rb +110 -0
- data/lib/shopify-cli/js_system.rb +98 -0
- data/lib/shopify-cli/messages/messages.rb +287 -0
- data/lib/shopify-cli/oauth.rb +192 -0
- data/lib/shopify-cli/oauth/servlet.rb +61 -0
- data/lib/shopify-cli/options.rb +40 -0
- data/lib/shopify-cli/packager.rb +116 -0
- data/lib/shopify-cli/partners_api.rb +114 -0
- data/lib/shopify-cli/partners_api/organizations.rb +32 -0
- data/lib/shopify-cli/process_supervision.rb +187 -0
- data/lib/shopify-cli/project.rb +191 -0
- data/lib/shopify-cli/project_type.rb +83 -0
- data/lib/shopify-cli/resources.rb +5 -0
- data/lib/shopify-cli/resources/env_file.rb +96 -0
- data/lib/shopify-cli/sub_command.rb +15 -0
- data/lib/shopify-cli/task.rb +10 -0
- data/lib/shopify-cli/tasks.rb +32 -0
- data/lib/shopify-cli/tasks/create_api_client.rb +29 -0
- data/lib/shopify-cli/tasks/ensure_dev_store.rb +41 -0
- data/lib/shopify-cli/tasks/ensure_env.rb +31 -0
- data/lib/shopify-cli/tasks/ensure_loopback_url.rb +20 -0
- data/lib/shopify-cli/tasks/update_dashboard_urls.rb +44 -0
- data/lib/shopify-cli/tunnel.rb +154 -0
- data/lib/shopify-cli/version.rb +3 -0
- data/lib/shopify_cli.rb +132 -0
- data/shopify-cli.gemspec +40 -0
- data/shopify.fish +12 -0
- data/shopify.sh +11 -0
- data/vendor/deps/cli-kit/REVISION +1 -0
- data/vendor/deps/cli-kit/lib/cli/kit.rb +60 -0
- data/vendor/deps/cli-kit/lib/cli/kit/autocall.rb +21 -0
- data/vendor/deps/cli-kit/lib/cli/kit/base_command.rb +49 -0
- data/vendor/deps/cli-kit/lib/cli/kit/command_registry.rb +94 -0
- data/vendor/deps/cli-kit/lib/cli/kit/config.rb +133 -0
- data/vendor/deps/cli-kit/lib/cli/kit/error_handler.rb +115 -0
- data/vendor/deps/cli-kit/lib/cli/kit/executor.rb +81 -0
- data/vendor/deps/cli-kit/lib/cli/kit/ini.rb +102 -0
- data/vendor/deps/cli-kit/lib/cli/kit/levenshtein.rb +82 -0
- data/vendor/deps/cli-kit/lib/cli/kit/logger.rb +76 -0
- data/vendor/deps/cli-kit/lib/cli/kit/resolver.rb +60 -0
- data/vendor/deps/cli-kit/lib/cli/kit/ruby_backports/enumerable.rb +6 -0
- data/vendor/deps/cli-kit/lib/cli/kit/support.rb +9 -0
- data/vendor/deps/cli-kit/lib/cli/kit/support/test_helper.rb +244 -0
- data/vendor/deps/cli-kit/lib/cli/kit/system.rb +207 -0
- data/vendor/deps/cli-kit/lib/cli/kit/util.rb +189 -0
- data/vendor/deps/cli-kit/lib/cli/kit/version.rb +5 -0
- data/vendor/deps/cli-ui/REVISION +1 -0
- data/vendor/deps/cli-ui/lib/cli/ui.rb +187 -0
- data/vendor/deps/cli-ui/lib/cli/ui/ansi.rb +153 -0
- data/vendor/deps/cli-ui/lib/cli/ui/box.rb +15 -0
- data/vendor/deps/cli-ui/lib/cli/ui/color.rb +79 -0
- data/vendor/deps/cli-ui/lib/cli/ui/formatter.rb +179 -0
- data/vendor/deps/cli-ui/lib/cli/ui/frame.rb +310 -0
- data/vendor/deps/cli-ui/lib/cli/ui/glyph.rb +78 -0
- data/vendor/deps/cli-ui/lib/cli/ui/progress.rb +88 -0
- data/vendor/deps/cli-ui/lib/cli/ui/prompt.rb +248 -0
- data/vendor/deps/cli-ui/lib/cli/ui/prompt/interactive_options.rb +472 -0
- data/vendor/deps/cli-ui/lib/cli/ui/prompt/options_handler.rb +24 -0
- data/vendor/deps/cli-ui/lib/cli/ui/spinner.rb +48 -0
- data/vendor/deps/cli-ui/lib/cli/ui/spinner/async.rb +40 -0
- data/vendor/deps/cli-ui/lib/cli/ui/spinner/spin_group.rb +241 -0
- data/vendor/deps/cli-ui/lib/cli/ui/stdout_router.rb +227 -0
- data/vendor/deps/cli-ui/lib/cli/ui/terminal.rb +36 -0
- data/vendor/deps/cli-ui/lib/cli/ui/truncater.rb +102 -0
- data/vendor/deps/cli-ui/lib/cli/ui/version.rb +5 -0
- data/vendor/deps/smart_properties/REVISION +1 -0
- data/vendor/deps/smart_properties/lib/smart_properties.rb +174 -0
- data/vendor/deps/smart_properties/lib/smart_properties/errors.rb +114 -0
- data/vendor/deps/smart_properties/lib/smart_properties/property.rb +162 -0
- data/vendor/deps/smart_properties/lib/smart_properties/property_collection.rb +83 -0
- data/vendor/deps/smart_properties/lib/smart_properties/validations.rb +8 -0
- data/vendor/deps/smart_properties/lib/smart_properties/validations/ancestor.rb +27 -0
- data/vendor/deps/smart_properties/lib/smart_properties/version.rb +3 -0
- data/vendor/lib/semantic/LICENSE +20 -0
- data/vendor/lib/semantic/semantic.rb +4 -0
- data/vendor/lib/semantic/version.rb +180 -0
- metadata +374 -0
data/lib/shopify_cli.rb
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Make sure we are using UTF 8 encoding
|
|
2
|
+
Encoding.default_external = Encoding::UTF_8
|
|
3
|
+
Encoding.default_internal = Encoding::UTF_8
|
|
4
|
+
|
|
5
|
+
Thread.report_on_exception = false
|
|
6
|
+
|
|
7
|
+
# Contains backports from newer rubies to make our lives easier
|
|
8
|
+
# require_relative 'support/ruby_backports'
|
|
9
|
+
|
|
10
|
+
# See bin/load_shopify.rb
|
|
11
|
+
ENV['PATH'] = ENV['PATH'].split(':').select { |p| p.start_with?('/', '~') }.join(':') unless defined?($original_env)
|
|
12
|
+
|
|
13
|
+
# Load vendor and CLI UI/Kit.
|
|
14
|
+
# Nothing else should be loaded at this point and nothing else should be added to the load path on boot
|
|
15
|
+
vendor_path = File.expand_path("../../vendor/lib", __FILE__)
|
|
16
|
+
$LOAD_PATH.unshift(vendor_path) unless $LOAD_PATH.include?(vendor_path)
|
|
17
|
+
|
|
18
|
+
deps = %w(cli-ui cli-kit smart_properties)
|
|
19
|
+
deps.each do |dep|
|
|
20
|
+
vendor_path = File.expand_path("../../vendor/deps/#{dep}/lib", __FILE__)
|
|
21
|
+
$LOAD_PATH.unshift(vendor_path) unless $LOAD_PATH.include?(vendor_path)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
require 'cli/ui'
|
|
25
|
+
require 'cli/kit'
|
|
26
|
+
require 'smart_properties'
|
|
27
|
+
require_relative 'shopify-cli/version'
|
|
28
|
+
|
|
29
|
+
# Enable stdout routing. At this point all calls to STDOUT (and STDERR) will go through this class.
|
|
30
|
+
# See https://github.com/Shopify/cli-ui/blob/master/lib/cli/ui/stdout_router.rb for more info
|
|
31
|
+
CLI::UI::StdoutRouter.enable
|
|
32
|
+
|
|
33
|
+
# The main file to load for `shopify-app-cli`
|
|
34
|
+
# Contains all high level constants, exit management, exception management,
|
|
35
|
+
# autoloads for commands, tasks, helpers, etc
|
|
36
|
+
#
|
|
37
|
+
# It is recommended to read through CLI Kit (https://github.com/shopify/cli-kit) and a CLI Kit example
|
|
38
|
+
# (https://github.com/Shopify/cli-kit-example) to fully understand how shopify-app-cli functions
|
|
39
|
+
module ShopifyCli
|
|
40
|
+
extend CLI::Kit::Autocall
|
|
41
|
+
|
|
42
|
+
TOOL_NAME = 'shopify'
|
|
43
|
+
TOOL_FULL_NAME = 'Shopify CLI'
|
|
44
|
+
ROOT = File.expand_path('../..', __FILE__)
|
|
45
|
+
PROJECT_TYPES_DIR = File.join(ROOT, 'lib', 'project_types')
|
|
46
|
+
TEMP_DIR = File.join(ROOT, '.tmp')
|
|
47
|
+
CACHE_DIR = File.join(File.expand_path(ENV.fetch('XDG_CACHE_HOME', '~/.cache')), TOOL_NAME)
|
|
48
|
+
TOOL_CONFIG_PATH = File.join(File.expand_path(ENV.fetch('XDG_CONFIG_HOME', '~/.config')), TOOL_NAME)
|
|
49
|
+
LOG_FILE = File.join(TOOL_CONFIG_PATH, 'logs', 'log.log')
|
|
50
|
+
DEBUG_LOG_FILE = File.join(TOOL_CONFIG_PATH, 'logs', 'debug.log')
|
|
51
|
+
|
|
52
|
+
# programmer emoji if default install location, else wrench emoji
|
|
53
|
+
EMOJI = ROOT == '/opt/shopify' ? "\u{1f469}\u{200d}\u{1f4bb}" : "\u{1f527}"
|
|
54
|
+
# shrug or boom emoji
|
|
55
|
+
FAILMOJI = ROOT == '/opt/shopify' ? "\u{1f937}" : "\u{1f4a5}"
|
|
56
|
+
|
|
57
|
+
# Exit management in `shopify-app-cli` follows the management set out by CLI Kit.
|
|
58
|
+
# https://github.com/Shopify/cli-kit/blob/master/lib/cli/kit.rb
|
|
59
|
+
# That is to say, we differentiate between exit success (0), exit failure (1), and exit bug (not 1)
|
|
60
|
+
#
|
|
61
|
+
# These should *never* be called outside of the entrypoint and its delegations.
|
|
62
|
+
EXIT_FAILURE_BUT_NOT_BUG = CLI::Kit::EXIT_FAILURE_BUT_NOT_BUG
|
|
63
|
+
EXIT_BUG = CLI::Kit::EXIT_BUG
|
|
64
|
+
EXIT_SUCCESS = CLI::Kit::EXIT_SUCCESS
|
|
65
|
+
|
|
66
|
+
# `shopify-app-cli` uses CLI Kit's exception management
|
|
67
|
+
# These are documented here: https://github.com/Shopify/cli-kit/blob/master/lib/cli/kit.rb
|
|
68
|
+
#
|
|
69
|
+
# You should never subclass these exceptions, but instead rescue another exception and re-raise.
|
|
70
|
+
# AbortSilent and BugSilent should never have messages. They are mostly used when we output explanations
|
|
71
|
+
# and need to exit
|
|
72
|
+
GenericAbort = CLI::Kit::GenericAbort
|
|
73
|
+
Abort = CLI::Kit::Abort
|
|
74
|
+
Bug = CLI::Kit::Bug
|
|
75
|
+
BugSilent = CLI::Kit::BugSilent
|
|
76
|
+
AbortSilent = CLI::Kit::AbortSilent
|
|
77
|
+
|
|
78
|
+
# The rest of this file outlines classes and modules required by the shopify-app-cli
|
|
79
|
+
# application and CLI kit framework.
|
|
80
|
+
# To understand how this works, read https://github.com/Shopify/cli-kit/blob/master/lib/cli/kit.rb
|
|
81
|
+
|
|
82
|
+
# ShopifyCli::Config
|
|
83
|
+
autocall(:Config) { CLI::Kit::Config.new(tool_name: TOOL_NAME) }
|
|
84
|
+
# ShopifyCli::Logger
|
|
85
|
+
autocall(:Logger) { CLI::Kit::Logger.new(debug_log_file: DEBUG_LOG_FILE) }
|
|
86
|
+
# ShopifyCli::Resolver
|
|
87
|
+
autocall(:Resolver) do
|
|
88
|
+
ShopifyCli::Core::HelpResolver.new(
|
|
89
|
+
tool_name: TOOL_NAME,
|
|
90
|
+
command_registry: ShopifyCli::Commands::Registry
|
|
91
|
+
)
|
|
92
|
+
end
|
|
93
|
+
# ShopifyCli::ErrorHandler
|
|
94
|
+
autocall(:ErrorHandler) do
|
|
95
|
+
CLI::Kit::ErrorHandler.new(
|
|
96
|
+
log_file: ShopifyCli::LOG_FILE,
|
|
97
|
+
exception_reporter: nil,
|
|
98
|
+
)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
autoload :AdminAPI, 'shopify-cli/admin_api'
|
|
102
|
+
autoload :API, 'shopify-cli/api'
|
|
103
|
+
autoload :Command, 'shopify-cli/command'
|
|
104
|
+
autoload :Commands, 'shopify-cli/commands'
|
|
105
|
+
autoload :Context, 'shopify-cli/context'
|
|
106
|
+
autoload :Core, 'shopify-cli/core'
|
|
107
|
+
autoload :DB, 'shopify-cli/db'
|
|
108
|
+
autoload :Form, 'shopify-cli/form'
|
|
109
|
+
autoload :Git, 'shopify-cli/git'
|
|
110
|
+
autoload :Helpers, 'shopify-cli/helpers'
|
|
111
|
+
autoload :Heroku, 'shopify-cli/heroku'
|
|
112
|
+
autoload :JsDeps, 'shopify-cli/js_deps'
|
|
113
|
+
autoload :JsSystem, 'shopify-cli/js_system'
|
|
114
|
+
autoload :Log, 'shopify-cli/log'
|
|
115
|
+
autoload :OAuth, 'shopify-cli/oauth'
|
|
116
|
+
autoload :Options, 'shopify-cli/options'
|
|
117
|
+
autoload :PartnersAPI, 'shopify-cli/partners_api'
|
|
118
|
+
autoload :ProcessSupervision, 'shopify-cli/process_supervision'
|
|
119
|
+
autoload :Project, 'shopify-cli/project'
|
|
120
|
+
autoload :ProjectType, 'shopify-cli/project_type'
|
|
121
|
+
autoload :Resources, 'shopify-cli/resources'
|
|
122
|
+
autoload :SubCommand, 'shopify-cli/sub_command'
|
|
123
|
+
autoload :Task, 'shopify-cli/task'
|
|
124
|
+
autoload :Tasks, 'shopify-cli/tasks'
|
|
125
|
+
autoload :Tunnel, 'shopify-cli/tunnel'
|
|
126
|
+
|
|
127
|
+
require 'shopify-cli/messages/messages'
|
|
128
|
+
Context.load_messages(ShopifyCli::Messages::MESSAGES)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Make sure the cache dir always exists
|
|
132
|
+
FileUtils.mkdir_p(ShopifyCli::CACHE_DIR)
|
data/shopify-cli.gemspec
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require_relative 'lib/shopify-cli/version'
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |spec|
|
|
4
|
+
spec.name = "shopify-cli"
|
|
5
|
+
spec.version = ShopifyCli::VERSION
|
|
6
|
+
spec.authors = ["Shopify"]
|
|
7
|
+
# need to change the email that ends with `shopify.com`
|
|
8
|
+
spec.email = ["developers@jadedpixel.com"]
|
|
9
|
+
spec.license = 'Nonstandard'
|
|
10
|
+
|
|
11
|
+
spec.summary = "Shopify CLI helps you build Shopify apps faster."
|
|
12
|
+
spec.description = <<~HERE
|
|
13
|
+
Shopify CLI helps you build Shopify apps faster. It quickly scaffolds Node.js
|
|
14
|
+
and Ruby on Rails embedded apps. It also automates many common tasks in the
|
|
15
|
+
development process and lets you quickly add popular features, such as billing
|
|
16
|
+
and webhooks.
|
|
17
|
+
HERE
|
|
18
|
+
spec.homepage = "https://shopify.github.io/shopify-app-cli/"
|
|
19
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
|
20
|
+
|
|
21
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
|
22
|
+
|
|
23
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
24
|
+
spec.metadata["source_code_uri"] = "https://github.com/Shopify/shopify-app-cli"
|
|
25
|
+
spec.metadata["changelog_uri"] = "https://github.com/Shopify/shopify-app-cli/blob/master/CHANGELOG.md"
|
|
26
|
+
|
|
27
|
+
# Specify which files should be added to the gem when it is released.
|
|
28
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
29
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
|
30
|
+
%x(git ls-files -z).split("\x0").reject { |f| f.match(%r{^(test|spec|features|packaging)/}) }
|
|
31
|
+
end
|
|
32
|
+
spec.bindir = "bin"
|
|
33
|
+
# spec.executables = spec.files.grep(%r{^bin/}) { |f| f.sub('bin/', '') }
|
|
34
|
+
spec.require_paths = ["lib", "vendor"]
|
|
35
|
+
spec.extensions = ["ext/shopify-cli/extconf.rb"]
|
|
36
|
+
|
|
37
|
+
spec.add_development_dependency('bundler', '~> 1.17')
|
|
38
|
+
spec.add_development_dependency('rake', '~> 12.3', '>= 12.3.3')
|
|
39
|
+
spec.add_development_dependency('minitest', '~> 5.0')
|
|
40
|
+
end
|
data/shopify.fish
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env fish
|
|
2
|
+
# vi: ft=fish
|
|
3
|
+
|
|
4
|
+
# If we're not already in a shim environment but still tried to set up a shim, print out the re-installation warning
|
|
5
|
+
type -t shopify > /dev/null 2>&1
|
|
6
|
+
if [ $status != "0" ]
|
|
7
|
+
echo "This version of Shopify App CLI is no longer supported. You’ll need to migrate to the new CLI version to continue.
|
|
8
|
+
|
|
9
|
+
Please visit this page for complete instructions:
|
|
10
|
+
https://shopify.github.io/shopify-app-cli/migrate/
|
|
11
|
+
"
|
|
12
|
+
end
|
data/shopify.sh
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
|
|
3
|
+
# If we're not already in a shim environment but still tried to set up a shim, print out the re-installation warning
|
|
4
|
+
typeset -f shopify > /dev/null 2>&1
|
|
5
|
+
if [ "$?" != "0" ]; then
|
|
6
|
+
echo "This version of Shopify App CLI is no longer supported. You’ll need to migrate to the new CLI version to continue.
|
|
7
|
+
|
|
8
|
+
Please visit this page for complete instructions:
|
|
9
|
+
https://shopify.github.io/shopify-app-cli/migrate/
|
|
10
|
+
"
|
|
11
|
+
fi
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
fc25a17dff9d3130bb705659c002282ef7827932
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require 'cli/ui'
|
|
2
|
+
require 'cli/kit/ruby_backports/enumerable'
|
|
3
|
+
|
|
4
|
+
module CLI
|
|
5
|
+
module Kit
|
|
6
|
+
autoload :Autocall, 'cli/kit/autocall'
|
|
7
|
+
autoload :BaseCommand, 'cli/kit/base_command'
|
|
8
|
+
autoload :CommandRegistry, 'cli/kit/command_registry'
|
|
9
|
+
autoload :Config, 'cli/kit/config'
|
|
10
|
+
autoload :ErrorHandler, 'cli/kit/error_handler'
|
|
11
|
+
autoload :Executor, 'cli/kit/executor'
|
|
12
|
+
autoload :Ini, 'cli/kit/ini'
|
|
13
|
+
autoload :Levenshtein, 'cli/kit/levenshtein'
|
|
14
|
+
autoload :Logger, 'cli/kit/logger'
|
|
15
|
+
autoload :Resolver, 'cli/kit/resolver'
|
|
16
|
+
autoload :Support, 'cli/kit/support'
|
|
17
|
+
autoload :System, 'cli/kit/system'
|
|
18
|
+
autoload :Util, 'cli/kit/util'
|
|
19
|
+
|
|
20
|
+
EXIT_FAILURE_BUT_NOT_BUG = 30
|
|
21
|
+
EXIT_BUG = 1
|
|
22
|
+
EXIT_SUCCESS = 0
|
|
23
|
+
|
|
24
|
+
# Abort, Bug, AbortSilent, and BugSilent are four ways of immediately bailing
|
|
25
|
+
# on command-line execution when an unrecoverable error occurs.
|
|
26
|
+
#
|
|
27
|
+
# Note that these don't inherit from StandardError, and so are not caught by
|
|
28
|
+
# a bare `rescue => e`.
|
|
29
|
+
#
|
|
30
|
+
# * Abort prints its message in red and exits 1;
|
|
31
|
+
# * Bug additionally submits the exception to Bugsnag;
|
|
32
|
+
# * AbortSilent and BugSilent do the same as above, but do not print
|
|
33
|
+
# messages before exiting.
|
|
34
|
+
#
|
|
35
|
+
# Treat these like panic() in Go:
|
|
36
|
+
# * Don't rescue them. Use a different Exception class if you plan to recover;
|
|
37
|
+
# * Provide a useful message, since it will be presented in brief to the
|
|
38
|
+
# user, and will be useful for debugging.
|
|
39
|
+
# * Avoid using it if it does actually make sense to recover from an error.
|
|
40
|
+
#
|
|
41
|
+
# Additionally:
|
|
42
|
+
# * Do not subclass these.
|
|
43
|
+
# * Only use AbortSilent or BugSilent if you prefer to print a more
|
|
44
|
+
# contextualized error than Abort or Bug would present to the user.
|
|
45
|
+
# * In general, don't attach a message to AbortSilent or BugSilent.
|
|
46
|
+
# * Never raise GenericAbort directly.
|
|
47
|
+
# * Think carefully about whether Abort or Bug is more appropriate. Is this
|
|
48
|
+
# a bug in the tool? Or is it just user error, transient network
|
|
49
|
+
# failure, etc.?
|
|
50
|
+
# * One case where it's ok to rescue (cli-kit internals or tests aside):
|
|
51
|
+
# 1. rescue Abort or Bug
|
|
52
|
+
# 2. Print a contextualized error message
|
|
53
|
+
# 3. Re-raise AbortSilent or BugSilent respectively.
|
|
54
|
+
GenericAbort = Class.new(Exception)
|
|
55
|
+
Abort = Class.new(GenericAbort)
|
|
56
|
+
Bug = Class.new(GenericAbort)
|
|
57
|
+
BugSilent = Class.new(GenericAbort)
|
|
58
|
+
AbortSilent = Class.new(GenericAbort)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require 'cli/kit'
|
|
2
|
+
|
|
3
|
+
module CLI
|
|
4
|
+
module Kit
|
|
5
|
+
module Autocall
|
|
6
|
+
def autocall(const, &block)
|
|
7
|
+
@autocalls ||= {}
|
|
8
|
+
@autocalls[const] = block
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def const_missing(const)
|
|
12
|
+
block = begin
|
|
13
|
+
@autocalls.fetch(const)
|
|
14
|
+
rescue KeyError
|
|
15
|
+
return super
|
|
16
|
+
end
|
|
17
|
+
const_set(const, block.call)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require 'cli/kit'
|
|
2
|
+
|
|
3
|
+
module CLI
|
|
4
|
+
module Kit
|
|
5
|
+
class BaseCommand
|
|
6
|
+
def self.defined?
|
|
7
|
+
true
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.statsd_increment(_metric, **_kwargs)
|
|
11
|
+
nil
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.statsd_time(_metric, **_kwargs)
|
|
15
|
+
yield
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.call(args, command_name)
|
|
19
|
+
cmd = new
|
|
20
|
+
stats_tags = cmd.stats_tags(args, command_name)
|
|
21
|
+
begin
|
|
22
|
+
statsd_increment("cli.command.invoked", tags: stats_tags)
|
|
23
|
+
statsd_time("cli.command.time", tags: stats_tags) do
|
|
24
|
+
cmd.call(args, command_name)
|
|
25
|
+
end
|
|
26
|
+
statsd_increment("cli.command.success", tags: stats_tags)
|
|
27
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
28
|
+
statsd_increment("cli.command.exception", tags: stats_tags + ["exception:#{e.class}"])
|
|
29
|
+
raise e
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def stats_tags(args, command_name)
|
|
34
|
+
tags = ["task:#{self.class}"]
|
|
35
|
+
tags << "command:#{command_name}" if command_name
|
|
36
|
+
tags << "subcommand:#{args.first}" if args&.first && has_subcommands?
|
|
37
|
+
tags
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def call(_args, _command_name)
|
|
41
|
+
raise NotImplementedError
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def has_subcommands?
|
|
45
|
+
false
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
require 'cli/kit'
|
|
2
|
+
|
|
3
|
+
module CLI
|
|
4
|
+
module Kit
|
|
5
|
+
class CommandRegistry
|
|
6
|
+
attr_reader :commands, :aliases
|
|
7
|
+
|
|
8
|
+
module NullContextualResolver
|
|
9
|
+
def self.command_names
|
|
10
|
+
[]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.aliases
|
|
14
|
+
{}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.command_class(_name)
|
|
18
|
+
nil
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def initialize(default:, contextual_resolver: nil)
|
|
23
|
+
@commands = {}
|
|
24
|
+
@aliases = {}
|
|
25
|
+
@default = default
|
|
26
|
+
@contextual_resolver = contextual_resolver || NullContextualResolver
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def resolved_commands
|
|
30
|
+
@commands.each_with_object({}) do |(k, v), a|
|
|
31
|
+
a[k] = resolve_class(v)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def add(const, name)
|
|
36
|
+
commands[name] = const
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def lookup_command(name)
|
|
40
|
+
name = @default if name.to_s.empty?
|
|
41
|
+
resolve_command(name)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def add_alias(from, to)
|
|
45
|
+
aliases[from] = to unless aliases[from]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def command_names
|
|
49
|
+
@contextual_resolver.command_names + commands.keys
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def exist?(name)
|
|
53
|
+
!resolve_command(name).first.nil?
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def resolve_alias(name)
|
|
59
|
+
aliases[name] || @contextual_resolver.aliases.fetch(name, name)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def resolve_command(name)
|
|
63
|
+
name = resolve_alias(name)
|
|
64
|
+
resolve_global_command(name) || \
|
|
65
|
+
resolve_contextual_command(name) || \
|
|
66
|
+
[nil, name]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def resolve_global_command(name)
|
|
70
|
+
klass = resolve_class(commands.fetch(name, nil))
|
|
71
|
+
return nil unless klass&.defined?
|
|
72
|
+
[klass, name]
|
|
73
|
+
rescue NameError
|
|
74
|
+
nil
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def resolve_contextual_command(name)
|
|
78
|
+
found = @contextual_resolver.command_names.include?(name)
|
|
79
|
+
return nil unless found
|
|
80
|
+
[@contextual_resolver.command_class(name), name]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def resolve_class(class_or_proc)
|
|
84
|
+
if class_or_proc.is_a?(Class)
|
|
85
|
+
class_or_proc
|
|
86
|
+
elsif class_or_proc.respond_to?(:call)
|
|
87
|
+
class_or_proc.call
|
|
88
|
+
else
|
|
89
|
+
class_or_proc
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
require 'cli/kit'
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
|
|
4
|
+
module CLI
|
|
5
|
+
module Kit
|
|
6
|
+
class Config
|
|
7
|
+
XDG_CONFIG_HOME = 'XDG_CONFIG_HOME'
|
|
8
|
+
|
|
9
|
+
def initialize(tool_name:)
|
|
10
|
+
@tool_name = tool_name
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Returns the config corresponding to `name` from the config file
|
|
14
|
+
# `false` is returned if it doesn't exist
|
|
15
|
+
#
|
|
16
|
+
# #### Parameters
|
|
17
|
+
# `section` : the section of the config value you are looking for
|
|
18
|
+
# `name` : the name of the config value you are looking for
|
|
19
|
+
#
|
|
20
|
+
# #### Returns
|
|
21
|
+
# `value` : the value of the config variable (false if none)
|
|
22
|
+
#
|
|
23
|
+
# #### Example Usage
|
|
24
|
+
# `config.get('name.of.config')`
|
|
25
|
+
#
|
|
26
|
+
def get(section, name, default: false)
|
|
27
|
+
all_configs.dig("[#{section}]", name) || default
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Coalesce and enforce the value of a config to a boolean
|
|
31
|
+
def get_bool(section, name, default: false)
|
|
32
|
+
case get(section, name, default: default).to_s
|
|
33
|
+
when "true"
|
|
34
|
+
true
|
|
35
|
+
when "false"
|
|
36
|
+
false
|
|
37
|
+
else
|
|
38
|
+
raise CLI::Kit::Abort, "Invalid config: #{section}.#{name} is expected to be true or false"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Sets the config value in the config file
|
|
43
|
+
#
|
|
44
|
+
# #### Parameters
|
|
45
|
+
# `section` : the section of the config you are setting
|
|
46
|
+
# `name` : the name of the config you are setting
|
|
47
|
+
# `value` : the value of the config you are setting
|
|
48
|
+
#
|
|
49
|
+
# #### Example Usage
|
|
50
|
+
# `config.set('section', 'name.of.config', 'value')`
|
|
51
|
+
#
|
|
52
|
+
def set(section, name, value)
|
|
53
|
+
all_configs["[#{section}]"] ||= {}
|
|
54
|
+
all_configs["[#{section}]"][name] = value.nil? ? nil : value.to_s
|
|
55
|
+
write_config
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Unsets a config value in the config file
|
|
59
|
+
#
|
|
60
|
+
# #### Parameters
|
|
61
|
+
# `section` : the section of the config you are deleting
|
|
62
|
+
# `name` : the name of the config you are deleting
|
|
63
|
+
#
|
|
64
|
+
# #### Example Usage
|
|
65
|
+
# `config.unset('section', 'name.of.config')`
|
|
66
|
+
#
|
|
67
|
+
def unset(section, name)
|
|
68
|
+
set(section, name, nil)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Gets the hash for the entire section
|
|
72
|
+
#
|
|
73
|
+
# #### Parameters
|
|
74
|
+
# `section` : the section of the config you are getting
|
|
75
|
+
#
|
|
76
|
+
# #### Example Usage
|
|
77
|
+
# `config.get_section('section')`
|
|
78
|
+
#
|
|
79
|
+
def get_section(section)
|
|
80
|
+
(all_configs["[#{section}]"] || {}).dup
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Returns a path from config in expanded form
|
|
84
|
+
# e.g. shopify corresponds to ~/src/shopify, but is expanded to /Users/name/src/shopify
|
|
85
|
+
#
|
|
86
|
+
# #### Example Usage
|
|
87
|
+
# `config.get_path('srcpath', 'shopify')`
|
|
88
|
+
#
|
|
89
|
+
# #### Returns
|
|
90
|
+
# `path` : the expanded path to the corrsponding value
|
|
91
|
+
#
|
|
92
|
+
def get_path(section, name = nil)
|
|
93
|
+
v = get(section, name)
|
|
94
|
+
false == v ? v : File.expand_path(v)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def to_s
|
|
98
|
+
ini.to_s
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# The path on disk at which the configuration is stored:
|
|
102
|
+
# `$XDG_CONFIG_HOME/<toolname>/config`
|
|
103
|
+
# if ENV['XDG_CONFIG_HOME'] is not set, we default to ~/.config, e.g.:
|
|
104
|
+
# ~/.config/tool/config
|
|
105
|
+
#
|
|
106
|
+
def file
|
|
107
|
+
config_home = ENV.fetch(XDG_CONFIG_HOME, '~/.config')
|
|
108
|
+
File.expand_path(File.join(@tool_name, 'config'), config_home)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
private
|
|
112
|
+
|
|
113
|
+
def all_configs
|
|
114
|
+
ini.ini
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def ini
|
|
118
|
+
@ini ||= CLI::Kit::Ini
|
|
119
|
+
.new(file, default_section: "[global]", convert_types: false)
|
|
120
|
+
.tap(&:parse)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def write_config
|
|
124
|
+
all_configs.each do |section, sub_config|
|
|
125
|
+
all_configs[section] = sub_config.reject { |_, value| value.nil? }
|
|
126
|
+
all_configs.delete(section) if all_configs[section].empty?
|
|
127
|
+
end
|
|
128
|
+
FileUtils.mkdir_p(File.dirname(file))
|
|
129
|
+
File.write(file, to_s)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|