package_protections 1.1.1 → 1.2.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
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 41803352508cd5d4ede0fe0caf1121516474fe3e95a030e4db7e0f05badfa9a2
         | 
| 4 | 
            +
              data.tar.gz: bc594511c3b3da35dcd9efcfba0f989928e074e0da429a8f0f538e0537eff5be
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 352aa3ea22bf0d7cf765023c35ed88c6026fda2b52036aa73fbcf35e4aca1df52df24486ce028a238dcf50db89f2864f4080e77d6da52d9f7be7fa984cd70dc7
         | 
| 7 | 
            +
              data.tar.gz: 6bb8ab188d27a3656cd6979d00fcaf725165176b17a42704be114ef0bbfbe7150facf21ae44ebc3c1633ce5b21876fd9b8088179c6e702f8addd49f890d7e1e3
         | 
| @@ -0,0 +1,54 @@ | |
| 1 | 
            +
            # typed: true
         | 
| 2 | 
            +
            # frozen_string_literal: true
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require 'fileutils'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module ApplicationFixtureHelper
         | 
| 7 | 
            +
              def write_file(path, content = '')
         | 
| 8 | 
            +
                pathname = Pathname.new(path)
         | 
| 9 | 
            +
                FileUtils.mkdir_p(pathname.dirname)
         | 
| 10 | 
            +
                pathname.write(content)
         | 
| 11 | 
            +
                path
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              def write_package_yml(
         | 
| 15 | 
            +
                pack_name,
         | 
| 16 | 
            +
                dependencies: [],
         | 
| 17 | 
            +
                enforce_dependencies: true,
         | 
| 18 | 
            +
                enforce_privacy: true,
         | 
| 19 | 
            +
                protections: {},
         | 
| 20 | 
            +
                global_namespaces: [],
         | 
| 21 | 
            +
                visible_to: []
         | 
| 22 | 
            +
              )
         | 
| 23 | 
            +
                defaults = {
         | 
| 24 | 
            +
                  'prevent_this_package_from_violating_its_stated_dependencies' => 'fail_on_new',
         | 
| 25 | 
            +
                  'prevent_other_packages_from_using_this_packages_internals' => 'fail_on_new',
         | 
| 26 | 
            +
                  'prevent_this_package_from_exposing_an_untyped_api' => 'fail_on_new',
         | 
| 27 | 
            +
                  'prevent_this_package_from_creating_other_namespaces' => 'fail_on_new',
         | 
| 28 | 
            +
                  'prevent_other_packages_from_using_this_package_without_explicit_visibility' => 'fail_never'
         | 
| 29 | 
            +
                }
         | 
| 30 | 
            +
                protections_with_defaults = defaults.merge(protections)
         | 
| 31 | 
            +
                metadata = { 'protections' => protections_with_defaults }
         | 
| 32 | 
            +
                if visible_to.any?
         | 
| 33 | 
            +
                  metadata.merge!('visible_to' => visible_to)
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                if global_namespaces.any?
         | 
| 37 | 
            +
                  metadata.merge!('global_namespaces' => global_namespaces)
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                package = ParsePackwerk::Package.new(
         | 
| 41 | 
            +
                  name: pack_name,
         | 
| 42 | 
            +
                  dependencies: dependencies,
         | 
| 43 | 
            +
                  enforce_dependencies: enforce_dependencies,
         | 
| 44 | 
            +
                  enforce_privacy: enforce_privacy,
         | 
| 45 | 
            +
                  metadata: metadata
         | 
| 46 | 
            +
                )
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                ParsePackwerk.write_package_yml!(package)
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
              def delete_app_file(path)
         | 
| 52 | 
            +
                File.delete(path)
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
            end
         | 
| @@ -0,0 +1,99 @@ | |
| 1 | 
            +
            def offense(
         | 
| 2 | 
            +
              package_name, message, file, violation_type
         | 
| 3 | 
            +
            )
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              package = ParsePackwerk.all.find { |p| p.name == package_name }
         | 
| 6 | 
            +
              PackageProtections::Offense.new(
         | 
| 7 | 
            +
                package: package,
         | 
| 8 | 
            +
                message: message,
         | 
| 9 | 
            +
                file: file,
         | 
| 10 | 
            +
                violation_type: violation_type
         | 
| 11 | 
            +
              )
         | 
| 12 | 
            +
            end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            def serialize_offenses_diff(actual_offenses, expected_offense)
         | 
| 15 | 
            +
              color_by_match = ->(actual, expected) { actual == expected ? Rainbow(actual).green : "#{Rainbow(actual).red} (expected: #{expected})" }
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              actual_offenses.map do |offense|
         | 
| 18 | 
            +
                # We color each field red or green depending on if the attributes match our expected
         | 
| 19 | 
            +
                <<~SERIALIZED_OFFENSE
         | 
| 20 | 
            +
                  File: #{color_by_match.call(offense.file, expected_offense.file)}
         | 
| 21 | 
            +
                  Message: #{color_by_match.call(offense.message, expected_offense.message)}
         | 
| 22 | 
            +
                  Violation Type: #{color_by_match.call(offense.violation_type, expected_offense.violation_type)}
         | 
| 23 | 
            +
                  Package: #{color_by_match.call(offense.package.name, expected_offense.package.name)}
         | 
| 24 | 
            +
                SERIALIZED_OFFENSE
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
            end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            def serialize_offenses(actual_offenses)
         | 
| 29 | 
            +
              actual_offenses.map do |offense|
         | 
| 30 | 
            +
                <<~SERIALIZED_OFFENSE
         | 
| 31 | 
            +
                  File: #{offense.file}
         | 
| 32 | 
            +
                  Message: #{offense.message}
         | 
| 33 | 
            +
                  Violation Type: #{offense.violation_type}
         | 
| 34 | 
            +
                  Package: #{offense.package.name}
         | 
| 35 | 
            +
                SERIALIZED_OFFENSE
         | 
| 36 | 
            +
              end
         | 
| 37 | 
            +
            end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            RSpec::Matchers.define(:include_offense) do |expected_offense|
         | 
| 40 | 
            +
              match do |actual_offenses|
         | 
| 41 | 
            +
                @actual_offenses = actual_offenses
         | 
| 42 | 
            +
                @expected_offense = expected_offense
         | 
| 43 | 
            +
                if ENV['DEBUG']
         | 
| 44 | 
            +
                  PackageProtections.print_offenses(actual_offenses)
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
                @matching_offense = actual_offenses.find do |actual_offense|
         | 
| 47 | 
            +
                  actual_offense.file == expected_offense.file &&
         | 
| 48 | 
            +
                    actual_offense.message == expected_offense.message &&
         | 
| 49 | 
            +
                    actual_offense.violation_type == expected_offense.violation_type &&
         | 
| 50 | 
            +
                    actual_offense.package.name == expected_offense.package.name
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
                !@matching_offense.nil?
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              description do
         | 
| 56 | 
            +
                "to have an offense with type `#{expected_offense.type}` tied to package `#{expected_offense.package_name}` with message `#{expected_offense.message}` and instances `#{expected_offense.submessages.join(', ')}`"
         | 
| 57 | 
            +
              end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
              failure_message do
         | 
| 60 | 
            +
                <<~MSG
         | 
| 61 | 
            +
                  Could not find offense! Here are the found offenses:
         | 
| 62 | 
            +
                  #{serialize_offenses_diff(@actual_offenses, expected_offense).join("\n\n")}
         | 
| 63 | 
            +
                MSG
         | 
| 64 | 
            +
              end
         | 
| 65 | 
            +
            end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
            RSpec::Matchers.define(:contain_exactly) do |number_of_offenses|
         | 
| 68 | 
            +
              match do |actual_offenses|
         | 
| 69 | 
            +
                @actual_offenses = actual_offenses || []
         | 
| 70 | 
            +
                @offenses = []
         | 
| 71 | 
            +
                @actual_offenses.each do |offense|
         | 
| 72 | 
            +
                  @offenses << offense
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
                @offenses.size == number_of_offenses
         | 
| 75 | 
            +
              end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
              chain :offense, :number_of_offenses
         | 
| 78 | 
            +
              chain :offenses, :number_of_offenses
         | 
| 79 | 
            +
             | 
| 80 | 
            +
              description do
         | 
| 81 | 
            +
                'to contain offenses'
         | 
| 82 | 
            +
              end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
              failure_message_when_negated do
         | 
| 85 | 
            +
                "Found the following offenses:\n#{@offenses.map { |r| "#{r.package_name}: #{r.message}" }}"
         | 
| 86 | 
            +
              end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
              failure_message do
         | 
| 89 | 
            +
                if @offenses.empty?
         | 
| 90 | 
            +
                  "Found #{@offenses.size} instead."
         | 
| 91 | 
            +
                else
         | 
| 92 | 
            +
                  <<~MSG
         | 
| 93 | 
            +
                    Found #{@offenses.size} instead.
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                    #{serialize_offenses(@offenses).join("\n")}
         | 
| 96 | 
            +
                  MSG
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
              end
         | 
| 99 | 
            +
            end
         | 
| @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # Require this file to load code that supports testing using RSpec.
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            require_relative 'application_fixture_helper'
         | 
| 6 | 
            +
            require_relative 'matchers'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            def get_resulting_rubocop
         | 
| 9 | 
            +
              write_file('config/default.yml', <<~YML.strip)
         | 
| 10 | 
            +
                <%= PackageProtections.rubocop_yml %>
         | 
| 11 | 
            +
              YML
         | 
| 12 | 
            +
              YAML.safe_load(ERB.new(File.read('config/default.yml')).result(binding))
         | 
| 13 | 
            +
            end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            RSpec.configure do |config|
         | 
| 16 | 
            +
              config.include ApplicationFixtureHelper
         | 
| 17 | 
            +
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: package_protections
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1. | 
| 4 | 
            +
              version: 1.2.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Gusto Engineers
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2022- | 
| 11 | 
            +
            date: 2022-07-01 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: activesupport
         | 
| @@ -186,6 +186,9 @@ files: | |
| 186 186 | 
             
            - lib/package_protections/private/visibility_protection.rb
         | 
| 187 187 | 
             
            - lib/package_protections/protected_package.rb
         | 
| 188 188 | 
             
            - lib/package_protections/protection_interface.rb
         | 
| 189 | 
            +
            - lib/package_protections/rspec/application_fixture_helper.rb
         | 
| 190 | 
            +
            - lib/package_protections/rspec/matchers.rb
         | 
| 191 | 
            +
            - lib/package_protections/rspec/support.rb
         | 
| 189 192 | 
             
            - lib/package_protections/rubocop_protection_interface.rb
         | 
| 190 193 | 
             
            - lib/package_protections/violation_behavior.rb
         | 
| 191 194 | 
             
            - lib/rubocop/cop/package_protections/namespaced_under_package_name.rb
         |