Repository navigation
Conversation
Each package and `.codeowner` becomes a `<directory>/**/**` glob and
each annotated file its own path. `validate` matches files against
them, and they're written to CODEOWNERS, where
`for-file --from-codeowners`, `validate <files>` and code_ownership's
`for_file` match them again. The package mappers escaped nothing and
the `.codeowner` and annotation mappers escaped only `[` and `]`, so
`packs/[ab]` claimed `packs/a`, `{legacy}` meant `legacy`, and in
`validate` a top-level `!legacy` was a negated glob that gave its team
every file outside `legacy`. A package or `.codeowner` at the project
root got `/**/**`, which matches no project-relative path, and the
CODEOWNERS line `//**/**`.
One escaper now puts a backslash before `\`, `*`, `?`, `[`, `]`, `{`,
`}` and `!` in every glob built from a path, team files and vendored
gems included. It leaves letters alone, since fast_glob reads `\b`,
`\n`, `\r` and `\t` as control characters. The project root's glob is
`**/**`, which CODEOWNERS gets as `/**/**`, and `for-file` describes a
package by the glob `validate` matches.
With the root matching, a file in `app/` matches both a root
`.codeowner` and `app/.codeowner`, and `validate` kept whichever came
first from the parallel walk, since `Source::len` counted separators
and gave both 0. It now counts components, which ranks the root below
every directory. The root's `.codeowner` is shown as `.codeowner`
rather than `/.codeowner`, and a `.codeowner` naming an invalid team
below it is reported as inheriting its owner from the project root
rather than from an empty name. When `validate` drops nested packages,
a root package encloses every other one, as for any nested package; a
test now pins that.
Repos with such names, or with a `.codeowner` or package at the root,
get a different CODEOWNERS, and `validate` reports it out of date until
they regenerate it. A root `.codeowner` or package now also owns files
in `validate` and CODEOWNERS, as `for-file` already reported, so a file
that another mechanism gives a different team is reported as having
more than one owner.
Fixes #146.
`validate <files>` excuses an optionally owned file only if nothing names an owner for it. Its owner lookup matched the directory of a `.codeowner` naming a team that doesn't exist by path prefix, because the directory mapper's glob missed one at the project root and read braces as alternatives. Since the previous commit that glob matches just the directory, and the mapper builds a matcher for every `.codeowner` whatever team it names, so the lookup now uses those matchers. An empty `.codeowner`, whose owner is "", still names an owner there, and a test now pins that. A package naming a team that doesn't exist is still matched by path prefix, since the package mappers leave it out. Refs #146.
`validate` counts each distinct team name its matchers report for a file as an owner. The team glob mapper reported a team's GitHub team, and the `.codeowner` mapper the name as written, which can also be the GitHub team, while the other mappers report the team's name. So a file that one team reached two such ways got two owners, though `for-file` and CODEOWNERS give it one. On main that happens wherever a team's owned globs cover a file the team also owns another way, as for `frontend/apps/public/index.tsx` in the javascript_only_project fixture, or where a `.codeowner` names its team by GitHub team and another mapper names it by name. Now that the root's glob matches every file, a repo that passes today would also fail wherever a root `.codeowner` or package meets an owned glob of the same team, or a root `.codeowner` naming the team by GitHub team meets an annotation or package naming it by name. Both mappers now report the team's name, and keep a name that matches no team as written. A multiple-owner report names a team glob's team by name too. Refs #146.
Escaping only brackets in a team file's path, as the `.codeowner` and
annotation mappers did before, passed every test, though the CODEOWNERS
lookup would then read the line for `config/teams/{dev}.yml` as
`config/teams/dev.yml`. Nor did any test cover how `for-file` describes
a `package.json` at the project root, the JavaScript side of
`nearest_package_owner`. Add both cases.
Refs #146.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Globs built from package,
.codeowner, annotated-file, team-file and vendored-gem paths now escape glob metacharacters. A package or.codeownerat the project root now gets**/**, written to CODEOWNERS as/**/**. With the issue's project,for-file,for-file --from-codeownersandvalidatenow agree on all eight files, andcrosscheck-ownerspasses.Fixes #146.
Why
These globs are matched by
validateand written to CODEOWNERS, wherefor-file --from-codeowners,validate <files>and the gem'sCodeOwnership.for_filematch them again withfast_glob. The names went in unescaped:packs/[ab]claimedpacks/a.{legacy}meantlegacy.validate, a top-level!legacywas a negated glob that gave its team every file outsidelegacy./**/**(CODEOWNERS//**/**), which matches nothing.Changes
escaper::escape_globreplacesescape_brackets. It puts a\before\ * ? [ ] { } !, one character at a time. Letters are never escaped, sincefast_globreads\b,\n,\r,\tas control characters.escaper::directory_globgives<escaped dir>/**/**, or**/**for the root.Where they're used:
Source::Package).codeownermapperfor-fileshowsUser-written
owned_globsare untouched.Fixes at the root, now that a root
.codeownermatches:Source::lencounts components, so the root ranks belowapp/.codeowner. Before, they tied, andvalidatepicked whichever the parallel walk found first.`.codeowner`rather than`/.codeowner`..codeownerunder it says it inherits "from the project root" rather than from an empty name.validate, as any outer package does.OwnerLookup(from Add optionally_owned_globs to permit files with no owner #135) no longer special-cases.codeownerfiles naming unknown teams with a path-prefix match. The directory mapper's own matchers are now correct, so it uses them. This doesn't change behavior. An empty.codeownerstill counts as naming an owner, and a new test pins that.A team now counts once in
validate, whichever way it's named (separate commit).validategroups a file's owners by the team name each matcher reports. The team-glob mapper reported the GitHub team, and the.codeownermapper the text as written, while every other mapper reportsteam.name. So one team reaching a file two ways counted as two owners.This already happens on main:
frontend/apps/public/index.tsxin thejavascript_only_projectfixture is reported with owners@DesignTeamandDesign. With the root fixed, it would also have failed any repo with a root.codeowneror package and an owned glob of the same team. Both mappers now reportteam.name, and keep an unknown name as written. Happy to split this into its own PR if you'd rather.Release notes
.codeowneror package at the root, get a different CODEOWNERS.validatereports it out of date until they rungenerate. Escaped names also sort differently (/app/\{old\}now comes before/app/old)..codeowneror package now owns files invalidateand CODEOWNERS, asfor-filealready reported. So a file that another mechanism gives a different team is reported as having more than one owner. With a root package, that includes annotations or.codeownerfiles naming a nested package's own team, sincevalidatefolds nested packages into the outer one.validateno longer reports one team reached two ways as two owners, and multiple-owner reports name a team glob's team by its name.for-file, invalidatemessages and in the gem'sfor_filereasons:.codeownershows as`.codeowner`**/**How GitHub reads
\-escaped characters and a/**/**line isn't verified here.\[has been emitted since #37. codeowners-rs and the gem match these lines correctly.Test plan
cargo fmt --all -- --check,cargo clippy --all-targets --all-features -- -D warningsandcargo testpass (281 passed, the 3 existing ignores).tests/path_globs_test.rs(11 tests, temp repos via thetests/commonhelpers):for-fileand--from-codeownersfor all eight files, exactvalidateandvalidate <files>output, andcrosscheck-ownerssuccess!legacy.codeowner, and a rootpackage.yml.codeowneroverridden byapp/.codeowner!,*and the rootnearest_package_owner's display glob, Ruby and JS, including a rootpackage.jsonSource::len, the display fixes and the team-name keying fails at least one test.for-fileoutput are byte-identical to main's for every fixture undertests/fixtures.validatediffers only where the same-team false positives are gone.Follow-ups (not in this PR)
.codeowner, such a file now falls through to the root's team in--from-codeowners.load_teamsandParser::absolute_team_files_globsjoin the unescaped root (pre-existing).package.json(now including a root one) overrides nestedpackage.ymllines in CODEOWNERS, whilefor-filepicks the nearest package (pre-existing for non-root nesting;validatereports it).tests/path_globs_test.rshas its owncodeownershelper. Normalize the paths validate, gv and for-file are given #148 moves the equivalent one intotests/common, so whichever lands second can share it.