A 100% Rust implementation of packwerk, a gradual modularization platform for Ruby.
- Currently can serve as a drop-in replacement on Gusto's extra-large Rails monolith
- This is a work in progress! Please see Verification for instructions on how to verify the output of
packs
is the same aspackwerk
.
- 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/alexevanczuk/packs for more information!
Usage: pks [OPTIONS] <COMMAND>
Commands:
greet Just saying hi
init Set up packs in this project
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
update-dependencies-for-constant Add missing dependencies for the pack that defines the constant
check-unnecessary-dependencies Check for dependencies that when removed produce no violations.
add-dependencies Add everything a pack depends on (may cause cycles)
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-pack-dependencies List packs that depend on a pack
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)
--disable-enforce-dependencies Globally disable enforce_dependency
--disable-enforce-folder-privacy Globally disable enforce_folder_privacy
--disable-enforce-layers Globally disable enforce_layers
--disable-enforce-privacy Globally disable enforce_privacy
--disable-enforce-visibility Globally disable enforce_visibility
-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 packs
is straightforward and results in a much more responsive experience.
Directions:
- Follow INSTALLATION.md instructions to install
packs
- Follow the configuration directions to configure the extension to use
packs
instead of the ruby gem by setting the executable topacks check
As packs
is still a work-in-progress, it's possible it will not produce the same results as the ruby implementation (see Not Yet Supported). If so, please file an issue – I'd love to try to support your use case!
Instructions:
- Follow the directions above to install
packs
- Run
packs update
- Confirm the output of
git diff
is empty - Please file an issue if it's not!
Me too! This is my first Rust project, so I'd love to have feedback, advice, and contributions!
Rust is a low-level language with high-level abstractions, a rich type system, with a focus on memory safety through innovative compile-time checks on memory usage.
If you're new to Rust, don't be intimidated! https://www.rust-lang.org has tons of great learning resources.
If you'd like to contribute but don't know where to start, please reach out! I'd love to help you get started.
- custom inflections
- custom load paths
- extensible plugin system
There are still some known behavioral differences between packs
and packwerk
. If you find any, please file an issue!
package_paths
must not end in a slash, e.g.packs/*/
is not supported, butpacks/*
is.- A
**
inpackage_paths
is supported, but is not a substitute for a single*
, e.g.packs/**
is supported and will matchpacks/*/*/package.yml
, but will not matchpacks/*/package.yml
.packs/*
must be used to match that.
packs
supports Zeitwerk default namespaces.
For example, if you're using packs-rails
and automatic_namespaces
to configure your default namespaces, and you have
packs/foo/app/models/bar.rb
which is configured to defineFoo::Bar
packs/foo/app/domain/baz.rb
which is configured to defineFoo::Baz
then packs
will automatically read the configuration as specified in the automatic_namespaces
gem and should interpret the namespaces correctly. Please file an issue if you find any problems. There is a known limitation here where acronym-based automatic namespaces are not yet supported (feel free to open an issue if you need this).
If you are not using automatic_namespaces
, you can also explicitly specify the namespaces in packwerk.yml
, like so:
autoload_roots:
packs/foo/app/models: "::Foo"
packs/foo/app/domain: "::Foo"
enforcement_globs_ignore
can be used to specify gitignore-style rules for not enforcing violations.
# packs/product_services/serv1/foo/package.yml
enforce_privacy: true
enforce_visibility: true
enforcement_globs_ignore:
- enforcements:
- privacy
- visiblity
ignores:
- "**/*"
# Enforce incoming privacy and visibility violation references _only_ in `packs/product_services/serv1/**/*`
- "!packs/product_services/serv1/**/*"
reason: "It was decided only to fix incoming violations from serv1. See ticket #232"
# packs/pack2/package.yml
enforce_dependencies: true
dependencies:
# not required because of the below enforcement_globs_ignore
# - packs/pack1
# required because of the enforcement_globs_ignore exception line
- packs/pack3
enforcement_globs_ignore:
- enforcements:
- dependency
ignores:
- "**/*"
# Enforce outgoing dependency violation references _only_ to `packs/pack3/**/*`
- "!packs/pack3/**/*"
reason: "The other dependency violations are fine as those packs will be absorbed into this one."
See BENCHMARKS.md
- 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