move package to root level · Entire

Home

Log in

move package to root level

36f1ca9→main·

Soph·2mo ago·24 files·+853 added/-853 removed

Sessions

Transcript data is unavailable for this checkpoint.

Changes

24

64 unmodified lines

65
66
67
68
68
69
70
71
72
72
73
74
75
76
76
77
78
78
79
80
81

64 unmodified lines

`git-sync` now has a two-tier Go API:

- `pkg/gitsync`
- `gitsync`
  - stable embedding surface for queue workers and other external callers
  - typed `Probe`, `Plan`, `Sync`, and `Replicate` requests/results
  - injected auth and HTTP client support
- `pkg/gitsync/unstable`
- `unstable`
  - explicitly non-stable surface for first-party tooling and advanced controls
  - includes `Bootstrap`, `Fetch`, batching and measurement knobs, and CLI-oriented execution options

If you are embedding `git-sync` outside this repo, prefer `pkg/gitsync`. The CLI and benchmark command use `pkg/gitsync/unstable` because they still need direct access to advanced engine controls that are intentionally not part of the stable API.
If you are embedding `git-sync` outside this repo, prefer `gitsync`. The CLI and benchmark command use `unstable` because they still need direct access to advanced engine controls that are intentionally not part of the stable API.

The stable `pkg/gitsync` results are shaped for workers:
The stable `gitsync` results are shaped for workers:

- `Refs`
  - per-ref outcomes

MREADME.md+4/-4

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257

package gitsync

import (
    "context"
    "errors"
    "fmt"
    "net/http"

"entire.io/entire/git-sync/internal/validation"
    "entire.io/entire/git-sync/internalbridge"
)

// Options configures a Client. It is intentionally small in the first public cut.
type Options struct {
    HTTPClient *http.Client
    Auth       AuthProvider
}

// Client provides the public orchestration API for git-sync.
type Client struct {
    httpClient *http.Client
    auth       AuthProvider
}

// New constructs a new Client.
func New(opts Options) *Client {
    return &Client{httpClient: opts.HTTPClient, auth: opts.Auth}
}

// Probe inspects a source remote and optional target remote.
func (c *Client) Probe(ctx context.Context, req ProbeRequest) (ProbeResult, error) {
    if err := req.Validate(); err != nil {
        return ProbeResult{}, err
    }
    cfg, err := c.buildProbeConfig(ctx, req)
    if err != nil {
        return ProbeResult{}, err
    }
    result, err := internalbridge.Probe(ctx, cfg)
    if err != nil {
        return ProbeResult{}, fmt.Errorf("probe: %w", err)
    }
    return internalbridge.FromProbeResult(result), nil
}

// Plan computes ref actions without pushing.
func (c *Client) Plan(ctx context.Context, req PlanRequest) (PlanResult, error) {
    if err := req.Validate(); err != nil {
        return PlanResult{}, err
    }
    cfg, err := c.buildSyncConfig(ctx, req.Source, req.Target, req.Scope, req.Policy, req.CollectStats, true)
    if err != nil {
        return PlanResult{}, err
    }
    result, err := internalbridge.Run(ctx, cfg)
    if err != nil {
        return PlanResult{}, fmt.Errorf("plan: %w", err)
    }
    return internalbridge.FromSyncResult(result), nil
}

// Sync executes a sync between two remotes.
func (c *Client) Sync(ctx context.Context, req SyncRequest) (SyncResult, error) {
    if err := req.Validate(); err != nil {
        return SyncResult{}, err
    }
    cfg, err := c.buildSyncConfig(ctx, req.Source, req.Target, req.Scope, req.Policy, req.CollectStats, false)
    if err != nil {
        return SyncResult{}, err
    }
    result, err := internalbridge.Run(ctx, cfg)
    if err != nil {
        return SyncResult{}, fmt.Errorf("sync: %w", err)
    }
    return internalbridge.FromSyncResult(result), nil
}

// Replicate executes source-authoritative relay-only replication between two remotes.
func (c *Client) Replicate(ctx context.Context, req SyncRequest) (SyncResult, error) {
    req.Policy.Mode = ModeReplicate
    return c.Sync(ctx, req)
}

func (c *Client) buildProbeConfig(ctx context.Context, req ProbeRequest) (internalbridge.Config, error) {
    sourceAuth, err := c.authFor(ctx, req.Source, SourceRole)
    if err != nil {
        return internalbridge.Config{}, err
    }
    var target *internalbridge.Endpoint
    targetAuth := internalbridge.EndpointAuth{}
    if req.Target != nil {
        resolvedTargetAuth, err := c.authFor(ctx, *req.Target, TargetRole)
        if err != nil {
            return internalbridge.Config{}, err
        }
        target = ptr(bridgeEndpoint(*req.Target))
        targetAuth = bridgeEndpointAuth(resolvedTargetAuth)
    }
    return internalbridge.ProbeConfig(
        bridgeEndpoint(req.Source),
        bridgeEndpointAuth(sourceAuth),
        target,
        targetAuth,
        internalbridge.ProtocolMode(req.Protocol),
        req.IncludeTags,
        req.CollectStats,
        c.httpClient,
    ), nil
}

func (c *Client) buildSyncConfig(ctx context.Context, source Endpoint, target Endpoint, scope RefScope, policy SyncPolicy, collectStats, dryRun bool) (internalbridge.Config, error) {
    sourceAuth, err := c.authFor(ctx, source, SourceRole)
    if err != nil {
        return internalbridge.Config{}, err
    }
    targetAuth, err := c.authFor(ctx, target, TargetRole)
    if err != nil {
        return internalbridge.Config{}, err
    }
    return internalbridge.SyncConfig(
        bridgeEndpoint(source),
        bridgeEndpointAuth(sourceAuth),
        bridgeEndpoint(target),
        bridgeEndpointAuth(targetAuth),
        bridgeScope(scope),
        bridgePolicy(policy),
        collectStats,
        dryRun,
        c.httpClient,
    ), nil
}

func (c *Client) authFor(ctx context.Context, endpoint Endpoint, role EndpointRole) (EndpointAuth, error) {
    if c.auth == nil {
        return EndpointAuth{}, nil
    }
    auth, err := c.auth.AuthFor(ctx, endpoint, role)
    if err != nil {
        return EndpointAuth{}, fmt.Errorf("resolve auth for %s: %w", role, err)
    }
    return auth, nil
}

func (r SyncRequest) Validate() error {
    if r.Source.URL == "" {
        return errors.New("source URL is required")
    }
    if r.Target.URL == "" {
        return errors.New("target URL is required")
    }
    if err := validateOperationMode(r.Policy.Mode); err != nil {
        return err
    }
    if _, err := validation.NormalizeProtocolMode(string(r.Policy.Protocol)); err != nil {
        return fmt.Errorf("normalize protocol: %w", err)
    }
    if _, err := validation.ValidateMappings(validationMappings(r.Scope.Mappings)); err != nil {
        return fmt.Errorf("validate mappings: %w", err)
    }
    return nil
}

func (r PlanRequest) Validate() error {
    if r.Source.URL == "" {
        return errors.New("source URL is required")
    }
    if r.Target.URL == "" {
        return errors.New("target URL is required")
    }
    if err := validateOperationMode(r.Policy.Mode); err != nil {
        return err
    }
    if _, err := validation.NormalizeProtocolMode(string(r.Policy.Protocol)); err != nil {
        return fmt.Errorf("normalize protocol: %w", err)
    }
    if _, err := validation.ValidateMappings(validationMappings(r.Scope.Mappings)); err != nil {
        return fmt.Errorf("validate mappings: %w", err)
    }
    return nil
}

func (r ProbeRequest) Validate() error {
    if r.Source.URL == "" {
        return errors.New("source URL is required")
    }
    if r.Target != nil && r.Target.URL == "" {
        return errors.New("target URL is required when target endpoint is provided")
    }
    if _, err := validation.NormalizeProtocolMode(string(r.Protocol)); err != nil {
        return fmt.Errorf("normalize protocol: %w", err)
    }
    return nil
}

func bridgeEndpoint(ep Endpoint) internalbridge.Endpoint {
    return internalbridge.Endpoint{
        URL:                    ep.URL,
        FollowInfoRefsRedirect: ep.FollowInfoRefsRedirect,
    }
}

func bridgeEndpointAuth(auth EndpointAuth) internalbridge.EndpointAuth {
    return internalbridge.EndpointAuth{
        Username:      auth.Username,
        Token:         auth.Token,
        BearerToken:   auth.BearerToken,
        SkipTLSVerify: auth.SkipTLSVerify,
    }
}

func bridgeScope(scope RefScope) internalbridge.RefScope {
    mappings := make([]internalbridge.RefMapping, 0, len(scope.Mappings))
    for _, mapping := range scope.Mappings {
        mappings = append(mappings, internalbridge.RefMapping{
            Source: mapping.Source,
            Target: mapping.Target,
        })
    }
    return internalbridge.RefScope{
        Branches: append([]string(nil), scope.Branches...),
        Mappings: mappings,
    }
}

func bridgePolicy(policy SyncPolicy) internalbridge.SyncPolicy {
    return internalbridge.SyncPolicy{
        Mode:        internalbridge.OperationMode(policy.Mode),
        IncludeTags: policy.IncludeTags,
        Force:       policy.Force,
        Prune:       policy.Prune,
        Protocol:    internalbridge.ProtocolMode(policy.Protocol),
    }
}

func validateOperationMode(mode OperationMode) error {
    switch mode {
    case "", ModeSync, ModeReplicate:
        return nil
    default:
        return fmt.Errorf("unsupported operation mode %q", mode)
    }
}

func ptr[T any](v T) *T {
    return &v
}

func validationMappings(mappings []RefMapping) []validation.RefMapping {
    out := make([]validation.RefMapping, 0, len(mappings))
    for _, mapping := range mappings {
        out = append(out, validation.RefMapping{
            Source: mapping.Source,
            Target: mapping.Target,
        })
    }
    return out
}

Aclient.go+257

1

No patch available.

Rclient_test.go

15 unmodified lines

16
17
18
19
20
19
20
21
22
23

15 unmodified lines

git "github.com/go-git/go-git/v6"

"entire.io/entire/git-sync/internal/validation"
    "entire.io/entire/git-sync/pkg/gitsync"
    "entire.io/entire/git-sync/pkg/gitsync/unstable"
    "entire.io/entire/git-sync"
    "entire.io/entire/git-sync/unstable"
)

type scenario string

Mcmd/git-sync-bench/main.go+2/-2

2 unmodified lines

3
4
5
6
6
7
8
9

2 unmodified lines

import (
    "testing"

"entire.io/entire/git-sync/pkg/gitsync/unstable"
    "entire.io/entire/git-sync/unstable"
)

func TestSummarizeRuns(t *testing.T) {

Mcmd/git-sync-bench/main_test.go+1/-1

9 unmodified lines

10
11
12
13
14
13
14
15
16
17

9 unmodified lines

"strings"

"entire.io/entire/git-sync/internal/validation"
    "entire.io/entire/git-sync/pkg/gitsync"
    "entire.io/entire/git-sync/pkg/gitsync/unstable"
    "entire.io/entire/git-sync"
    "entire.io/entire/git-sync/unstable"
    "github.com/go-git/go-git/v6/plumbing"
)

Mcmd/git-sync/main.go+2/-2

13 unmodified lines

14
15
16
17
17
18
19
20

13 unmodified lines

"testing"
    "time"

"entire.io/entire/git-sync/pkg/gitsync/unstable"
    "entire.io/entire/git-sync/unstable"
    billy "github.com/go-git/go-billy/v6"
    "github.com/go-git/go-billy/v6/memfs"
    git "github.com/go-git/go-git/v6"

Mcmd/git-sync/main_test.go+1/-1

1

No patch available.

Rdoc.go

89 unmodified lines

90
91
92
93
93
94
95
96
97
97
98
99
100
19 unmodified lines

120
121
122
123
123
124
125
125
126
127
128
7 unmodified lines

136
137
138
139
140
139
140
141
142
143

89 unmodified lines

## Package Model

- `pkg/gitsync`
- `gitsync`
  - stable public embedding API
  - typed `Probe`, `Plan`, `Sync`, and `Replicate` requests/results
  - auth and HTTP client injection for worker-style callers
- `pkg/gitsync/unstable`
- `unstable`
  - explicitly non-stable first-party tooling surface
  - advanced controls, `Bootstrap`, `Fetch`, and CLI-oriented knobs
- `internal/gitproto`
19 unmodified lines

The project now separates embedding concerns from first-party tooling concerns:

- `pkg/gitsync` is the stable library boundary.
- `gitsync` is the stable library boundary.
  Callers express orchestration intent through typed probe, plan, sync, and replicate requests. Auth and transport are injected. Execution strategy remains internal.
- `pkg/gitsync/unstable` is the escape hatch for advanced controls.
- `unstable` is the escape hatch for advanced controls.
  It exists so the CLI and benchmark tool can use batching limits, memory measurement, verbose progress, bootstrap, and fetch without widening the stable API prematurely.

The stable result contract is also intentionally worker-oriented:
7 unmodified lines

That split is intentional:

- external embedders should depend on `pkg/gitsync`
- first-party tools inside this repo may use `pkg/gitsync/unstable`
- external embedders should depend on `gitsync`
- first-party tools inside this repo may use `unstable`
- strategy selection, batching heuristics, and materialized fallback controls are not yet treated as stable product contracts

## Protocol Boundaries

Mdocs/architecture.md+6/-6

3 unmodified lines

4
5
6
7
8
7
8
9
10
11
12
12
13
14
15
2 unmodified lines

18
19
20
21
21
22
23
24
2 unmodified lines

27
28
29
30
30
31
32
33
15 unmodified lines

49
50
51
52
52
53
54
55
27 unmodified lines

83
84
85
86
86
87
88
89
71 unmodified lines

161
162
163
164
164

3 unmodified lines

For most embedders, there are two important rules:

- use `pkg/gitsync`
- avoid depending on `pkg/gitsync/unstable` unless you are acting like first-party tooling
- use `gitsync`
- avoid depending on `unstable` unless you are acting like first-party tooling

## Stable vs Unstable

Use `pkg/gitsync` when you want a durable worker-facing API:
Use `gitsync` when you want a durable worker-facing API:

- `Probe`
- `Plan`
2 unmodified lines

- typed requests and results
- injected auth and HTTP client support

Use `pkg/gitsync/unstable` only when you need controls that are intentionally not yet stable:
Use `unstable` only when you need controls that are intentionally not yet stable:

- `Bootstrap`
- `Fetch`
2 unmodified lines

- verbose execution controls
- other engine-adjacent tuning

The CLI and benchmark command use `pkg/gitsync/unstable` because they still need those controls. External workers should generally not.
The CLI and benchmark command use `unstable` because they still need those controls. External workers should generally not.

## Worker Shape

15 unmodified lines

"context"
    "net/http"

"entire.io/entire/git-sync/pkg/gitsync"
    "entire.io/entire/git-sync"
)

func runSync(ctx context.Context) error {
27 unmodified lines

## Auth Injection

`pkg/gitsync` uses one auth ownership model:
`gitsync` uses one auth ownership model:

- requests carry endpoint identity
- `AuthProvider` resolves source and target auth
71 unmodified lines

- temp refs
- exact relay strategy names beyond coarse execution summary

Those are implementation details or advanced controls that currently belong in `pkg/gitsync/unstable`, not the stable embedding contract.
Those are implementation details or advanced controls that currently belong in `unstable`, not the stable embedding contract.

Mdocs/embedding.md+8/-8

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

package gitsync_test

import (
    "context"
    "net/http"

"entire.io/entire/git-sync"
)

func ExampleClient_Sync() {
    client := gitsync.New(gitsync.Options{
        HTTPClient: &http.Client{},
        Auth: gitsync.StaticAuthProvider{
            Source: gitsync.EndpointAuth{Token: "source-token"},
            Target: gitsync.EndpointAuth{Token: "target-token"},
        },
    })

if _, err := client.Sync(context.Background(), gitsync.SyncRequest{
        Source: gitsync.Endpoint{URL: "https://github.example/source/repo.git"},
        Target: gitsync.Endpoint{URL: "https://git.example/target/repo.git"},
        Scope:  gitsync.RefScope{Branches: []string{"main"}},
        Policy: gitsync.SyncPolicy{
            IncludeTags: true,
            Protocol:    gitsync.ProtocolAuto,
        },
    }); err != nil {
        return // network error expected in example environment
    }

// Output:
}

Aexample_test.go+32

1

No patch available.

Rinternalbridge/config.go

1

No patch available.

Rinternalbridge/model.go

1

No patch available.

Rinternalbridge/model_test.go

package gitsync

import ( "context" "errors" "fmt" "net/http"

"entire.io/entire/git-sync/internal/validation" "entire.io/entire/git-sync/pkg/gitsync/internalbridge" )

// Options configures a Client. It is intentionally small in the first public cut. type Options struct { HTTPClient *http.Client Auth AuthProvider }

// Client provides the public orchestration API for git-sync. type Client struct { httpClient *http.Client auth AuthProvider }

// New constructs a new Client. func New(opts Options) *Client { return &Client{httpClient: opts.HTTPClient, auth: opts.Auth} }

func bridgeEndpoint(ep Endpoint) internalbridge.Endpoint { return internalbridge.Endpoint{ URL: ep.URL, FollowInfoRefsRedirect: ep.FollowInfoRefsRedirect, } }

func ptr[T any](v T) *T { return &v }

Dpkg/gitsync/client.go-257

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

package gitsync_test

import (
    "context"
    "net/http"

"entire.io/entire/git-sync/pkg/gitsync"
)

// Output:
}

Dpkg/gitsync/example_test.go-32

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151

package gitsync

import (
    "context"
    "entire.io/entire/git-sync/pkg/gitsync/internalbridge"
)

// ProtocolMode controls source-side protocol negotiation.
type ProtocolMode string

const (
    ProtocolAuto ProtocolMode = "auto"
    ProtocolV1   ProtocolMode = "v1"
    ProtocolV2   ProtocolMode = "v2"
)

// OperationMode controls high-level sync semantics.
type OperationMode string

const (
    ModeSync      OperationMode = "sync"
    ModeReplicate OperationMode = "replicate"
)

// Endpoint identifies a remote Git endpoint.
type Endpoint struct {
    URL string `json:"url"`

// FollowInfoRefsRedirect, when true, rewrites this endpoint's
    // effective host to the final URL returned by /info/refs after
    // HTTP redirects. Subsequent git RPCs (git-upload-pack,
    // git-receive-pack) then target the redirected host directly.
    // Matches vanilla git's smart-HTTP behaviour for discovery-aware
    // servers that 307 /info/refs to a hosting replica.
    FollowInfoRefsRedirect bool `json:"followInfoRefsRedirect,omitempty"`
}

// EndpointAuth carries explicit per-request auth and TLS settings.
// It is resolved through an AuthProvider rather than embedded in Endpoint so
// endpoint identity does not also become the public auth-precedence boundary.
type EndpointAuth struct {
    Username      string `json:"username"`
    Token         string `json:"token"`
    BearerToken   string `json:"bearerToken"`
    SkipTLSVerify bool   `json:"skipTlsVerify"`
}

// EndpointRole identifies whether auth is being resolved for the source or target.
type EndpointRole string

const (
    SourceRole EndpointRole = "source"
    TargetRole EndpointRole = "target"
)

// AuthProvider resolves auth for a request endpoint.
type AuthProvider interface {
    AuthFor(ctx context.Context, endpoint Endpoint, role EndpointRole) (EndpointAuth, error)
}

// StaticAuthProvider returns fixed source and target auth values.
type StaticAuthProvider struct {
    Source EndpointAuth `json:"source"`
    Target EndpointAuth `json:"target"`
}

// AuthFor implements AuthProvider.
func (p StaticAuthProvider) AuthFor(_ context.Context, _ Endpoint, role EndpointRole) (EndpointAuth, error) { //nolint:unparam // implements AuthProvider interface
    if role == TargetRole {
        return p.Target, nil
    }
    return p.Source, nil
}

// RefMapping is an explicit source-to-target ref mapping.
type RefMapping struct {
    Source string `json:"source"`
    Target string `json:"target"`
}

// RefScope constrains which refs a request manages.
type RefScope struct {
    Branches []string     `json:"branches"`
    Mappings []RefMapping `json:"mappings"`
}

// SyncPolicy controls high-level sync behavior.
type SyncPolicy struct {
    Mode        OperationMode `json:"mode"`
    IncludeTags bool          `json:"includeTags"`
    Force       bool          `json:"force"`
    Prune       bool          `json:"prune"`
    Protocol    ProtocolMode  `json:"protocol"`
}

// ProbeRequest inspects source refs and optional target capabilities.
type ProbeRequest struct {
    Source       Endpoint     `json:"source"`
    Target       *Endpoint    `json:"target"`
    IncludeTags  bool         `json:"includeTags"`
    Protocol     ProtocolMode `json:"protocol"`
    CollectStats bool         `json:"collectStats"`
}

// PlanRequest computes ref actions without pushing.
type PlanRequest struct {
    Source       Endpoint   `json:"source"`
    Target       Endpoint   `json:"target"`
    Scope        RefScope   `json:"scope"`
    Policy       SyncPolicy `json:"policy"`
    CollectStats bool       `json:"collectStats"`
}

// SyncRequest executes a sync between two remotes.
type SyncRequest struct {
    Source       Endpoint   `json:"source"`
    Target       Endpoint   `json:"target"`
    Scope        RefScope   `json:"scope"`
    Policy       SyncPolicy `json:"policy"`
    CollectStats bool       `json:"collectStats"`
}

type RefKind = internalbridge.RefKind

const (
    RefKindBranch RefKind = internalbridge.RefKindBranch
    RefKindTag    RefKind = internalbridge.RefKindTag
)

type Action = internalbridge.Action

const (
    ActionCreate Action = internalbridge.ActionCreate
    ActionUpdate Action = internalbridge.ActionUpdate
    ActionDelete Action = internalbridge.ActionDelete
    ActionSkip   Action = internalbridge.ActionSkip
    ActionBlock  Action = internalbridge.ActionBlock
)

type RefResult = internalbridge.RefResult
type RefPlan = internalbridge.RefPlan
type RefInfo = internalbridge.RefInfo
type ServiceStats = internalbridge.ServiceStats
type Stats = internalbridge.Stats
type Measurement = internalbridge.Measurement
type ProbeResult = internalbridge.ProbeResult
type SyncCounts = internalbridge.SyncCounts
type BatchSummary = internalbridge.BatchSummary
type ExecutionSummary = internalbridge.ExecutionSummary
type SyncResult = internalbridge.SyncResult
type PlanResult = internalbridge.PlanResult

Dpkg/gitsync/types.go-151

package unstable

import ( "context" "fmt" "net/http"

"github.com/go-git/go-git/v6/plumbing"

"entire.io/entire/git-sync/internal/syncer" "entire.io/entire/git-sync/internal/validation" "entire.io/entire/git-sync/pkg/gitsync" "entire.io/entire/git-sync/pkg/gitsync/internalbridge" )

const DefaultMaterializedMaxObjects = syncer.DefaultMaterializedMaxObjects

type ( Result = syncer.Result ProbeResult = syncer.ProbeResult FetchResult = syncer.FetchResult RefInfo = syncer.RefInfo Stats = syncer.Stats Measurement = syncer.Measurement )

type Options struct { HTTPClient *http.Client Auth gitsync.AuthProvider }

type Client struct { httpClient *http.Client auth gitsync.AuthProvider }

type AdvancedOptions struct { CollectStats bool json:"collectStats" MeasureMemory bool json:"measureMemory" Verbose bool json:"verbose" MaxPackBytes int64 json:"maxPackBytes" TargetMaxPackBytes int64 json:"targetMaxPackBytes" MaterializedMaxObjects int json:"materializedMaxObjects" }

type ProbeRequest struct { Source gitsync.Endpoint Target *gitsync.Endpoint IncludeTags bool Protocol gitsync.ProtocolMode Options AdvancedOptions }

type SyncRequest struct { Source gitsync.Endpoint Target gitsync.Endpoint Scope gitsync.RefScope Policy gitsync.SyncPolicy DryRun bool Options AdvancedOptions }

type BootstrapRequest struct { Source gitsync.Endpoint Target gitsync.Endpoint Scope gitsync.RefScope IncludeTags bool Protocol gitsync.ProtocolMode Options AdvancedOptions }

type FetchRequest struct { Source gitsync.Endpoint Scope gitsync.RefScope IncludeTags bool Protocol gitsync.ProtocolMode HaveRefs []string HaveHashes []plumbing.Hash Options AdvancedOptions }

func New(opts Options) *Client { return &Client{httpClient: opts.HTTPClient, auth: opts.Auth} }

func (c *Client) Probe(ctx context.Context, req ProbeRequest) (ProbeResult, error) { cfg, err := c.buildProbeConfig(ctx, req) if err != nil { return ProbeResult{}, err } result, err := syncer.Probe(ctx, cfg) if err != nil { return ProbeResult{}, fmt.Errorf("probe: %w", err) } return result, nil }

func (c *Client) Plan(ctx context.Context, req SyncRequest) (Result, error) { planReq := req planReq.DryRun = true cfg, err := c.buildSyncConfig(ctx, planReq) if err != nil { return Result{}, err } result, err := syncer.Run(ctx, cfg) if err != nil { return Result{}, fmt.Errorf("plan: %w", err) } return result, nil }

func (c *Client) Sync(ctx context.Context, req SyncRequest) (Result, error) { cfg, err := c.buildSyncConfig(ctx, req) if err != nil { return Result{}, err } result, err := syncer.Run(ctx, cfg) if err != nil { return Result{}, fmt.Errorf("sync: %w", err) } return result, nil }

func (c *Client) Replicate(ctx context.Context, req SyncRequest) (Result, error) { req.Policy.Mode = gitsync.ModeReplicate cfg, err := c.buildSyncConfig(ctx, req) if err != nil { return Result{}, err } result, err := syncer.Run(ctx, cfg) if err != nil { return Result{}, fmt.Errorf("replicate: %w", err) } return result, nil }

func (c *Client) Bootstrap(ctx context.Context, req BootstrapRequest) (Result, error) { cfg, err := c.buildBootstrapConfig(ctx, req) if err != nil { return Result{}, err } result, err := syncer.Bootstrap(ctx, cfg) if err != nil { return Result{}, fmt.Errorf("bootstrap: %w", err) } return result, nil }

func (c *Client) Fetch(ctx context.Context, req FetchRequest) (FetchResult, error) { cfg, err := c.buildFetchConfig(ctx, req) if err != nil { return FetchResult{}, err } result, err := syncer.Fetch(ctx, cfg, append([]string(nil), req.HaveRefs...), append([]plumbing.Hash(nil), req.HaveHashes...)) if err != nil { return FetchResult{}, fmt.Errorf("fetch: %w", err) } return result, nil }

func (c *Client) buildProbeConfig(ctx context.Context, req ProbeRequest) (syncer.Config, error) { source, err := c.resolveEndpoint(ctx, req.Source, gitsync.SourceRole) if err != nil { return syncer.Config{}, err } cfg := syncer.Config{ Source: source, HTTPClient: c.httpClient, IncludeTags: req.IncludeTags, ShowStats: req.Options.CollectStats, MeasureMemory: req.Options.MeasureMemory, ProtocolMode: protocolString(req.Protocol), Verbose: req.Options.Verbose, } if req.Target != nil { target, err := c.resolveEndpoint(ctx, *req.Target, gitsync.TargetRole) if err != nil { return syncer.Config{}, err } cfg.Target = target } return cfg, nil }

func (c *Client) buildSyncConfig(ctx context.Context, req SyncRequest) (syncer.Config, error) { source, err := c.resolveEndpoint(ctx, req.Source, gitsync.SourceRole) if err != nil { return syncer.Config{}, err } target, err := c.resolveEndpoint(ctx, req.Target, gitsync.TargetRole) if err != nil { return syncer.Config{}, err } maxObjects := req.Options.MaterializedMaxObjects if maxObjects == 0 { maxObjects = DefaultMaterializedMaxObjects } return syncer.Config{ Source: source, Target: target, HTTPClient: c.httpClient, Branches: append([]string(nil), req.Scope.Branches...), Mappings: validationMappings(req.Scope.Mappings), IncludeTags: req.Policy.IncludeTags, DryRun: req.DryRun, ShowStats: req.Options.CollectStats, MeasureMemory: req.Options.MeasureMemory, Mode: operationModeString(req.Policy.Mode), Force: req.Policy.Force, Prune: req.Policy.Prune, MaxPackBytes: req.Options.MaxPackBytes, TargetMaxPackBytes: req.Options.TargetMaxPackBytes, MaterializedMaxObjects: maxObjects, ProtocolMode: protocolString(req.Policy.Protocol), Verbose: req.Options.Verbose, }, nil }

func (c *Client) buildBootstrapConfig(ctx context.Context, req BootstrapRequest) (syncer.Config, error) { source, err := c.resolveEndpoint(ctx, req.Source, gitsync.SourceRole) if err != nil { return syncer.Config{}, err } target, err := c.resolveEndpoint(ctx, req.Target, gitsync.TargetRole) if err != nil { return syncer.Config{}, err } return syncer.Config{ Source: source, Target: target, HTTPClient: c.httpClient, Branches: append([]string(nil), req.Scope.Branches...), Mappings: validationMappings(req.Scope.Mappings), IncludeTags: req.IncludeTags, ShowStats: req.Options.CollectStats, MeasureMemory: req.Options.MeasureMemory, MaxPackBytes: req.Options.MaxPackBytes, TargetMaxPackBytes: req.Options.TargetMaxPackBytes, ProtocolMode: protocolString(req.Protocol), Verbose: req.Options.Verbose, }, nil }

func (c *Client) buildFetchConfig(ctx context.Context, req FetchRequest) (syncer.Config, error) { source, err := c.resolveEndpoint(ctx, req.Source, gitsync.SourceRole) if err != nil { return syncer.Config{}, err } return syncer.Config{ Source: source, HTTPClient: c.httpClient, Branches: append([]string(nil), req.Scope.Branches...), IncludeTags: req.IncludeTags, ShowStats: req.Options.CollectStats, MeasureMemory: req.Options.MeasureMemory, ProtocolMode: protocolString(req.Protocol), Verbose: req.Options.Verbose, }, nil }

func (c *Client) authFor(ctx context.Context, endpoint gitsync.Endpoint, role gitsync.EndpointRole) (gitsync.EndpointAuth, error) { if c.auth == nil { return gitsync.EndpointAuth{}, nil } auth, err := c.auth.AuthFor(ctx, endpoint, role) if err != nil { return gitsync.EndpointAuth{}, fmt.Errorf("resolve auth for %s: %w", role, err) } return auth, nil }

func (c *Client) resolveEndpoint(ctx context.Context, endpoint gitsync.Endpoint, role gitsync.EndpointRole) (syncer.Endpoint, error) { auth, err := c.authFor(ctx, endpoint, role) if err != nil { return syncer.Endpoint{}, err } return syncerEndpoint(endpoint, auth), nil }

func protocolString(mode gitsync.ProtocolMode) string { if mode == "" { return string(gitsync.ProtocolAuto) } return string(mode) }

func operationModeString(mode gitsync.OperationMode) string { if mode == "" { return string(gitsync.ModeSync) } return string(mode) }

func syncerEndpoint(endpoint gitsync.Endpoint, auth gitsync.EndpointAuth) syncer.Endpoint { return internalbridge.ToSyncerEndpoint( internalbridge.Endpoint{ URL: endpoint.URL, FollowInfoRefsRedirect: endpoint.FollowInfoRefsRedirect, }, internalbridge.EndpointAuth{ Username: auth.Username, Token: auth.Token, BearerToken: auth.BearerToken, SkipTLSVerify: auth.SkipTLSVerify, }, ) }

func validationMappings(mappings []gitsync.RefMapping) []validation.RefMapping { bridgeMappings := make([]internalbridge.RefMapping, 0, len(mappings)) for _, mapping := range mappings { bridgeMappings = append(bridgeMappings, internalbridge.RefMapping{ Source: mapping.Source, Target: mapping.Target, }) } return internalbridge.ToValidationMappings(bridgeMappings) }


Dpkg/gitsync/unstable/client.go-318

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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62

package unstable

import ( "context" "net/http" "testing"

"github.com/go-git/go-git/v6/plumbing"

"entire.io/entire/git-sync/pkg/gitsync" )

func TestBuildSyncConfigCarriesAdvancedOptions(t *testing.T) { cfg, err := New(Options{ HTTPClient: &http.Client{}, Auth: gitsync.StaticAuthProvider{ Source: gitsync.EndpointAuth{Token: "src"}, Target: gitsync.EndpointAuth{Token: "dst"}, }, }).buildSyncConfig(context.Background(), SyncRequest{ Source: gitsync.Endpoint{URL: "https://source.example/repo.git", FollowInfoRefsRedirect: true}, Target: gitsync.Endpoint{URL: "https://target.example/repo.git", FollowInfoRefsRedirect: true}, Scope: gitsync.RefScope{Branches: []string{"main"}}, Policy: gitsync.SyncPolicy{IncludeTags: true, Force: true, Prune: true}, DryRun: true, Options: AdvancedOptions{ CollectStats: true, MeasureMemory: true, Verbose: true, MaterializedMaxObjects: 123, }, }) if err != nil { t.Fatalf("buildSyncConfig: %v", err) } if !cfg.DryRun || !cfg.ShowStats || !cfg.MeasureMemory || !cfg.Verbose { t.Fatalf("advanced booleans not propagated: %+v", cfg) } if cfg.MaterializedMaxObjects != 123 { t.Fatalf("materialized max objects = %d, want 123", cfg.MaterializedMaxObjects) } if cfg.Source.Token != "src" || cfg.Target.Token != "dst" { t.Fatalf("auth not propagated: %+v %+v", cfg.Source, cfg.Target) } if !cfg.Source.FollowInfoRefsRedirect || !cfg.Target.FollowInfoRefsRedirect { t.Fatalf("follow-info-refs redirect flags not propagated: %+v %+v", cfg.Source, cfg.Target) } }

func TestBuildFetchConfigCopiesHaveHashesAtCallSite(t *testing.T) { req := FetchRequest{ Source: gitsync.Endpoint{URL: "https://source.example/repo.git"}, HaveHashes: []plumbing.Hash{plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")}, } cfg, err := New(Options{}).buildFetchConfig(context.Background(), req) if err != nil { t.Fatalf("buildFetchConfig: %v", err) } if cfg.Source.URL == "" { t.Fatalf("source URL not set") } }


Dpkg/gitsync/unstable/client\_test.go-62

1 2 3 4 5 6 7 8 9

// Package unstable exposes advanced git-sync controls and commands that are // intentionally outside the stable pkg/gitsync surface. // // This package exists for first-party consumers such as the CLI and benchmark // tool that still need direct access to engine-adjacent controls like batch // sizing, heap measurement, verbose progress, and fetch/bootstrap entrypoints. // // The API in this package is explicitly not stable. package unstable


Dpkg/gitsync/unstable/doc.go-9

package gitsync

import (
    "context"
    "entire.io/entire/git-sync/internalbridge"
)

// ProtocolMode controls source-side protocol negotiation.
type ProtocolMode string

const (
    ProtocolAuto ProtocolMode = "auto"
    ProtocolV1   ProtocolMode = "v1"
    ProtocolV2   ProtocolMode = "v2"
)

// OperationMode controls high-level sync semantics.
type OperationMode string

const (
    ModeSync      OperationMode = "sync"
    ModeReplicate OperationMode = "replicate"
)

// Endpoint identifies a remote Git endpoint.
type Endpoint struct {
    URL string `json:"url"`

// EndpointRole identifies whether auth is being resolved for the source or target.
type EndpointRole string

const (
    SourceRole EndpointRole = "source"
    TargetRole EndpointRole = "target"
)

// RefMapping is an explicit source-to-target ref mapping.
type RefMapping struct {
    Source string `json:"source"`
    Target string `json:"target"`
}

// RefScope constrains which refs a request manages.
type RefScope struct {
    Branches []string     `json:"branches"`
    Mappings []RefMapping `json:"mappings"`
}

type RefKind = internalbridge.RefKind

const (
    RefKindBranch RefKind = internalbridge.RefKindBranch
    RefKindTag    RefKind = internalbridge.RefKindTag
)

type Action = internalbridge.Action

Atypes.go+151

package unstable

import (
    "context"
    "fmt"
    "net/http"

"github.com/go-git/go-git/v6/plumbing"

"entire.io/entire/git-sync/internal/syncer"
    "entire.io/entire/git-sync/internal/validation"
    "entire.io/entire/git-sync"
    "entire.io/entire/git-sync/internalbridge"
)

const DefaultMaterializedMaxObjects = syncer.DefaultMaterializedMaxObjects

type Options struct {
    HTTPClient *http.Client
    Auth       gitsync.AuthProvider
}

type Client struct {
    httpClient *http.Client
    auth       gitsync.AuthProvider
}

type ProbeRequest struct {
    Source      gitsync.Endpoint
    Target      *gitsync.Endpoint
    IncludeTags bool
    Protocol    gitsync.ProtocolMode
    Options     AdvancedOptions
}

func New(opts Options) *Client {
    return &Client{httpClient: opts.HTTPClient, auth: opts.Auth}
}

func protocolString(mode gitsync.ProtocolMode) string {
    if mode == "" {
        return string(gitsync.ProtocolAuto)
    }
    return string(mode)
}

func operationModeString(mode gitsync.OperationMode) string {
    if mode == "" {
        return string(gitsync.ModeSync)
    }
    return string(mode)
}

Aunstable/client.go+318

package unstable

import (
    "context"
    "net/http"
    "testing"

"github.com/go-git/go-git/v6/plumbing"

"entire.io/entire/git-sync"
)

Aunstable/client\_test.go+62

1 2 3 4 5 6 7 8 9

// Package unstable exposes advanced git-sync controls and commands that are // intentionally outside the stable gitsync surface. // // This package exists for first-party consumers such as the CLI and benchmark // tool that still need direct access to engine-adjacent controls like batch // sizing, heap measurement, verbose progress, and fetch/bootstrap entrypoints. // // The API in this package is explicitly not stable. package unstable


Aunstable/doc.go+9