package_protections 1.1.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 64a576d5910c24c2f966b5f8db6c025af93a31b866c3956cd98b54adbc7d52da
4
- data.tar.gz: e8a696185cdffdb44eb1a136ad4dba73498d249faacadcb731d1559eb7e94847
3
+ metadata.gz: '0906fd78fb0cee92c4e34663c700eda94ba7e050636cda905a8fe184e115efb1'
4
+ data.tar.gz: c28f5248b823d626875dc5f7ac5ee33b7a533fbb000d0f6f67e439b7aec102cd
5
5
  SHA512:
6
- metadata.gz: 63194b36d9bd8edf9faab0136a58994b9a93dcfb49001ab75d057dbb7bf2522b1b235ffae8352ffbe6b3d3f13c8f5f171a3e7498ade041787ff72cd466762958
7
- data.tar.gz: 67417571f0fe028ef5960985518f753de09a7b253ff56dd878f4dec89e3ca0e796a9f928da78db68da1cd29e47bd6bbd56ebff4a4882941bf6635033eb2bf76b
6
+ metadata.gz: '00937a5119e9589c7ea323c76df2c7ed5feb3334356a99b7cb0308f469ba7af53e1cea358a74736aaf49238e5885741ffdfd54b52fc15316023cacf170bd16d4'
7
+ data.tar.gz: '0972c6fab2b22557de1d9e64efe882747c8088d2653869b6fad65f2a7144e012b61c3b15634c450ea375eb646cfe80f3d1bfbfd722342130a912e896eae1f6f4'
@@ -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
@@ -108,7 +108,7 @@ module PackageProtections
108
108
  T.absurd(violation_behavior)
109
109
  end
110
110
 
111
- package.original_package.directory.glob('**/**/*.*').each do |relative_path_to_file|
111
+ package.original_package.directory.glob(included_globs_for_pack).each do |relative_path_to_file|
112
112
  next unless exclude_list.include?(relative_path_to_file.to_s)
113
113
 
114
114
  file = relative_path_to_file.to_s
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.0
4
+ version: 1.3.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-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