blockwatch-cli 0.15.0__tar.gz → 0.16.0__tar.gz
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.
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/CHANGELOG.md +33 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/Cargo.lock +1 -1
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/Cargo.toml +1 -1
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/PKG-INFO +1 -1
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/README.md +1 -1
- blockwatch_cli-0.16.0/clippy.toml +11 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/docs/ci.md +2 -2
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/docs/cli.md +13 -13
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/docs/validators/check-ai.md +3 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/docs/validators/check-lua.md +3 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/docs/validators/keep-sorted.md +3 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/docs/validators/line-count.md +3 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/docs/validators/same-as.md +46 -26
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/setup.py +1 -1
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/blocks.rs +37 -3
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/config.rs +6 -2
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/fs.rs +5 -1
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/path_arguments.rs +15 -3
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/paths_glob.rs +66 -1
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/settings.rs +17 -1
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/skill.md +27 -23
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/validators/check_ai.rs +27 -1
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/validators/check_lua.rs +33 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/validators/keep_sorted.rs +37 -1
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/validators/line_count.rs +30 -1
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/validators/mod.rs +49 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/validators/same_as.rs +313 -59
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/explain.rs +13 -5
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/same_as.rs +35 -0
- blockwatch_cli-0.15.0/clippy.toml +0 -5
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/.agents/.claude-plugin/plugin.json +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/.agents/skills/blockwatch/SKILL.md +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/.claude-plugin/marketplace.json +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/.github/dependabot.yml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/.github/workflows/blockwatch.yml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/.github/workflows/codeql.yml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/.github/workflows/markdownlint.yml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/.github/workflows/pypi.yml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/.github/workflows/release.yml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/.github/workflows/rust.yml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/.github/workflows/trusted-checks.yml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/.gitignore +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/.markdownlint-cli2.yaml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/.pre-commit-config.yaml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/.pre-commit-hooks.yaml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/AGENTS.md +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/CLAUDE.md +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/LICENSE +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/SECURITY.md +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/blockwatch.toml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/codecov.yml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/demo.gif +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/dist-workspace.toml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/docs/agents.md +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/docs/symbols.md +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/docs/validators/README.md +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/docs/validators/affects.md +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/docs/validators/keep-unique.md +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/docs/validators/line-pattern.md +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/pypi/pyproject.toml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/pyproject.toml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/release.toml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/scripts/check_language_count.lua +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/scripts/check_latest_gpt_nano_model.lua +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/block_parser.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/cli.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/diff_parser.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/explain.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/flags.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/bash.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/c.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/c_sharp.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/cmake.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/cpp.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/css.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/dart.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/dockerfile.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/elixir.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/go.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/graphql.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/groovy.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/hcl.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/html.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/java.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/javascript.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/json.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/json.scm +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/kotlin.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/lean.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/lua.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/makefile.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/markdown.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/mdx.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/mod.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/nix.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/ocaml.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/php.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/proto.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/python.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/ruby.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/rust.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/scala.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/sql.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/starlark.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/swift.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/toml.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/tsx.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/typescript.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/xml.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/language_parsers/yaml.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/lib.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/main.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/repo_path.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/report.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/sarif.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/scan.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/symbol_path.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/symbols.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/tag_parser.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/target_reference.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/validators/affects.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/validators/keep_unique.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/validators/line_pattern.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/violation_address.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/src/virtual_blocks.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/affects.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/check_ai.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/check_lua.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/common/empty.toml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/common/mod.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/config.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/encoding.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/general.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/keep_sorted.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/keep_unique.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/line_count.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/line_pattern.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/list.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/path_arguments.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/repository_root.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/sarif.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/skill.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/suppressions.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/symbol_references.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/affects/cross_file_source.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/affects/cross_file_target.md +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/affects/custom_ext_source.javascript +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/affects/custom_ext_target.javascript +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/affects/dangling.md +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/affects/missing_symbol.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/affects/more_added_than_deleted.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/affects/multiline_tag_source.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/affects/multiline_tag_target.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/affects/nested_mapping_source.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/affects/nested_mapping_target.yaml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/affects/sibling.md +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/affects/symbol_source.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/affects/symbol_target.json +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/affects/table_source.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/affects/table_target.toml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/affects/tag_and_content_source.ts +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/affects/tag_and_content_target.ts +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/affects/whole_file_source.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/affects/whole_file_target.json +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/check_ai/two_blocks.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/check_lua/affects.lua +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/check_lua/affects.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/check_lua/affects_drift.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/check_lua/blocking_io.lua +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/check_lua/blocking_io.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/check_lua/echo.lua +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/check_lua/fail.lua +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/check_lua/mode.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/check_lua/os.lua +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/check_lua/pathfmt/report_path.lua +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/check_lua/pathfmt/source.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/check_lua/pattern.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/check_lua/pattern_many_matches.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/check_lua/pattern_no_match.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/check_lua/success.lua +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/check_lua/success_and_failure.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/config/glob_target/config.toml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/config/glob_target/package.json +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/config/glob_target/plugins/blue/package.json +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/config/glob_target/plugins/green/package.json +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/config/glob_target/plugins/red/package.json +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/explain/a.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/explain/check.lua +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/general/block_selection.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/general/custom_file_extension.javascript +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/general/custom_file_extension.python +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/general/disable_enable.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/general/paths/invalid.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/general/paths/subdir/nested_invalid.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/general/paths/valid.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/general/severity.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/hidden/.github/workflows/ci.yml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/keep_sorted/orderings.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/keep_unique/basic_and_pattern.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/line_count/equality_and_greater_than.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/line_pattern/forbidden_text.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/line_pattern/hex_and_word_patterns.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/list/.hg/.gitkeep +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/list/a.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/list/b.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/positions/unicode_columns.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/same_as/custom_ext_source.javascript +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/same_as/custom_ext_target.javascript +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/same_as/package.json +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/same_as/paths/alpha/item.txt +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/same_as/paths/beta/item.txt +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/same_as/paths_and_block_source.md +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/same_as/paths_source.md +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/same_as/sibling.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/same_as/source.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/same_as/split_table_source.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/same_as/split_table_target.toml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/same_as/symbol_mismatch_source.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/same_as/symbol_source.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/same_as/target.md +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/same_as/whole_file_mismatch_source.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/same_as/whole_file_source.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/same_as/whole_file_target.json +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/sarif/advisory.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/sarif/clean.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/sarif/sorted.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/sarif/unnamed.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/suppressions/commit_msg.txt +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/suppressions/commit_msg_gone_a.txt +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/suppressions/commit_msg_gone_b.txt +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/suppressions/commit_msg_lowercase_trailer.txt +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/suppressions/commit_msg_malformed.txt +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/suppressions/commit_msg_with_noise.txt +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/suppressions/dangling.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/suppressions/sorted.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/suppressions/symbol.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/suppressions/symbol_target.json +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/suppressions/two_rules.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/suppressions/unnamed.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/symbol_references/escapes_source.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/symbol_references/escapes_target.json +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/symbol_references/filter_source.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/symbol_references/filter_target.yaml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/symbol_references/json_source.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/symbol_references/json_target.json +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/symbol_references/jsonc_source.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/symbol_references/jsonc_target.jsonc +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/symbol_references/markdown_source.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/symbol_references/markdown_target.markdown +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/symbol_references/md_source.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/symbol_references/md_target.md +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/symbol_references/mdx_source.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/symbol_references/mdx_target.mdx +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/symbol_references/mixed_list_source.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/symbol_references/mixed_list_target.json +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/symbol_references/toml_source.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/symbol_references/toml_target.toml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/symbol_references/yaml_source.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/symbol_references/yaml_target.yaml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/symbol_references/yml_source.rs +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/symbol_references/yml_target.yml +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/verbosity/affects/pair.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/verbosity/clean/counted.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/verbosity/clean/noop.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/verbosity/clean/notes.txt +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/verbosity/clean/sorted.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/verbosity/diff/two_blocks.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/verbosity/failing/unsorted.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/testdata/verbosity/mixed/both_kinds.py +0 -0
- {blockwatch_cli-0.15.0 → blockwatch_cli-0.16.0}/tests/verbosity.rs +0 -0
|
@@ -12,6 +12,39 @@ Releases up to and including v0.3.11 predate this file. Their notes live on the
|
|
|
12
12
|
|
|
13
13
|
## [Unreleased] - ReleaseDate
|
|
14
14
|
|
|
15
|
+
## [0.16.0] - 2026-10-24
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- Without `same-as-pattern`, `same-as` compares each line that isn't blank as one value. Before, the whole block was
|
|
20
|
+
one value, so `same-as-mode` and `same-as-format` didn't work on a block of several lines. For example,
|
|
21
|
+
`same-as-mode="single"` passed with two lines, `subset` failed on a line that the target had, and `numeric` reported
|
|
22
|
+
that `1` and `2` together are not a number ([#179](https://github.com/mennanov/blockwatch/issues/179)).
|
|
23
|
+
- Without `same-as-mode`, a block without `same-as-pattern` compares in `sequence` mode. So it still fails when its
|
|
24
|
+
lines differ from the target's or come in another order. A block with a pattern still compares in `set` mode.
|
|
25
|
+
- A block without `same-as-pattern` now fails when its target's pattern finds a value more than once, or in another
|
|
26
|
+
order: `["1.2.3"] != ["1.2.3", "1.2.3"]`. Before, the default `set` mode ignored this. To keep the old result, add
|
|
27
|
+
`same-as-mode="set"`, or `same-as-mode="subset"` when the target may hold other values too.
|
|
28
|
+
- `same-as` now fails on two empty blocks, as it does on a pattern that matches nothing. The message for both is
|
|
29
|
+
`this block or its target has no values to compare`.
|
|
30
|
+
- `same-as-paths` no longer needs `same-as-pattern`. Without one, each line of the block must be a path, in sorted
|
|
31
|
+
order.
|
|
32
|
+
- An attribute that changes how a validator works now stops the run on a block without the validator's main
|
|
33
|
+
attribute. Before, it did nothing there. For example, `same-as-mode="sequence"` on a target let values in another
|
|
34
|
+
order pass. The attributes are:
|
|
35
|
+
- `same-as-mode` and `same-as-format`, without `same-as` or `same-as-paths`;
|
|
36
|
+
- `same-as-pattern`, without `same-as`, `same-as-paths` or `name`, because only a block with a name can be a target;
|
|
37
|
+
- `keep-sorted-pattern` and `keep-sorted-format`, without `keep-sorted`;
|
|
38
|
+
- `line-count-pattern`, without `line-count`;
|
|
39
|
+
- `check-ai-pattern`, without `check-ai`;
|
|
40
|
+
- `check-lua-pattern` and `check-lua-timeout`, without `check-lua`.
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
|
|
44
|
+
- An empty alternative in braces now matches, so `{,sub/}a.yml` matches both `a.yml` and `sub/a.yml`. Before, it
|
|
45
|
+
matched only `sub/a.yml`, with no error. This was true of every glob: a `[[block]]` target, `same-as-paths`, the
|
|
46
|
+
`ignore` setting and `--ignore`, and a path argument ([#177](https://github.com/mennanov/blockwatch/issues/177)).
|
|
47
|
+
|
|
15
48
|
## [0.15.0] - 2026-10-21
|
|
16
49
|
|
|
17
50
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: blockwatch-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.16.0
|
|
4
4
|
Summary: Language agnostic linter that keeps your code and documentation in sync and valid
|
|
5
5
|
Keywords: linter,sync,drift,comments,agents
|
|
6
6
|
Home-Page: https://github.com/mennanov/blockwatch
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# The integration tests run inside this repository, so a run would read its blockwatch.toml, which
|
|
2
|
+
# skips tests/testdata.
|
|
3
|
+
disallowed-macros = [
|
|
4
|
+
{ path = "assert_cmd::cargo_bin_cmd", reason = "use common::cargo_bin_cmd!, which passes an empty config" },
|
|
5
|
+
]
|
|
6
|
+
# globset drops an empty alternative in braces by default, so `{,sub/}a.yml` would not match
|
|
7
|
+
# `a.yml`.
|
|
8
|
+
disallowed-methods = [
|
|
9
|
+
{ path = "globset::Glob::new", reason = "use paths_glob::glob_builder, which keeps an empty alternative" },
|
|
10
|
+
{ path = "globset::GlobBuilder::new", reason = "use paths_glob::glob_builder, which keeps an empty alternative" },
|
|
11
|
+
]
|
|
@@ -19,7 +19,7 @@ To use [pre-commit](https://pre-commit.com), add the hook to `.pre-commit-config
|
|
|
19
19
|
|
|
20
20
|
```yaml
|
|
21
21
|
- repo: https://github.com/mennanov/blockwatch
|
|
22
|
-
rev: v0.
|
|
22
|
+
rev: v0.16.0 # Use latest release
|
|
23
23
|
hooks:
|
|
24
24
|
- id: blockwatch
|
|
25
25
|
```
|
|
@@ -59,7 +59,7 @@ The `blockwatch` hook runs before the commit message exists, so it cannot see a
|
|
|
59
59
|
|
|
60
60
|
```yaml
|
|
61
61
|
- repo: https://github.com/mennanov/blockwatch
|
|
62
|
-
rev: v0.
|
|
62
|
+
rev: v0.16.0 # Use latest release
|
|
63
63
|
hooks:
|
|
64
64
|
- id: blockwatch-commit-msg
|
|
65
65
|
```
|
|
@@ -557,15 +557,15 @@ Nothing else about the run changes: the same violations are found, and the exit
|
|
|
557
557
|
"tool": {
|
|
558
558
|
"driver": {
|
|
559
559
|
"name": "blockwatch",
|
|
560
|
-
"version": "0.
|
|
561
|
-
"semanticVersion": "0.
|
|
560
|
+
"version": "0.16.0",
|
|
561
|
+
"semanticVersion": "0.16.0",
|
|
562
562
|
"informationUri": "https://github.com/mennanov/blockwatch",
|
|
563
563
|
"rules": [
|
|
564
564
|
{
|
|
565
565
|
"id": "keep-sorted",
|
|
566
566
|
"name": "keep-sorted",
|
|
567
567
|
"shortDescription": { "text": "Requires the lines of a block to stay in order." },
|
|
568
|
-
"helpUri": "https://github.com/mennanov/blockwatch/blob/v0.
|
|
568
|
+
"helpUri": "https://github.com/mennanov/blockwatch/blob/v0.16.0/docs/validators/keep-sorted.md"
|
|
569
569
|
}
|
|
570
570
|
]
|
|
571
571
|
}
|
|
@@ -790,16 +790,16 @@ A trace stops where its validator stops, such as at the first line out of order.
|
|
|
790
790
|
file, and `text` is that line trimmed, as the validator compares it. A key that holds what a violation reports has the
|
|
791
791
|
name the violation's `data` gives it, such as `reason`.
|
|
792
792
|
|
|
793
|
-
| Validator | What it shows
|
|
794
|
-
|
|
795
|
-
| `same-as` | `records`: each match of `same-as-pattern` in the block, with its `line`, the text the regex matched (`match`), and each named group after `same-as-format` (`groups`). `targets`: the `records` of each target, and the `reason` of its violation, or `null` when the two agree. `paths`: with `same-as-paths`, the `glob`, the paths it `matched`, their `records` and the `reason`. |
|
|
796
|
-
| `keep-sorted` | `lines`: each line read, with the `value` it compares, or `null` when `keep-sorted-pattern` skips the line. `format` and `order`.
|
|
797
|
-
| `keep-unique` | `lines`, as for `keep-sorted`. A repeated value has `duplicate_of`: the line where it first appeared.
|
|
798
|
-
| `line-pattern` | `lines`: each line read, with the text `line-pattern` matched (`match`). A line that fails has `fails`: the check it fails. `forbidden_match` is the text `line-pattern-forbidden` found.
|
|
799
|
-
| `line-count` | `lines`, as for `keep-sorted`. A line counts when its `value` isn't `null`. The number of lines counted (`actual`), and the rule (`op` and `expected`).
|
|
800
|
-
| `affects` | Whether the diff changed the block (`block_changed`). For each target: whether the diff `changed` it, or the `reason` it doesn't resolve.
|
|
801
|
-
| `check-lua` | The `content` and the `affects` the script got, and the message it returned (`lua_error`), or `null` when it passed. `affects` is `ctx.affects` without the content of each target.
|
|
802
|
-
| `check-ai` | The `model`, the `prompt` sent and the `response`. Both are `null` when `check-ai-pattern` matched nothing, so no request was sent.
|
|
793
|
+
| Validator | What it shows |
|
|
794
|
+
|----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
795
|
+
| `same-as` | `records`: each match of `same-as-pattern` in the block, or each line without a pattern, with its `line`, the text the regex matched (`match`), and each named group after `same-as-format` (`groups`). `mode`: the `same-as-mode` it compared with. `targets`: the `records` of each target, and the `reason` of its violation, or `null` when the two agree. `paths`: with `same-as-paths`, the `glob`, the paths it `matched`, their `records` and the `reason`. |
|
|
796
|
+
| `keep-sorted` | `lines`: each line read, with the `value` it compares, or `null` when `keep-sorted-pattern` skips the line. `format` and `order`. |
|
|
797
|
+
| `keep-unique` | `lines`, as for `keep-sorted`. A repeated value has `duplicate_of`: the line where it first appeared. |
|
|
798
|
+
| `line-pattern` | `lines`: each line read, with the text `line-pattern` matched (`match`). A line that fails has `fails`: the check it fails. `forbidden_match` is the text `line-pattern-forbidden` found. |
|
|
799
|
+
| `line-count` | `lines`, as for `keep-sorted`. A line counts when its `value` isn't `null`. The number of lines counted (`actual`), and the rule (`op` and `expected`). |
|
|
800
|
+
| `affects` | Whether the diff changed the block (`block_changed`). For each target: whether the diff `changed` it, or the `reason` it doesn't resolve. |
|
|
801
|
+
| `check-lua` | The `content` and the `affects` the script got, and the message it returned (`lua_error`), or `null` when it passed. `affects` is `ctx.affects` without the content of each target. |
|
|
802
|
+
| `check-ai` | The `model`, the `prompt` sent and the `response`. Both are `null` when `check-ai-pattern` matched nothing, so no request was sent. |
|
|
803
803
|
|
|
804
804
|
## Run Reports
|
|
805
805
|
|
|
@@ -10,6 +10,9 @@ mention the company name", "no TODOs left", "the tone matches the rest of the pa
|
|
|
10
10
|
| `check-ai` | the condition, in natural language | — |
|
|
11
11
|
| `check-ai-pattern` | regex; every match's `(?P<value>…)` group, or whole match | whole block |
|
|
12
12
|
|
|
13
|
+
`check-ai-pattern` works only next to `check-ai`. On a block without it, it stops the run, because it would do nothing
|
|
14
|
+
there.
|
|
15
|
+
|
|
13
16
|
## Example
|
|
14
17
|
|
|
15
18
|
```html
|
|
@@ -19,6 +19,9 @@ string when it fails.
|
|
|
19
19
|
|
|
20
20
|
<!-- </block> -->
|
|
21
21
|
|
|
22
|
+
`check-lua-pattern` and `check-lua-timeout` work only next to `check-lua`. On a block without it, they stop the run,
|
|
23
|
+
because they would do nothing there.
|
|
24
|
+
|
|
22
25
|
The script path must point to a file inside the repository. Paths that escape it — absolute paths outside the project,
|
|
23
26
|
`../` traversal, or symlinks pointing outward — are rejected.
|
|
24
27
|
|
|
@@ -10,6 +10,9 @@ Keeps a list ordered. Eliminates "please sort this" review nits.
|
|
|
10
10
|
| `keep-sorted-pattern` | regex; the `(?P<value>…)` group, or the whole match | whole line |
|
|
11
11
|
| `keep-sorted-format` | `numeric` | text |
|
|
12
12
|
|
|
13
|
+
`keep-sorted-pattern` and `keep-sorted-format` work only next to `keep-sorted`. On a block without it, they stop the
|
|
14
|
+
run, because they would do nothing there.
|
|
15
|
+
|
|
13
16
|
## Example
|
|
14
17
|
|
|
15
18
|
```python
|
|
@@ -10,6 +10,9 @@ size of the enum it maps.
|
|
|
10
10
|
| `line-count` | `<N`, `<=N`, `==N`, `>=N`, `>N` | — |
|
|
11
11
|
| `line-count-pattern` | regex; a line counts when it gives a value | every line that has text |
|
|
12
12
|
|
|
13
|
+
`line-count-pattern` works only next to `line-count`. On a block without it, it stops the run, because it would do
|
|
14
|
+
nothing there.
|
|
15
|
+
|
|
13
16
|
## Example
|
|
14
17
|
|
|
15
18
|
```python
|
|
@@ -8,13 +8,17 @@ It runs on every scan, with or without a diff.
|
|
|
8
8
|
|
|
9
9
|
## Syntax
|
|
10
10
|
|
|
11
|
-
| Attribute | Value | Default
|
|
12
|
-
|
|
13
|
-
| `same-as` | One or more [targets](#targets), comma-separated | —
|
|
14
|
-
| `same-as-paths` | A [glob](#paths-as-targets) of paths to compare | —
|
|
15
|
-
| `same-as-pattern` | A regex that picks the values to compare |
|
|
16
|
-
| `same-as-mode` | `set`, `sequence`, `single` or `subset` | `set`
|
|
17
|
-
| `same-as-format` | `numeric`, `identifier` or `flat`, or [one for each group](#a-format-for-each-group) | text
|
|
11
|
+
| Attribute | Value | Default |
|
|
12
|
+
|-------------------|--------------------------------------------------------------------------------------|------------------------------------------|
|
|
13
|
+
| `same-as` | One or more [targets](#targets), comma-separated | — |
|
|
14
|
+
| `same-as-paths` | A [glob](#paths-as-targets) of paths to compare | — |
|
|
15
|
+
| `same-as-pattern` | A regex that picks the values to compare | each line |
|
|
16
|
+
| `same-as-mode` | `set`, `sequence`, `single` or `subset` | [`sequence` or `set`](#comparison-modes) |
|
|
17
|
+
| `same-as-format` | `numeric`, `identifier` or `flat`, or [one for each group](#a-format-for-each-group) | text |
|
|
18
|
+
|
|
19
|
+
`same-as-mode` and `same-as-format` work only next to `same-as` or `same-as-paths`. `same-as-pattern` also works next
|
|
20
|
+
to `name`, because a block with a name can be a target. On any other block, they stop the run, because they would do
|
|
21
|
+
nothing there.
|
|
18
22
|
|
|
19
23
|
## Targets
|
|
20
24
|
|
|
@@ -28,7 +32,8 @@ It runs on every scan, with or without a diff.
|
|
|
28
32
|
|
|
29
33
|
## Example
|
|
30
34
|
|
|
31
|
-
With no other attributes, both blocks must hold **identical** text
|
|
35
|
+
With no other attributes, both blocks must hold **identical** text: the same lines in the same order. Blank lines and
|
|
36
|
+
indentation don't count. This suits
|
|
32
37
|
text you can't share any other way, such as a command shown in two docs:
|
|
33
38
|
|
|
34
39
|
**README.md**:
|
|
@@ -145,7 +150,8 @@ pub const NODE_VERSION: &str = "20.11.1";
|
|
|
145
150
|
```
|
|
146
151
|
|
|
147
152
|
The file has no block of its own, so the referencing block's pattern reads both sides. For anything longer than one
|
|
148
|
-
line, you will want a pattern: without one, the block must equal the entire file.
|
|
153
|
+
line, you will want a pattern: without one, the block must equal the entire file. Or add `same-as-mode="subset"`: then
|
|
154
|
+
each line of the block must be a line of the file.
|
|
149
155
|
|
|
150
156
|
## Symbols as targets
|
|
151
157
|
|
|
@@ -187,10 +193,10 @@ show every entry of a directory, such as the services in a README:
|
|
|
187
193
|
The glob lists `services/auth/`, `services/billing/` and `services/search/`. The pattern picks `auth`, `billing` and
|
|
188
194
|
`search` from them, and `billing` and `search` from the block. So the run reports that `auth` is missing.
|
|
189
195
|
|
|
190
|
-
-
|
|
191
|
-
`
|
|
192
|
-
- **The paths are sorted, and each one is listed once.**
|
|
193
|
-
|
|
196
|
+
- **The block's `same-as-pattern` reads both the block and the paths.** Without a pattern, each line of the block must
|
|
197
|
+
be a path, such as `services/billing/`.
|
|
198
|
+
- **The paths are sorted, and each one is listed once.** Without a pattern, or with `same-as-mode="sequence"`, the
|
|
199
|
+
block must list them in that order. To allow any order, add `same-as-mode="set"`.
|
|
194
200
|
- **A trailing `/` lists directories.** A directory is listed when it holds a file, and `services/billing/` is listed
|
|
195
201
|
once however many files it holds. Without the `/`, the glob lists files: `crates/*/AGENTS.md`.
|
|
196
202
|
- **`*` stops at `/`.** `services/*/` lists `services/billing/`, but not `services/billing/src/`. `**` matches any
|
|
@@ -205,12 +211,22 @@ A block can have both `same-as` and `same-as-paths`. Each target is compared wit
|
|
|
205
211
|
|
|
206
212
|
## Comparison modes
|
|
207
213
|
|
|
208
|
-
| `same-as-mode`
|
|
209
|
-
|
|
210
|
-
| `set`
|
|
211
|
-
| `sequence`
|
|
212
|
-
| `single`
|
|
213
|
-
| `subset`
|
|
214
|
+
| `same-as-mode` | Passes when |
|
|
215
|
+
|----------------|-----------------------------------------------------------------|
|
|
216
|
+
| `set` | both sides hold the same values, in any order, ignoring repeats |
|
|
217
|
+
| `sequence` | both sides hold the same values in the same order |
|
|
218
|
+
| `single` | each side holds exactly one value, and the two are equal |
|
|
219
|
+
| `subset` | every value in this block also appears in the target |
|
|
220
|
+
|
|
221
|
+
Without `same-as-mode`, the mode depends on the block's own `same-as-pattern`. A target's pattern doesn't change it.
|
|
222
|
+
|
|
223
|
+
- **The block has no pattern:** `sequence`. Each line that isn't blank is a value, so the two sides must hold the same
|
|
224
|
+
lines in the same order, as in the [example](#example).
|
|
225
|
+
- **The block has a pattern:** `set`.
|
|
226
|
+
|
|
227
|
+
So adding a pattern stops order from mattering. To keep the order, add `same-as-mode="sequence"` too. And a block
|
|
228
|
+
without a pattern fails when its target's pattern finds a value twice: `["1.2.3"] != ["1.2.3", "1.2.3"]`. To allow
|
|
229
|
+
repeats, add `same-as-mode="set"` or `same-as-mode="subset"`.
|
|
214
230
|
|
|
215
231
|
`subset` checks one direction only. Use it when one side should hold part of the other, like a test that uses only
|
|
216
232
|
some of the environment variables:
|
|
@@ -356,14 +372,18 @@ const TIMEOUT_SECS: f64 = 2.0;
|
|
|
356
372
|
## Notes
|
|
357
373
|
|
|
358
374
|
- **Which block's attributes count.** The referencing block's `same-as-mode` and `same-as-format` decide how to compare.
|
|
359
|
-
|
|
360
|
-
|
|
375
|
+
On a target, or any block without `same-as` or `same-as-paths`, they stop the run, because they would do nothing
|
|
376
|
+
there. Each block's `same-as-pattern` reads only that block. A whole file, a symbol or a list of paths has no block,
|
|
377
|
+
so the referencing block's pattern reads it too. Only a block with a name can be a target, so `same-as-pattern` on a
|
|
378
|
+
block without `same-as`, `same-as-paths` or `name` stops the run.
|
|
361
379
|
- **Violations:** a missing block, a missing or repeated key, a value that is not a number under `numeric`, a `single`
|
|
362
|
-
side without exactly one value, a
|
|
363
|
-
and a `same-as-paths` glob that matches no path. Two empty sides do not count as
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
380
|
+
side without exactly one value, a side with no values (such as an empty block, or a pattern that matches nothing),
|
|
381
|
+
two patterns with different groups, and a `same-as-paths` glob that matches no path. Two empty sides do not count as
|
|
382
|
+
equal.
|
|
383
|
+
- **Errors that stop the run:** an unknown `same-as-mode` or `same-as-format`, either of them on a block without
|
|
384
|
+
`same-as` or `same-as-paths`, `same-as-pattern` on a block without `same-as`, `same-as-paths` or `name`, a
|
|
385
|
+
`same-as-format` entry for a group that the pattern doesn't have, an invalid regex or glob, a missing target file,
|
|
386
|
+
and a symbol in a file with a syntax error, without symbols, or with several YAML documents.
|
|
367
387
|
- **Run it on the whole tree now and then.** `same-as` needs no diff, so a plain `blockwatch` run catches drift that
|
|
368
388
|
`--only-changed` misses. See [CI integration](../ci.md).
|
|
369
389
|
- **A rename alone goes unnoticed in a diff.** Renaming a target file without changing it (a plain `git mv`) gives
|
|
@@ -6,7 +6,7 @@ setup(
|
|
|
6
6
|
name='blockwatch-pre-commit-placeholder',
|
|
7
7
|
version='0.0.0',
|
|
8
8
|
# <block name="blockwatch-cli-version" same-as-pattern="blockwatch-cli==(?P<value>\d+\.\d+\.\d+)">
|
|
9
|
-
install_requires=['blockwatch-cli==0.
|
|
9
|
+
install_requires=['blockwatch-cli==0.16.0'],
|
|
10
10
|
# </block>
|
|
11
11
|
# Without it, setuptools takes the directories under src/ for Python packages.
|
|
12
12
|
packages=[],
|
|
@@ -13,6 +13,7 @@ use std::collections::{BTreeMap, BTreeSet, HashMap};
|
|
|
13
13
|
use std::fmt;
|
|
14
14
|
use std::ops::Range;
|
|
15
15
|
use std::str::FromStr;
|
|
16
|
+
use strum::IntoEnumIterator;
|
|
16
17
|
use strum_macros::EnumString;
|
|
17
18
|
|
|
18
19
|
const UNNAMED_BLOCK_LABEL: &str = "(unnamed)";
|
|
@@ -496,11 +497,12 @@ const RECOGNIZED_ATTRIBUTES: &[&str] = &[
|
|
|
496
497
|
// </block>
|
|
497
498
|
];
|
|
498
499
|
|
|
499
|
-
/// Checks that each attribute of a block is recognized
|
|
500
|
+
/// Checks that each attribute of a block is recognized, and that each validator accepts the
|
|
501
|
+
/// attributes it reads (see [`Validator::check_attributes`]). It does not check the values: see
|
|
500
502
|
/// [`BlockSeverity::from_attributes`] for `severity`.
|
|
501
503
|
///
|
|
502
504
|
/// # Errors
|
|
503
|
-
/// Returns an error that quotes the attribute
|
|
505
|
+
/// Returns an error that quotes the first attribute found to be wrong, such as
|
|
504
506
|
/// ``unrecognized attribute `keep-sortd` ``.
|
|
505
507
|
pub(crate) fn validate_attributes(attributes: &HashMap<String, String>) -> anyhow::Result<()> {
|
|
506
508
|
if let Some(name) = attributes
|
|
@@ -509,7 +511,39 @@ pub(crate) fn validate_attributes(attributes: &HashMap<String, String>) -> anyho
|
|
|
509
511
|
{
|
|
510
512
|
bail!("unrecognized attribute `{name}`");
|
|
511
513
|
}
|
|
512
|
-
|
|
514
|
+
// A block can be in a file that the run reads only to find a target, and no validator runs on
|
|
515
|
+
// such a block. Every parsed block passes here.
|
|
516
|
+
Validator::iter().try_for_each(|validator| validator.check_attributes(attributes))
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
#[cfg(test)]
|
|
520
|
+
mod validate_attributes_tests {
|
|
521
|
+
use crate::blocks::{RECOGNIZED_ATTRIBUTES, validate_attributes};
|
|
522
|
+
use crate::validators::Validator;
|
|
523
|
+
use std::collections::HashMap;
|
|
524
|
+
use strum::IntoEnumIterator;
|
|
525
|
+
|
|
526
|
+
#[test]
|
|
527
|
+
fn single_attribute_returns_an_error_only_when_it_needs_another() {
|
|
528
|
+
// A new attribute must either work alone or stop the run alone. Otherwise a block with
|
|
529
|
+
// only that attribute would pass without being checked.
|
|
530
|
+
let needs_no_other: Vec<&str> = Validator::iter()
|
|
531
|
+
.map(Validator::as_str)
|
|
532
|
+
.chain([
|
|
533
|
+
"line-pattern-forbidden",
|
|
534
|
+
"name",
|
|
535
|
+
"same-as-paths",
|
|
536
|
+
"severity",
|
|
537
|
+
])
|
|
538
|
+
.collect();
|
|
539
|
+
for name in RECOGNIZED_ATTRIBUTES {
|
|
540
|
+
let attributes = HashMap::from([(name.to_string(), String::new())]);
|
|
541
|
+
|
|
542
|
+
let result = validate_attributes(&attributes);
|
|
543
|
+
|
|
544
|
+
assert_eq!(result.is_ok(), needs_no_other.contains(name), "{name}");
|
|
545
|
+
}
|
|
546
|
+
}
|
|
513
547
|
}
|
|
514
548
|
|
|
515
549
|
#[cfg(test)]
|
|
@@ -272,7 +272,9 @@ fn is_entry_modified(
|
|
|
272
272
|
/// - the target's file is a glob that is not valid or that matches no file;
|
|
273
273
|
/// - `target-pattern` comes with a symbol, or is not a valid regex;
|
|
274
274
|
/// - an attribute is not one a tag can have, or has a value that is not a string, `true` or an
|
|
275
|
-
/// integer
|
|
275
|
+
/// integer;
|
|
276
|
+
/// - an attribute comes without the attribute its validator needs, such as `check-lua-timeout`
|
|
277
|
+
/// without `check-lua`.
|
|
276
278
|
fn entry_blocks(
|
|
277
279
|
block_entry: BlockEntry,
|
|
278
280
|
entry: ConfigEntry,
|
|
@@ -433,6 +435,7 @@ mod tests {
|
|
|
433
435
|
target = 'package.json#/version'
|
|
434
436
|
name = 'version'
|
|
435
437
|
keep-unique = true
|
|
438
|
+
check-lua = 'check.lua'
|
|
436
439
|
check-lua-timeout = 30
|
|
437
440
|
|
|
438
441
|
[[block]]
|
|
@@ -450,6 +453,7 @@ target = 'a.yaml#/b'
|
|
|
450
453
|
attributes: HashMap::from([
|
|
451
454
|
("name".to_string(), "version".to_string()),
|
|
452
455
|
("keep-unique".to_string(), String::new()),
|
|
456
|
+
("check-lua".to_string(), "check.lua".to_string()),
|
|
453
457
|
("check-lua-timeout".to_string(), "30".to_string()),
|
|
454
458
|
]),
|
|
455
459
|
entry: ConfigEntry {
|
|
@@ -465,7 +469,7 @@ target = 'a.yaml#/b'
|
|
|
465
469
|
attributes: HashMap::new(),
|
|
466
470
|
entry: ConfigEntry {
|
|
467
471
|
file: PathBuf::from(DEFAULT_FILE),
|
|
468
|
-
line:
|
|
472
|
+
line: 10,
|
|
469
473
|
},
|
|
470
474
|
is_entry_modified: false,
|
|
471
475
|
severity: BlockSeverity::Error,
|
|
@@ -263,7 +263,11 @@ impl PathFilter {
|
|
|
263
263
|
fn glob_set(globs: impl IntoIterator<Item = impl AsRef<str>>) -> GlobSet {
|
|
264
264
|
let mut builder = GlobSet::builder();
|
|
265
265
|
for glob in globs {
|
|
266
|
-
builder.add(
|
|
266
|
+
builder.add(
|
|
267
|
+
crate::paths_glob::glob_builder(glob.as_ref())
|
|
268
|
+
.build()
|
|
269
|
+
.expect("malformed test glob"),
|
|
270
|
+
);
|
|
267
271
|
}
|
|
268
272
|
builder.build().expect("failed to build test glob set")
|
|
269
273
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
//! The paths and globs given on the command line, and the check that each selects a file.
|
|
2
2
|
|
|
3
3
|
use crate::fs::{Entry, FileSystem};
|
|
4
|
+
use crate::paths_glob::glob_builder;
|
|
4
5
|
use crate::repo_path::RepoPath;
|
|
5
6
|
use anyhow::{Context, bail};
|
|
6
|
-
use globset::{
|
|
7
|
+
use globset::{GlobSet, GlobSetBuilder};
|
|
7
8
|
use std::collections::BTreeSet;
|
|
8
9
|
use std::path::Path;
|
|
9
10
|
|
|
@@ -42,7 +43,9 @@ impl PathArguments {
|
|
|
42
43
|
None => argument.trim_start_matches("./").to_string(),
|
|
43
44
|
};
|
|
44
45
|
builder.add(
|
|
45
|
-
|
|
46
|
+
glob_builder(&glob)
|
|
47
|
+
.build()
|
|
48
|
+
.with_context(|| format!("invalid glob pattern: {argument}"))?,
|
|
46
49
|
);
|
|
47
50
|
}
|
|
48
51
|
Ok(Self {
|
|
@@ -55,7 +58,7 @@ impl PathArguments {
|
|
|
55
58
|
/// no arguments, because then every file is checked.
|
|
56
59
|
pub(crate) fn allowed_globs(&self) -> anyhow::Result<GlobSet> {
|
|
57
60
|
if self.written.is_empty() {
|
|
58
|
-
return Ok(GlobSet::new([
|
|
61
|
+
return Ok(GlobSet::new([glob_builder("**").build()?])?);
|
|
59
62
|
}
|
|
60
63
|
Ok(self.globs.clone())
|
|
61
64
|
}
|
|
@@ -180,6 +183,15 @@ mod tests {
|
|
|
180
183
|
Ok(())
|
|
181
184
|
}
|
|
182
185
|
|
|
186
|
+
#[test]
|
|
187
|
+
fn glob_argument_with_empty_alternative_selects_the_path_without_it() -> anyhow::Result<()> {
|
|
188
|
+
assert_eq!(
|
|
189
|
+
selected_files(&["a.py", "sub/a.py", "other/a.py"], &["{,sub/}a.py"])?,
|
|
190
|
+
["a.py", "sub/a.py"]
|
|
191
|
+
);
|
|
192
|
+
Ok(())
|
|
193
|
+
}
|
|
194
|
+
|
|
183
195
|
#[test]
|
|
184
196
|
fn error_in_files_ensure_each_matches_returns_the_error() -> anyhow::Result<()> {
|
|
185
197
|
let error = resolve_globs(&["src/**"])?
|
|
@@ -9,6 +9,20 @@ use std::collections::BTreeSet;
|
|
|
9
9
|
/// path it spells.
|
|
10
10
|
pub(crate) const GLOB_CHARACTERS: [char; 5] = ['*', '?', '[', '{', '\\'];
|
|
11
11
|
|
|
12
|
+
/// Starts building `glob`, so that an empty alternative in braces matches the empty string:
|
|
13
|
+
/// `{,sub/}a.yml` matches both `a.yml` and `sub/a.yml`.
|
|
14
|
+
pub(crate) fn glob_builder(glob: &str) -> GlobBuilder<'_> {
|
|
15
|
+
// globset drops an empty alternative by default, so `{,sub/}a.yml` would match only
|
|
16
|
+
// `sub/a.yml`. Shells and most other glob tools match both.
|
|
17
|
+
#[expect(
|
|
18
|
+
clippy::disallowed_methods,
|
|
19
|
+
reason = "the one place that sets empty_alternates"
|
|
20
|
+
)]
|
|
21
|
+
let mut builder = GlobBuilder::new(glob);
|
|
22
|
+
builder.empty_alternates(true);
|
|
23
|
+
builder
|
|
24
|
+
}
|
|
25
|
+
|
|
12
26
|
/// A glob over the repository's paths, compiled to list the files or the directories it matches.
|
|
13
27
|
pub(crate) struct PathsGlob {
|
|
14
28
|
/// Matches a path written without a trailing `/`.
|
|
@@ -25,7 +39,7 @@ impl PathsGlob {
|
|
|
25
39
|
pub(crate) fn new(glob: &str) -> anyhow::Result<Self> {
|
|
26
40
|
// `*` stops at `/`, so `services/*` matches the services and not the directories inside
|
|
27
41
|
// them.
|
|
28
|
-
let matcher =
|
|
42
|
+
let matcher = glob_builder(glob)
|
|
29
43
|
.literal_separator(true)
|
|
30
44
|
.build()?
|
|
31
45
|
.compile_matcher();
|
|
@@ -93,3 +107,54 @@ fn base_directory(glob: &str) -> Option<RepoPath> {
|
|
|
93
107
|
// the walk. Walking the whole repository gives the same paths, only more slowly.
|
|
94
108
|
RepoPath::from_reference(&fixed_segments.join("/")).ok()
|
|
95
109
|
}
|
|
110
|
+
|
|
111
|
+
#[cfg(test)]
|
|
112
|
+
mod tests {
|
|
113
|
+
use super::*;
|
|
114
|
+
use crate::fs::test_utils::FakeFileSystem;
|
|
115
|
+
|
|
116
|
+
/// The files that `glob` matches in a repository that holds `files`, sorted.
|
|
117
|
+
fn matching_files(glob: &str, files: &[&str]) -> anyhow::Result<Vec<String>> {
|
|
118
|
+
let file_system = FakeFileSystem::new(
|
|
119
|
+
files
|
|
120
|
+
.iter()
|
|
121
|
+
.map(|file| (file.to_string(), String::new()))
|
|
122
|
+
.collect(),
|
|
123
|
+
);
|
|
124
|
+
Ok(PathsGlob::new(glob)?
|
|
125
|
+
.matching_files(&file_system)?
|
|
126
|
+
.iter()
|
|
127
|
+
.map(ToString::to_string)
|
|
128
|
+
.collect())
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
#[test]
|
|
132
|
+
fn empty_first_alternative_matches_the_path_without_it() -> anyhow::Result<()> {
|
|
133
|
+
assert_eq!(
|
|
134
|
+
matching_files(
|
|
135
|
+
"{,sub/}a.yml",
|
|
136
|
+
&["a.yml", "sub/a.yml", "other/a.yml", "b.yml"]
|
|
137
|
+
)?,
|
|
138
|
+
["a.yml", "sub/a.yml"]
|
|
139
|
+
);
|
|
140
|
+
Ok(())
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
#[test]
|
|
144
|
+
fn empty_last_alternative_matches_the_path_without_it() -> anyhow::Result<()> {
|
|
145
|
+
assert_eq!(
|
|
146
|
+
matching_files("a{.yml,}", &["a", "a.yml", "a.json", "ab"])?,
|
|
147
|
+
["a", "a.yml"]
|
|
148
|
+
);
|
|
149
|
+
Ok(())
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
#[test]
|
|
153
|
+
fn empty_nested_alternative_matches_the_path_without_it() -> anyhow::Result<()> {
|
|
154
|
+
assert_eq!(
|
|
155
|
+
matching_files("{x,{,y}}a", &["a", "xa", "ya", "za"])?,
|
|
156
|
+
["a", "xa", "ya"]
|
|
157
|
+
);
|
|
158
|
+
Ok(())
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
//! Project settings: how they are validated, and how the flags and the config file are merged.
|
|
2
2
|
|
|
3
3
|
use crate::blocks::BlockAddress;
|
|
4
|
+
use crate::paths_glob::glob_builder;
|
|
4
5
|
use crate::validators::Validator;
|
|
5
6
|
use anyhow::{Context, bail};
|
|
6
7
|
use globset::{Glob, GlobSet, GlobSetBuilder};
|
|
@@ -36,7 +37,9 @@ impl RawSettings {
|
|
|
36
37
|
.ignore
|
|
37
38
|
.iter()
|
|
38
39
|
.map(|glob| {
|
|
39
|
-
|
|
40
|
+
glob_builder(glob)
|
|
41
|
+
.build()
|
|
42
|
+
.with_context(|| format!("invalid ignore glob pattern: {glob}"))
|
|
40
43
|
})
|
|
41
44
|
.collect::<anyhow::Result<_>>()?;
|
|
42
45
|
let extensions = self
|
|
@@ -286,6 +289,19 @@ mod tests {
|
|
|
286
289
|
Ok(())
|
|
287
290
|
}
|
|
288
291
|
|
|
292
|
+
#[test]
|
|
293
|
+
fn ignore_glob_with_empty_alternative_ignores_the_path_without_it() -> anyhow::Result<()> {
|
|
294
|
+
let config_file = RawSettings {
|
|
295
|
+
ignore: to_string_vec(&["{,sub/}a.rs"]),
|
|
296
|
+
..RawSettings::default()
|
|
297
|
+
};
|
|
298
|
+
let settings = resolve(RawSettings::default(), config_file)?;
|
|
299
|
+
assert!(settings.ignored_globs.is_match("a.rs"));
|
|
300
|
+
assert!(settings.ignored_globs.is_match("sub/a.rs"));
|
|
301
|
+
assert!(!settings.ignored_globs.is_match("other/a.rs"));
|
|
302
|
+
Ok(())
|
|
303
|
+
}
|
|
304
|
+
|
|
289
305
|
#[test]
|
|
290
306
|
fn extensions_in_flags_and_config_file_are_combined() -> anyhow::Result<()> {
|
|
291
307
|
let flags = RawSettings {
|