Repository navigation
Resolve groups to repository selectors (GH-20) - #58
Merged
Merged
Conversation
internal/config/resolve.go turns the groups section into selectors rather than repository lists: enumeration needs the network and lands with #33, and keeping this package offline is what makes composition, cycles, and glob precedence testable with plain stdlib tests. - Exactly one source per group; zero or two is ErrAmbiguousGroupSource - include_groups flattened as a deduplicated union, cycles reported as the chain that has to be broken (ErrCyclicGroup) - include then exclude, via fnmatch over the repository name only - The user split — authenticated user or not — is carried on the selector, because internal/github cannot re-derive it, and asking for a private visibility of somebody else's repositories warns rather than silently selecting nothing - Resolution.Desired implements the resolution rule, including the case the tool's safety rests on: no group resolves to R, so R wants nothing
Architecture: what a selector is and why it is not a repository list, the one-source rule, diamond versus cycle, filter precedence, the user split, and the resolution rule. Usage: the same rules from the config file's side, including the one that matters most — a repository no group selects is never touched.
The rebase onto main landed validate.go's group checks alongside
resolve.go's, and the two copies had already drifted: the cycle chain was
printed with " → " in one and " -> " in the other. Since Validate runs
first, users only ever saw the arrow the docs did not promise.
resolve.go now owns all four rules about the group graph — one source per
group, the groups include_groups names, no cycle, and owner/repo
references — because resolution cannot produce a selector without
establishing each of them anyway. validateGroups delegates to
Resolve(""), so there is one implementation and one message per rule.
ParseRepoRef becomes the single judge of a reference, so a repos: entry
and a --repo are held to the same rule. It gained the character check
validate.go had: whitespace around either half is still trimmed, a space
or colon inside one is now ErrInvalidRepoRef.
A new test drives the four messages through Validate and pins them by
name, since that is the call a user's failure comes from, and says that a
change to it is a change to both docs pages.
Ilyes512
force-pushed
the
feat/GH-20-resolve-groups-to-selectors
branch
from
August 9, 2026 16:46
6a39a14 to
d71bc08
Compare
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.
Closes #20.
Summary
internal/config/resolve.goturns thegroupssection into selectors, not repository lists — enumeration needs the network and lands with repos.go: repository enumeration, pagination and filtering #33, and staying offline is what makes composition, cycles, and glob precedence testable without an HTTP mock.ErrAmbiguousGroupSource.include_groupsflattens into a deduplicated union, and a cycle is reported as the chain to break (a -> b -> c -> a,ErrCyclicGroup).includethenexcludeviafnmatch, over the repository name only. Theusersplit rides on the selector asAuthenticatedUser, andvisibility: privatefor anybody else warns instead of silently selecting nothing.Resolution.Desiredis the resolution rule, including the case the tool's safety rests on: no group resolves to R, so R wants nothing — which is not the same answer as an empty config.github.com/danwakefield/fnmatch, for the glob matching the design'sgroupsreference specifies.Rebased on main, and what that turned up
This branch was rebased onto
mainafter #60 (validate.go) and #59 (palette) merged. The docs conflicts were consolidations — both sections kept,main's Validation section alongside this branch's Resolution section — but the rebase also put two implementations of the same four rules into one package, and they had already drifted:validate.go(as merged)resolve.go(this branch)a → b → aa -> b -> astrings.Join(sources, " and ")describeSources(...)Validateruns first, so the only message a user could ever see was the one neither docs page documented.validate.go:165also carried a comment saying it walked the graph "so thatresolve.gocan expand a group without a visited set of its own" — which this branch'sresolve.gohad made untrue.So the last commit gives the four group-graph rules one owner:
validateGroupsis now_, err := c.Resolve(""); return err. Resolution has to establish all four before it can build a selector — a group with two sources has no one source to resolve, a cycle cannot be flattened — so a copy invalidate.gowas a second implementation of a rule that already existed.validateGroupSource,validateNoGroupCycle,repoRefPattern, and the newly-unusedsortedKeysare gone;validate.gois ~100 lines shorter.Resolveis empty because none of the four rules depends on it, and the resolution is discarded — which also keeps thevisibility: privatewarning a property of the run rather than something validation swallows.ParseRepoRefwas weaker than the pattern it replaces — it acceptedspecsnl/label sync, whichvalidate.gorejected and has a test for. Delegating naively would have silently dropped a real rule, soParseRepoRefpicked up the character check and is now the single judge of a reference, for arepos:entry and a--repoalike. Whitespace around either half is still trimmed (specsnl / labelsyncparses); a space or colon inside one is nowErrInvalidRepoRef..gitis caught was wrong and is corrected: a dot is in GitHub's character set, sodocs.example.comis an ordinary name and.gitcannot be told apart from one. There is now a test recording that it is taken literally.TestValidateReportsTheResolveMessagepins all four messages throughValidate, because that is the call a user's failure comes from, and its doc comment says that changing it is a change tousage/_index.mdandarchitecture/configuration.mdtoo.Scope
Every box on #20 is ticked. Judgement calls the issue did not settle, all documented in the code:
FNM_CASEFOLD), because GitHub repository names are —owner/Repoandowner/repoare one repository.reposselector carries no filters at all.include/exclude/skip_*/visibilityapply toorganduserper the design's reference table; a filter that dropped a repository the config named outright would be a surprise rather than a safety net.resolve.gorather thanvalidate.go, per the section above. The rule table inarchitecture/configuration.mdgained a "Checked in" column so the split is on the page rather than something to rediscover.Docs
architecture/configuration.md— the Resolution section (selectors, one source and composition, filters, theusersplit, the resolution rule), plus the rule table's "Checked in" column and a section on why the group rules have one owner.usage/_index.md— the same rules from the config file's side, including the one that matters most: a repository no group selects is never touched.Checks
task checkallis green, as is the CI sequence run locally:go mod tidy -diff,golangci-lint run(0 issues),go test -race -tags=integration ./..., and markdownlint (0 issues).