packwerk 2.2.2 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/RESOLVING_VIOLATIONS.md +7 -7
- data/TROUBLESHOOT.md +1 -1
- data/USAGE.md +11 -11
- data/lib/packwerk/cli.rb +11 -1
- data/lib/packwerk/formatters/offenses_formatter.rb +1 -1
- data/lib/packwerk/offense_collection.rb +38 -26
- data/lib/packwerk/{deprecated_references.rb → package_todo.rb} +18 -8
- data/lib/packwerk/parse_run.rb +3 -3
- data/lib/packwerk/version.rb +1 -1
- data/lib/packwerk.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 94a6e793dd2c071a7bfb9a739fb6aaf2e07b0d2183bf743afb38da5e3847f0e2
|
4
|
+
data.tar.gz: dd779094dad5572746476d25d63e26f29819cf0b57519f73a4f693610cfd238e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9ff4d2a2aa7b6cb34e59362321ea3fb22826d10430addeef1b6143408d1a1b510ff8915105f2e74e9173334dfcfbd0a76610ea00242a2720c6feb9c75b53556f
|
7
|
+
data.tar.gz: 7ed4cbe3376821d54a5a58e2bc25ac3baf1f03a84928e027c0e1e1f39d52a14df4e18cc8d9ecfa50de3756507f46c0e9687d04464d3ec149037a7fdecb8eed0e
|
data/Gemfile.lock
CHANGED
data/RESOLVING_VIOLATIONS.md
CHANGED
@@ -3,27 +3,27 @@
|
|
3
3
|
Violations can be [recorded as a deprecation](#recording-violations) or (better!) [eliminated](#eliminating-violations).
|
4
4
|
|
5
5
|
## Recording Violations
|
6
|
-
💡 New privacy and dependency violations are never hard-blocked. There are many very valid reasons to run `bin/packwerk update-
|
6
|
+
💡 New privacy and dependency violations are never hard-blocked. There are many very valid reasons to run `bin/packwerk update-todo`, adding new violations to `package_todo.yml` files. Even if you feel your reason might not be "valid," if your judgement says adding the violation and shipping your change will produce positive impact, trust your gut.
|
7
7
|
|
8
8
|
### Emergency Fixes
|
9
9
|
❔ Is it a revert or is there a lot of urgency because you are fixing a production bug impacting customers?
|
10
10
|
|
11
|
-
➡️ Simply run `bin/packwerk update-
|
11
|
+
➡️ Simply run `bin/packwerk update-todo`, and address the violation when the customer issue is resolved.
|
12
12
|
|
13
13
|
### Improving System Design
|
14
14
|
❔ Are you improving system boundaries by renaming or moving a file, class, constant,` or module?
|
15
15
|
|
16
|
-
➡️ Simply run `bin/packwerk update-
|
16
|
+
➡️ Simply run `bin/packwerk update-todo`. We've improved how our system is organized, so the new violations are natural.
|
17
17
|
|
18
18
|
### Making Things Explicit
|
19
19
|
❔ Are you making something that was implicit (hidden to Packwerk) explicit, such as adding a Sorbet signature, using a class instead of a method call, or something similar?
|
20
20
|
|
21
|
-
➡️ Simply run `bin/packwerk update-
|
21
|
+
➡️ Simply run `bin/packwerk update-todo`. Making something implicit explicit and capturing that as a new violation is a strict improvement.
|
22
22
|
|
23
23
|
### Temporary State
|
24
24
|
❔ Is the violation temporary because you will soon delete the code or is it part of a refactor to improve system boundaries and reduce violations overall?
|
25
25
|
|
26
|
-
➡️ Simply run `bin/packwerk update-
|
26
|
+
➡️ Simply run `bin/packwerk update-todo`. Sometimes things get "worse" before they get better.
|
27
27
|
|
28
28
|
### Delivering Features
|
29
29
|
❔ Are you in a rush to get a feature out and product, your manager, or an internal sense of urgency has made you feel like you can't resolve system design issues?
|
@@ -57,7 +57,7 @@ An explicit and implementation-hiding public API is a cornerstone of well-modula
|
|
57
57
|
|
58
58
|
➡️ Work together on a new public API and use that instead! If the thing we're using should be public, move it to the public folder to make it public!
|
59
59
|
|
60
|
-
⛈️ If working with the package's owner to improve the API is not possible, run `bin/packwerk update-
|
60
|
+
⛈️ If working with the package's owner to improve the API is not possible, run `bin/packwerk update-todo`. Add some context to your PR about why it's not possible.
|
61
61
|
|
62
62
|
### Dependency Violations
|
63
63
|
💡 Packwerk thinks something is a dependency violation if you're referencing a constant, class, module defined ANYWHERE but your package doesn't list it as an explicit dependency in its `package.yml`. We care about these because it allows us to be systematically intentional about what our code needs to run and helps us untangle and remove dependency cycles from our system.
|
@@ -78,4 +78,4 @@ Thoughtful dependency management is another cornerstone of well-modularized code
|
|
78
78
|
|
79
79
|
➡️ Work with the owners of the relevant packages, as well as your team, to think through a design that doesn't include the unwanted dependency.
|
80
80
|
|
81
|
-
⛈️ If this is not possible within the scope of your changes (think hard about this one!), run `bin/packwerk update-
|
81
|
+
⛈️ If this is not possible within the scope of your changes (think hard about this one!), run `bin/packwerk update-todo`. Add some context to your PR about why it's not possible, and any additional context you may have, such as a possible solution.
|
data/TROUBLESHOOT.md
CHANGED
@@ -16,7 +16,7 @@ You can specify folders or packages in Packwerk commands for a shorter run time:
|
|
16
16
|
|
17
17
|
bin/packwerk check components/your_package
|
18
18
|
|
19
|
-
bin/packwerk update-
|
19
|
+
bin/packwerk update-todo components/your_package
|
20
20
|
|
21
21
|
_Note: You cannot specify folders or packages for `bin/packwerk validate` because the command runs for the entire application._
|
22
22
|
|
data/USAGE.md
CHANGED
@@ -20,7 +20,7 @@
|
|
20
20
|
* [Resolving new violations](#resolving-new-violations)
|
21
21
|
* [Understanding how to respond to new violations](#understanding-how-to-respond-to-new-violations)
|
22
22
|
* [Recording existing violations](#recording-existing-violations)
|
23
|
-
* [Understanding the
|
23
|
+
* [Understanding the package todo file](#understanding-the-package-todo-file)
|
24
24
|
|
25
25
|
## What problem does Packwerk solve?
|
26
26
|
|
@@ -235,28 +235,28 @@ See: [RESOLVING_VIOLATIONS.md](RESOLVING_VIOLATIONS.md)
|
|
235
235
|
|
236
236
|
For existing codebases, packages are likely to have existing boundary violations.
|
237
237
|
|
238
|
-
If so, you will want to stop the bleeding and prevent more violations from occuring. The existing violations in the codebase can be recorded in a [
|
238
|
+
If so, you will want to stop the bleeding and prevent more violations from occuring. The existing violations in the codebase can be recorded in a [todo list](#understanding-the-package-todo-file) by executing:
|
239
239
|
|
240
|
-
bin/packwerk update-
|
240
|
+
bin/packwerk update-todo
|
241
241
|
|
242
|
-
Similar to `bin/packwerk check`, you may also run `bin/packwerk update-
|
242
|
+
Similar to `bin/packwerk check`, you may also run `bin/packwerk update-todo` on folders or packages:
|
243
243
|
|
244
|
-
bin/packwerk update-
|
244
|
+
bin/packwerk update-todo components/your_package
|
245
245
|
|
246
246
|
![](static/packwerk_update.gif)
|
247
247
|
|
248
248
|
_Note: Changing dependencies or enabling dependencies will not require a full update of the codebase, only the package that changed. On the other hand, changing or enabling privacy will require a full update of the codebase._
|
249
249
|
|
250
|
-
`bin/packwerk update-
|
250
|
+
`bin/packwerk update-todo` should only be run to record existing violations and to remove violations that have been worked off. Running `bin/packwerk update-todo` to resolve a violation should be the very last resort.
|
251
251
|
|
252
252
|
See: [TROUBLESHOOT.md - Troubleshooting violations](TROUBLESHOOT.md#Troubleshooting_violations)
|
253
253
|
|
254
254
|
|
255
|
-
### Understanding the
|
255
|
+
### Understanding the package todo file
|
256
256
|
|
257
|
-
The
|
257
|
+
The package TODO list is called `package_todo.yml` and can be found in the package folder. The list outlines the constant violations of the package, where the violation is located, and the file defining the violation.
|
258
258
|
|
259
|
-
The
|
259
|
+
The package TODO list should not be added to, but worked off over time.
|
260
260
|
|
261
261
|
```yaml
|
262
262
|
components/merchant:
|
@@ -267,11 +267,11 @@ components/merchant:
|
|
267
267
|
- components/merchant/app/public/merchant/generate_order.rb
|
268
268
|
```
|
269
269
|
|
270
|
-
Above is an example of a constant violation entry in `
|
270
|
+
Above is an example of a constant violation entry in `package_todo.yml`.
|
271
271
|
|
272
272
|
* `components/merchant` - package where the constant violation is found
|
273
273
|
* `::Checkouts::Core::CheckoutId` - violated constant in question
|
274
274
|
* `dependency` - type of violation, either dependency or privacy
|
275
275
|
* `components/merchant/app/public/merchant/generate_order.rb` - path to the file containing the violated constant
|
276
276
|
|
277
|
-
Violations exist within the package that makes a violating reference. This means privacy violations of your package can be found listed in `
|
277
|
+
Violations exist within the package that makes a violating reference. This means privacy violations of your package can be found listed in `package_todo.yml` files in the packages with the reference to a private constant.
|
data/lib/packwerk/cli.rb
CHANGED
@@ -55,7 +55,17 @@ module Packwerk
|
|
55
55
|
when "detect-stale-violations"
|
56
56
|
output_result(parse_run(args).detect_stale_violations)
|
57
57
|
when "update-deprecations"
|
58
|
-
|
58
|
+
warning = <<~WARNING.squish
|
59
|
+
DEPRECATION WARNING: `update-deprecations` is deprecated in favor of
|
60
|
+
`update-todo`.
|
61
|
+
WARNING
|
62
|
+
|
63
|
+
warn(warning)
|
64
|
+
output_result(parse_run(args).update_todo)
|
65
|
+
when "update-todo"
|
66
|
+
output_result(parse_run(args).update_todo)
|
67
|
+
when "update"
|
68
|
+
output_result(parse_run(args).update_todo)
|
59
69
|
when "validate"
|
60
70
|
validate(args)
|
61
71
|
when nil, "help"
|
@@ -26,7 +26,7 @@ module Packwerk
|
|
26
26
|
sig { override.params(offense_collection: Packwerk::OffenseCollection, fileset: T::Set[String]).returns(String) }
|
27
27
|
def show_stale_violations(offense_collection, fileset)
|
28
28
|
if offense_collection.stale_violations?(fileset)
|
29
|
-
"There were stale violations found, please run `packwerk update-
|
29
|
+
"There were stale violations found, please run `packwerk update-todo`"
|
30
30
|
else
|
31
31
|
"No stale violations detected"
|
32
32
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "pathname"
|
5
|
+
|
4
6
|
module Packwerk
|
5
7
|
class OffenseCollection
|
6
8
|
extend T::Sig
|
@@ -9,12 +11,12 @@ module Packwerk
|
|
9
11
|
sig do
|
10
12
|
params(
|
11
13
|
root_path: String,
|
12
|
-
|
14
|
+
package_todo: T::Hash[Packwerk::Package, Packwerk::PackageTodo]
|
13
15
|
).void
|
14
16
|
end
|
15
|
-
def initialize(root_path,
|
17
|
+
def initialize(root_path, package_todo = {})
|
16
18
|
@root_path = root_path
|
17
|
-
@
|
19
|
+
@package_todo = T.let(package_todo, T::Hash[Packwerk::Package, Packwerk::PackageTodo])
|
18
20
|
@new_violations = T.let([], T::Array[Packwerk::ReferenceOffense])
|
19
21
|
@errors = T.let([], T::Array[Packwerk::Offense])
|
20
22
|
end
|
@@ -33,7 +35,7 @@ module Packwerk
|
|
33
35
|
return false unless offense.is_a?(ReferenceOffense)
|
34
36
|
|
35
37
|
reference = offense.reference
|
36
|
-
|
38
|
+
package_todo_for(reference.source_package).listed?(reference, violation_type: offense.violation_type)
|
37
39
|
end
|
38
40
|
|
39
41
|
sig do
|
@@ -44,23 +46,23 @@ module Packwerk
|
|
44
46
|
@errors << offense
|
45
47
|
return
|
46
48
|
end
|
47
|
-
|
48
|
-
unless
|
49
|
+
package_todo = package_todo_for(offense.reference.source_package)
|
50
|
+
unless package_todo.add_entries(offense.reference, offense.violation_type)
|
49
51
|
new_violations << offense
|
50
52
|
end
|
51
53
|
end
|
52
54
|
|
53
55
|
sig { params(for_files: T::Set[String]).returns(T::Boolean) }
|
54
56
|
def stale_violations?(for_files)
|
55
|
-
@
|
56
|
-
|
57
|
+
@package_todo.values.any? do |package_todo|
|
58
|
+
package_todo.stale_violations?(for_files)
|
57
59
|
end
|
58
60
|
end
|
59
61
|
|
60
62
|
sig { params(package_set: Packwerk::PackageSet).void }
|
61
|
-
def
|
62
|
-
|
63
|
-
|
63
|
+
def persist_package_todo_files(package_set)
|
64
|
+
dump_package_todo_files
|
65
|
+
cleanup_extra_package_todo_files(package_set)
|
64
66
|
end
|
65
67
|
|
66
68
|
sig { returns(T::Array[Packwerk::Offense]) }
|
@@ -68,36 +70,46 @@ module Packwerk
|
|
68
70
|
errors + new_violations
|
69
71
|
end
|
70
72
|
|
73
|
+
sig { void }
|
74
|
+
def dump_package_todo_files
|
75
|
+
@package_todo.each_value(&:dump)
|
76
|
+
end
|
77
|
+
|
71
78
|
private
|
72
79
|
|
73
80
|
sig { params(package_set: Packwerk::PackageSet).void }
|
74
|
-
def
|
75
|
-
packages_without_todos = (package_set.packages.values - @
|
81
|
+
def cleanup_extra_package_todo_files(package_set)
|
82
|
+
packages_without_todos = (package_set.packages.values - @package_todo.keys)
|
76
83
|
|
77
84
|
packages_without_todos.each do |package|
|
78
|
-
Packwerk::
|
85
|
+
Packwerk::PackageTodo.new(
|
79
86
|
package,
|
80
|
-
|
87
|
+
package_todo_file_for(package),
|
81
88
|
).delete_if_exists
|
82
89
|
end
|
83
90
|
end
|
84
91
|
|
85
|
-
sig {
|
86
|
-
def
|
87
|
-
@
|
88
|
-
end
|
89
|
-
|
90
|
-
sig { params(package: Packwerk::Package).returns(Packwerk::DeprecatedReferences) }
|
91
|
-
def deprecated_references_for(package)
|
92
|
-
@deprecated_references[package] ||= Packwerk::DeprecatedReferences.new(
|
92
|
+
sig { params(package: Packwerk::Package).returns(Packwerk::PackageTodo) }
|
93
|
+
def package_todo_for(package)
|
94
|
+
@package_todo[package] ||= Packwerk::PackageTodo.new(
|
93
95
|
package,
|
94
|
-
|
96
|
+
package_todo_file_for(package),
|
95
97
|
)
|
96
98
|
end
|
97
99
|
|
98
100
|
sig { params(package: Packwerk::Package).returns(String) }
|
99
|
-
def
|
100
|
-
|
101
|
+
def package_todo_file_for(package)
|
102
|
+
if Pathname.new(@root_path).join(package.name, "deprecated_references.yml").exist?
|
103
|
+
warning = <<~WARNING.squish
|
104
|
+
DEPRECATION WARNING: `deprecated_references.yml` files have been renamed to `package_todo.yml`.
|
105
|
+
Run `packwerk update-todo` to rename files automatically.
|
106
|
+
WARNING
|
107
|
+
|
108
|
+
warn(warning)
|
109
|
+
File.join(@root_path, package.name, "deprecated_references.yml")
|
110
|
+
else
|
111
|
+
File.join(@root_path, package.name, "package_todo.yml")
|
112
|
+
end
|
101
113
|
end
|
102
114
|
end
|
103
115
|
end
|
@@ -4,7 +4,7 @@
|
|
4
4
|
require "yaml"
|
5
5
|
|
6
6
|
module Packwerk
|
7
|
-
class
|
7
|
+
class PackageTodo
|
8
8
|
extend T::Sig
|
9
9
|
|
10
10
|
EntriesType = T.type_alias do
|
@@ -16,7 +16,7 @@ module Packwerk
|
|
16
16
|
@package = package
|
17
17
|
@filepath = filepath
|
18
18
|
@new_entries = T.let({}, EntriesType)
|
19
|
-
@
|
19
|
+
@todo_list = T.let(nil, T.nilable(EntriesType))
|
20
20
|
end
|
21
21
|
|
22
22
|
sig do
|
@@ -24,7 +24,7 @@ module Packwerk
|
|
24
24
|
.returns(T::Boolean)
|
25
25
|
end
|
26
26
|
def listed?(reference, violation_type:)
|
27
|
-
violated_constants_found =
|
27
|
+
violated_constants_found = todo_list.dig(reference.constant.package.name, reference.constant.name)
|
28
28
|
return false unless violated_constants_found
|
29
29
|
|
30
30
|
violated_constant_in_file = violated_constants_found.fetch("files", []).include?(reference.relative_path)
|
@@ -52,7 +52,7 @@ module Packwerk
|
|
52
52
|
def stale_violations?(for_files)
|
53
53
|
prepare_entries_for_dump
|
54
54
|
|
55
|
-
|
55
|
+
todo_list.any? do |package, package_violations|
|
56
56
|
package_violations_for_files = {}
|
57
57
|
package_violations.each do |constant_name, entries_for_constant|
|
58
58
|
entries_for_files = for_files & entries_for_constant["files"]
|
@@ -83,6 +83,8 @@ module Packwerk
|
|
83
83
|
|
84
84
|
sig { void }
|
85
85
|
def dump
|
86
|
+
delete_old_deprecated_references
|
87
|
+
|
86
88
|
if @new_entries.empty?
|
87
89
|
delete_if_exists
|
88
90
|
else
|
@@ -93,9 +95,11 @@ module Packwerk
|
|
93
95
|
#
|
94
96
|
# You can regenerate this file using the following command:
|
95
97
|
#
|
96
|
-
# bin/packwerk update-
|
98
|
+
# bin/packwerk update-todo #{@package.name}
|
97
99
|
MESSAGE
|
98
|
-
File.
|
100
|
+
package_todo_filepath = File.join(File.dirname(@filepath), "package_todo.yml")
|
101
|
+
|
102
|
+
File.open(package_todo_filepath, "w") do |f|
|
99
103
|
f.write(message)
|
100
104
|
f.write(@new_entries.to_yaml)
|
101
105
|
end
|
@@ -109,6 +113,12 @@ module Packwerk
|
|
109
113
|
|
110
114
|
private
|
111
115
|
|
116
|
+
sig { void }
|
117
|
+
def delete_old_deprecated_references
|
118
|
+
deprecated_references_filepath = File.join(File.dirname(@filepath), "deprecated_references.yml")
|
119
|
+
File.delete(deprecated_references_filepath) if File.exist?(deprecated_references_filepath)
|
120
|
+
end
|
121
|
+
|
112
122
|
sig { returns(EntriesType) }
|
113
123
|
def prepare_entries_for_dump
|
114
124
|
@new_entries.each do |package_name, package_violations|
|
@@ -123,8 +133,8 @@ module Packwerk
|
|
123
133
|
end
|
124
134
|
|
125
135
|
sig { returns(EntriesType) }
|
126
|
-
def
|
127
|
-
@
|
136
|
+
def todo_list
|
137
|
+
@todo_list ||= if File.exist?(@filepath)
|
128
138
|
load_yaml(@filepath)
|
129
139
|
else
|
130
140
|
{}
|
data/lib/packwerk/parse_run.rb
CHANGED
@@ -49,14 +49,14 @@ module Packwerk
|
|
49
49
|
end
|
50
50
|
|
51
51
|
sig { returns(Result) }
|
52
|
-
def
|
52
|
+
def update_todo
|
53
53
|
run_context = Packwerk::RunContext.from_configuration(@configuration)
|
54
54
|
offense_collection = find_offenses(run_context)
|
55
|
-
offense_collection.
|
55
|
+
offense_collection.persist_package_todo_files(run_context.package_set)
|
56
56
|
|
57
57
|
message = <<~EOS
|
58
58
|
#{@offenses_formatter.show_offenses(offense_collection.errors)}
|
59
|
-
✅ `
|
59
|
+
✅ `package_todo.yml` has been updated.
|
60
60
|
EOS
|
61
61
|
|
62
62
|
Result.new(message: message, status: offense_collection.errors.empty?)
|
data/lib/packwerk/version.rb
CHANGED
data/lib/packwerk.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: packwerk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-02-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -188,7 +188,6 @@ files:
|
|
188
188
|
- lib/packwerk/const_node_inspector.rb
|
189
189
|
- lib/packwerk/constant_discovery.rb
|
190
190
|
- lib/packwerk/constant_name_inspector.rb
|
191
|
-
- lib/packwerk/deprecated_references.rb
|
192
191
|
- lib/packwerk/file_processor.rb
|
193
192
|
- lib/packwerk/files_for_processing.rb
|
194
193
|
- lib/packwerk/formatters/offenses_formatter.rb
|
@@ -211,6 +210,7 @@ files:
|
|
211
210
|
- lib/packwerk/output_styles/plain.rb
|
212
211
|
- lib/packwerk/package.rb
|
213
212
|
- lib/packwerk/package_set.rb
|
213
|
+
- lib/packwerk/package_todo.rb
|
214
214
|
- lib/packwerk/parse_run.rb
|
215
215
|
- lib/packwerk/parsed_constant_definitions.rb
|
216
216
|
- lib/packwerk/parsers.rb
|