Skip to content

Commit

Permalink
Checker error template overrides (#2)
Browse files Browse the repository at this point in the history
* adding checker error template overrides

* bumping version

* adding error template overrides description
  • Loading branch information
perryqh authored Jul 30, 2024
1 parent 0261a10 commit ea87ed9
Show file tree
Hide file tree
Showing 42 changed files with 318 additions and 21 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[package]
name = "pks"
version = "0.2.16"
version = "0.2.17"
edition = "2021"
description = "Welcome! Please see https://github.com/rubyatscale/pks for more information!"
license = "MIT"
Expand Down
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ Instructions:
- Confirm the output of `git diff` is empty
- Please file an issue if it's not!


# New to Rust?
Me too! This is my first Rust project, so I'd love to have feedback, advice, and contributions!

Expand Down Expand Up @@ -155,6 +156,32 @@ enforcement_globs_ignore:
reason: "The other dependency violations are fine as those packs will be absorbed into this one."
```
## "check" error messages
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
```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"
```
# Benchmarks
See [BENCHMARKS.md](https://github.com/rubyatscale/pks/blob/main/BENCHMARKS.md)
Expand Down
67 changes: 48 additions & 19 deletions src/packs/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,35 +180,64 @@ fn build_violation_checker_configuration(
violation_checker_overrides: Option<&CheckerOverrides>,
) -> HashMap<CheckerType, CheckerConfiguration> {
let mut checker_configurations = HashMap::new();
checker_configurations.insert(
CheckerType::Dependency,
CheckerConfiguration::new(CheckerType::Dependency),
);
checker_configurations.insert(
CheckerType::Privacy,
CheckerConfiguration::new(CheckerType::Privacy),
);
checker_configurations.insert(
CheckerType::Layer,
CheckerConfiguration::new(CheckerType::Layer),
);
checker_configurations.insert(
CheckerType::Visibility,
CheckerConfiguration::new(CheckerType::Visibility),
);
let mut checker_configuration =
let mut folder_privacy_checker_configuration =
CheckerConfiguration::new(CheckerType::FolderPrivacy);
let mut privacy_checker_configuration =
CheckerConfiguration::new(CheckerType::Privacy);
let mut dependency_checker_configuration =
CheckerConfiguration::new(CheckerType::Dependency);
let mut layer_checker_configuration =
CheckerConfiguration::new(CheckerType::Layer);
let mut visibility_checker_configuration =
CheckerConfiguration::new(CheckerType::Visibility);

if let Some(violation_checker_overrides) = violation_checker_overrides {
if let Some(error_template) = violation_checker_overrides
.folder_privacy_error_template
.clone()
{
checker_configuration.override_error_template =
folder_privacy_checker_configuration.override_error_template =
Some(error_template);
}
if let Some(error_template) =
violation_checker_overrides.privacy_error_template.clone()
{
privacy_checker_configuration.override_error_template =
Some(error_template);
}
if let Some(error_template) =
violation_checker_overrides.layer_error_template.clone()
{
layer_checker_configuration.override_error_template =
Some(error_template);
}
if let Some(error_template) = violation_checker_overrides
.visibility_error_template
.clone()
{
visibility_checker_configuration.override_error_template =
Some(error_template);
}
if let Some(error_template) = violation_checker_overrides
.dependency_error_template
.clone()
{
dependency_checker_configuration.override_error_template =
Some(error_template);
}
}
checker_configurations.insert(
CheckerType::FolderPrivacy,
folder_privacy_checker_configuration,
);
checker_configurations
.insert(CheckerType::Dependency, dependency_checker_configuration);
checker_configurations
.insert(CheckerType::Privacy, privacy_checker_configuration);
checker_configurations
.insert(CheckerType::Layer, layer_checker_configuration);
checker_configurations
.insert(CheckerType::FolderPrivacy, checker_configuration);
.insert(CheckerType::Visibility, visibility_checker_configuration);

checker_configurations
}
Expand Down
4 changes: 4 additions & 0 deletions src/packs/raw_configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ pub(crate) struct RawConfiguration {
pub struct CheckerOverrides {
/// Mustache style error message template
pub folder_privacy_error_template: Option<String>,
pub privacy_error_template: Option<String>,
pub layer_error_template: Option<String>,
pub visibility_error_template: Option<String>,
pub dependency_error_template: Option<String>,
}

pub(crate) fn get(absolute_root: &Path) -> anyhow::Result<RawConfiguration> {
Expand Down
23 changes: 23 additions & 0 deletions tests/check_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,29 @@ pub fn stripped_output(output: Vec<u8>) -> String {
String::from_utf8_lossy(&strip_ansi_escapes::strip(output)).to_string()
}

#[test]
fn test_check_with_privacy_dependency_error_template_overrides(
) -> Result<(), Box<dyn Error>> {
let output = Command::cargo_bin("pks")?
.arg("--project-root")
.arg("tests/fixtures/privacy_violation_overrides")
.arg("--debug")
.arg("check")
.assert()
.failure()
.get_output()
.stdout
.clone();

let stripped_output = stripped_output(output);

assert!(stripped_output.contains("2 violation(s) detected:"));
assert!(stripped_output.contains("packs/foo/app/services/foo.rb:3:4\nDependency violation: `::Bar` belongs to `packs/bar`, but `packs/foo/package.yml` does not specify a dependency on `packs/bar`. See https://go/pks-dependency"));
assert!(stripped_output.contains("packs/foo/app/services/foo.rb:3:4\nPrivacy violation: `::Bar` is private to `packs/bar`, but referenced from `packs/foo`. See https://go/pks-privacy"));

common::teardown();
Ok(())
}
#[test]
fn test_check() -> Result<(), Box<dyn Error>> {
let output = Command::cargo_bin("pks")?
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
ActiveSupport::Inflector.inflections do |do_not_couple_implementation_to_this_string|
do_not_couple_implementation_to_this_string.acronym 'API'

# Using single vs double quotes inconsistently
do_not_couple_implementation_to_this_string.acronym "CSV"
end
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# feature_flags, a utility pack, should not rely on Payments, a product pack
Payments
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
enforce_layers: true
layer: utilities
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module Payments
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
enforce_layers: true
layer: product
32 changes: 32 additions & 0 deletions tests/fixtures/layer_violations_with_overrides/packwerk.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# See: Setting up the configuration file
# https://github.com/Shopify/packwerk/blob/main/USAGE.md#setting-up-the-configuration-file

# List of patterns for folder paths to include
# include:
# - "**/*.{rb,rake,erb}"

# List of patterns for folder paths to exclude
# exclude:
# - "{bin,node_modules,script,tmp,vendor}/**/*"

# Patterns to find package configuration files
# package_paths: "**/"

# List of custom associations, if any
# custom_associations:
# - "cache_belongs_to"

# Whether or not you want the cache enabled (disabled by default)
cache: false

# Where you want the cache to be stored (default below)
# cache_directory: 'tmp/cache/packwerk'

layers:
- tooling
- deprecated
- product
- utilities

checker_overrides:
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"
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Company
class Widget end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
class SomeRootClass; end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module UiHelper
def self.help
"I'm a helper"
end
end

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module SomeConcern; end
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Bar
def bar; end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
enforce_privacy: true
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Foo
def calls_bar_without_a_stated_dependency
::Bar
end

def calls_baz_with_a_stated_dependency
Baz
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# This defines ::Foo::Bar, which is different than ::Bar
module Foo
module Bar
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
enforce_dependencies: true
enforce_privacy: true
dependencies:
- packs/baz
30 changes: 30 additions & 0 deletions tests/fixtures/privacy_violation_overrides/packwerk.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# See: Setting up the configuration file
# https://github.com/Shopify/packwerk/blob/main/USAGE.md#setting-up-the-configuration-file

# List of patterns for folder paths to include
# include:
# - "**/*.{rb,rake,erb}"

# List of patterns for folder paths to exclude
# exclude:
# - "{bin,node_modules,script,tmp,vendor}/**/*"

# Patterns to find package configuration files
# package_paths: "**/"

# List of custom associations, if any
# custom_associations:
# - "cache_belongs_to"

# Whether or not you want the cache enabled (disabled by default)
cache: false

autoload_roots:
app/company_data: "::Company"

# Where you want the cache to be stored (default below)
# cache_directory: 'tmp/cache/packwerk'

checker_overrides:
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"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# some ruby script
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module Baz
def sumpn
Foo.nothing
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dependencies:
- packs/foos/foo
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module Foo
def self.nothing
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module OtherFoo
def calls_bar_without_a_stated_dependency
::Bar
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
enforce_visibility: true
visible_to:
- packs/foos/too

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module Too
def sumpn
Foo.nothing
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dependencies:
- packs/foos/foo
25 changes: 25 additions & 0 deletions tests/fixtures/visibility_violations_with_overrides/packwerk.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# See: Setting up the configuration file
# https://github.com/Shopify/packwerk/blob/main/USAGE.md#setting-up-the-configuration-file

# List of patterns for folder paths to include
# include:
# - "**/*.{rb,rake,erb}"

# List of patterns for folder paths to exclude
# exclude:
# - "{bin,node_modules,script,tmp,vendor}/**/*"

# Patterns to find package configuration files
# package_paths: "**/"

# List of custom associations, if any
# custom_associations:
# - "cache_belongs_to"

# Whether or not you want the cache enabled (disabled by default)
cache: false

# Where you want the cache to be stored (default below)
# cache_directory: 'tmp/cache/packwerk'
checker_overrides:
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"
2 changes: 1 addition & 1 deletion tests/folder_privacy_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fn test_check() -> Result<(), Box<dyn Error>> {
}

#[test]
fn test_check_with_overrides() -> Result<(), Box<dyn Error>> {
fn test_check_with_error_template_overrides() -> Result<(), Box<dyn Error>> {
Command::cargo_bin("pks")?
.arg("--project-root")
.arg("tests/fixtures/folder_privacy_violations_with_overrides")
Expand Down
Loading

0 comments on commit ea87ed9

Please sign in to comment.