diff --git a/README.md b/README.md index 872aee5b7..7ef1214bd 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,7 @@ The settings in the `shipit.yml` file relate to the different things you can do * [Custom Tasks](#custom-tasks) (`tasks`) * [Custom links](#custom-links) (`links`) * [Review Process](#review-process) (`review.checklist`, `review.monitoring`, `review.checks`) +* [Inherit From](#inherit-from)(`inherit_from`) All the settings in `shipit.yml` are optional. Most applications can be deployed from Shipit without any configuration. @@ -130,6 +131,8 @@ Also, if your repository is deployed different ways depending on the environment For example for a stack like: `my-org/my-repo/staging`, `shipit.staging.yml` will have priority over `shipit.yml`. +In order to reduce duplication across different environment specific files, you can specify an `inherit_from` key in your relevant `shipit.yml` file. This key expects a string of the file name to inherit from. If this key is specified, a deep-merge will be performed on the file therein, overwriting any duplicated values from the parent. See [Inherit From](#inherit-from)(`inherit_From`) for example. + Lastly, if you override the `app_name` configuration in your Shipit deployment, `yourapp.yml` and `yourapp.staging.yml` will work. * * * @@ -613,6 +616,42 @@ review: - bundle exec rake db:migrate:status ``` +

Inherit From

+ +If the `inherit_from` key is specified, a deep-merge will be performed on the file therein, overwriting any duplicated values from the parent. Keys may be chained across files. Example: + +``` yaml +# shipit.production.yml +inherit_from: shipit.staging.yml + +machine: + environment: + PUBLIC: true +``` + +``` yaml +# shipit.staging.yml +inherit_from: shipit.yml + +deploy: + override: + - ./some_deployment_process.sh ${PUBLIC} +``` + +``` yaml +# shipit.yml + +machine: + environment: + TEST: true + PUBLIC: false +``` + +Loading 'shipit.production.yml' would result in: +```rb +{"machine"=>{"environment"=>{"TEST"=>true, "PUBLIC"=>true}}, "deploy"=>{"override"=>["./some_deployment_process.sh ${PUBLIC}"]}} +``` +

Shell commands timeout

All the shell commands can take an optional `timeout` parameter. This is the value in seconds that a command can be inactive before Shipit will terminate the task. diff --git a/app/models/shipit/deploy_spec/file_system.rb b/app/models/shipit/deploy_spec/file_system.rb index e5efe2491..0fca76532 100644 --- a/app/models/shipit/deploy_spec/file_system.rb +++ b/app/models/shipit/deploy_spec/file_system.rb @@ -100,7 +100,8 @@ def load_config return { 'deploy' => { 'pre' => [shipit_not_obeying_bare_file_echo_command, 'exit 1'] } } end - read_config(config_file_path) + config_obj = read_config(config_file_path) + build_config(config_file_path, config_obj) end def shipit_file_names_in_priority_order @@ -136,6 +137,20 @@ def app_name @app_name ||= Shipit.app_name.downcase end + SHIPIT_CONFIG_INHERIT_FROM_KEY = "inherit_from" + def build_config(path, config_obj) + return config_obj if config_obj.blank? || !config_obj.key?(SHIPIT_CONFIG_INHERIT_FROM_KEY) + + inherits_from_path = path.dirname.join(config_obj.delete(SHIPIT_CONFIG_INHERIT_FROM_KEY)) + if inherits_from_path.exist? + inherits_config_obj = read_config(inherits_from_path) + config_obj = inherits_config_obj.deep_merge(config_obj) + path = inherits_from_path + end + + build_config(path, config_obj) + end + def read_config(path) SafeYAML.load(path.read) if path.exist? end diff --git a/test/models/shipit/deploy_spec/file_system_test.rb b/test/models/shipit/deploy_spec/file_system_test.rb index ece77e67b..4e48c939a 100644 --- a/test/models/shipit/deploy_spec/file_system_test.rb +++ b/test/models/shipit/deploy_spec/file_system_test.rb @@ -63,6 +63,35 @@ class FileSystemTest < ActiveSupport::TestCase assert loaded_config["deploy"]["pre"].include?('exit 1') end + test '#load_config builds proper config if inherit_from is present' do + Shipit.expects(:respect_bare_shipit_file?).returns(true).at_least_once + stack = shipit_stacks(:shipit) + deploy_spec = Shipit::DeploySpec::FileSystem.new(Dir.tmpdir, stack) + deploy_spec.expects(:config_file_path).returns(Pathname.new(Dir.tmpdir) + '/shipit_1.yml').at_least_once + deploy_spec.expects(:read_config).returns(SafeYAML.load(deploy_spec_inherit_from_yaml), SafeYAML.load(deploy_spec_yaml)).at_least_once + Pathname.any_instance.stubs(:exist?).returns(true) + loaded_config = deploy_spec.send(:load_config) + assert loaded_config.key?("deploy") + assert loaded_config["deploy"].key?("pre") + assert loaded_config["deploy"]["pre"].include?("test 2") + assert loaded_config["deploy"]["override"].include?("test 11") + assert_not loaded_config.include?(Shipit::DeploySpec::FileSystem::SHIPIT_CONFIG_INHERIT_FROM_KEY) + end + + test '#load_config builds valid config if inherit_from path is missing' do + Shipit.expects(:respect_bare_shipit_file?).returns(true).at_least_once + stack = shipit_stacks(:shipit) + deploy_spec = Shipit::DeploySpec::FileSystem.new(Dir.tmpdir, stack) + deploy_spec.expects(:config_file_path).returns(Pathname.new(Dir.tmpdir) + '/shipit_1.yml').at_least_once + deploy_spec.expects(:read_config).returns(SafeYAML.load(deploy_spec_inherit_from_yaml)).at_least_once + Pathname.any_instance.stubs(:exist?).returns(false) + loaded_config = deploy_spec.send(:load_config) + assert loaded_config.key?("deploy") + assert_not loaded_config["deploy"].include?("pre") + assert loaded_config["deploy"]["override"].include?("test 11") + assert_not loaded_config.include?(Shipit::DeploySpec::FileSystem::SHIPIT_CONFIG_INHERIT_FROM_KEY) + end + def deploy_spec_yaml <<~EOYAML deploy: @@ -73,6 +102,15 @@ def deploy_spec_yaml EOYAML end + def deploy_spec_inherit_from_yaml + <<~EOYAML + inherit_from: shipit.yml + deploy: + override: + - test 11 + EOYAML + end + def deploy_spec_missing_deploy_yaml <<~EOYAML production_platform: