package_protections 1.1.1 → 1.4.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: 27281e8109b1edc0fff691f606a70260be38e130810e088d48e2d73d31424fbb
4
- data.tar.gz: cd99fe90189671daa334be828aaad40b0928742098cfb1430164f5080d3060ca
3
+ metadata.gz: 7b99c93d81087956b947c89fbede35844a2c973f20e2bee3c8f474383ff99a88
4
+ data.tar.gz: 64ad87cf10597e5fceeefa315dd3e665f7a684ac36db30c206012c15c2a0f3f2
5
5
  SHA512:
6
- metadata.gz: b1d22a6dee650ffbe8dd4027d5815b6c104f0825d9d257a40aee08dd44ad4c458f72b502e3ed28caa848f0aa6c468502edb4e4f6c5acd7943b808a8ca43096a3
7
- data.tar.gz: 91ac08c0a0697dad9fbacd7e7d62ab2a22ef058ad074b2d0371b4faf67f9b264aa4b7ed9a597fb09280b44fead58b641aeda165001568ccb7db015dff03d0bc4
6
+ metadata.gz: 9a5a70b90e8ecf6a00e8b22db867e6cec3a83ba34a8fa6f18b45449c96f3eb9fd248c80f9a047d9462e2976d233c8f6b29957837feef224a94d8ce93e008a55d
7
+ data.tar.gz: 500e49a77a3ee17aa2c4cb05edf2806e37010b5168ebb9cf1a13e1addbb5db5eb11c0caaa82f4a10451dbf96d304c7e010400b19ba7240b920b29a4c93780a90
@@ -88,27 +88,6 @@ module PackageProtections
88
88
  end
89
89
  end
90
90
 
91
- sig do
92
- params(
93
- package_names: T::Array[String],
94
- all_packages: T::Array[ParsePackwerk::Package]
95
- ).returns(T::Array[ParsePackwerk::Package])
96
- end
97
- def self.packages_for_names(package_names, all_packages)
98
- all_packages_indexed_by_name = {}
99
- all_packages.each { |package| all_packages_indexed_by_name[package.name] = package }
100
-
101
- package_names.map do |package_name|
102
- clean_pack_name = package_name.gsub(%r{/$}, '')
103
- package = all_packages_indexed_by_name[clean_pack_name]
104
- if package.nil?
105
- raise "Sorry, we couldn't find a package with name #{package_name}. Here are all of the package names we know about: #{all_packages.map(&:name).sort.inspect}"
106
- end
107
-
108
- package
109
- end
110
- end
111
-
112
91
  sig { params(root_pathname: Pathname).returns(String) }
113
92
  def self.rubocop_yml(root_pathname:)
114
93
  protected_packages = Dir.chdir(root_pathname) { all_protected_packages }
@@ -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,21 @@
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(root_pathname: Pathname.pwd) %>
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
+
18
+ config.before do
19
+ PackageProtections.bust_cache!
20
+ end
21
+ end
@@ -116,16 +116,6 @@ module PackageProtections
116
116
  Private.private_cop_config(identifier)
117
117
  end
118
118
 
119
- sig do
120
- params(
121
- package_names: T::Array[String],
122
- all_packages: T::Array[ParsePackwerk::Package]
123
- ).returns(T::Array[ParsePackwerk::Package])
124
- end
125
- def self.packages_for_names(package_names, all_packages)
126
- Private.packages_for_names(package_names, all_packages)
127
- end
128
-
129
119
  sig { void }
130
120
  def self.bust_cache!
131
121
  Private.bust_cache!
@@ -20,9 +20,7 @@ module RuboCop
20
20
  # This cop only works for files in `app`
21
21
  return if !relative_filename.include?('app/')
22
22
 
23
- match = relative_filename.match(%r{((#{::PackageProtections::EXPECTED_PACK_DIRECTORIES.join("|")})/.*?)/})
24
- package_name = match && match[1]
25
-
23
+ package_name = ParsePackwerk.package_from_path(relative_filename)&.name
26
24
  return if package_name.nil?
27
25
 
28
26
  return if relative_filepath.extname != '.rb'
@@ -105,7 +103,11 @@ module RuboCop
105
103
 
106
104
  # The reason for this is precondition is the `MultipleNamespacesProtection` assumes this to work properly.
107
105
  # To remove this precondition, we need to modify `MultipleNamespacesProtection` to be more generalized!
108
- if ::PackageProtections::EXPECTED_PACK_DIRECTORIES.include?(Pathname.new(package.name).dirname.to_s) || package.name == ParsePackwerk::ROOT_PACKAGE_NAME
106
+ is_root_package = package.name == ParsePackwerk::ROOT_PACKAGE_NAME
107
+ in_allowed_directory = ::PackageProtections::EXPECTED_PACK_DIRECTORIES.any? do |expected_package_directory|
108
+ package.directory.to_s.start_with?(expected_package_directory)
109
+ end
110
+ if in_allowed_directory || is_root_package
109
111
  nil
110
112
  else
111
113
  "Package #{package.name} must be located in one of #{::PackageProtections::EXPECTED_PACK_DIRECTORIES.join(', ')} (or be the root) to use this protection"
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.1.1
4
+ version: 1.4.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-06-30 00:00:00.000000000 Z
11
+ date: 2022-08-13 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