Commit a8d8eaa

Nick Faro committed on
Support mirror pushes
commit a8d8eaab976b7eebf9afe635ea5a6a78030088c5 parent 8347704
5 changed files +89−10
Modifiedcmd/dumbforge/main.go +20−1
@@ -178,7 +178,12 @@ func remoteCommand(args []string) error {
178 if fetchURL == "" { 178 if fetchURL == "" {
179 return fmt.Errorf("public URL is required") 179 return fmt.Errorf("public URL is required")
180 } 180 }
181 if err := runGit("remote", "add", "--", name, fetchURL); err != nil { 181 remoteArgs := []string{"remote", "add"}
182 if isMirrorRepository() {
183 remoteArgs = append(remoteArgs, "--mirror=push")
184 }
185 remoteArgs = append(remoteArgs, "--", name, fetchURL)
186 if err := runGit(remoteArgs...); err != nil {
182 return err 187 return err
183 } 188 }
184 if err := runGit("config", "--add", "remote."+name+".pushurl", pushURL); err != nil { 189 if err := runGit("config", "--add", "remote."+name+".pushurl", pushURL); err != nil {
@@ -188,6 +193,20 @@ func remoteCommand(args []string) error {
188 return nil 193 return nil
189 } 194 }
190 195
196 func isMirrorRepository() bool {
197 out, err := gitOutput("config", "--type=bool", "--get-regexp", `^remote\..*\.mirror$`)
198 if err != nil {
199 return false
200 }
201 for _, line := range strings.Split(string(out), "\n") {
202 fields := strings.Fields(line)
203 if len(fields) == 2 && fields[1] == "true" {
204 return true
205 }
206 }
207 return false
208 }
209
191 func runGit(args ...string) error { 210 func runGit(args ...string) error {
192 // #nosec G204 G702 -- arguments are passed directly to Git without a shell. 211 // #nosec G204 G702 -- arguments are passed directly to Git without a shell.
193 cmd := exec.Command("git", args...) 212 cmd := exec.Command("git", args...)
Modifiedinternal/gitutil/git.go +8−0
@@ -50,6 +50,14 @@ func (g Git) RevParse(ctx context.Context, rev string) (string, error) {
50 return strings.TrimSpace(string(out)), nil 50 return strings.TrimSpace(string(out)), nil
51 } 51 }
52 52
53 func (g Git) SymbolicRef(ctx context.Context, name string) (string, error) {
54 out, err := g.Run(ctx, "symbolic-ref", "--quiet", name)
55 if err != nil {
56 return "", err
57 }
58 return strings.TrimSpace(string(out)), nil
59 }
60
53 func (g Git) Peel(ctx context.Context, oid string) (string, error) { 61 func (g Git) Peel(ctx context.Context, oid string) (string, error) {
54 out, err := g.Run(ctx, "rev-parse", "--verify", oid+"^{}") 62 out, err := g.Run(ctx, "rev-parse", "--verify", oid+"^{}")
55 if err != nil { 63 if err != nil {
Modifiedinternal/helper/helper.go +11−7
@@ -69,13 +69,7 @@ func Run(ctx context.Context, rawURL string, stdin io.Reader, stdout, stderr io.
69 for name, ref := range metadata.Refs { 69 for name, ref := range metadata.Refs {
70 advertised[name] = ref.OID 70 advertised[name] = ref.OID
71 } 71 }
72 if metadata.Head != "" { 72 writeRefList(writer, metadata, line == "list")
73 fmt.Fprintf(writer, "@%s HEAD\n", metadata.Head)
74 }
75 for _, name := range sortedRefNames(metadata.Refs) {
76 fmt.Fprintf(writer, "%s %s\n", metadata.Refs[name].OID, name)
77 }
78 fmt.Fprintln(writer)
79 if err := writer.Flush(); err != nil { 73 if err := writer.Flush(); err != nil {
80 return err 74 return err
81 } 75 }
@@ -218,3 +212,13 @@ func sortedRefNames(refs map[string]publish.Ref) []string {
218 } 212 }
219 return result 213 return result
220 } 214 }
215
216 func writeRefList(writer io.Writer, metadata publish.Metadata, includeHead bool) {
217 if includeHead && metadata.Head != "" {
218 fmt.Fprintf(writer, "@%s HEAD\n", metadata.Head)
219 }
220 for _, name := range sortedRefNames(metadata.Refs) {
221 fmt.Fprintf(writer, "%s %s\n", metadata.Refs[name].OID, name)
222 }
223 fmt.Fprintln(writer)
224 }
Modifiedinternal/helper/helper_test.go +38−1
@@ -1,6 +1,12 @@
1 package helper 1 package helper
2 2
3 import "testing" 3 import (
4 "bytes"
5 "strings"
6 "testing"
7
8 "github.com/npfaro/dumbforge/internal/publish"
9 )
4 10
5 func TestParsePushes(t *testing.T) { 11 func TestParsePushes(t *testing.T) {
6 updates, err := parsePushes([]string{ 12 updates, err := parsePushes([]string{
@@ -26,3 +32,34 @@ func TestParsePushesRejectsMissingDestination(t *testing.T) {
26 t.Fatal("expected an error") 32 t.Fatal("expected an error")
27 } 33 }
28 } 34 }
35
36 func TestPushRefListDoesNotAdvertiseSymbolicHead(t *testing.T) {
37 metadata := publish.Metadata{
38 Head: "refs/heads/hUGETracker",
39 Refs: map[string]publish.Ref{
40 "refs/heads/hUGETracker": {OID: "aa259a4936b9b539131abf51cdc8486829d2bab4"},
41 },
42 }
43 var output bytes.Buffer
44 writeRefList(&output, metadata, false)
45 if strings.Contains(output.String(), " HEAD\n") {
46 t.Fatalf("push advertisement included symbolic HEAD:\n%s", output.String())
47 }
48 if !strings.Contains(output.String(), " refs/heads/hUGETracker\n") {
49 t.Fatalf("push advertisement omitted branch:\n%s", output.String())
50 }
51 }
52
53 func TestFetchRefListAdvertisesSymbolicHead(t *testing.T) {
54 metadata := publish.Metadata{
55 Head: "refs/heads/hUGETracker",
56 Refs: map[string]publish.Ref{
57 "refs/heads/hUGETracker": {OID: "aa259a4936b9b539131abf51cdc8486829d2bab4"},
58 },
59 }
60 var output bytes.Buffer
61 writeRefList(&output, metadata, true)
62 if !strings.Contains(output.String(), "@refs/heads/hUGETracker HEAD\n") {
63 t.Fatalf("fetch advertisement omitted symbolic HEAD:\n%s", output.String())
64 }
65 }
Modifiedinternal/publish/publish.go +12−1
@@ -224,7 +224,7 @@ func (p *Publisher) Push(ctx context.Context, updates []Update, expected map[str
224 metadata.Refs[update.Dst] = ref 224 metadata.Refs[update.Dst] = ref
225 } 225 }
226 if metadata.Head == "" { 226 if metadata.Head == "" {
227 metadata.Head = chooseHead(resolved) 227 metadata.Head = p.chooseInitialHead(ctx, resolved)
228 } 228 }
229 result.Head = metadata.Head 229 result.Head = metadata.Head
230 result.Branches = metadata.BranchNames() 230 result.Branches = metadata.BranchNames()
@@ -355,6 +355,17 @@ func chooseHead(updates []AppliedUpdate) string {
355 return "" 355 return ""
356 } 356 }
357 357
358 func (p *Publisher) chooseInitialHead(ctx context.Context, updates []AppliedUpdate) string {
359 if localHead, err := p.Git.SymbolicRef(ctx, "HEAD"); err == nil {
360 for _, update := range updates {
361 if update.Dst == localHead && update.NewOID != "" {
362 return localHead
363 }
364 }
365 }
366 return chooseHead(updates)
367 }
368
358 func (p *Publisher) progress(format string, args ...any) { 369 func (p *Publisher) progress(format string, args ...any) {
359 if p.Progress != nil { 370 if p.Progress != nil {
360 p.Progress(format, args...) 371 p.Progress(format, args...)