Treat incomplete remote tag observations as uncertain
This commit is contained in:
@@ -43,7 +43,7 @@ const (
|
||||
)
|
||||
|
||||
// RemoteRef holds an exact ref's object and optional peeled commit. State is
|
||||
// RefConflict only when more than one exact result or malformed data is found.
|
||||
// RefConflict when records are inconsistent or incomplete.
|
||||
type RemoteRef struct {
|
||||
State RefState
|
||||
Object string
|
||||
@@ -130,7 +130,7 @@ func (endpoint Endpoint) Query(ctx context.Context, repositoryDir, tag string) (
|
||||
remoteTag = mergeRef(remoteTag, object, true)
|
||||
}
|
||||
}
|
||||
return Snapshot{Main: main, Tag: remoteTag}, nil
|
||||
return Snapshot{Main: main, Tag: finalizeRef(remoteTag)}, nil
|
||||
}
|
||||
|
||||
// RequireCandidate requires exact main equality and an absent remote
|
||||
@@ -294,6 +294,13 @@ func mergeRef(ref RemoteRef, object string, peeled bool) RemoteRef {
|
||||
return ref
|
||||
}
|
||||
|
||||
func finalizeRef(ref RemoteRef) RemoteRef {
|
||||
if ref.State == RefPresent && ref.Object == "" && ref.Peeled != "" {
|
||||
ref.State = RefConflict
|
||||
}
|
||||
return ref
|
||||
}
|
||||
|
||||
func checkTagRef(ctx context.Context, repositoryDir, tag string) error {
|
||||
if tag == "" {
|
||||
return fmt.Errorf("invalid Git tag name %q", tag)
|
||||
|
||||
@@ -184,6 +184,52 @@ func TestMergeRefMarksDuplicateExactResultsConflicting(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTagQueryFinalizesCompleteResponse(t *testing.T) {
|
||||
repo := newRemoteFixture(t)
|
||||
endpoint := resolveFixture(t, repo)
|
||||
realGit, err := exec.LookPath("git")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
bin := t.TempDir()
|
||||
response := filepath.Join(t.TempDir(), "response")
|
||||
script := "#!/bin/sh\ncase \" $* \" in *' ls-remote '* ) case \" $* \" in *' --get-url '* ) ;; *) cat \"$RESPONSE\"; exit 0;; esac;; esac\nexec \"$REAL_GIT\" \"$@\"\n"
|
||||
if err := os.WriteFile(filepath.Join(bin, "git"), []byte(script), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Setenv("PATH", bin+string(os.PathListSeparator)+os.Getenv("PATH"))
|
||||
t.Setenv("REAL_GIT", realGit)
|
||||
t.Setenv("RESPONSE", response)
|
||||
object, peeled := strings.Repeat("a", 40), strings.Repeat("b", 40)
|
||||
exact := object + "\trefs/tags/v1.2.3\n"
|
||||
peel := peeled + "\trefs/tags/v1.2.3^{}\n"
|
||||
for _, tc := range []struct {
|
||||
name, output string
|
||||
want RemoteRef
|
||||
}{
|
||||
{"empty", "", RemoteRef{State: RefAbsent}},
|
||||
{"exact", exact, RemoteRef{State: RefPresent, Object: object}},
|
||||
{"exact then peeled", exact + peel, RemoteRef{State: RefPresent, Object: object, Peeled: peeled}},
|
||||
{"peeled then exact", peel + exact, RemoteRef{State: RefPresent, Object: object, Peeled: peeled}},
|
||||
{"peeled only", peel, RemoteRef{State: RefConflict, Peeled: peeled}},
|
||||
{"duplicate", exact + exact, RemoteRef{State: RefConflict, Object: object}},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if err := os.WriteFile(response, []byte(tc.output), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
snapshot, err := endpoint.Query(context.Background(), repo.work, "v1.2.3")
|
||||
if err != nil || snapshot.Tag != tc.want {
|
||||
t.Fatalf("Query tag = %#v, error = %v, want %#v", snapshot.Tag, err, tc.want)
|
||||
}
|
||||
got, err := endpoint.QueryTag(context.Background(), repo.work, "v1.2.3")
|
||||
if err != nil || got != tc.want {
|
||||
t.Fatalf("QueryTag = %#v, error = %v, want %#v", got, err, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type remoteFixture struct {
|
||||
work string
|
||||
bare string
|
||||
|
||||
@@ -124,7 +124,7 @@ func (endpoint Endpoint) QueryTag(ctx context.Context, repositoryDir, tag string
|
||||
remoteTag = mergeRef(remoteTag, object, true)
|
||||
}
|
||||
}
|
||||
return remoteTag, nil
|
||||
return finalizeRef(remoteTag), nil
|
||||
}
|
||||
|
||||
// PushTag publishes only the captured tag object to its exact tag ref.
|
||||
|
||||
Reference in New Issue
Block a user