Move stable gitsync model behind bridge · Entire
Move stable gitsync model behind bridge
ccf984d→main·
Sessions
1f9417321410View transcript
Changes
4
pkg/gitsync
- Mclient.go+3/-3
Mclient_test.go+3/-2
internalbridge
- Amodel.go+207
Mtypes.go+22/-210
37 unmodified lines
38
39
40
41
41
42
43
44
9 unmodified lines
54
55
56
57
57
58
59
60
9 unmodified lines
70
71
72
73
73
74
75
76
37 unmodified lines
if err != nil {
return ProbeResult{}, err
}
return fromSyncerProbeResult(result), nil
return internalbridge.FromProbeResult(result), nil
}
// Plan computes ref actions without pushing.
9 unmodified lines
if err != nil {
return PlanResult{}, err
}
return fromSyncerResult(result), nil
return internalbridge.FromSyncResult(result), nil
}
// Sync executes a sync between two remotes.
9 unmodified lines
if err != nil {
return SyncResult{}, err
}
return fromSyncerResult(result), nil
return internalbridge.FromSyncResult(result), nil
}
func (c *Client) buildProbeConfig(ctx context.Context, req ProbeRequest) (internalbridge.Config, error) {
Mpkg/gitsync/client.go+3/-3
9 unmodified lines
10
11
12
13
14
15
16
71 unmodified lines
88
89
90
90
91
92
93
94
9 unmodified lines
104
105
106
106
107
108
109
110
9 unmodified lines
"github.com/soph/git-sync/internal/planner"
"github.com/soph/git-sync/internal/syncer"
"github.com/soph/git-sync/pkg/gitsync/internalbridge"
func TestBuildSyncConfigUsesDefaultProtocolAndMaterializedLimit(t *testing.T) {
71 unmodified lines
}
func TestFromSyncerResultZeroHashesAreEmptyStrings(t *testing.T) {
got := hashString(plumbing.ZeroHash)
got := internalbridge.HashString(plumbing.ZeroHash)
if got != "" {
t.Fatalf("hashString(zero) = %q, want empty string", got)
}
9 unmodified lines
}
func TestFromSyncerResultShapesStableSummary(t *testing.T) {
got := fromSyncerResult(syncer.Result{
got := internalbridge.FromSyncResult(syncer.Result{
Plans: []planner.BranchPlan{
{
Branch: "main",
Mpkg/gitsync/client_test.go+3/-2
package internalbridge
import (
"context"
"github.com/go-git/go-git/v6/plumbing"
"github.com/soph/git-sync/internal/planner"
"github.com/soph/git-sync/internal/syncer"
"github.com/soph/git-sync/internal/validation"
"github.com/soph/git-sync/pkg/gitsync/internalbridge"
)
// ProtocolMode controls source-side protocol negotiation.
// RefKind distinguishes branch refs from tag refs.
type RefKind string
const (
RefKindBranch RefKind = RefKind(planner.RefKindBranch)
RefKindTag RefKind = RefKind(planner.RefKindTag)
)
type Action string
const (
ActionCreate Action = Action(planner.ActionCreate)
ActionUpdate Action = Action(planner.ActionUpdate)
ActionDelete Action = Action(planner.ActionDelete)
ActionSkip Action = Action(planner.ActionSkip)
ActionBlock Action = Action(planner.ActionBlock)
)
// RefPlan describes the outcome for a single ref.
type RefResult struct {
Branch string `json:"branch"`
SourceRef string `json:"source_ref"`
TargetRef string `json:"target_ref"`
SourceHash string `json:"source_hash"`
TargetHash string `json:"target_hash"`
Kind RefKind `json:"kind"`
Action Action `json:"action"`
Reason string `json:"reason"`
}
// RefPlan is kept as an alias for early adopters while the stable result
// surface converges on the more explicit RefResult name.
type RefPlan = RefResult
// RefInfo identifies a named ref.
type RefInfo struct {
Name string `json:"name"`
Hash string `json:"hash"`
}
// ServiceStats tracks transfer statistics for a single service.
type ServiceStats struct {
Name string `json:"name"`
Requests int `json:"requests"`
RequestBytes int64 `json:"request_bytes"`
ResponseBytes int64 `json:"response_bytes"`
Wants int `json:"wants"`
Haves int `json:"haves"`
Commands int `json:"commands"`
}
// Stats summarizes transfer metrics.
type Stats struct {
Enabled bool `json:"enabled"`
Items map[string]*ServiceStats `json:"items"`
}
// Measurement summarizes elapsed time and Go heap usage.
type Measurement struct {
Enabled bool `json:"enabled"`
ElapsedMillis int64 `json:"elapsed_millis"`
PeakAllocBytes uint64 `json:"peak_alloc_bytes"`
PeakHeapInuseBytes uint64 `json:"peak_heap_inuse_bytes"`
TotalAllocBytes uint64 `json:"total_alloc_bytes"`
GCCount uint32 `json:"gc_count"`
}
// ProbeResult holds structured probe output suitable for workers.
type ProbeResult struct {
SourceURL string `json:"source_url"`
TargetURL string `json:"target_url,omitempty"`
RequestedMode string `json:"requested_mode"`
Protocol string `json:"protocol"`
RefPrefixes []string `json:"ref_prefixes"`
Capabilities []string `json:"source_capabilities"`
TargetCaps []string `json:"target_capabilities,omitempty"`
Refs []RefInfo `json:"refs"`
Stats Stats `json:"stats"`
Measurement Measurement `json:"measurement"`
}
// SyncCounts summarizes per-run ref outcomes.
type SyncCounts struct {
Applied int `json:"applied"`
Skipped int `json:"skipped"`
Blocked int `json:"blocked"`
Deleted int `json:"deleted"`
}
// BatchSummary describes batching behavior when it was used.
type BatchSummary struct {
Enabled bool `json:"enabled"`
Planned int `json:"planned"`
Done int `json:"done"`
}
// ExecutionSummary describes how a sync was carried out.
type ExecutionSummary struct {
DryRun bool `json:"dry_run"`
Protocol string `json:"protocol"`
Relay bool `json:"relay"`
Mode string `json:"mode"`
Reason string `json:"reason"`
BootstrapSuggested bool `json:"bootstrap_suggested"`
Batch BatchSummary `json:"batch"`
}
// SyncResult holds structured sync output suitable for workers.
type SyncResult struct {
Refs []RefResult `json:"refs"`
Counts SyncCounts `json:"counts"`
Execution ExecutionSummary `json:"execution"`
Stats Stats `json:"stats"`
Measurement Measurement `json:"measurement"`
}
// PlanResult is the dry-run form of SyncResult.
type PlanResult = SyncResult
func fromSyncerProbeResult(result syncer.ProbeResult) ProbeResult {
out := ProbeResult{
SourceURL: result.SourceURL,
TargetURL: result.TargetURL,
RequestedMode: result.RequestedMode,
Protocol: result.Protocol,
RefPrefixes: append([]string(nil), result.RefPrefixes...),
Capabilities: append([]string(nil), result.Capabilities...),
TargetCaps: append([]string(nil), result.TargetCaps...),
Refs: make([]RefInfo, 0, len(result.Refs)),
Stats: fromSyncerStats(result.Stats),
Measurement: fromSyncerMeasurement(result.Measurement),
}
for _, ref := range result.Refs {
out.Refs = append(out.Refs, RefInfo{Name: ref.Name, Hash: ref.Hash.String()})
}
return out
}
func fromSyncerResult(result syncer.Result) SyncResult {
out := SyncResult{
Refs: make([]RefResult, 0, len(result.Plans)),
Counts: SyncCounts{
Applied: result.Pushed,
Skipped: result.Skipped,
Blocked: result.Blocked,
Deleted: result.Deleted,
},
Execution: ExecutionSummary{
DryRun: result.DryRun,
Protocol: result.Protocol,
Relay: result.Relay,
Mode: result.RelayMode,
Reason: result.RelayReason,
BootstrapSuggested: result.BootstrapSuggested,
Batch: BatchSummary{
Enabled: result.Batching,
Planned: result.PlannedBatchCount,
Done: result.BatchCount,
},
},
Stats: fromSyncerStats(result.Stats),
Measurement: fromSyncerMeasurement(result.Measurement),
}
for _, plan := range result.Plans {
out.Refs = append(out.Refs, RefResult{
Branch: plan.Branch,
SourceRef: plan.SourceRef.String(),
TargetRef: plan.TargetRef.String(),
SourceHash: hashString(plan.SourceHash),
TargetHash: hashString(plan.TargetHash),
Kind: RefKind(plan.Kind),
Action: Action(plan.Action),
Reason: plan.Reason,
})
}
return out
}
func fromSyncerStats(stats syncer.Stats) Stats {
out := Stats{Enabled: stats.Enabled, Items: make(map[string]*ServiceStats, len(stats.Items))}
for key, item := range stats.Items {
copyItem := *item
out.Items[key] = &ServiceStats{
Name: copyItem.Name,
Requests: copyItem.Requests,
RequestBytes: copyItem.RequestBytes,
ResponseBytes: copyItem.ResponseBytes,
Wants: copyItem.Wants,
Haves: copyItem.Haves,
Commands: copyItem.Commands,
}
}
return out
}
func fromSyncerMeasurement(m syncer.Measurement) Measurement {
return Measurement{
Enabled: m.Enabled,
ElapsedMillis: m.ElapsedMillis,
PeakAllocBytes: m.PeakAllocBytes,
PeakHeapInuseBytes: m.PeakHeapInuseBytes,
TotalAllocBytes: m.TotalAllocBytes,
GCCount: m.GCCount,
}
}
func hashString(hash plumbing.Hash) string {
if hash.IsZero() {
return ""
}
return hash.String()
}