Skip to content

Commit 0e412cd

Browse files
committed
More plumbing
Entire-Checkpoint: 409696665b2c
1 parent e658292 commit 0e412cd

4 files changed

Lines changed: 197 additions & 5 deletions

File tree

cmd/git-sync/main.go

Lines changed: 147 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ func runSyncLike(ctx context.Context, name string, args []string, dryRun bool, d
5959

6060
fs.StringVar(&req.Source.URL, "source-url", "", "source repository URL")
6161
fs.StringVar(&req.Target.URL, "target-url", "", "target repository URL")
62+
fs.BoolVar(&req.Source.FollowInfoRefsRedirect, "source-follow-info-refs-redirect", envBool("GITSYNC_SOURCE_FOLLOW_INFO_REFS_REDIRECT"), "send follow-up source RPCs to the final /info/refs redirect host")
63+
fs.BoolVar(&req.Target.FollowInfoRefsRedirect, "target-follow-info-refs-redirect", envBool("GITSYNC_TARGET_FOLLOW_INFO_REFS_REDIRECT"), "send follow-up target RPCs to the final /info/refs redirect host")
6264

6365
fs.StringVar(&sourceAuth.Token, "source-token", envOr("GITSYNC_SOURCE_TOKEN", ""), "source token/password")
6466
fs.StringVar(&targetAuth.Token, "target-token", envOr("GITSYNC_TARGET_TOKEN", ""), "target token/password")
@@ -163,6 +165,8 @@ func runBootstrap(ctx context.Context, args []string) error {
163165

164166
fs.StringVar(&req.Source.URL, "source-url", "", "source repository URL")
165167
fs.StringVar(&req.Target.URL, "target-url", "", "target repository URL")
168+
fs.BoolVar(&req.Source.FollowInfoRefsRedirect, "source-follow-info-refs-redirect", envBool("GITSYNC_SOURCE_FOLLOW_INFO_REFS_REDIRECT"), "send follow-up source RPCs to the final /info/refs redirect host")
169+
fs.BoolVar(&req.Target.FollowInfoRefsRedirect, "target-follow-info-refs-redirect", envBool("GITSYNC_TARGET_FOLLOW_INFO_REFS_REDIRECT"), "send follow-up target RPCs to the final /info/refs redirect host")
166170

167171
fs.StringVar(&sourceAuth.Token, "source-token", envOr("GITSYNC_SOURCE_TOKEN", ""), "source token/password")
168172
fs.StringVar(&targetAuth.Token, "target-token", envOr("GITSYNC_TARGET_TOKEN", ""), "target token/password")
@@ -237,9 +241,12 @@ func runProbe(ctx context.Context, args []string) error {
237241
var jsonOutput bool
238242
var sourceAuth gitsync.EndpointAuth
239243
var targetAuth gitsync.EndpointAuth
244+
var targetFollowInfoRefsRedirect bool
240245
req := unstable.ProbeRequest{}
241246
fs.StringVar(&req.Source.URL, "source-url", "", "source repository URL")
242247
targetURL := fs.String("target-url", "", "optional target repository URL")
248+
fs.BoolVar(&req.Source.FollowInfoRefsRedirect, "source-follow-info-refs-redirect", envBool("GITSYNC_SOURCE_FOLLOW_INFO_REFS_REDIRECT"), "send follow-up source RPCs to the final /info/refs redirect host")
249+
fs.BoolVar(&targetFollowInfoRefsRedirect, "target-follow-info-refs-redirect", envBool("GITSYNC_TARGET_FOLLOW_INFO_REFS_REDIRECT"), "send follow-up target RPCs to the final /info/refs redirect host")
243250
fs.StringVar(&sourceAuth.Token, "source-token", envOr("GITSYNC_SOURCE_TOKEN", ""), "source token/password")
244251
fs.StringVar(&targetAuth.Token, "target-token", envOr("GITSYNC_TARGET_TOKEN", ""), "target token/password")
245252
fs.StringVar(&sourceAuth.Username, "source-username", envOr("GITSYNC_SOURCE_USERNAME", "git"), "source basic auth username")
@@ -274,7 +281,10 @@ func runProbe(ctx context.Context, args []string) error {
274281
return usageError("probe requires a source repository URL")
275282
}
276283
if *targetURL != "" {
277-
req.Target = &gitsync.Endpoint{URL: *targetURL}
284+
req.Target = &gitsync.Endpoint{
285+
URL: *targetURL,
286+
FollowInfoRefsRedirect: targetFollowInfoRefsRedirect,
287+
}
278288
}
279289

280290
result, err := unstable.New(unstable.Options{
@@ -298,6 +308,7 @@ func runFetch(ctx context.Context, args []string) error {
298308
req := unstable.FetchRequest{}
299309

300310
fs.StringVar(&req.Source.URL, "source-url", "", "source repository URL")
311+
fs.BoolVar(&req.Source.FollowInfoRefsRedirect, "source-follow-info-refs-redirect", envBool("GITSYNC_SOURCE_FOLLOW_INFO_REFS_REDIRECT"), "send follow-up source RPCs to the final /info/refs redirect host")
301312
fs.StringVar(&sourceAuth.Token, "source-token", envOr("GITSYNC_SOURCE_TOKEN", ""), "source token/password")
302313
fs.StringVar(&sourceAuth.Username, "source-username", envOr("GITSYNC_SOURCE_USERNAME", "git"), "source basic auth username")
303314
fs.StringVar(&sourceAuth.BearerToken, "source-bearer-token", envOr("GITSYNC_SOURCE_BEARER_TOKEN", ""), "source bearer token")
@@ -413,7 +424,141 @@ func envBool(key string) bool {
413424
}
414425

415426
func usageError(message string) error {
416-
usage := fmt.Sprintf("usage:\n git-sync sync [flags] <source-url> <target-url>\n git-sync replicate [flags] <source-url> <target-url>\n git-sync plan [flags] <source-url> <target-url>\n git-sync bootstrap [flags] <source-url> <target-url>\n git-sync probe [flags] <source-url> [target-url]\n git-sync fetch [flags] <source-url>\n\nsync flags:\n --branch main,dev\n --map main:stable\n --tags\n --force\n --prune\n --stats\n --measure-memory\n --json\n --materialized-max-objects %d\n --max-pack-bytes <bytes>\n --target-max-pack-bytes <bytes>\n --protocol auto|v1|v2\n --source-token ...\n --target-token ...\n --source-username git\n --target-username git\n --source-bearer-token ...\n --target-bearer-token ...\n --source-insecure-skip-tls-verify\n --target-insecure-skip-tls-verify\n -v\n\nreplicate flags:\n --branch main,dev\n --map main:stable\n --tags\n --prune\n --stats\n --measure-memory\n --json\n --max-pack-bytes <bytes>\n --target-max-pack-bytes <bytes>\n --protocol auto|v1|v2\n --source-token ...\n --target-token ...\n --source-username git\n --target-username git\n --source-bearer-token ...\n --target-bearer-token ...\n --source-insecure-skip-tls-verify\n --target-insecure-skip-tls-verify\n -v\n\nplan flags:\n --mode sync|replicate\n --branch main,dev\n --map main:stable\n --tags\n --force\n --prune\n --stats\n --measure-memory\n --json\n --max-pack-bytes <bytes>\n --target-max-pack-bytes <bytes>\n --protocol auto|v1|v2\n --source-token ...\n --target-token ...\n --source-username git\n --target-username git\n --source-bearer-token ...\n --target-bearer-token ...\n --source-insecure-skip-tls-verify\n --target-insecure-skip-tls-verify\n -v\n\nbootstrap flags:\n --branch main,dev\n --map main:stable\n --tags\n --max-pack-bytes 104857600\n --target-max-pack-bytes 1073741824\n --stats\n --measure-memory\n --json\n --protocol auto|v1|v2\n --source-token ...\n --target-token ...\n --source-username git\n --target-username git\n --source-bearer-token ...\n --target-bearer-token ...\n --source-insecure-skip-tls-verify\n --target-insecure-skip-tls-verify\n -v\n\nprobe flags:\n --tags\n --stats\n --measure-memory\n --json\n --protocol auto|v1|v2\n --source-token ...\n --source-username git\n --source-bearer-token ...\n --target-token ...\n --target-username git\n --target-bearer-token ...\n --source-insecure-skip-tls-verify\n --target-insecure-skip-tls-verify\n\nfetch flags:\n --branch main,dev\n --tags\n --stats\n --measure-memory\n --json\n --protocol auto|v1|v2\n --have-ref main\n --have <hash>\n --source-token ...\n --source-username git\n --source-bearer-token ...\n --source-insecure-skip-tls-verify\n", unstable.DefaultMaterializedMaxObjects)
427+
usage := fmt.Sprintf(`usage:
428+
git-sync sync [flags] <source-url> <target-url>
429+
git-sync replicate [flags] <source-url> <target-url>
430+
git-sync plan [flags] <source-url> <target-url>
431+
git-sync bootstrap [flags] <source-url> <target-url>
432+
git-sync probe [flags] <source-url> [target-url]
433+
git-sync fetch [flags] <source-url>
434+
435+
sync flags:
436+
--branch main,dev
437+
--map main:stable
438+
--tags
439+
--force
440+
--prune
441+
--stats
442+
--measure-memory
443+
--json
444+
--materialized-max-objects %d
445+
--max-pack-bytes <bytes>
446+
--target-max-pack-bytes <bytes>
447+
--protocol auto|v1|v2
448+
--source-token ...
449+
--target-token ...
450+
--source-username git
451+
--target-username git
452+
--source-bearer-token ...
453+
--target-bearer-token ...
454+
--source-insecure-skip-tls-verify
455+
--target-insecure-skip-tls-verify
456+
--source-follow-info-refs-redirect
457+
--target-follow-info-refs-redirect
458+
-v
459+
460+
replicate flags:
461+
--branch main,dev
462+
--map main:stable
463+
--tags
464+
--prune
465+
--stats
466+
--measure-memory
467+
--json
468+
--max-pack-bytes <bytes>
469+
--target-max-pack-bytes <bytes>
470+
--protocol auto|v1|v2
471+
--source-token ...
472+
--target-token ...
473+
--source-username git
474+
--target-username git
475+
--source-bearer-token ...
476+
--target-bearer-token ...
477+
--source-insecure-skip-tls-verify
478+
--target-insecure-skip-tls-verify
479+
--source-follow-info-refs-redirect
480+
--target-follow-info-refs-redirect
481+
-v
482+
483+
plan flags:
484+
--mode sync|replicate
485+
--branch main,dev
486+
--map main:stable
487+
--tags
488+
--force
489+
--prune
490+
--stats
491+
--measure-memory
492+
--json
493+
--max-pack-bytes <bytes>
494+
--target-max-pack-bytes <bytes>
495+
--protocol auto|v1|v2
496+
--source-token ...
497+
--target-token ...
498+
--source-username git
499+
--target-username git
500+
--source-bearer-token ...
501+
--target-bearer-token ...
502+
--source-insecure-skip-tls-verify
503+
--target-insecure-skip-tls-verify
504+
--source-follow-info-refs-redirect
505+
--target-follow-info-refs-redirect
506+
-v
507+
508+
bootstrap flags:
509+
--branch main,dev
510+
--map main:stable
511+
--tags
512+
--max-pack-bytes 104857600
513+
--target-max-pack-bytes 1073741824
514+
--stats
515+
--measure-memory
516+
--json
517+
--protocol auto|v1|v2
518+
--source-token ...
519+
--target-token ...
520+
--source-username git
521+
--target-username git
522+
--source-bearer-token ...
523+
--target-bearer-token ...
524+
--source-insecure-skip-tls-verify
525+
--target-insecure-skip-tls-verify
526+
--source-follow-info-refs-redirect
527+
--target-follow-info-refs-redirect
528+
-v
529+
530+
probe flags:
531+
--tags
532+
--stats
533+
--measure-memory
534+
--json
535+
--protocol auto|v1|v2
536+
--source-token ...
537+
--source-username git
538+
--source-bearer-token ...
539+
--target-token ...
540+
--target-username git
541+
--target-bearer-token ...
542+
--source-insecure-skip-tls-verify
543+
--target-insecure-skip-tls-verify
544+
--source-follow-info-refs-redirect
545+
--target-follow-info-refs-redirect
546+
547+
fetch flags:
548+
--branch main,dev
549+
--tags
550+
--stats
551+
--measure-memory
552+
--json
553+
--protocol auto|v1|v2
554+
--have-ref main
555+
--have <hash>
556+
--source-token ...
557+
--source-username git
558+
--source-bearer-token ...
559+
--source-insecure-skip-tls-verify
560+
--source-follow-info-refs-redirect
561+
`, unstable.DefaultMaterializedMaxObjects)
417562
if message == "" {
418563
return errors.New(strings.TrimSpace(usage))
419564
}

cmd/git-sync/main_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,47 @@ func TestRun_Replicate_SubcommandRejectsForce(t *testing.T) {
270270
}
271271
}
272272

273+
func TestRun_Fetch_SourceFollowInfoRefsRedirectFlag(t *testing.T) {
274+
sourceRepo, sourceFS := newSourceRepo(t)
275+
makeCommits(t, sourceRepo, sourceFS, 1)
276+
277+
sourceServer := newSmartHTTPRepoServer(t, sourceRepo)
278+
defer sourceServer.Close()
279+
280+
entry := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
281+
switch {
282+
case r.Method == http.MethodGet && r.URL.Path == sourceServer.repoPath+"/info/refs":
283+
http.Redirect(w, r, sourceServer.server.URL+r.URL.Path+"?"+r.URL.RawQuery, http.StatusTemporaryRedirect)
284+
case r.Method == http.MethodPost:
285+
http.Error(w, "entry domain rejects packs", http.StatusMethodNotAllowed)
286+
default:
287+
http.NotFound(w, r)
288+
}
289+
}))
290+
defer entry.Close()
291+
292+
output, err := captureStdout(func() error {
293+
return run(context.Background(), []string{
294+
"fetch",
295+
"--source-follow-info-refs-redirect",
296+
"--branch", testBranch,
297+
"--json",
298+
entry.URL + sourceServer.repoPath,
299+
})
300+
})
301+
if err != nil {
302+
t.Fatalf("run fetch: %v", err)
303+
}
304+
305+
var result map[string]any
306+
if err := json.Unmarshal([]byte(output), &result); err != nil {
307+
t.Fatalf("decode fetch json: %v\noutput=%s", err, output)
308+
}
309+
if got, ok := result["fetchedObjects"].(float64); !ok || got == 0 {
310+
t.Fatalf("expected fetched objects from redirected source, got %#v", result["fetchedObjects"])
311+
}
312+
}
313+
273314
func captureStdout(fn func() error) (string, error) {
274315
old := os.Stdout
275316
r, w, err := os.Pipe()

pkg/gitsync/unstable/client.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,10 @@ func operationModeString(mode gitsync.OperationMode) string {
293293

294294
func syncerEndpoint(endpoint gitsync.Endpoint, auth gitsync.EndpointAuth) syncer.Endpoint {
295295
return internalbridge.ToSyncerEndpoint(
296-
internalbridge.Endpoint{URL: endpoint.URL},
296+
internalbridge.Endpoint{
297+
URL: endpoint.URL,
298+
FollowInfoRefsRedirect: endpoint.FollowInfoRefsRedirect,
299+
},
297300
internalbridge.EndpointAuth{
298301
Username: auth.Username,
299302
Token: auth.Token,

pkg/gitsync/unstable/client_test.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ func TestBuildSyncConfigCarriesAdvancedOptions(t *testing.T) {
1818
Target: gitsync.EndpointAuth{Token: "dst"},
1919
},
2020
}).buildSyncConfig(context.Background(), SyncRequest{
21-
Source: gitsync.Endpoint{URL: "https://source.example/repo.git"},
22-
Target: gitsync.Endpoint{URL: "https://target.example/repo.git"},
21+
Source: gitsync.Endpoint{URL: "https://source.example/repo.git", FollowInfoRefsRedirect: true},
22+
Target: gitsync.Endpoint{URL: "https://target.example/repo.git", FollowInfoRefsRedirect: true},
2323
Scope: gitsync.RefScope{Branches: []string{"main"}},
2424
Policy: gitsync.SyncPolicy{IncludeTags: true, Force: true, Prune: true},
2525
DryRun: true,
@@ -42,6 +42,9 @@ func TestBuildSyncConfigCarriesAdvancedOptions(t *testing.T) {
4242
if cfg.Source.Token != "src" || cfg.Target.Token != "dst" {
4343
t.Fatalf("auth not propagated: %+v %+v", cfg.Source, cfg.Target)
4444
}
45+
if !cfg.Source.FollowInfoRefsRedirect || !cfg.Target.FollowInfoRefsRedirect {
46+
t.Fatalf("follow-info-refs redirect flags not propagated: %+v %+v", cfg.Source, cfg.Target)
47+
}
4548
}
4649

4750
func TestBuildFetchConfigCopiesHaveHashesAtCallSite(t *testing.T) {

0 commit comments

Comments
 (0)