Merge pull request #98 from entireio/entire-unmirrored-exclude-exact · Entire
Merge pull request #98 from entireio/entire-unmirrored-exclude-exact
1e91421→main·
nodo·2w ago·8 files·+81 added/-7 removed
refscope: add ExcludeRefs for exact ref-name exclusion
Changes
8
MCHANGELOG.md+4
Mclient.go+1
internal
internalbridge
Mconfig.go+2
planner
Aexclude_test.go+47
Mplanner.go+8/-3
Mtypes.go+10/-2
syncer
Msyncer.go+4/-2
Mtypes.go+5
6 unmodified lines
7
8
9
10
11
12
13
14
15
16
6 unmodified lines
## [Unreleased]
### Added
- `RefScope.ExcludeRefs` — exact ref-name exclusion, alongside the existing prefix-based `ExcludeRefPrefixes`. An excluded exact name is not pulled, pushed, or pruned, but — unlike a prefix — its children are unaffected, so a caller can reserve a directory-anchor ref like `refs/heads/entire` while still mirroring `refs/heads/entire/foo`. Threaded through `RefScope` → planner `PlanConfig`; `IsRefExcluded` now takes both prefix and exact lists.
### Removed
- The built-in Entire DB credential store integration (`hosts.json` active-user lookup, the file/keyring token store, and OAuth refresh-token handling). `auth.Resolve` now resolves only explicit token/bearer credentials; everything else defers to the git credential helper on a 401, exactly as for any other remote. The Entire mirroring pipeline and the `git-remote-entire` helper already supply credentials directly (installation / repo-scoped tokens at the transport layer), so nothing produced the `hosts.json`/token-store layout this code read. This drops the `github.com/zalando/go-keyring` dependency and, with the file token store gone, the package now compiles on Windows without a `flock` shim.
MCHANGELOG.md+4
216 unmodified lines
217
218
219
220
221
222
223
216 unmodified lines
Mappings: mappings,
AllRefs: scope.AllRefs,
ExcludeRefPrefixes: append([]string(nil), scope.ExcludeRefPrefixes...),
ExcludeRefs: append([]string(nil), scope.ExcludeRefs...),
}
}
Mclient.go+1
43 unmodified lines
44
45
46
47
48
49
50
31 unmodified lines
82
83
84
85
86
87
88
43 unmodified lines
Mappings []RefMapping
AllRefs bool
ExcludeRefPrefixes []string
ExcludeRefs []string
}
type SyncPolicy struct {
31 unmodified lines
Mappings: ToValidationMappings(scope.Mappings),
AllRefs: scope.AllRefs,
ExcludeRefPrefixes: append([]string(nil), scope.ExcludeRefPrefixes...),
ExcludeRefs: append([]string(nil), scope.ExcludeRefs...),
IncludeTags: policy.IncludeTags,
DryRun: dryRun,
ShowStats: collectStats,
Minternal/internalbridge/config.go+2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package planner
import (
"testing"
"github.com/go-git/go-git/v6/plumbing"
)
func TestIsRefExcluded(t *testing.T) {
t.Parallel()
cases := []struct {
name string
ref string
prefixes []string
exact []string
want bool
}{
{"no filters", "refs/heads/main", nil, nil, false},
// Prefix matching (unchanged behavior).
{"prefix under", "refs/pull/1/head", []string{"refs/pull/"}, nil, true},
{"prefix boundary sibling", "refs/pullx", []string{"refs/pull/"}, nil, false},
{"prefix blank entry skipped", "refs/heads/main", []string{" "}, nil, false},
// Exact matching: matches the whole name only, never children — so a
// caller can reserve refs/heads/entire while still mirroring
// refs/heads/entire/foo.
{"exact hit", "refs/heads/entire", nil, []string{"refs/heads/entire"}, true},
{"exact does not match child", "refs/heads/entire/foo", nil, []string{"refs/heads/entire"}, false},
{"exact does not match sibling", "refs/heads/entirely", nil, []string{"refs/heads/entire"}, false},
{"exact blank entry skipped", "refs/heads/main", nil, []string{" \\nfalse},
// Combined: prefix and exact together.
{"prefix wins", "refs/heads/entire/unmirrored/x", []string{"refs/heads/entire/unmirrored/"}, []string{"refs/heads/entire"}, true},
{"exact wins", "refs/heads/entire", []string{"refs/heads/entire/unmirrored/"}, []string{"refs/heads/entire"}, true},
{"neither", "refs/heads/entire/foo", []string{"refs/heads/entire/unmirrored/"}, []string{"refs/heads/entire"}, false},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
t.Parallel()
got := IsRefExcluded(plumbing.ReferenceName(c.ref), c.prefixes, c.exact)
if got != c.want {
t.Errorf("IsRefExcluded(%q, %v, %v) = %v, want %v", c.ref, c.prefixes, c.exact, got, c.want)
}
})
}
}
Ainternal/planner/exclude_test.go+47
27 unmodified lines
28
29
30
31
32
33
34
35
36
37
38
44 unmodified lines
83
84
85
81
86
87
88
89
15 unmodified lines
105
106
107
103
108
109
110
111
145 unmodified lines
257
258
259
255
260
261
262
263
27 unmodified lines
// whose name starts with any of these prefixes is not pulled, pushed,
// or pruned. Explicit Mappings are not subject to this filter.
ExcludeRefPrefixes []string
// ExcludeRefs subtracts exact ref names (whole-name match) from
// auto-discovery, same effect as ExcludeRefPrefixes but without matching
// children — so a caller can reserve refs/heads/entire while still
// mirroring refs/heads/entire/foo. Explicit Mappings are not subject to it.
ExcludeRefs []string
}
// BuildDesiredRefs constructs the set of desired refs and managed targets from
44 unmodified lines
selected := SelectBranches(branches, cfg.Branches)
for branch, hash := range selected {
refName := plumbing.NewBranchReferenceName(branch)
if IsRefExcluded(refName, cfg.ExcludeRefPrefixes) {
if IsRefExcluded(refName, cfg.ExcludeRefPrefixes, cfg.ExcludeRefs) {
continue
}
if err := addManaged(refName, refName, RefKindBranch, hash); err != nil {
15 unmodified lines
default:
continue
}
if IsRefExcluded(refName, cfg.ExcludeRefPrefixes) {
if IsRefExcluded(refName, cfg.ExcludeRefPrefixes, cfg.ExcludeRefs) {
continue
}
if _, ok := desired[refName]; ok {
continue
}
}
}
Minternal/planner/planner.go+8/-3
104 unmodified lines
105
106
107
108
109
108
109
110
111
112
6 unmodified lines
119
120
121
122
123
124
125
126
127
128
129
130
131
132
104 unmodified lines
// IsRefExcluded reports whether name matches any of the exclude prefixes.
// Empty prefixes are ignored. Used to subtract specific namespaces from
// auto-discovery (e.g. refs/pull/* under --all-refs against GitHub).
func IsRefExcluded(name plumbing.ReferenceName, excludePrefixes []string) bool {
if len(excludePrefixes) == 0 {
func IsRefExcluded(name plumbing.ReferenceName, excludePrefixes, excludeExact []string) bool {
if len(excludePrefixes) == 0 && len(excludeExact) == 0 {
return false
}
s := name.String()
6 unmodified lines
return true
}
}
// Exact names match the whole ref, so a caller can reserve
// refs/heads/entire without also excluding refs/heads/entire/foo — which
a prefix cannot express (it would also catch refs/heads/entirely).
for _, e := range excludeExact {
if e = strings.TrimSpace(e); e != "" && s == e {
return true
}
}
return false
}
Minternal/planner/types.go+10/-2
71 unmodified lines
72
73
74
75
76
77
78
440 unmodified lines
519
520
521
522
523
524
525
520 unmodified lines
1046
1047
1048
1047
1049
1050
1051
1052
215 unmodified lines
1268
1269
1270
1269
1271
1272
1273
1274
71 unmodified lines
Mappings []RefMapping
AllRefs bool
ExcludeRefPrefixes []string
ExcludeRefs []string
IncludeTags bool
DryRun bool
Verbose bool
440 unmodified lines
IncludeTags: cfg.IncludeTags,
AllRefs: cfg.AllRefs,
ExcludeRefPrefixes: cfg.ExcludeRefPrefixes,
ExcludeRefs: cfg.ExcludeRefs,
Force: cfg.ForceAny(),
Prune: cfg.Prune,
}
520 unmodified lines
if _, ok := desiredRefs[targetRef]; ok {
continue
}
if planner.IsRefExcluded(targetRef, s.cfg.ExcludeRefPrefixes) {
if planner.IsRefExcluded(targetRef, s.cfg.ExcludeRefPrefixes, s.cfg.ExcludeRefs) {
continue
}
// AllRefs overrides per-namespace allowlists: under "all refs" a
215 unmodified lines
func (s *syncSession) newProbeResult() ProbeResult {
refInfos := make([]RefInfo, 0, len(s.sourceRefMap))
for name, hash := range s.sourceRefMap {
if planner.IsRefExcluded(name, s.cfg.ExcludeRefPrefixes) {
if planner.IsRefExcluded(name, s.cfg.ExcludeRefPrefixes, s.cfg.ExcludeRefs) {
continue
}
refInfos = append(refInfos, RefInfo{Name: name.String(), Hash: hash})