move package to root level · Entire
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
MREADME.md+4/-4
Aclient.go+257
Rclient_test.go
cmd
git-sync-bench
Mmain.go+2/-2
Mmain_test.go+1/-1
git-sync
Mmain.go+2/-2
Mmain_test.go+1/-1
Rdoc.go
docs
Marchitecture.md+6/-6
Membedding.md+8/-8
Aexample_test.go+32
internalbridge
Rconfig.go
Rmodel.go
Rmodel_test.go
pkg/gitsync
Dclient.go-257
Dexample_test.go-32
Dtypes.go-151
unstable
Dclient.go-318
Dclient_test.go-62
Ddoc.go-9
Atypes.go+151
unstable
Aclient.go+318
Aclient_test.go+62
Adoc.go+9
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