danger-packwerk 0.8.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +47 -32
- data/lib/danger-packwerk/basic_reference_offense.rb +19 -19
- data/lib/danger-packwerk/check/default_formatter.rb +3 -3
- data/lib/danger-packwerk/{danger_deprecated_references_yml_changes.rb → danger_package_todo_yml_changes.rb} +25 -25
- data/lib/danger-packwerk/packwerk_wrapper.rb +10 -0
- data/lib/danger-packwerk/private/{deprecated_references.rb → package_todo.rb} +8 -8
- data/lib/danger-packwerk/private.rb +1 -1
- data/lib/danger-packwerk/version.rb +1 -1
- data/lib/danger-packwerk.rb +4 -2
- data/sorbet/rbi/gems/{packwerk@2.2.1.rbi → packwerk@2.2.1-7e8e7a50705833b41fe40c201eadb85ff9a1a422.rbi} +852 -231
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cf8895f7ae2e94e0b1d09e146176b6777ba8f952fe03381380071114fcec2bd4
|
4
|
+
data.tar.gz: 2e68e2096375433cfad68d85ca8d5eac7855dcb542b34f79417a53b1e4c9eb1b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bfbe7e86650ac170c89c014f7519d0d40abdb3d864b995d8cdb5ef2478ec3e4fe6f2e3afc4854dbc216e622a8adc99761bd7d9a420167553db6b70084446740f
|
7
|
+
data.tar.gz: b6729e1b52a5f25ae5da573d706bb1f3fc4217c750976dcd51aafd7611a5c7f53b4851727e3fd20cb66065570d2a99b0c4a4c367572c84fc08fb6918dfdfeb6b
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
`danger-packwerk` integrates [`packwerk`](https://github.com/Shopify/packwerk) with [`danger`](https://github.com/danger/danger) to provide inline comments in PRs related to boundaries in a Rails application.
|
4
4
|
|
5
|
-
## Installation
|
5
|
+
## Installation and Basic Usage
|
6
6
|
Step 1: Add this line to your `Gemfile` (to whatever group your CI uses, as it is not needed in production) and `bundle install`:
|
7
7
|
|
8
8
|
```ruby
|
@@ -13,41 +13,50 @@ Step 2: Add these to your `Dangerfile`:
|
|
13
13
|
|
14
14
|
```ruby
|
15
15
|
packwerk.check
|
16
|
-
|
16
|
+
package_todo_yml_changes.check
|
17
17
|
```
|
18
18
|
|
19
19
|
That's it for basic usage!
|
20
20
|
|
21
|
-
## Usage
|
21
|
+
## Advanced Usage
|
22
22
|
|
23
23
|
There are currently two danger checks that ship with `danger-packwerk`:
|
24
24
|
1) One that runs `bin/packwerk check` and leaves inline comments in source code on new violations
|
25
|
-
2) One that looks at changes to `
|
25
|
+
2) One that looks at changes to `package_todo.yml` files and leaves inline comments on added violations.
|
26
26
|
|
27
27
|
In upcoming iterations, we will include other danger checks, including:
|
28
28
|
1) A danger check that detects changes to `package.yml` files and posts user-configurable messages on the `package.yml` files that are modified.
|
29
29
|
2) A danger check that detects changes to `packwerk.yml` files and allows you to specify the action taken when that happens.
|
30
30
|
|
31
31
|
## packwerk.check
|
32
|
-
![This is an image displaying a comment from the Danger github bot
|
32
|
+
![This is an image displaying a comment from the Danger github bot after running bin/packwerk check.](docs/check_1.png)
|
33
|
+
![This is an image displaying a comment from the Danger github bot after running bin/packwerk check with the "quick suggestions" accordian open](docs/check_2.png)
|
33
34
|
|
34
|
-
Without any configuration, `packwerk.check` should just work. By default, it will post a maximum of 15 messages in a PR
|
35
|
+
Without any configuration, `packwerk.check` should just work. By default, it will post a maximum of 15 messages in a PR and it will not fail the build.
|
35
36
|
|
36
37
|
`packwerk.check` can be configured to in the following ways:
|
37
38
|
|
38
39
|
### Change the message that displays in the markdown
|
39
|
-
|
40
|
+
To customize the message in the GitHub comment, pass in `offenses_formatter` to `packwerk.check` in your `Dangerfile`. Here's a simple example:
|
40
41
|
```ruby
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
42
|
+
class MyFormatter
|
43
|
+
extend T::Sig
|
44
|
+
include DangerPackwerk::Check::OffensesFormatter
|
45
|
+
# Packwerk::ReferenceOffense: https://github.com/Shopify/packwerk/blob/main/lib/packwerk/reference_offense.rb
|
46
|
+
sig { override.params(offenses: T::Array[Packwerk::ReferenceOffense], repo_link: String, org_name: String).returns(String) }
|
47
|
+
def format_offenses(offenses, repo_link, org_name)
|
48
|
+
# your logic here
|
45
49
|
end
|
46
|
-
|
50
|
+
end
|
51
|
+
|
52
|
+
packwerk.check(offenses_formatter: MyFormatter.new)
|
47
53
|
```
|
48
54
|
|
49
|
-
|
50
|
-
|
55
|
+
If you'd like to keep the default messaging but add some context customized to your organization, you can pass that in as follows:
|
56
|
+
```ruby
|
57
|
+
custom_help_message = "Need help? Check out our internal docs [here](www.example.com)"
|
58
|
+
packwerk.check(offenses_formatter: DangerPackwerk::Check::DefaultFormatter.new(custom_help_message: custom_help_message))
|
59
|
+
```
|
51
60
|
|
52
61
|
### Fail the build on new violations
|
53
62
|
Simply pass in `fail_build: true` into `check`, as such:
|
@@ -75,42 +84,48 @@ packwerk.check(
|
|
75
84
|
)
|
76
85
|
```
|
77
86
|
|
78
|
-
##
|
79
|
-
![This is an image displaying
|
87
|
+
## package_todo_yml_changes.check
|
88
|
+
![This is an image displaying an inline comment from the Danger github bot.](docs/update.png)
|
80
89
|
|
81
|
-
Without any configuration, `
|
90
|
+
Without any configuration, `package_todo_yml_changes.check` should just work. By default, it will post a maximum of 15 messages in a PR, using default messaging defined within this gem.
|
82
91
|
|
83
|
-
`
|
92
|
+
`package_todo_yml_changes.check` can be configured to in the following ways:
|
84
93
|
|
85
94
|
### Change the message that displays in the markdown
|
86
|
-
|
95
|
+
To customize the message in the GitHub comment, pass in `offenses_formatter` to `package_todo_yml_changes.check` in your `Dangerfile`. Here's a simple example:
|
87
96
|
```ruby
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
97
|
+
class MyFormatter
|
98
|
+
extend T::Sig
|
99
|
+
include DangerPackwerk::Update::OffensesFormatter
|
100
|
+
# DangerPackwerk::BasicReferenceOffense
|
101
|
+
sig { override.params(offenses: T::Array[DangerPackwerk::BasicReferenceOffense], repo_link: String, org_name: String).returns(String) }
|
102
|
+
def format_offenses(offenses, repo_link, org_name)
|
103
|
+
# your logic here
|
92
104
|
end
|
93
|
-
|
105
|
+
end
|
106
|
+
|
107
|
+
package_todo_yml_changes.check(offenses_formatter: MyFormatter.new)
|
94
108
|
```
|
95
109
|
|
96
|
-
|
97
|
-
|
98
|
-
Need help?
|
99
|
-
|
110
|
+
If you'd like to keep the default messaging but add some context customized to your organization, you can pass that in as follows:
|
111
|
+
```ruby
|
112
|
+
custom_help_message = "Need help? Check out our internal docs [here](www.example.com)"
|
113
|
+
package_todo_yml_changes.check(offenses_formatter: DangerPackwerk::Update::DefaultFormatter.new(custom_help_message: custom_help_message))
|
114
|
+
```
|
100
115
|
|
101
116
|
### Change the max number of comments that will display
|
102
117
|
If you do not change this, the default max is 15. More information about why we chose this number in the source code.
|
103
118
|
```ruby
|
104
|
-
|
119
|
+
package_todo_yml_changes.check(max_comments: 3)
|
105
120
|
```
|
106
121
|
|
107
122
|
### Do something extra before we leave comments
|
108
123
|
Maybe you want to notify slack or do something else before we leave comments.
|
109
124
|
|
110
125
|
```ruby
|
111
|
-
|
112
|
-
# violation_diff is a DangerPackwerk::ViolationDiff and
|
113
|
-
before_comment: -> (violation_diff,
|
126
|
+
package_todo_yml_changes.check(
|
127
|
+
# violation_diff is a DangerPackwerk::ViolationDiff and changed_package_todo_ymls is a T::Array[String]
|
128
|
+
before_comment: -> (violation_diff, changed_package_todo_ymls) do
|
114
129
|
# Notify slack or otherwise do something extra!
|
115
130
|
end
|
116
131
|
)
|
@@ -4,8 +4,8 @@ module DangerPackwerk
|
|
4
4
|
#
|
5
5
|
# We call this BasicReferenceOffense as it is intended to have a subset of the interface of Packwerk::ReferenceOffense, located here:
|
6
6
|
# https://github.com/Shopify/packwerk/blob/a22862b59f7760abf22bda6804d41a52d05301d8/lib/packwerk/reference_offense.rb#L1
|
7
|
-
# However, we cannot actually construct a Packwerk::ReferenceOffense from `
|
8
|
-
# constructed in packwerk when packwerk parses the AST and actually outputs `
|
7
|
+
# However, we cannot actually construct a Packwerk::ReferenceOffense from `package_todo.yml` alone, since they are normally
|
8
|
+
# constructed in packwerk when packwerk parses the AST and actually outputs `package_todo.yml`, a process in which some information,
|
9
9
|
# such as the location where the constant is defined, is lost.
|
10
10
|
#
|
11
11
|
class BasicReferenceOffense < T::Struct
|
@@ -41,21 +41,21 @@ module DangerPackwerk
|
|
41
41
|
const :type, String
|
42
42
|
const :file_location, Location
|
43
43
|
|
44
|
-
sig { params(
|
45
|
-
def self.from(
|
46
|
-
|
44
|
+
sig { params(package_todo_yml: String).returns(T::Array[BasicReferenceOffense]) }
|
45
|
+
def self.from(package_todo_yml)
|
46
|
+
package_todo_yml_pathname = Pathname.new(package_todo_yml)
|
47
47
|
|
48
|
-
from_package = ParsePackwerk.package_from_path(
|
48
|
+
from_package = ParsePackwerk.package_from_path(package_todo_yml_pathname)
|
49
49
|
from_package_name = from_package.name
|
50
|
-
violations = Private::
|
50
|
+
violations = Private::PackageTodo.from(package_todo_yml_pathname).violations
|
51
51
|
|
52
52
|
# See the larger comment below for more information on why we need this information.
|
53
53
|
# This is a small optimization that lets us find the location of referenced files within
|
54
|
-
# a `
|
54
|
+
# a `package_todo.yml` file. Getting this now allows us to avoid reading through the file
|
55
55
|
# once for every referenced file in the inner loop below.
|
56
56
|
file_reference_to_line_number_index = T.let({}, T::Hash[String, T::Array[Integer]])
|
57
57
|
all_referenced_files = violations.flat_map(&:files).uniq
|
58
|
-
|
58
|
+
package_todo_yml_pathname.readlines.each_with_index do |line, index|
|
59
59
|
# We can use `find` here to exit early since each line will include one path that is unique to that file.
|
60
60
|
# Paths should not be substrings of each other, since they are all paths relative to the root.
|
61
61
|
file_on_line = all_referenced_files.find { |file| line.include?(file) }
|
@@ -69,24 +69,24 @@ module DangerPackwerk
|
|
69
69
|
violations.flat_map do |violation|
|
70
70
|
#
|
71
71
|
# We identify two locations associated with this violation.
|
72
|
-
# First, we find the reference to the constant within the `
|
73
|
-
# We know that each constant reference can occur only once per `
|
72
|
+
# First, we find the reference to the constant within the `package_todo.yml` file.
|
73
|
+
# We know that each constant reference can occur only once per `package_todo.yml` file
|
74
74
|
# The reason for this is that we know that only one file in the codebase can define a constant, and packwerk's constant_resolver will actually
|
75
75
|
# raise if this assumption is not true: https://github.com/Shopify/constant_resolver/blob/e78af0c8d5782b06292c068cfe4176e016c51b34/lib/constant_resolver.rb#L74
|
76
76
|
#
|
77
|
-
# Second, we find the reference to the specific file that references the constant within the `
|
78
|
-
# This can occur multiple times per `
|
77
|
+
# Second, we find the reference to the specific file that references the constant within the `package_todo.yml` file.
|
78
|
+
# This can occur multiple times per `package_todo.yml` file, but we know that the very first reference to the file after the class name key will be the one we care
|
79
79
|
# about, so we take the first instance that occurs after the class is listed.
|
80
80
|
#
|
81
|
-
# Note though that since one constant reference in a `
|
81
|
+
# Note though that since one constant reference in a `package_todo.yml` can be both a privacy and a dependency violation AND it can occur in many files,
|
82
82
|
# we need to group them. That is -- if `MyPrivateConstant` is both a dependency and a privacy violation AND it occurs in 10 files, that would represent 20 violations.
|
83
83
|
# Therefore we will group all of those 20 into one message to the user rather than providing 20 messages.
|
84
84
|
#
|
85
|
-
_line, class_name_line_number =
|
85
|
+
_line, class_name_line_number = package_todo_yml_pathname.readlines.each_with_index.find do |line, _index|
|
86
86
|
# If you have a class `::MyClass`, then you can get a false match if another constant in the file
|
87
87
|
# is named `MyOtherClass::MyClassThing`. Therefore we include quotes in our match to ensure that we match
|
88
88
|
# the constant and only the constant.
|
89
|
-
# Right now `packwerk` `
|
89
|
+
# Right now `packwerk` `package_todo.yml` files typically use double quotes, but sometimes folks linters change this to single quotes.
|
90
90
|
# To be defensive, we match against either.
|
91
91
|
class_name_with_quote_boundaries = /["|']#{violation.class_name}["|']:/
|
92
92
|
line.match?(class_name_with_quote_boundaries)
|
@@ -94,16 +94,16 @@ module DangerPackwerk
|
|
94
94
|
|
95
95
|
if class_name_line_number.nil?
|
96
96
|
debug_info = { class_name: violation.class_name, to_package_name: violation.to_package_name, type: violation.type }
|
97
|
-
raise "Unable to find reference to violation #{debug_info} in #{
|
97
|
+
raise "Unable to find reference to violation #{debug_info} in #{package_todo_yml}"
|
98
98
|
end
|
99
99
|
|
100
100
|
violation.files.map do |file|
|
101
101
|
file_line_numbers = file_reference_to_line_number_index.fetch(file, [])
|
102
102
|
file_line_number = file_line_numbers.select { |index| index > class_name_line_number }.min
|
103
|
-
raise "Unable to find reference to violation #{{ file: file, to_package_name: violation.to_package_name, type: violation.type }} in #{
|
103
|
+
raise "Unable to find reference to violation #{{ file: file, to_package_name: violation.to_package_name, type: violation.type }} in #{package_todo_yml}" if file_line_number.nil?
|
104
104
|
|
105
105
|
# We add one to the line number since `each_with_index` is zero-based indexed but Github line numbers are one-based indexed
|
106
|
-
file_location = Location.new(file:
|
106
|
+
file_location = Location.new(file: package_todo_yml, line_number: file_line_number + 1)
|
107
107
|
|
108
108
|
BasicReferenceOffense.new(
|
109
109
|
class_name: violation.class_name,
|
@@ -45,7 +45,7 @@ module DangerPackwerk
|
|
45
45
|
team_to_work_with = constant_source_package_ownership_info.owning_team ? constant_source_package_ownership_info.markdown_link_to_github_members_no_tag : 'the pack owner'
|
46
46
|
privacy_violation_message = "- Does API in #{constant_source_package.name}/public support this use case?\n - If not, can we work with #{team_to_work_with} to create and use a public API?\n - If `#{constant_name}` should already be public, try `bin/packs make_public #{constant_location}`."
|
47
47
|
|
48
|
-
if violation_types.include?(
|
48
|
+
if violation_types.include?(::DangerPackwerk::DEPENDENCY_VIOLATION_TYPE) && violation_types.include?(::DangerPackwerk::PRIVACY_VIOLATION_TYPE)
|
49
49
|
<<~MESSAGE
|
50
50
|
**Packwerk Violation**
|
51
51
|
- Type: Privacy :lock: + Dependency :knot:
|
@@ -65,7 +65,7 @@ module DangerPackwerk
|
|
65
65
|
|
66
66
|
_#{@custom_help_message}_
|
67
67
|
MESSAGE
|
68
|
-
elsif violation_types.include?(
|
68
|
+
elsif violation_types.include?(::DangerPackwerk::DEPENDENCY_VIOLATION_TYPE)
|
69
69
|
<<~MESSAGE
|
70
70
|
**Packwerk Violation**
|
71
71
|
- Type: Dependency :knot:
|
@@ -84,7 +84,7 @@ module DangerPackwerk
|
|
84
84
|
|
85
85
|
_#{@custom_help_message}_
|
86
86
|
MESSAGE
|
87
|
-
else # violation_types.include?(
|
87
|
+
else # violation_types.include?(::DangerPackwerk::PRIVACY_VIOLATION_TYPE)
|
88
88
|
<<~MESSAGE
|
89
89
|
**Packwerk Violation**
|
90
90
|
- Type: Privacy :lock:
|
@@ -9,7 +9,7 @@ require 'danger-packwerk/violation_diff'
|
|
9
9
|
require 'open3'
|
10
10
|
|
11
11
|
module DangerPackwerk
|
12
|
-
class
|
12
|
+
class DangerPackageTodoYmlChanges < Danger::Plugin
|
13
13
|
extend T::Sig
|
14
14
|
|
15
15
|
# We choose 5 here because violation additions tend to fall into a bimodal distribution, where most PRs only add a handful (<10) of new violations,
|
@@ -17,8 +17,8 @@ module DangerPackwerk
|
|
17
17
|
# Therefore we hope to capture the majority case of people making changes to code while not spamming PRs that do a big rename.
|
18
18
|
# We set a max (rather than unlimited) to avoid GitHub rate limiting and general spam if a PR does some sort of mass rename.
|
19
19
|
DEFAULT_MAX_COMMENTS = 5
|
20
|
-
BeforeComment = T.type_alias { T.proc.params(violation_diff: ViolationDiff,
|
21
|
-
DEFAULT_BEFORE_COMMENT = T.let(->(violation_diff,
|
20
|
+
BeforeComment = T.type_alias { T.proc.params(violation_diff: ViolationDiff, changed_package_todo_ymls: T::Array[String]).void }
|
21
|
+
DEFAULT_BEFORE_COMMENT = T.let(->(violation_diff, changed_package_todo_ymls) {}, BeforeComment)
|
22
22
|
|
23
23
|
sig do
|
24
24
|
params(
|
@@ -36,13 +36,13 @@ module DangerPackwerk
|
|
36
36
|
repo_link = github.pr_json[:base][:repo][:html_url]
|
37
37
|
org_name = github.pr_json[:base][:repo][:owner][:login]
|
38
38
|
|
39
|
-
|
39
|
+
changed_package_todo_ymls = (git.modified_files + git.added_files + git.deleted_files).grep(PACKAGE_TODO_PATTERN)
|
40
40
|
|
41
41
|
violation_diff = get_violation_diff
|
42
42
|
|
43
43
|
before_comment.call(
|
44
44
|
violation_diff,
|
45
|
-
|
45
|
+
changed_package_todo_ymls.to_a
|
46
46
|
)
|
47
47
|
|
48
48
|
current_comment_count = 0
|
@@ -67,16 +67,16 @@ module DangerPackwerk
|
|
67
67
|
added_violations = T.let([], T::Array[BasicReferenceOffense])
|
68
68
|
removed_violations = T.let([], T::Array[BasicReferenceOffense])
|
69
69
|
|
70
|
-
git.added_files.grep(
|
70
|
+
git.added_files.grep(PACKAGE_TODO_PATTERN).each do |added_package_todo_yml_file|
|
71
71
|
# Since the file is added, we know on the base commit there are no violations related to this pack,
|
72
72
|
# and that all violations from this file are new
|
73
|
-
added_violations += BasicReferenceOffense.from(
|
73
|
+
added_violations += BasicReferenceOffense.from(added_package_todo_yml_file)
|
74
74
|
end
|
75
75
|
|
76
|
-
git.deleted_files.grep(
|
76
|
+
git.deleted_files.grep(PACKAGE_TODO_PATTERN).each do |deleted_package_todo_yml_file|
|
77
77
|
# Since the file is deleted, we know on the HEAD commit there are no violations related to this pack,
|
78
78
|
# and that all violations from this file are deleted
|
79
|
-
deleted_violations = get_violations_before_patch_for(
|
79
|
+
deleted_violations = get_violations_before_patch_for(deleted_package_todo_yml_file)
|
80
80
|
removed_violations += deleted_violations
|
81
81
|
end
|
82
82
|
|
@@ -84,13 +84,13 @@ module DangerPackwerk
|
|
84
84
|
renamed_files_before = git.renamed_files.map { |before_after_file| before_after_file[:before] }
|
85
85
|
renamed_files_after = git.renamed_files.map { |before_after_file| before_after_file[:after] }
|
86
86
|
|
87
|
-
git.modified_files.grep(
|
88
|
-
# We skip over modified files if one of the modified files is a renamed `
|
87
|
+
git.modified_files.grep(PACKAGE_TODO_PATTERN).each do |modified_package_todo_yml_file|
|
88
|
+
# We skip over modified files if one of the modified files is a renamed `package_todo.yml` file.
|
89
89
|
# This allows us to rename packs while ignoring "new violations" in those renamed packs.
|
90
|
-
next if renamed_files_before.include?(
|
90
|
+
next if renamed_files_before.include?(modified_package_todo_yml_file)
|
91
91
|
|
92
|
-
head_commit_violations = BasicReferenceOffense.from(
|
93
|
-
base_commit_violations = get_violations_before_patch_for(
|
92
|
+
head_commit_violations = BasicReferenceOffense.from(modified_package_todo_yml_file)
|
93
|
+
base_commit_violations = get_violations_before_patch_for(modified_package_todo_yml_file)
|
94
94
|
added_violations += head_commit_violations - base_commit_violations
|
95
95
|
removed_violations += base_commit_violations - head_commit_violations
|
96
96
|
end
|
@@ -128,9 +128,9 @@ module DangerPackwerk
|
|
128
128
|
|
129
129
|
private
|
130
130
|
|
131
|
-
sig { params(
|
132
|
-
def get_violations_before_patch_for(
|
133
|
-
# The strategy to get the violations before this PR is to reverse the patch on each `
|
131
|
+
sig { params(package_todo_yml_file: String).returns(T::Array[BasicReferenceOffense]) }
|
132
|
+
def get_violations_before_patch_for(package_todo_yml_file)
|
133
|
+
# The strategy to get the violations before this PR is to reverse the patch on each `package_todo.yml`.
|
134
134
|
# A previous strategy attempted to use `git merge-base --fork-point`, but there are many situations where it returns
|
135
135
|
# empty values. That strategy is fickle because it depends on the state of the `reflog` within the CI suite, which appears
|
136
136
|
# to not be reliable to depend on.
|
@@ -139,24 +139,24 @@ module DangerPackwerk
|
|
139
139
|
# the PR without needing to use git commands that interpret the branch history based on local git history.
|
140
140
|
#
|
141
141
|
# We apply the patch to the original file so that we can seamlessly reverse the patch applied to that file (since patches are coupled to
|
142
|
-
# the files they modify). After parsing the violations from that `
|
142
|
+
# the files they modify). After parsing the violations from that `package_todo.yml` file with the patch reversed,
|
143
143
|
# we use a temporary copy of the original file to rewrite to it with the original contents.
|
144
144
|
# Note that practically speaking, we don't need to rewrite the original contents (since we already fetched the
|
145
145
|
# original contents above and the CI file system should be ephemeral). However, we do this anyways in case we later change these
|
146
146
|
# assumptions, or another client's environment is different and expects these files not to be mutated.
|
147
147
|
|
148
148
|
# Keep track of the original file contents. If the original file has been deleted, then we delete the file after inverting the patch at the end, rather than rewriting it.
|
149
|
-
|
149
|
+
package_todo_yml_file_copy = (File.read(package_todo_yml_file) if File.exist?(package_todo_yml_file))
|
150
150
|
|
151
151
|
Tempfile.create do |patch_file|
|
152
|
-
# Normally we'd use `git.diff_for_file(
|
152
|
+
# Normally we'd use `git.diff_for_file(package_todo_yml_file).patch` here, but there is a bug where it does not work for deleted files yet.
|
153
153
|
# I have a fix for that here: https://github.com/danger/danger/pull/1357
|
154
154
|
# Until that lands, I'm just using the underlying implementation of that method to get the diff for a file.
|
155
155
|
# Note that I might want to use a safe escape operator, `&.patch` and return gracefully if the patch cannot be found.
|
156
156
|
# However I'd be interested in why that ever happens, so for now going to proceed as is.
|
157
157
|
# (Note that better yet we'd have observability into these so I can just log under those circumstances rather than surfacing an error to the user,
|
158
158
|
# but we don't have that quite yet.)
|
159
|
-
patch_for_file = git.diff[
|
159
|
+
patch_for_file = git.diff[package_todo_yml_file].patch
|
160
160
|
# This appears to be a known issue that patches require new lines at the end. It seems like this is an issue with Danger that
|
161
161
|
# it gives us a patch without a newline.
|
162
162
|
# https://stackoverflow.com/questions/18142870/git-error-fatal-corrupt-patch-at-line-36
|
@@ -165,13 +165,13 @@ module DangerPackwerk
|
|
165
165
|
# https://git-scm.com/docs/git-apply
|
166
166
|
_stdout, _stderr, _status = Open3.capture3("git apply --reverse #{patch_file.path}")
|
167
167
|
# https://www.rubyguides.com/2019/05/ruby-tempfile/
|
168
|
-
BasicReferenceOffense.from(
|
168
|
+
BasicReferenceOffense.from(package_todo_yml_file)
|
169
169
|
end
|
170
170
|
ensure
|
171
|
-
if
|
172
|
-
File.write(
|
171
|
+
if package_todo_yml_file_copy
|
172
|
+
File.write(package_todo_yml_file, package_todo_yml_file_copy)
|
173
173
|
else
|
174
|
-
File.delete(
|
174
|
+
File.delete(package_todo_yml_file)
|
175
175
|
end
|
176
176
|
end
|
177
177
|
end
|
@@ -60,6 +60,16 @@ module DangerPackwerk
|
|
60
60
|
def show_stale_violations(offense_collection, for_files)
|
61
61
|
''
|
62
62
|
end
|
63
|
+
|
64
|
+
sig { override.params(strict_mode_violations: T::Array[::Packwerk::ReferenceOffense]).returns(::String) }
|
65
|
+
def show_strict_mode_violations(strict_mode_violations)
|
66
|
+
''
|
67
|
+
end
|
68
|
+
|
69
|
+
sig { override.returns(::String) }
|
70
|
+
def identifier
|
71
|
+
'offenses_aggregator'
|
72
|
+
end
|
63
73
|
end
|
64
74
|
end
|
65
75
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module DangerPackwerk
|
4
4
|
module Private
|
5
5
|
#
|
6
|
-
# The `Violation` and `
|
6
|
+
# The `Violation` and `PackageTodo` classes come from Gusto's private `ParsePackwerk` gem.
|
7
7
|
# Until we decide to open source that gem, we inline these as a private implementation detail of `DangerPackwerk` for now.
|
8
8
|
#
|
9
9
|
class Violation < T::Struct
|
@@ -25,22 +25,22 @@ module DangerPackwerk
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
class
|
28
|
+
class PackageTodo < T::Struct
|
29
29
|
extend T::Sig
|
30
30
|
|
31
31
|
const :pathname, Pathname
|
32
32
|
const :violations, T::Array[Violation]
|
33
33
|
|
34
|
-
sig { params(pathname: Pathname).returns(
|
34
|
+
sig { params(pathname: Pathname).returns(PackageTodo) }
|
35
35
|
def self.from(pathname)
|
36
36
|
if pathname.exist?
|
37
|
-
|
37
|
+
package_todo_loaded_yml = YAML.load_file(pathname)
|
38
38
|
|
39
39
|
all_violations = []
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
symbol_usage =
|
40
|
+
package_todo_loaded_yml&.each_key do |to_package_name|
|
41
|
+
package_todo_per_package = package_todo_loaded_yml[to_package_name]
|
42
|
+
package_todo_per_package.each_key do |class_name|
|
43
|
+
symbol_usage = package_todo_per_package[class_name]
|
44
44
|
files = symbol_usage['files']
|
45
45
|
violations = symbol_usage['violations']
|
46
46
|
all_violations << Violation.new(type: 'dependency', to_package_name: to_package_name, class_name: class_name, files: files) if violations.include? 'dependency'
|
data/lib/danger-packwerk.rb
CHANGED
@@ -5,10 +5,12 @@
|
|
5
5
|
require 'sorbet-runtime'
|
6
6
|
|
7
7
|
module DangerPackwerk
|
8
|
-
|
8
|
+
PACKAGE_TODO_PATTERN = T.let(/.*?package_todo\.yml\z/.freeze, Regexp)
|
9
|
+
DEPENDENCY_VIOLATION_TYPE = 'dependency'
|
10
|
+
PRIVACY_VIOLATION_TYPE = 'privacy'
|
9
11
|
|
10
12
|
require 'danger-packwerk/danger_packwerk'
|
11
|
-
require 'danger-packwerk/
|
13
|
+
require 'danger-packwerk/danger_package_todo_yml_changes'
|
12
14
|
require 'danger-packwerk/check/offenses_formatter'
|
13
15
|
require 'danger-packwerk/check/default_formatter'
|
14
16
|
require 'danger-packwerk/update/offenses_formatter'
|