packwerk 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
![](static/packwerk_validate.gif)
|
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
|
![](static/packwerk_check.gif)
|
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
|
![](static/packwerk_update.gif)
|
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
|