danger-packwerk 0.7.1 → 0.8.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 +4 -4
- data/README.md +2 -2
- data/lib/danger-packwerk/check/default_formatter.rb +110 -0
- data/lib/danger-packwerk/check/offenses_formatter.rb +23 -0
- data/lib/danger-packwerk/danger_deprecated_references_yml_changes.rb +7 -5
- data/lib/danger-packwerk/danger_packwerk.rb +7 -5
- data/lib/danger-packwerk/private/ownership_information.rb +60 -0
- data/lib/danger-packwerk/private.rb +1 -1
- data/lib/danger-packwerk/update/default_formatter.rb +67 -0
- data/lib/danger-packwerk/update/offenses_formatter.rb +23 -0
- data/lib/danger-packwerk/version.rb +1 -1
- data/lib/danger-packwerk.rb +4 -0
- data/sorbet/rbi/gems/code_ownership@1.29.1.rbi +336 -0
- data/sorbet/rbi/gems/code_teams@1.0.0.rbi +120 -0
- metadata +23 -4
- data/lib/danger-packwerk/private/default_offenses_formatter.rb +0 -37
- data/sorbet/rbi/gems/json@2.6.2.rbi +0 -1418
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ad3f46c259911564eb4b955fb62e8152962db5c5682a6ba6000054dde9d8128
|
4
|
+
data.tar.gz: ef61b403058b20869cda462629724943317e55f5f4b1bfe125f9be7323a634c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cfc452185f83aef5f9ff5e53c475ac7472d7cfcda477a0a1f1fcc863704df6e93358a6e6096f5d4f1afb68176c687c8748a8c2c2562458c03bac47d120de91b4
|
7
|
+
data.tar.gz: '08df3f5e1d6e6d7367c3dc85f464346cf479dd13b5d831b26847b194f90190cb33e429c92095517124ae78eb0d23479c2a3b1146ab5a1ef529bfe967b9cf9192'
|
data/README.md
CHANGED
@@ -83,11 +83,11 @@ Without any configuration, `deprecated_references_yml_changes.check` should just
|
|
83
83
|
`deprecated_references_yml_changes.check` can be configured to in the following ways:
|
84
84
|
|
85
85
|
### Change the message that displays in the markdown
|
86
|
-
The default message displayed is from `lib/danger-packwerk/private/default_offenses_formatter.rb`. To customize this this, pass in `
|
86
|
+
The default message displayed is from `lib/danger-packwerk/private/default_offenses_formatter.rb`. To customize this this, pass in `offenses_formatter` to `deprecated_references_yml_changes.check` in your `Dangerfile`. Here's a simple example:
|
87
87
|
```ruby
|
88
88
|
deprecated_references_yml_changes.check(
|
89
89
|
# Offenses are a T::Array[DangerPackwerk::BasicReferenceOffense]
|
90
|
-
|
90
|
+
offenses_formatter: -> (added_offenses) do
|
91
91
|
"There are #{added_offenses.count} new violations this line!"
|
92
92
|
end
|
93
93
|
)
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
require 'code_ownership'
|
4
|
+
|
5
|
+
module DangerPackwerk
|
6
|
+
module Check
|
7
|
+
class DefaultFormatter
|
8
|
+
include OffensesFormatter
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
sig do
|
12
|
+
params(
|
13
|
+
custom_help_message: T.nilable(String)
|
14
|
+
).void
|
15
|
+
end
|
16
|
+
def initialize(custom_help_message: nil)
|
17
|
+
@custom_help_message = custom_help_message
|
18
|
+
end
|
19
|
+
|
20
|
+
sig do
|
21
|
+
override.params(
|
22
|
+
offenses: T::Array[Packwerk::ReferenceOffense],
|
23
|
+
repo_link: String,
|
24
|
+
org_name: String
|
25
|
+
).returns(String)
|
26
|
+
end
|
27
|
+
def format_offenses(offenses, repo_link, org_name)
|
28
|
+
reference_offense = T.must(offenses.first)
|
29
|
+
violation_types = offenses.map(&:violation_type)
|
30
|
+
referencing_file = reference_offense.reference.relative_path
|
31
|
+
referencing_file_pack = ParsePackwerk.package_from_path(referencing_file).name
|
32
|
+
# We remove leading double colons as they feel like an implementation detail of packwerk.
|
33
|
+
constant_name = reference_offense.reference.constant.name.delete_prefix('::')
|
34
|
+
|
35
|
+
constant_source_package_name = reference_offense.reference.constant.package.name
|
36
|
+
|
37
|
+
constant_location = reference_offense.reference.constant.location
|
38
|
+
constant_source_package = T.must(ParsePackwerk.all.find { |p| p.name == constant_source_package_name })
|
39
|
+
constant_source_package_ownership_info = Private::OwnershipInformation.for_package(constant_source_package, org_name)
|
40
|
+
|
41
|
+
disclaimer = 'Before you run `bin/packwerk update-deprecations`, check out these quick suggestions:'
|
42
|
+
referencing_code_in_right_pack = "- Does the code you are writing live in the right pack?\n - If not, try `bin/packs move packs/destination_pack #{referencing_file}`"
|
43
|
+
referenced_code_in_right_pack = "- Does #{constant_name} live in the right pack?\n - If not, try `bin/packs move packs/destination_pack #{constant_location}`"
|
44
|
+
dependency_violation_message = "- Do we actually want to depend on #{constant_source_package_name}?\n - If so, try `bin/packs add_dependency #{referencing_file_pack} #{constant_source_package_name}`\n - If not, what can we change about the design so we do not have to depend on #{constant_source_package_name}?"
|
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
|
+
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
|
+
|
48
|
+
if violation_types.include?(Packwerk::ViolationType::Dependency) && violation_types.include?(Packwerk::ViolationType::Privacy)
|
49
|
+
<<~MESSAGE
|
50
|
+
**Packwerk Violation**
|
51
|
+
- Type: Privacy :lock: + Dependency :knot:
|
52
|
+
- Constant: [<ins>`#{constant_name}`</ins>](#{repo_link}/blob/main/#{constant_location})
|
53
|
+
- Owning pack: #{constant_source_package_name}
|
54
|
+
#{constant_source_package_ownership_info.ownership_copy}
|
55
|
+
|
56
|
+
<details><summary>Quick suggestions :bulb:</summary>
|
57
|
+
|
58
|
+
#{disclaimer}
|
59
|
+
#{referencing_code_in_right_pack}
|
60
|
+
#{referenced_code_in_right_pack}
|
61
|
+
#{dependency_violation_message}
|
62
|
+
#{privacy_violation_message}
|
63
|
+
|
64
|
+
</details>
|
65
|
+
|
66
|
+
_#{@custom_help_message}_
|
67
|
+
MESSAGE
|
68
|
+
elsif violation_types.include?(Packwerk::ViolationType::Dependency)
|
69
|
+
<<~MESSAGE
|
70
|
+
**Packwerk Violation**
|
71
|
+
- Type: Dependency :knot:
|
72
|
+
- Constant: [<ins>`#{constant_name}`</ins>](#{repo_link}/blob/main/#{constant_location})
|
73
|
+
- Owning pack: #{constant_source_package_name}
|
74
|
+
#{constant_source_package_ownership_info.ownership_copy}
|
75
|
+
|
76
|
+
<details><summary>Quick suggestions :bulb:</summary>
|
77
|
+
|
78
|
+
#{disclaimer}
|
79
|
+
#{referencing_code_in_right_pack}
|
80
|
+
#{referenced_code_in_right_pack}
|
81
|
+
#{dependency_violation_message}
|
82
|
+
|
83
|
+
</details>
|
84
|
+
|
85
|
+
_#{@custom_help_message}_
|
86
|
+
MESSAGE
|
87
|
+
else # violation_types.include?(Packwerk::ViolationType::Privacy)
|
88
|
+
<<~MESSAGE
|
89
|
+
**Packwerk Violation**
|
90
|
+
- Type: Privacy :lock:
|
91
|
+
- Constant: [<ins>`#{constant_name}`</ins>](#{repo_link}/blob/main/#{constant_location})
|
92
|
+
- Owning pack: #{constant_source_package_name}
|
93
|
+
#{constant_source_package_ownership_info.ownership_copy}
|
94
|
+
|
95
|
+
<details><summary>Quick suggestions :bulb:</summary>
|
96
|
+
|
97
|
+
#{disclaimer}
|
98
|
+
#{referencing_code_in_right_pack}
|
99
|
+
#{referenced_code_in_right_pack}
|
100
|
+
#{privacy_violation_message}
|
101
|
+
|
102
|
+
</details>
|
103
|
+
|
104
|
+
_#{@custom_help_message}_
|
105
|
+
MESSAGE
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
require 'code_ownership'
|
4
|
+
|
5
|
+
module DangerPackwerk
|
6
|
+
module Check
|
7
|
+
module OffensesFormatter
|
8
|
+
extend T::Sig
|
9
|
+
extend T::Helpers
|
10
|
+
|
11
|
+
interface!
|
12
|
+
|
13
|
+
sig do
|
14
|
+
abstract.params(
|
15
|
+
offenses: T::Array[Packwerk::ReferenceOffense],
|
16
|
+
repo_link: String,
|
17
|
+
org_name: String
|
18
|
+
).returns(String)
|
19
|
+
end
|
20
|
+
def format_offenses(offenses, repo_link, org_name); end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -17,23 +17,25 @@ 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
|
-
AddedOffensesFormatter = T.type_alias { T.proc.params(added_violations: T::Array[BasicReferenceOffense]).returns(String) }
|
21
|
-
DEFAULT_ADDED_OFFENSES_FORMATTER = T.let(->(added_violations) { Private::DefaultAddedOffensesFormatter.format(added_violations) }, AddedOffensesFormatter)
|
22
20
|
BeforeComment = T.type_alias { T.proc.params(violation_diff: ViolationDiff, changed_deprecated_references_ymls: T::Array[String]).void }
|
23
21
|
DEFAULT_BEFORE_COMMENT = T.let(->(violation_diff, changed_deprecated_references_ymls) {}, BeforeComment)
|
24
22
|
|
25
23
|
sig do
|
26
24
|
params(
|
27
|
-
|
25
|
+
offenses_formatter: T.nilable(Update::OffensesFormatter),
|
28
26
|
before_comment: BeforeComment,
|
29
27
|
max_comments: Integer
|
30
28
|
).void
|
31
29
|
end
|
32
30
|
def check(
|
33
|
-
|
31
|
+
offenses_formatter: nil,
|
34
32
|
before_comment: DEFAULT_BEFORE_COMMENT,
|
35
33
|
max_comments: DEFAULT_MAX_COMMENTS
|
36
34
|
)
|
35
|
+
offenses_formatter ||= Update::DefaultFormatter.new
|
36
|
+
repo_link = github.pr_json[:base][:repo][:html_url]
|
37
|
+
org_name = github.pr_json[:base][:repo][:owner][:login]
|
38
|
+
|
37
39
|
changed_deprecated_references_ymls = (git.modified_files + git.added_files + git.deleted_files).grep(DEPRECATED_REFERENCES_PATTERN)
|
38
40
|
|
39
41
|
violation_diff = get_violation_diff
|
@@ -51,7 +53,7 @@ module DangerPackwerk
|
|
51
53
|
location = T.must(violations.first).file_location
|
52
54
|
|
53
55
|
markdown(
|
54
|
-
|
56
|
+
offenses_formatter.format_offenses(violations, repo_link, org_name),
|
55
57
|
line: location.line_number,
|
56
58
|
file: location.file
|
57
59
|
)
|
@@ -19,8 +19,6 @@ module DangerPackwerk
|
|
19
19
|
DEFAULT_MAX_COMMENTS = 15
|
20
20
|
OnFailure = T.type_alias { T.proc.params(offenses: T::Array[Packwerk::ReferenceOffense]).void }
|
21
21
|
DEFAULT_ON_FAILURE = T.let(->(offenses) {}, OnFailure)
|
22
|
-
OffensesFormatter = T.type_alias { T.proc.params(offenses: T::Array[Packwerk::ReferenceOffense]).returns(String) }
|
23
|
-
DEFAULT_OFFENSES_FORMATTER = T.let(->(offenses) { offenses.map(&:message).join("\n\n") }, OffensesFormatter)
|
24
22
|
DEFAULT_FAIL = false
|
25
23
|
DEFAULT_FAILURE_MESSAGE = 'Packwerk violations were detected! Please resolve them to unblock the build.'
|
26
24
|
|
@@ -35,8 +33,8 @@ module DangerPackwerk
|
|
35
33
|
|
36
34
|
sig do
|
37
35
|
params(
|
36
|
+
offenses_formatter: T.nilable(Check::OffensesFormatter),
|
38
37
|
max_comments: Integer,
|
39
|
-
offenses_formatter: OffensesFormatter,
|
40
38
|
fail_build: T::Boolean,
|
41
39
|
failure_message: String,
|
42
40
|
on_failure: OnFailure,
|
@@ -44,13 +42,17 @@ module DangerPackwerk
|
|
44
42
|
).void
|
45
43
|
end
|
46
44
|
def check(
|
45
|
+
offenses_formatter: nil,
|
47
46
|
max_comments: DEFAULT_MAX_COMMENTS,
|
48
|
-
offenses_formatter: DEFAULT_OFFENSES_FORMATTER,
|
49
47
|
fail_build: DEFAULT_FAIL,
|
50
48
|
failure_message: DEFAULT_FAILURE_MESSAGE,
|
51
49
|
on_failure: DEFAULT_ON_FAILURE,
|
52
50
|
grouping_strategy: CommentGroupingStrategy::PerConstantPerLocation
|
53
51
|
)
|
52
|
+
offenses_formatter ||= Check::DefaultFormatter.new
|
53
|
+
repo_link = github.pr_json[:base][:repo][:html_url]
|
54
|
+
org_name = github.pr_json[:base][:repo][:owner][:login]
|
55
|
+
|
54
56
|
# This is important because by default, Danger will leave a concantenated list of all its messages if it can't find a commentable place in the
|
55
57
|
# diff to leave its message. This is an especially bad UX because it will be a huge wall of text not connected to the source of the issue.
|
56
58
|
# Furthermore, dismissing these ensures that something like moving a file from pack to pack does not trigger the danger message. That is,
|
@@ -120,7 +122,7 @@ module DangerPackwerk
|
|
120
122
|
line_number = reference_offense.location&.line
|
121
123
|
referencing_file = reference_offense.reference.relative_path
|
122
124
|
|
123
|
-
message = offenses_formatter.
|
125
|
+
message = offenses_formatter.format_offenses(unique_packwerk_reference_offenses, repo_link, org_name)
|
124
126
|
|
125
127
|
markdown(message, file: referencing_file, line: line_number)
|
126
128
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
require 'code_ownership'
|
4
|
+
|
5
|
+
module DangerPackwerk
|
6
|
+
module Private
|
7
|
+
class OwnershipInformation < T::Struct
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
const :owning_team, T.nilable(CodeTeams::Team)
|
11
|
+
const :github_team, T.nilable(String)
|
12
|
+
const :slack_channel, T.nilable(String)
|
13
|
+
const :org_name, T.nilable(String)
|
14
|
+
|
15
|
+
sig { params(package: ParsePackwerk::Package, org_name: String).returns(OwnershipInformation) }
|
16
|
+
def self.for_package(package, org_name)
|
17
|
+
team = CodeOwnership.for_package(package)
|
18
|
+
|
19
|
+
if team.nil?
|
20
|
+
OwnershipInformation.new
|
21
|
+
else
|
22
|
+
OwnershipInformation.new(
|
23
|
+
owning_team: team,
|
24
|
+
github_team: team.raw_hash.fetch('github', {}).fetch('team', nil),
|
25
|
+
slack_channel: team.raw_hash.fetch('slack', {}).fetch('room_for_humans', nil),
|
26
|
+
org_name: org_name
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
sig { returns(String) }
|
32
|
+
def ownership_copy
|
33
|
+
github_team_flow_sensitive = github_team
|
34
|
+
slack_channel_flow_sensitive = slack_channel
|
35
|
+
|
36
|
+
if owning_team && github_team_flow_sensitive && slack_channel_flow_sensitive
|
37
|
+
team_slack_link = markdown_link_to_slack_room
|
38
|
+
"- Owned by #{markdown_link_to_github_members_no_tag} (Slack: #{team_slack_link})"
|
39
|
+
else
|
40
|
+
'- This pack is unowned.'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
sig { returns(String) }
|
45
|
+
def markdown_link_to_slack_room
|
46
|
+
"[<ins>#{slack_channel}</ins>](https://slack.com/app_redirect?channel=#{T.must(slack_channel).delete('#')})"
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# Note this will NOT tag the team on Github, but it will link
|
51
|
+
# to the mentioned team's members page. If you want to tag and
|
52
|
+
# link the team, simply use the string and Github will handle it.
|
53
|
+
#
|
54
|
+
sig { returns(String) }
|
55
|
+
def markdown_link_to_github_members_no_tag
|
56
|
+
"[<ins>#{github_team}</ins>](https://github.com/orgs/#{org_name}/teams/#{T.must(github_team).gsub("@#{org_name}/", '')}/members)"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
module DangerPackwerk
|
4
|
+
module Update
|
5
|
+
class DefaultFormatter
|
6
|
+
extend T::Sig
|
7
|
+
include OffensesFormatter
|
8
|
+
|
9
|
+
sig do
|
10
|
+
params(
|
11
|
+
custom_help_message: T.nilable(String)
|
12
|
+
).void
|
13
|
+
end
|
14
|
+
def initialize(custom_help_message: nil)
|
15
|
+
@custom_help_message = custom_help_message
|
16
|
+
end
|
17
|
+
|
18
|
+
sig { override.params(offenses: T::Array[BasicReferenceOffense], repo_link: String, org_name: String).returns(String) }
|
19
|
+
def format_offenses(offenses, repo_link, org_name)
|
20
|
+
violation = T.must(offenses.first)
|
21
|
+
referencing_file_pack = ParsePackwerk.package_from_path(violation.file)
|
22
|
+
# We remove leading double colons as they feel like an implementation detail of packwerk.
|
23
|
+
constant_name = violation.class_name.delete_prefix('::')
|
24
|
+
constant_source_package_name = violation.to_package_name
|
25
|
+
|
26
|
+
constant_source_package = T.must(ParsePackwerk.find(constant_source_package_name))
|
27
|
+
constant_source_package_owner = Private::OwnershipInformation.for_package(constant_source_package, org_name)
|
28
|
+
|
29
|
+
package_referring_to_constant_owner = Private::OwnershipInformation.for_package(referencing_file_pack, org_name)
|
30
|
+
|
31
|
+
disclaimer = 'We noticed you ran `bin/packwerk update-deprecations`. Check out [the docs](https://github.com/Shopify/packwerk/blob/main/RESOLVING_VIOLATIONS.md) to see other ways to resolve violations.'
|
32
|
+
pluralized_violation = offenses.count > 1 ? 'these violations' : 'this violation'
|
33
|
+
request_to_add_context = "- Could you add some context as a reply here about why we needed to add #{pluralized_violation}?\n"
|
34
|
+
|
35
|
+
dependency_violation_message = "- cc #{package_referring_to_constant_owner.github_team} (#{package_referring_to_constant_owner.markdown_link_to_slack_room}) for the dependency violation.\n" if package_referring_to_constant_owner.owning_team
|
36
|
+
|
37
|
+
privacy_violation_message = "- cc #{constant_source_package_owner.github_team} (#{constant_source_package_owner.markdown_link_to_slack_room}) for the privacy violation.\n" if constant_source_package_owner.owning_team
|
38
|
+
|
39
|
+
if offenses.any?(&:dependency?) && offenses.any?(&:privacy?)
|
40
|
+
<<~MESSAGE.chomp
|
41
|
+
Hi again! It looks like `#{constant_name}` is private API of `#{constant_source_package_name}`, which is also not in `#{referencing_file_pack.name}`'s list of dependencies.
|
42
|
+
#{disclaimer}
|
43
|
+
|
44
|
+
#{request_to_add_context}#{dependency_violation_message}#{privacy_violation_message}
|
45
|
+
#{@custom_help_message}
|
46
|
+
MESSAGE
|
47
|
+
elsif offenses.any?(&:dependency?)
|
48
|
+
<<~MESSAGE.chomp
|
49
|
+
Hi again! It looks like `#{constant_name}` belongs to `#{constant_source_package_name}`, which is not in `#{referencing_file_pack.name}`'s list of dependencies.
|
50
|
+
#{disclaimer}
|
51
|
+
|
52
|
+
#{request_to_add_context}#{dependency_violation_message}
|
53
|
+
#{@custom_help_message}
|
54
|
+
MESSAGE
|
55
|
+
else # violations.any?(&:privacy?)
|
56
|
+
<<~MESSAGE.chomp
|
57
|
+
Hi again! It looks like `#{constant_name}` is private API of `#{constant_source_package_name}`.
|
58
|
+
#{disclaimer}
|
59
|
+
|
60
|
+
#{request_to_add_context}#{privacy_violation_message}
|
61
|
+
#{@custom_help_message}
|
62
|
+
MESSAGE
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
require 'code_ownership'
|
4
|
+
|
5
|
+
module DangerPackwerk
|
6
|
+
module Update
|
7
|
+
module OffensesFormatter
|
8
|
+
extend T::Sig
|
9
|
+
extend T::Helpers
|
10
|
+
|
11
|
+
interface!
|
12
|
+
|
13
|
+
sig do
|
14
|
+
abstract.params(
|
15
|
+
offenses: T::Array[BasicReferenceOffense],
|
16
|
+
repo_link: String,
|
17
|
+
org_name: String
|
18
|
+
).returns(String)
|
19
|
+
end
|
20
|
+
def format_offenses(offenses, repo_link, org_name); end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/danger-packwerk.rb
CHANGED
@@ -9,4 +9,8 @@ module DangerPackwerk
|
|
9
9
|
|
10
10
|
require 'danger-packwerk/danger_packwerk'
|
11
11
|
require 'danger-packwerk/danger_deprecated_references_yml_changes'
|
12
|
+
require 'danger-packwerk/check/offenses_formatter'
|
13
|
+
require 'danger-packwerk/check/default_formatter'
|
14
|
+
require 'danger-packwerk/update/offenses_formatter'
|
15
|
+
require 'danger-packwerk/update/default_formatter'
|
12
16
|
end
|