packwerk 1.2.0 → 1.3.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/.github/workflows/ci.yml +3 -3
- data/Gemfile.lock +1 -1
- data/README.md +4 -4
- data/TROUBLESHOOT.md +2 -2
- data/USAGE.md +13 -14
- data/bin/m +29 -0
- data/bin/rake +29 -0
- data/bin/rubocop +29 -0
- data/bin/srb +29 -0
- data/bin/tapioca +29 -0
- data/dev.yml +6 -6
- data/lib/packwerk.rb +0 -1
- data/lib/packwerk/application_load_paths.rb +18 -8
- data/lib/packwerk/application_validator.rb +22 -18
- data/lib/packwerk/cli.rb +7 -32
- data/lib/packwerk/deprecated_references.rb +1 -1
- data/lib/packwerk/formatters/offenses_formatter.rb +10 -1
- data/lib/packwerk/offenses_formatter.rb +4 -0
- data/lib/packwerk/package.rb +9 -1
- data/lib/packwerk/parse_run.rb +9 -7
- data/lib/packwerk/version.rb +1 -1
- metadata +8 -7
- data/lib/packwerk/generators/application_validation.rb +0 -62
- data/lib/packwerk/generators/templates/packwerk +0 -23
- data/lib/packwerk/generators/templates/packwerk_validator_test.rb +0 -11
- data/lib/packwerk/spring_command.rb +0 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c73a1063d42615133bdd6a3cc797838f0d4a0b71c716a5a6dd2730c125723ca8
|
4
|
+
data.tar.gz: ede1cf336fb3c64f0093c9b853bc4db9e06004d8bfd297fd9ff60a6253c16e1c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3a2dd60787d4b8bb40e40491fb364321c0e2d086df7315b64ccb7a27b21f28d4e5e1855d40b52c1aadcbaf61d02ececa43d186fc10abdbe1de2c9609469586ef
|
7
|
+
data.tar.gz: 327267b82f798960ccf5a295125a980077bcfb9dfc00587583dcefbdcd1bfcea935f79f0f4c2efebad8d90d08807d6f08d800240bc4315ded513abdca9efcce0
|
data/.github/workflows/ci.yml
CHANGED
@@ -30,7 +30,7 @@ jobs:
|
|
30
30
|
run: |
|
31
31
|
gem install bundler
|
32
32
|
bundle install --jobs 4 --retry 3
|
33
|
-
|
33
|
+
bin/rake
|
34
34
|
lint:
|
35
35
|
runs-on: ubuntu-latest
|
36
36
|
steps:
|
@@ -43,7 +43,7 @@ jobs:
|
|
43
43
|
run: |
|
44
44
|
gem install bundler
|
45
45
|
bundle install --jobs 4 --retry 3
|
46
|
-
|
46
|
+
bin/rubocop
|
47
47
|
static-type-checking:
|
48
48
|
runs-on: ubuntu-latest
|
49
49
|
steps:
|
@@ -56,4 +56,4 @@ jobs:
|
|
56
56
|
run: |
|
57
57
|
gem install bundler
|
58
58
|
bundle install --jobs 4 --retry 3
|
59
|
-
|
59
|
+
bin/srb tc
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
## NOTE: Packwerk is considered to be feature-complete for Shopify's uses. We are currently accepting bug fixes only, and it is not being actively developed. Please fork this project if you are interested in adding new features.
|
4
4
|
|
5
|
-
> "I know who you are and because of that I know what you do."
|
6
|
-
> This knowledge is a dependency that raises the cost of change.
|
5
|
+
> "I know who you are and because of that I know what you do."
|
6
|
+
> This knowledge is a dependency that raises the cost of change.
|
7
7
|
|
8
8
|
-- _Sandi Metz, Practical Object-Oriented Design in Ruby_
|
9
9
|
|
@@ -47,7 +47,7 @@ Or install it yourself as:
|
|
47
47
|
|
48
48
|
$ gem install packwerk
|
49
49
|
|
50
|
-
3. Run `
|
50
|
+
3. Run `packwerk init` to generate the configuration files
|
51
51
|
|
52
52
|
## Usage
|
53
53
|
|
@@ -59,7 +59,7 @@ Read [USAGE.md](USAGE.md) for usage once Packwerk is installed on your project.
|
|
59
59
|
|
60
60
|
## Development
|
61
61
|
|
62
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `
|
62
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
63
63
|
|
64
64
|
## Limitations
|
65
65
|
|
data/TROUBLESHOOT.md
CHANGED
@@ -14,9 +14,9 @@ Packwerk can give feedback via continuous integration (CI) if you have it set up
|
|
14
14
|
|
15
15
|
You can specify folders or packages in Packwerk commands for a shorter run time:
|
16
16
|
|
17
|
-
|
17
|
+
packwerk check components/your_package
|
18
18
|
|
19
|
-
|
19
|
+
packwerk update-deprecations components/your_package
|
20
20
|
|
21
21
|
_Note: You cannot specify folders or packages for `packwerk validate` because the command runs for the entire application._
|
22
22
|
|
data/USAGE.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Packwerk usage
|
2
2
|
|
3
3
|
## Table of Contents
|
4
|
+
|
4
5
|
* [What problem does Packwerk solve?](#what-problem-does-packwerk-solve)
|
5
6
|
* [What is a package?](#what-is-a-package)
|
6
7
|
* [Package principles](#package-principles)
|
@@ -19,11 +20,13 @@
|
|
19
20
|
* [Understanding the list of deprecated references](#understanding-the-list-of-deprecated-references)
|
20
21
|
|
21
22
|
## What problem does Packwerk solve?
|
23
|
+
|
22
24
|
Large applications need clear boundaries to avoid turning into a [ball of mud](https://en.wikipedia.org/wiki/Big_ball_of_mud). However, Ruby does not provide a good solution to enforcing boundaries between code.
|
23
25
|
|
24
26
|
Packwerk is a gem that can be used to enforce boundaries between groups of code we call packages.
|
25
27
|
|
26
28
|
## What is a package?
|
29
|
+
|
27
30
|
A package is a folder containing autoloaded code. To decide whether code belongs together in a package, these are some design best practices:
|
28
31
|
|
29
32
|
- We should package things together that have high functional [cohesion](https://en.wikipedia.org/wiki/Cohesion_(computer_science)).
|
@@ -41,7 +44,7 @@ The [package principles](https://en.wikipedia.org/wiki/Package_principles) page
|
|
41
44
|
|
42
45
|
After including Packwerk in the Gemfile, you can generate the necessary files to get Packwerk running by executing:
|
43
46
|
|
44
|
-
|
47
|
+
packwerk init
|
45
48
|
|
46
49
|
Here is a list of files generated:
|
47
50
|
|
@@ -49,8 +52,6 @@ Here is a list of files generated:
|
|
49
52
|
|-----------------------------|--------------|------------|
|
50
53
|
| Packwerk configuration | packwerk.yml | See [Setting up the configuration file](#Setting-up-the-configuration-file) |
|
51
54
|
| Root package | package.yml | A package for the root folder |
|
52
|
-
| Bin script | bin/packwerk | For Rails applications to run Packwerk validation on CI, see [Validating the package system](#Validating-the-package-system) |
|
53
|
-
| Validation test | test/packwerk_validator_test.rb | For Ruby projects to run Packwerk validation using tests, see [Validating the package system](#Validating-the-package-system) |
|
54
55
|
| Custom inflections | config/inflections.yml | A custom inflections file is only required if you have custom inflections in `inflections.rb`, see [Inflections](#Inflections) |
|
55
56
|
|
56
57
|
After that, you may begin creating packages for your application. See [Defining packages](#Defining-packages)
|
@@ -133,18 +134,14 @@ Any new inflectors should be added to `config/inflections.yml`.
|
|
133
134
|
|
134
135
|
There are some criteria that an application must meet in order to have a valid package system. These criteria include having a valid autoload path cache, package definition files, and application folder structure. The dependency graph within the package system also has to be acyclic.
|
135
136
|
|
136
|
-
The package system can be validated through a series of built in validation checks. Currently, the validation checks require the application to be booted either through `spring` or as part of its test suite.
|
137
|
-
|
138
137
|
We recommend setting up the package system validation for your Rails application in a CI step (or through a test suite for Ruby projects) separate from `packwerk check`.
|
139
138
|
|
140
|
-
|
139
|
+
Use the following command to validate the application:
|
141
140
|
|
142
|
-
|
141
|
+
packwerk validate
|
143
142
|
|
144
143
|

|
145
144
|
|
146
|
-
If running `packwerk init` on your Ruby project generates `test/packwerk_validator_test.rb`, you can use this test as validation.
|
147
|
-
|
148
145
|
## Defining packages
|
149
146
|
|
150
147
|
You can create a `package.yml` in any folder to make it a package. The package name is the path to the folder from the project root.
|
@@ -152,6 +149,7 @@ You can create a `package.yml` in any folder to make it a package. The package n
|
|
152
149
|
_Note: It is helpful to define a namespace that corresponds to the package name and contains at least all the public constants of the package. This makes it more obvious which package a constant is defined in._
|
153
150
|
|
154
151
|
### Package metadata
|
152
|
+
|
155
153
|
Package metadata can be included in the `package.yml`. Metadata won't be validated, and can thus be anything. We recommend including information on ownership and stewardship of the package.
|
156
154
|
|
157
155
|
Example:
|
@@ -169,6 +167,7 @@ Example:
|
|
169
167
|
Packwerk can perform two types of boundary checks: privacy and dependency.
|
170
168
|
|
171
169
|
#### Enforcing privacy boundary
|
170
|
+
|
172
171
|
A package's privacy boundary is violated when there is a reference to the package's private constants from a source outside the package.
|
173
172
|
|
174
173
|
There are two ways you can enforce privacy for your package:
|
@@ -228,13 +227,13 @@ It will be a dependency violation when `components/shop_identity` tries to refer
|
|
228
227
|
|
229
228
|
After enforcing the boundary checks for a package, you may execute:
|
230
229
|
|
231
|
-
|
230
|
+
packwerk check
|
232
231
|
|
233
|
-
Packwerk will check the entire codebase for any violations.
|
232
|
+
Packwerk will check the entire codebase for any new or stale violations.
|
234
233
|
|
235
234
|
You can also specify folders or packages for a shorter run time:
|
236
235
|
|
237
|
-
|
236
|
+
packwerk check components/your_package
|
238
237
|
|
239
238
|

|
240
239
|
|
@@ -248,11 +247,11 @@ For existing codebases, packages are likely to have existing boundary violations
|
|
248
247
|
|
249
248
|
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 [deprecated references list](#Understanding_the_list_of_deprecated_references) by executing:
|
250
249
|
|
251
|
-
|
250
|
+
packwerk update-deprecations
|
252
251
|
|
253
252
|
Similar to `packwerk check`, you may also run `packwerk update-deprecations` on folders or packages:
|
254
253
|
|
255
|
-
|
254
|
+
packwerk update-deprecations components/your_package
|
256
255
|
|
257
256
|

|
258
257
|
|
data/bin/m
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'm' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("m", "m")
|
data/bin/rake
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rake' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rake", "rake")
|
data/bin/rubocop
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rubocop' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rubocop", "rubocop")
|
data/bin/srb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'srb' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("sorbet", "srb")
|
data/bin/tapioca
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'tapioca' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("tapioca", "tapioca")
|
data/dev.yml
CHANGED
@@ -12,21 +12,21 @@ commands:
|
|
12
12
|
if [[ "$*" =~ ":"[0-9]+ ]];
|
13
13
|
then
|
14
14
|
# run test by its line number
|
15
|
-
|
15
|
+
bin/m "$@"
|
16
16
|
elif [[ "$#" -eq 1 && -f "$1" ]];
|
17
17
|
then
|
18
18
|
# run all tests in given file(s)
|
19
|
-
|
19
|
+
bin/rake test TEST="$@"
|
20
20
|
else
|
21
21
|
# run all tests
|
22
|
-
|
22
|
+
bin/rake test
|
23
23
|
fi
|
24
|
-
style: "
|
24
|
+
style: "bin/rubocop -D --auto-correct"
|
25
25
|
typecheck:
|
26
26
|
desc: "run Sorbet typechecking"
|
27
|
-
run: "
|
27
|
+
run: "bin/srb tc"
|
28
28
|
aliases: ['tc']
|
29
29
|
subcommands:
|
30
30
|
update:
|
31
31
|
desc: "update RBIs for gems"
|
32
|
-
run: "
|
32
|
+
run: "bin/tapioca sync -c 'dev typecheck update'"
|
data/lib/packwerk.rb
CHANGED
@@ -8,20 +8,15 @@ module Packwerk
|
|
8
8
|
class << self
|
9
9
|
extend T::Sig
|
10
10
|
|
11
|
-
sig { returns(T::Array[String]) }
|
12
|
-
def extract_relevant_paths
|
13
|
-
|
11
|
+
sig { params(root: String, environment: String).returns(T::Array[String]) }
|
12
|
+
def extract_relevant_paths(root, environment)
|
13
|
+
require_application(root, environment)
|
14
14
|
all_paths = extract_application_autoload_paths
|
15
15
|
relevant_paths = filter_relevant_paths(all_paths)
|
16
16
|
assert_load_paths_present(relevant_paths)
|
17
17
|
relative_path_strings(relevant_paths)
|
18
18
|
end
|
19
19
|
|
20
|
-
sig { void }
|
21
|
-
def assert_application_booted
|
22
|
-
raise "The application needs to be booted to extract load paths" unless defined?(::Rails)
|
23
|
-
end
|
24
|
-
|
25
20
|
sig { returns(T::Array[String]) }
|
26
21
|
def extract_application_autoload_paths
|
27
22
|
Rails.application.railties
|
@@ -54,6 +49,21 @@ module Packwerk
|
|
54
49
|
.uniq
|
55
50
|
end
|
56
51
|
|
52
|
+
private
|
53
|
+
|
54
|
+
sig { params(root: String, environment: String).void }
|
55
|
+
def require_application(root, environment)
|
56
|
+
environment_file = "#{root}/config/environment"
|
57
|
+
|
58
|
+
if File.file?("#{environment_file}.rb")
|
59
|
+
ENV["RAILS_ENV"] ||= environment
|
60
|
+
|
61
|
+
require environment_file
|
62
|
+
else
|
63
|
+
raise "A Rails application could not be found in #{root}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
57
67
|
sig { params(paths: T::Array[T.untyped]).void }
|
58
68
|
def assert_load_paths_present(paths)
|
59
69
|
if paths.empty?
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: true
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "active_support/inflector/inflections"
|
@@ -8,11 +8,12 @@ require "yaml"
|
|
8
8
|
|
9
9
|
module Packwerk
|
10
10
|
class ApplicationValidator
|
11
|
-
def initialize(config_file_path:, configuration:)
|
11
|
+
def initialize(config_file_path:, configuration:, environment:)
|
12
12
|
@config_file_path = config_file_path
|
13
13
|
@configuration = configuration
|
14
|
+
@environment = environment
|
14
15
|
|
15
|
-
@application_load_paths = ApplicationLoadPaths.extract_relevant_paths
|
16
|
+
@application_load_paths = ApplicationLoadPaths.extract_relevant_paths(configuration.root_path, environment)
|
16
17
|
end
|
17
18
|
|
18
19
|
Result = Struct.new(:ok?, :error_value)
|
@@ -176,21 +177,9 @@ module Packwerk
|
|
176
177
|
edges = package_set.flat_map do |package|
|
177
178
|
package.dependencies.map { |dependency| [package, package_set.fetch(dependency)] }
|
178
179
|
end
|
179
|
-
dependency_graph = Packwerk::Graph.new(*edges)
|
180
|
-
|
181
|
-
|
182
|
-
#
|
183
|
-
# [a, b, c]
|
184
|
-
#
|
185
|
-
# to the string
|
186
|
-
#
|
187
|
-
# a -> b -> c -> a
|
188
|
-
#
|
189
|
-
cycle_strings = dependency_graph.cycles.map do |cycle|
|
190
|
-
cycle_strings = cycle.map(&:to_s)
|
191
|
-
cycle_strings << cycle.first.to_s
|
192
|
-
"\t- #{cycle_strings.join(" → ")}"
|
193
|
-
end
|
180
|
+
dependency_graph = Packwerk::Graph.new(*T.unsafe(edges))
|
181
|
+
|
182
|
+
cycle_strings = build_cycle_strings(dependency_graph.cycles)
|
194
183
|
|
195
184
|
if dependency_graph.acyclic?
|
196
185
|
Result.new(true)
|
@@ -279,6 +268,21 @@ module Packwerk
|
|
279
268
|
|
280
269
|
private
|
281
270
|
|
271
|
+
# Convert the cycles:
|
272
|
+
#
|
273
|
+
# [[a, b, c], [b, c]]
|
274
|
+
#
|
275
|
+
# to the string:
|
276
|
+
#
|
277
|
+
# ["a -> b -> c -> a", "b -> c -> b"]
|
278
|
+
def build_cycle_strings(cycles)
|
279
|
+
cycles.map do |cycle|
|
280
|
+
cycle_strings = cycle.map(&:to_s)
|
281
|
+
cycle_strings << cycle.first.to_s
|
282
|
+
"\t- #{cycle_strings.join(" → ")}"
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
282
286
|
def package_manifests_settings_for(setting)
|
283
287
|
package_manifests.map { |f| [f, (YAML.load_file(File.join(f)) || {})[setting]] }
|
284
288
|
end
|
data/lib/packwerk/cli.rb
CHANGED
@@ -10,6 +10,7 @@ module Packwerk
|
|
10
10
|
configuration: T.nilable(Configuration),
|
11
11
|
out: T.any(StringIO, IO),
|
12
12
|
err_out: T.any(StringIO, IO),
|
13
|
+
environment: String,
|
13
14
|
style: Packwerk::OutputStyle,
|
14
15
|
offenses_formatter: T.nilable(Packwerk::OffensesFormatter)
|
15
16
|
).void
|
@@ -18,11 +19,13 @@ module Packwerk
|
|
18
19
|
configuration: nil,
|
19
20
|
out: $stdout,
|
20
21
|
err_out: $stderr,
|
22
|
+
environment: "test",
|
21
23
|
style: OutputStyles::Plain.new,
|
22
24
|
offenses_formatter: nil
|
23
25
|
)
|
24
26
|
@out = out
|
25
27
|
@err_out = err_out
|
28
|
+
@environment = environment
|
26
29
|
@style = style
|
27
30
|
@configuration = configuration || Configuration.from_path
|
28
31
|
@progress_formatter = Formatters::ProgressFormatter.new(@out, style: style)
|
@@ -77,28 +80,12 @@ module Packwerk
|
|
77
80
|
def init
|
78
81
|
@out.puts("📦 Initializing Packwerk...")
|
79
82
|
|
80
|
-
|
81
|
-
for_rails_app: rails_app?,
|
82
|
-
root: @configuration.root_path,
|
83
|
-
out: @out
|
84
|
-
)
|
85
|
-
|
86
|
-
if application_validation
|
87
|
-
if rails_app?
|
88
|
-
# To run in the same space as the Rails process,
|
89
|
-
# in order to fetch load paths for the configuration generator
|
90
|
-
exec("bin/packwerk", "generate_configs")
|
91
|
-
else
|
92
|
-
generate_configurations = generate_configs
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
application_validation && generate_configurations
|
83
|
+
generate_configs
|
97
84
|
end
|
98
85
|
|
99
86
|
def generate_configs
|
100
87
|
configuration_file = Packwerk::Generators::ConfigurationFile.generate(
|
101
|
-
load_paths: Packwerk::ApplicationLoadPaths.extract_relevant_paths,
|
88
|
+
load_paths: Packwerk::ApplicationLoadPaths.extract_relevant_paths(@configuration.root_path, @environment),
|
102
89
|
root: @configuration.root_path,
|
103
90
|
out: @out
|
104
91
|
)
|
@@ -144,14 +131,11 @@ module Packwerk
|
|
144
131
|
end
|
145
132
|
|
146
133
|
def validate(_paths)
|
147
|
-
warn("`packwerk validate` should be run within the application. "\
|
148
|
-
"Generate the bin script using `packwerk init` and"\
|
149
|
-
" use `bin/packwerk validate` instead.") unless defined?(::Rails)
|
150
|
-
|
151
134
|
@progress_formatter.started_validation do
|
152
135
|
checker = Packwerk::ApplicationValidator.new(
|
153
136
|
config_file_path: @configuration.config_path,
|
154
|
-
configuration: @configuration
|
137
|
+
configuration: @configuration,
|
138
|
+
environment: @environment,
|
155
139
|
)
|
156
140
|
result = checker.check_all
|
157
141
|
|
@@ -171,15 +155,6 @@ module Packwerk
|
|
171
155
|
end
|
172
156
|
end
|
173
157
|
|
174
|
-
sig { returns(T::Boolean) }
|
175
|
-
def rails_app?
|
176
|
-
if File.exist?("config/application.rb") && File.exist?("bin/rails")
|
177
|
-
File.foreach("Gemfile").any? { |line| line.match?(/['"]rails['"]/) }
|
178
|
-
else
|
179
|
-
false
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
158
|
def parse_run(paths)
|
184
159
|
ParseRun.new(
|
185
160
|
files: fetch_files_to_process(paths),
|
@@ -73,7 +73,7 @@ module Packwerk
|
|
73
73
|
#
|
74
74
|
# You can regenerate this file using the following command:
|
75
75
|
#
|
76
|
-
#
|
76
|
+
# packwerk update-deprecations #{@package.name}
|
77
77
|
MESSAGE
|
78
78
|
File.open(@filepath, "w") do |f|
|
79
79
|
f.write(message)
|
@@ -15,7 +15,7 @@ module Packwerk
|
|
15
15
|
|
16
16
|
sig { override.params(offenses: T::Array[T.nilable(Offense)]).returns(String) }
|
17
17
|
def show_offenses(offenses)
|
18
|
-
return "No offenses detected
|
18
|
+
return "No offenses detected" if offenses.empty?
|
19
19
|
|
20
20
|
<<~EOS
|
21
21
|
#{offenses_list(offenses)}
|
@@ -23,6 +23,15 @@ module Packwerk
|
|
23
23
|
EOS
|
24
24
|
end
|
25
25
|
|
26
|
+
sig { override.params(offense_collection: Packwerk::OffenseCollection).returns(String) }
|
27
|
+
def show_stale_violations(offense_collection)
|
28
|
+
if offense_collection.stale_violations?
|
29
|
+
"There were stale violations found, please run `packwerk update-deprecations`"
|
30
|
+
else
|
31
|
+
"No stale violations detected"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
26
35
|
private
|
27
36
|
|
28
37
|
sig { params(offenses: T::Array[T.nilable(Offense)]).returns(String) }
|
@@ -11,5 +11,9 @@ module Packwerk
|
|
11
11
|
sig { abstract.params(offenses: T::Array[T.nilable(Offense)]).returns(String) }
|
12
12
|
def show_offenses(offenses)
|
13
13
|
end
|
14
|
+
|
15
|
+
sig { abstract.params(offense_collection: Packwerk::OffenseCollection).returns(String) }
|
16
|
+
def show_stale_violations(offense_collection)
|
17
|
+
end
|
14
18
|
end
|
15
19
|
end
|
data/lib/packwerk/package.rb
CHANGED
@@ -33,7 +33,15 @@ module Packwerk
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def public_path
|
36
|
-
@public_path ||=
|
36
|
+
@public_path ||= begin
|
37
|
+
unprefixed_public_path = user_defined_public_path || "app/public/"
|
38
|
+
|
39
|
+
if root?
|
40
|
+
unprefixed_public_path
|
41
|
+
else
|
42
|
+
File.join(@name, unprefixed_public_path)
|
43
|
+
end
|
44
|
+
end
|
37
45
|
end
|
38
46
|
|
39
47
|
def public_path?(path)
|
data/lib/packwerk/parse_run.rb
CHANGED
@@ -24,11 +24,7 @@ module Packwerk
|
|
24
24
|
offense_collection = find_offenses
|
25
25
|
|
26
26
|
result_status = !offense_collection.stale_violations?
|
27
|
-
message =
|
28
|
-
"No stale violations detected"
|
29
|
-
else
|
30
|
-
"There were stale violations found, please run `packwerk update-deprecations`"
|
31
|
-
end
|
27
|
+
message = @offenses_formatter.show_stale_violations(offense_collection)
|
32
28
|
|
33
29
|
Result.new(message: message, status: result_status)
|
34
30
|
end
|
@@ -47,8 +43,14 @@ module Packwerk
|
|
47
43
|
|
48
44
|
def check
|
49
45
|
offense_collection = find_offenses(show_errors: true)
|
50
|
-
|
51
|
-
|
46
|
+
|
47
|
+
messages = [
|
48
|
+
@offenses_formatter.show_offenses(offense_collection.outstanding_offenses),
|
49
|
+
@offenses_formatter.show_stale_violations(offense_collection),
|
50
|
+
]
|
51
|
+
result_status = offense_collection.outstanding_offenses.empty? && !offense_collection.stale_violations?
|
52
|
+
|
53
|
+
Result.new(message: messages.join("\n") + "\n", status: result_status)
|
52
54
|
end
|
53
55
|
|
54
56
|
private
|
data/lib/packwerk/version.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: 1.
|
4
|
+
version: 1.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: 2021-
|
11
|
+
date: 2021-07-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -192,7 +192,12 @@ files:
|
|
192
192
|
- TROUBLESHOOT.md
|
193
193
|
- USAGE.md
|
194
194
|
- bin/console
|
195
|
+
- bin/m
|
196
|
+
- bin/rake
|
197
|
+
- bin/rubocop
|
195
198
|
- bin/setup
|
199
|
+
- bin/srb
|
200
|
+
- bin/tapioca
|
196
201
|
- dev.yml
|
197
202
|
- docs/cohesion.png
|
198
203
|
- exe/packwerk
|
@@ -213,15 +218,12 @@ files:
|
|
213
218
|
- lib/packwerk/files_for_processing.rb
|
214
219
|
- lib/packwerk/formatters/offenses_formatter.rb
|
215
220
|
- lib/packwerk/formatters/progress_formatter.rb
|
216
|
-
- lib/packwerk/generators/application_validation.rb
|
217
221
|
- lib/packwerk/generators/configuration_file.rb
|
218
222
|
- lib/packwerk/generators/inflections_file.rb
|
219
223
|
- lib/packwerk/generators/root_package.rb
|
220
224
|
- lib/packwerk/generators/templates/inflections.yml
|
221
225
|
- lib/packwerk/generators/templates/package.yml
|
222
|
-
- lib/packwerk/generators/templates/packwerk
|
223
226
|
- lib/packwerk/generators/templates/packwerk.yml.erb
|
224
|
-
- lib/packwerk/generators/templates/packwerk_validator_test.rb
|
225
227
|
- lib/packwerk/graph.rb
|
226
228
|
- lib/packwerk/inflections/custom.rb
|
227
229
|
- lib/packwerk/inflections/default.rb
|
@@ -251,7 +253,6 @@ files:
|
|
251
253
|
- lib/packwerk/result.rb
|
252
254
|
- lib/packwerk/run_context.rb
|
253
255
|
- lib/packwerk/sanity_checker.rb
|
254
|
-
- lib/packwerk/spring_command.rb
|
255
256
|
- lib/packwerk/version.rb
|
256
257
|
- lib/packwerk/violation_type.rb
|
257
258
|
- library.yml
|
@@ -353,7 +354,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
353
354
|
- !ruby/object:Gem::Version
|
354
355
|
version: '0'
|
355
356
|
requirements: []
|
356
|
-
rubygems_version: 3.2.
|
357
|
+
rubygems_version: 3.2.20
|
357
358
|
signing_key:
|
358
359
|
specification_version: 4
|
359
360
|
summary: Packages for applications based on the zeitwerk autoloader
|
@@ -1,62 +0,0 @@
|
|
1
|
-
# typed: true
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
module Packwerk
|
5
|
-
module Generators
|
6
|
-
class ApplicationValidation
|
7
|
-
class << self
|
8
|
-
def generate(for_rails_app: false, root: ".", out: $stdout)
|
9
|
-
new(root, out: out).generate(for_rails_app: for_rails_app)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
def initialize(root, out: $stdout)
|
14
|
-
@root = root
|
15
|
-
@out = out
|
16
|
-
end
|
17
|
-
|
18
|
-
def generate(for_rails_app:)
|
19
|
-
@out.puts("📦 Generating application validator...")
|
20
|
-
if for_rails_app
|
21
|
-
generate_packwerk_validate_script
|
22
|
-
else
|
23
|
-
generate_validation_test
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
def generate_packwerk_validate_script
|
30
|
-
destination_file_path = File.join(@root, "bin")
|
31
|
-
FileUtils.mkdir_p(destination_file_path)
|
32
|
-
|
33
|
-
if File.exist?(File.join(destination_file_path, "packwerk"))
|
34
|
-
@out.puts("⚠️ Packwerk application validation bin script already exists.")
|
35
|
-
return true
|
36
|
-
end
|
37
|
-
|
38
|
-
source_file_path = File.expand_path("templates/packwerk", __dir__)
|
39
|
-
FileUtils.cp(source_file_path, destination_file_path)
|
40
|
-
|
41
|
-
@out.puts("✅ Packwerk application validation bin script generated in #{destination_file_path}")
|
42
|
-
true
|
43
|
-
end
|
44
|
-
|
45
|
-
def generate_validation_test
|
46
|
-
destination_file_path = File.join(@root, "test")
|
47
|
-
FileUtils.mkdir_p(destination_file_path)
|
48
|
-
|
49
|
-
if File.exist?(File.join(destination_file_path, "packwerk_validator_test.rb"))
|
50
|
-
@out.puts("⚠️ Packwerk application validation test already exists.")
|
51
|
-
return true
|
52
|
-
end
|
53
|
-
|
54
|
-
source_file_path = File.expand_path("templates/packwerk_validator_test.rb", __dir__)
|
55
|
-
FileUtils.cp(source_file_path, destination_file_path)
|
56
|
-
|
57
|
-
@out.puts("✅ Packwerk application validation test generated in #{destination_file_path}")
|
58
|
-
true
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
# This file was auto-generated by Packwerk through `packwerk init`
|
5
|
-
|
6
|
-
# Needs to be run in test environment in order to have test helper paths available in the autoload paths
|
7
|
-
ENV["RAILS_ENV"] = "test"
|
8
|
-
|
9
|
-
# Command line arguments needs to be duplicated because spring modifies it
|
10
|
-
packwerk_argv = ARGV.dup
|
11
|
-
|
12
|
-
begin
|
13
|
-
load(File.expand_path("spring", __dir__))
|
14
|
-
rescue LoadError => e
|
15
|
-
raise unless e.message.include?("spring")
|
16
|
-
end
|
17
|
-
|
18
|
-
require File.expand_path("../config/environment", __dir__)
|
19
|
-
|
20
|
-
require "packwerk"
|
21
|
-
|
22
|
-
cli = Packwerk::Cli.new
|
23
|
-
cli.run(packwerk_argv)
|
@@ -1,11 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "test_helper"
|
4
|
-
require "packwerk"
|
5
|
-
|
6
|
-
# This test is necessary to make sure that the package system is working correctly
|
7
|
-
class PackwerkValidatorTest < Minitest::Test
|
8
|
-
def test_the_application_is_correctly_set_up_for_the_package_system
|
9
|
-
assert(Packwerk::Cli.new.execute_command(["validate"]))
|
10
|
-
end
|
11
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
# typed: false
|
3
|
-
|
4
|
-
require "spring/commands"
|
5
|
-
|
6
|
-
module Packwerk
|
7
|
-
class SpringCommand
|
8
|
-
def env(*)
|
9
|
-
# Packwerk needs to run in a test environment, which has a set of autoload paths that are
|
10
|
-
# often a superset of the dev/prod paths (for example, test/support/helpers)
|
11
|
-
"test"
|
12
|
-
end
|
13
|
-
|
14
|
-
def exec_name
|
15
|
-
"packwerk"
|
16
|
-
end
|
17
|
-
|
18
|
-
def gem_name
|
19
|
-
"packwerk"
|
20
|
-
end
|
21
|
-
|
22
|
-
def call
|
23
|
-
load(Gem.bin_path(gem_name, exec_name))
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
Spring.register_command("packwerk", SpringCommand.new)
|
28
|
-
end
|