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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 51e9864c6b966cd4b115e469a1f9fc2c2e316fd634590d43eea1e5030b353f0c
4
- data.tar.gz: fbe8f70cb7c3d79017ea379ba7d8ec6fae2961fba98bb254f6b9ff99b1fa30be
3
+ metadata.gz: 0ad3f46c259911564eb4b955fb62e8152962db5c5682a6ba6000054dde9d8128
4
+ data.tar.gz: ef61b403058b20869cda462629724943317e55f5f4b1bfe125f9be7323a634c4
5
5
  SHA512:
6
- metadata.gz: 8cff9b5c28a2357e3c910cfa3c4ee0fd502267950165422ad95c74a07fd0f01f57c613e092670cc41de4d57921a9d88308b02d6de516b481b92a36bfa9fff19c
7
- data.tar.gz: fee7d3f4a94bed71d61ebcd878a63c8ad89bd60c9a44e4e18c4d2040442d2ede40483f28eb3ea66cefa69f3f0d5ecabadf3f3b4dc6b766b8b5d4e6ab2810fc4e
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 `added_offenses_formatter` to `deprecated_references_yml_changes.check` in your `Dangerfile`. Here's a simple example:
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
- added_offenses_formatter: -> (added_offenses) do
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
- added_offenses_formatter: AddedOffensesFormatter,
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
- added_offenses_formatter: DEFAULT_ADDED_OFFENSES_FORMATTER,
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
- added_offenses_formatter.call(violations),
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.call(unique_packwerk_reference_offenses)
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
@@ -1,7 +1,7 @@
1
1
  # typed: strict
2
2
 
3
3
  require 'danger-packwerk/private/deprecated_references'
4
- require 'danger-packwerk/private/default_offenses_formatter'
4
+ require 'danger-packwerk/private/ownership_information'
5
5
  require 'constant_resolver'
6
6
 
7
7
  module DangerPackwerk
@@ -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
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module DangerPackwerk
5
- VERSION = '0.7.1'
5
+ VERSION = '0.8.0'
6
6
  end
@@ -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