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

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})