A 100% Rust implementation of packwerk and packwerk-extensions, a gradual modularization platform for Ruby.
Currently, it ships the following checkers to help improve the boundaries between packages. These checkers are:
- A
dependencychecker requires that a pack specifies packs on which it depends. Cyclic dependencies are not allowed. See packwerk - A
privacychecker that ensures other packages are using your package's public API - A
visibilitychecker that allows packages to be private except to an explicit group of other packages. - A
folder_privacychecker that allows packages to their sibling packs and parent pack (to be used in an application that uses folder packs) - A
layer(formerlyarchitecture) checker that allows packages to specify their "layer" and requires that each layer only communicate with layers below it.
See Checkers for detailed descriptions.
- Automatically respects
.gitignorefiles (both local and global) - Improves performance by skipping ignored directories during traversal
- Can be disabled via
respect_gitignore: falseconfiguration if needed
This repo was forked directly from https://github.com/alexevanczuk/packs
- Currently ~10-20x as fast as the ruby implementation. See BENCHMARKS.md.
- Your mileage may vary!
- Other performance improvements are coming soon!
- Currently supports non-Rails apps through an experimental implementation
- Uses the same public API as
packwerk, but has different behavior. - See EXPERIMENTAL_PARSER_USAGE.md for more info
Once installed and added to your $PATH, just call pks to see the CLI help message and documentation.
Welcome! Please see https://github.com/rubyatscale/pks for more information!
Usage: pks [OPTIONS] <COMMAND>
Commands:
greet Just saying hi
create Create a new pack
check Look for violations in the codebase
check-contents Check file contents piped to stdin
update Update package_todo.yml files with the current violations
validate Look for validation errors in the codebase
add-dependency Add a dependency from one pack to another
check-unused-dependencies Check for dependencies that when removed produce no violations.
lint-package-yml-files Lint package.yml files
expose-monkey-patches Expose monkey patches of the Ruby stdlib, gems your app uses, and your application itself
delete-cache `rm -rf` on your cache directory, default `tmp/cache/packwerk`
list-packs List packs based on configuration in packwerk.yml (for debugging purposes)
list-included-files List analyzed files based on configuration in packwerk.yml (for debugging purposes)
list-definitions List the constants that packs sees and where it sees them (for debugging purposes)
help Print this message or the help of the given subcommand(s)
Options:
--project-root <PROJECT_ROOT> Path for the root of the project [default: .]
-d, --debug Run with performance debug mode
-e, --experimental-parser Run with the experimental parser, which gets constant definitions directly from the AST
--no-cache Run without the cache (good for CI, testing)
-p, --print-files Print to console when files begin and finish processing (to identify files that panic when processing files concurrently)
-h, --help Print help
-V, --version Print version
See INSTALLATION.md
packwerk has a VSCode Extension: https://github.com/rubyatscale/packwerk-vscode/tree/main
It also has a RubyMine Extension: https://github.com/vinted/packwerk-intellij
Using the extension with pks is straightforward and results in a much more responsive experience.
Directions:
- Follow INSTALLATION.md instructions to install
pks - Follow the configuration directions to configure the extension to use
pksinstead of the ruby gem by setting the executable topks check
- custom inflections
- custom load paths
- extensible plugin system
There are still some known behavioral differences between pks and packwerk. If you find any, please file an issue!
package_pathsmust not end in a slash, e.g.pks/*/is not supported, butpks/*is.- A
**inpackage_pathsis supported, but is not a substitute for a single*, e.g.pks/**is supported and will matchpks/*/*/package.yml, but will not matchpks/*/package.yml.pks/*must be used to match that.
pks automatically respects .gitignore files when analyzing your codebase. This means:
- Files listed in
.gitignoreare automatically excluded from analysis - Respects global gitignore from
core.excludesFilegit config - Respects
.git/info/exclude - Improves performance by skipping ignored directories entirely
- Can be disabled by setting
respect_gitignore: falseinpackwerk.yml
This feature is enabled by default. If you need behavior identical to packwerk, set respect_gitignore: false.
For detailed configuration, precedence rules, and troubleshooting, see ADVANCED_USAGE.md.
pks supports Zeitwerk default namespaces. However, since it doesn't have access to the Rails runtime, you need to explicitly specify the namespaces in packwerk.yml.
For example, if you're using packs-rails and automatic_namespaces to configure your default namespaces, and you have
pks/foo/app/models/bar.rbwhich is configured to defineFoo::Barpks/foo/app/domain/baz.rbwhich is configured to defineFoo::Baz
You'll need to specify the default namespaces in packwerk.yml like so:
autoload_roots:
packs/foo/app/models: "::Foo"
packs/foo/app/domain: "::Foo"The error messages resulting from running pks check can be customized with mustache-style interpolation. The available
variables are:
- violation_name
- referencing_pack_name
- defining_pack_name
- constant_name
- reference_location
- referencing_pack_relative_yml
Layer violations also have
- defining_layer
- referencing_layer
Example: packwerk.yml
checker_overrides:
folder_privacy_error_template: "{{reference_location}} {{violation_name}} / Product Service Privacy Violation: `{{constant_name}}` belongs to the `{{defining_pack_name}}` product service, which is not visible to `{{referencing_pack_name}}` as it is a different product service. See https://go/pks-folder-privacy"
layer_error_template: "{{reference_location}}Layer violation: `{{constant_name}}` belongs to `{{defining_pack_name}}` (whose layer is `{{defining_layer}}`) cannot be accessed from `{{referencing_pack_name}}` (whose layer is `{{referencing_layer}}`). See https://go/pks-layer"
visibility_error_template: "{{reference_location}}Visibility violation: `{{constant_name}}` belongs to `{{defining_pack_name}}`, which is not visible to `{{referencing_pack_name}}`. See https://go/pks-visibility"
privacy_error_template: "{{reference_location}}Privacy violation: `{{constant_name}}` is private to `{{defining_pack_name}}`, but referenced from `{{referencing_pack_name}}`. See https://go/pks-privacy"
dependency_error_template: "{{reference_location}}Dependency violation: `{{constant_name}}` belongs to `{{defining_pack_name}}`, but `{{referencing_pack_relative_yml}}` does not specify a dependency on `{{defining_pack_name}}`. See https://go/pks-dependency"See BENCHMARKS.md
- @alexevanczuk for https://github.com/alexevanczuk/packs
- Current (@gmcgibbon, @rafaelfranca), and Ex-Shopifolks (@exterm, @wildmaples) for open-sourcing and maintaining
packwerk - Gusties, and the Ruby/Rails Modularity Slack Server, for continued feedback and support
- @mzruya for the initial implementation and Rust inspiration
