diff --git a/Gemfile.lock b/Gemfile.lock index 9ce9bc7..cc33cdd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -17,7 +17,7 @@ GIT PATH remote: . specs: - packwerk-extensions (0.1.11) + packwerk-extensions (0.2.0) packwerk (>= 2.2.1) railties (>= 6.0.0) sorbet-runtime diff --git a/README.md b/README.md index 71c68fb..772fa93 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Currently, it ships the following checkers to help improve the boundaries betwee - A `privacy` checker that ensures other packages are using your package's public API - A `visibility` checker that allows packages to be private except to an explicit group of other packages. - A `folder_visibility` checker that allows packages to their sibling packs and parent pack (to be used in an application that uses folder packs) -- An `architecture` checker that allows packages to specify their "layer" and requires that each layer only communicate with layers below it. +- An `layer` checker that allows packages to specify their "layer" and requires that each layer only communicate with layers below it. ## Installation @@ -26,7 +26,7 @@ require: - packwerk/privacy/checker - packwerk/visibility/checker - packwerk/folder_visibility/checker - - packwerk/architecture/checker + - packwerk/layer/checker ``` ## Privacy Checker @@ -194,12 +194,12 @@ packs/b/packs/h OK (sibling) packs/c VIOLATION ``` -## Architecture Checker -The architecture checker can be used to enforce constraints on what can depend on what. +## Layer Checker +The layer checker can be used to enforce constraints on what can depend on what. -To enforce architecture for your package, first define the `architecture_layers` in `packwerk.yml`, for example: +To enforce layers for your package, first define the `layers` in `packwerk.yml`, for example: ``` -architecture_layers: +layers: - package - utility ``` @@ -207,7 +207,7 @@ architecture_layers: Then, turn on the checker in your package: ```yaml # components/merchandising/package.yml -enforce_architecture: true +enforce_layers: true layer: utility ``` diff --git a/lib/packwerk-extensions.rb b/lib/packwerk-extensions.rb index 3df1afb..8e48d8e 100644 --- a/lib/packwerk-extensions.rb +++ b/lib/packwerk-extensions.rb @@ -7,7 +7,7 @@ require 'packwerk/privacy/checker' require 'packwerk/visibility/checker' require 'packwerk/folder_visibility/checker' -require 'packwerk/architecture/checker' +require 'packwerk/layer/checker' module Packwerk module Extensions diff --git a/lib/packwerk/architecture/checker.rb b/lib/packwerk/layer/checker.rb similarity index 78% rename from lib/packwerk/architecture/checker.rb rename to lib/packwerk/layer/checker.rb index e45f18d..a332bfa 100644 --- a/lib/packwerk/architecture/checker.rb +++ b/lib/packwerk/layer/checker.rb @@ -1,16 +1,16 @@ # typed: strict # frozen_string_literal: true -require 'packwerk/architecture/layers' -require 'packwerk/architecture/package' -require 'packwerk/architecture/validator' +require 'packwerk/layer/layers' +require 'packwerk/layer/package' +require 'packwerk/layer/validator' module Packwerk - module Architecture + module Layer # This enforces "layered architecture," which allows each class to be designated as one of N layers # configured by the client in `packwerk.yml`, for example: # - # architecture_layers: + # layers: # - orchestrator # - business_domain # - platform @@ -18,7 +18,7 @@ module Architecture # - specification # # Then a package can configure: - # enforce_architecture: true | false | strict + # enforce_layers: true | false | strict # layer: utility # # This is intended to provide: @@ -30,7 +30,7 @@ class Checker extend T::Sig include Packwerk::Checker - VIOLATION_TYPE = T.let('architecture', String) + VIOLATION_TYPE = T.let('layer', String) sig { override.returns(String) } def violation_type @@ -55,7 +55,7 @@ def invalid_reference?(reference) end def strict_mode_violation?(listed_offense) constant_package = listed_offense.reference.package - constant_package.config['enforce_architecture'] == 'strict' + constant_package.config['enforce_layers'] == 'strict' end sig do @@ -68,9 +68,9 @@ def message(reference) referencing_package = Package.from(reference.package, layers) message = <<~MESSAGE - Architecture layer violation: '#{reference.constant.name}' belongs to '#{reference.constant.package}', whose architecture layer type is "#{constant_package.layer}." - This constant cannot be referenced by '#{reference.package}', whose architecture layer type is "#{referencing_package.layer}." - Packs in a lower layer may not access packs in a higher layer. See the `architecture_layers` in packwerk.yml. Current hierarchy: + Layer violation: '#{reference.constant.name}' belongs to '#{reference.constant.package}', whose layer type is "#{constant_package.layer}." + This constant cannot be referenced by '#{reference.package}', whose layer type is "#{referencing_package.layer}." + Packs in a lower layer may not access packs in a higher layer. See the `layers` in packwerk.yml. Current hierarchy: - #{layers.names_list.join("\n- ")} #{standard_help_message(reference)} @@ -92,7 +92,7 @@ def standard_help_message(reference) sig { returns(Layers) } def layers - @layers ||= T.let(Layers.new, T.nilable(Packwerk::Architecture::Layers)) + @layers ||= T.let(Layers.new, T.nilable(Packwerk::Layer::Layers)) end end end diff --git a/lib/packwerk/architecture/layers.rb b/lib/packwerk/layer/layers.rb similarity index 87% rename from lib/packwerk/architecture/layers.rb rename to lib/packwerk/layer/layers.rb index 24b91e7..32f622d 100644 --- a/lib/packwerk/architecture/layers.rb +++ b/lib/packwerk/layer/layers.rb @@ -2,7 +2,7 @@ # frozen_string_literal: true module Packwerk - module Architecture + module Layer class Layers extend T::Sig @@ -29,7 +29,7 @@ def names sig { returns(T::Array[String]) } def names_list - @names_list ||= YAML.load_file('packwerk.yml')['architecture_layers'] || [] + @names_list ||= YAML.load_file('packwerk.yml')['layers'] || [] end end end diff --git a/lib/packwerk/architecture/package.rb b/lib/packwerk/layer/package.rb similarity index 94% rename from lib/packwerk/architecture/package.rb rename to lib/packwerk/layer/package.rb index cb889a8..8f11494 100644 --- a/lib/packwerk/architecture/package.rb +++ b/lib/packwerk/layer/package.rb @@ -2,7 +2,7 @@ # frozen_string_literal: true module Packwerk - module Architecture + module Layer class Package < T::Struct extend T::Sig @@ -46,7 +46,7 @@ def from(package, layers) Package.new( layer: layer, - enforcement_setting: config['enforce_architecture'], + enforcement_setting: config['enforce_layers'], config: config ) end diff --git a/lib/packwerk/architecture/validator.rb b/lib/packwerk/layer/validator.rb similarity index 78% rename from lib/packwerk/architecture/validator.rb rename to lib/packwerk/layer/validator.rb index 6cde68c..7312c04 100644 --- a/lib/packwerk/architecture/validator.rb +++ b/lib/packwerk/layer/validator.rb @@ -2,7 +2,7 @@ # frozen_string_literal: true module Packwerk - module Architecture + module Layer class Validator extend T::Sig include Packwerk::Validator @@ -18,7 +18,7 @@ def call(package_set, configuration) f = Pathname.new(package.name).join('package.yml').to_s next if !config - result = check_enforce_architecture_setting(f, config['enforce_architecture']) + result = check_enforce_layers_setting(f, config['enforce_layers']) results << result next if !result.ok? @@ -34,12 +34,12 @@ def call(package_set, configuration) sig { returns(Layers) } def layers - @layers ||= T.let(Layers.new, T.nilable(Packwerk::Architecture::Layers)) + @layers ||= T.let(Layers.new, T.nilable(Packwerk::Layer::Layers)) end sig { override.returns(T::Array[String]) } def permitted_keys - %w[enforce_architecture layer] + %w[enforce_layers layer] end sig do @@ -52,7 +52,7 @@ def check_layer_setting(package, config_file_path) if layer.nil? && package.enforces? Result.new( ok: false, - error_value: "Invalid 'layer' option in #{config_file_path.inspect}: #{package.layer.inspect}. `layer` must be set if `enforce_architecture` is on." + error_value: "Invalid 'layer' option in #{config_file_path.inspect}: #{package.layer.inspect}. `layer` must be set if `enforce_layers` is on." ) elsif valid_layer Result.new(ok: true) @@ -67,19 +67,19 @@ def check_layer_setting(package, config_file_path) sig do params(config_file_path: String, setting: T.untyped).returns(Result) end - def check_enforce_architecture_setting(config_file_path, setting) + def check_enforce_layers_setting(config_file_path, setting) activated_value = [true, 'strict'].include?(setting) valid_value = [true, nil, false, 'strict'].include?(setting) layers_set = layers.names.any? if !valid_value Result.new( ok: false, - error_value: "Invalid 'enforce_architecture' option in #{config_file_path.inspect}: #{setting.inspect}" + error_value: "Invalid 'enforce_layers' option in #{config_file_path.inspect}: #{setting.inspect}" ) elsif activated_value && !layers_set Result.new( ok: false, - error_value: "Cannot set 'enforce_architecture' option in #{config_file_path.inspect} until `architectural_layers` have been specified in `packwerk.yml`" + error_value: "Cannot set 'enforce_layers' option in #{config_file_path.inspect} until `layers` have been specified in `packwerk.yml`" ) else Result.new(ok: true) diff --git a/packwerk-extensions.gemspec b/packwerk-extensions.gemspec index eba08ed..4d7e862 100644 --- a/packwerk-extensions.gemspec +++ b/packwerk-extensions.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |spec| spec.name = 'packwerk-extensions' - spec.version = '0.1.11' + spec.version = '0.2.0' spec.authors = ['Gusto Engineers'] spec.email = ['dev@gusto.com'] diff --git a/test/unit/architecture/checker_test.rb b/test/unit/layer/checker_test.rb similarity index 79% rename from test/unit/architecture/checker_test.rb rename to test/unit/layer/checker_test.rb index a3f54e8..ddce131 100644 --- a/test/unit/architecture/checker_test.rb +++ b/test/unit/layer/checker_test.rb @@ -4,7 +4,7 @@ require 'test_helper' module Packwerk - module Architecture + module Layer class CheckerTest < Minitest::Test extend T::Sig include FactoryHelper @@ -12,7 +12,7 @@ class CheckerTest < Minitest::Test def write_config write_app_file('packwerk.yml', <<~YML) - architecture_layers: + layers: - orchestrator - business_domain - platform @@ -22,11 +22,11 @@ def write_config end def orchestrator_pack(enforce: false) - Packwerk::Package.new(name: 'packs/orchestrator', config: { 'enforce_architecture' => enforce, 'layer' => 'orchestrator' }) + Packwerk::Package.new(name: 'packs/orchestrator', config: { 'enforce_layers' => enforce, 'layer' => 'orchestrator' }) end def utility_pack(enforce: false) - Packwerk::Package.new(name: 'packs/utility', config: { 'enforce_architecture' => enforce, 'layer' => 'utility' }) + Packwerk::Package.new(name: 'packs/utility', config: { 'enforce_layers' => enforce, 'layer' => 'utility' }) end setup do @@ -40,7 +40,7 @@ def utility_pack(enforce: false) end test 'ignores if origin package is not enforcing' do - checker = architecture_checker + checker = layer_checker reference = build_reference( source_package: utility_pack(enforce: false), destination_package: orchestrator_pack(enforce: false) @@ -50,7 +50,7 @@ def utility_pack(enforce: false) end test 'is an invalid reference if destination pack is above source package' do - checker = architecture_checker + checker = layer_checker reference = build_reference( source_package: utility_pack(enforce: true), destination_package: orchestrator_pack(enforce: false) @@ -60,9 +60,9 @@ def utility_pack(enforce: false) end test 'infers layer based on root directory' do - orchestrator_pack = Packwerk::Package.new(name: 'orchestrator/some_pack', config: { 'enforce_architecture' => true }) - utility_pack = Packwerk::Package.new(name: 'utility/some_other_pack', config: { 'enforce_architecture' => true }) - checker = architecture_checker + orchestrator_pack = Packwerk::Package.new(name: 'orchestrator/some_pack', config: { 'enforce_layers' => true }) + utility_pack = Packwerk::Package.new(name: 'utility/some_other_pack', config: { 'enforce_layers' => true }) + checker = layer_checker reference = build_reference( source_package: utility_pack, destination_package: orchestrator_pack @@ -74,9 +74,9 @@ def utility_pack(enforce: false) end test 'allows layer setting to override root directory location' do - orchestrator_pack = Packwerk::Package.new(name: 'orchestrator/some_pack', config: { 'layer' => 'specification', 'enforce_architecture' => true }) - utility_pack = Packwerk::Package.new(name: 'utility/some_other_pack', config: { 'enforce_architecture' => true }) - checker = architecture_checker + orchestrator_pack = Packwerk::Package.new(name: 'orchestrator/some_pack', config: { 'layer' => 'specification', 'enforce_layers' => true }) + utility_pack = Packwerk::Package.new(name: 'utility/some_other_pack', config: { 'enforce_layers' => true }) + checker = layer_checker reference = build_reference( source_package: utility_pack, destination_package: orchestrator_pack @@ -88,7 +88,7 @@ def utility_pack(enforce: false) end test 'is not an invalid reference if destination pack is below source package' do - checker = architecture_checker + checker = layer_checker reference = build_reference( source_package: orchestrator_pack(enforce: true), destination_package: utility_pack(enforce: false) @@ -103,10 +103,10 @@ def utility_pack(enforce: false) destination_package: orchestrator_pack(enforce: false) ) - assert_equal architecture_checker.message(reference), <<~MSG.chomp - Architecture layer violation: '::SomeName' belongs to 'packs/orchestrator', whose architecture layer type is "orchestrator." - This constant cannot be referenced by 'packs/utility', whose architecture layer type is "utility." - Packs in a lower layer may not access packs in a higher layer. See the `architecture_layers` in packwerk.yml. Current hierarchy: + assert_equal layer_checker.message(reference), <<~MSG.chomp + Layer violation: '::SomeName' belongs to 'packs/orchestrator', whose layer type is "orchestrator." + This constant cannot be referenced by 'packs/utility', whose layer type is "utility." + Packs in a lower layer may not access packs in a higher layer. See the `layers` in packwerk.yml. Current hierarchy: - orchestrator - business_domain - platform @@ -121,8 +121,8 @@ def utility_pack(enforce: false) private sig { returns(Checker) } - def architecture_checker - Packwerk::Architecture::Checker.new + def layer_checker + Packwerk::Layer::Checker.new end end end diff --git a/test/unit/architecture/validator_test.rb b/test/unit/layer/validator_test.rb similarity index 64% rename from test/unit/architecture/validator_test.rb rename to test/unit/layer/validator_test.rb index 2bc7ebd..5949e83 100644 --- a/test/unit/architecture/validator_test.rb +++ b/test/unit/layer/validator_test.rb @@ -4,7 +4,7 @@ require 'test_helper' module Packwerk - module Architecture + module Layer class ValidatorTest < Minitest::Test extend T::Sig include ApplicationFixtureHelper @@ -12,7 +12,7 @@ class ValidatorTest < Minitest::Test def write_config write_app_file('packwerk.yml', <<~YML) - architecture_layers: + layers: - package - utility YML @@ -28,7 +28,7 @@ def write_config teardown_application_fixture end - test 'call returns no error if enforce_architecture is unset' do + test 'call returns no error if enforce_layers is unset' do write_app_file('packwerk.yml', <<~YML) {} YML @@ -38,16 +38,16 @@ def write_config end test 'call returns an error for invalid enforce_visibility value' do - merge_into_app_yaml_file('package.yml', { 'enforce_architecture' => 'yes, please.' }) + merge_into_app_yaml_file('package.yml', { 'enforce_layers' => 'yes, please.' }) result = validator.call(package_set, config) refute result.ok? - assert_match(/Invalid 'enforce_architecture' option/, result.error_value) + assert_match(/Invalid 'enforce_layers' option/, result.error_value) end - test 'call returns success when enforce_architecture is set to strict' do - merge_into_app_yaml_file('package.yml', { 'enforce_architecture' => 'strict', 'layer' => 'utility' }) + test 'call returns success when enforce_layers is set to strict' do + merge_into_app_yaml_file('package.yml', { 'enforce_layers' => 'strict', 'layer' => 'utility' }) result = validator.call(package_set, config) assert result.ok? @@ -62,53 +62,53 @@ def write_config assert_match(/Invalid 'layer' option in.*?package.yml": "blah". Must be one of \["package", "utility"\]/, result.error_value) end - # We return no error here because it's possible we want to set layers for things so consuming packages can enforce their architecture + # We return no error here because it's possible we want to set layers for things so consuming packages can enforce their layer # without the publishing package needing to enforce it. - test 'call returns no error if a layer is set with enforce_architecture not on' do + test 'call returns no error if a layer is set with enforce_layers not on' do merge_into_app_yaml_file('package.yml', { 'layer' => 'utility' }) result = validator.call(package_set, config) assert result.ok? end - test 'call returns an error if a layer is unset with enforce_architecture on' do - merge_into_app_yaml_file('package.yml', { 'enforce_architecture' => true }) + test 'call returns an error if a layer is unset with enforce_layers on' do + merge_into_app_yaml_file('package.yml', { 'enforce_layers' => true }) result = validator.call(package_set, config) refute result.ok? - assert_match(/Invalid 'layer' option in.*?package.yml": nil. `layer` must be set if `enforce_architecture` is on./, result.error_value) + assert_match(/Invalid 'layer' option in.*?package.yml": nil. `layer` must be set if `enforce_layers` is on./, result.error_value) end - test 'call returns an error if enforce_architecture is set without layers specified' do + test 'call returns an error if enforce_layers is set without layers specified' do write_app_file('packwerk.yml', <<~YML) {} YML - merge_into_app_yaml_file('package.yml', { 'enforce_architecture' => true }) + merge_into_app_yaml_file('package.yml', { 'enforce_layers' => true }) result = validator.call(package_set, config) refute result.ok? - assert_match(/Cannot set 'enforce_architecture' option in.*?package.yml" until `architectural_layers` have been specified in `packwerk.yml`/, result.error_value) + assert_match(/Cannot set 'enforce_layers' option in.*?package.yml" until `layers` have been specified in `packwerk.yml`/, result.error_value) end test 'call returns no error for valid layer value' do - merge_into_app_yaml_file('package.yml', { 'enforce_architecture' => true, 'layer' => 'utility' }) + merge_into_app_yaml_file('package.yml', { 'enforce_layers' => true, 'layer' => 'utility' }) result = validator.call(package_set, config) assert result.ok? end test 'call returns no error for no layer value if layer is implied by root location' do - merge_into_app_yaml_file('utility/package.yml', { 'enforce_architecture' => true }) + merge_into_app_yaml_file('utility/package.yml', { 'enforce_layers' => true }) result = validator.call(package_set, config) assert result.ok? end - sig { returns(Packwerk::Architecture::Validator) } + sig { returns(Packwerk::Layer::Validator) } def validator - @validator ||= Packwerk::Architecture::Validator.new + @validator ||= Packwerk::Layer::Validator.new end end end