From 47435b370926626be01b99473b91291366660f4b Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Fri, 16 Jan 2026 17:47:39 -0800 Subject: [PATCH 01/40] simplify RegisterFile --- client/indexd/convert.go | 2 + client/indexd/indexd_client.go | 324 +++++++++++++++++----------- client/indexd/indexd_client_test.go | 70 ++++++ docs/indexd-registerfile-upsert.md | 71 ++++++ 4 files changed, 336 insertions(+), 131 deletions(-) create mode 100644 docs/indexd-registerfile-upsert.md diff --git a/client/indexd/convert.go b/client/indexd/convert.go index 6826606a..a8e203f8 100644 --- a/client/indexd/convert.go +++ b/client/indexd/convert.go @@ -8,6 +8,7 @@ import ( "github.com/calypr/git-drs/drs" ) +// IndexdRecord represents a simplified version of an indexd record for conversion purposes func indexdRecordFromDrsObject(drsObj *drs.DRSObject) (*IndexdRecord, error) { indexdObj := &IndexdRecord{ Did: drsObj.Id, @@ -63,6 +64,7 @@ func drsAccessMethodsFromIndexdURLs(urls []string, authz []string) ([]drs.Access return accessMethods, nil } +// extract authz values from DRS access methods func indexdAuthzFromDrsAccessMethods(accessMethods []drs.AccessMethod) []string { var authz []string for _, drsURL := range accessMethods { diff --git a/client/indexd/indexd_client.go b/client/indexd/indexd_client.go index af161f85..49ba2ed8 100644 --- a/client/indexd/indexd_client.go +++ b/client/indexd/indexd_client.go @@ -9,7 +9,10 @@ import ( "net/http" "net/url" "os" + "os/exec" "path/filepath" + "strconv" + "strings" "time" "github.com/bytedance/sonic" @@ -39,6 +42,8 @@ type IndexDClient struct { HttpClient *retryablehttp.Client SConfig sonic.API + + ForcePush bool } //////////////////// @@ -78,6 +83,11 @@ func NewIndexDClient(profileConfig conf.Credential, remote Gen3Remote, logger *l retryClient.RetryWaitMin = 5 * time.Second retryClient.RetryWaitMax = 15 * time.Second + forcePush, err := getLfsCustomTransferBool("lfs.customtransfer.drs.force-push", false) + if err != nil { + return nil, err + } + return &IndexDClient{ Base: baseUrl, ProjectId: projectId, @@ -86,6 +96,7 @@ func NewIndexDClient(profileConfig conf.Credential, remote Gen3Remote, logger *l AuthHandler: &RealAuthHandler{profileConfig}, // Use real auth in production HttpClient: retryClient, SConfig: sonic.ConfigFastest, + ForcePush: forcePush, }, err } @@ -93,6 +104,23 @@ func (cl *IndexDClient) GetProjectId() string { return cl.ProjectId } +func getLfsCustomTransferBool(key string, defaultValue bool) (bool, error) { + defaultText := strconv.FormatBool(defaultValue) + cmd := exec.Command("git", "config", "--get", "--default", defaultText, key) + output, err := cmd.Output() + if err != nil { + return defaultValue, fmt.Errorf("error reading git config %s: %s", key, strings.TrimSpace(string(output))) + } + + value := strings.TrimSpace(string(output)) + + parsed, err := strconv.ParseBool(value) + if err != nil { + return defaultValue, fmt.Errorf("invalid boolean value for %s: >%q<", key, value) + } + return parsed, nil +} + // GetProfile extracts the profile from the auth handler if available // This is only needed for external APIs like g3cmd that require it func (cl *IndexDClient) GetProfile() (string, error) { @@ -280,168 +308,202 @@ func (cl *IndexDClient) getDownloadURLFromRecords(oid string, records []drs.DRSO // This function registers a file with gen3 indexd, writes the file to the bucket, // and returns the successful DRS object. // DRS will use any matching indexd record / file that already exists -func (cl *IndexDClient) RegisterFile(oid string) (*drs.DRSObject, error) { +func (cl *IndexDClient) RegisterFile(oid string) (drsObject *drs.DRSObject, err error) { cl.Logger.Printf("register file started for oid: %s", oid) - // get all existing hashes - records, err := cl.GetObjectByHash(&hash.Checksum{Type: hash.ChecksumTypeSHA256, Checksum: oid}) - if err != nil { - return nil, fmt.Errorf("error querying indexd server for matches to hash %s: %v", oid, err) - } + // attempt registration up to 2 times (normal, then with force push, if necessary) + for attempt := 0; attempt < 2; attempt++ { + var records []drs.DRSObject - // use any indexd record from the same project if it exists - // * addresses edge case where user X registering in project A has access to record in project B - // * but still needs create a new record to so user Y reading the file in project A can access it - // * even if they don't have access to project B - var drsObject *drs.DRSObject - if len(records) > 0 { - var err error - drsObject, err = drsmap.FindMatchingRecord(records, cl.ProjectId) - if err != nil { - return nil, fmt.Errorf("error finding matching record for project %s: %v", cl.ProjectId, err) + // first, see if indexd record already exists for this hash + if cl.ForcePush { + // get all existing hashes + records, err = cl.GetObjectByHash(&hash.Checksum{Type: hash.ChecksumTypeSHA256, Checksum: oid}) + if err != nil { + return nil, fmt.Errorf("error querying indexd server for matches to hash %s: %v", oid, err) + } + + // use any indexd record from the same project if it exists + // * addresses edge case where user X registering in project A has access to record in project B + // * but still needs to create a new record to so user Y reading the file in project A can access it + // * even if they don't have access to project B + if len(records) > 0 { + drsObject, err = drsmap.FindMatchingRecord(records, cl.ProjectId) + if err != nil { + return nil, fmt.Errorf("error finding matching record for project %s: %v", cl.ProjectId, err) + } + } } - } - if drsObject == nil { - // otherwise, create indexd record - cl.Logger.Print("creating record: no existing indexd record for this project") + if drsObject == nil { + // otherwise, create indexd record + // transform git pre push record to drs object + drsObject, err = drsmap.DrsInfoFromOid(oid) + if err != nil { + return nil, fmt.Errorf("error getting indexd object for oid %s: %v", oid, err) + } - // get indexd object using drs map - drsObject, err = drsmap.DrsInfoFromOid(oid) - if err != nil { - return nil, fmt.Errorf("error getting indexd object for oid %s: %v", oid, err) - } + // convert to indexd record + indexdObj, err := indexdRecordFromDrsObject(drsObject) + if err != nil { + return nil, fmt.Errorf("error converting DRS object to indexd record: %v", err) + } - indexdObj, err := indexdRecordFromDrsObject(drsObject) - if err != nil { - return nil, fmt.Errorf("error converting DRS object to indexd record: %v", err) + // save the record + drsObject, err = cl.RegisterIndexdRecord(indexdObj) + if err != nil { + // if error and force push is not enabled, retry with force push + if !cl.ForcePush { + cl.Logger.Printf("error saving indexd record without force push: %s; retrying with force push enabled", err) + cl.ForcePush = true + drsObject = nil + continue + } + cl.Logger.Printf("error saving indexd record: %s", err) + return nil, fmt.Errorf("error saving indexd record: %v", err) + } + + // delete indexd record only if it's been registered in this repo + // TODO - improve this logic later if we have a better way to track this + defer func() { + if err != nil { + cl.Logger.Printf("registration incomplete, cleaning up indexd record for oid %s", oid) + cleanupErr := cl.DeleteIndexdRecord(drsObject.Id) + if cleanupErr != nil { + cl.Logger.Printf("error cleaning up indexd record on failed registration for oid %s: %s", oid, cleanupErr) + cl.Logger.Printf("please delete the indexd record manually if needed for DRS ID: %s", drsObject.Id) + cl.Logger.Printf("see https://uc-cdis.github.io/gen3sdk-python/_build/html/indexing.html") + return + } + cl.Logger.Printf("cleaned up indexd record for oid %s", oid) + } + }() } - // register the record - drsObject, err = cl.RegisterIndexdRecord(indexdObj) + isDownloadable := false - if err != nil { - cl.Logger.Printf("error registering indexd record: %s", err) - return nil, fmt.Errorf("error registering indexd record: %v", err) + if cl.ForcePush { + // TODO why is this transformed again? + recordsForDownload := ensureDrsObjectInRecords(records, drsObject) + + // determine if file is downloadable + // TODO optimize to avoid double lookup? + // TODO simply take drsObject as a parameter + isDownloadable, err = cl.isFileDownloadable(oid, recordsForDownload) + if err != nil { + return nil, err + } } - // delete indexd record only if it's been registered in this repo - defer func() { + // if file is not downloadable, then upload it to bucket + if !isDownloadable { + cl.Logger.Printf("Proceeding to upload %s", oid) + + filePath, err := drsmap.GetObjectPath(projectdir.LFS_OBJS_PATH, oid) + if err != nil { + cl.Logger.Printf("error getting object path for oid %s: %s", oid, err) + return nil, fmt.Errorf("error getting object path for oid %s: %v", oid, err) + } + + profile, err := cl.GetProfile() + if err != nil { + return nil, fmt.Errorf("error getting profile for upload: %v", err) + } + + // TODO - should we deprecate this gen3-client style logger in favor of drslog.Logger? + // TODO - or can we "wrap it" so both work together? + logger, closer := logs.New(profile, logs.WithBaseLogger(cl.Logger)) + defer closer() + + g3, err := dataClient.NewGen3Interface(profile, logger) + if err != nil { + return nil, fmt.Errorf("error creating Gen3 interface: %v", err) + } + + file, err := os.Open(filePath) + if err != nil { + return nil, fmt.Errorf("error opening file %s: %v", filePath, err) + } + + stat, err := file.Stat() if err != nil { - cl.Logger.Printf("registration incomplete, cleaning up indexd record for oid %s", oid) - err = cl.DeleteIndexdRecord(drsObject.Id) + return nil, fmt.Errorf("Error stating file %s: %v", file.Name(), err) + } + // TODO - Can we reuse Auth to ensure we are not repeatedly refreshing tokens? + if stat.Size() < 5*common.GB { + err := upload.UploadSingle(context.Background(), g3.GetCredential().Profile, drsObject.Id, filePath, cl.BucketName, false) + if err != nil { + cl.Logger.Printf("error uploading single file to bucket: %s", err) + return nil, fmt.Errorf("error uploading single file to bucket: %s", err) + } + } else { + err = upload.MultipartUpload( + context.TODO(), + g3, + common.FileUploadRequestObject{ + FilePath: filePath, + Filename: filepath.Base(filePath), + GUID: drsObject.Id, + FileMetadata: common.FileMetadata{}, + Bucket: cl.BucketName, + }, + file, false, + ) if err != nil { - cl.Logger.Printf("error cleaning up indexd record on failed registration for oid %s: %s", oid, err) - cl.Logger.Printf("please delete the indexd record manually if needed for DRS ID: %s", drsObject.Id) - cl.Logger.Printf("see https://uc-cdis.github.io/gen3sdk-python/_build/html/indexing.html") - return + cl.Logger.Printf("error uploading file to bucket: %s", err) + return nil, fmt.Errorf("error uploading file to bucket: %v", err) } - cl.Logger.Printf("cleaned up indexd record for oid %s", oid) } - }() + } else { + cl.Logger.Print("file exists in bucket, skipping upload") + } + + // no implicit cleanup of DRS objects, should be done manually + + // return drsObject + return drsObject, nil } - recordsForDownload := records - if drsObject != nil { - found := false - for _, record := range recordsForDownload { - if record.Id == drsObject.Id { - found = true - break - } - } - if !found { - recordsForDownload = append(recordsForDownload, *drsObject) - } + return nil, fmt.Errorf("indexd registration failed after retry for oid %s", oid) +} + +func (cl *IndexDClient) isFileDownloadable(oid string, records []drs.DRSObject) (bool, error) { + if !cl.ForcePush { + cl.Logger.Printf("force push disabled; proceeding to upload oid %s", oid) + return false, nil } - // determine if file is downloadable - isDownloadable := true cl.Logger.Printf("checking if %s file is downloadable", oid) - signedUrl, err := cl.getDownloadURLFromRecords(oid, recordsForDownload) + signedUrl, err := cl.getDownloadURLFromRecords(oid, records) if err != nil { cl.Logger.Printf("error getting signed URL for file with oid %s: %s", oid, err) - return nil, fmt.Errorf("error getting signed URL for file with oid %s: %s", oid, err) + return false, fmt.Errorf("error getting signed URL for file with oid %s: %s", oid, err) } if signedUrl == nil { - isDownloadable = false - } else { // signedUrl exists - err = utils.CanDownloadFile(signedUrl.URL) - if err != nil { - isDownloadable = false - cl.Logger.Printf("file with oid %s does not exist in bucket: %s", oid, err) - } else { - cl.Logger.Printf("file with oid %s exists in bucket", oid) - } + return false, nil } - // if file is not downloadable, then upload it to bucket - if !isDownloadable { - cl.Logger.Printf("Proceeding to upload %s", oid) - - filePath, err := drsmap.GetObjectPath(projectdir.LFS_OBJS_PATH, oid) - if err != nil { - cl.Logger.Printf("error getting object path for oid %s: %s", oid, err) - return nil, fmt.Errorf("error getting object path for oid %s: %v", oid, err) - } - - profile, err := cl.GetProfile() - if err != nil { - return nil, fmt.Errorf("error getting profile for upload: %v", err) - } - - // TODO - should we deprecate this gen3-client style logger in favor of drslog.Logger? - // TODO - or can we "wrap it" so both work together? - logger, closer := logs.New(profile, logs.WithBaseLogger(cl.Logger)) - defer closer() - - g3, err := dataClient.NewGen3Interface(profile, logger) - if err != nil { - return nil, fmt.Errorf("error creating Gen3 interface: %v", err) - } + err = utils.CanDownloadFile(signedUrl.URL) + if err != nil { + cl.Logger.Printf("file with oid %s does not exist in bucket: %s", oid, err) + return false, nil + } + cl.Logger.Printf("file with oid %s exists in bucket", oid) + return true, nil +} - file, err := os.Open(filePath) - if err != nil { - return nil, fmt.Errorf("error opening file %s: %v", filePath, err) - } +func ensureDrsObjectInRecords(records []drs.DRSObject, drsObject *drs.DRSObject) []drs.DRSObject { + if drsObject == nil { + return records + } - stat, err := file.Stat() - if err != nil { - return nil, fmt.Errorf("Error stating file %s: %v", file.Name(), err) - } - // TODO - Can we reuse Auth to ensure we are not repeatedly refreshing tokens? - if stat.Size() < 5*common.GB { - err := upload.UploadSingle(context.Background(), g3.GetCredential().Profile, drsObject.Id, filePath, cl.BucketName, false) - if err != nil { - cl.Logger.Printf("error uploading single file to bucket: %s", err) - return nil, fmt.Errorf("error uploading single file to bucket: %s", err) - } - } else { - err = upload.MultipartUpload( - context.TODO(), - g3, - common.FileUploadRequestObject{ - FilePath: filePath, - Filename: filepath.Base(filePath), - GUID: drsObject.Id, - FileMetadata: common.FileMetadata{}, - Bucket: cl.BucketName, - }, - file, false, - ) - if err != nil { - cl.Logger.Printf("error uploading file to bucket: %s", err) - return nil, fmt.Errorf("error uploading file to bucket: %v", err) - } + for _, record := range records { + if record.Id == drsObject.Id { + return records } - } else { - cl.Logger.Print("file exists in bucket, skipping upload") } - // no implicit cleanup of DRS objects, should be done manually - - // return drsObject - return drsObject, nil + return append(records, *drsObject) } func (cl *IndexDClient) GetObject(id string) (*drs.DRSObject, error) { @@ -581,7 +643,7 @@ func (cl *IndexDClient) RegisterIndexdRecord(indexdObj *IndexdRecord) (*drs.DRSO return nil, err } - cl.Logger.Printf("retrieved IndexdObj: %s", string(jsonBytes)) + cl.Logger.Printf("writing IndexdObj: %s", string(jsonBytes)) // register DRS object via /index POST // (setup post request to indexd) diff --git a/client/indexd/indexd_client_test.go b/client/indexd/indexd_client_test.go index 22e8bd79..ec5ca55f 100644 --- a/client/indexd/indexd_client_test.go +++ b/client/indexd/indexd_client_test.go @@ -7,6 +7,7 @@ import ( "net/http/httptest" "net/url" "os" + "os/exec" "path/filepath" "strings" "sync" @@ -372,6 +373,12 @@ func TestIndexdClient_GetProjectSample_DefaultLimit(t *testing.T) { } func TestIndexdClient_NewIndexDClient(t *testing.T) { + repoDir := initTestGitRepo(t) + restore := chdirForTest(t, repoDir) + defer restore() + + runGit(t, repoDir, "config", "lfs.customtransfer.drs.force-push", "false") + cred := conf.Credential{APIEndpoint: "https://example.com"} remote := Gen3Remote{ProjectID: "project", Bucket: "bucket"} client, err := NewIndexDClient(cred, remote, drslog.NewNoOpLogger()) @@ -388,4 +395,67 @@ func TestIndexdClient_NewIndexDClient(t *testing.T) { if indexd.HttpClient.HTTPClient.Timeout != 30*time.Second { t.Fatalf("unexpected http timeout: %v", indexd.HttpClient.HTTPClient.Timeout) } + if indexd.ForcePush { + t.Fatalf("expected force push disabled, got %v", indexd.ForcePush) + } +} + +func TestGetLfsCustomTransferBool_DefaultValue(t *testing.T) { + repoDir := initTestGitRepo(t) + restore := chdirForTest(t, repoDir) + defer restore() + + value, err := getLfsCustomTransferBool("lfs.customtransfer.drs.force-push", false) + if err != nil { + t.Fatalf("getLfsCustomTransferBool error: %v", err) + } + if value { + t.Fatalf("expected default false, got %v", value) + } +} + +func TestGetLfsCustomTransferBool_MissingKeyReturnsDefault(t *testing.T) { + repoDir := initTestGitRepo(t) + restore := chdirForTest(t, repoDir) + defer restore() + + value, err := getLfsCustomTransferBool("lfs.customtransfer.drs.force-push", false) + if err != nil { + t.Fatalf("getLfsCustomTransferBool error: %v", err) + } + if value { + t.Fatalf("expected false, got %v", value) + } +} + +func initTestGitRepo(t *testing.T) string { + t.Helper() + repoDir := t.TempDir() + runGit(t, repoDir, "init") + return repoDir +} + +func runGit(t *testing.T, dir string, args ...string) { + t.Helper() + cmd := exec.Command("git", args...) + cmd.Dir = dir + if output, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("git %s failed: %v (%s)", strings.Join(args, " "), err, strings.TrimSpace(string(output))) + } +} + +func chdirForTest(t *testing.T, dir string) func() { + t.Helper() + cwd, err := os.Getwd() + if err != nil { + t.Fatalf("Getwd error: %v", err) + } + if err := os.Chdir(dir); err != nil { + t.Fatalf("Chdir error: %v", err) + } + return func() { + if err := os.Chdir(cwd); err != nil { + t.Fatalf("restore Chdir error: %v", err) + } + } } diff --git a/docs/indexd-registerfile-upsert.md b/docs/indexd-registerfile-upsert.md new file mode 100644 index 00000000..d570385b --- /dev/null +++ b/docs/indexd-registerfile-upsert.md @@ -0,0 +1,71 @@ +# ADR 0001: Configure RegisterFile upsert/bucket checks via git LFS config + +## Status +Accepted + +## Context +The Indexd `RegisterFile` flow needs toggles for: +- whether to upsert (create) indexd records when no matching project record exists +- whether to check bucket existence before uploading + +These toggles must be controlled per-repository using git LFS configuration (`git config` entries under `lfs.customtransfer.drs.*`). This keeps behavior in repo-local configuration and avoids coupling to remote YAML configuration. + +## Decision +Read `lfs.customtransfer.drs.force-push` from git config during Indexd client initialization. Missing values default to `false`. Invalid values fail initialization with a clear error. + +## Before + +```mermaid +flowchart TD + A[RegisterFile] --> B[Query indexd by hash] + B --> C{Matching project record} + C -- Yes --> D[Reuse existing record] + C -- No --> E[Build DRS and indexd record] + E --> F[POST indexd register] + F --> G[Defer cleanup on failure] + D --> H[Prepare records for download] + G --> H + H --> I[Call drs objects and access to get signed URL] + I --> J[Check bucket via signed URL] + J --> K{Downloadable} + K -- Yes --> L[Skip upload] + K -- No --> M[Upload to bucket] +``` + +## After + + +### fresh (no existing record) + +```mermaid +flowchart TD + A[RegisterFile] --> H[Prepare records for download] + H --> J[Upload to bucket] +``` + +### force (retry on indexd register error) + +```mermaid +flowchart TD + A[RegisterFile] --> B[POST indexd register] + B --> C{Register success} + C -- Yes --> D[Prepare records for download] + C -- No --> E[Enable force push] + E --> F[Query indexd by hash] + F --> G{Matching project record} + G -- Yes --> H[Reuse existing record] + G -- No --> I[POST indexd register] + H --> D + I --> D + D --> J{force push} + J -- No --> K[Upload to bucket] + J -- Yes --> L[Get signed URL and check bucket] + L --> M{Downloadable} + M -- Yes --> N[Skip upload] + M -- No --> K +``` + +## Consequences +- Operators can control behavior using `git config` without editing remote YAML. +- Defaults remain disabled when keys are missing. +- Misconfigured values fail fast during initialization. From db7509ec7633b20d906409651a43cc9fe9b2e787 Mon Sep 17 00:00:00 2001 From: Brian Date: Fri, 16 Jan 2026 19:59:14 -0800 Subject: [PATCH 02/40] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- client/indexd/indexd_client.go | 12 +++++++++--- docs/indexd-registerfile-upsert.md | 3 ++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/client/indexd/indexd_client.go b/client/indexd/indexd_client.go index 49ba2ed8..9c159c57 100644 --- a/client/indexd/indexd_client.go +++ b/client/indexd/indexd_client.go @@ -97,7 +97,7 @@ func NewIndexDClient(profileConfig conf.Credential, remote Gen3Remote, logger *l HttpClient: retryClient, SConfig: sonic.ConfigFastest, ForcePush: forcePush, - }, err + }, nil } func (cl *IndexDClient) GetProjectId() string { @@ -313,6 +313,7 @@ func (cl *IndexDClient) RegisterFile(oid string) (drsObject *drs.DRSObject, err // attempt registration up to 2 times (normal, then with force push, if necessary) for attempt := 0; attempt < 2; attempt++ { + cl.Logger.Printf("register file attempt %d for oid: %s", attempt+1, oid) var records []drs.DRSObject // first, see if indexd record already exists for this hash @@ -325,7 +326,7 @@ func (cl *IndexDClient) RegisterFile(oid string) (drsObject *drs.DRSObject, err // use any indexd record from the same project if it exists // * addresses edge case where user X registering in project A has access to record in project B - // * but still needs to create a new record to so user Y reading the file in project A can access it + // * but still needs to create a new record so that user Y reading the file in project A can access it // * even if they don't have access to project B if len(records) > 0 { drsObject, err = drsmap.FindMatchingRecord(records, cl.ProjectId) @@ -354,9 +355,13 @@ func (cl *IndexDClient) RegisterFile(oid string) (drsObject *drs.DRSObject, err if err != nil { // if error and force push is not enabled, retry with force push if !cl.ForcePush { + originalForcePush := cl.ForcePush cl.Logger.Printf("error saving indexd record without force push: %s; retrying with force push enabled", err) cl.ForcePush = true drsObject = nil + defer func() { + cl.ForcePush = originalForcePush + }() continue } cl.Logger.Printf("error saving indexd record: %s", err) @@ -424,10 +429,11 @@ func (cl *IndexDClient) RegisterFile(oid string) (drsObject *drs.DRSObject, err if err != nil { return nil, fmt.Errorf("error opening file %s: %v", filePath, err) } + defer file.Close() stat, err := file.Stat() if err != nil { - return nil, fmt.Errorf("Error stating file %s: %v", file.Name(), err) + return nil, fmt.Errorf("error stating file %s: %v", file.Name(), err) } // TODO - Can we reuse Auth to ensure we are not repeatedly refreshing tokens? if stat.Size() < 5*common.GB { diff --git a/docs/indexd-registerfile-upsert.md b/docs/indexd-registerfile-upsert.md index d570385b..f5740ecb 100644 --- a/docs/indexd-registerfile-upsert.md +++ b/docs/indexd-registerfile-upsert.md @@ -39,7 +39,8 @@ flowchart TD ```mermaid flowchart TD - A[RegisterFile] --> H[Prepare records for download] + A[RegisterFile] --> B[POST indexd register] + B --> H[Prepare records for download] H --> J[Upload to bucket] ``` From 0d867b6c83430d1939907c7fdfeecb3212cae2ce Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Fri, 16 Jan 2026 20:03:44 -0800 Subject: [PATCH 03/40] wip --- client/indexd/indexd_client.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/indexd/indexd_client.go b/client/indexd/indexd_client.go index 9c159c57..36ad7107 100644 --- a/client/indexd/indexd_client.go +++ b/client/indexd/indexd_client.go @@ -109,7 +109,7 @@ func getLfsCustomTransferBool(key string, defaultValue bool) (bool, error) { cmd := exec.Command("git", "config", "--get", "--default", defaultText, key) output, err := cmd.Output() if err != nil { - return defaultValue, fmt.Errorf("error reading git config %s: %s", key, strings.TrimSpace(string(output))) + return defaultValue, fmt.Errorf("error reading git config %s: %v", key, err) } value := strings.TrimSpace(string(output)) @@ -370,6 +370,7 @@ func (cl *IndexDClient) RegisterFile(oid string) (drsObject *drs.DRSObject, err // delete indexd record only if it's been registered in this repo // TODO - improve this logic later if we have a better way to track this + // The defer function creates a closure that captures the named return variable "err", but the function returns early on line 464 within the loop without setting "err" to nil. This means if the function succeeds, the deferred cleanup could still execute because "err" might contain a value from a previous error that was later recovered from. The cleanup logic should check a more specific condition or the defer should be inside the if drsObject == nil block where the record is actually created. defer func() { if err != nil { cl.Logger.Printf("registration incomplete, cleaning up indexd record for oid %s", oid) From c4328e4a3adad9ee3e3f973fda122a2bef20debd Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Fri, 16 Jan 2026 22:54:40 -0800 Subject: [PATCH 04/40] refactor cleanup --- client/indexd/indexd_client.go | 104 +++++++++++++++------------------ 1 file changed, 47 insertions(+), 57 deletions(-) diff --git a/client/indexd/indexd_client.go b/client/indexd/indexd_client.go index 36ad7107..fa10d0e5 100644 --- a/client/indexd/indexd_client.go +++ b/client/indexd/indexd_client.go @@ -305,29 +305,38 @@ func (cl *IndexDClient) getDownloadURLFromRecords(oid string, records []drs.DRSO } // RegisterFile implements DRSClient. -// This function registers a file with gen3 indexd, writes the file to the bucket, -// and returns the successful DRS object. -// DRS will use any matching indexd record / file that already exists -func (cl *IndexDClient) RegisterFile(oid string) (drsObject *drs.DRSObject, err error) { +// It registers (or reuses) an indexd record for the oid, uploads the object if it +// is not already available in the bucket, and returns the resulting DRS object. +// When registration fails without force push, it retries once with force push +// enabled to reuse existing records and avoid duplicate uploads. +func (cl *IndexDClient) RegisterFile(oid string) (*drs.DRSObject, error) { cl.Logger.Printf("register file started for oid: %s", oid) - // attempt registration up to 2 times (normal, then with force push, if necessary) - for attempt := 0; attempt < 2; attempt++ { - cl.Logger.Printf("register file attempt %d for oid: %s", attempt+1, oid) + originalForcePush := cl.ForcePush + attempts := 1 + if !originalForcePush { + attempts = 2 + defer func() { + cl.ForcePush = originalForcePush + }() + } + + for attempt := 0; attempt < attempts; attempt++ { + if attempt == 1 { + cl.ForcePush = true + } + cl.Logger.Printf("register file attempt %d for oid: %s (force push: %t)", attempt+1, oid, cl.ForcePush) + var records []drs.DRSObject + var err error + var drsObject *drs.DRSObject + createdRecord := false - // first, see if indexd record already exists for this hash if cl.ForcePush { - // get all existing hashes records, err = cl.GetObjectByHash(&hash.Checksum{Type: hash.ChecksumTypeSHA256, Checksum: oid}) if err != nil { return nil, fmt.Errorf("error querying indexd server for matches to hash %s: %v", oid, err) } - - // use any indexd record from the same project if it exists - // * addresses edge case where user X registering in project A has access to record in project B - // * but still needs to create a new record so that user Y reading the file in project A can access it - // * even if they don't have access to project B if len(records) > 0 { drsObject, err = drsmap.FindMatchingRecord(records, cl.ProjectId) if err != nil { @@ -337,83 +346,66 @@ func (cl *IndexDClient) RegisterFile(oid string) (drsObject *drs.DRSObject, err } if drsObject == nil { - // otherwise, create indexd record - // transform git pre push record to drs object drsObject, err = drsmap.DrsInfoFromOid(oid) if err != nil { return nil, fmt.Errorf("error getting indexd object for oid %s: %v", oid, err) } - // convert to indexd record indexdObj, err := indexdRecordFromDrsObject(drsObject) if err != nil { return nil, fmt.Errorf("error converting DRS object to indexd record: %v", err) } - // save the record drsObject, err = cl.RegisterIndexdRecord(indexdObj) if err != nil { - // if error and force push is not enabled, retry with force push if !cl.ForcePush { - originalForcePush := cl.ForcePush cl.Logger.Printf("error saving indexd record without force push: %s; retrying with force push enabled", err) - cl.ForcePush = true - drsObject = nil - defer func() { - cl.ForcePush = originalForcePush - }() continue } cl.Logger.Printf("error saving indexd record: %s", err) return nil, fmt.Errorf("error saving indexd record: %v", err) } + createdRecord = true + } - // delete indexd record only if it's been registered in this repo - // TODO - improve this logic later if we have a better way to track this - // The defer function creates a closure that captures the named return variable "err", but the function returns early on line 464 within the loop without setting "err" to nil. This means if the function succeeds, the deferred cleanup could still execute because "err" might contain a value from a previous error that was later recovered from. The cleanup logic should check a more specific condition or the defer should be inside the if drsObject == nil block where the record is actually created. - defer func() { - if err != nil { - cl.Logger.Printf("registration incomplete, cleaning up indexd record for oid %s", oid) - cleanupErr := cl.DeleteIndexdRecord(drsObject.Id) - if cleanupErr != nil { - cl.Logger.Printf("error cleaning up indexd record on failed registration for oid %s: %s", oid, cleanupErr) - cl.Logger.Printf("please delete the indexd record manually if needed for DRS ID: %s", drsObject.Id) - cl.Logger.Printf("see https://uc-cdis.github.io/gen3sdk-python/_build/html/indexing.html") - return - } + cleanupOnError := func(cause error) (*drs.DRSObject, error) { + if createdRecord && drsObject != nil { + cl.Logger.Printf("registration incomplete, cleaning up indexd record for oid %s", oid) + cleanupErr := cl.DeleteIndexdRecord(drsObject.Id) + if cleanupErr != nil { + cl.Logger.Printf("error cleaning up indexd record on failed registration for oid %s: %s", oid, cleanupErr) + cl.Logger.Printf("please delete the indexd record manually if needed for DRS ID: %s", drsObject.Id) + cl.Logger.Printf("see https://uc-cdis.github.io/gen3sdk-python/_build/html/indexing.html") + } else { cl.Logger.Printf("cleaned up indexd record for oid %s", oid) } - }() + } + return nil, cause } isDownloadable := false - if cl.ForcePush { - // TODO why is this transformed again? + // TODO why is this a list of records? recordsForDownload := ensureDrsObjectInRecords(records, drsObject) - - // determine if file is downloadable - // TODO optimize to avoid double lookup? - // TODO simply take drsObject as a parameter + // TODO don't we already know this from earlier? isDownloadable, err = cl.isFileDownloadable(oid, recordsForDownload) if err != nil { - return nil, err + return cleanupOnError(err) } } - // if file is not downloadable, then upload it to bucket if !isDownloadable { cl.Logger.Printf("Proceeding to upload %s", oid) filePath, err := drsmap.GetObjectPath(projectdir.LFS_OBJS_PATH, oid) if err != nil { cl.Logger.Printf("error getting object path for oid %s: %s", oid, err) - return nil, fmt.Errorf("error getting object path for oid %s: %v", oid, err) + return cleanupOnError(fmt.Errorf("error getting object path for oid %s: %v", oid, err)) } profile, err := cl.GetProfile() if err != nil { - return nil, fmt.Errorf("error getting profile for upload: %v", err) + return cleanupOnError(fmt.Errorf("error getting profile for upload: %v", err)) } // TODO - should we deprecate this gen3-client style logger in favor of drslog.Logger? @@ -423,25 +415,26 @@ func (cl *IndexDClient) RegisterFile(oid string) (drsObject *drs.DRSObject, err g3, err := dataClient.NewGen3Interface(profile, logger) if err != nil { - return nil, fmt.Errorf("error creating Gen3 interface: %v", err) + return cleanupOnError(fmt.Errorf("error creating Gen3 interface: %v", err)) } file, err := os.Open(filePath) if err != nil { - return nil, fmt.Errorf("error opening file %s: %v", filePath, err) + return cleanupOnError(fmt.Errorf("error opening file %s: %v", filePath, err)) } defer file.Close() stat, err := file.Stat() if err != nil { - return nil, fmt.Errorf("error stating file %s: %v", file.Name(), err) + return cleanupOnError(fmt.Errorf("error stating file %s: %v", file.Name(), err)) } + // TODO - Can we reuse Auth to ensure we are not repeatedly refreshing tokens? if stat.Size() < 5*common.GB { err := upload.UploadSingle(context.Background(), g3.GetCredential().Profile, drsObject.Id, filePath, cl.BucketName, false) if err != nil { cl.Logger.Printf("error uploading single file to bucket: %s", err) - return nil, fmt.Errorf("error uploading single file to bucket: %s", err) + return cleanupOnError(fmt.Errorf("error uploading single file to bucket: %s", err)) } } else { err = upload.MultipartUpload( @@ -458,16 +451,13 @@ func (cl *IndexDClient) RegisterFile(oid string) (drsObject *drs.DRSObject, err ) if err != nil { cl.Logger.Printf("error uploading file to bucket: %s", err) - return nil, fmt.Errorf("error uploading file to bucket: %v", err) + return cleanupOnError(fmt.Errorf("error uploading file to bucket: %v", err)) } } } else { cl.Logger.Print("file exists in bucket, skipping upload") } - // no implicit cleanup of DRS objects, should be done manually - - // return drsObject return drsObject, nil } From 06ab01027d4ac7b992182ef83eadb29edf9768a3 Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Sat, 17 Jan 2026 13:28:55 -0800 Subject: [PATCH 05/40] fix:ensure AccessMethod.Type --- client/indexd/convert.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/client/indexd/convert.go b/client/indexd/convert.go index a8e203f8..95b23073 100644 --- a/client/indexd/convert.go +++ b/client/indexd/convert.go @@ -4,6 +4,7 @@ package indexd_client import ( "fmt" + "net/url" "github.com/calypr/git-drs/drs" ) @@ -47,9 +48,17 @@ func indexdRecordToDrsObject(indexdObj *IndexdRecord) (*drs.DRSObject, error) { func drsAccessMethodsFromIndexdURLs(urls []string, authz []string) ([]drs.AccessMethod, error) { var accessMethods []drs.AccessMethod - for _, url := range urls { + for _, urlString := range urls { var method drs.AccessMethod - method.AccessURL = drs.AccessURL{URL: url} + method.AccessURL = drs.AccessURL{URL: urlString} + + parsed, err := url.Parse(urlString) + if err != nil || parsed.Scheme == "" { + // default to https if no scheme or parse error + method.Type = "https" + } else { + method.Type = parsed.Scheme + } // check if authz is null or 0-length, then error if authz == nil { From 8f2998468b2686fe7caffc1f2de2bc6301125992 Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Sat, 17 Jan 2026 13:30:17 -0800 Subject: [PATCH 06/40] fix: eliminate conversions --- client/indexd/indexd_client.go | 84 +++++++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 22 deletions(-) diff --git a/client/indexd/indexd_client.go b/client/indexd/indexd_client.go index fa10d0e5..d88ef535 100644 --- a/client/indexd/indexd_client.go +++ b/client/indexd/indexd_client.go @@ -77,6 +77,23 @@ func NewIndexDClient(profileConfig conf.Credential, remote Gen3Remote, logger *l retryClient := retryablehttp.NewClient() retryClient.HTTPClient = httpClient + // Custom CheckRetry: do not retry when response body contains "already exists" + retryClient.CheckRetry = func(ctx context.Context, resp *http.Response, err error) (bool, error) { + if resp != nil && resp.Body != nil { + bodyBytes, readErr := io.ReadAll(resp.Body) + // restore body for downstream consumers + resp.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) + if readErr == nil { + if strings.Contains(string(bodyBytes), "already exists") { + // do not retry on "already exists" messages + return false, nil + } + } + } + // fallback to default policy + return retryablehttp.DefaultRetryPolicy(ctx, resp, err) + } + retryClient.Logger = logger // TODO - make these configurable? retryClient.RetryMax = 5 @@ -268,40 +285,60 @@ func (cl *IndexDClient) getDownloadURLFromRecords(oid string, records []drs.DRSO // naively get access ID from splitting first path into : accessId := drsObj.AccessMethods[0].AccessID + did := drsObj.Id + + accessUrl, err := cl.getDownloadURL(did, accessId) + if err != nil { + return nil, err + } + + return &accessUrl, nil +} +// getDownloadURL gets a signed URL for the given DRS ID and access ID +func (cl *IndexDClient) getDownloadURL(did string, accessId string) (drs.AccessURL, error) { // get signed url a := *cl.Base - a.Path = filepath.Join(a.Path, "ga4gh/drs/v1/objects", drsObj.Id, "access", accessId) + a.Path = filepath.Join(a.Path, "ga4gh/drs/v1/objects", did, "access", accessId) req, err := retryablehttp.NewRequest("GET", a.String(), nil) if err != nil { - return nil, err + return drs.AccessURL{}, err } err = cl.AuthHandler.AddAuthHeader(req.Request) if err != nil { - return nil, fmt.Errorf("error adding Gen3 auth header: %v", err) + return drs.AccessURL{}, fmt.Errorf("error adding Gen3 auth header: %v", err) } response, err := cl.HttpClient.Do(req) if err != nil { - return nil, fmt.Errorf("error getting signed URL: %v", err) + return drs.AccessURL{}, fmt.Errorf("error getting signed URL: %v", err) } - defer response.Body.Close() + defer func(Body io.ReadCloser) { + _ = Body.Close() + }(response.Body) accessUrl := drs.AccessURL{} - if err := cl.SConfig.NewDecoder(response.Body).Decode(&accessUrl); err != nil { - return nil, fmt.Errorf("unable to decode response into drs.AccessURL: %v", err) + + // read full body so we can both decode and include it in any error + bodyBytes, readErr := io.ReadAll(response.Body) + if readErr != nil { + return drs.AccessURL{}, fmt.Errorf("unable to read response body: %v", readErr) + } + + if err := cl.SConfig.NewDecoder(bytes.NewReader(bodyBytes)).Decode(&accessUrl); err != nil { + return drs.AccessURL{}, fmt.Errorf("unable to decode response into drs.AccessURL: %v; body: %s", err, string(bodyBytes)) } // check if empty if accessUrl.URL == "" { - return nil, fmt.Errorf("signed url is empty %#v %s", accessUrl, response.Status) + return drs.AccessURL{}, fmt.Errorf("signed url is empty %#v %s", accessUrl, response.Status) } cl.Logger.Printf("signed url retrieved: %s", response.Status) - return &accessUrl, nil + return accessUrl, nil } // RegisterFile implements DRSClient. @@ -350,11 +387,13 @@ func (cl *IndexDClient) RegisterFile(oid string) (*drs.DRSObject, error) { if err != nil { return nil, fmt.Errorf("error getting indexd object for oid %s: %v", oid, err) } + cl.Logger.Printf("DrsInfoFromOid: %v", drsObject) indexdObj, err := indexdRecordFromDrsObject(drsObject) if err != nil { return nil, fmt.Errorf("error converting DRS object to indexd record: %v", err) } + cl.Logger.Printf("indexdRecordFromDrsObject: %v", indexdObj) drsObject, err = cl.RegisterIndexdRecord(indexdObj) if err != nil { @@ -366,6 +405,8 @@ func (cl *IndexDClient) RegisterFile(oid string) (*drs.DRSObject, error) { return nil, fmt.Errorf("error saving indexd record: %v", err) } createdRecord = true + cl.Logger.Printf("RegisterIndexdRecord: %v", drsObject) + } cleanupOnError := func(cause error) (*drs.DRSObject, error) { @@ -385,10 +426,7 @@ func (cl *IndexDClient) RegisterFile(oid string) (*drs.DRSObject, error) { isDownloadable := false if cl.ForcePush { - // TODO why is this a list of records? - recordsForDownload := ensureDrsObjectInRecords(records, drsObject) - // TODO don't we already know this from earlier? - isDownloadable, err = cl.isFileDownloadable(oid, recordsForDownload) + isDownloadable, err = cl.isFileDownloadable(drsObject) if err != nil { return cleanupOnError(err) } @@ -413,6 +451,8 @@ func (cl *IndexDClient) RegisterFile(oid string) (*drs.DRSObject, error) { logger, closer := logs.New(profile, logs.WithBaseLogger(cl.Logger)) defer closer() + // Instantiate interface to Gen3 + // TODO - Can we reuse this interface to avoid repeated config parsing and most likely repeated token refresh? g3, err := dataClient.NewGen3Interface(profile, logger) if err != nil { return cleanupOnError(fmt.Errorf("error creating Gen3 interface: %v", err)) @@ -464,28 +504,28 @@ func (cl *IndexDClient) RegisterFile(oid string) (*drs.DRSObject, error) { return nil, fmt.Errorf("indexd registration failed after retry for oid %s", oid) } -func (cl *IndexDClient) isFileDownloadable(oid string, records []drs.DRSObject) (bool, error) { +func (cl *IndexDClient) isFileDownloadable(drsObject *drs.DRSObject) (bool, error) { if !cl.ForcePush { - cl.Logger.Printf("force push disabled; proceeding to upload oid %s", oid) + cl.Logger.Printf("force push disabled; proceeding to upload oid %s", drsObject.Id) return false, nil } - cl.Logger.Printf("checking if %s file is downloadable", oid) - signedUrl, err := cl.getDownloadURLFromRecords(oid, records) + cl.Logger.Printf("checking if %s file is downloadable %v %v %v", drsObject.Id, drsObject.AccessMethods[0].AccessID, drsObject.AccessMethods[0].Type, drsObject.AccessMethods[0].AccessURL) + signedUrl, err := cl.getDownloadURL(drsObject.Id, drsObject.AccessMethods[0].Type) if err != nil { - cl.Logger.Printf("error getting signed URL for file with oid %s: %s", oid, err) - return false, fmt.Errorf("error getting signed URL for file with oid %s: %s", oid, err) + cl.Logger.Printf("error getting signed URL for file with oid %s: %s", drsObject.Id, err) + return false, fmt.Errorf("error getting signed URL for file with oid %s: %s", drsObject.Id, err) } - if signedUrl == nil { + if signedUrl.URL == "" { return false, nil } err = utils.CanDownloadFile(signedUrl.URL) if err != nil { - cl.Logger.Printf("file with oid %s does not exist in bucket: %s", oid, err) + cl.Logger.Printf("file with oid %s does not exist in bucket: %s", drsObject.Id, err) return false, nil } - cl.Logger.Printf("file with oid %s exists in bucket", oid) + cl.Logger.Printf("file with oid %s exists in bucket", drsObject.Id) return true, nil } From 8b1acccf311f2ce8d29d3bb038ea19e9edb73c23 Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Sat, 17 Jan 2026 13:30:53 -0800 Subject: [PATCH 07/40] fix: clarify name --- drsmap/drs_map.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drsmap/drs_map.go b/drsmap/drs_map.go index a90fbd2e..fe804248 100644 --- a/drsmap/drs_map.go +++ b/drsmap/drs_map.go @@ -266,7 +266,7 @@ func DrsUUID(projectId string, hash string) string { return uuid.NewSHA1(NAMESPACE, []byte(hashStr)).String() } -// creates index record from file +// creates drsObject record from file func DrsInfoFromOid(oid string) (*drs.DRSObject, error) { // unmarshal the DRS object path, err := GetObjectPath(projectdir.DRS_OBJS_PATH, oid) @@ -274,18 +274,18 @@ func DrsInfoFromOid(oid string) (*drs.DRSObject, error) { return nil, fmt.Errorf("error getting object path for oid %s: %v", oid, err) } - indexdObjBytes, err := os.ReadFile(path) + drsObjBytes, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("error reading DRS object for oid %s: %v", oid, err) } - var indexdObj drs.DRSObject - err = sonic.ConfigFastest.Unmarshal(indexdObjBytes, &indexdObj) + var drsObject drs.DRSObject + err = sonic.ConfigFastest.Unmarshal(drsObjBytes, &drsObject) if err != nil { return nil, fmt.Errorf("error unmarshaling DRS object for oid %s: %v", oid, err) } - return &indexdObj, nil + return &drsObject, nil } func GetObjectPath(basePath string, oid string) (string, error) { From 8f3cd5b6ea86b48d83c74212a3ae45114df89d88 Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Sat, 17 Jan 2026 13:31:40 -0800 Subject: [PATCH 08/40] fix: optimize CanDownloadFile --- utils/util.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/utils/util.go b/utils/util.go index ee421e89..f684f3a9 100644 --- a/utils/util.go +++ b/utils/util.go @@ -37,20 +37,26 @@ func DrsTopLevel() (string, error) { return filepath.Join(base, DRS_DIR), nil } +// CanDownloadFile checks if a file can be downloaded from the given signed URL +// by issuing a ranged GET for a single byte to mimic HEAD behavior. func CanDownloadFile(signedURL string) error { - // Create an HTTP GET request - resp, err := http.Get(signedURL) + req, err := http.NewRequest("GET", signedURL, nil) if err != nil { - return fmt.Errorf("Error while sending the request: %v\n", err) + return fmt.Errorf("failed to create request: %w", err) + } + req.Header.Set("Range", "bytes=0-0") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return fmt.Errorf("error while sending the request: %v", err) } defer resp.Body.Close() - // Check if the response status is 200 OK - if resp.StatusCode == http.StatusOK { + if resp.StatusCode == http.StatusPartialContent || resp.StatusCode == http.StatusOK { return nil } - return fmt.Errorf("failed to download file, HTTP Status: %d", resp.StatusCode) + return fmt.Errorf("failed to access file, HTTP status: %d", resp.StatusCode) } func ParseEmailFromToken(tokenString string) (string, error) { From a0180e52801485486b591b5d98ab0ec7ec657740 Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Sat, 17 Jan 2026 13:32:36 -0800 Subject: [PATCH 09/40] chore: fixtures-large/ --- tests/monorepos/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/monorepos/.gitignore b/tests/monorepos/.gitignore index 38c9cea7..f70e8970 100644 --- a/tests/monorepos/.gitignore +++ b/tests/monorepos/.gitignore @@ -1,2 +1,3 @@ fixtures/ +fixtures-large/ generate-fixtures From 2af686d7867227cf9a2c1a21dce803cf6545d349 Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Sat, 17 Jan 2026 16:29:28 -0800 Subject: [PATCH 10/40] adds --clone --- tests/monorepos/run-test.sh | 41 ++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/tests/monorepos/run-test.sh b/tests/monorepos/run-test.sh index bb5c68a1..3caeda56 100755 --- a/tests/monorepos/run-test.sh +++ b/tests/monorepos/run-test.sh @@ -12,6 +12,7 @@ PROFILE_DEFAULT="calypr-dev" PROJECT_DEFAULT="cbds-monorepos" GIT_REMOTE_DEFAULT="https://github.com/calypr/monorepo.git" CLEAN_DEFAULT="false" +CLONE_DEFAULT="false" # Parse optional flags (can also be provided via environment variables) while [ $# -gt 0 ]; do @@ -56,6 +57,14 @@ while [ $# -gt 0 ]; do CLEAN="true" shift ;; + --clone=*) + CLONE="${1#*=}" + shift + ;; + --clone) + CLONE="true" + shift + ;; -h|--help) echo "Usage: $0 [--credentials-path PATH] [--profile NAME] [--project NAME] [--clean] [--git-remote NAME]" >&2 exit 0 @@ -72,7 +81,7 @@ PROFILE="${PROFILE:-$PROFILE_DEFAULT}" PROJECT="${PROJECT:-$PROJECT_DEFAULT}" GIT_REMOTE="${GIT_REMOTE:-$GIT_REMOTE_DEFAULT}" CLEAN="${CLEAN:-$CLEAN_DEFAULT}" - +CLONE="${CLONE:-$CLONE_DEFAULT}" IFS='-' read -r PROGRAM PROJECT <<< "$PROJECT" @@ -81,6 +90,7 @@ export PROFILE export PROGRAM export PROJECT export GIT_REMOTE +export CLONE echo "Using CREDENTIALS_PATH=$CREDENTIALS_PATH" >&2 echo "Using PROFILE=$PROFILE" >&2 @@ -88,6 +98,8 @@ echo "Using PROGRAM=$PROGRAM" >&2 echo "Using PROJECT=$PROJECT" >&2 echo "Using GIT_REMOTE=$GIT_REMOTE" >&2 echo "Using CLEAN=$CLEAN" >&2 +echo "Using CLONE=$CLONE" >&2 + if [ "$(basename "$PWD")" != "monorepos" ] || [ "$(basename "$(dirname "$PWD")")" != "tests" ]; then echo 'error: must run from tests/monorepos directory' >&2 @@ -160,6 +172,33 @@ else echo "CLEAN flag not set to true; skipping git state cleanup" >&2 fi +if [ "$CLONE" = "true" ]; then + rm -rf ../clone || true + mkdir ../clone || true + cd ../clone + echo "Cloning remote repository into ../clone" >&2 + # clone into current directory + if ! git clone "$GIT_REMOTE" .; then + echo "error: git clone failed" >&2 + exit 1 + fi + echo "Finished cloning remote repository into `pwd`" >&2 + echo "Verifying contents of TARGET-ALL-P2/sub-directory-1/file-0001.dat:" >&2 + if ! grep -q 'https://git-lfs.github.com/spec/v1' ./TARGET-ALL-P2/sub-directory-1/file-0001.dat; then + echo "error: expected LFS pointer missing in `TARGET-ALL-P2/sub-directory-1/file-0001.dat`" >&2 + exit 1 + fi + echo "Pulling LFS objects from remote" >&2 + git drs init + git lfs pull origin main + if grep -q 'https://git-lfs.github.com/spec/v1' ./TARGET-ALL-P2/sub-directory-1/file-0001.dat; then + echo "error: LFS pointer resolved and data in `TARGET-ALL-P2/sub-directory-1/file-0001.dat`" >&2 + exit 1 + fi + echo "Clone and LFS pull successful" >&2 + exit 0 +fi + # init git repo if not already a git repo if [ -d .git ]; then echo "Git repository already initialized; skipping git init" >&2 From 013ddd83afcf932460665fe27fbcd3c1ae65912f Mon Sep 17 00:00:00 2001 From: Brian Date: Tue, 20 Jan 2026 16:55:33 -0800 Subject: [PATCH 11/40] bug/version-hard-coded #154 (#159) --- cmd/version/main.go | 40 +++++++++++++++++++++++++++++++++++-- cmd/version/version_test.go | 8 ++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/cmd/version/main.go b/cmd/version/main.go index 8870ce4c..855bfb54 100644 --- a/cmd/version/main.go +++ b/cmd/version/main.go @@ -2,6 +2,7 @@ package version import ( "fmt" + "runtime/debug" "github.com/spf13/cobra" ) @@ -12,7 +13,42 @@ var Cmd = &cobra.Command{ Short: "Get version", Long: ``, Run: func(cmd *cobra.Command, args []string) { - var version = "0.5.0" - fmt.Println("git-drs", version) + fmt.Println("git-drs", buildVersion()) }, } + +func buildVersion() string { + tag := "" + commit := "" + if info, ok := debug.ReadBuildInfo(); ok { + if info.Main.Version != "" && info.Main.Version != "(devel)" { + tag = info.Main.Version + } + for _, setting := range info.Settings { + switch setting.Key { + case "vcs.revision": + commit = setting.Value + case "vcs.tag": + if tag == "" { + tag = setting.Value + } + } + } + } + + commitShort := commit + if len(commitShort) > 7 { + commitShort = commitShort[:7] + } + + switch { + case tag != "" && commitShort != "": + return fmt.Sprintf("%s-%s", tag, commitShort) + case tag != "": + return tag + case commitShort != "": + return fmt.Sprintf("dev-%s", commitShort) + default: + return "dev-unknown" + } +} diff --git a/cmd/version/version_test.go b/cmd/version/version_test.go index 3978b017..18e5cfcf 100644 --- a/cmd/version/version_test.go +++ b/cmd/version/version_test.go @@ -119,3 +119,11 @@ func TestVersionCommand_Output(t *testing.T) { }) } } + +func TestBuildVersion(t *testing.T) { + version := buildVersion() + assert.NotEmpty(t, version, "build version should not be empty") + + hasSeparator := strings.Contains(version, ".") || strings.Contains(version, "-") + assert.True(t, hasSeparator, "version should include a separator") +} From ac4c73480708e39f620a3b0b15764a1db4b7add1 Mon Sep 17 00:00:00 2001 From: Brian Date: Tue, 20 Jan 2026 16:57:16 -0800 Subject: [PATCH 12/40] bug/git-drs-list-marshall-error #148 (#158) --- drs/hash/hash.go | 27 +++++++++++++++++++++++++++ drs/hash/hash_test.go | 22 ++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/drs/hash/hash.go b/drs/hash/hash.go index cab91729..9f7ca356 100644 --- a/drs/hash/hash.go +++ b/drs/hash/hash.go @@ -1,5 +1,10 @@ package hash +import ( + "encoding/json" + "fmt" +) + // ChecksumType represents the digest method used to create the checksum type ChecksumType string @@ -62,6 +67,28 @@ type HashInfo struct { ETag string `json:"etag,omitempty"` } +// UnmarshalJSON accepts both the DRS map-based schema and the array-of-checksums schema. +func (h *HashInfo) UnmarshalJSON(data []byte) error { + if string(data) == "null" { + *h = HashInfo{} + return nil + } + + var mapPayload map[string]string + if err := json.Unmarshal(data, &mapPayload); err == nil { + *h = ConvertStringMapToHashInfo(mapPayload) + return nil + } + + var checksumPayload []Checksum + if err := json.Unmarshal(data, &checksumPayload); err == nil { + *h = ConvertChecksumsToHashInfo(checksumPayload) + return nil + } + + return fmt.Errorf("unsupported HashInfo payload: %s", string(data)) +} + func ConvertStringMapToHashInfo(inputHashes map[string]string) HashInfo { hashInfo := HashInfo{} diff --git a/drs/hash/hash_test.go b/drs/hash/hash_test.go index 26f940c3..9529efdb 100644 --- a/drs/hash/hash_test.go +++ b/drs/hash/hash_test.go @@ -1,6 +1,7 @@ package hash import ( + "encoding/json" "reflect" "testing" ) @@ -277,3 +278,24 @@ func TestConvertChecksumsToHashInfo(t *testing.T) { }) } } + +func TestHashInfoUnmarshalJSONChecksumsArray(t *testing.T) { + payload := []byte(`[ + {"checksum":"8f200381b52333426dcad04771eb18f1","type":"md5"}, + {"checksum":"3d0658efb439683ae9649c6213299219","type":"sha256"} + ]`) + + var got HashInfo + if err := json.Unmarshal(payload, &got); err != nil { + t.Fatalf("json.Unmarshal() error = %v", err) + } + + want := HashInfo{ + MD5: "8f200381b52333426dcad04771eb18f1", + SHA256: "3d0658efb439683ae9649c6213299219", + } + + if !reflect.DeepEqual(got, want) { + t.Fatalf("json.Unmarshal() got = %+v, want %+v", got, want) + } +} From 9490ab1a3cc2fc4e6b8a26510efbafcc5a8d4857 Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Tue, 20 Jan 2026 16:28:20 -0800 Subject: [PATCH 13/40] adds upsert; multipart-threshold --- client/indexd/indexd_client.go | 75 +++++++++++++++++++++-------- client/indexd/indexd_client_test.go | 15 ++++-- 2 files changed, 65 insertions(+), 25 deletions(-) diff --git a/client/indexd/indexd_client.go b/client/indexd/indexd_client.go index d88ef535..44c0cbd3 100644 --- a/client/indexd/indexd_client.go +++ b/client/indexd/indexd_client.go @@ -43,7 +43,8 @@ type IndexDClient struct { HttpClient *retryablehttp.Client SConfig sonic.API - ForcePush bool + Upsert bool // whether to force push (upsert) indexd records and file uploads + MultiPartThreshold int64 // threshold for multipart uploads } //////////////////// @@ -100,20 +101,24 @@ func NewIndexDClient(profileConfig conf.Credential, remote Gen3Remote, logger *l retryClient.RetryWaitMin = 5 * time.Second retryClient.RetryWaitMax = 15 * time.Second - forcePush, err := getLfsCustomTransferBool("lfs.customtransfer.drs.force-push", false) + upsert, err := getLfsCustomTransferBool("lfs.customtransfer.drs.upsert", false) if err != nil { return nil, err } + multiPartThresholdInt, err := getLfsCustomTransferInt("lfs.customtransfer.drs.multipart-threshold", 500) + var multiPartThreshold int64 = multiPartThresholdInt * common.MB // default 100 MB + return &IndexDClient{ - Base: baseUrl, - ProjectId: projectId, - BucketName: bucketName, - Logger: logger, - AuthHandler: &RealAuthHandler{profileConfig}, // Use real auth in production - HttpClient: retryClient, - SConfig: sonic.ConfigFastest, - ForcePush: forcePush, + Base: baseUrl, + ProjectId: projectId, + BucketName: bucketName, + Logger: logger, + AuthHandler: &RealAuthHandler{profileConfig}, // Use real auth in production + HttpClient: retryClient, + SConfig: sonic.ConfigFastest, + Upsert: upsert, + MultiPartThreshold: multiPartThreshold, }, nil } @@ -123,6 +128,7 @@ func (cl *IndexDClient) GetProjectId() string { func getLfsCustomTransferBool(key string, defaultValue bool) (bool, error) { defaultText := strconv.FormatBool(defaultValue) + // TODO cache or get all the configs at once? cmd := exec.Command("git", "config", "--get", "--default", defaultText, key) output, err := cmd.Output() if err != nil { @@ -138,6 +144,33 @@ func getLfsCustomTransferBool(key string, defaultValue bool) (bool, error) { return parsed, nil } +func getLfsCustomTransferInt(key string, defaultValue int64) (int64, error) { + defaultText := strconv.FormatInt(defaultValue, 10) + // TODO cache or get all the configs at once? + cmd := exec.Command("git", "config", "--get", "--default", defaultText, key) + output, err := cmd.Output() + if err != nil { + return defaultValue, fmt.Errorf("error reading git config %s: %v", key, err) + } + + value := strings.TrimSpace(string(output)) + + parsed, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return defaultValue, fmt.Errorf("invalid int value for %s: >%q<", key, value) + } + + if parsed < 0 { + return defaultValue, fmt.Errorf("invalid negative int value for %s: %d", key, parsed) + } + + if parsed == 0 || parsed > 500 { + return defaultValue, fmt.Errorf("invalid int value for %s: %d. Must be between 1 and 500", key, parsed) + } + + return parsed, nil +} + // GetProfile extracts the profile from the auth handler if available // This is only needed for external APIs like g3cmd that require it func (cl *IndexDClient) GetProfile() (string, error) { @@ -349,27 +382,27 @@ func (cl *IndexDClient) getDownloadURL(did string, accessId string) (drs.AccessU func (cl *IndexDClient) RegisterFile(oid string) (*drs.DRSObject, error) { cl.Logger.Printf("register file started for oid: %s", oid) - originalForcePush := cl.ForcePush + originalUpsert := cl.Upsert attempts := 1 - if !originalForcePush { + if !originalUpsert { attempts = 2 defer func() { - cl.ForcePush = originalForcePush + cl.Upsert = originalUpsert }() } for attempt := 0; attempt < attempts; attempt++ { if attempt == 1 { - cl.ForcePush = true + cl.Upsert = true } - cl.Logger.Printf("register file attempt %d for oid: %s (force push: %t)", attempt+1, oid, cl.ForcePush) + cl.Logger.Printf("register file attempt %d for oid: %s (force push: %t)", attempt+1, oid, cl.Upsert) var records []drs.DRSObject var err error var drsObject *drs.DRSObject createdRecord := false - if cl.ForcePush { + if cl.Upsert { records, err = cl.GetObjectByHash(&hash.Checksum{Type: hash.ChecksumTypeSHA256, Checksum: oid}) if err != nil { return nil, fmt.Errorf("error querying indexd server for matches to hash %s: %v", oid, err) @@ -397,7 +430,7 @@ func (cl *IndexDClient) RegisterFile(oid string) (*drs.DRSObject, error) { drsObject, err = cl.RegisterIndexdRecord(indexdObj) if err != nil { - if !cl.ForcePush { + if !cl.Upsert { cl.Logger.Printf("error saving indexd record without force push: %s; retrying with force push enabled", err) continue } @@ -425,7 +458,7 @@ func (cl *IndexDClient) RegisterFile(oid string) (*drs.DRSObject, error) { } isDownloadable := false - if cl.ForcePush { + if cl.Upsert { isDownloadable, err = cl.isFileDownloadable(drsObject) if err != nil { return cleanupOnError(err) @@ -470,13 +503,15 @@ func (cl *IndexDClient) RegisterFile(oid string) (*drs.DRSObject, error) { } // TODO - Can we reuse Auth to ensure we are not repeatedly refreshing tokens? - if stat.Size() < 5*common.GB { + if stat.Size() < cl.MultiPartThreshold { + cl.Logger.Printf("UploadSingle for small file %s", filePath) err := upload.UploadSingle(context.Background(), g3.GetCredential().Profile, drsObject.Id, filePath, cl.BucketName, false) if err != nil { cl.Logger.Printf("error uploading single file to bucket: %s", err) return cleanupOnError(fmt.Errorf("error uploading single file to bucket: %s", err)) } } else { + cl.Logger.Printf("MultipartUpload for large file %s", filePath) err = upload.MultipartUpload( context.TODO(), g3, @@ -505,7 +540,7 @@ func (cl *IndexDClient) RegisterFile(oid string) (*drs.DRSObject, error) { } func (cl *IndexDClient) isFileDownloadable(drsObject *drs.DRSObject) (bool, error) { - if !cl.ForcePush { + if !cl.Upsert { cl.Logger.Printf("force push disabled; proceeding to upload oid %s", drsObject.Id) return false, nil } diff --git a/client/indexd/indexd_client_test.go b/client/indexd/indexd_client_test.go index ec5ca55f..1103d24b 100644 --- a/client/indexd/indexd_client_test.go +++ b/client/indexd/indexd_client_test.go @@ -16,6 +16,7 @@ import ( "github.com/bytedance/sonic" "github.com/bytedance/sonic/encoder" + "github.com/calypr/data-client/client/common" "github.com/calypr/data-client/client/conf" "github.com/calypr/git-drs/drs" "github.com/calypr/git-drs/drs/hash" @@ -377,7 +378,8 @@ func TestIndexdClient_NewIndexDClient(t *testing.T) { restore := chdirForTest(t, repoDir) defer restore() - runGit(t, repoDir, "config", "lfs.customtransfer.drs.force-push", "false") + runGit(t, repoDir, "config", "lfs.customtransfer.drs.upsert", "false") + runGit(t, repoDir, "config", "lfs.customtransfer.drs.multipart-threshold", "222") cred := conf.Credential{APIEndpoint: "https://example.com"} remote := Gen3Remote{ProjectID: "project", Bucket: "bucket"} @@ -395,8 +397,11 @@ func TestIndexdClient_NewIndexDClient(t *testing.T) { if indexd.HttpClient.HTTPClient.Timeout != 30*time.Second { t.Fatalf("unexpected http timeout: %v", indexd.HttpClient.HTTPClient.Timeout) } - if indexd.ForcePush { - t.Fatalf("expected force push disabled, got %v", indexd.ForcePush) + if indexd.Upsert { + t.Fatalf("expected force push disabled, got %v", indexd.Upsert) + } + if indexd.MultiPartThreshold != 222*common.MB { + t.Fatalf("expected multipart threshold 222, got %d", indexd.MultiPartThreshold) } } @@ -405,7 +410,7 @@ func TestGetLfsCustomTransferBool_DefaultValue(t *testing.T) { restore := chdirForTest(t, repoDir) defer restore() - value, err := getLfsCustomTransferBool("lfs.customtransfer.drs.force-push", false) + value, err := getLfsCustomTransferBool("lfs.customtransfer.drs.upsert", false) if err != nil { t.Fatalf("getLfsCustomTransferBool error: %v", err) } @@ -419,7 +424,7 @@ func TestGetLfsCustomTransferBool_MissingKeyReturnsDefault(t *testing.T) { restore := chdirForTest(t, repoDir) defer restore() - value, err := getLfsCustomTransferBool("lfs.customtransfer.drs.force-push", false) + value, err := getLfsCustomTransferBool("lfs.customtransfer.drs.upsert", false) if err != nil { t.Fatalf("getLfsCustomTransferBool error: %v", err) } From 0191704e1c284135e42e322c0c0377976ba38ac9 Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Tue, 20 Jan 2026 16:30:07 -0800 Subject: [PATCH 14/40] improve integration tests --- go.mod | 4 +- go.sum | 2 - tests/coverage-test.sh | 119 +++++++++++++++++++++ tests/monorepos/.gitignore | 1 + tests/monorepos/create-20MB-test-file.sh | 35 ++++++ tests/monorepos/create-5GB-test-file.sh | 35 ++++++ tests/monorepos/run-test.sh | 25 ++++- tests/scripts/utils/.gitignore | 1 + tests/scripts/utils/README.md | 37 +++++++ tests/scripts/utils/broken-pipe-finder.sh | 56 ++++++++++ tests/scripts/utils/clean-indexd.sh | 102 ++++++++++++++++++ tests/scripts/utils/create-minio-alias.sh | 52 +++++++++ tests/scripts/utils/delete-s3-by-sha256.sh | 93 ++++++++++++++++ tests/scripts/utils/list-indexd-sha256.sh | 84 +++++++++++++++ tests/scripts/utils/list-indexd.sh | 74 +++++++++++++ tests/scripts/utils/list-s3-by-sha256.sh | 89 +++++++++++++++ tests/scripts/utils/mc-ping-alias.sh | 38 +++++++ 17 files changed, 840 insertions(+), 7 deletions(-) create mode 100755 tests/coverage-test.sh create mode 100755 tests/monorepos/create-20MB-test-file.sh create mode 100755 tests/monorepos/create-5GB-test-file.sh create mode 100644 tests/scripts/utils/.gitignore create mode 100644 tests/scripts/utils/README.md create mode 100755 tests/scripts/utils/broken-pipe-finder.sh create mode 100755 tests/scripts/utils/clean-indexd.sh create mode 100755 tests/scripts/utils/create-minio-alias.sh create mode 100755 tests/scripts/utils/delete-s3-by-sha256.sh create mode 100755 tests/scripts/utils/list-indexd-sha256.sh create mode 100755 tests/scripts/utils/list-indexd.sh create mode 100755 tests/scripts/utils/list-s3-by-sha256.sh create mode 100755 tests/scripts/utils/mc-ping-alias.sh diff --git a/go.mod b/go.mod index 604e7623..2a7a02a5 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.24.2 require ( github.com/bytedance/sonic v1.14.2 - github.com/calypr/data-client v0.0.0-20260113181633-0aaac8b88668 + github.com/calypr/data-client v0.0.0-20260116155232-958f16679e8c github.com/golang-jwt/jwt/v5 v5.3.0 github.com/google/uuid v1.6.0 github.com/hashicorp/go-multierror v1.1.1 @@ -64,3 +64,5 @@ require ( golang.org/x/sys v0.39.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect ) + +replace github.com/calypr/data-client => /Users/walsbr/calypr/data-client diff --git a/go.sum b/go.sum index 8fc7cf20..4f2bcd52 100644 --- a/go.sum +++ b/go.sum @@ -48,8 +48,6 @@ github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPII github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980= github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o= github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= -github.com/calypr/data-client v0.0.0-20260113181633-0aaac8b88668 h1:bECBtUj2EM3YDNZUbZl/TU0nzuDvIUlnB+SOHwtlGpE= -github.com/calypr/data-client v0.0.0-20260113181633-0aaac8b88668/go.mod h1:/GPTQWCRSZpNPoojP/dUsNlqQa4bykRlTnrJRebwqQ4= github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= diff --git a/tests/coverage-test.sh b/tests/coverage-test.sh new file mode 100755 index 00000000..a8e83bda --- /dev/null +++ b/tests/coverage-test.sh @@ -0,0 +1,119 @@ +#!/bin/bash +# coverage-test.sh +# Removes objects from the bucket and indexd records, then runs monorepo tests (clean, normal, clone) twice. +set -euo pipefail + +# echo commands as they are executed +if [ -z "${GIT_TRACE:-}" ]; then + echo "For more verbose git output, consider setting the following environment variables before re-running the script:" >&2 + echo "# export GIT_TRACE=1 GIT_TRANSFER_TRACE=1" >&2 +else + set -x +fi + +# determine the script directory and cd to its parent (project root) +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" +if [ -z "${SCRIPT_DIR:-}" ]; then + echo "error: unable to determine script directory" >&2 + exit 1 +fi +cd "$SCRIPT_DIR/.." || { echo "error: failed to cd to parent of $SCRIPT_DIR" >&2; exit 1; } + + +# Accept named parameters (flags override environment variables) +POD="${POD:-}" +POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-}" +RESOURCE="${RESOURCE:-}" +MINIO_ALIAS="${MINIO_ALIAS:-}" +BUCKET="${BUCKET:-}" + +while [ $# -gt 0 ]; do + case "$1" in + --pod=*) POD="${1#*=}"; shift ;; + --pod) POD="$2"; shift 2 ;; + --postgres-password=*) POSTGRES_PASSWORD="${1#*=}"; shift ;; + --postgres-password) POSTGRES_PASSWORD="$2"; shift 2 ;; + --resource=*) RESOURCE="${1#*=}"; shift ;; + --resource) RESOURCE="$2"; shift 2 ;; + --minio-alias=*) MINIO_ALIAS="${1#*=}"; shift ;; + --minio-alias) MINIO_ALIAS="$2"; shift 2 ;; + --bucket=*) BUCKET="${1#*=}"; shift ;; + --bucket) BUCKET="$2"; shift 2 ;; + -h|--help) + echo "Usage: $0 [--pod POD] [--postgres-password PASS] [--resource RES] [--minio-alias ALIAS] [--bucket BUCKET]" + exit 0 + ;; + *) + break + ;; + esac +done + + +UTIL_DIR="tests/scripts/utils" +MONOREPO_DIR="tests/monorepos" +RUN_TEST="./run-test.sh" + +# helpers +err() { echo "error: $*" >&2; } +run_and_check() { + echo "=== running: $* ===" >&2 + if ! "$@"; then + err "command failed: $*" + exit 1 + fi +} + +# Validate required inputs +if [ -z "$POD" ] || [ -z "$POSTGRES_PASSWORD" ] || [ -z "$RESOURCE" ] || [ -z "$MINIO_ALIAS" ] || [ -z "$BUCKET" ]; then + err "One or more required environment variables are missing. Please set: POD, POSTGRES_PASSWORD, RESOURCE, MINIO_ALIAS, BUCKET" + exit 1 +fi + +# Ensure utilities exist +if [ ! -d "$UTIL_DIR" ]; then + err "utils directory not found: \`$UTIL_DIR\`" + exit 1 +fi + +pushd "$UTIL_DIR" >/dev/null + +# 1) Remove objects from bucket using indexd->s3 list/delete pipeline +echo "Removing bucket objects by sha256 via \`./list-indexd-sha256.sh $POD $RESOURCE | ./list-s3-by-sha256.sh $MINIO_ALIAS $BUCKET\`" >&2 +run_and_check ./list-indexd-sha256.sh "$POD" "$POSTGRES_PASSWORD" "$RESOURCE" \| ./list-s3-by-sha256.sh "$MINIO_ALIAS" "$BUCKET" +echo "Bucket object removal pipeline completed." >&2 + +# 2) Remove indexd records +echo "Removing indexd records via \`./clean-indexd.sh $POD \`" >&2 +run_and_check ./clean-indexd.sh "$POD" "$POSTGRES_PASSWORD" "$RESOURCE" +echo "Indexd cleanup completed." >&2 + +popd >/dev/null + +# Ensure monorepo test runner exists +if [ ! -d "$MONOREPO_DIR" ]; then + err "monorepo tests directory not found: \`$MONOREPO_DIR\`" + exit 1 +fi + +pushd "$MONOREPO_DIR" >/dev/null + +# Run sequence twice: (--clean, normal, --clone) +for pass in 1 2; do + echo "=== Test sequence pass #$pass ===" >&2 + + echo "-> Running: \`$RUN_TEST --clean\`" >&2 + run_and_check "$RUN_TEST" --clean + + echo "-> Running: \`$RUN_TEST\`" >&2 + run_and_check "$RUN_TEST" + + echo "-> Running: \`$RUN_TEST --clone\`" >&2 + run_and_check "$RUN_TEST" --clone + + echo "=== Test sequence pass #$pass completed ===" >&2 +done + +popd >/dev/null + +echo "coverage-test.sh: all steps completed successfully." >&2 diff --git a/tests/monorepos/.gitignore b/tests/monorepos/.gitignore index f70e8970..dffc3360 100644 --- a/tests/monorepos/.gitignore +++ b/tests/monorepos/.gitignore @@ -1,3 +1,4 @@ fixtures/ fixtures-large/ generate-fixtures +clone/ \ No newline at end of file diff --git a/tests/monorepos/create-20MB-test-file.sh b/tests/monorepos/create-20MB-test-file.sh new file mode 100755 index 00000000..a188625d --- /dev/null +++ b/tests/monorepos/create-20MB-test-file.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# bash +set -euo pipefail + +FILE='fixtures/TARGET-ALL-P2/sub-directory-1/file-0003.dat' +LINE='TARGET-ALL-P2/sub-directory-1/file-0003.dat' +TARGET_BYTES=$((20 * 1024 * 1024)) # 5 GiB + +mkdir -p "$(dirname "$FILE")" + +# create an empty file if it doesn't exist +: > "$FILE" + +# make a temporary chunk file (10k lines) to reduce syscalls +TMP_CHUNK="$(mktemp)" +trap 'rm -f "$TMP_CHUNK"' EXIT + +for ((i=0; i<10000; i++)); do + printf '%s\n' "$LINE" >> "$TMP_CHUNK" +done + +# Append chunks until file size exceeds target +while true; do + current_size=$(stat -f%z "$FILE" 2>/dev/null || echo 0) + if [ "$current_size" -gt "$TARGET_BYTES" ]; then + printf 'done: %s is %d bytes\n' "$FILE" "$current_size" >&2 + break + fi + + cat "$TMP_CHUNK" >> "$FILE" + + # optional progress output every iteration + current_size=$(stat -f%z "$FILE") + printf 'progress: %s is %d bytes\n' "$FILE" "$current_size" >&2 +done diff --git a/tests/monorepos/create-5GB-test-file.sh b/tests/monorepos/create-5GB-test-file.sh new file mode 100755 index 00000000..0e4f8aff --- /dev/null +++ b/tests/monorepos/create-5GB-test-file.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# bash +set -euo pipefail + +FILE='fixtures/TARGET-ALL-P2/sub-directory-1/file-0002.dat' +LINE='TARGET-ALL-P2/sub-directory-1/file-0002.dat' +TARGET_BYTES=$((5 * 1024 * 1024 * 1024)) # 5 GiB + +mkdir -p "$(dirname "$FILE")" + +# create an empty file if it doesn't exist +: > "$FILE" + +# make a temporary chunk file (10k lines) to reduce syscalls +TMP_CHUNK="$(mktemp)" +trap 'rm -f "$TMP_CHUNK"' EXIT + +for ((i=0; i<10000; i++)); do + printf '%s\n' "$LINE" >> "$TMP_CHUNK" +done + +# Append chunks until file size exceeds target +while true; do + current_size=$(stat -f%z "$FILE" 2>/dev/null || echo 0) + if [ "$current_size" -gt "$TARGET_BYTES" ]; then + printf 'done: %s is %d bytes\n' "$FILE" "$current_size" >&2 + break + fi + + cat "$TMP_CHUNK" >> "$FILE" + + # optional progress output every iteration + current_size=$(stat -f%z "$FILE") + printf 'progress: %s is %d bytes\n' "$FILE" "$current_size" >&2 +done diff --git a/tests/monorepos/run-test.sh b/tests/monorepos/run-test.sh index 3caeda56..b6cb3e8e 100755 --- a/tests/monorepos/run-test.sh +++ b/tests/monorepos/run-test.sh @@ -3,8 +3,10 @@ # strict set -euo pipefail # echo commands as they are executed -set -x - +if [ "${GIT_TRACE:-}" ]; then + set -x +fi + # Defaults CREDENTIALS_PATH_DEFAULT="$HOME/.gen3/calypr-dev.json" @@ -214,6 +216,13 @@ else # Initialize drs configuration for this repo git drs init -t 16 git drs remote add gen3 "$PROFILE" --cred "$CREDENTIALS_PATH" --bucket cbds --project "$PROGRAM-$PROJECT" --url https://calypr-dev.ohsu.edu + # Set multipart-threshold to 10 (MB) for testing purposes + # Using a smaller threshold to force a multipart upload for testing + # default is 500 (MB) + git config --local lfs.customtransfer.drs.multipart-threshold 10 + # Set multipart-min-chunk-size to 5 (MB) for testing purposes + # Using a smaller chunk size to will force a large + git config --local lfs.customtransfer.drs.multipart-min-chunk-size 5 # verify fixtures/.drs/config.yaml exists if [ ! -f ".drs/config.yaml" ]; then @@ -240,7 +249,11 @@ else echo "Finished init. Force pushing to remote." >&2 git remote -v - GIT_TRACE=1 GIT_TRANSFER_TRACE=1 git push -f origin main 2>&1 | tee lfs-console.log + if [ -z "${GIT_TRACE:-}" ]; then + echo "For more verbose git output, consider setting the following environment variables before re-running the script:" >&2 + echo "# export GIT_TRACE=1 GIT_TRANSFER_TRACE=1" >&2 + fi + git push -f origin main 2>&1 | tee lfs-console.log echo "Finished init. Finished pushing to remote." >&2 exit 0 @@ -261,7 +274,11 @@ for dir in */ ; do git add "$dir" git commit -am "Add $dir" 2>&1 | tee commit.log cat commit.log >> commit-aggregate.log - GIT_TRACE=1 GIT_TRANSFER_TRACE=1 git push origin main 2>&1 | tee lfs-console.log + if [ -z "${GIT_TRACE:-}" ]; then + echo "For more verbose git output, consider setting the following environment variables before re-running the script:" >&2 + echo "# export GIT_TRACE=1 GIT_TRANSFER_TRACE=1" >&2 + fi + git push origin main 2>&1 | tee lfs-console.log echo "##########################################" >> lfs-console.log echo "# finished pushing $dir to remote." >> lfs-console.log # if .drs/lfs/objects exists, log last 3 lines of tree diff --git a/tests/scripts/utils/.gitignore b/tests/scripts/utils/.gitignore new file mode 100644 index 00000000..2eea525d --- /dev/null +++ b/tests/scripts/utils/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/tests/scripts/utils/README.md b/tests/scripts/utils/README.md new file mode 100644 index 00000000..771dfc65 --- /dev/null +++ b/tests/scripts/utils/README.md @@ -0,0 +1,37 @@ +# Repository helper scripts + +This repository includes small helper scripts used for listing and cleaning indexd records and bucket items. + +## Scripts + +- `create-minio-alias.sh` — Configure a MinIO\/S3 alias for `mc` using positional parameters: `alias`, `endpoint`, `access_key`, `secret_key`, optional `--insecure`. +- `list-indexd-sha256` — List indexd records with their did, sha256, file_name and resource. +- `list-s3-by-sha256` — Given a list of sha256 hashes, list S3 URLs for the files. +- `delete-s3-by-sha256` — Given a list of sha256 hashes, delete files from S3 by their sha256. + +## Requirements + +- `git` (repo must be clean or changes will be committed). +- `mc` (MinIO client) for `create-minio-alias.sh`. + +## Credentials +- For `create-minio-alias.sh`, you need access credentials (access key and secret key) for the MinIO or S3-compatible service you are configuring. +- For `list-indexd-sha256`, ensure you have postgres pod and postgres user credentials. +- Create a MinIO alias (positional parameters): + ./create-minio-alias.sh myminio https://play.min.io YOURACCESS YOURSECRET --insecure +The `--insecure` flag is optional and will pass `--insecure` to `mc alias set` to skip TLS verification. + + +## Notes and safety + +- Do not commit secrets. If you store credentials for tests, keep them out of the repository or add the file to `.gitignore`. +- List data first to be careful with destructive commands. + - `list-indexd-sha256` will list the did, sha256, file_name and resource. You can pipe the output of this script into: + - list-s3-by-sha256 to get a list of S3 URLs for the files. + - delete-s3-by-sha256 to delete files from S3 by their sha256. + +## Usage +```bash +./list-indexd-sha256.sh | ./delete-s3-by-sha256.sh +./clean-indexd.sh +``` \ No newline at end of file diff --git a/tests/scripts/utils/broken-pipe-finder.sh b/tests/scripts/utils/broken-pipe-finder.sh new file mode 100755 index 00000000..be18914e --- /dev/null +++ b/tests/scripts/utils/broken-pipe-finder.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash + +# strict +set -euo pipefail + +# echo commands as they are executed +# set -x + +# check parameter is a file +if [ ! -f "$1" ]; then + echo "Usage: $0 \`\`" >&2 + echo "error: \`$1\` does not exist or is not a regular file" >&2 + exit 2 +fi + +# example +# echo '[foo] bar' | sed 's/[][]//g' +# prints: foo bar +# capture START pids into array (portable; works on macOS bash) +STARTED_PIDS=() +while IFS= read -r pid; do + STARTED_PIDS+=("$pid") +done < <(grep "START" "$1" | awk '{print $1}' | sed 's/[][]//g' | sort -n || true) + +# if no started pids were found, print error to stderr and exit non-zero +if [ "${#STARTED_PIDS[@]}" -eq 0 ]; then + echo "Error: no START entries found in $1" >&2 + exit 3 +fi + +echo "Found ${#STARTED_PIDS[@]} started pids" + +# capture pids with "stdin closed" into array (portable) +PROPERLY_CLOSED_PIDS=() +while IFS= read -r pid; do + PROPERLY_CLOSED_PIDS+=("$pid") +done < <(grep "COMPLETED" "$1" | awk '{print $1}' | sed 's/[][]//g' | sort -n | uniq || true) + +echo "Found ${#PROPERLY_CLOSED_PIDS[@]} properly closed pids" + +# compute started-but-not-properly-closed pids (portable) +NOT_CLOSED=() +while IFS= read -r pid; do + NOT_CLOSED+=("$pid") +done < <(comm -23 \ + <(printf "%s\n" "${STARTED_PIDS[@]}" | sort -n | uniq) \ + <(printf "%s\n" "${PROPERLY_CLOSED_PIDS[@]}" | sort -n | uniq) || true) + +for pid in "${NOT_CLOSED[@]}"; do + printf '%s\n' "----- PID: $pid Last activity -----" + grep -F "$pid" "$1" | tail -n 10 || true +done + +#echo "Searching for pids with 'missing authorizations' in $1" +#grep "missing authorizations" $1 | awk '{print $5}' | sed 's/[][]//g' | sort | uniq -c + diff --git a/tests/scripts/utils/clean-indexd.sh b/tests/scripts/utils/clean-indexd.sh new file mode 100755 index 00000000..b48b3240 --- /dev/null +++ b/tests/scripts/utils/clean-indexd.sh @@ -0,0 +1,102 @@ +#!/usr/bin/env bash + +set -euo pipefail + +show_help() { + cat << 'EOF' +cleanup_resource.sh — Delete all Indexd records associated with a given resource path. + +USAGE: + cleanup_resource.sh [resource-name] + +PARAMETERS: + pod-name + The name of the Kubernetes pod running Postgres. + Example: local-postgresql-0 + + postgres-password + The password for the Postgres user inside the pod. + Example: + + resource-name (optional) + The Indexd resource path to delete. + Example: /programs/cbds/projects/monorepos + +OPTIONS: + --help + Show this documentation and exit. + +EXAMPLE: + cleanup_resource.sh local-postgresql-0 + cleanup_resource.sh local-postgresql-0 "/programs/myproj/resource" + +DESCRIPTION: + This script: + • connects to a Postgres pod via kubectl exec + • runs SQL that deletes all Indexd records associated with the resource + • cleans up metadata, hash, url, authz, and main index_record rows + • uses dynamic SQL so the resource path is customizable + +EOF +} + +# Detect --help anywhere +for arg in "$@"; do + if [[ "$arg" == "--help" ]]; then + show_help + exit 0 + fi +done + +# Parameters +POD_NAME="${1:-}" +POSTGRES_PASSWORD="${2:-}" +RESOURCE_NAME="${3:-}" +DATABASE_NAME="indexd_local" + +if [[ -z "$POD_NAME" || -z "$POSTGRES_PASSWORD" || -z "$RESOURCE_NAME" ]]; then + echo "Error: missing required parameters." + echo "Run with --help for usage." + exit 1 +fi + +echo "🔧 Cleaning Indexd records for resource: $RESOURCE_NAME" + +# SQL script with dynamic resource name (variables will be expanded) +SQL=$(cat < [--insecure] + +EXAMPLE: + ./create-minio-alias.sh myminio https://127.0.0.1:9000 minioadmin minioadmin --insecure +HELP +} + +# Detect help anywhere +for a in "$@"; do + if [[ "$a" == "--help" || "$a" == "-h" ]]; then + usage + exit 0 + fi +done + +if (( $# < 4 )); then + echo "Error: missing required parameters." + usage + exit 1 +fi + +ALIAS="$1" +ENDPOINT="$2" +ACCESS_KEY="$3" +SECRET_KEY="$4" + +# optional fifth arg may be --insecure +INSECURE=false +if (( $# >= 5 )) && [[ "${5}" == "--insecure" ]]; then + INSECURE=true +fi + +if ! command -v mc >/dev/null 2>&1; then + echo "Error: mc (MinIO client) not found in PATH." + exit 1 +fi + +if [[ "$INSECURE" == "true" ]]; then + mc alias set "$ALIAS" "$ENDPOINT" "$ACCESS_KEY" "$SECRET_KEY" --insecure --api S3v4 +else + mc alias set "$ALIAS" "$ENDPOINT" "$ACCESS_KEY" "$SECRET_KEY" --api S3v4 +fi + +echo "Alias '$ALIAS' configured for endpoint '$ENDPOINT'." diff --git a/tests/scripts/utils/delete-s3-by-sha256.sh b/tests/scripts/utils/delete-s3-by-sha256.sh new file mode 100755 index 00000000..a71bee4d --- /dev/null +++ b/tests/scripts/utils/delete-s3-by-sha256.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash +set -euo pipefail + +show_help() { + cat << 'HELP' +delete-s3-by-sha256.sh — format sha256 hashes as s3 URLs and delete via the MinIO client. + +USAGE: + delete-s3-by-sha256.sh [prefix] + +PARAMETERS: + mc-alias + The MinIO client alias (as configured by `mc alias set`). + Example: minio + + bucket + The bucket name containing the objects. + Example: drs-objects + + prefix (optional) + Optional object key prefix (no leading slash required). + Example: uploads/ or gen3/ + +OPTIONS: + --help + Show this documentation and exit. + +EXAMPLE: + list-indexd-sha256.sh local-postgresql-0 | + delete-s3-by-sha256.sh minio drs-objects uploads/ + +DESCRIPTION: + This script: + • reads sha256 hashes from stdin + • formats each hash into an s3 URL + • deletes each object using `mc rm` + +HELP +} + +# Detect --help anywhere +for arg in "$@"; do + if [[ "$arg" == "--help" ]]; then + show_help + exit 0 + fi +done + +MC_ALIAS="${1:-}" +BUCKET="${2:-}" +PREFIX="${3:-}" + +if [[ -z "$MC_ALIAS" || -z "$BUCKET" ]]; then + echo "Error: missing required parameters." + echo "Run with --help for usage." + exit 1 +fi + + +if [[ -n "$PREFIX" && "$PREFIX" != */ ]]; then + PREFIX="${PREFIX}/" +fi + + +# Read 4 fields per line: separated by space or tab +while IFS=$' \t' read -r did hash file_name resource; do + # skip empty lines + if [[ -z "${did:-}" ]]; then + echo "No DID found, skipping..." + continue + fi + if [[ -z "${hash:-}" ]]; then + echo "No HASH found, skipping..." + continue + fi + + object_key="${PREFIX}${did}/${hash}" + s3_url="s3://${BUCKET}/${object_key}" + + if [[ "${DEBUG:-}" == "1" ]]; then + echo "$s3_url" + echo "${MC_ALIAS}/${BUCKET}/${object_key}" + fi + # Run mc rm and show output; report based on mc exit status + if mc rm --force "${MC_ALIAS}/${BUCKET}/${object_key}"; then + echo "Deleted: ${resource} ${file_name}" + else + echo "ERROR: Failed to delete ${MC_ALIAS}/${BUCKET}/${object_key}" + fi +done + + + diff --git a/tests/scripts/utils/list-indexd-sha256.sh b/tests/scripts/utils/list-indexd-sha256.sh new file mode 100755 index 00000000..51607c28 --- /dev/null +++ b/tests/scripts/utils/list-indexd-sha256.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +set -euo pipefail + +show_help() { + cat << 'HELP' +list-indexd-sha256.sh — list sha256 hashes for Indexd records associated with a given resource path. + +USAGE: + list-indexd-sha256.sh [resource-name] + +PARAMETERS: + pod-name + The name of the Kubernetes pod running Postgres. + Example: local-postgresql-0 + + postgres-password + The password for the Postgres user inside the pod. + Example: + + resource-name (optional) + The Indexd resource path to list. + Example: /programs/cbds/projects/monorepos + +OPTIONS: + --help + Show this documentation and exit. + +EXAMPLE: + list-indexd-sha256.sh local-postgresql-0 + list-indexd-sha256.sh local-postgresql-0 "/programs/myproj/resource" + +DESCRIPTION: + This script: + • connects to a Postgres pod via kubectl exec + • runs SQL that lists all sha256 hashes for Indexd records associated with the resource + +HELP +} + +# Detect --help anywhere +for arg in "$@"; do + if [[ "$arg" == "--help" ]]; then + show_help + exit 0 + fi +done + +# Parameters +POD_NAME="${1:-}" +POSTGRES_PASSWORD="${2:-}" +RESOURCE_NAME="${3:-}" +DATABASE_NAME="indexd_local" + +if [[ -z "$POD_NAME" || -z "$POSTGRES_PASSWORD" || -z "$RESOURCE_NAME" ]]; then + echo "Error: missing required parameters." + echo "Run with --help for usage." + exit 1 +fi + +# SQL script using psql variable substitution (do not expand $RESOURCE_NAME here) +SQL=$(cat <<'SQL_EOF' +-- List all sha256 hashes associated with the resource (use psql variable substitution) +-- dont show totals or headers +\pset footer off +\pset tuples_only on +-- output as unaligned with no field separator +\pset format unaligned +\pset fieldsep ' ' +-- Select sha256 hashes for the relevant DIDs; :'resource_name' is substituted as a SQL string literal +SELECT h.did, h.hash_value, r.file_name, a.resource + FROM index_record_hash AS h + JOIN index_record_authz AS a + ON a.did = h.did + JOIN index_record AS r + ON r.did = h.did + WHERE a.resource = :'resource_name' + AND h.hash_type = 'sha256'; +SQL_EOF +) + +# Execute SQL inside the pod, passing resource_name via -v so psql does safe quoting +printf '%s\n' "$SQL" | kubectl exec -c postgresql -i "$POD_NAME" -- \ + env PGPASSWORD="$POSTGRES_PASSWORD" \ + psql -U postgres -d "$DATABASE_NAME" -q -v resource_name="$RESOURCE_NAME" -v ON_ERROR_STOP=1 -f - diff --git a/tests/scripts/utils/list-indexd.sh b/tests/scripts/utils/list-indexd.sh new file mode 100755 index 00000000..4db598e9 --- /dev/null +++ b/tests/scripts/utils/list-indexd.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +set -euo pipefail + +show_help() { + cat << 'EOF' +list-indexd.sh — list all Indexd records associated with a given resource path. + +USAGE: + list-indexd.sh [resource-name] + +PARAMETERS: + pod-name + The name of the Kubernetes pod running Postgres. + Example: local-postgresql-0 + + postgres-password + The password for the Postgres user inside the pod. + Example: + + resource-name (optional) + The Indexd resource path to list. + Example: /programs/cbds/projects/monorepos + +OPTIONS: + --help + Show this documentation and exit. + +EXAMPLE: + list-indexd.sh local-postgresql-0 + list-indexd.sh local-postgresql-0 "/programs/myproj/resource" + +DESCRIPTION: + This script: + • connects to a Postgres pod via kubectl exec + • runs SQL that lists all Indexd records associated with the resource + +EOF +} + +# Detect --help anywhere +for arg in "$@"; do + if [[ "$arg" == "--help" ]]; then + show_help + exit 0 + fi +done + +# Parameters +POD_NAME="${1:-}" +POSTGRES_PASSWORD="${2:-}" +RESOURCE_NAME="${3:-}" +DATABASE_NAME="indexd_local" + +if [[ -z "$POD_NAME" || -z "$POSTGRES_PASSWORD" || -z "$RESOURCE_NAME" ]]; then + echo "Error: missing required parameters." + echo "Run with --help for usage." + exit 1 +fi + +# SQL script using psql variable substitution (do not expand $RESOURCE_NAME here) +SQL=$(cat <<'EOF' +-- List all records associated with the resource (use psql variable substitution) +-- dont show totals or headers +\pset footer off +\pset tuples_only on +-- Select the relevant DIDs; :'resource_name' is substituted as a SQL string literal +SELECT did FROM index_record_authz WHERE resource = :'resource_name'; +EOF +) + +# Execute SQL inside the pod, passing resource_name via -v so psql does safe quoting +printf '%s\n' "$SQL" | kubectl exec -i "$POD_NAME" -- \ + env PGPASSWORD="$POSTGRES_PASSWORD" \ + psql -U postgres -d "$DATABASE_NAME" -v resource_name="$RESOURCE_NAME" -v ON_ERROR_STOP=1 -f - diff --git a/tests/scripts/utils/list-s3-by-sha256.sh b/tests/scripts/utils/list-s3-by-sha256.sh new file mode 100755 index 00000000..797d63f9 --- /dev/null +++ b/tests/scripts/utils/list-s3-by-sha256.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +set -euo pipefail + +show_help() { + cat << 'HELP' +list-s3-by-sha256.sh — format sha256 hashes as s3 URLs and list via the MinIO client. + +USAGE: + list-s3-by-sha256.sh [prefix] + +PARAMETERS: + mc-alias + The MinIO client alias (as configured by `mc alias set`). + Example: minio + + bucket + The bucket name containing the objects. + Example: drs-objects + + prefix (optional) + Optional object key prefix (no leading slash required). + Example: uploads/ or gen3/ + +OPTIONS: + --help + Show this documentation and exit. + +EXAMPLE: + list-indexd-sha256.sh local-postgresql-0 | + list-s3-by-sha256.sh minio drs-objects uploads/ + +DESCRIPTION: + This script: + • reads sha256 hashes from stdin + • formats each hash into an s3 URL + • runs `mc ls` for each object to show metadata or an error +HELP +} + +# Detect --help anywhere +for arg in "$@"; do + if [[ "$arg" == "--help" || "$arg" == "-h" ]]; then + show_help + exit 0 + fi +done + +MC_ALIAS="${1:-}" +BUCKET="${2:-}" +PREFIX="${3:-}" + +if [[ -z "$MC_ALIAS" || -z "$BUCKET" ]]; then + echo "Error: missing required parameters." + echo "Run with --help for usage." + exit 1 +fi + +if [[ -n "$PREFIX" && "$PREFIX" != */ ]]; then + PREFIX="${PREFIX}/" +fi + +if ! command -v mc >/dev/null 2>&1; then + echo "Error: mc (MinIO client) not found in PATH." + exit 2 +fi + + +# Read 4 fields per line: separated by space or tab +while IFS=$' \t' read -r did hash file_name resource; do + # skip empty lines + if [[ -z "${did:-}" ]]; then + echo "No DID found, skipping..." + continue + fi + if [[ -z "${hash:-}" ]]; then + echo "No HASH found, skipping..." + continue + fi + + object_key="${PREFIX}${did}/${hash}" + s3_url="s3://${BUCKET}/${object_key}" + + # capture mc ls output, append file_name on success; on failure print error plus file_name + if output=$(mc ls "${MC_ALIAS}/${BUCKET}/${object_key}" 2>/dev/null); then + printf '%s %s %s %s\n' "$output" "$file_name" "$resource" "$object_key" + else + printf 'ERROR: alias %s object %s not found or unreachable %s\n' "$MC_ALIAS" "$object_key" "$file_name" + fi +done diff --git a/tests/scripts/utils/mc-ping-alias.sh b/tests/scripts/utils/mc-ping-alias.sh new file mode 100755 index 00000000..4c8bb8c0 --- /dev/null +++ b/tests/scripts/utils/mc-ping-alias.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +set -u -o pipefail + +usage() { + cat <<'USAGE' +Usage: ./mc-ping-alias.sh [--insecure] + +Example: + ./mc-ping-alias.sh myminio --insecure +USAGE +} + +if (( $# < 1 )); then + usage + exit 1 +fi + +ALIAS="$1" +INSEC_FLAG="" +if [[ "${2:-}" == "--insecure" ]]; then + INSEC_FLAG="--insecure" +fi + +if ! command -v mc >/dev/null 2>&1; then + echo "Error: mc (MinIO client) not found in PATH." + exit 2 +fi + +# Try a simple listing on the alias root to verify connectivity. +# Suppress normal output; on failure re-run without suppression to show error details. +if mc "${INSEC_FLAG}" ls "${ALIAS}" >/dev/null 2>&1; then + echo "OK: alias '${ALIAS}' reachable" + exit 0 +else + echo "ERROR: alias '${ALIAS}' unreachable - showing details:" + mc "${INSEC_FLAG}" ls "${ALIAS}" || true + exit 3 +fi From 82def5b3e984d936ec818387a7f311ef92bb411c Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Tue, 20 Jan 2026 17:08:22 -0800 Subject: [PATCH 15/40] bump: data_client --- go.mod | 4 ++-- go.sum | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 2a7a02a5..e8102f31 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.24.2 require ( github.com/bytedance/sonic v1.14.2 - github.com/calypr/data-client v0.0.0-20260116155232-958f16679e8c + github.com/calypr/data-client v0.0.0-20260121002104-bfba83c8cd17 github.com/golang-jwt/jwt/v5 v5.3.0 github.com/google/uuid v1.6.0 github.com/hashicorp/go-multierror v1.1.1 @@ -65,4 +65,4 @@ require ( gopkg.in/ini.v1 v1.67.0 // indirect ) -replace github.com/calypr/data-client => /Users/walsbr/calypr/data-client +//replace github.com/calypr/data-client => /Users/walsbr/calypr/data-client diff --git a/go.sum b/go.sum index 4f2bcd52..36b27e8c 100644 --- a/go.sum +++ b/go.sum @@ -48,6 +48,8 @@ github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPII github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980= github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o= github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/calypr/data-client v0.0.0-20260121002104-bfba83c8cd17 h1:pd9+D3YhP7M4C9H9NNLuFk+/vbn5RtNmElJgc30MYHg= +github.com/calypr/data-client v0.0.0-20260121002104-bfba83c8cd17/go.mod h1:/GPTQWCRSZpNPoojP/dUsNlqQa4bykRlTnrJRebwqQ4= github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= From beb131c058640337b25c549a41a0131b3061f584 Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Tue, 20 Jan 2026 17:11:59 -0800 Subject: [PATCH 16/40] document multipart-min-chunk-size --- tests/monorepos/run-test.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/monorepos/run-test.sh b/tests/monorepos/run-test.sh index b6cb3e8e..daeb450f 100755 --- a/tests/monorepos/run-test.sh +++ b/tests/monorepos/run-test.sh @@ -221,8 +221,9 @@ else # default is 500 (MB) git config --local lfs.customtransfer.drs.multipart-threshold 10 # Set multipart-min-chunk-size to 5 (MB) for testing purposes - # Using a smaller chunk size to will force a large - git config --local lfs.customtransfer.drs.multipart-min-chunk-size 5 + # Using a smaller chunk size to will force a large number of parts for testing + # To test this, you will need to disable data_clients.OptimalChunkSize + # git config --local lfs.customtransfer.drs.multipart-min-chunk-size 5 # verify fixtures/.drs/config.yaml exists if [ ! -f ".drs/config.yaml" ]; then From 8240700d1d2efc3bb48fa927336d479a9dd75e8a Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Tue, 20 Jan 2026 17:16:17 -0800 Subject: [PATCH 17/40] deprecate:ensureDrsObjectInRecords --- client/indexd/indexd_client.go | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/client/indexd/indexd_client.go b/client/indexd/indexd_client.go index 44c0cbd3..ff3ba560 100644 --- a/client/indexd/indexd_client.go +++ b/client/indexd/indexd_client.go @@ -564,20 +564,6 @@ func (cl *IndexDClient) isFileDownloadable(drsObject *drs.DRSObject) (bool, erro return true, nil } -func ensureDrsObjectInRecords(records []drs.DRSObject, drsObject *drs.DRSObject) []drs.DRSObject { - if drsObject == nil { - return records - } - - for _, record := range records { - if record.Id == drsObject.Id { - return records - } - } - - return append(records, *drsObject) -} - func (cl *IndexDClient) GetObject(id string) (*drs.DRSObject, error) { a := *cl.Base From af9c91b4bd06c884febbe380ca6a21da75bfc101 Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Tue, 20 Jan 2026 18:23:39 -0800 Subject: [PATCH 18/40] PR feedback --- client/indexd/indexd_client.go | 31 ++-- client/indexd/tests/client_read_test.go | 226 ------------------------ 2 files changed, 19 insertions(+), 238 deletions(-) diff --git a/client/indexd/indexd_client.go b/client/indexd/indexd_client.go index ff3ba560..65c3ac0b 100644 --- a/client/indexd/indexd_client.go +++ b/client/indexd/indexd_client.go @@ -303,12 +303,7 @@ func (cl *IndexDClient) getDownloadURLFromRecords(oid string, records []drs.DRSO } cl.Logger.Printf("Matching record: %#v for oid %s", matchingRecord, oid) - // Get the DRS object for the matching record - drsObj, err := cl.GetObject(matchingRecord.Id) - if err != nil { - cl.Logger.Printf("error getting DRS object for matching record %s: %s", matchingRecord.Id, err) - return nil, fmt.Errorf("error getting DRS object for matching record %s: %v", matchingRecord.Id, err) - } + drsObj := matchingRecord // Check if access methods exist if len(drsObj.AccessMethods) == 0 { @@ -317,10 +312,14 @@ func (cl *IndexDClient) getDownloadURLFromRecords(oid string, records []drs.DRSO } // naively get access ID from splitting first path into : - accessId := drsObj.AccessMethods[0].AccessID + accessType := drsObj.AccessMethods[0].Type + if accessType == "" { + cl.Logger.Printf("no accessType found in access method for DRS object %v", drsObj.AccessMethods[0]) + return nil, fmt.Errorf("no accessType found in access method for DRS object %v", drsObj.AccessMethods[0]) + } did := drsObj.Id - accessUrl, err := cl.getDownloadURL(did, accessId) + accessUrl, err := cl.getDownloadURL(did, accessType) if err != nil { return nil, err } @@ -348,9 +347,17 @@ func (cl *IndexDClient) getDownloadURL(did string, accessId string) (drs.AccessU if err != nil { return drs.AccessURL{}, fmt.Errorf("error getting signed URL: %v", err) } - defer func(Body io.ReadCloser) { - _ = Body.Close() - }(response.Body) + defer func() { + closeErr := response.Body.Close() + if err == nil { + // If no error has happened yet, take the close error + err = closeErr + } else if closeErr != nil { + // If an error already exists, you can log the close error + // or wrap it so you don't lose the original context + err = fmt.Errorf("%w; additionally, body close failed: %v", err, closeErr) + } + }() accessUrl := drs.AccessURL{} @@ -360,7 +367,7 @@ func (cl *IndexDClient) getDownloadURL(did string, accessId string) (drs.AccessU return drs.AccessURL{}, fmt.Errorf("unable to read response body: %v", readErr) } - if err := cl.SConfig.NewDecoder(bytes.NewReader(bodyBytes)).Decode(&accessUrl); err != nil { + if err := cl.SConfig.Unmarshal(bodyBytes, &accessUrl); err != nil { return drs.AccessURL{}, fmt.Errorf("unable to decode response into drs.AccessURL: %v; body: %s", err, string(bodyBytes)) } diff --git a/client/indexd/tests/client_read_test.go b/client/indexd/tests/client_read_test.go index f400e72d..51857e1a 100644 --- a/client/indexd/tests/client_read_test.go +++ b/client/indexd/tests/client_read_test.go @@ -1,16 +1,9 @@ package indexd_tests import ( - "fmt" "testing" - "github.com/bytedance/sonic" - indexd_client "github.com/calypr/git-drs/client/indexd" - "github.com/calypr/git-drs/drs" "github.com/calypr/git-drs/drs/hash" - "github.com/calypr/git-drs/drslog" - "github.com/calypr/git-drs/s3_utils" - "github.com/hashicorp/go-retryablehttp" "github.com/stretchr/testify/require" ) @@ -102,225 +95,6 @@ func TestIndexdClient_GetObjectsByHash(t *testing.T) { require.Len(t, emptyResults, 0) } -/////////////////////////////// -// GetDownloadURL Tests -/////////////////////////////// - -// TestIndexdClient_GetDownloadURL tests the complete flow of getting a signed download URL -// Flow: GetObjectsByHash -> FindMatchingRecord -> GetObject -> Extract AccessID -> Get Signed URL -func TestIndexdClient_GetDownloadURL(t *testing.T) { - tests := []struct { - name string - oid string - setupMockData func(*MockIndexdServer) *MockIndexdRecord // Return record for validation - expectError string - validateAccessURL func(*testing.T, *drs.AccessURL, *MockIndexdRecord) - }{ - { - name: "successful download URL retrieval", - oid: testSHA256Hash, - setupMockData: func(server *MockIndexdServer) *MockIndexdRecord { - // Add a record that matches the test project - record := newTestRecord("test-did-123") - addRecordWithHashIndex(server, record, "sha256", testSHA256Hash) - return record - }, - validateAccessURL: func(t *testing.T, accessURL *drs.AccessURL, record *MockIndexdRecord) { - require.NotNil(t, accessURL) - require.NotEmpty(t, accessURL.URL) - // The mock server creates signed URLs in the format: https://signed-url.example.com/{did}/{accessId} - require.Contains(t, accessURL.URL, "https://signed-url.example.com/"+record.Did+"/") - }, - }, - { - name: "no records found for hash", - oid: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", - setupMockData: func(server *MockIndexdServer) *MockIndexdRecord { - // No records added - will return empty response - return nil - }, - expectError: "no DRS object found for OID bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", - }, - { - name: "successful download with matching project", - oid: "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", - setupMockData: func(server *MockIndexdServer) *MockIndexdRecord { - // Add record with matching project authorization - record := newTestRecord("test-did-456", - withTestRecordFileName("matching.bam"), - withTestRecordSize(2048), - withTestRecordHash("sha256", "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc")) - addRecordWithHashIndex(server, record, "sha256", record.Hashes["sha256"]) - return record - }, - validateAccessURL: func(t *testing.T, accessURL *drs.AccessURL, record *MockIndexdRecord) { - require.NotNil(t, accessURL) - require.NotEmpty(t, accessURL.URL) - require.Contains(t, accessURL.URL, "https://signed-url.example.com/"+record.Did+"/") - }, - }, - { - name: "successful second download URL with different hash", - oid: "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", - setupMockData: func(server *MockIndexdServer) *MockIndexdRecord { - // Add another valid record with different hash - record := newTestRecord("test-did-789", - withTestRecordFileName("second.bam"), - withTestRecordSize(512), - withTestRecordHash("sha256", "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd")) - addRecordWithHashIndex(server, record, "sha256", record.Hashes["sha256"]) - return record - }, - validateAccessURL: func(t *testing.T, accessURL *drs.AccessURL, record *MockIndexdRecord) { - require.NotNil(t, accessURL) - require.NotEmpty(t, accessURL.URL) - require.Contains(t, accessURL.URL, "https://signed-url.example.com/"+record.Did+"/") - }, - }, - { - name: "auth handler returns error", - oid: "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", - setupMockData: func(server *MockIndexdServer) *MockIndexdRecord { - record := newTestRecord("test-did-auth-error", - withTestRecordFileName("auth-error.bam"), - withTestRecordHash("sha256", "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee")) - addRecordWithHashIndex(server, record, "sha256", record.Hashes["sha256"]) - return record - }, - expectError: "error getting DRS object for OID eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Setup mock server - mockServer := NewMockIndexdServer(t) - defer mockServer.Close() - record := tt.setupMockData(mockServer) - - // Create client with appropriate auth handler - var authHandler s3_utils.AuthHandler = &MockAuthHandler{} - if tt.name == "auth handler returns error" { - authHandler = &testErrorAuthHandler{err: fmt.Errorf("auth failed")} - } - - client := &indexd_client.IndexDClient{ - Base: parseURL(mockServer.URL()), - ProjectId: "test-project", // This will become /programs/test/projects/project - BucketName: "test-bucket", - Logger: drslog.NewNoOpLogger(), - AuthHandler: authHandler, - HttpClient: &retryablehttp.Client{}, - SConfig: sonic.ConfigFastest, - } - - // Execute method under test - result, err := client.GetDownloadURL(tt.oid) - - // Validate results - if tt.expectError != "" { - require.Error(t, err) - require.Contains(t, err.Error(), tt.expectError) - require.Nil(t, result) - } else { - require.NoError(t, err) - require.NotNil(t, result) - if tt.validateAccessURL != nil { - tt.validateAccessURL(t, result, record) - } - } - }) - } -} - -// TestIndexdClient_GetDownloadURL_EdgeCases tests edge cases and error handling scenarios -func TestIndexdClient_GetDownloadURL_EdgeCases(t *testing.T) { - t.Run("empty OID should still make request", func(t *testing.T) { - mockServer := NewMockIndexdServer(t) - defer mockServer.Close() - - client := testIndexdClientWithMockAuth(mockServer.URL()) - - result, err := client.GetDownloadURL("") - - // Should fail because no records exist for empty hash - require.Error(t, err) - require.Nil(t, result) - require.Contains(t, err.Error(), "no DRS object found for OID") - }) - - t.Run("OID with special characters causes URL parse error", func(t *testing.T) { - mockServer := NewMockIndexdServer(t) - defer mockServer.Close() - - client := testIndexdClientWithMockAuth(mockServer.URL()) - specialOid := "test-hash-with-special-chars!@#$%" - - result, err := client.GetDownloadURL(specialOid) - - require.Error(t, err) - require.Nil(t, result) - require.Contains(t, err.Error(), "invalid URL escape") - }) -} - -// TestIndexdClient_GetDownloadURL_PanicScenarios tests error conditions that previously caused panics -// These tests verify that the bugs have been fixed and now return proper errors instead of panicking. -func TestIndexdClient_GetDownloadURL_PanicScenarios(t *testing.T) { - t.Run("no matching record returns proper error", func(t *testing.T) { - mockServer := NewMockIndexdServer(t) - defer mockServer.Close() - - // Add record with different project authorization that won't match - differentProjectAuthz := "/programs/other/projects/other-project" - testHash := "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" - record := newTestRecord("test-did-different-project", - withTestRecordFileName("other.bam"), - withTestRecordSize(2048), - withTestRecordURLs("s3://other-bucket/other.bam"), - withTestRecordHash("sha256", testHash)) - record.Authz = []string{differentProjectAuthz} // Override with different project - addRecordWithHashIndex(mockServer, record, "sha256", testHash) - - client := testIndexdClientWithMockAuth(mockServer.URL()) - - // This should return error - result, err := client.GetDownloadURL(testHash) - - // Verify proper error handling - require.Error(t, err) - require.Nil(t, result) - require.Contains(t, err.Error(), "no matching record found for project") - }) - - t.Run("no access methods returns proper error", func(t *testing.T) { - mockServer := NewMockIndexdServer(t) - defer mockServer.Close() - - // Add record with no URLs (which creates DRS object with no access methods) - // Note: A record with no URLs can't be matched by project because FindMatchingRecord - // requires access methods with authorizations. So this will fail at the matching stage. - testHash := "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" - record := newTestRecord("test-did-no-access", - withTestRecordFileName("no-access.bam"), - withTestRecordSize(512), - withTestRecordURLs(), // Empty URLs means no access methods - withTestRecordHash("sha256", testHash)) - addRecordWithHashIndex(mockServer, record, "sha256", testHash) - - client := testIndexdClientWithMockAuth(mockServer.URL()) - - // This should return an error about no matching record - // (because records without access methods can't be matched by project) - result, err := client.GetDownloadURL(testHash) - - // Verify proper error handling - require.Error(t, err) - require.Nil(t, result) - require.Contains(t, err.Error(), "no matching record found for project") - }) -} - /////////////////////////////// // GetProjectId Tests /////////////////////////////// From 7b60785468e99f5cf5cdd378bc69dd106c88c2c4 Mon Sep 17 00:00:00 2001 From: Brian Date: Tue, 20 Jan 2026 18:28:04 -0800 Subject: [PATCH 19/40] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- go.mod | 2 -- 1 file changed, 2 deletions(-) diff --git a/go.mod b/go.mod index e8102f31..1d4e0b0e 100644 --- a/go.mod +++ b/go.mod @@ -64,5 +64,3 @@ require ( golang.org/x/sys v0.39.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect ) - -//replace github.com/calypr/data-client => /Users/walsbr/calypr/data-client From 11007056aad3f0bb2b3330f8442ad57517cf726c Mon Sep 17 00:00:00 2001 From: Brian Date: Tue, 20 Jan 2026 18:28:23 -0800 Subject: [PATCH 20/40] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/coverage-test.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/coverage-test.sh b/tests/coverage-test.sh index a8e83bda..48a80d8e 100755 --- a/tests/coverage-test.sh +++ b/tests/coverage-test.sh @@ -80,7 +80,10 @@ pushd "$UTIL_DIR" >/dev/null # 1) Remove objects from bucket using indexd->s3 list/delete pipeline echo "Removing bucket objects by sha256 via \`./list-indexd-sha256.sh $POD $RESOURCE | ./list-s3-by-sha256.sh $MINIO_ALIAS $BUCKET\`" >&2 -run_and_check ./list-indexd-sha256.sh "$POD" "$POSTGRES_PASSWORD" "$RESOURCE" \| ./list-s3-by-sha256.sh "$MINIO_ALIAS" "$BUCKET" +if ! ./list-indexd-sha256.sh "$POD" "$POSTGRES_PASSWORD" "$RESOURCE" | ./list-s3-by-sha256.sh "$MINIO_ALIAS" "$BUCKET"; then + err "command failed: ./list-indexd-sha256.sh \"$POD\" \"$POSTGRES_PASSWORD\" \"$RESOURCE\" | ./list-s3-by-sha256.sh \"$MINIO_ALIAS\" \"$BUCKET\"" + exit 1 +fi echo "Bucket object removal pipeline completed." >&2 # 2) Remove indexd records From 5a89834c9f1086a9e01d8bd42ba9fd475001f740 Mon Sep 17 00:00:00 2001 From: Brian Date: Tue, 20 Jan 2026 18:28:49 -0800 Subject: [PATCH 21/40] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/scripts/utils/mc-ping-alias.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/scripts/utils/mc-ping-alias.sh b/tests/scripts/utils/mc-ping-alias.sh index 4c8bb8c0..e295befa 100755 --- a/tests/scripts/utils/mc-ping-alias.sh +++ b/tests/scripts/utils/mc-ping-alias.sh @@ -28,11 +28,16 @@ fi # Try a simple listing on the alias root to verify connectivity. # Suppress normal output; on failure re-run without suppression to show error details. -if mc "${INSEC_FLAG}" ls "${ALIAS}" >/dev/null 2>&1; then +CMD=(mc) +if [[ -n "${INSEC_FLAG}" ]]; then + CMD+=("${INSEC_FLAG}") +fi +CMD+=(ls "${ALIAS}") +if "${CMD[@]}" >/dev/null 2>&1; then echo "OK: alias '${ALIAS}' reachable" exit 0 else echo "ERROR: alias '${ALIAS}' unreachable - showing details:" - mc "${INSEC_FLAG}" ls "${ALIAS}" || true + "${CMD[@]}" || true exit 3 fi From fc03ab9225b88489892125085e9090ad99b6a20a Mon Sep 17 00:00:00 2001 From: Brian Date: Tue, 20 Jan 2026 18:29:21 -0800 Subject: [PATCH 22/40] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/monorepos/create-20MB-test-file.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/monorepos/create-20MB-test-file.sh b/tests/monorepos/create-20MB-test-file.sh index a188625d..78cc2705 100755 --- a/tests/monorepos/create-20MB-test-file.sh +++ b/tests/monorepos/create-20MB-test-file.sh @@ -4,7 +4,7 @@ set -euo pipefail FILE='fixtures/TARGET-ALL-P2/sub-directory-1/file-0003.dat' LINE='TARGET-ALL-P2/sub-directory-1/file-0003.dat' -TARGET_BYTES=$((20 * 1024 * 1024)) # 5 GiB +TARGET_BYTES=$((20 * 1024 * 1024)) # 20 MiB mkdir -p "$(dirname "$FILE")" From 15f28b22250a7cab3c8ada9cd512c31eae5208da Mon Sep 17 00:00:00 2001 From: Brian Date: Tue, 20 Jan 2026 18:29:35 -0800 Subject: [PATCH 23/40] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/indexd-registerfile-upsert.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/indexd-registerfile-upsert.md b/docs/indexd-registerfile-upsert.md index f5740ecb..06b61c0c 100644 --- a/docs/indexd-registerfile-upsert.md +++ b/docs/indexd-registerfile-upsert.md @@ -11,7 +11,7 @@ The Indexd `RegisterFile` flow needs toggles for: These toggles must be controlled per-repository using git LFS configuration (`git config` entries under `lfs.customtransfer.drs.*`). This keeps behavior in repo-local configuration and avoids coupling to remote YAML configuration. ## Decision -Read `lfs.customtransfer.drs.force-push` from git config during Indexd client initialization. Missing values default to `false`. Invalid values fail initialization with a clear error. +Read `lfs.customtransfer.drs.upsert` from git config during Indexd client initialization. Missing values default to `false`. Invalid values fail initialization with a clear error. ## Before From b85407facc11ccb14a3f2e0c04a1b0995adf1eeb Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Wed, 21 Jan 2026 05:42:00 -0800 Subject: [PATCH 24/40] adds --upsert --- tests/monorepos/run-test.sh | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/tests/monorepos/run-test.sh b/tests/monorepos/run-test.sh index daeb450f..d06882ca 100755 --- a/tests/monorepos/run-test.sh +++ b/tests/monorepos/run-test.sh @@ -15,6 +15,7 @@ PROJECT_DEFAULT="cbds-monorepos" GIT_REMOTE_DEFAULT="https://github.com/calypr/monorepo.git" CLEAN_DEFAULT="false" CLONE_DEFAULT="false" +UPSERT_DEFAULT="false" # Parse optional flags (can also be provided via environment variables) while [ $# -gt 0 ]; do @@ -67,8 +68,16 @@ while [ $# -gt 0 ]; do CLONE="true" shift ;; + --upsert=*) + UPSERT="${1#*=}" + shift + ;; + --upsert) + UPSERT="true" + shift + ;; -h|--help) - echo "Usage: $0 [--credentials-path PATH] [--profile NAME] [--project NAME] [--clean] [--git-remote NAME]" >&2 + echo "Usage: $0 [--credentials-path PATH] [--profile NAME] [--project NAME] [--clean] [--git-remote NAME] [--upsert]" >&2 exit 0 ;; *) @@ -84,6 +93,7 @@ PROJECT="${PROJECT:-$PROJECT_DEFAULT}" GIT_REMOTE="${GIT_REMOTE:-$GIT_REMOTE_DEFAULT}" CLEAN="${CLEAN:-$CLEAN_DEFAULT}" CLONE="${CLONE:-$CLONE_DEFAULT}" +UPSERT="${UPSERT:-$UPSERT_DEFAULT}" IFS='-' read -r PROGRAM PROJECT <<< "$PROJECT" @@ -93,6 +103,7 @@ export PROGRAM export PROJECT export GIT_REMOTE export CLONE +export UPSERT echo "Using CREDENTIALS_PATH=$CREDENTIALS_PATH" >&2 echo "Using PROFILE=$PROFILE" >&2 @@ -101,6 +112,7 @@ echo "Using PROJECT=$PROJECT" >&2 echo "Using GIT_REMOTE=$GIT_REMOTE" >&2 echo "Using CLEAN=$CLEAN" >&2 echo "Using CLONE=$CLONE" >&2 +echo "Using UPSERT=$UPSERT" >&2 if [ "$(basename "$PWD")" != "monorepos" ] || [ "$(basename "$(dirname "$PWD")")" != "tests" ]; then @@ -220,11 +232,21 @@ else # Using a smaller threshold to force a multipart upload for testing # default is 500 (MB) git config --local lfs.customtransfer.drs.multipart-threshold 10 + # Set multipart-min-chunk-size to 5 (MB) for testing purposes # Using a smaller chunk size to will force a large number of parts for testing - # To test this, you will need to disable data_clients.OptimalChunkSize + # To test this, you will need to disable data_clients.OptimalChunkSize in code + # We used this to test a 5GB+ file upload with many parts which causes a minio error # git config --local lfs.customtransfer.drs.multipart-min-chunk-size 5 + # Enable upsert for testing purposes, when adding files to indexd, if the object already exists, delete and re-add it + if [ "$UPSERT" = "true" ]; then + git config --local lfs.customtransfer.drs.upsert true + echo "UPSERT is enabled; set lfs.customtransfer.drs.upsert to true" >&2 + else + echo "UPSERT is disabled; not setting lfs.customtransfer.drs.upsert" >&2 + fi + # verify fixtures/.drs/config.yaml exists if [ ! -f ".drs/config.yaml" ]; then echo "error: .drs/config.yaml not found after git drs init" >&2 From 8bdd9e7974315c69c7e8732fedec9eb2523db529 Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Wed, 21 Jan 2026 05:42:18 -0800 Subject: [PATCH 25/40] adds --upsert --- tests/coverage-test.sh | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/tests/coverage-test.sh b/tests/coverage-test.sh index 48a80d8e..f0ff003c 100755 --- a/tests/coverage-test.sh +++ b/tests/coverage-test.sh @@ -3,6 +3,7 @@ # Removes objects from the bucket and indexd records, then runs monorepo tests (clean, normal, clone) twice. set -euo pipefail + # echo commands as they are executed if [ -z "${GIT_TRACE:-}" ]; then echo "For more verbose git output, consider setting the following environment variables before re-running the script:" >&2 @@ -19,6 +20,17 @@ if [ -z "${SCRIPT_DIR:-}" ]; then fi cd "$SCRIPT_DIR/.." || { echo "error: failed to cd to parent of $SCRIPT_DIR" >&2; exit 1; } +# lint +go vet ./... +gofmt -s -w . + +# build to ensure no compile errors +go build + +# unit tests +go test -v -race -coverprofile=coverage.out -covermode=atomic -coverpkg=./... $(go list ./... | grep -vE 'tests/integration/calypr|client/indexd/tests') | grep FAIL && echo "unit tests failed" >&2 && exit 1 + + # Accept named parameters (flags override environment variables) POD="${POD:-}" @@ -102,15 +114,26 @@ fi pushd "$MONOREPO_DIR" >/dev/null # Run sequence twice: (--clean, normal, --clone) +# The first sequence ensures a clean state, the second verifies idempotency. + for pass in 1 2; do echo "=== Test sequence pass #$pass ===" >&2 - echo "-> Running: \`$RUN_TEST --clean\`" >&2 - run_and_check "$RUN_TEST" --clean + # enable --upsert only on the second pass + if [ "$pass" -eq 2 ]; then + echo "-> Running: \`$RUN_TEST --clean --upsert\`" >&2 + run_and_check "$RUN_TEST" --clean --upsert + else + echo "-> Running: \`$RUN_TEST --clean\`" >&2 + run_and_check "$RUN_TEST" --clean + fi + + # on the second pass, this will NOT replace existing indexd records echo "-> Running: \`$RUN_TEST\`" >&2 run_and_check "$RUN_TEST" + echo "-> Running: \`$RUN_TEST --clone\`" >&2 run_and_check "$RUN_TEST" --clone From eaaf51547c245beb81e371473bf1870482123420 Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Wed, 21 Jan 2026 05:42:36 -0800 Subject: [PATCH 26/40] cleanup --- client/indexd/indexd_client.go | 241 +++++++++++----------------- client/indexd/indexd_client_test.go | 45 ++++++ 2 files changed, 141 insertions(+), 145 deletions(-) diff --git a/client/indexd/indexd_client.go b/client/indexd/indexd_client.go index 65c3ac0b..087fa89d 100644 --- a/client/indexd/indexd_client.go +++ b/client/indexd/indexd_client.go @@ -80,6 +80,12 @@ func NewIndexDClient(profileConfig conf.Credential, remote Gen3Remote, logger *l // Custom CheckRetry: do not retry when response body contains "already exists" retryClient.CheckRetry = func(ctx context.Context, resp *http.Response, err error) (bool, error) { + if resp != nil && resp.StatusCode < 500 && resp.StatusCode >= 400 { + // do not retry on 4xx + // 400 => "The request could not be understood by the + // server due to malformed syntax". + return false, nil + } if resp != nil && resp.Body != nil { bodyBytes, readErr := io.ReadAll(resp.Body) // restore body for downstream consumers @@ -389,169 +395,113 @@ func (cl *IndexDClient) getDownloadURL(did string, accessId string) (drs.AccessU func (cl *IndexDClient) RegisterFile(oid string) (*drs.DRSObject, error) { cl.Logger.Printf("register file started for oid: %s", oid) - originalUpsert := cl.Upsert - attempts := 1 - if !originalUpsert { - attempts = 2 - defer func() { - cl.Upsert = originalUpsert - }() + // load the DRS object from oid created by prepush + drsObject, err := drsmap.DrsInfoFromOid(oid) + if err != nil { + return nil, fmt.Errorf("error getting drs object for oid %s: %v", oid, err) } - for attempt := 0; attempt < attempts; attempt++ { - if attempt == 1 { - cl.Upsert = true - } - cl.Logger.Printf("register file attempt %d for oid: %s (force push: %t)", attempt+1, oid, cl.Upsert) - - var records []drs.DRSObject - var err error - var drsObject *drs.DRSObject - createdRecord := false + // convert to indexd record + indexdObj, err := indexdRecordFromDrsObject(drsObject) + if err != nil { + return nil, fmt.Errorf("error converting DRS object oid %s to indexd record: %v", oid, err) + } - if cl.Upsert { - records, err = cl.GetObjectByHash(&hash.Checksum{Type: hash.ChecksumTypeSHA256, Checksum: oid}) - if err != nil { - return nil, fmt.Errorf("error querying indexd server for matches to hash %s: %v", oid, err) - } - if len(records) > 0 { - drsObject, err = drsmap.FindMatchingRecord(records, cl.ProjectId) + // save the indexd record + _, err = cl.RegisterIndexdRecord(indexdObj) + if err != nil { + // handle "already exists" error ie upsert behavior + if strings.Contains(err.Error(), "already exists") { + if !cl.Upsert { + cl.Logger.Printf("indexd record already exists, proceeding for oid %s: did: %s err: %v", oid, indexdObj.Did, err) + } else { + cl.Logger.Printf("indexd record already exists, deleting and re-adding for oid %s: did: %s err: %v", oid, indexdObj.Did, err) + err = cl.deleteIndexdRecord(indexdObj.Did) if err != nil { - return nil, fmt.Errorf("error finding matching record for project %s: %v", cl.ProjectId, err) + return nil, fmt.Errorf("error deleting existing indexd record oid %s: did: %s err: %v", oid, indexdObj.Did, err) } - } - } - - if drsObject == nil { - drsObject, err = drsmap.DrsInfoFromOid(oid) - if err != nil { - return nil, fmt.Errorf("error getting indexd object for oid %s: %v", oid, err) - } - cl.Logger.Printf("DrsInfoFromOid: %v", drsObject) - - indexdObj, err := indexdRecordFromDrsObject(drsObject) - if err != nil { - return nil, fmt.Errorf("error converting DRS object to indexd record: %v", err) - } - cl.Logger.Printf("indexdRecordFromDrsObject: %v", indexdObj) - - drsObject, err = cl.RegisterIndexdRecord(indexdObj) - if err != nil { - if !cl.Upsert { - cl.Logger.Printf("error saving indexd record without force push: %s; retrying with force push enabled", err) - continue - } - cl.Logger.Printf("error saving indexd record: %s", err) - return nil, fmt.Errorf("error saving indexd record: %v", err) - } - createdRecord = true - cl.Logger.Printf("RegisterIndexdRecord: %v", drsObject) - - } - - cleanupOnError := func(cause error) (*drs.DRSObject, error) { - if createdRecord && drsObject != nil { - cl.Logger.Printf("registration incomplete, cleaning up indexd record for oid %s", oid) - cleanupErr := cl.DeleteIndexdRecord(drsObject.Id) - if cleanupErr != nil { - cl.Logger.Printf("error cleaning up indexd record on failed registration for oid %s: %s", oid, cleanupErr) - cl.Logger.Printf("please delete the indexd record manually if needed for DRS ID: %s", drsObject.Id) - cl.Logger.Printf("see https://uc-cdis.github.io/gen3sdk-python/_build/html/indexing.html") - } else { - cl.Logger.Printf("cleaned up indexd record for oid %s", oid) + _, err = cl.RegisterIndexdRecord(indexdObj) + if err != nil { + return nil, fmt.Errorf("error re-saving indexd record after deletion: oid %s: did: %s err: %v", oid, indexdObj.Did, err) } } - return nil, cause - } - isDownloadable := false - if cl.Upsert { - isDownloadable, err = cl.isFileDownloadable(drsObject) - if err != nil { - return cleanupOnError(err) - } + } else { + return nil, fmt.Errorf("error saving oid %s indexd record: %v", oid, err) } + } - if !isDownloadable { - cl.Logger.Printf("Proceeding to upload %s", oid) - - filePath, err := drsmap.GetObjectPath(projectdir.LFS_OBJS_PATH, oid) - if err != nil { - cl.Logger.Printf("error getting object path for oid %s: %s", oid, err) - return cleanupOnError(fmt.Errorf("error getting object path for oid %s: %v", oid, err)) - } - - profile, err := cl.GetProfile() - if err != nil { - return cleanupOnError(fmt.Errorf("error getting profile for upload: %v", err)) - } - - // TODO - should we deprecate this gen3-client style logger in favor of drslog.Logger? - // TODO - or can we "wrap it" so both work together? - logger, closer := logs.New(profile, logs.WithBaseLogger(cl.Logger)) - defer closer() - - // Instantiate interface to Gen3 - // TODO - Can we reuse this interface to avoid repeated config parsing and most likely repeated token refresh? - g3, err := dataClient.NewGen3Interface(profile, logger) - if err != nil { - return cleanupOnError(fmt.Errorf("error creating Gen3 interface: %v", err)) - } - - file, err := os.Open(filePath) - if err != nil { - return cleanupOnError(fmt.Errorf("error opening file %s: %v", filePath, err)) - } - defer file.Close() + // Now attempt to upload the file if not already available + downloadable, err := cl.isFileDownloadable(drsObject) + if err != nil { + return nil, fmt.Errorf("error checking if file is downloadable: oid %s %v", oid, err) + } + if downloadable { + cl.Logger.Printf("file %s is already available for download, skipping upload", oid) + return drsObject, nil + } - stat, err := file.Stat() - if err != nil { - return cleanupOnError(fmt.Errorf("error stating file %s: %v", file.Name(), err)) - } + // Proceed to upload the file ------------------- + profile, err := cl.GetProfile() + if err != nil { + return nil, fmt.Errorf("error getting profile for upload: %v", err) + } + // TODO - should we deprecate this gen3-client style logger in favor of drslog.Logger? + // TODO - or can we "wrap it" so both work together? + logger, closer := logs.New(profile, logs.WithBaseLogger(cl.Logger)) + defer closer() + // Instantiate interface to Gen3 + // TODO - Can we reuse this interface to avoid repeated config parsing and most likely repeated token refresh? + // TODO - Can we reuse Auth to ensure we are not repeatedly refreshing tokens? + g3, err := dataClient.NewGen3Interface(profile, logger) + if err != nil { + return nil, fmt.Errorf("error creating Gen3 interface: %v", err) + } - // TODO - Can we reuse Auth to ensure we are not repeatedly refreshing tokens? - if stat.Size() < cl.MultiPartThreshold { - cl.Logger.Printf("UploadSingle for small file %s", filePath) - err := upload.UploadSingle(context.Background(), g3.GetCredential().Profile, drsObject.Id, filePath, cl.BucketName, false) - if err != nil { - cl.Logger.Printf("error uploading single file to bucket: %s", err) - return cleanupOnError(fmt.Errorf("error uploading single file to bucket: %s", err)) - } - } else { - cl.Logger.Printf("MultipartUpload for large file %s", filePath) - err = upload.MultipartUpload( - context.TODO(), - g3, - common.FileUploadRequestObject{ - FilePath: filePath, - Filename: filepath.Base(filePath), - GUID: drsObject.Id, - FileMetadata: common.FileMetadata{}, - Bucket: cl.BucketName, - }, - file, false, - ) - if err != nil { - cl.Logger.Printf("error uploading file to bucket: %s", err) - return cleanupOnError(fmt.Errorf("error uploading file to bucket: %v", err)) - } - } - } else { - cl.Logger.Print("file exists in bucket, skipping upload") + filePath, err := drsmap.GetObjectPath(projectdir.LFS_OBJS_PATH, oid) + if err != nil { + return nil, fmt.Errorf("error getting object path for oid %s: %v", oid, err) + } + file, err := os.Open(filePath) + if err != nil { + return nil, fmt.Errorf("error opening file %s: %v", filePath, err) + } + defer func(file *os.File) { + err := file.Close() + if err != nil { + cl.Logger.Printf("warning: error closing file %s: %v", filePath, err) } + }(file) - return drsObject, nil + if drsObject.Size < cl.MultiPartThreshold { + cl.Logger.Printf("UploadSingle size: %d path: %s", drsObject.Size, filePath) + err := upload.UploadSingle(context.Background(), g3.GetCredential().Profile, drsObject.Id, filePath, cl.BucketName, false) + if err != nil { + return nil, fmt.Errorf("UploadSingle error: %s", err) + } + } else { + cl.Logger.Printf("MultipartUpload size: %d path: %s", drsObject.Size, filePath) + err = upload.MultipartUpload( + context.TODO(), + g3, + common.FileUploadRequestObject{ + FilePath: filePath, + Filename: filepath.Base(filePath), + GUID: drsObject.Id, + FileMetadata: common.FileMetadata{}, + Bucket: cl.BucketName, + }, + file, false, + ) + if err != nil { + return nil, fmt.Errorf("MultipartUpload error: %s", err) + } } + return drsObject, nil - return nil, fmt.Errorf("indexd registration failed after retry for oid %s", oid) } func (cl *IndexDClient) isFileDownloadable(drsObject *drs.DRSObject) (bool, error) { - if !cl.Upsert { - cl.Logger.Printf("force push disabled; proceeding to upload oid %s", drsObject.Id) - return false, nil - } - cl.Logger.Printf("checking if %s file is downloadable %v %v %v", drsObject.Id, drsObject.AccessMethods[0].AccessID, drsObject.AccessMethods[0].Type, drsObject.AccessMethods[0].AccessURL) signedUrl, err := cl.getDownloadURL(drsObject.Id, drsObject.AccessMethods[0].Type) if err != nil { @@ -1122,6 +1072,7 @@ func (cl *IndexDClient) BuildDrsObj(fileName string, checksum string, size int64 return nil, fmt.Errorf("error: bucket name is empty in config file") } + //TODO: support other storage backends fileURL := fmt.Sprintf("s3://%s", filepath.Join(bucket, drsId, checksum)) authzStr, err := utils.ProjectToResource(cl.GetProjectId()) @@ -1137,7 +1088,7 @@ func (cl *IndexDClient) BuildDrsObj(fileName string, checksum string, size int64 Id: drsId, Name: fileName, // TODO: ensure that we can retrieve the access method during submission (happens in transfer) - AccessMethods: []drs.AccessMethod{{AccessURL: drs.AccessURL{URL: fileURL}, Authorizations: &authorizations}}, + AccessMethods: []drs.AccessMethod{{Type: "s3", AccessURL: drs.AccessURL{URL: fileURL}, Authorizations: &authorizations}}, Checksums: hash.HashInfo{SHA256: checksum}, Size: size, } diff --git a/client/indexd/indexd_client_test.go b/client/indexd/indexd_client_test.go index 1103d24b..e5b2c044 100644 --- a/client/indexd/indexd_client_test.go +++ b/client/indexd/indexd_client_test.go @@ -464,3 +464,48 @@ func chdirForTest(t *testing.T, dir string) func() { } } } + +func TestBuildDrsObj_Success(t *testing.T) { + client := &IndexDClient{ + ProjectId: "test-project", + BucketName: "bucket", + } + + obj, err := client.BuildDrsObj("file.txt", "sha-256", 12, "did-1") + if err != nil { + t.Fatalf("BuildDrsObj error: %v", err) + } + if obj.Id != "did-1" { + t.Fatalf("unexpected Id: %s", obj.Id) + } + if obj.Name != "file.txt" { + t.Fatalf("unexpected Name: %s", obj.Name) + } + if obj.Checksums.SHA256 != "sha-256" { + t.Fatalf("unexpected checksum: %v", obj.Checksums) + } + if obj.Size != 12 { + t.Fatalf("unexpected size: %d", obj.Size) + } + if len(obj.AccessMethods) != 1 { + t.Fatalf("expected 1 access method, got %d", len(obj.AccessMethods)) + } + if !strings.Contains(obj.AccessMethods[0].AccessURL.URL, filepath.Join("bucket", "did-1", "sha-256")) { + t.Fatalf("unexpected access URL: %s", obj.AccessMethods[0].AccessURL.URL) + } + if obj.AccessMethods[0].Type != "s3" { + t.Fatalf("unexpected access method type: %s", obj.AccessMethods[0].Type) + } +} + +func TestBuildDrsObj_EmptyBucket(t *testing.T) { + client := &IndexDClient{ + ProjectId: "test-project", + BucketName: "", + } + + _, err := client.BuildDrsObj("file.txt", "sha-256", 12, "did-1") + if err == nil { + t.Fatalf("expected error when BucketName is empty") + } +} From c75b65602cc68a4c65b1b15c728fbc9c661887b6 Mon Sep 17 00:00:00 2001 From: Brian Date: Wed, 21 Jan 2026 09:00:52 -0800 Subject: [PATCH 27/40] Update tests/scripts/utils/broken-pipe-finder.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/scripts/utils/broken-pipe-finder.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/scripts/utils/broken-pipe-finder.sh b/tests/scripts/utils/broken-pipe-finder.sh index be18914e..1d1537c3 100755 --- a/tests/scripts/utils/broken-pipe-finder.sh +++ b/tests/scripts/utils/broken-pipe-finder.sh @@ -7,9 +7,9 @@ set -euo pipefail # set -x # check parameter is a file -if [ ! -f "$1" ]; then +if [ "$#" -lt 1 ] || [ ! -f "${1:-}" ]; then echo "Usage: $0 \`\`" >&2 - echo "error: \`$1\` does not exist or is not a regular file" >&2 + echo "error: \`${1:-}\` does not exist or is not a regular file" >&2 exit 2 fi From ab1996373dc3136b260044d15ee6711ba4a09cbf Mon Sep 17 00:00:00 2001 From: Brian Date: Wed, 21 Jan 2026 09:01:07 -0800 Subject: [PATCH 28/40] Update tests/coverage-test.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/coverage-test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/coverage-test.sh b/tests/coverage-test.sh index f0ff003c..2afb5a9f 100755 --- a/tests/coverage-test.sh +++ b/tests/coverage-test.sh @@ -28,7 +28,7 @@ gofmt -s -w . go build # unit tests -go test -v -race -coverprofile=coverage.out -covermode=atomic -coverpkg=./... $(go list ./... | grep -vE 'tests/integration/calypr|client/indexd/tests') | grep FAIL && echo "unit tests failed" >&2 && exit 1 +go test -v -race -coverprofile=coverage.out -covermode=atomic -coverpkg=./... $(go list ./... | grep -vE 'tests/integration/calypr|client/indexd/tests') || { echo "unit tests failed" >&2; exit 1; } From 75d17bbaf60f72ba058b9f6fb71456475ebb543d Mon Sep 17 00:00:00 2001 From: Brian Date: Wed, 21 Jan 2026 09:01:25 -0800 Subject: [PATCH 29/40] Update tests/monorepos/run-test.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/monorepos/run-test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/monorepos/run-test.sh b/tests/monorepos/run-test.sh index d06882ca..173ec7ce 100755 --- a/tests/monorepos/run-test.sh +++ b/tests/monorepos/run-test.sh @@ -77,7 +77,7 @@ while [ $# -gt 0 ]; do shift ;; -h|--help) - echo "Usage: $0 [--credentials-path PATH] [--profile NAME] [--project NAME] [--clean] [--git-remote NAME] [--upsert]" >&2 + echo "Usage: $0 [--credentials-path PATH] [--profile NAME] [--project NAME] [--clean] [--clone] [--git-remote NAME] [--upsert]" >&2 exit 0 ;; *) From eacbaf1062481bfcf4f076d6a58dd09d538bca47 Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Wed, 21 Jan 2026 09:03:29 -0800 Subject: [PATCH 30/40] PR feedback --- client/indexd/convert.go | 5 ++++- client/indexd/indexd_client.go | 29 +++++++++++++---------------- docs/indexd-registerfile-upsert.md | 4 ++-- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/client/indexd/convert.go b/client/indexd/convert.go index 95b23073..ef047ec3 100644 --- a/client/indexd/convert.go +++ b/client/indexd/convert.go @@ -53,7 +53,10 @@ func drsAccessMethodsFromIndexdURLs(urls []string, authz []string) ([]drs.Access method.AccessURL = drs.AccessURL{URL: urlString} parsed, err := url.Parse(urlString) - if err != nil || parsed.Scheme == "" { + if err != nil { + return nil, fmt.Errorf("failed to parse url %q: %v", urlString, err) + } + if parsed.Scheme == "" { // default to https if no scheme or parse error method.Type = "https" } else { diff --git a/client/indexd/indexd_client.go b/client/indexd/indexd_client.go index 087fa89d..f087dcbe 100644 --- a/client/indexd/indexd_client.go +++ b/client/indexd/indexd_client.go @@ -166,11 +166,7 @@ func getLfsCustomTransferInt(key string, defaultValue int64) (int64, error) { return defaultValue, fmt.Errorf("invalid int value for %s: >%q<", key, value) } - if parsed < 0 { - return defaultValue, fmt.Errorf("invalid negative int value for %s: %d", key, parsed) - } - - if parsed == 0 || parsed > 500 { + if parsed < 1 || parsed > 500 { return defaultValue, fmt.Errorf("invalid int value for %s: %d. Must be between 1 and 500", key, parsed) } @@ -333,11 +329,11 @@ func (cl *IndexDClient) getDownloadURLFromRecords(oid string, records []drs.DRSO return &accessUrl, nil } -// getDownloadURL gets a signed URL for the given DRS ID and access ID -func (cl *IndexDClient) getDownloadURL(did string, accessId string) (drs.AccessURL, error) { +// getDownloadURL gets a signed URL for the given DRS ID and accessType (eg s3) +func (cl *IndexDClient) getDownloadURL(did string, accessType string) (drs.AccessURL, error) { // get signed url a := *cl.Base - a.Path = filepath.Join(a.Path, "ga4gh/drs/v1/objects", did, "access", accessId) + a.Path = filepath.Join(a.Path, "ga4gh/drs/v1/objects", did, "access", accessType) req, err := retryablehttp.NewRequest("GET", a.String(), nil) if err != nil { @@ -354,14 +350,8 @@ func (cl *IndexDClient) getDownloadURL(did string, accessId string) (drs.AccessU return drs.AccessURL{}, fmt.Errorf("error getting signed URL: %v", err) } defer func() { - closeErr := response.Body.Close() - if err == nil { - // If no error has happened yet, take the close error - err = closeErr - } else if closeErr != nil { - // If an error already exists, you can log the close error - // or wrap it so you don't lose the original context - err = fmt.Errorf("%w; additionally, body close failed: %v", err, closeErr) + if closeErr := response.Body.Close(); closeErr != nil { + log.Printf("error closing response body: %v", closeErr) } }() @@ -502,6 +492,13 @@ func (cl *IndexDClient) RegisterFile(oid string) (*drs.DRSObject, error) { } func (cl *IndexDClient) isFileDownloadable(drsObject *drs.DRSObject) (bool, error) { + if drsObject == nil { + return false, fmt.Errorf("drsObject is nil") + } + if len(drsObject.AccessMethods) == 0 { + cl.Logger.Printf("DRS object %s has no access methods; proceeding to upload", drsObject.Id) + return false, nil + } cl.Logger.Printf("checking if %s file is downloadable %v %v %v", drsObject.Id, drsObject.AccessMethods[0].AccessID, drsObject.AccessMethods[0].Type, drsObject.AccessMethods[0].AccessURL) signedUrl, err := cl.getDownloadURL(drsObject.Id, drsObject.AccessMethods[0].Type) if err != nil { diff --git a/docs/indexd-registerfile-upsert.md b/docs/indexd-registerfile-upsert.md index 06b61c0c..21770751 100644 --- a/docs/indexd-registerfile-upsert.md +++ b/docs/indexd-registerfile-upsert.md @@ -5,8 +5,8 @@ Accepted ## Context The Indexd `RegisterFile` flow needs toggles for: -- whether to upsert (create) indexd records when no matching project record exists -- whether to check bucket existence before uploading +- whether to upsert indexd records (create when no matching project record exists, or replace by deleting and re-registering when a ma +- whether to check bucket existence before uploading (Unimplemented, currently always checks and skips upload if already present) These toggles must be controlled per-repository using git LFS configuration (`git config` entries under `lfs.customtransfer.drs.*`). This keeps behavior in repo-local configuration and avoids coupling to remote YAML configuration. From 5c785cbb08770c49be42486260d26e4ade9b15f1 Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Wed, 21 Jan 2026 09:18:34 -0800 Subject: [PATCH 31/40] fix: url path --- client/indexd/indexd_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/indexd/indexd_client.go b/client/indexd/indexd_client.go index f087dcbe..dcf33931 100644 --- a/client/indexd/indexd_client.go +++ b/client/indexd/indexd_client.go @@ -1070,7 +1070,7 @@ func (cl *IndexDClient) BuildDrsObj(fileName string, checksum string, size int64 } //TODO: support other storage backends - fileURL := fmt.Sprintf("s3://%s", filepath.Join(bucket, drsId, checksum)) + fileURL := fmt.Sprintf("s3://%s", filepath.Join(bucket, checksum)) authzStr, err := utils.ProjectToResource(cl.GetProjectId()) if err != nil { From d2489f3048ea4fd65322bc70ceed5172edba6a52 Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Wed, 21 Jan 2026 13:56:42 -0800 Subject: [PATCH 32/40] Revert "fix: url path" This reverts commit 5c785cbb08770c49be42486260d26e4ade9b15f1. --- client/indexd/indexd_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/indexd/indexd_client.go b/client/indexd/indexd_client.go index dcf33931..f087dcbe 100644 --- a/client/indexd/indexd_client.go +++ b/client/indexd/indexd_client.go @@ -1070,7 +1070,7 @@ func (cl *IndexDClient) BuildDrsObj(fileName string, checksum string, size int64 } //TODO: support other storage backends - fileURL := fmt.Sprintf("s3://%s", filepath.Join(bucket, checksum)) + fileURL := fmt.Sprintf("s3://%s", filepath.Join(bucket, drsId, checksum)) authzStr, err := utils.ProjectToResource(cl.GetProjectId()) if err != nil { From 8205afcfe80790e226b0d7317086d4d7099c729d Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Wed, 21 Jan 2026 14:05:26 -0800 Subject: [PATCH 33/40] improve tests --- tests/coverage-test.sh | 29 ++++++++++++++++++++---- tests/monorepos/run-test.sh | 12 +++++++++- tests/scripts/utils/list-s3-by-sha256.sh | 8 ++++++- 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/tests/coverage-test.sh b/tests/coverage-test.sh index 2afb5a9f..a8a046dd 100755 --- a/tests/coverage-test.sh +++ b/tests/coverage-test.sh @@ -62,6 +62,25 @@ while [ $# -gt 0 ]; do done +# Derive PROJECT_ID from RESOURCE (e.g. /programs//projects/ -> -) +_resource_clean="${RESOURCE#/}" # drop leading slash if present +_resource_clean="${_resource_clean%/}" # drop trailing slash if present + +IFS='/' read -r -a _parts <<< "$_resource_clean" + +_prog="" +_proj="" +for i in "${!_parts[@]}"; do + if [ "${_parts[i]}" = "programs" ] && [ $((i+1)) -lt ${#_parts[@]} ]; then + _prog="${_parts[i+1]}" + fi + if [ "${_parts[i]}" = "projects" ] && [ $((i+1)) -lt ${#_parts[@]} ]; then + _proj="${_parts[i+1]}" + fi +done + +PROJECT_ID="${_prog}-${_proj}" + UTIL_DIR="tests/scripts/utils" MONOREPO_DIR="tests/monorepos" RUN_TEST="./run-test.sh" @@ -91,9 +110,9 @@ fi pushd "$UTIL_DIR" >/dev/null # 1) Remove objects from bucket using indexd->s3 list/delete pipeline -echo "Removing bucket objects by sha256 via \`./list-indexd-sha256.sh $POD $RESOURCE | ./list-s3-by-sha256.sh $MINIO_ALIAS $BUCKET\`" >&2 -if ! ./list-indexd-sha256.sh "$POD" "$POSTGRES_PASSWORD" "$RESOURCE" | ./list-s3-by-sha256.sh "$MINIO_ALIAS" "$BUCKET"; then - err "command failed: ./list-indexd-sha256.sh \"$POD\" \"$POSTGRES_PASSWORD\" \"$RESOURCE\" | ./list-s3-by-sha256.sh \"$MINIO_ALIAS\" \"$BUCKET\"" +echo "Removing bucket objects by sha256 via \`./list-indexd-sha256.sh $POD $RESOURCE | ./delete-s3-by-sha256.sh $MINIO_ALIAS $BUCKET\`" >&2 +if ! ./list-indexd-sha256.sh "$POD" "$POSTGRES_PASSWORD" "$RESOURCE" | ./delete-s3-by-sha256.sh "$MINIO_ALIAS" "$BUCKET"; then + err "command failed: ./list-indexd-sha256.sh \"$POD\" \"$POSTGRES_PASSWORD\" \"$RESOURCE\" | ./delete-s3-by-sha256.sh \"$MINIO_ALIAS\" \"$BUCKET\"" exit 1 fi echo "Bucket object removal pipeline completed." >&2 @@ -122,10 +141,10 @@ for pass in 1 2; do # enable --upsert only on the second pass if [ "$pass" -eq 2 ]; then echo "-> Running: \`$RUN_TEST --clean --upsert\`" >&2 - run_and_check "$RUN_TEST" --clean --upsert + run_and_check "$RUN_TEST" --clean --upsert --bucket=$BUCKET --project=$PROJECT_ID else echo "-> Running: \`$RUN_TEST --clean\`" >&2 - run_and_check "$RUN_TEST" --clean + run_and_check "$RUN_TEST" --clean --bucket=$BUCKET --project=$PROJECT_ID fi diff --git a/tests/monorepos/run-test.sh b/tests/monorepos/run-test.sh index 173ec7ce..2f3bbd3e 100755 --- a/tests/monorepos/run-test.sh +++ b/tests/monorepos/run-test.sh @@ -16,6 +16,7 @@ GIT_REMOTE_DEFAULT="https://github.com/calypr/monorepo.git" CLEAN_DEFAULT="false" CLONE_DEFAULT="false" UPSERT_DEFAULT="false" +BUCKET_DEFAULT="cbds" # Parse optional flags (can also be provided via environment variables) while [ $# -gt 0 ]; do @@ -76,6 +77,14 @@ while [ $# -gt 0 ]; do UPSERT="true" shift ;; + --bucket=*) + BUCKET="${1#*=}" + shift + ;; + --bucket) + BUCKET="$2" + shift 2 + ;; -h|--help) echo "Usage: $0 [--credentials-path PATH] [--profile NAME] [--project NAME] [--clean] [--clone] [--git-remote NAME] [--upsert]" >&2 exit 0 @@ -94,6 +103,7 @@ GIT_REMOTE="${GIT_REMOTE:-$GIT_REMOTE_DEFAULT}" CLEAN="${CLEAN:-$CLEAN_DEFAULT}" CLONE="${CLONE:-$CLONE_DEFAULT}" UPSERT="${UPSERT:-$UPSERT_DEFAULT}" +BUCKET="${BUCKET:-$BUCKET_DEFAULT}" IFS='-' read -r PROGRAM PROJECT <<< "$PROJECT" @@ -227,7 +237,7 @@ else # Initialize drs configuration for this repo git drs init -t 16 - git drs remote add gen3 "$PROFILE" --cred "$CREDENTIALS_PATH" --bucket cbds --project "$PROGRAM-$PROJECT" --url https://calypr-dev.ohsu.edu + git drs remote add gen3 "$PROFILE" --cred "$CREDENTIALS_PATH" --bucket $BUCKET --project "$PROGRAM-$PROJECT" --url https://calypr-dev.ohsu.edu # Set multipart-threshold to 10 (MB) for testing purposes # Using a smaller threshold to force a multipart upload for testing # default is 500 (MB) diff --git a/tests/scripts/utils/list-s3-by-sha256.sh b/tests/scripts/utils/list-s3-by-sha256.sh index 797d63f9..8af6d4ae 100755 --- a/tests/scripts/utils/list-s3-by-sha256.sh +++ b/tests/scripts/utils/list-s3-by-sha256.sh @@ -82,7 +82,13 @@ while IFS=$' \t' read -r did hash file_name resource; do # capture mc ls output, append file_name on success; on failure print error plus file_name if output=$(mc ls "${MC_ALIAS}/${BUCKET}/${object_key}" 2>/dev/null); then - printf '%s %s %s %s\n' "$output" "$file_name" "$resource" "$object_key" + if [[ -z "$output" ]]; then + printf 'ERROR: alias: %s object: %s not found or unreachable file_name:%s\n' "$MC_ALIAS" "$object_key" "$file_name" + exit 1 + else + printf '%s %s %s %s\n' "$output" "$file_name" "$resource" "$object_key" + fi + else printf 'ERROR: alias %s object %s not found or unreachable %s\n' "$MC_ALIAS" "$object_key" "$file_name" fi From 6d6d30c4f89ba60a5c098de48be25e7c5d5e4048 Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Wed, 21 Jan 2026 14:21:46 -0800 Subject: [PATCH 34/40] adds listing --- tests/coverage-test.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/coverage-test.sh b/tests/coverage-test.sh index a8a046dd..021024ee 100755 --- a/tests/coverage-test.sh +++ b/tests/coverage-test.sh @@ -147,7 +147,6 @@ for pass in 1 2; do run_and_check "$RUN_TEST" --clean --bucket=$BUCKET --project=$PROJECT_ID fi - # on the second pass, this will NOT replace existing indexd records echo "-> Running: \`$RUN_TEST\`" >&2 run_and_check "$RUN_TEST" @@ -161,4 +160,11 @@ done popd >/dev/null +echo "Listing bucket objects by sha256 via \`./list-indexd-sha256.sh $POD $RESOURCE | ./list-s3-by-sha256.sh $MINIO_ALIAS $BUCKET\`" >&2 +if ! $UTIL_DIR/list-indexd-sha256.sh "$POD" "$POSTGRES_PASSWORD" "$RESOURCE" | $UTIL_DIR/list-s3-by-sha256.sh "$MINIO_ALIAS" "$BUCKET"; then + err "command failed: ./list-indexd-sha256.sh \"$POD\" \"$POSTGRES_PASSWORD\" \"$RESOURCE\" | ./list-s3-by-sha256.sh \"$MINIO_ALIAS\" \"$BUCKET\"" + exit 1 +fi + + echo "coverage-test.sh: all steps completed successfully." >&2 From 962d84187607d3c0296eaf4eba0195240cc27880 Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Wed, 21 Jan 2026 15:52:15 -0800 Subject: [PATCH 35/40] bump data-client --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1d4e0b0e..b5a56b0c 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.24.2 require ( github.com/bytedance/sonic v1.14.2 - github.com/calypr/data-client v0.0.0-20260121002104-bfba83c8cd17 + github.com/calypr/data-client v0.0.0-20260121234835-1510a7d25ee7 github.com/golang-jwt/jwt/v5 v5.3.0 github.com/google/uuid v1.6.0 github.com/hashicorp/go-multierror v1.1.1 diff --git a/go.sum b/go.sum index 36b27e8c..7113deca 100644 --- a/go.sum +++ b/go.sum @@ -48,8 +48,8 @@ github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPII github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980= github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o= github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= -github.com/calypr/data-client v0.0.0-20260121002104-bfba83c8cd17 h1:pd9+D3YhP7M4C9H9NNLuFk+/vbn5RtNmElJgc30MYHg= -github.com/calypr/data-client v0.0.0-20260121002104-bfba83c8cd17/go.mod h1:/GPTQWCRSZpNPoojP/dUsNlqQa4bykRlTnrJRebwqQ4= +github.com/calypr/data-client v0.0.0-20260121234835-1510a7d25ee7 h1:0k6Npo4h5K/TwZMnC33unfeJaEQzQxHWmIKhG5NT7ug= +github.com/calypr/data-client v0.0.0-20260121234835-1510a7d25ee7/go.mod h1:/GPTQWCRSZpNPoojP/dUsNlqQa4bykRlTnrJRebwqQ4= github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= From c1c9a6e07551cb05ad172d21de0cc80313c4f97d Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Wed, 21 Jan 2026 16:48:06 -0800 Subject: [PATCH 36/40] rm outdated tests --- tests/integration/calypr/README.md | 70 --- tests/integration/calypr/end_to_end_test.go | 468 -------------------- 2 files changed, 538 deletions(-) delete mode 100644 tests/integration/calypr/README.md delete mode 100644 tests/integration/calypr/end_to_end_test.go diff --git a/tests/integration/calypr/README.md b/tests/integration/calypr/README.md deleted file mode 100644 index 7404ca7b..00000000 --- a/tests/integration/calypr/README.md +++ /dev/null @@ -1,70 +0,0 @@ -# End-to-End Integration Test - git-drs - -Location: `tests/integration/calypr/end_to_end_test.go` - -## Overview - -This integration test exercises a full git + Git LFS + git-drs workflow against a GitHub Enterprise Server (GHE) instance. It creates a temporary repo, configures LFS, adds a test file, attempts pushes/pulls using `git-drs`, and verifies local object storage paths and cleanup. - -## What it validates - -- Repository creation and deletion on the GHE org. -- `git-drs` initialization and remote configuration. -- Git LFS tracking and pointer/file handling. -- Detection and mapping of LFS objects via `drsmap.GetAllLfsFiles`. -- Basic push/pull behavior against a dummy DRS endpoint. -- Cleanup of created local repos and cloned copies. - -## Prerequisites - -- macOS (developer environment) -- Go toolchain (project uses Go modules) -- Binaries on `PATH`: - - `git` - - `git-lfs` - - `git-drs` - - `calypr_admin` -- Network access to the target GHE instance. -- A GitHub Personal Access Token (PAT) with permissions to create and delete repos in the target org. - -## Required environment variables - -- `GH_PAT` — GitHub Personal Access Token used to create/delete repos. -- `GIT_DRS_REMOTE` — name of the profile/remote used by the project (expected by config loader). - -Example: -```bash -export GH_PAT="ghp_..." -export GIT_DRS_REMOTE="my-profile" -``` - -## Running the test - -From the project root, run: - -```bash -go test ./tests/integration/calypr -v -run TestEndToEndGitDRSWorkflow -``` - -Notes: -- The test is destructive on the GHE org (creates and removes a repository). Ensure the token and org are appropriate. -- The test runs external commands and will fail if required binaries are missing or not on `PATH`. -- The test prints temporary directories and command outputs to help debugging. - -## Expected behavior - -- The test should create a repo named `test-` under the configured org, add a single LFS-tracked file, and exercise `git-drs` commands. -- `drsmap.GetAllLfsFiles` should return a single LFS file entry for the created test file. -- The test will attempt `git push`/`git lfs pull` which may fail against a dummy DRS server; failures are expected and asserted accordingly in the test. - -## Troubleshooting - -- Missing binaries: ensure required tools are installed and discoverable via `which `. -- Permission errors from GHE: verify `GH_PAT` scopes (repo creation/deletion) and that the token is valid. -- If the test fails while cleaning up, manually check and delete the temporary repo in the org. -- For verbose debugging, run the test with `-v` (already recommended). - -## Safety & CI - -- Consider running this test in an isolated org or a test GHE instance. -- Run in CI only if the runner has network access and safe permissions. diff --git a/tests/integration/calypr/end_to_end_test.go b/tests/integration/calypr/end_to_end_test.go deleted file mode 100644 index 0f1856c8..00000000 --- a/tests/integration/calypr/end_to_end_test.go +++ /dev/null @@ -1,468 +0,0 @@ -package test - -import ( - "bytes" - "fmt" - "io" - "log" - "math/rand" - "net/http" - "os" - "os/exec" - "path/filepath" - "testing" - "time" - - "github.com/bytedance/sonic" - "github.com/calypr/data-client/client/conf" - "github.com/calypr/git-drs/drsmap" - "github.com/calypr/git-drs/projectdir" - "github.com/calypr/git-drs/utils" -) - -const DEFAULT_BUCKET string = "cbds" - -func TestEndToEndGitDRSWorkflow(t *testing.T) { - // GitHub Enterprise Server details - host := "https://source.ohsu.edu" - owner := "CBDS" - project := generateRandomString(8) - repoName := "test-" + project - token := os.Getenv("GH_PAT") - if token == "" { - t.Fatal("GH_PAT environment variable not set") - } - - remote := os.Getenv("GIT_DRS_REMOTE") - if remote == "" { - t.Fatal("GIT_DRS_REMOTE environment variable not set") - } - - var err error - // Create a temporary directory for the test repository - tmpDir, err := os.MkdirTemp("", "git-drs-e2e-") - if err != nil { - t.Fatalf("Failed to create temp dir: %v", err) - } - // Print the temporary directory path for debugging - t.Logf("Temporary directory: %s", tmpDir) - - defer func() { - if err := os.RemoveAll(tmpDir); err != nil { - t.Errorf("Failed to remove temp dir %s: %v", tmpDir, err) - } - }() - - // Create remote repo via API - if err = createRemoteRepo(host, owner, repoName, token); err != nil { - t.Fatalf("Failed to create remote repo: %v", err) - } - - defer func() { - if err := deleteRemoteRepo(host, owner, repoName, token); err != nil { - t.Errorf("Failed to delete host repo %s/%s: %v", owner, repoName, err) - } - if repoExists, _ := checkRepoExists(host, owner, repoName, token); repoExists { - t.Errorf("Remote repository %s/%s was not deleted", owner, repoName) - } - }() - - oldDir, err := os.Getwd() - if err != nil { - t.Fatalf("Failed to get current dir: %v", err) - } - defer func() { - if err = os.Chdir(oldDir); err != nil { - t.Errorf("Failed to change back to original dir: %v", err) - } - }() - if err = os.Chdir(tmpDir); err != nil { - t.Fatalf("Failed to change dir %s: %v", tmpDir, err) - } - - repoDir := filepath.Join(tmpDir, repoName) - if err = os.Mkdir(repoDir, 0755); err != nil { - t.Fatalf("Failed to create repo dir %s: %v", repoDir, err) - } - if err = os.Chdir(repoDir); err != nil { - t.Fatalf("Failed to change to repo dir %s: %v", repoDir, err) - } - - cmd := exec.Command("git", "init") - if err = cmd.Run(); err != nil { - t.Fatalf("Failed to git init in %s: %v", repoDir, err) - } - - cred, err := conf.NewConfigure(nil).Load(remote) - if err != nil { - t.Fatalf("Parse config: %v", err) - } - - email, err := utils.ParseEmailFromToken(cred.AccessToken) - if err != nil { - t.Fatal(err) - } - - t.Log("Running CMD: ", "calypr_admin", - "collaborators", - "add", - email, - "/programs/test/projects/"+project, - "--project_id", - repoName, - "--profile", - remote, - "-w", - "-a") - - cmd = exec.Command( - "calypr_admin", - "collaborators", - "add", - email, - "/programs/test/projects/"+project, - "--project_id", - repoName, - "--profile", - remote, - "-w", - "-a", - ) - - var out []byte - if out, err = cmd.Output(); err != nil { - t.Fatalf("Failed to calypr_admin collaborators add %s: %s", repoName, out) - } - t.Logf("calypr_admin collaborators add: %s", string(out)) - - cmd = exec.Command("git", "lfs", "install", "--skip-smudge") - if err := cmd.Run(); err != nil { - t.Fatalf("Failed to git lfs install in %s: %v", repoDir, err) - } - - cmd = exec.Command("git-drs", "init") - output, err := cmd.CombinedOutput() - if err != nil { - t.Logf("git-drs init output: %s", output) - t.Fatalf("Failed to git-drs init in %s: %v", repoDir, err) - } - t.Logf("git-drs add remote output: %s", output) - - cmd = exec.Command("git-drs", "remote", "add", "gen3", remote, "--project", repoName, "--bucket", DEFAULT_BUCKET) - output, err = cmd.CombinedOutput() - if err != nil { - t.Logf("git-drs add remote output: %s", output) - t.Fatalf("Failed to git-drs add remote %s: %v", repoName, err) - } - t.Logf("git-drs add remote output: %s", output) - - // Verify .drs/config.yaml exists - configPath := filepath.Join(".drs", "config.yaml") - if _, err := os.Stat(configPath); err != nil { - if os.IsNotExist(err) { - t.Fatalf(".drs/config.yaml not created in %s", repoDir) - } - t.Fatalf("Failed to stat .drs/config.yaml in %s: %v", repoDir, err) - } - // Log config.yaml contents for debugging - configContent, err := os.ReadFile(configPath) - if err != nil { - t.Fatalf("Failed to read .drs/config.yaml: %v", err) - } else { - t.Logf(".drs/config.yaml contents: %s", configContent) - } - - cmd = exec.Command("git", "lfs", "track", "*.txt") - if err := cmd.Run(); err != nil { - t.Fatalf("Failed to git lfs track in %s: %v", repoDir, err) - } - - cmd = exec.Command("git", "add", ".gitattributes") - if err := cmd.Run(); err != nil { - t.Fatalf("Failed to git add .gitattributes in %s: %v", repoDir, err) - } - - // Create a dummy data file - dataFile := "data.txt" - // Make the string random so that each new indexd record that is created only exists for this specific integration test - if err := os.WriteFile(dataFile, []byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."+generateRandomString(4)), 0644); err != nil { - t.Fatalf("Failed to create data file %s: %v", dataFile, err) - } - - // Configure Git to use PAT for HTTPS push - cmd = exec.Command("git", "config", "credential.helper", fmt.Sprintf("!f() { echo username=x-oauth-basic; echo password=%s; }; f", token)) - if err := cmd.Run(); err != nil { - t.Fatalf("Failed to configure git credential helper in %s: %v", repoDir, err) - } - - cmd = exec.Command("git", "branch", "-M", "main") - if err := cmd.Run(); err != nil { - t.Fatalf("Failed to add main branch: %v", err) - } - - // add remote - remoteURL := fmt.Sprintf("%s/%s/%s.git", host, owner, repoName) - t.Log("Remote URL: ", remoteURL) - cmd = exec.Command("git", "remote", "add", remote, remoteURL) - if err := cmd.Run(); err != nil { - t.Fatalf("Failed to add remote %s: %v", remoteURL, err) - } - - // add + commit - cmd = exec.Command("git", "add", dataFile) - if err := cmd.Run(); err != nil { - t.Fatalf("Failed to git add data file %s: %v", dataFile, err) - } - - cmd = exec.Command("git", "commit", "-m", "Add test file") - output, err = cmd.CombinedOutput() - if err != nil { - t.Logf("Commit output: %s", output) - t.Fatalf("Failed to git commit in %s: %v", repoDir, err) - } - - // create a drs logger and pass it to GetAllLfsFiles - logger := &log.Logger{} - - // verify LFS files are listed - lfsFiles, err := drsmap.GetAllLfsFiles(remote, remoteURL, []string{"main"}, logger) - if err != nil { - t.Fatalf("Failed to get LFS files: %v", err) - } - if len(lfsFiles) != 1 { - t.Fatalf("Expected 1 LFS file, found %d", len(lfsFiles)) - } - - // push - cmd = exec.Command("git", "push") - output, err = cmd.CombinedOutput() - if err != nil { - t.Fatalf("Expected push failure with dummy DRS server: %v\nOutput: %s", err, output) - } else { - t.Log("Push succeeded with dummy DRS server") - } - t.Log("OUTPUT: ", string(output)) - - // test local files exist - path, err := drsmap.GetObjectPath(projectdir.DRS_OBJS_PATH, lfsFiles[dataFile].Oid) - if err != nil { - t.Fatalf("Failed to get object path %s: %v", path, err) - } - if path == "" { - t.Fatalf("Expecting path but got %s instead", path) - } - t.Logf("Path: %s", path) - - _, err = os.Stat(path) - - if os.IsNotExist(err) { - t.Fatalf("File or directory not found at path: %s", path) - } - if err != nil { - t.Fatalf("Error checking path existence %s: %v", path, err) - } - - // Clean up the initial repository - if err := os.Chdir(tmpDir); err != nil { - t.Fatalf("Failed to change back to tmp dir %s: %v", tmpDir, err) - } - if err := os.RemoveAll(repoDir); err != nil { - t.Errorf("Failed to remove initial repo dir %s: %v", repoDir, err) - } - - cloneDir, err := os.MkdirTemp("", "git-drs-clone-") - if err != nil { - t.Fatalf("Failed to create clone dir: %v", err) - } - t.Logf("Clone directory: %s", cloneDir) - defer func() { - if err := os.RemoveAll(cloneDir); err != nil { - t.Errorf("Failed to remove clone dir %s: %v", cloneDir, err) - } - }() - - if err := os.Chdir(cloneDir); err != nil { - t.Fatalf("Failed to change to clone dir %s: %v", cloneDir, err) - } - - // Clone the repository - cmd = exec.Command("git", "clone", remoteURL, "cloned-repo") - cmd.Env = append(os.Environ(), fmt.Sprintf("GIT_ASKPASS=echo %s", token)) - if cmdOut, err := cmd.Output(); err != nil { - t.Fatalf("Failed to git clone %s: %s", remoteURL, cmdOut) - } - - // Change to cloned repo - cloneRepoDir := filepath.Join(cloneDir, "cloned-repo") - if err := os.Chdir(cloneRepoDir); err != nil { - t.Fatalf("Failed to change to cloned repo dir %s: %v", cloneRepoDir, err) - } - - cmd = exec.Command("git-drs", "init") - output, err = cmd.CombinedOutput() - if err != nil { - t.Logf("git-drs init (clone) output: %s", output) - t.Fatalf("Failed to git-drs init in %s: %v", cloneRepoDir, err) - } - t.Logf("git-drs init (clone) output: %s", output) - - cmd = exec.Command("git-drs", "remote", "add", "gen3", remote, "--project", repoName, "--bucket", DEFAULT_BUCKET) - output, err = cmd.CombinedOutput() - if err != nil { - t.Logf("git-drs add remote output: %s", output) - t.Fatalf("Failed to git-drs add remote %s: %v", repoName, err) - } - t.Logf("git-drs add remote output: %s", output) - - cmd = exec.Command("git", "remote", "add", remote, remoteURL) - if err := cmd.Run(); err != nil { - t.Fatalf("Failed to add remote %s: %v", remoteURL, err) - } - - cmd = exec.Command("git", "lfs", "pull", remote, "-I", dataFile) - output, err = cmd.CombinedOutput() - if err != nil { - t.Fatalf("Expected pull failure with dummy DRS server: %v\nOutput: %s", err, output) - } else { - t.Log("Pull succeeded with dummy DRS server") - } - - // Verify data.txt exists (even if content fetch fails, pointer file should exist) - if _, err := os.Stat(dataFile); err != nil { - if os.IsNotExist(err) { - t.Fatalf("data.txt not found after git lfs pull in %s", cloneRepoDir) - } - t.Fatalf("Failed to stat data.txt in %s: %v", cloneRepoDir, err) - } - - // verify LFS files are listed - lfsFiles, err = drsmap.GetAllLfsFiles(remote, remoteURL, []string{"main"}, logger) - if err != nil { - t.Fatalf("Failed to get LFS files: %v", err) - } - - cmd = exec.Command("git-drs", "delete", "sha256", lfsFiles[dataFile].Oid, "--remote", remote, "--confirm") - _, err = cmd.Output() - if err != nil { - t.Fatalf("Failed to delete indexd record %s: %v", lfsFiles[dataFile].Oid, err) - } - - // Verify .gitattributes exists and contains the txt pattern - gitAttributes, err := os.ReadFile(".gitattributes") - if err != nil { - t.Fatalf("Failed to read .gitattributes in %s: %v", cloneRepoDir, err) - } - if string(gitAttributes) != "*.txt filter=lfs diff=lfs merge=lfs -text\n" { - t.Fatalf("Unexpected .gitattributes content in %s: %s", cloneRepoDir, gitAttributes) - } - - if err := os.Chdir(cloneDir); err != nil { - t.Fatalf("Failed to change back to clone dir %s: %v", cloneDir, err) - } - if err := os.RemoveAll(cloneRepoDir); err != nil { - t.Errorf("Failed to remove cloned repo dir %s: %v", cloneRepoDir, err) - } - -} - -// createRemoteRepo creates a new repo on GHE via API -func createRemoteRepo(host, owner, repoName, token string) error { - url := fmt.Sprintf("%s/api/v3/orgs/%s/repos", host, owner) - body := map[string]any{ - "name": repoName, - "description": "Test repo for git-drs e2e test", - "private": true, - } - jsonBody, err := sonic.ConfigFastest.Marshal(body) - if err != nil { - return fmt.Errorf("failed to marshal create repo request: %v", err) - } - - req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonBody)) - if err != nil { - return fmt.Errorf("failed to create repo HTTP request: %v", err) - } - req.Header.Set("Authorization", "token "+token) - //req.Header.Set("Accept", "application/vnd.github+json") - //req.Header.Set("X-GitHub-Api-Version", "2022-11-28") - - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - return fmt.Errorf("failed to send create repo request: %v", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusCreated { - bodyBytes, _ := io.ReadAll(resp.Body) - return fmt.Errorf("failed to create repo: %s %d %s", repoName, resp.StatusCode, string(bodyBytes)) - } - return nil -} - -// deleteRemoteRepo deletes the repo on GHE via API -func deleteRemoteRepo(host, owner, repoName, token string) error { - url := fmt.Sprintf("%s/api/v3/repos/%s/%s", host, owner, repoName) - - req, err := http.NewRequest("DELETE", url, nil) - if err != nil { - return fmt.Errorf("failed to create HTTP request: %v", err) - } - req.Header.Set("Authorization", "token "+token) - req.Header.Set("Accept", "application/vnd.github+json") - req.Header.Set("X-GitHub-Api-Version", "2022-11-28") - - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - return fmt.Errorf("failed to send delete repo request: %v", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusNoContent { - bodyBytes, _ := io.ReadAll(resp.Body) - return fmt.Errorf("failed to delete repo: %d %s", resp.StatusCode, string(bodyBytes)) - } - return nil -} - -// checkRepoExists checks if the repository exists via API -func checkRepoExists(host, owner, repoName, token string) (bool, error) { - url := fmt.Sprintf("%s/api/v3/repos/%s/%s", host, owner, repoName) - - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return false, fmt.Errorf("failed to create HTTP request: %v", err) - } - req.Header.Set("Authorization", "token "+token) - req.Header.Set("Accept", "application/vnd.github+json") - req.Header.Set("X-GitHub-Api-Version", "2022-11-28") - - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - return false, fmt.Errorf("failed to send check repo request: %v", err) - } - defer resp.Body.Close() - - if resp.StatusCode == http.StatusOK { - return true, nil - } - if resp.StatusCode == http.StatusNotFound { - return false, nil - } - bodyBytes, _ := io.ReadAll(resp.Body) - return false, fmt.Errorf("unexpected status code checking repo: %d %s", resp.StatusCode, string(bodyBytes)) -} - -// generateRandomString generates a truly random string for unique repo names -func generateRandomString(length int) string { - const chars = "abcdefghijklmnopqrstuvwxyz0123456789" - source := rand.NewSource(time.Now().UnixNano()) - r := rand.New(source) - result := make([]byte, length) - for i := range result { - result[i] = chars[r.Intn(len(chars))] - } - return string(result) -} From db47840d28beb5b99586a17a26a3fa51fe1b63ae Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Wed, 21 Jan 2026 16:48:35 -0800 Subject: [PATCH 37/40] tech-debt:TODO --- .../tests/{client_read_test.go => client_read_test.go.todo} | 0 .../tests/{client_write_test.go => client_write_test.go.todo} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename client/indexd/tests/{client_read_test.go => client_read_test.go.todo} (100%) rename client/indexd/tests/{client_write_test.go => client_write_test.go.todo} (100%) diff --git a/client/indexd/tests/client_read_test.go b/client/indexd/tests/client_read_test.go.todo similarity index 100% rename from client/indexd/tests/client_read_test.go rename to client/indexd/tests/client_read_test.go.todo diff --git a/client/indexd/tests/client_write_test.go b/client/indexd/tests/client_write_test.go.todo similarity index 100% rename from client/indexd/tests/client_write_test.go rename to client/indexd/tests/client_write_test.go.todo From 0158bea788542f2cbfc07c760ff7d46165be8590 Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Wed, 21 Jan 2026 16:52:49 -0800 Subject: [PATCH 38/40] wip: coverage #147 --- coverage/combined.out | 1632 ++++++++++++++++++++ coverage/integration/.gitignore | 1 + coverage/merged/.gitignore | 1 + coverage/unit/.gitignore | 1 + tests/coverage-test.sh | 28 +- tests/monorepos/run-test.sh | 18 +- tests/scripts/coverage/combine-coverage.sh | 31 + 7 files changed, 1699 insertions(+), 13 deletions(-) create mode 100644 coverage/combined.out create mode 100644 coverage/integration/.gitignore create mode 100644 coverage/merged/.gitignore create mode 100644 coverage/unit/.gitignore create mode 100755 tests/scripts/coverage/combine-coverage.sh diff --git a/coverage/combined.out b/coverage/combined.out new file mode 100644 index 00000000..148539f8 --- /dev/null +++ b/coverage/combined.out @@ -0,0 +1,1632 @@ +mode: atomic +github.com/calypr/git-drs/git-drs.go:11.13,14.16 2 126 +github.com/calypr/git-drs/git-drs.go:14.16,17.3 2 0 +github.com/calypr/git-drs/git-drs.go:19.2,19.46 1 126 +github.com/calypr/git-drs/git-drs.go:19.46,22.3 2 0 +github.com/calypr/git-drs/client/anvil/anvil_client.go:23.75,26.16 2 0 +github.com/calypr/git-drs/client/anvil/anvil_client.go:26.16,28.3 1 0 +github.com/calypr/git-drs/client/anvil/anvil_client.go:30.2,35.16 3 0 +github.com/calypr/git-drs/client/anvil/anvil_client.go:35.16,37.3 1 0 +github.com/calypr/git-drs/client/anvil/anvil_client.go:39.2,40.16 2 0 +github.com/calypr/git-drs/client/anvil/anvil_client.go:40.16,42.3 1 0 +github.com/calypr/git-drs/client/anvil/anvil_client.go:43.2,48.16 5 0 +github.com/calypr/git-drs/client/anvil/anvil_client.go:48.16,50.3 1 0 +github.com/calypr/git-drs/client/anvil/anvil_client.go:51.2,54.27 3 0 +github.com/calypr/git-drs/client/anvil/anvil_client.go:54.27,59.47 4 0 +github.com/calypr/git-drs/client/anvil/anvil_client.go:59.47,61.4 1 0 +github.com/calypr/git-drs/client/anvil/anvil_client.go:62.3,62.43 1 0 +github.com/calypr/git-drs/client/anvil/anvil_client.go:68.2,73.64 2 0 +github.com/calypr/git-drs/client/anvil/anvil_client.go:73.64,75.3 1 0 +github.com/calypr/git-drs/client/anvil/anvil_client.go:77.2,83.8 1 0 +github.com/calypr/git-drs/client/anvil/anvil_client.go:88.37,91.16 3 0 +github.com/calypr/git-drs/client/anvil/anvil_client.go:91.16,93.3 1 0 +github.com/calypr/git-drs/client/anvil/anvil_client.go:95.2,97.16 3 0 +github.com/calypr/git-drs/client/anvil/anvil_client.go:97.16,99.3 1 0 +github.com/calypr/git-drs/client/anvil/anvil_client.go:101.2,101.47 1 0 +github.com/calypr/git-drs/client/anvil/anvil_client.go:101.47,103.3 1 0 +github.com/calypr/git-drs/client/anvil/anvil_client.go:105.2,105.31 1 0 +github.com/calypr/git-drs/client/anvil/remote.go:21.44,23.2 1 0 +github.com/calypr/git-drs/client/anvil/remote.go:25.43,27.2 1 0 +github.com/calypr/git-drs/client/anvil/remote.go:29.45,31.2 1 0 +github.com/calypr/git-drs/client/anvil/remote.go:33.104,36.2 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:30.132,36.2 3 0 +github.com/calypr/git-drs/client/indexd/add_url.go:40.218,44.16 2 0 +github.com/calypr/git-drs/client/indexd/add_url.go:44.16,46.3 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:49.2,53.21 4 0 +github.com/calypr/git-drs/client/indexd/add_url.go:53.21,56.17 2 0 +github.com/calypr/git-drs/client/indexd/add_url.go:56.17,58.4 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:61.3,64.47 2 0 +github.com/calypr/git-drs/client/indexd/add_url.go:64.47,72.4 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:75.3,76.19 2 0 +github.com/calypr/git-drs/client/indexd/add_url.go:76.19,78.4 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:78.9,78.40 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:78.40,80.4 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:81.3,81.24 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:81.24,83.4 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:86.3,86.29 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:86.29,88.18 2 0 +github.com/calypr/git-drs/client/indexd/add_url.go:88.18,90.5 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:94.3,95.21 2 0 +github.com/calypr/git-drs/client/indexd/add_url.go:95.21,98.4 2 0 +github.com/calypr/git-drs/client/indexd/add_url.go:98.9,98.45 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:98.45,100.4 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:104.3,108.56 3 0 +github.com/calypr/git-drs/client/indexd/add_url.go:108.56,109.27 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:109.27,111.5 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:112.4,112.25 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:118.2,118.24 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:118.24,122.34 2 0 +github.com/calypr/git-drs/client/indexd/add_url.go:122.34,124.45 2 0 +github.com/calypr/git-drs/client/indexd/add_url.go:124.45,126.5 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:130.3,130.24 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:130.24,132.4 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:135.3,135.26 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:135.26,137.4 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:144.3,144.29 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:144.29,147.40 3 0 +github.com/calypr/git-drs/client/indexd/add_url.go:147.40,149.5 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:150.4,157.47 8 0 +github.com/calypr/git-drs/client/indexd/add_url.go:162.2,162.21 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:162.21,164.3 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:166.2,172.16 3 0 +github.com/calypr/git-drs/client/indexd/add_url.go:172.16,174.3 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:176.2,177.31 2 0 +github.com/calypr/git-drs/client/indexd/add_url.go:177.31,179.3 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:179.8,181.3 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:183.2,183.67 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:188.212,192.16 2 0 +github.com/calypr/git-drs/client/indexd/add_url.go:192.16,194.3 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:196.2,197.16 2 0 +github.com/calypr/git-drs/client/indexd/add_url.go:197.16,199.3 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:200.2,200.26 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:200.26,203.3 2 0 +github.com/calypr/git-drs/client/indexd/add_url.go:205.2,205.132 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:211.132,217.16 4 0 +github.com/calypr/git-drs/client/indexd/add_url.go:217.16,219.3 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:221.2,222.16 2 0 +github.com/calypr/git-drs/client/indexd/add_url.go:222.16,224.3 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:226.2,226.56 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:226.56,228.85 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:228.85,231.4 2 0 +github.com/calypr/git-drs/client/indexd/add_url.go:234.3,234.115 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:234.115,239.18 4 0 +github.com/calypr/git-drs/client/indexd/add_url.go:239.18,241.5 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:242.4,242.22 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:247.2,249.16 3 0 +github.com/calypr/git-drs/client/indexd/add_url.go:249.16,251.3 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:252.2,253.16 2 0 +github.com/calypr/git-drs/client/indexd/add_url.go:253.16,255.3 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:257.2,269.16 3 0 +github.com/calypr/git-drs/client/indexd/add_url.go:269.16,271.3 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:273.2,275.16 2 0 +github.com/calypr/git-drs/client/indexd/add_url.go:275.16,277.3 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:278.2,278.20 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:282.165,289.27 4 0 +github.com/calypr/git-drs/client/indexd/add_url.go:289.27,291.3 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:294.2,294.23 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:294.23,296.3 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:299.2,299.63 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:299.63,301.3 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:304.2,305.16 2 0 +github.com/calypr/git-drs/client/indexd/add_url.go:305.16,307.3 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:310.2,311.16 2 0 +github.com/calypr/git-drs/client/indexd/add_url.go:311.16,313.3 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:314.2,314.12 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:314.12,316.3 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:319.2,321.16 3 0 +github.com/calypr/git-drs/client/indexd/add_url.go:321.16,323.43 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:323.43,325.4 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:326.3,326.79 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:330.2,337.16 6 0 +github.com/calypr/git-drs/client/indexd/add_url.go:337.16,339.3 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:342.2,343.16 2 0 +github.com/calypr/git-drs/client/indexd/add_url.go:343.16,345.3 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:346.2,346.71 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:346.71,348.3 1 0 +github.com/calypr/git-drs/client/indexd/add_url.go:350.2,355.8 2 0 +github.com/calypr/git-drs/client/indexd/auth_handler.go:22.67,24.2 1 66 +github.com/calypr/git-drs/client/indexd/auth_handler.go:27.59,31.16 3 66 +github.com/calypr/git-drs/client/indexd/auth_handler.go:31.16,33.3 1 0 +github.com/calypr/git-drs/client/indexd/auth_handler.go:36.2,36.54 1 66 +github.com/calypr/git-drs/client/indexd/auth_handler.go:36.54,37.45 1 66 +github.com/calypr/git-drs/client/indexd/auth_handler.go:37.45,41.4 3 66 +github.com/calypr/git-drs/client/indexd/auth_handler.go:43.2,43.100 1 0 +github.com/calypr/git-drs/client/indexd/auth_handler.go:47.69,52.16 3 66 +github.com/calypr/git-drs/client/indexd/auth_handler.go:52.16,54.3 1 0 +github.com/calypr/git-drs/client/indexd/auth_handler.go:54.8,54.42 1 66 +github.com/calypr/git-drs/client/indexd/auth_handler.go:54.42,56.3 1 0 +github.com/calypr/git-drs/client/indexd/auth_handler.go:58.2,58.17 1 66 +github.com/calypr/git-drs/client/indexd/auth_handler.go:58.17,62.17 3 0 +github.com/calypr/git-drs/client/indexd/auth_handler.go:62.17,64.4 1 0 +github.com/calypr/git-drs/client/indexd/auth_handler.go:65.3,68.10 4 0 +github.com/calypr/git-drs/client/indexd/auth_handler.go:68.10,70.4 1 0 +github.com/calypr/git-drs/client/indexd/auth_handler.go:71.3,72.17 2 0 +github.com/calypr/git-drs/client/indexd/auth_handler.go:72.17,74.4 1 0 +github.com/calypr/git-drs/client/indexd/auth_handler.go:75.3,77.17 3 0 +github.com/calypr/git-drs/client/indexd/auth_handler.go:77.17,79.4 1 0 +github.com/calypr/git-drs/client/indexd/auth_handler.go:81.2,81.12 1 66 +github.com/calypr/git-drs/client/indexd/auth_handler.go:84.68,86.2 1 66 +github.com/calypr/git-drs/client/indexd/auth_handler.go:88.92,90.16 2 66 +github.com/calypr/git-drs/client/indexd/auth_handler.go:90.16,92.3 1 0 +github.com/calypr/git-drs/client/indexd/auth_handler.go:94.2,96.12 3 66 +github.com/calypr/git-drs/client/indexd/convert.go:13.78,25.2 2 12 +github.com/calypr/git-drs/client/indexd/convert.go:27.79,29.16 2 24 +github.com/calypr/git-drs/client/indexd/convert.go:29.16,31.3 1 0 +github.com/calypr/git-drs/client/indexd/convert.go:32.2,32.35 1 24 +github.com/calypr/git-drs/client/indexd/convert.go:32.35,33.64 1 24 +github.com/calypr/git-drs/client/indexd/convert.go:33.64,35.4 1 0 +github.com/calypr/git-drs/client/indexd/convert.go:38.2,46.8 1 24 +github.com/calypr/git-drs/client/indexd/convert.go:49.96,51.33 2 24 +github.com/calypr/git-drs/client/indexd/convert.go:51.33,56.17 4 24 +github.com/calypr/git-drs/client/indexd/convert.go:56.17,58.4 1 0 +github.com/calypr/git-drs/client/indexd/convert.go:59.3,59.26 1 24 +github.com/calypr/git-drs/client/indexd/convert.go:59.26,62.4 1 0 +github.com/calypr/git-drs/client/indexd/convert.go:62.9,64.4 1 24 +github.com/calypr/git-drs/client/indexd/convert.go:67.3,67.19 1 24 +github.com/calypr/git-drs/client/indexd/convert.go:67.19,69.4 1 0 +github.com/calypr/git-drs/client/indexd/convert.go:73.3,74.48 2 24 +github.com/calypr/git-drs/client/indexd/convert.go:76.2,76.27 1 24 +github.com/calypr/git-drs/client/indexd/convert.go:80.81,82.39 2 12 +github.com/calypr/git-drs/client/indexd/convert.go:82.39,83.35 1 12 +github.com/calypr/git-drs/client/indexd/convert.go:83.35,85.4 1 12 +github.com/calypr/git-drs/client/indexd/convert.go:87.2,87.14 1 12 +github.com/calypr/git-drs/client/indexd/convert.go:90.76,92.39 2 12 +github.com/calypr/git-drs/client/indexd/convert.go:92.39,94.3 1 12 +github.com/calypr/git-drs/client/indexd/convert.go:95.2,95.13 1 12 +github.com/calypr/git-drs/client/indexd/convert.go:98.64,100.16 2 0 +github.com/calypr/git-drs/client/indexd/convert.go:100.16,102.3 1 0 +github.com/calypr/git-drs/client/indexd/convert.go:103.2,103.15 1 0 +github.com/calypr/git-drs/client/indexd/gen3_remote.go:17.43,19.2 1 108 +github.com/calypr/git-drs/client/indexd/gen3_remote.go:21.42,23.2 1 0 +github.com/calypr/git-drs/client/indexd/gen3_remote.go:25.44,27.2 1 108 +github.com/calypr/git-drs/client/indexd/gen3_remote.go:29.103,31.16 2 108 +github.com/calypr/git-drs/client/indexd/gen3_remote.go:31.16,33.3 1 0 +github.com/calypr/git-drs/client/indexd/gen3_remote.go:34.2,34.42 1 108 +github.com/calypr/git-drs/client/indexd/indexd_client.go:55.118,60.21 3 108 +github.com/calypr/git-drs/client/indexd/indexd_client.go:60.21,62.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:64.2,65.22 2 108 +github.com/calypr/git-drs/client/indexd/indexd_client.go:65.22,67.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:69.2,82.99 5 108 +github.com/calypr/git-drs/client/indexd/indexd_client.go:82.99,83.69 1 66 +github.com/calypr/git-drs/client/indexd/indexd_client.go:83.69,88.4 1 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:89.3,89.38 1 60 +github.com/calypr/git-drs/client/indexd/indexd_client.go:89.38,93.22 3 60 +github.com/calypr/git-drs/client/indexd/indexd_client.go:93.22,94.62 1 60 +github.com/calypr/git-drs/client/indexd/indexd_client.go:94.62,97.6 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:101.3,101.58 1 60 +github.com/calypr/git-drs/client/indexd/indexd_client.go:104.2,111.16 6 108 +github.com/calypr/git-drs/client/indexd/indexd_client.go:111.16,113.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:115.2,128.8 3 108 +github.com/calypr/git-drs/client/indexd/indexd_client.go:131.47,133.2 1 36 +github.com/calypr/git-drs/client/indexd/indexd_client.go:135.76,140.16 4 108 +github.com/calypr/git-drs/client/indexd/indexd_client.go:140.16,142.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:144.2,147.16 3 108 +github.com/calypr/git-drs/client/indexd/indexd_client.go:147.16,149.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:150.2,150.20 1 108 +github.com/calypr/git-drs/client/indexd/indexd_client.go:153.77,158.16 4 108 +github.com/calypr/git-drs/client/indexd/indexd_client.go:158.16,160.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:162.2,165.16 3 108 +github.com/calypr/git-drs/client/indexd/indexd_client.go:165.16,167.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:169.2,169.32 1 108 +github.com/calypr/git-drs/client/indexd/indexd_client.go:169.32,171.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:173.2,173.20 1 108 +github.com/calypr/git-drs/client/indexd/indexd_client.go:178.54,179.53 1 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:179.53,181.3 1 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:182.2,182.85 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:185.72,187.16 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:187.16,189.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:190.2,190.24 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:190.24,191.77 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:191.77,192.50 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:192.50,194.19 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:194.19,196.14 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:201.2,201.12 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:204.56,207.16 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:207.16,209.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:210.2,210.22 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:210.22,212.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:215.2,216.16 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:216.16,218.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:219.2,219.27 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:219.27,221.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:224.2,224.49 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:227.62,230.16 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:230.16,232.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:235.2,237.16 3 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:237.16,239.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:241.2,242.16 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:242.16,244.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:246.2,249.16 3 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:249.16,251.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:252.2,255.31 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:255.31,257.21 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:257.21,259.4 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:260.3,261.99 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:263.2,263.12 1 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:266.87,269.16 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:269.16,271.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:273.2,273.46 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:277.76,283.16 3 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:283.16,286.3 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:287.2,287.51 1 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:290.112,291.23 1 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:291.23,294.3 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:297.2,298.16 2 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:298.16,301.3 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:302.2,302.27 1 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:302.27,305.3 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:307.2,311.36 3 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:311.36,314.3 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:317.2,318.22 2 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:318.22,321.3 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:322.2,325.16 3 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:325.16,327.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:329.2,329.24 1 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:333.94,339.16 4 24 +github.com/calypr/git-drs/client/indexd/indexd_client.go:339.16,341.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:343.2,344.16 2 24 +github.com/calypr/git-drs/client/indexd/indexd_client.go:344.16,346.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:348.2,349.16 2 24 +github.com/calypr/git-drs/client/indexd/indexd_client.go:349.16,351.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:352.2,352.15 1 24 +github.com/calypr/git-drs/client/indexd/indexd_client.go:352.15,353.57 1 24 +github.com/calypr/git-drs/client/indexd/indexd_client.go:353.57,355.4 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:358.2,362.20 3 24 +github.com/calypr/git-drs/client/indexd/indexd_client.go:362.20,364.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:366.2,366.68 1 24 +github.com/calypr/git-drs/client/indexd/indexd_client.go:366.68,368.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:371.2,371.25 1 24 +github.com/calypr/git-drs/client/indexd/indexd_client.go:371.25,373.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:375.2,377.23 2 24 +github.com/calypr/git-drs/client/indexd/indexd_client.go:385.74,390.16 3 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:390.16,392.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:395.2,396.16 2 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:396.16,398.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:401.2,402.16 2 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:402.16,404.54 1 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:404.54,405.18 1 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:405.18,407.5 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:407.10,410.19 3 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:410.19,412.6 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:413.5,414.19 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:414.19,416.6 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:419.9,421.4 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:425.2,426.16 2 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:426.16,428.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:429.2,429.18 1 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:429.18,432.3 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:435.2,436.16 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:436.16,438.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:441.2,447.16 4 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:447.16,449.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:451.2,452.16 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:452.16,454.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:455.2,456.16 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:456.16,458.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:459.2,459.28 1 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:459.28,461.17 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:461.17,463.4 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:466.2,466.44 1 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:466.44,469.17 3 3 +github.com/calypr/git-drs/client/indexd/indexd_client.go:469.17,471.4 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:472.8,486.17 3 3 +github.com/calypr/git-drs/client/indexd/indexd_client.go:486.17,488.4 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:490.2,490.23 1 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:494.84,495.22 1 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:495.22,497.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:498.2,498.39 1 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:498.39,501.3 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:502.2,504.16 3 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:504.16,507.3 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:508.2,508.25 1 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:508.25,510.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:512.2,513.16 2 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:513.16,516.3 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:517.2,518.18 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:521.70,527.16 4 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:527.16,529.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:531.2,532.16 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:532.16,534.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:536.2,537.16 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:537.16,539.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:540.2,542.30 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:542.30,544.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:546.2,547.73 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:547.73,549.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:550.2,550.53 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:554.98,560.16 5 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:560.16,562.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:564.2,569.12 4 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:569.12,576.14 4 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:576.14,578.18 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:578.18,580.10 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:583.4,589.68 6 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:589.68,591.10 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:594.4,595.18 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:595.18,597.10 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:601.4,604.18 3 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:604.18,606.10 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:609.4,609.44 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:609.44,611.10 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:614.4,615.60 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:615.60,617.10 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:620.4,620.30 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:620.30,622.5 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:624.4,624.38 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:624.38,626.19 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:626.19,629.14 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:631.5,631.47 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:633.4,633.13 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:637.3,637.26 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:637.26,639.4 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:642.2,642.17 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:647.95,654.16 3 18 +github.com/calypr/git-drs/client/indexd/indexd_client.go:654.16,656.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:658.2,666.16 5 18 +github.com/calypr/git-drs/client/indexd/indexd_client.go:666.16,668.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:670.2,675.16 4 18 +github.com/calypr/git-drs/client/indexd/indexd_client.go:675.16,677.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:679.2,681.16 3 18 +github.com/calypr/git-drs/client/indexd/indexd_client.go:681.16,683.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:684.2,688.42 3 18 +github.com/calypr/git-drs/client/indexd/indexd_client.go:688.42,691.3 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:692.2,696.16 3 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:696.16,698.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:699.2,699.20 1 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:703.62,706.16 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:706.16,708.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:711.2,713.16 3 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:713.16,715.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:717.2,718.16 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:718.16,720.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:722.2,724.16 3 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:724.16,726.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:727.2,729.31 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:729.31,731.21 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:731.21,733.4 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:734.3,735.99 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:737.2,737.12 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:741.86,746.16 4 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:746.16,749.3 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:750.2,753.16 3 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:753.16,755.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:756.2,760.16 3 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:760.16,762.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:763.2,765.38 2 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:765.38,768.3 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:771.2,773.16 3 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:773.16,775.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:777.2,782.31 3 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:782.31,784.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:786.2,788.41 2 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:788.41,791.34 2 36 +github.com/calypr/git-drs/client/indexd/indexd_client.go:791.34,792.25 1 36 +github.com/calypr/git-drs/client/indexd/indexd_client.go:792.25,794.10 2 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:797.3,797.13 1 36 +github.com/calypr/git-drs/client/indexd/indexd_client.go:797.13,798.12 1 24 +github.com/calypr/git-drs/client/indexd/indexd_client.go:801.3,802.17 2 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:802.17,804.4 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:805.3,805.29 1 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:808.2,808.17 1 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:813.96,814.16 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:814.16,816.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:818.2,822.16 3 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:822.16,824.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:826.2,827.33 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:827.33,828.29 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:828.29,830.4 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:831.3,834.27 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:834.27,836.14 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:836.14,837.23 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:837.24,838.6 0 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:840.4,840.9 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:844.2,845.20 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:849.73,861.12 7 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:861.12,864.14 3 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:864.14,867.18 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:867.18,871.5 3 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:873.4,879.18 6 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:879.18,883.5 3 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:886.4,888.18 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:888.18,892.5 3 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:894.4,896.18 3 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:896.18,900.5 3 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:901.4,901.44 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:901.44,905.5 3 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:908.4,910.18 3 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:910.18,914.5 3 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:915.4,915.41 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:915.41,917.5 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:918.4,918.33 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:918.33,920.5 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:921.4,921.13 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:924.3,924.57 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:926.2,926.17 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:931.101,934.16 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:934.16,936.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:939.2,950.39 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:950.39,953.46 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:953.46,955.4 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:956.3,960.68 3 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:964.2,964.27 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:964.27,966.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:969.2,969.30 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:969.30,971.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:974.2,974.34 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:974.34,975.36 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:975.36,977.4 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:978.3,978.65 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:981.2,982.16 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:982.16,984.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:986.2,992.16 4 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:992.16,994.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:997.2,1001.16 4 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1001.16,1003.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1005.2,1009.16 3 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1009.16,1011.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1012.2,1015.42 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1015.42,1018.3 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1020.2,1024.16 3 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1024.16,1026.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1028.2,1029.27 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1033.79,1037.16 3 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1037.16,1039.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1041.2,1042.16 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1042.16,1044.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1045.2,1048.16 3 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1048.16,1050.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1051.2,1053.38 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1053.38,1056.3 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1058.2,1059.72 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1059.72,1061.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1063.2,1063.20 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1066.121,1068.18 2 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1068.18,1070.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1073.2,1076.16 3 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1076.16,1078.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1079.2,1093.21 3 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1097.79,1101.16 3 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1101.16,1103.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1105.2,1106.16 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1106.16,1108.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1109.2,1112.16 3 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1112.16,1114.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1115.2,1117.38 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1117.38,1120.3 2 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1122.2,1123.72 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1123.72,1125.3 1 0 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1127.2,1127.20 1 6 +github.com/calypr/git-drs/client/indexd/records.go:90.62,102.2 1 12 +github.com/calypr/git-drs/client/indexd/utils.go:21.174,23.23 1 0 +github.com/calypr/git-drs/client/indexd/utils.go:23.23,25.3 1 0 +github.com/calypr/git-drs/client/indexd/utils.go:27.2,28.16 2 0 +github.com/calypr/git-drs/client/indexd/utils.go:28.16,30.3 1 0 +github.com/calypr/git-drs/client/indexd/utils.go:33.2,33.24 1 0 +github.com/calypr/git-drs/client/indexd/utils.go:33.24,34.56 1 0 +github.com/calypr/git-drs/client/indexd/utils.go:34.56,36.4 1 0 +github.com/calypr/git-drs/client/indexd/utils.go:39.2,40.16 2 0 +github.com/calypr/git-drs/client/indexd/utils.go:40.16,42.3 1 0 +github.com/calypr/git-drs/client/indexd/utils.go:43.2,45.38 2 0 +github.com/calypr/git-drs/client/indexd/utils.go:45.38,47.3 1 0 +github.com/calypr/git-drs/client/indexd/utils.go:50.2,51.86 2 0 +github.com/calypr/git-drs/client/indexd/utils.go:51.86,53.3 1 0 +github.com/calypr/git-drs/client/indexd/utils.go:55.2,55.58 1 0 +github.com/calypr/git-drs/client/indexd/utils.go:55.58,56.50 1 0 +github.com/calypr/git-drs/client/indexd/utils.go:56.50,58.4 1 0 +github.com/calypr/git-drs/client/indexd/utils.go:59.3,59.72 1 0 +github.com/calypr/git-drs/client/indexd/utils.go:62.2,62.17 1 0 +github.com/calypr/git-drs/cmd/root.go:30.61,32.3 0 126 +github.com/calypr/git-drs/cmd/root.go:35.13,58.2 21 126 +github.com/calypr/git-drs/cmd/addref/add-ref.go:21.54,30.17 6 0 +github.com/calypr/git-drs/cmd/addref/add-ref.go:30.17,32.4 1 0 +github.com/calypr/git-drs/cmd/addref/add-ref.go:34.3,35.17 2 0 +github.com/calypr/git-drs/cmd/addref/add-ref.go:35.17,38.4 2 0 +github.com/calypr/git-drs/cmd/addref/add-ref.go:40.3,41.17 2 0 +github.com/calypr/git-drs/cmd/addref/add-ref.go:41.17,43.4 1 0 +github.com/calypr/git-drs/cmd/addref/add-ref.go:45.3,46.17 2 0 +github.com/calypr/git-drs/cmd/addref/add-ref.go:46.17,48.4 1 0 +github.com/calypr/git-drs/cmd/addref/add-ref.go:49.3,50.70 2 0 +github.com/calypr/git-drs/cmd/addref/add-ref.go:50.70,51.51 1 0 +github.com/calypr/git-drs/cmd/addref/add-ref.go:51.51,53.5 1 0 +github.com/calypr/git-drs/cmd/addref/add-ref.go:55.3,55.19 1 0 +github.com/calypr/git-drs/cmd/addref/add-ref.go:55.19,57.4 1 0 +github.com/calypr/git-drs/cmd/addref/add-ref.go:58.3,60.25 3 0 +github.com/calypr/git-drs/cmd/addref/add-ref.go:60.25,63.4 1 0 +github.com/calypr/git-drs/cmd/addref/add-ref.go:65.3,66.13 2 0 +github.com/calypr/git-drs/cmd/addref/add-ref.go:70.13,72.2 1 126 +github.com/calypr/git-drs/cmd/addurl/main.go:21.54,22.21 1 0 +github.com/calypr/git-drs/cmd/addurl/main.go:22.21,25.4 2 0 +github.com/calypr/git-drs/cmd/addurl/main.go:26.3,26.13 1 0 +github.com/calypr/git-drs/cmd/addurl/main.go:28.54,33.41 3 0 +github.com/calypr/git-drs/cmd/addurl/main.go:33.41,35.4 1 0 +github.com/calypr/git-drs/cmd/addurl/main.go:38.3,47.95 8 0 +github.com/calypr/git-drs/cmd/addurl/main.go:47.95,49.4 1 0 +github.com/calypr/git-drs/cmd/addurl/main.go:52.3,52.47 1 0 +github.com/calypr/git-drs/cmd/addurl/main.go:52.47,54.4 1 0 +github.com/calypr/git-drs/cmd/addurl/main.go:56.3,57.17 2 0 +github.com/calypr/git-drs/cmd/addurl/main.go:57.17,59.4 1 0 +github.com/calypr/git-drs/cmd/addurl/main.go:61.3,62.17 2 0 +github.com/calypr/git-drs/cmd/addurl/main.go:62.17,64.4 1 0 +github.com/calypr/git-drs/cmd/addurl/main.go:66.3,67.17 2 0 +github.com/calypr/git-drs/cmd/addurl/main.go:67.17,69.4 1 0 +github.com/calypr/git-drs/cmd/addurl/main.go:72.3,73.17 2 0 +github.com/calypr/git-drs/cmd/addurl/main.go:73.17,75.4 1 0 +github.com/calypr/git-drs/cmd/addurl/main.go:78.3,79.17 2 0 +github.com/calypr/git-drs/cmd/addurl/main.go:79.17,81.4 1 0 +github.com/calypr/git-drs/cmd/addurl/main.go:82.3,82.77 1 0 +github.com/calypr/git-drs/cmd/addurl/main.go:82.77,84.4 1 0 +github.com/calypr/git-drs/cmd/addurl/main.go:85.3,86.13 2 0 +github.com/calypr/git-drs/cmd/addurl/main.go:90.13,96.2 5 126 +github.com/calypr/git-drs/cmd/addurl/main.go:98.80,103.66 2 0 +github.com/calypr/git-drs/cmd/addurl/main.go:103.66,105.3 1 0 +github.com/calypr/git-drs/cmd/addurl/main.go:108.2,108.77 1 0 +github.com/calypr/git-drs/cmd/addurl/main.go:108.77,110.3 1 0 +github.com/calypr/git-drs/cmd/addurl/main.go:113.2,114.34 2 0 +github.com/calypr/git-drs/cmd/addurl/main.go:114.34,116.3 1 0 +github.com/calypr/git-drs/cmd/addurl/main.go:118.2,118.12 1 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:20.54,25.17 3 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:25.17,27.4 1 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:28.3,36.17 5 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:36.17,38.4 1 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:41.3,42.30 2 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:42.30,44.4 1 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:47.3,49.23 3 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:49.23,51.4 1 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:54.3,54.7 1 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:54.7,56.18 2 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:56.18,57.22 1 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:57.22,58.11 1 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:60.5,60.62 1 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:62.4,68.18 5 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:68.18,70.5 1 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:72.4,72.67 1 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:72.67,74.5 1 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:76.4,77.72 2 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:77.72,79.5 1 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:82.4,83.18 2 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:83.18,85.5 1 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:86.4,86.70 1 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:86.70,88.5 1 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:89.4,89.70 1 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:89.70,91.5 1 0 +github.com/calypr/git-drs/cmd/cache/create-cache.go:94.3,95.13 2 0 +github.com/calypr/git-drs/cmd/delete/main.go:28.54,32.51 2 0 +github.com/calypr/git-drs/cmd/delete/main.go:32.51,34.4 1 0 +github.com/calypr/git-drs/cmd/delete/main.go:36.3,39.17 3 0 +github.com/calypr/git-drs/cmd/delete/main.go:39.17,41.4 1 0 +github.com/calypr/git-drs/cmd/delete/main.go:43.3,44.17 2 0 +github.com/calypr/git-drs/cmd/delete/main.go:44.17,46.4 1 0 +github.com/calypr/git-drs/cmd/delete/main.go:48.3,49.17 2 0 +github.com/calypr/git-drs/cmd/delete/main.go:49.17,52.4 2 0 +github.com/calypr/git-drs/cmd/delete/main.go:55.3,56.17 2 0 +github.com/calypr/git-drs/cmd/delete/main.go:56.17,58.4 1 0 +github.com/calypr/git-drs/cmd/delete/main.go:59.3,59.24 1 0 +github.com/calypr/git-drs/cmd/delete/main.go:59.24,61.4 1 0 +github.com/calypr/git-drs/cmd/delete/main.go:64.3,66.17 3 0 +github.com/calypr/git-drs/cmd/delete/main.go:66.17,68.4 1 0 +github.com/calypr/git-drs/cmd/delete/main.go:69.3,69.28 1 0 +github.com/calypr/git-drs/cmd/delete/main.go:69.28,71.4 1 0 +github.com/calypr/git-drs/cmd/delete/main.go:74.3,74.19 1 0 +github.com/calypr/git-drs/cmd/delete/main.go:74.19,81.33 7 0 +github.com/calypr/git-drs/cmd/delete/main.go:81.33,83.5 1 0 +github.com/calypr/git-drs/cmd/delete/main.go:84.4,87.129 3 0 +github.com/calypr/git-drs/cmd/delete/main.go:87.129,89.5 1 0 +github.com/calypr/git-drs/cmd/delete/main.go:93.3,94.17 2 0 +github.com/calypr/git-drs/cmd/delete/main.go:94.17,96.4 1 0 +github.com/calypr/git-drs/cmd/delete/main.go:98.3,99.13 2 0 +github.com/calypr/git-drs/cmd/delete/main.go:103.13,106.2 2 126 +github.com/calypr/git-drs/cmd/deleteproject/main.go:26.54,31.17 4 0 +github.com/calypr/git-drs/cmd/deleteproject/main.go:31.17,33.4 1 0 +github.com/calypr/git-drs/cmd/deleteproject/main.go:35.3,36.17 2 0 +github.com/calypr/git-drs/cmd/deleteproject/main.go:36.17,38.4 1 0 +github.com/calypr/git-drs/cmd/deleteproject/main.go:40.3,41.17 2 0 +github.com/calypr/git-drs/cmd/deleteproject/main.go:41.17,44.4 2 0 +github.com/calypr/git-drs/cmd/deleteproject/main.go:47.3,48.10 2 0 +github.com/calypr/git-drs/cmd/deleteproject/main.go:48.10,50.4 1 0 +github.com/calypr/git-drs/cmd/deleteproject/main.go:53.3,54.17 2 0 +github.com/calypr/git-drs/cmd/deleteproject/main.go:54.17,56.4 1 0 +github.com/calypr/git-drs/cmd/deleteproject/main.go:59.3,59.52 1 0 +github.com/calypr/git-drs/cmd/deleteproject/main.go:59.52,61.4 1 0 +github.com/calypr/git-drs/cmd/deleteproject/main.go:62.3,62.31 1 0 +github.com/calypr/git-drs/cmd/deleteproject/main.go:62.31,67.30 4 0 +github.com/calypr/git-drs/cmd/deleteproject/main.go:67.30,71.26 4 0 +github.com/calypr/git-drs/cmd/deleteproject/main.go:71.26,73.6 1 0 +github.com/calypr/git-drs/cmd/deleteproject/main.go:74.5,75.33 2 0 +github.com/calypr/git-drs/cmd/deleteproject/main.go:75.33,77.6 1 0 +github.com/calypr/git-drs/cmd/deleteproject/main.go:78.10,80.5 1 0 +github.com/calypr/git-drs/cmd/deleteproject/main.go:82.4,85.154 3 0 +github.com/calypr/git-drs/cmd/deleteproject/main.go:85.154,87.5 1 0 +github.com/calypr/git-drs/cmd/deleteproject/main.go:91.3,93.17 3 0 +github.com/calypr/git-drs/cmd/deleteproject/main.go:93.17,95.4 1 0 +github.com/calypr/git-drs/cmd/deleteproject/main.go:97.3,98.13 2 0 +github.com/calypr/git-drs/cmd/deleteproject/main.go:102.13,105.2 2 126 +github.com/calypr/git-drs/cmd/download/main.go:25.54,26.21 1 0 +github.com/calypr/git-drs/cmd/download/main.go:26.21,29.4 2 0 +github.com/calypr/git-drs/cmd/download/main.go:30.3,30.13 1 0 +github.com/calypr/git-drs/cmd/download/main.go:32.54,38.17 4 0 +github.com/calypr/git-drs/cmd/download/main.go:38.17,40.4 1 0 +github.com/calypr/git-drs/cmd/download/main.go:42.3,43.17 2 0 +github.com/calypr/git-drs/cmd/download/main.go:43.17,45.4 1 0 +github.com/calypr/git-drs/cmd/download/main.go:47.3,48.17 2 0 +github.com/calypr/git-drs/cmd/download/main.go:48.17,51.4 2 0 +github.com/calypr/git-drs/cmd/download/main.go:54.3,55.17 2 0 +github.com/calypr/git-drs/cmd/download/main.go:55.17,57.4 1 0 +github.com/calypr/git-drs/cmd/download/main.go:58.3,58.26 1 0 +github.com/calypr/git-drs/cmd/download/main.go:58.26,60.4 1 0 +github.com/calypr/git-drs/cmd/download/main.go:63.3,63.20 1 0 +github.com/calypr/git-drs/cmd/download/main.go:63.20,65.4 1 0 +github.com/calypr/git-drs/cmd/download/main.go:66.3,66.17 1 0 +github.com/calypr/git-drs/cmd/download/main.go:66.17,68.4 1 0 +github.com/calypr/git-drs/cmd/download/main.go:69.3,70.17 2 0 +github.com/calypr/git-drs/cmd/download/main.go:70.17,72.4 1 0 +github.com/calypr/git-drs/cmd/download/main.go:74.3,74.17 1 0 +github.com/calypr/git-drs/cmd/download/main.go:74.17,76.4 1 0 +github.com/calypr/git-drs/cmd/download/main.go:78.3,80.13 2 0 +github.com/calypr/git-drs/cmd/download/main.go:84.13,87.2 2 126 +github.com/calypr/git-drs/cmd/fetch/main.go:16.54,17.20 1 0 +github.com/calypr/git-drs/cmd/fetch/main.go:17.20,20.4 2 0 +github.com/calypr/git-drs/cmd/fetch/main.go:21.3,21.13 1 0 +github.com/calypr/git-drs/cmd/fetch/main.go:23.54,27.17 3 0 +github.com/calypr/git-drs/cmd/fetch/main.go:27.17,29.4 1 0 +github.com/calypr/git-drs/cmd/fetch/main.go:31.3,32.20 2 0 +github.com/calypr/git-drs/cmd/fetch/main.go:32.20,34.4 1 0 +github.com/calypr/git-drs/cmd/fetch/main.go:34.9,36.18 2 0 +github.com/calypr/git-drs/cmd/fetch/main.go:36.18,39.5 2 0 +github.com/calypr/git-drs/cmd/fetch/main.go:42.3,43.17 2 0 +github.com/calypr/git-drs/cmd/fetch/main.go:43.17,46.4 2 0 +github.com/calypr/git-drs/cmd/fetch/main.go:48.3,49.17 2 0 +github.com/calypr/git-drs/cmd/fetch/main.go:49.17,51.4 1 0 +github.com/calypr/git-drs/cmd/fetch/main.go:53.3,53.13 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:29.54,30.21 1 12 +github.com/calypr/git-drs/cmd/initialize/main.go:30.21,33.4 2 0 +github.com/calypr/git-drs/cmd/initialize/main.go:34.3,34.13 1 12 +github.com/calypr/git-drs/cmd/initialize/main.go:36.54,41.17 3 12 +github.com/calypr/git-drs/cmd/initialize/main.go:41.17,43.4 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:46.3,47.17 2 12 +github.com/calypr/git-drs/cmd/initialize/main.go:47.17,49.4 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:52.3,53.17 2 12 +github.com/calypr/git-drs/cmd/initialize/main.go:53.17,56.4 2 0 +github.com/calypr/git-drs/cmd/initialize/main.go:59.3,63.45 4 12 +github.com/calypr/git-drs/cmd/initialize/main.go:63.45,64.64 1 36 +github.com/calypr/git-drs/cmd/initialize/main.go:64.64,66.5 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:70.3,72.17 3 12 +github.com/calypr/git-drs/cmd/initialize/main.go:72.17,74.4 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:75.3,75.22 1 12 +github.com/calypr/git-drs/cmd/initialize/main.go:75.22,77.4 1 6 +github.com/calypr/git-drs/cmd/initialize/main.go:77.9,79.4 1 6 +github.com/calypr/git-drs/cmd/initialize/main.go:82.3,83.49 2 12 +github.com/calypr/git-drs/cmd/initialize/main.go:83.49,85.4 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:89.3,90.17 2 12 +github.com/calypr/git-drs/cmd/initialize/main.go:90.17,92.4 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:95.3,96.17 2 12 +github.com/calypr/git-drs/cmd/initialize/main.go:96.17,98.4 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:101.3,103.13 3 12 +github.com/calypr/git-drs/cmd/initialize/main.go:107.28,118.31 2 12 +github.com/calypr/git-drs/cmd/initialize/main.go:118.31,120.46 2 72 +github.com/calypr/git-drs/cmd/initialize/main.go:120.46,122.4 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:125.2,125.12 1 12 +github.com/calypr/git-drs/cmd/initialize/main.go:128.13,130.2 1 126 +github.com/calypr/git-drs/cmd/initialize/main.go:132.51,135.16 3 12 +github.com/calypr/git-drs/cmd/initialize/main.go:135.16,137.3 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:138.2,140.52 3 12 +github.com/calypr/git-drs/cmd/initialize/main.go:140.52,142.3 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:144.2,164.16 5 12 +github.com/calypr/git-drs/cmd/initialize/main.go:164.16,169.73 3 6 +github.com/calypr/git-drs/cmd/initialize/main.go:169.73,171.4 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:172.3,172.45 1 6 +github.com/calypr/git-drs/cmd/initialize/main.go:172.45,174.4 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:175.3,175.75 1 6 +github.com/calypr/git-drs/cmd/initialize/main.go:178.2,178.39 1 12 +github.com/calypr/git-drs/cmd/initialize/main.go:178.39,180.3 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:182.2,183.16 2 12 +github.com/calypr/git-drs/cmd/initialize/main.go:183.16,185.3 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:186.2,187.12 2 12 +github.com/calypr/git-drs/cmd/initialize/main.go:192.77,202.16 5 36 +github.com/calypr/git-drs/cmd/initialize/main.go:202.16,205.22 3 36 +github.com/calypr/git-drs/cmd/initialize/main.go:205.22,208.48 2 180 +github.com/calypr/git-drs/cmd/initialize/main.go:208.48,210.5 1 36 +github.com/calypr/git-drs/cmd/initialize/main.go:211.4,211.31 1 180 +github.com/calypr/git-drs/cmd/initialize/main.go:213.3,213.39 1 36 +github.com/calypr/git-drs/cmd/initialize/main.go:213.39,215.4 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:216.8,216.31 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:216.31,219.3 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:219.8,221.3 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:223.2,223.11 1 36 +github.com/calypr/git-drs/cmd/initialize/main.go:223.11,225.3 1 36 +github.com/calypr/git-drs/cmd/initialize/main.go:228.2,228.68 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:228.68,230.3 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:231.2,235.16 3 0 +github.com/calypr/git-drs/cmd/initialize/main.go:235.16,237.3 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:238.2,241.26 3 0 +github.com/calypr/git-drs/cmd/initialize/main.go:241.26,242.12 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:242.12,244.4 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:245.3,245.26 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:248.2,249.34 2 0 +github.com/calypr/git-drs/cmd/initialize/main.go:249.34,251.3 1 0 +github.com/calypr/git-drs/cmd/initialize/main.go:253.2,253.12 1 0 +github.com/calypr/git-drs/cmd/list/main.go:25.69,26.22 1 0 +github.com/calypr/git-drs/cmd/list/main.go:26.22,27.13 1 0 +github.com/calypr/git-drs/cmd/list/main.go:27.13,29.4 1 0 +github.com/calypr/git-drs/cmd/list/main.go:31.2,31.11 1 0 +github.com/calypr/git-drs/cmd/list/main.go:35.47,38.79 3 0 +github.com/calypr/git-drs/cmd/list/main.go:38.79,40.28 2 0 +github.com/calypr/git-drs/cmd/list/main.go:40.28,43.4 2 0 +github.com/calypr/git-drs/cmd/list/main.go:45.2,45.15 1 0 +github.com/calypr/git-drs/cmd/list/main.go:52.54,53.21 1 0 +github.com/calypr/git-drs/cmd/list/main.go:53.21,56.4 2 0 +github.com/calypr/git-drs/cmd/list/main.go:57.3,57.13 1 0 +github.com/calypr/git-drs/cmd/list/main.go:59.54,63.24 3 0 +github.com/calypr/git-drs/cmd/list/main.go:63.24,65.18 2 0 +github.com/calypr/git-drs/cmd/list/main.go:65.18,67.5 1 0 +github.com/calypr/git-drs/cmd/list/main.go:68.4,69.17 2 0 +github.com/calypr/git-drs/cmd/list/main.go:70.9,72.4 1 0 +github.com/calypr/git-drs/cmd/list/main.go:74.3,75.17 2 0 +github.com/calypr/git-drs/cmd/list/main.go:75.17,77.4 1 0 +github.com/calypr/git-drs/cmd/list/main.go:79.3,80.17 2 0 +github.com/calypr/git-drs/cmd/list/main.go:80.17,82.4 1 0 +github.com/calypr/git-drs/cmd/list/main.go:84.3,85.17 2 0 +github.com/calypr/git-drs/cmd/list/main.go:85.17,88.4 2 0 +github.com/calypr/git-drs/cmd/list/main.go:89.3,90.17 2 0 +github.com/calypr/git-drs/cmd/list/main.go:90.17,92.4 1 0 +github.com/calypr/git-drs/cmd/list/main.go:93.3,93.15 1 0 +github.com/calypr/git-drs/cmd/list/main.go:93.15,95.4 1 0 +github.com/calypr/git-drs/cmd/list/main.go:98.3,98.34 1 0 +github.com/calypr/git-drs/cmd/list/main.go:98.34,99.30 1 0 +github.com/calypr/git-drs/cmd/list/main.go:99.30,101.5 1 0 +github.com/calypr/git-drs/cmd/list/main.go:102.4,103.15 2 0 +github.com/calypr/git-drs/cmd/list/main.go:103.15,105.19 2 0 +github.com/calypr/git-drs/cmd/list/main.go:105.19,107.6 1 0 +github.com/calypr/git-drs/cmd/list/main.go:108.5,108.48 1 0 +github.com/calypr/git-drs/cmd/list/main.go:109.10,111.5 1 0 +github.com/calypr/git-drs/cmd/list/main.go:113.3,113.13 1 0 +github.com/calypr/git-drs/cmd/list/main.go:119.54,120.21 1 0 +github.com/calypr/git-drs/cmd/list/main.go:120.21,123.4 2 0 +github.com/calypr/git-drs/cmd/list/main.go:124.3,124.13 1 0 +github.com/calypr/git-drs/cmd/list/main.go:126.54,130.17 3 0 +github.com/calypr/git-drs/cmd/list/main.go:130.17,132.4 1 0 +github.com/calypr/git-drs/cmd/list/main.go:134.3,135.17 2 0 +github.com/calypr/git-drs/cmd/list/main.go:135.17,137.4 1 0 +github.com/calypr/git-drs/cmd/list/main.go:139.3,140.17 2 0 +github.com/calypr/git-drs/cmd/list/main.go:140.17,142.4 1 0 +github.com/calypr/git-drs/cmd/list/main.go:143.3,144.17 2 0 +github.com/calypr/git-drs/cmd/list/main.go:144.17,146.4 1 0 +github.com/calypr/git-drs/cmd/list/main.go:148.3,150.20 3 0 +github.com/calypr/git-drs/cmd/list/main.go:150.20,152.18 2 0 +github.com/calypr/git-drs/cmd/list/main.go:152.18,154.5 1 0 +github.com/calypr/git-drs/cmd/list/main.go:155.4,156.17 2 0 +github.com/calypr/git-drs/cmd/list/main.go:157.9,159.4 1 0 +github.com/calypr/git-drs/cmd/list/main.go:160.3,160.34 1 0 +github.com/calypr/git-drs/cmd/list/main.go:160.34,161.30 1 0 +github.com/calypr/git-drs/cmd/list/main.go:161.30,163.5 1 0 +github.com/calypr/git-drs/cmd/list/main.go:164.4,166.18 3 0 +github.com/calypr/git-drs/cmd/list/main.go:166.18,168.5 1 0 +github.com/calypr/git-drs/cmd/list/main.go:169.4,170.18 2 0 +github.com/calypr/git-drs/cmd/list/main.go:170.18,172.5 1 0 +github.com/calypr/git-drs/cmd/list/main.go:173.4,174.18 2 0 +github.com/calypr/git-drs/cmd/list/main.go:174.18,176.5 1 0 +github.com/calypr/git-drs/cmd/list/main.go:178.3,178.13 1 0 +github.com/calypr/git-drs/cmd/list/main.go:182.13,188.2 5 126 +github.com/calypr/git-drs/cmd/listconfig/main.go:22.54,23.21 1 0 +github.com/calypr/git-drs/cmd/listconfig/main.go:23.21,26.4 2 0 +github.com/calypr/git-drs/cmd/listconfig/main.go:27.3,27.13 1 0 +github.com/calypr/git-drs/cmd/listconfig/main.go:29.54,32.17 2 0 +github.com/calypr/git-drs/cmd/listconfig/main.go:32.17,34.4 1 0 +github.com/calypr/git-drs/cmd/listconfig/main.go:36.3,36.17 1 0 +github.com/calypr/git-drs/cmd/listconfig/main.go:36.17,41.4 3 0 +github.com/calypr/git-drs/cmd/listconfig/main.go:41.9,48.4 4 0 +github.com/calypr/git-drs/cmd/listconfig/main.go:52.13,54.2 1 126 +github.com/calypr/git-drs/cmd/prepush/main.go:24.54,27.17 2 12 +github.com/calypr/git-drs/cmd/prepush/main.go:27.17,29.4 1 0 +github.com/calypr/git-drs/cmd/prepush/main.go:31.3,34.17 3 12 +github.com/calypr/git-drs/cmd/prepush/main.go:34.17,36.4 1 0 +github.com/calypr/git-drs/cmd/prepush/main.go:42.3,44.21 3 12 +github.com/calypr/git-drs/cmd/prepush/main.go:44.21,46.4 1 12 +github.com/calypr/git-drs/cmd/prepush/main.go:47.3,47.21 1 12 +github.com/calypr/git-drs/cmd/prepush/main.go:47.21,49.4 1 12 +github.com/calypr/git-drs/cmd/prepush/main.go:50.3,50.26 1 12 +github.com/calypr/git-drs/cmd/prepush/main.go:50.26,52.4 1 0 +github.com/calypr/git-drs/cmd/prepush/main.go:53.3,58.17 4 12 +github.com/calypr/git-drs/cmd/prepush/main.go:58.17,63.4 3 0 +github.com/calypr/git-drs/cmd/prepush/main.go:66.3,67.17 2 12 +github.com/calypr/git-drs/cmd/prepush/main.go:67.17,72.4 3 0 +github.com/calypr/git-drs/cmd/prepush/main.go:74.3,75.10 2 12 +github.com/calypr/git-drs/cmd/prepush/main.go:75.10,77.4 1 0 +github.com/calypr/git-drs/cmd/prepush/main.go:78.3,82.17 3 12 +github.com/calypr/git-drs/cmd/prepush/main.go:82.17,85.4 2 0 +github.com/calypr/git-drs/cmd/prepush/main.go:86.3,86.16 1 12 +github.com/calypr/git-drs/cmd/prepush/main.go:86.16,89.4 2 12 +github.com/calypr/git-drs/cmd/prepush/main.go:92.3,92.51 1 12 +github.com/calypr/git-drs/cmd/prepush/main.go:92.51,95.4 2 0 +github.com/calypr/git-drs/cmd/prepush/main.go:98.3,98.43 1 12 +github.com/calypr/git-drs/cmd/prepush/main.go:98.43,101.4 2 0 +github.com/calypr/git-drs/cmd/prepush/main.go:104.3,105.17 2 12 +github.com/calypr/git-drs/cmd/prepush/main.go:105.17,108.4 2 0 +github.com/calypr/git-drs/cmd/prepush/main.go:110.3,112.17 3 12 +github.com/calypr/git-drs/cmd/prepush/main.go:112.17,115.4 2 0 +github.com/calypr/git-drs/cmd/prepush/main.go:116.3,119.13 3 12 +github.com/calypr/git-drs/cmd/prepush/main.go:126.55,130.40 1 12 +github.com/calypr/git-drs/cmd/prepush/main.go:130.40,132.3 1 0 +github.com/calypr/git-drs/cmd/prepush/main.go:133.2,135.21 3 12 +github.com/calypr/git-drs/cmd/prepush/main.go:135.21,138.22 3 12 +github.com/calypr/git-drs/cmd/prepush/main.go:138.22,139.12 1 0 +github.com/calypr/git-drs/cmd/prepush/main.go:141.3,143.42 3 12 +github.com/calypr/git-drs/cmd/prepush/main.go:143.42,145.20 2 12 +github.com/calypr/git-drs/cmd/prepush/main.go:145.20,147.5 1 12 +github.com/calypr/git-drs/cmd/prepush/main.go:150.2,150.38 1 12 +github.com/calypr/git-drs/cmd/prepush/main.go:150.38,152.3 1 0 +github.com/calypr/git-drs/cmd/prepush/main.go:153.2,154.21 2 12 +github.com/calypr/git-drs/cmd/prepush/main.go:154.21,156.3 1 12 +github.com/calypr/git-drs/cmd/prepush/main.go:157.2,159.40 2 12 +github.com/calypr/git-drs/cmd/prepush/main.go:159.40,161.3 1 0 +github.com/calypr/git-drs/cmd/prepush/main.go:162.2,162.22 1 12 +github.com/calypr/git-drs/cmd/push/main.go:16.54,17.20 1 0 +github.com/calypr/git-drs/cmd/push/main.go:17.20,20.4 2 0 +github.com/calypr/git-drs/cmd/push/main.go:21.3,21.13 1 0 +github.com/calypr/git-drs/cmd/push/main.go:23.54,26.17 3 0 +github.com/calypr/git-drs/cmd/push/main.go:26.17,29.4 2 0 +github.com/calypr/git-drs/cmd/push/main.go:31.3,32.20 2 0 +github.com/calypr/git-drs/cmd/push/main.go:32.20,34.4 1 0 +github.com/calypr/git-drs/cmd/push/main.go:34.9,36.18 2 0 +github.com/calypr/git-drs/cmd/push/main.go:36.18,39.5 2 0 +github.com/calypr/git-drs/cmd/push/main.go:42.3,43.17 2 0 +github.com/calypr/git-drs/cmd/push/main.go:43.17,46.4 2 0 +github.com/calypr/git-drs/cmd/push/main.go:48.3,49.17 2 0 +github.com/calypr/git-drs/cmd/push/main.go:49.17,51.4 1 0 +github.com/calypr/git-drs/cmd/push/main.go:53.3,53.13 1 0 +github.com/calypr/git-drs/cmd/query/main.go:19.54,20.21 1 0 +github.com/calypr/git-drs/cmd/query/main.go:20.21,23.4 2 0 +github.com/calypr/git-drs/cmd/query/main.go:24.3,24.13 1 0 +github.com/calypr/git-drs/cmd/query/main.go:26.54,30.17 3 0 +github.com/calypr/git-drs/cmd/query/main.go:30.17,32.4 1 0 +github.com/calypr/git-drs/cmd/query/main.go:34.3,35.17 2 0 +github.com/calypr/git-drs/cmd/query/main.go:35.17,38.4 2 0 +github.com/calypr/git-drs/cmd/query/main.go:40.3,41.17 2 0 +github.com/calypr/git-drs/cmd/query/main.go:41.17,43.4 1 0 +github.com/calypr/git-drs/cmd/query/main.go:45.3,46.17 2 0 +github.com/calypr/git-drs/cmd/query/main.go:46.17,48.4 1 0 +github.com/calypr/git-drs/cmd/query/main.go:49.3,50.17 2 0 +github.com/calypr/git-drs/cmd/query/main.go:50.17,52.4 1 0 +github.com/calypr/git-drs/cmd/query/main.go:53.3,54.13 2 0 +github.com/calypr/git-drs/cmd/query/main.go:58.13,60.2 1 126 +github.com/calypr/git-drs/cmd/register/main.go:20.54,21.21 1 0 +github.com/calypr/git-drs/cmd/register/main.go:21.21,24.4 2 0 +github.com/calypr/git-drs/cmd/register/main.go:25.3,25.13 1 0 +github.com/calypr/git-drs/cmd/register/main.go:27.54,29.17 2 0 +github.com/calypr/git-drs/cmd/register/main.go:29.17,31.4 1 0 +github.com/calypr/git-drs/cmd/register/main.go:33.3,34.17 2 0 +github.com/calypr/git-drs/cmd/register/main.go:34.17,36.4 1 0 +github.com/calypr/git-drs/cmd/register/main.go:38.3,39.17 2 0 +github.com/calypr/git-drs/cmd/register/main.go:39.17,42.4 2 0 +github.com/calypr/git-drs/cmd/register/main.go:44.3,45.17 2 0 +github.com/calypr/git-drs/cmd/register/main.go:45.17,47.4 1 0 +github.com/calypr/git-drs/cmd/register/main.go:48.3,49.10 2 0 +github.com/calypr/git-drs/cmd/register/main.go:49.10,51.4 1 0 +github.com/calypr/git-drs/cmd/register/main.go:54.3,55.17 2 0 +github.com/calypr/git-drs/cmd/register/main.go:55.17,57.4 1 0 +github.com/calypr/git-drs/cmd/register/main.go:59.3,59.31 1 0 +github.com/calypr/git-drs/cmd/register/main.go:59.31,62.4 2 0 +github.com/calypr/git-drs/cmd/register/main.go:64.3,71.38 5 0 +github.com/calypr/git-drs/cmd/register/main.go:71.38,76.18 3 0 +github.com/calypr/git-drs/cmd/register/main.go:76.18,79.13 3 0 +github.com/calypr/git-drs/cmd/register/main.go:83.4,84.18 2 0 +github.com/calypr/git-drs/cmd/register/main.go:84.18,87.13 3 0 +github.com/calypr/git-drs/cmd/register/main.go:91.4,92.35 2 0 +github.com/calypr/git-drs/cmd/register/main.go:92.35,93.34 1 0 +github.com/calypr/git-drs/cmd/register/main.go:93.34,95.11 2 0 +github.com/calypr/git-drs/cmd/register/main.go:99.4,99.21 1 0 +github.com/calypr/git-drs/cmd/register/main.go:99.21,102.13 3 0 +github.com/calypr/git-drs/cmd/register/main.go:106.4,107.18 2 0 +github.com/calypr/git-drs/cmd/register/main.go:107.18,110.13 3 0 +github.com/calypr/git-drs/cmd/register/main.go:113.4,114.21 2 0 +github.com/calypr/git-drs/cmd/register/main.go:118.3,121.21 2 0 +github.com/calypr/git-drs/cmd/register/main.go:121.21,123.4 1 0 +github.com/calypr/git-drs/cmd/register/main.go:125.3,125.13 1 0 +github.com/calypr/git-drs/cmd/register/main.go:129.13,131.2 1 126 +github.com/calypr/git-drs/cmd/remote/list.go:14.54,15.21 1 0 +github.com/calypr/git-drs/cmd/remote/list.go:15.21,18.4 2 0 +github.com/calypr/git-drs/cmd/remote/list.go:19.3,19.13 1 0 +github.com/calypr/git-drs/cmd/remote/list.go:21.54,24.17 3 0 +github.com/calypr/git-drs/cmd/remote/list.go:24.17,27.4 2 0 +github.com/calypr/git-drs/cmd/remote/list.go:29.3,29.47 1 0 +github.com/calypr/git-drs/cmd/remote/list.go:29.47,33.17 3 0 +github.com/calypr/git-drs/cmd/remote/list.go:33.17,35.5 1 0 +github.com/calypr/git-drs/cmd/remote/list.go:38.4,40.32 3 0 +github.com/calypr/git-drs/cmd/remote/list.go:40.32,43.5 2 0 +github.com/calypr/git-drs/cmd/remote/list.go:43.10,43.40 1 0 +github.com/calypr/git-drs/cmd/remote/list.go:43.40,46.5 2 0 +github.com/calypr/git-drs/cmd/remote/list.go:46.10,48.5 1 0 +github.com/calypr/git-drs/cmd/remote/list.go:50.4,51.21 2 0 +github.com/calypr/git-drs/cmd/remote/list.go:51.21,53.5 1 0 +github.com/calypr/git-drs/cmd/remote/list.go:55.4,55.72 1 0 +github.com/calypr/git-drs/cmd/remote/list.go:57.3,57.13 1 0 +github.com/calypr/git-drs/cmd/remote/root.go:14.13,18.2 3 126 +github.com/calypr/git-drs/cmd/remote/set.go:15.54,16.21 1 0 +github.com/calypr/git-drs/cmd/remote/set.go:16.21,19.4 2 0 +github.com/calypr/git-drs/cmd/remote/set.go:20.3,20.13 1 0 +github.com/calypr/git-drs/cmd/remote/set.go:22.54,27.17 4 0 +github.com/calypr/git-drs/cmd/remote/set.go:27.17,29.4 1 0 +github.com/calypr/git-drs/cmd/remote/set.go:32.3,33.40 2 0 +github.com/calypr/git-drs/cmd/remote/set.go:33.40,35.34 2 0 +github.com/calypr/git-drs/cmd/remote/set.go:35.34,37.5 1 0 +github.com/calypr/git-drs/cmd/remote/set.go:38.4,42.5 1 0 +github.com/calypr/git-drs/cmd/remote/set.go:46.3,48.48 2 0 +github.com/calypr/git-drs/cmd/remote/set.go:48.48,50.4 1 0 +github.com/calypr/git-drs/cmd/remote/set.go:52.3,53.13 2 0 +github.com/calypr/git-drs/cmd/remote/add/anvil.go:14.54,15.20 1 0 +github.com/calypr/git-drs/cmd/remote/add/anvil.go:15.20,18.4 2 0 +github.com/calypr/git-drs/cmd/remote/add/anvil.go:19.3,19.13 1 0 +github.com/calypr/git-drs/cmd/remote/add/anvil.go:21.54,23.3 1 0 +github.com/calypr/git-drs/cmd/remote/add/anvil.go:26.63,28.24 1 0 +github.com/calypr/git-drs/cmd/remote/add/anvil.go:28.24,41.17 4 0 +github.com/calypr/git-drs/cmd/remote/add/anvil.go:41.17,43.4 1 0 +github.com/calypr/git-drs/cmd/remote/add/anvil.go:47.2,47.12 1 0 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:17.54,18.20 1 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:18.20,21.4 2 0 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:22.3,22.13 1 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:24.54,28.59 2 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:28.59,30.4 1 0 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:33.3,33.19 1 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:33.19,35.4 1 0 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:37.3,38.20 2 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:38.20,40.4 1 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:42.3,43.17 2 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:43.17,45.4 1 0 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:46.3,46.13 1 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:50.96,51.22 1 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:51.22,53.3 1 0 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:54.2,54.35 1 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:54.35,56.3 1 0 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:58.2,60.9 3 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:61.24,65.17 4 0 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:65.17,67.4 1 0 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:69.22,71.17 2 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:71.17,73.4 1 0 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:74.3,79.17 5 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:79.17,81.4 1 0 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:83.10,85.17 2 0 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:85.17,90.4 4 0 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:90.9,92.4 1 0 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:95.2,95.23 1 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:95.23,97.3 1 0 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:99.2,108.67 3 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:108.67,110.3 1 0 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:111.2,124.45 3 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:124.45,126.3 1 0 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:128.2,129.12 2 6 +github.com/calypr/git-drs/cmd/remote/add/init.go:21.13,32.2 9 126 +github.com/calypr/git-drs/cmd/transfer/main.go:47.54,59.22 8 96 +github.com/calypr/git-drs/cmd/transfer/main.go:59.22,64.4 4 0 +github.com/calypr/git-drs/cmd/transfer/main.go:66.3,69.64 4 96 +github.com/calypr/git-drs/cmd/transfer/main.go:69.64,73.4 3 0 +github.com/calypr/git-drs/cmd/transfer/main.go:75.3,75.30 1 96 +github.com/calypr/git-drs/cmd/transfer/main.go:75.30,80.4 4 0 +github.com/calypr/git-drs/cmd/transfer/main.go:82.3,86.17 3 96 +github.com/calypr/git-drs/cmd/transfer/main.go:86.17,90.4 3 0 +github.com/calypr/git-drs/cmd/transfer/main.go:93.3,94.17 2 96 +github.com/calypr/git-drs/cmd/transfer/main.go:94.17,98.4 3 0 +github.com/calypr/git-drs/cmd/transfer/main.go:100.3,101.17 2 96 +github.com/calypr/git-drs/cmd/transfer/main.go:101.17,105.4 3 0 +github.com/calypr/git-drs/cmd/transfer/main.go:108.3,108.87 1 96 +github.com/calypr/git-drs/cmd/transfer/main.go:108.87,111.4 2 96 +github.com/calypr/git-drs/cmd/transfer/main.go:111.9,116.4 4 0 +github.com/calypr/git-drs/cmd/transfer/main.go:117.3,117.64 1 96 +github.com/calypr/git-drs/cmd/transfer/main.go:117.64,120.4 2 0 +github.com/calypr/git-drs/cmd/transfer/main.go:122.3,122.22 1 96 +github.com/calypr/git-drs/cmd/transfer/main.go:122.22,125.18 3 120 +github.com/calypr/git-drs/cmd/transfer/main.go:125.18,127.13 2 0 +github.com/calypr/git-drs/cmd/transfer/main.go:130.4,130.56 1 120 +github.com/calypr/git-drs/cmd/transfer/main.go:130.56,136.76 3 12 +github.com/calypr/git-drs/cmd/transfer/main.go:136.76,140.14 4 0 +github.com/calypr/git-drs/cmd/transfer/main.go:142.5,146.19 3 12 +github.com/calypr/git-drs/cmd/transfer/main.go:146.19,150.14 4 0 +github.com/calypr/git-drs/cmd/transfer/main.go:152.5,152.28 1 12 +github.com/calypr/git-drs/cmd/transfer/main.go:152.28,156.14 4 0 +github.com/calypr/git-drs/cmd/transfer/main.go:160.5,161.19 2 12 +github.com/calypr/git-drs/cmd/transfer/main.go:161.19,165.14 4 0 +github.com/calypr/git-drs/cmd/transfer/main.go:167.5,168.19 2 12 +github.com/calypr/git-drs/cmd/transfer/main.go:168.19,172.14 4 0 +github.com/calypr/git-drs/cmd/transfer/main.go:176.5,178.70 2 12 +github.com/calypr/git-drs/cmd/transfer/main.go:180.10,180.61 1 108 +github.com/calypr/git-drs/cmd/transfer/main.go:180.61,186.74 3 12 +github.com/calypr/git-drs/cmd/transfer/main.go:186.74,190.14 4 0 +github.com/calypr/git-drs/cmd/transfer/main.go:192.5,194.19 3 12 +github.com/calypr/git-drs/cmd/transfer/main.go:194.19,198.14 4 0 +github.com/calypr/git-drs/cmd/transfer/main.go:201.5,202.63 2 12 +github.com/calypr/git-drs/cmd/transfer/main.go:204.10,204.64 1 96 +github.com/calypr/git-drs/cmd/transfer/main.go:204.64,206.5 1 96 +github.com/calypr/git-drs/cmd/transfer/main.go:209.3,209.39 1 96 +github.com/calypr/git-drs/cmd/transfer/main.go:209.39,211.4 1 0 +github.com/calypr/git-drs/cmd/transfer/main.go:213.3,214.13 2 96 +github.com/calypr/git-drs/cmd/transferref/main.go:32.54,42.17 6 0 +github.com/calypr/git-drs/cmd/transferref/main.go:42.17,45.4 2 0 +github.com/calypr/git-drs/cmd/transferref/main.go:47.3,50.22 2 0 +github.com/calypr/git-drs/cmd/transferref/main.go:50.22,56.4 4 0 +github.com/calypr/git-drs/cmd/transferref/main.go:58.3,59.70 2 0 +github.com/calypr/git-drs/cmd/transferref/main.go:59.70,62.4 2 0 +github.com/calypr/git-drs/cmd/transferref/main.go:65.3,65.55 1 0 +github.com/calypr/git-drs/cmd/transferref/main.go:65.55,68.18 2 0 +github.com/calypr/git-drs/cmd/transferref/main.go:68.18,72.5 3 0 +github.com/calypr/git-drs/cmd/transferref/main.go:73.4,77.30 3 0 +github.com/calypr/git-drs/cmd/transferref/main.go:78.9,83.4 4 0 +github.com/calypr/git-drs/cmd/transferref/main.go:85.3,86.17 2 0 +github.com/calypr/git-drs/cmd/transferref/main.go:86.17,90.4 3 0 +github.com/calypr/git-drs/cmd/transferref/main.go:92.3,92.22 1 0 +github.com/calypr/git-drs/cmd/transferref/main.go:92.22,95.18 3 0 +github.com/calypr/git-drs/cmd/transferref/main.go:95.18,97.13 2 0 +github.com/calypr/git-drs/cmd/transferref/main.go:99.4,102.52 2 0 +github.com/calypr/git-drs/cmd/transferref/main.go:102.52,109.5 3 0 +github.com/calypr/git-drs/cmd/transferref/main.go:109.10,109.63 1 0 +github.com/calypr/git-drs/cmd/transferref/main.go:109.63,115.76 3 0 +github.com/calypr/git-drs/cmd/transferref/main.go:115.76,119.14 4 0 +github.com/calypr/git-drs/cmd/transferref/main.go:123.5,124.19 2 0 +github.com/calypr/git-drs/cmd/transferref/main.go:124.19,128.14 4 0 +github.com/calypr/git-drs/cmd/transferref/main.go:131.5,140.32 4 0 +github.com/calypr/git-drs/cmd/transferref/main.go:141.10,141.61 1 0 +github.com/calypr/git-drs/cmd/transferref/main.go:141.61,148.74 4 0 +github.com/calypr/git-drs/cmd/transferref/main.go:148.74,152.6 3 0 +github.com/calypr/git-drs/cmd/transferref/main.go:153.5,161.32 4 0 +github.com/calypr/git-drs/cmd/transferref/main.go:162.10,162.64 1 0 +github.com/calypr/git-drs/cmd/transferref/main.go:162.64,165.5 1 0 +github.com/calypr/git-drs/cmd/transferref/main.go:168.3,168.39 1 0 +github.com/calypr/git-drs/cmd/transferref/main.go:168.39,170.4 1 0 +github.com/calypr/git-drs/cmd/transferref/main.go:172.3,174.13 2 0 +github.com/calypr/git-drs/cmd/transferref/main.go:178.69,185.16 4 0 +github.com/calypr/git-drs/cmd/transferref/main.go:185.16,187.3 1 0 +github.com/calypr/git-drs/cmd/transferref/main.go:189.2,190.16 2 0 +github.com/calypr/git-drs/cmd/transferref/main.go:190.16,192.3 1 0 +github.com/calypr/git-drs/cmd/transferref/main.go:194.2,197.16 3 0 +github.com/calypr/git-drs/cmd/transferref/main.go:197.16,199.3 1 0 +github.com/calypr/git-drs/cmd/transferref/main.go:200.2,204.16 3 0 +github.com/calypr/git-drs/cmd/transferref/main.go:204.16,206.3 1 0 +github.com/calypr/git-drs/cmd/transferref/main.go:207.2,213.21 6 0 +github.com/calypr/git-drs/cmd/transferref/main.go:213.21,217.19 4 0 +github.com/calypr/git-drs/cmd/transferref/main.go:217.19,221.9 3 0 +github.com/calypr/git-drs/cmd/transferref/main.go:225.2,226.18 2 0 +github.com/calypr/git-drs/cmd/transferref/main.go:226.18,228.3 1 0 +github.com/calypr/git-drs/cmd/transferref/main.go:229.2,230.16 2 0 +github.com/calypr/git-drs/cmd/transferref/main.go:230.16,232.3 1 0 +github.com/calypr/git-drs/cmd/transferref/main.go:233.2,233.19 1 0 +github.com/calypr/git-drs/cmd/transferref/main.go:233.19,235.3 1 0 +github.com/calypr/git-drs/cmd/transferref/main.go:237.2,241.16 3 0 +github.com/calypr/git-drs/cmd/transferref/main.go:241.16,243.3 1 0 +github.com/calypr/git-drs/cmd/transferref/main.go:244.2,258.16 9 0 +github.com/calypr/git-drs/cmd/transferref/main.go:258.16,260.3 1 0 +github.com/calypr/git-drs/cmd/transferref/main.go:263.2,265.16 3 0 +github.com/calypr/git-drs/cmd/transferref/main.go:265.16,267.3 1 0 +github.com/calypr/git-drs/cmd/transferref/main.go:269.2,269.21 1 0 +github.com/calypr/git-drs/cmd/version/main.go:15.47,17.3 1 0 +github.com/calypr/git-drs/cmd/version/main.go:20.28,23.43 3 0 +github.com/calypr/git-drs/cmd/version/main.go:23.43,24.64 1 0 +github.com/calypr/git-drs/cmd/version/main.go:24.64,26.4 1 0 +github.com/calypr/git-drs/cmd/version/main.go:27.3,27.41 1 0 +github.com/calypr/git-drs/cmd/version/main.go:27.41,28.23 1 0 +github.com/calypr/git-drs/cmd/version/main.go:29.24,30.27 1 0 +github.com/calypr/git-drs/cmd/version/main.go:31.19,32.18 1 0 +github.com/calypr/git-drs/cmd/version/main.go:32.18,34.6 1 0 +github.com/calypr/git-drs/cmd/version/main.go:39.2,40.26 2 0 +github.com/calypr/git-drs/cmd/version/main.go:40.26,42.3 1 0 +github.com/calypr/git-drs/cmd/version/main.go:44.2,44.9 1 0 +github.com/calypr/git-drs/cmd/version/main.go:45.38,46.48 1 0 +github.com/calypr/git-drs/cmd/version/main.go:47.17,48.13 1 0 +github.com/calypr/git-drs/cmd/version/main.go:49.25,50.44 1 0 +github.com/calypr/git-drs/cmd/version/main.go:51.10,52.23 1 0 +github.com/calypr/git-drs/config/config.go:30.36,32.2 1 0 +github.com/calypr/git-drs/config/config.go:34.43,36.37 2 0 +github.com/calypr/git-drs/config/config.go:36.37,38.3 1 0 +github.com/calypr/git-drs/config/config.go:40.2,40.40 1 0 +github.com/calypr/git-drs/config/config.go:40.40,41.32 1 0 +github.com/calypr/git-drs/config/config.go:41.32,43.4 1 0 +github.com/calypr/git-drs/config/config.go:46.2,46.102 1 0 +github.com/calypr/git-drs/config/config.go:68.94,70.9 2 108 +github.com/calypr/git-drs/config/config.go:70.9,72.3 1 0 +github.com/calypr/git-drs/config/config.go:73.2,73.19 1 108 +github.com/calypr/git-drs/config/config.go:73.19,79.3 5 108 +github.com/calypr/git-drs/config/config.go:79.8,79.27 1 0 +github.com/calypr/git-drs/config/config.go:79.27,81.3 1 0 +github.com/calypr/git-drs/config/config.go:82.2,82.94 1 0 +github.com/calypr/git-drs/config/config.go:85.52,87.9 2 0 +github.com/calypr/git-drs/config/config.go:87.9,89.3 1 0 +github.com/calypr/git-drs/config/config.go:90.2,90.19 1 0 +github.com/calypr/git-drs/config/config.go:90.19,92.3 1 0 +github.com/calypr/git-drs/config/config.go:92.8,92.27 1 0 +github.com/calypr/git-drs/config/config.go:92.27,94.3 1 0 +github.com/calypr/git-drs/config/config.go:95.2,95.12 1 0 +github.com/calypr/git-drs/config/config.go:99.52,100.27 1 108 +github.com/calypr/git-drs/config/config.go:100.27,107.3 1 0 +github.com/calypr/git-drs/config/config.go:109.2,109.46 1 108 +github.com/calypr/git-drs/config/config.go:109.46,116.3 1 0 +github.com/calypr/git-drs/config/config.go:118.2,118.29 1 108 +github.com/calypr/git-drs/config/config.go:123.67,124.18 1 0 +github.com/calypr/git-drs/config/config.go:124.18,126.3 1 0 +github.com/calypr/git-drs/config/config.go:127.2,127.29 1 0 +github.com/calypr/git-drs/config/config.go:131.44,133.30 2 0 +github.com/calypr/git-drs/config/config.go:133.30,135.3 1 0 +github.com/calypr/git-drs/config/config.go:136.2,136.14 1 0 +github.com/calypr/git-drs/config/config.go:139.38,141.16 2 138 +github.com/calypr/git-drs/config/config.go:141.16,143.3 1 0 +github.com/calypr/git-drs/config/config.go:145.2,146.24 2 138 +github.com/calypr/git-drs/config/config.go:154.70,156.16 2 6 +github.com/calypr/git-drs/config/config.go:156.16,158.3 1 0 +github.com/calypr/git-drs/config/config.go:161.2,161.55 1 6 +github.com/calypr/git-drs/config/config.go:161.55,162.69 1 0 +github.com/calypr/git-drs/config/config.go:162.69,164.4 1 0 +github.com/calypr/git-drs/config/config.go:168.2,169.16 2 6 +github.com/calypr/git-drs/config/config.go:169.16,171.3 1 0 +github.com/calypr/git-drs/config/config.go:172.2,176.59 3 6 +github.com/calypr/git-drs/config/config.go:176.59,181.3 1 6 +github.com/calypr/git-drs/config/config.go:183.2,183.24 1 6 +github.com/calypr/git-drs/config/config.go:183.24,185.3 1 0 +github.com/calypr/git-drs/config/config.go:188.2,188.54 1 6 +github.com/calypr/git-drs/config/config.go:188.54,190.3 1 6 +github.com/calypr/git-drs/config/config.go:192.2,197.58 4 6 +github.com/calypr/git-drs/config/config.go:197.58,199.3 1 0 +github.com/calypr/git-drs/config/config.go:200.2,200.18 1 6 +github.com/calypr/git-drs/config/config.go:204.36,206.16 2 120 +github.com/calypr/git-drs/config/config.go:206.16,208.3 1 0 +github.com/calypr/git-drs/config/config.go:210.2,210.55 1 120 +github.com/calypr/git-drs/config/config.go:210.55,212.3 1 0 +github.com/calypr/git-drs/config/config.go:214.2,215.16 2 120 +github.com/calypr/git-drs/config/config.go:215.16,217.3 1 0 +github.com/calypr/git-drs/config/config.go:218.2,221.16 3 120 +github.com/calypr/git-drs/config/config.go:221.16,223.3 1 0 +github.com/calypr/git-drs/config/config.go:225.2,227.16 3 120 +github.com/calypr/git-drs/config/config.go:227.16,229.3 1 0 +github.com/calypr/git-drs/config/config.go:233.2,233.55 1 120 +github.com/calypr/git-drs/config/config.go:233.55,235.34 2 0 +github.com/calypr/git-drs/config/config.go:235.34,237.4 1 0 +github.com/calypr/git-drs/config/config.go:238.3,245.4 1 0 +github.com/calypr/git-drs/config/config.go:248.2,248.19 1 120 +github.com/calypr/git-drs/config/config.go:251.32,253.16 2 12 +github.com/calypr/git-drs/config/config.go:253.16,255.3 1 0 +github.com/calypr/git-drs/config/config.go:256.2,256.55 1 12 +github.com/calypr/git-drs/config/config.go:256.55,257.69 1 6 +github.com/calypr/git-drs/config/config.go:257.69,259.4 1 0 +github.com/calypr/git-drs/config/config.go:263.2,264.16 2 12 +github.com/calypr/git-drs/config/config.go:264.16,266.3 1 0 +github.com/calypr/git-drs/config/config.go:267.2,269.12 2 12 +github.com/calypr/git-drs/config/config.go:272.50,274.16 2 0 +github.com/calypr/git-drs/config/config.go:274.16,276.3 1 0 +github.com/calypr/git-drs/config/config.go:277.2,278.16 2 0 +github.com/calypr/git-drs/config/config.go:278.16,280.3 1 0 +github.com/calypr/git-drs/config/config.go:281.2,281.32 1 0 +github.com/calypr/git-drs/config/config.go:285.36,287.16 2 0 +github.com/calypr/git-drs/config/config.go:287.16,289.3 1 0 +github.com/calypr/git-drs/config/config.go:291.2,292.16 2 0 +github.com/calypr/git-drs/config/config.go:292.16,294.3 1 0 +github.com/calypr/git-drs/config/config.go:295.2,297.58 2 0 +github.com/calypr/git-drs/config/config.go:297.58,299.3 1 0 +github.com/calypr/git-drs/config/config.go:301.2,301.12 1 0 +github.com/calypr/git-drs/drs/object.go:70.66,71.15 1 0 +github.com/calypr/git-drs/drs/object.go:71.15,73.3 1 0 +github.com/calypr/git-drs/drs/object.go:76.2,95.3 2 0 +github.com/calypr/git-drs/drs/store.go:20.70,24.55 3 0 +github.com/calypr/git-drs/drs/store.go:24.55,26.3 1 0 +github.com/calypr/git-drs/drs/store.go:27.2,27.88 1 0 +github.com/calypr/git-drs/drs/store.go:27.88,28.17 1 0 +github.com/calypr/git-drs/drs/store.go:28.17,30.4 1 0 +github.com/calypr/git-drs/drs/store.go:31.3,31.19 1 0 +github.com/calypr/git-drs/drs/store.go:31.19,33.4 1 0 +github.com/calypr/git-drs/drs/store.go:34.3,35.17 2 0 +github.com/calypr/git-drs/drs/store.go:35.17,37.4 1 0 +github.com/calypr/git-drs/drs/store.go:38.3,39.22 2 0 +github.com/calypr/git-drs/drs/store.go:39.22,42.4 2 0 +github.com/calypr/git-drs/drs/store.go:43.3,47.13 3 0 +github.com/calypr/git-drs/drs/store.go:49.2,49.16 1 0 +github.com/calypr/git-drs/drs/store.go:49.16,51.3 1 0 +github.com/calypr/git-drs/drs/store.go:52.2,53.21 2 0 +github.com/calypr/git-drs/drs/store.go:56.74,59.55 3 0 +github.com/calypr/git-drs/drs/store.go:59.55,62.3 2 0 +github.com/calypr/git-drs/drs/store.go:64.2,64.88 1 0 +github.com/calypr/git-drs/drs/store.go:64.88,65.17 1 0 +github.com/calypr/git-drs/drs/store.go:65.17,68.4 2 0 +github.com/calypr/git-drs/drs/store.go:69.3,69.19 1 0 +github.com/calypr/git-drs/drs/store.go:69.19,71.4 1 0 +github.com/calypr/git-drs/drs/store.go:72.3,73.17 2 0 +github.com/calypr/git-drs/drs/store.go:73.17,75.4 1 0 +github.com/calypr/git-drs/drs/store.go:76.3,77.22 2 0 +github.com/calypr/git-drs/drs/store.go:77.22,80.4 2 0 +github.com/calypr/git-drs/drs/store.go:81.3,82.17 2 0 +github.com/calypr/git-drs/drs/store.go:82.17,85.4 2 0 +github.com/calypr/git-drs/drs/store.go:86.3,87.73 2 0 +github.com/calypr/git-drs/drs/store.go:87.73,90.4 2 0 +github.com/calypr/git-drs/drs/store.go:93.3,93.39 1 0 +github.com/calypr/git-drs/drs/store.go:93.39,95.4 1 0 +github.com/calypr/git-drs/drs/store.go:97.3,98.13 2 0 +github.com/calypr/git-drs/drs/store.go:100.2,100.16 1 0 +github.com/calypr/git-drs/drs/store.go:100.16,102.3 1 0 +github.com/calypr/git-drs/drs/store.go:103.2,104.21 2 0 +github.com/calypr/git-drs/drs/util.go:16.32,18.16 2 0 +github.com/calypr/git-drs/drs/util.go:18.16,20.3 1 0 +github.com/calypr/git-drs/drs/util.go:21.2,21.49 1 0 +github.com/calypr/git-drs/drs/util.go:29.74,31.16 2 0 +github.com/calypr/git-drs/drs/util.go:31.16,33.3 1 0 +github.com/calypr/git-drs/drs/util.go:34.2,36.16 3 0 +github.com/calypr/git-drs/drs/util.go:36.16,38.3 1 0 +github.com/calypr/git-drs/drs/util.go:39.2,40.16 2 0 +github.com/calypr/git-drs/drs/util.go:40.16,42.3 1 0 +github.com/calypr/git-drs/drs/util.go:43.2,43.34 1 0 +github.com/calypr/git-drs/drs/util.go:46.38,48.16 2 0 +github.com/calypr/git-drs/drs/util.go:48.16,50.3 1 0 +github.com/calypr/git-drs/drs/util.go:51.2,52.43 2 0 +github.com/calypr/git-drs/drs/hash/hash.go:33.39,34.12 1 0 +github.com/calypr/git-drs/drs/hash/hash.go:36.62,37.14 1 0 +github.com/calypr/git-drs/drs/hash/hash.go:38.10,39.15 1 0 +github.com/calypr/git-drs/drs/hash/hash.go:44.40,46.2 1 24 +github.com/calypr/git-drs/drs/hash/hash.go:71.53,72.28 1 66 +github.com/calypr/git-drs/drs/hash/hash.go:72.28,75.3 2 0 +github.com/calypr/git-drs/drs/hash/hash.go:77.2,78.58 2 66 +github.com/calypr/git-drs/drs/hash/hash.go:78.58,81.3 2 66 +github.com/calypr/git-drs/drs/hash/hash.go:83.2,84.63 2 0 +github.com/calypr/git-drs/drs/hash/hash.go:84.63,87.3 2 0 +github.com/calypr/git-drs/drs/hash/hash.go:89.2,89.69 1 0 +github.com/calypr/git-drs/drs/hash/hash.go:92.73,95.38 2 66 +github.com/calypr/git-drs/drs/hash/hash.go:95.38,96.31 1 66 +github.com/calypr/git-drs/drs/hash/hash.go:96.31,97.12 1 0 +github.com/calypr/git-drs/drs/hash/hash.go:100.3,100.14 1 66 +github.com/calypr/git-drs/drs/hash/hash.go:101.32,102.24 1 0 +github.com/calypr/git-drs/drs/hash/hash.go:103.33,104.24 1 0 +github.com/calypr/git-drs/drs/hash/hash.go:105.35,106.27 1 66 +github.com/calypr/git-drs/drs/hash/hash.go:107.35,108.27 1 0 +github.com/calypr/git-drs/drs/hash/hash.go:109.35,110.24 1 0 +github.com/calypr/git-drs/drs/hash/hash.go:111.33,112.25 1 0 +github.com/calypr/git-drs/drs/hash/hash.go:113.11,113.11 0 0 +github.com/calypr/git-drs/drs/hash/hash.go:117.2,117.17 1 66 +github.com/calypr/git-drs/drs/hash/hash.go:121.62,123.22 2 0 +github.com/calypr/git-drs/drs/hash/hash.go:123.22,125.3 1 0 +github.com/calypr/git-drs/drs/hash/hash.go:126.2,126.22 1 0 +github.com/calypr/git-drs/drs/hash/hash.go:126.22,128.3 1 0 +github.com/calypr/git-drs/drs/hash/hash.go:129.2,129.25 1 0 +github.com/calypr/git-drs/drs/hash/hash.go:129.25,131.3 1 0 +github.com/calypr/git-drs/drs/hash/hash.go:132.2,132.25 1 0 +github.com/calypr/git-drs/drs/hash/hash.go:132.25,134.3 1 0 +github.com/calypr/git-drs/drs/hash/hash.go:135.2,135.22 1 0 +github.com/calypr/git-drs/drs/hash/hash.go:135.22,137.3 1 0 +github.com/calypr/git-drs/drs/hash/hash.go:138.2,138.23 1 0 +github.com/calypr/git-drs/drs/hash/hash.go:138.23,140.3 1 0 +github.com/calypr/git-drs/drs/hash/hash.go:141.2,141.15 1 0 +github.com/calypr/git-drs/drs/hash/hash.go:144.68,146.30 2 0 +github.com/calypr/git-drs/drs/hash/hash.go:146.30,150.3 1 0 +github.com/calypr/git-drs/drs/hash/hash.go:151.2,151.15 1 0 +github.com/calypr/git-drs/drs/hash/hash.go:156.64,162.2 2 0 +github.com/calypr/git-drs/drslog/logger.go:21.72,24.20 2 138 +github.com/calypr/git-drs/drslog/logger.go:24.20,26.63 1 138 +github.com/calypr/git-drs/drslog/logger.go:26.63,28.4 1 0 +github.com/calypr/git-drs/drslog/logger.go:30.3,30.62 1 138 +github.com/calypr/git-drs/drslog/logger.go:33.2,34.16 2 138 +github.com/calypr/git-drs/drslog/logger.go:34.16,36.3 1 0 +github.com/calypr/git-drs/drslog/logger.go:37.2,39.17 2 138 +github.com/calypr/git-drs/drslog/logger.go:39.17,41.3 1 126 +github.com/calypr/git-drs/drslog/logger.go:43.2,55.26 8 138 +github.com/calypr/git-drs/drslog/logger.go:58.30,59.29 1 114 +github.com/calypr/git-drs/drslog/logger.go:59.29,60.26 1 114 +github.com/calypr/git-drs/drslog/logger.go:60.26,62.4 1 0 +github.com/calypr/git-drs/drslog/logger.go:64.2,64.21 1 114 +github.com/calypr/git-drs/drslog/logger.go:68.20,71.26 3 0 +github.com/calypr/git-drs/drslog/logger.go:71.26,76.3 3 0 +github.com/calypr/git-drs/drslog/logger.go:77.2,77.12 1 0 +github.com/calypr/git-drs/drslog/logger.go:81.34,83.2 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:35.116,36.41 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:36.41,38.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:41.2,53.16 10 12 +github.com/calypr/git-drs/drsmap/drs_map.go:53.16,55.16 2 0 +github.com/calypr/git-drs/drsmap/drs_map.go:55.16,57.4 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:58.3,58.67 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:60.2,60.17 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:79.82,82.16 2 0 +github.com/calypr/git-drs/drsmap/drs_map.go:82.16,84.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:87.2,88.33 2 0 +github.com/calypr/git-drs/drsmap/drs_map.go:88.33,89.70 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:89.70,90.51 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:90.51,95.5 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:99.2,100.27 2 0 +github.com/calypr/git-drs/drsmap/drs_map.go:100.27,102.17 2 0 +github.com/calypr/git-drs/drsmap/drs_map.go:102.17,104.4 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:106.3,106.24 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:106.24,108.12 2 0 +github.com/calypr/git-drs/drsmap/drs_map.go:110.3,113.31 2 0 +github.com/calypr/git-drs/drsmap/drs_map.go:113.31,114.34 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:114.34,117.5 2 0 +github.com/calypr/git-drs/drsmap/drs_map.go:119.3,119.13 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:119.13,121.4 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:124.2,124.33 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:124.33,126.10 2 0 +github.com/calypr/git-drs/drsmap/drs_map.go:126.10,128.4 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:129.3,129.62 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:129.62,132.18 3 0 +github.com/calypr/git-drs/drsmap/drs_map.go:132.18,134.5 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:136.9,138.18 2 0 +github.com/calypr/git-drs/drsmap/drs_map.go:138.18,140.5 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:143.2,143.12 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:146.81,148.16 2 0 +github.com/calypr/git-drs/drsmap/drs_map.go:148.16,150.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:151.2,152.30 2 0 +github.com/calypr/git-drs/drsmap/drs_map.go:152.30,153.27 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:153.27,155.12 2 0 +github.com/calypr/git-drs/drsmap/drs_map.go:157.3,158.23 2 0 +github.com/calypr/git-drs/drsmap/drs_map.go:158.23,160.4 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:161.3,162.36 2 0 +github.com/calypr/git-drs/drsmap/drs_map.go:162.36,163.51 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:163.51,166.19 3 0 +github.com/calypr/git-drs/drsmap/drs_map.go:166.19,168.6 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:172.3,172.36 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:172.36,176.18 3 0 +github.com/calypr/git-drs/drsmap/drs_map.go:176.18,178.5 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:181.2,182.12 2 0 +github.com/calypr/git-drs/drsmap/drs_map.go:185.137,191.16 3 12 +github.com/calypr/git-drs/drsmap/drs_map.go:191.16,193.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:196.2,197.21 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:197.21,199.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:203.2,203.32 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:203.32,206.17 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:206.17,208.4 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:209.3,209.48 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:209.48,211.12 2 0 +github.com/calypr/git-drs/drsmap/drs_map.go:217.3,222.17 3 12 +github.com/calypr/git-drs/drsmap/drs_map.go:222.17,224.4 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:225.3,225.50 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:225.50,227.4 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:229.3,230.17 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:230.17,232.4 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:235.3,236.17 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:236.17,238.4 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:239.3,239.101 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:242.2,242.12 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:245.78,248.16 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:248.16,250.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:251.2,251.68 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:251.68,253.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:256.2,257.16 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:257.16,259.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:260.2,260.12 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:263.52,267.2 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:270.57,273.16 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:273.16,275.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:277.2,278.16 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:278.16,280.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:282.2,284.16 3 12 +github.com/calypr/git-drs/drsmap/drs_map.go:284.16,286.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:288.2,288.24 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:291.65,293.20 1 54 +github.com/calypr/git-drs/drsmap/drs_map.go:293.20,295.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:297.2,297.61 1 54 +github.com/calypr/git-drs/drsmap/drs_map.go:306.66,310.16 3 0 +github.com/calypr/git-drs/drsmap/drs_map.go:310.16,313.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:316.2,316.46 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:316.46,318.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:321.2,323.16 3 0 +github.com/calypr/git-drs/drsmap/drs_map.go:323.16,325.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:328.2,328.31 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:328.31,330.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:333.2,344.27 3 0 +github.com/calypr/git-drs/drsmap/drs_map.go:347.41,353.16 3 0 +github.com/calypr/git-drs/drsmap/drs_map.go:353.16,355.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:356.2,357.25 2 0 +github.com/calypr/git-drs/drsmap/drs_map.go:360.56,364.16 3 0 +github.com/calypr/git-drs/drsmap/drs_map.go:364.16,366.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:368.2,370.22 3 0 +github.com/calypr/git-drs/drsmap/drs_map.go:373.133,374.19 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:374.19,376.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:377.2,378.16 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:378.16,380.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:383.2,389.25 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:389.25,391.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:392.2,392.29 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:392.29,394.3 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:394.8,396.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:398.2,400.27 3 12 +github.com/calypr/git-drs/drsmap/drs_map.go:400.27,406.17 3 12 +github.com/calypr/git-drs/drsmap/drs_map.go:406.17,408.4 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:410.3,410.81 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:410.81,412.4 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:415.2,415.24 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:418.47,419.24 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:419.24,421.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:422.2,424.34 3 12 +github.com/calypr/git-drs/drsmap/drs_map.go:424.34,426.19 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:426.19,427.12 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:429.3,430.62 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:430.62,432.4 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:433.3,433.29 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:433.29,434.12 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:436.3,437.27 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:439.2,439.20 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:439.20,441.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:442.2,442.13 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:445.110,447.34 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:447.34,450.3 2 0 +github.com/calypr/git-drs/drsmap/drs_map.go:453.2,455.75 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:455.75,457.17 2 18 +github.com/calypr/git-drs/drsmap/drs_map.go:457.17,458.12 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:460.3,461.21 2 18 +github.com/calypr/git-drs/drsmap/drs_map.go:461.21,462.12 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:464.3,468.33 3 18 +github.com/calypr/git-drs/drsmap/drs_map.go:468.33,470.12 2 0 +github.com/calypr/git-drs/drsmap/drs_map.go:474.3,474.124 1 18 +github.com/calypr/git-drs/drsmap/drs_map.go:474.124,476.12 2 6 +github.com/calypr/git-drs/drsmap/drs_map.go:480.3,480.86 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:480.86,482.4 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:483.3,485.45 3 12 +github.com/calypr/git-drs/drsmap/drs_map.go:485.45,487.4 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:488.3,488.48 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:488.48,490.4 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:490.9,492.101 2 0 +github.com/calypr/git-drs/drsmap/drs_map.go:492.101,494.5 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:495.4,495.12 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:500.3,500.30 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:500.30,501.61 1 6 +github.com/calypr/git-drs/drsmap/drs_map.go:501.61,503.112 2 6 +github.com/calypr/git-drs/drsmap/drs_map.go:503.112,505.127 2 0 +github.com/calypr/git-drs/drsmap/drs_map.go:505.127,507.7 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:508.6,508.14 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:513.3,519.4 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:523.2,523.12 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:529.63,531.66 2 0 +github.com/calypr/git-drs/drsmap/drs_map.go:531.66,533.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:534.2,538.25 3 0 +github.com/calypr/git-drs/drsmap/drs_map.go:538.25,539.15 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:539.15,541.9 2 0 +github.com/calypr/git-drs/drsmap/drs_map.go:544.2,544.20 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:544.20,546.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:547.2,549.54 3 0 +github.com/calypr/git-drs/drsmap/drs_map.go:554.92,555.23 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:555.23,557.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:560.2,561.16 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:561.16,563.3 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:567.2,567.33 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:567.33,568.47 1 48 +github.com/calypr/git-drs/drsmap/drs_map.go:568.47,570.36 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:570.36,572.5 1 0 +github.com/calypr/git-drs/drsmap/drs_map.go:573.4,573.52 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:573.52,575.5 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:579.2,579.17 1 0 +github.com/calypr/git-drs/drsmap/lfs_utils.go:11.64,13.22 2 0 +github.com/calypr/git-drs/drsmap/lfs_utils.go:13.22,15.3 1 0 +github.com/calypr/git-drs/drsmap/lfs_utils.go:18.2,19.33 2 0 +github.com/calypr/git-drs/drsmap/lfs_utils.go:19.33,20.49 1 0 +github.com/calypr/git-drs/drsmap/lfs_utils.go:20.49,22.9 2 0 +github.com/calypr/git-drs/drsmap/lfs_utils.go:25.2,25.18 1 0 +github.com/calypr/git-drs/drsmap/lfs_utils.go:25.18,27.3 1 0 +github.com/calypr/git-drs/drsmap/lfs_utils.go:30.2,36.16 5 0 +github.com/calypr/git-drs/drsmap/lfs_utils.go:36.16,38.3 1 0 +github.com/calypr/git-drs/drsmap/lfs_utils.go:40.2,40.12 1 0 +github.com/calypr/git-drs/lfs/messages.go:82.85,91.2 2 0 +github.com/calypr/git-drs/lfs/messages.go:93.93,104.2 2 0 +github.com/calypr/git-drs/lfs/messages.go:106.84,114.2 2 24 +github.com/calypr/git-drs/s3_utils/download.go:12.64,15.16 2 12 +github.com/calypr/git-drs/s3_utils/download.go:15.16,17.3 1 0 +github.com/calypr/git-drs/s3_utils/download.go:18.2,21.46 2 12 +github.com/calypr/git-drs/s3_utils/download.go:21.46,23.17 2 0 +github.com/calypr/git-drs/s3_utils/download.go:23.17,25.4 1 0 +github.com/calypr/git-drs/s3_utils/download.go:26.3,26.119 1 0 +github.com/calypr/git-drs/s3_utils/download.go:30.2,31.16 2 12 +github.com/calypr/git-drs/s3_utils/download.go:31.16,33.3 1 0 +github.com/calypr/git-drs/s3_utils/download.go:36.2,37.16 2 12 +github.com/calypr/git-drs/s3_utils/download.go:37.16,39.3 1 0 +github.com/calypr/git-drs/s3_utils/download.go:40.2,44.16 3 12 +github.com/calypr/git-drs/s3_utils/download.go:44.16,46.3 1 0 +github.com/calypr/git-drs/s3_utils/download.go:48.2,48.12 1 12 +github.com/calypr/git-drs/s3_utils/s3.go:49.96,53.2 1 0 +github.com/calypr/git-drs/s3_utils/s3.go:66.51,67.33 1 0 +github.com/calypr/git-drs/s3_utils/s3.go:67.33,69.3 1 0 +github.com/calypr/git-drs/s3_utils/s3.go:73.55,74.33 1 0 +github.com/calypr/git-drs/s3_utils/s3.go:74.33,76.3 1 0 +github.com/calypr/git-drs/s3_utils/s3.go:80.50,81.33 1 0 +github.com/calypr/git-drs/s3_utils/s3.go:81.33,83.3 1 0 +github.com/calypr/git-drs/s3_utils/validate.go:9.56,10.40 1 0 +github.com/calypr/git-drs/s3_utils/validate.go:10.40,12.3 1 0 +github.com/calypr/git-drs/s3_utils/validate.go:15.2,16.23 2 0 +github.com/calypr/git-drs/s3_utils/validate.go:16.23,18.3 1 0 +github.com/calypr/git-drs/s3_utils/validate.go:20.2,20.52 1 0 +github.com/calypr/git-drs/s3_utils/validate.go:20.52,22.3 1 0 +github.com/calypr/git-drs/s3_utils/validate.go:24.2,24.12 1 0 +github.com/calypr/git-drs/utils/common.go:12.56,13.37 1 36 +github.com/calypr/git-drs/utils/common.go:13.37,15.3 1 0 +github.com/calypr/git-drs/utils/common.go:16.2,17.77 2 36 +github.com/calypr/git-drs/utils/common.go:22.59,27.32 2 0 +github.com/calypr/git-drs/utils/common.go:27.32,29.3 1 0 +github.com/calypr/git-drs/utils/common.go:31.2,31.29 1 0 +github.com/calypr/git-drs/utils/common.go:31.29,33.37 1 0 +github.com/calypr/git-drs/utils/common.go:33.37,37.4 2 0 +github.com/calypr/git-drs/utils/common.go:39.2,39.17 1 0 +github.com/calypr/git-drs/utils/confirmation.go:19.107,24.16 4 0 +github.com/calypr/git-drs/utils/confirmation.go:24.16,26.3 1 0 +github.com/calypr/git-drs/utils/confirmation.go:28.2,29.20 2 0 +github.com/calypr/git-drs/utils/confirmation.go:29.20,32.3 2 0 +github.com/calypr/git-drs/utils/confirmation.go:34.2,34.34 1 0 +github.com/calypr/git-drs/utils/confirmation.go:34.34,36.3 1 0 +github.com/calypr/git-drs/utils/confirmation.go:38.2,38.12 1 0 +github.com/calypr/git-drs/utils/confirmation.go:42.58,44.2 1 0 +github.com/calypr/git-drs/utils/confirmation.go:47.51,49.2 1 0 +github.com/calypr/git-drs/utils/confirmation.go:52.33,54.2 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:18.65,22.21 3 0 +github.com/calypr/git-drs/utils/lfs-track.go:22.21,26.49 2 0 +github.com/calypr/git-drs/utils/lfs-track.go:26.49,27.12 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:30.3,31.17 2 0 +github.com/calypr/git-drs/utils/lfs-track.go:31.17,32.12 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:35.3,35.40 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:38.2,38.34 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:42.51,44.20 2 0 +github.com/calypr/git-drs/utils/lfs-track.go:44.20,46.3 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:48.2,51.33 3 0 +github.com/calypr/git-drs/utils/lfs-track.go:51.33,52.34 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:52.34,56.4 2 0 +github.com/calypr/git-drs/utils/lfs-track.go:56.9,56.42 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:56.42,59.4 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:59.9,62.4 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:65.2,68.8 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:71.73,73.16 2 0 +github.com/calypr/git-drs/utils/lfs-track.go:73.16,75.3 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:77.2,77.61 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:81.79,83.16 2 0 +github.com/calypr/git-drs/utils/lfs-track.go:83.16,85.3 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:88.2,90.34 2 0 +github.com/calypr/git-drs/utils/lfs-track.go:90.34,91.45 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:91.45,93.59 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:93.59,95.5 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:99.2,99.19 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:103.52,105.25 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:105.25,107.3 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:110.2,110.37 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:110.37,112.3 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:115.2,115.37 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:115.37,117.3 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:120.2,120.36 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:120.36,123.17 2 0 +github.com/calypr/git-drs/utils/lfs-track.go:123.17,125.4 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:126.3,126.17 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:132.2,133.16 2 0 +github.com/calypr/git-drs/utils/lfs-track.go:133.16,135.3 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:137.2,137.16 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:141.62,146.21 2 0 +github.com/calypr/git-drs/utils/lfs-track.go:146.21,149.3 2 0 +github.com/calypr/git-drs/utils/lfs-track.go:155.2,157.29 2 0 +github.com/calypr/git-drs/utils/lfs-track.go:157.29,158.17 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:158.17,159.12 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:162.3,162.13 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:162.13,164.36 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:164.36,166.46 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:166.46,168.6 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:169.5,169.42 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:170.10,174.5 2 0 +github.com/calypr/git-drs/utils/lfs-track.go:175.9,175.31 1 0 +github.com/calypr/git-drs/utils/lfs-track.go:175.31,180.35 2 0 +github.com/calypr/git-drs/utils/lfs-track.go:180.35,184.5 2 0 +github.com/calypr/git-drs/utils/lfs-track.go:184.10,189.5 3 0 +github.com/calypr/git-drs/utils/lfs-track.go:194.2,194.13 1 0 +github.com/calypr/git-drs/utils/util.go:14.36,16.16 2 150 +github.com/calypr/git-drs/utils/util.go:16.16,18.3 1 0 +github.com/calypr/git-drs/utils/util.go:19.2,19.44 1 150 +github.com/calypr/git-drs/utils/util.go:22.47,24.16 2 150 +github.com/calypr/git-drs/utils/util.go:24.16,26.3 1 0 +github.com/calypr/git-drs/utils/util.go:27.2,29.28 3 150 +github.com/calypr/git-drs/utils/util.go:32.36,34.16 2 0 +github.com/calypr/git-drs/utils/util.go:34.16,36.3 1 0 +github.com/calypr/git-drs/utils/util.go:37.2,37.42 1 0 +github.com/calypr/git-drs/utils/util.go:42.46,44.16 2 12 +github.com/calypr/git-drs/utils/util.go:44.16,46.3 1 0 +github.com/calypr/git-drs/utils/util.go:47.2,50.16 3 12 +github.com/calypr/git-drs/utils/util.go:50.16,52.3 1 0 +github.com/calypr/git-drs/utils/util.go:53.2,55.86 2 12 +github.com/calypr/git-drs/utils/util.go:55.86,57.3 1 6 +github.com/calypr/git-drs/utils/util.go:59.2,59.78 1 6 +github.com/calypr/git-drs/utils/util.go:62.62,65.16 3 0 +github.com/calypr/git-drs/utils/util.go:65.16,67.3 1 0 +github.com/calypr/git-drs/utils/util.go:68.2,69.9 2 0 +github.com/calypr/git-drs/utils/util.go:69.9,71.3 1 0 +github.com/calypr/git-drs/utils/util.go:72.2,73.9 2 0 +github.com/calypr/git-drs/utils/util.go:73.9,75.3 1 0 +github.com/calypr/git-drs/utils/util.go:76.2,77.9 2 0 +github.com/calypr/git-drs/utils/util.go:77.9,79.3 1 0 +github.com/calypr/git-drs/utils/util.go:80.2,80.18 1 0 +github.com/calypr/git-drs/utils/util.go:83.68,86.16 3 6 +github.com/calypr/git-drs/utils/util.go:86.16,88.3 1 0 +github.com/calypr/git-drs/utils/util.go:89.2,90.9 2 6 +github.com/calypr/git-drs/utils/util.go:90.9,92.3 1 0 +github.com/calypr/git-drs/utils/util.go:93.2,94.16 2 6 +github.com/calypr/git-drs/utils/util.go:94.16,96.3 1 0 +github.com/calypr/git-drs/utils/util.go:97.2,97.70 1 6 +github.com/calypr/git-drs/utils/util.go:100.55,102.41 2 0 +github.com/calypr/git-drs/utils/util.go:102.41,104.3 1 0 +github.com/calypr/git-drs/utils/util.go:105.2,107.54 3 0 +github.com/calypr/git-drs/utils/util.go:107.54,109.3 1 0 +github.com/calypr/git-drs/utils/util.go:110.2,110.58 1 0 diff --git a/coverage/integration/.gitignore b/coverage/integration/.gitignore new file mode 100644 index 00000000..1778a78b --- /dev/null +++ b/coverage/integration/.gitignore @@ -0,0 +1 @@ +raw/ \ No newline at end of file diff --git a/coverage/merged/.gitignore b/coverage/merged/.gitignore new file mode 100644 index 00000000..1778a78b --- /dev/null +++ b/coverage/merged/.gitignore @@ -0,0 +1 @@ +raw/ \ No newline at end of file diff --git a/coverage/unit/.gitignore b/coverage/unit/.gitignore new file mode 100644 index 00000000..1778a78b --- /dev/null +++ b/coverage/unit/.gitignore @@ -0,0 +1 @@ +raw/ \ No newline at end of file diff --git a/tests/coverage-test.sh b/tests/coverage-test.sh index 021024ee..79011fbb 100755 --- a/tests/coverage-test.sh +++ b/tests/coverage-test.sh @@ -3,7 +3,6 @@ # Removes objects from the bucket and indexd records, then runs monorepo tests (clean, normal, clone) twice. set -euo pipefail - # echo commands as they are executed if [ -z "${GIT_TRACE:-}" ]; then echo "For more verbose git output, consider setting the following environment variables before re-running the script:" >&2 @@ -27,9 +26,6 @@ gofmt -s -w . # build to ensure no compile errors go build -# unit tests -go test -v -race -coverprofile=coverage.out -covermode=atomic -coverpkg=./... $(go list ./... | grep -vE 'tests/integration/calypr|client/indexd/tests') || { echo "unit tests failed" >&2; exit 1; } - # Accept named parameters (flags override environment variables) @@ -81,6 +77,13 @@ done PROJECT_ID="${_prog}-${_proj}" +ROOT_DIR=$(git rev-parse --show-toplevel) +COVERAGE_ROOT="${COVERAGE_ROOT:-${ROOT_DIR}/coverage}" +INTEGRATION_COV_DIR="${INTEGRATION_COV_DIR:-${COVERAGE_ROOT}/integration/raw}" +INTEGRATION_PROFILE="${INTEGRATION_PROFILE:-${COVERAGE_ROOT}/integration/coverage.out}" +BUILD_DIR="${BUILD_DIR:-${ROOT_DIR}/build/coverage}" +mkdir -p $INTEGRATION_COV_DIR + UTIL_DIR="tests/scripts/utils" MONOREPO_DIR="tests/monorepos" RUN_TEST="./run-test.sh" @@ -107,6 +110,13 @@ if [ ! -d "$UTIL_DIR" ]; then exit 1 fi +# before running tests, build the executables with coverage instrumentation + +go build -cover -covermode=atomic -coverpkg=./... -o "${BUILD_DIR}/git-drs" . + +export PATH="${BUILD_DIR}:${PATH}" +export GOCOVERDIR="${INTEGRATION_COV_DIR}" + pushd "$UTIL_DIR" >/dev/null # 1) Remove objects from bucket using indexd->s3 list/delete pipeline @@ -168,3 +178,13 @@ fi echo "coverage-test.sh: all steps completed successfully." >&2 +go tool covdata textfmt -i="${INTEGRATION_COV_DIR}" -o "${INTEGRATION_PROFILE}" + +echo "Integration coverage profile saved to ${INTEGRATION_PROFILE}" + +# unit tests +which git-drs +export GOCOVERDIR=coverage/unit/raw +go test -cover -covermode=atomic -coverpkg=./... ./... || { echo "error: unit tests failed" >&2; exit 1; } + + diff --git a/tests/monorepos/run-test.sh b/tests/monorepos/run-test.sh index 2f3bbd3e..c71cb1b1 100755 --- a/tests/monorepos/run-test.sh +++ b/tests/monorepos/run-test.sh @@ -167,15 +167,15 @@ fi # ensure git-drs is running from this project's build # run `which git-drs` and check if it's in the build directory -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" -ABS_PATH="$(cd "$SCRIPT_DIR/../.." && pwd -P)" - -GIT_DRS_EXE=$ABS_PATH/git-drs -if [ ! -f "$GIT_DRS_EXE" ]; then - echo "error: git-drs executable not found at $GIT_DRS_EXE" >&2 - exit 1 -fi -export PATH="$ABS_PATH:$PATH" +#SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +#ABS_PATH="$(cd "$SCRIPT_DIR/../.." && pwd -P)" +# +#GIT_DRS_EXE=$ABS_PATH/git-drs +#if [ ! -f "$GIT_DRS_EXE" ]; then +# echo "error: git-drs executable not found at $GIT_DRS_EXE" >&2 +# exit 1 +#fi +#export PATH="$ABS_PATH:$PATH" echo "Using git-drs from: $(which git-drs)" >&2 diff --git a/tests/scripts/coverage/combine-coverage.sh b/tests/scripts/coverage/combine-coverage.sh new file mode 100755 index 00000000..ef016304 --- /dev/null +++ b/tests/scripts/coverage/combine-coverage.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR=$(git rev-parse --show-toplevel) +COVERAGE_ROOT="${COVERAGE_ROOT:-${ROOT_DIR}/coverage}" + +UNIT_COV_DIR="${1:-${UNIT_COV_DIR:-${COVERAGE_ROOT}/unit/raw}}" +INTEGRATION_COV_DIR="${2:-${INTEGRATION_COV_DIR:-${COVERAGE_ROOT}/integration/raw}}" +MERGED_COV_DIR="${3:-${MERGED_COV_DIR:-${COVERAGE_ROOT}/merged/raw}}" +COMBINED_PROFILE="${4:-${COMBINED_PROFILE:-${COVERAGE_ROOT}/combined.out}}" + +if [[ ! -d "${UNIT_COV_DIR}" ]]; then +echo "Unit coverage directory not found: ${UNIT_COV_DIR}" >&2 +echo "Run unit tests with GOCOVERDIR set, e.g.:" >&2 +echo " GOCOVERDIR=${COVERAGE_ROOT}/unit/raw go test -cover ./..." >&2 +exit 1 +fi + +if [[ ! -d "${INTEGRATION_COV_DIR}" ]]; then +echo "Integration coverage directory not found: ${INTEGRATION_COV_DIR}" >&2 +exit 1 +fi + +mkdir -p "${MERGED_COV_DIR}" "$(dirname "${COMBINED_PROFILE}")" + +rm -rf "${MERGED_COV_DIR:?}"/* + +go tool covdata merge -i="${UNIT_COV_DIR},${INTEGRATION_COV_DIR}" -o "${MERGED_COV_DIR}" +go tool covdata textfmt -i="${MERGED_COV_DIR}" -o "${COMBINED_PROFILE}" + +echo "Combined coverage profile saved to ${COMBINED_PROFILE}" From 6af9557068de2064e995588166dd26176d6d168c Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Wed, 21 Jan 2026 17:00:10 -0800 Subject: [PATCH 39/40] wip: coverage #147 --- coverage/combined.out | 802 ++++++++++++++++++++--------------------- tests/coverage-test.sh | 8 +- 2 files changed, 407 insertions(+), 403 deletions(-) diff --git a/coverage/combined.out b/coverage/combined.out index 148539f8..d21f103d 100644 --- a/coverage/combined.out +++ b/coverage/combined.out @@ -1,7 +1,7 @@ mode: atomic -github.com/calypr/git-drs/git-drs.go:11.13,14.16 2 126 +github.com/calypr/git-drs/git-drs.go:11.13,14.16 2 378 github.com/calypr/git-drs/git-drs.go:14.16,17.3 2 0 -github.com/calypr/git-drs/git-drs.go:19.2,19.46 1 126 +github.com/calypr/git-drs/git-drs.go:19.2,19.46 1 378 github.com/calypr/git-drs/git-drs.go:19.46,22.3 2 0 github.com/calypr/git-drs/client/anvil/anvil_client.go:23.75,26.16 2 0 github.com/calypr/git-drs/client/anvil/anvil_client.go:26.16,28.3 1 0 @@ -124,18 +124,18 @@ github.com/calypr/git-drs/client/indexd/add_url.go:343.16,345.3 1 0 github.com/calypr/git-drs/client/indexd/add_url.go:346.2,346.71 1 0 github.com/calypr/git-drs/client/indexd/add_url.go:346.71,348.3 1 0 github.com/calypr/git-drs/client/indexd/add_url.go:350.2,355.8 2 0 -github.com/calypr/git-drs/client/indexd/auth_handler.go:22.67,24.2 1 66 -github.com/calypr/git-drs/client/indexd/auth_handler.go:27.59,31.16 3 66 +github.com/calypr/git-drs/client/indexd/auth_handler.go:22.67,24.2 1 198 +github.com/calypr/git-drs/client/indexd/auth_handler.go:27.59,31.16 3 198 github.com/calypr/git-drs/client/indexd/auth_handler.go:31.16,33.3 1 0 -github.com/calypr/git-drs/client/indexd/auth_handler.go:36.2,36.54 1 66 -github.com/calypr/git-drs/client/indexd/auth_handler.go:36.54,37.45 1 66 -github.com/calypr/git-drs/client/indexd/auth_handler.go:37.45,41.4 3 66 +github.com/calypr/git-drs/client/indexd/auth_handler.go:36.2,36.54 1 198 +github.com/calypr/git-drs/client/indexd/auth_handler.go:36.54,37.45 1 198 +github.com/calypr/git-drs/client/indexd/auth_handler.go:37.45,41.4 3 198 github.com/calypr/git-drs/client/indexd/auth_handler.go:43.2,43.100 1 0 -github.com/calypr/git-drs/client/indexd/auth_handler.go:47.69,52.16 3 66 +github.com/calypr/git-drs/client/indexd/auth_handler.go:47.69,52.16 3 198 github.com/calypr/git-drs/client/indexd/auth_handler.go:52.16,54.3 1 0 -github.com/calypr/git-drs/client/indexd/auth_handler.go:54.8,54.42 1 66 +github.com/calypr/git-drs/client/indexd/auth_handler.go:54.8,54.42 1 198 github.com/calypr/git-drs/client/indexd/auth_handler.go:54.42,56.3 1 0 -github.com/calypr/git-drs/client/indexd/auth_handler.go:58.2,58.17 1 66 +github.com/calypr/git-drs/client/indexd/auth_handler.go:58.2,58.17 1 198 github.com/calypr/git-drs/client/indexd/auth_handler.go:58.17,62.17 3 0 github.com/calypr/git-drs/client/indexd/auth_handler.go:62.17,64.4 1 0 github.com/calypr/git-drs/client/indexd/auth_handler.go:65.3,68.10 4 0 @@ -144,74 +144,74 @@ github.com/calypr/git-drs/client/indexd/auth_handler.go:71.3,72.17 2 0 github.com/calypr/git-drs/client/indexd/auth_handler.go:72.17,74.4 1 0 github.com/calypr/git-drs/client/indexd/auth_handler.go:75.3,77.17 3 0 github.com/calypr/git-drs/client/indexd/auth_handler.go:77.17,79.4 1 0 -github.com/calypr/git-drs/client/indexd/auth_handler.go:81.2,81.12 1 66 -github.com/calypr/git-drs/client/indexd/auth_handler.go:84.68,86.2 1 66 -github.com/calypr/git-drs/client/indexd/auth_handler.go:88.92,90.16 2 66 +github.com/calypr/git-drs/client/indexd/auth_handler.go:81.2,81.12 1 198 +github.com/calypr/git-drs/client/indexd/auth_handler.go:84.68,86.2 1 198 +github.com/calypr/git-drs/client/indexd/auth_handler.go:88.92,90.16 2 198 github.com/calypr/git-drs/client/indexd/auth_handler.go:90.16,92.3 1 0 -github.com/calypr/git-drs/client/indexd/auth_handler.go:94.2,96.12 3 66 -github.com/calypr/git-drs/client/indexd/convert.go:13.78,25.2 2 12 -github.com/calypr/git-drs/client/indexd/convert.go:27.79,29.16 2 24 +github.com/calypr/git-drs/client/indexd/auth_handler.go:94.2,96.12 3 198 +github.com/calypr/git-drs/client/indexd/convert.go:13.78,25.2 2 36 +github.com/calypr/git-drs/client/indexd/convert.go:27.79,29.16 2 72 github.com/calypr/git-drs/client/indexd/convert.go:29.16,31.3 1 0 -github.com/calypr/git-drs/client/indexd/convert.go:32.2,32.35 1 24 -github.com/calypr/git-drs/client/indexd/convert.go:32.35,33.64 1 24 +github.com/calypr/git-drs/client/indexd/convert.go:32.2,32.35 1 72 +github.com/calypr/git-drs/client/indexd/convert.go:32.35,33.64 1 72 github.com/calypr/git-drs/client/indexd/convert.go:33.64,35.4 1 0 -github.com/calypr/git-drs/client/indexd/convert.go:38.2,46.8 1 24 -github.com/calypr/git-drs/client/indexd/convert.go:49.96,51.33 2 24 -github.com/calypr/git-drs/client/indexd/convert.go:51.33,56.17 4 24 +github.com/calypr/git-drs/client/indexd/convert.go:38.2,46.8 1 72 +github.com/calypr/git-drs/client/indexd/convert.go:49.96,51.33 2 72 +github.com/calypr/git-drs/client/indexd/convert.go:51.33,56.17 4 72 github.com/calypr/git-drs/client/indexd/convert.go:56.17,58.4 1 0 -github.com/calypr/git-drs/client/indexd/convert.go:59.3,59.26 1 24 +github.com/calypr/git-drs/client/indexd/convert.go:59.3,59.26 1 72 github.com/calypr/git-drs/client/indexd/convert.go:59.26,62.4 1 0 -github.com/calypr/git-drs/client/indexd/convert.go:62.9,64.4 1 24 -github.com/calypr/git-drs/client/indexd/convert.go:67.3,67.19 1 24 +github.com/calypr/git-drs/client/indexd/convert.go:62.9,64.4 1 72 +github.com/calypr/git-drs/client/indexd/convert.go:67.3,67.19 1 72 github.com/calypr/git-drs/client/indexd/convert.go:67.19,69.4 1 0 -github.com/calypr/git-drs/client/indexd/convert.go:73.3,74.48 2 24 -github.com/calypr/git-drs/client/indexd/convert.go:76.2,76.27 1 24 -github.com/calypr/git-drs/client/indexd/convert.go:80.81,82.39 2 12 -github.com/calypr/git-drs/client/indexd/convert.go:82.39,83.35 1 12 -github.com/calypr/git-drs/client/indexd/convert.go:83.35,85.4 1 12 -github.com/calypr/git-drs/client/indexd/convert.go:87.2,87.14 1 12 -github.com/calypr/git-drs/client/indexd/convert.go:90.76,92.39 2 12 -github.com/calypr/git-drs/client/indexd/convert.go:92.39,94.3 1 12 -github.com/calypr/git-drs/client/indexd/convert.go:95.2,95.13 1 12 +github.com/calypr/git-drs/client/indexd/convert.go:73.3,74.48 2 72 +github.com/calypr/git-drs/client/indexd/convert.go:76.2,76.27 1 72 +github.com/calypr/git-drs/client/indexd/convert.go:80.81,82.39 2 36 +github.com/calypr/git-drs/client/indexd/convert.go:82.39,83.35 1 36 +github.com/calypr/git-drs/client/indexd/convert.go:83.35,85.4 1 36 +github.com/calypr/git-drs/client/indexd/convert.go:87.2,87.14 1 36 +github.com/calypr/git-drs/client/indexd/convert.go:90.76,92.39 2 36 +github.com/calypr/git-drs/client/indexd/convert.go:92.39,94.3 1 36 +github.com/calypr/git-drs/client/indexd/convert.go:95.2,95.13 1 36 github.com/calypr/git-drs/client/indexd/convert.go:98.64,100.16 2 0 github.com/calypr/git-drs/client/indexd/convert.go:100.16,102.3 1 0 github.com/calypr/git-drs/client/indexd/convert.go:103.2,103.15 1 0 -github.com/calypr/git-drs/client/indexd/gen3_remote.go:17.43,19.2 1 108 +github.com/calypr/git-drs/client/indexd/gen3_remote.go:17.43,19.2 1 324 github.com/calypr/git-drs/client/indexd/gen3_remote.go:21.42,23.2 1 0 -github.com/calypr/git-drs/client/indexd/gen3_remote.go:25.44,27.2 1 108 -github.com/calypr/git-drs/client/indexd/gen3_remote.go:29.103,31.16 2 108 +github.com/calypr/git-drs/client/indexd/gen3_remote.go:25.44,27.2 1 324 +github.com/calypr/git-drs/client/indexd/gen3_remote.go:29.103,31.16 2 324 github.com/calypr/git-drs/client/indexd/gen3_remote.go:31.16,33.3 1 0 -github.com/calypr/git-drs/client/indexd/gen3_remote.go:34.2,34.42 1 108 -github.com/calypr/git-drs/client/indexd/indexd_client.go:55.118,60.21 3 108 +github.com/calypr/git-drs/client/indexd/gen3_remote.go:34.2,34.42 1 324 +github.com/calypr/git-drs/client/indexd/indexd_client.go:55.118,60.21 3 324 github.com/calypr/git-drs/client/indexd/indexd_client.go:60.21,62.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:64.2,65.22 2 108 +github.com/calypr/git-drs/client/indexd/indexd_client.go:64.2,65.22 2 324 github.com/calypr/git-drs/client/indexd/indexd_client.go:65.22,67.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:69.2,82.99 5 108 -github.com/calypr/git-drs/client/indexd/indexd_client.go:82.99,83.69 1 66 -github.com/calypr/git-drs/client/indexd/indexd_client.go:83.69,88.4 1 6 -github.com/calypr/git-drs/client/indexd/indexd_client.go:89.3,89.38 1 60 -github.com/calypr/git-drs/client/indexd/indexd_client.go:89.38,93.22 3 60 -github.com/calypr/git-drs/client/indexd/indexd_client.go:93.22,94.62 1 60 +github.com/calypr/git-drs/client/indexd/indexd_client.go:69.2,82.99 5 324 +github.com/calypr/git-drs/client/indexd/indexd_client.go:82.99,83.69 1 198 +github.com/calypr/git-drs/client/indexd/indexd_client.go:83.69,88.4 1 18 +github.com/calypr/git-drs/client/indexd/indexd_client.go:89.3,89.38 1 180 +github.com/calypr/git-drs/client/indexd/indexd_client.go:89.38,93.22 3 180 +github.com/calypr/git-drs/client/indexd/indexd_client.go:93.22,94.62 1 180 github.com/calypr/git-drs/client/indexd/indexd_client.go:94.62,97.6 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:101.3,101.58 1 60 -github.com/calypr/git-drs/client/indexd/indexd_client.go:104.2,111.16 6 108 +github.com/calypr/git-drs/client/indexd/indexd_client.go:101.3,101.58 1 180 +github.com/calypr/git-drs/client/indexd/indexd_client.go:104.2,111.16 6 324 github.com/calypr/git-drs/client/indexd/indexd_client.go:111.16,113.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:115.2,128.8 3 108 -github.com/calypr/git-drs/client/indexd/indexd_client.go:131.47,133.2 1 36 -github.com/calypr/git-drs/client/indexd/indexd_client.go:135.76,140.16 4 108 +github.com/calypr/git-drs/client/indexd/indexd_client.go:115.2,128.8 3 324 +github.com/calypr/git-drs/client/indexd/indexd_client.go:131.47,133.2 1 108 +github.com/calypr/git-drs/client/indexd/indexd_client.go:135.76,140.16 4 324 github.com/calypr/git-drs/client/indexd/indexd_client.go:140.16,142.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:144.2,147.16 3 108 +github.com/calypr/git-drs/client/indexd/indexd_client.go:144.2,147.16 3 324 github.com/calypr/git-drs/client/indexd/indexd_client.go:147.16,149.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:150.2,150.20 1 108 -github.com/calypr/git-drs/client/indexd/indexd_client.go:153.77,158.16 4 108 +github.com/calypr/git-drs/client/indexd/indexd_client.go:150.2,150.20 1 324 +github.com/calypr/git-drs/client/indexd/indexd_client.go:153.77,158.16 4 324 github.com/calypr/git-drs/client/indexd/indexd_client.go:158.16,160.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:162.2,165.16 3 108 +github.com/calypr/git-drs/client/indexd/indexd_client.go:162.2,165.16 3 324 github.com/calypr/git-drs/client/indexd/indexd_client.go:165.16,167.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:169.2,169.32 1 108 +github.com/calypr/git-drs/client/indexd/indexd_client.go:169.2,169.32 1 324 github.com/calypr/git-drs/client/indexd/indexd_client.go:169.32,171.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:173.2,173.20 1 108 -github.com/calypr/git-drs/client/indexd/indexd_client.go:178.54,179.53 1 6 -github.com/calypr/git-drs/client/indexd/indexd_client.go:179.53,181.3 1 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:173.2,173.20 1 324 +github.com/calypr/git-drs/client/indexd/indexd_client.go:178.54,179.53 1 18 +github.com/calypr/git-drs/client/indexd/indexd_client.go:179.53,181.3 1 18 github.com/calypr/git-drs/client/indexd/indexd_client.go:182.2,182.85 1 0 github.com/calypr/git-drs/client/indexd/indexd_client.go:185.72,187.16 2 0 github.com/calypr/git-drs/client/indexd/indexd_client.go:187.16,189.3 1 0 @@ -230,99 +230,99 @@ github.com/calypr/git-drs/client/indexd/indexd_client.go:216.16,218.3 1 0 github.com/calypr/git-drs/client/indexd/indexd_client.go:219.2,219.27 1 0 github.com/calypr/git-drs/client/indexd/indexd_client.go:219.27,221.3 1 0 github.com/calypr/git-drs/client/indexd/indexd_client.go:224.2,224.49 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:227.62,230.16 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:227.62,230.16 2 18 github.com/calypr/git-drs/client/indexd/indexd_client.go:230.16,232.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:235.2,237.16 3 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:235.2,237.16 3 18 github.com/calypr/git-drs/client/indexd/indexd_client.go:237.16,239.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:241.2,242.16 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:241.2,242.16 2 18 github.com/calypr/git-drs/client/indexd/indexd_client.go:242.16,244.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:246.2,249.16 3 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:246.2,249.16 3 18 github.com/calypr/git-drs/client/indexd/indexd_client.go:249.16,251.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:252.2,255.31 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:252.2,255.31 2 18 github.com/calypr/git-drs/client/indexd/indexd_client.go:255.31,257.21 2 0 github.com/calypr/git-drs/client/indexd/indexd_client.go:257.21,259.4 1 0 github.com/calypr/git-drs/client/indexd/indexd_client.go:260.3,261.99 2 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:263.2,263.12 1 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:263.2,263.12 1 18 github.com/calypr/git-drs/client/indexd/indexd_client.go:266.87,269.16 2 0 github.com/calypr/git-drs/client/indexd/indexd_client.go:269.16,271.3 1 0 github.com/calypr/git-drs/client/indexd/indexd_client.go:273.2,273.46 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:277.76,283.16 3 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:277.76,283.16 3 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:283.16,286.3 2 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:287.2,287.51 1 12 -github.com/calypr/git-drs/client/indexd/indexd_client.go:290.112,291.23 1 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:287.2,287.51 1 36 +github.com/calypr/git-drs/client/indexd/indexd_client.go:290.112,291.23 1 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:291.23,294.3 2 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:297.2,298.16 2 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:297.2,298.16 2 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:298.16,301.3 2 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:302.2,302.27 1 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:302.2,302.27 1 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:302.27,305.3 2 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:307.2,311.36 3 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:307.2,311.36 3 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:311.36,314.3 2 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:317.2,318.22 2 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:317.2,318.22 2 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:318.22,321.3 2 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:322.2,325.16 3 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:322.2,325.16 3 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:325.16,327.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:329.2,329.24 1 12 -github.com/calypr/git-drs/client/indexd/indexd_client.go:333.94,339.16 4 24 +github.com/calypr/git-drs/client/indexd/indexd_client.go:329.2,329.24 1 36 +github.com/calypr/git-drs/client/indexd/indexd_client.go:333.94,339.16 4 72 github.com/calypr/git-drs/client/indexd/indexd_client.go:339.16,341.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:343.2,344.16 2 24 +github.com/calypr/git-drs/client/indexd/indexd_client.go:343.2,344.16 2 72 github.com/calypr/git-drs/client/indexd/indexd_client.go:344.16,346.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:348.2,349.16 2 24 +github.com/calypr/git-drs/client/indexd/indexd_client.go:348.2,349.16 2 72 github.com/calypr/git-drs/client/indexd/indexd_client.go:349.16,351.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:352.2,352.15 1 24 -github.com/calypr/git-drs/client/indexd/indexd_client.go:352.15,353.57 1 24 +github.com/calypr/git-drs/client/indexd/indexd_client.go:352.2,352.15 1 72 +github.com/calypr/git-drs/client/indexd/indexd_client.go:352.15,353.57 1 72 github.com/calypr/git-drs/client/indexd/indexd_client.go:353.57,355.4 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:358.2,362.20 3 24 +github.com/calypr/git-drs/client/indexd/indexd_client.go:358.2,362.20 3 72 github.com/calypr/git-drs/client/indexd/indexd_client.go:362.20,364.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:366.2,366.68 1 24 +github.com/calypr/git-drs/client/indexd/indexd_client.go:366.2,366.68 1 72 github.com/calypr/git-drs/client/indexd/indexd_client.go:366.68,368.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:371.2,371.25 1 24 +github.com/calypr/git-drs/client/indexd/indexd_client.go:371.2,371.25 1 72 github.com/calypr/git-drs/client/indexd/indexd_client.go:371.25,373.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:375.2,377.23 2 24 -github.com/calypr/git-drs/client/indexd/indexd_client.go:385.74,390.16 3 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:375.2,377.23 2 72 +github.com/calypr/git-drs/client/indexd/indexd_client.go:385.74,390.16 3 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:390.16,392.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:395.2,396.16 2 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:395.2,396.16 2 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:396.16,398.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:401.2,402.16 2 12 -github.com/calypr/git-drs/client/indexd/indexd_client.go:402.16,404.54 1 6 -github.com/calypr/git-drs/client/indexd/indexd_client.go:404.54,405.18 1 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:401.2,402.16 2 36 +github.com/calypr/git-drs/client/indexd/indexd_client.go:402.16,404.54 1 18 +github.com/calypr/git-drs/client/indexd/indexd_client.go:404.54,405.18 1 18 github.com/calypr/git-drs/client/indexd/indexd_client.go:405.18,407.5 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:407.10,410.19 3 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:407.10,410.19 3 18 github.com/calypr/git-drs/client/indexd/indexd_client.go:410.19,412.6 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:413.5,414.19 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:413.5,414.19 2 18 github.com/calypr/git-drs/client/indexd/indexd_client.go:414.19,416.6 1 0 github.com/calypr/git-drs/client/indexd/indexd_client.go:419.9,421.4 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:425.2,426.16 2 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:425.2,426.16 2 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:426.16,428.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:429.2,429.18 1 12 -github.com/calypr/git-drs/client/indexd/indexd_client.go:429.18,432.3 2 6 -github.com/calypr/git-drs/client/indexd/indexd_client.go:435.2,436.16 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:429.2,429.18 1 36 +github.com/calypr/git-drs/client/indexd/indexd_client.go:429.18,432.3 2 18 +github.com/calypr/git-drs/client/indexd/indexd_client.go:435.2,436.16 2 18 github.com/calypr/git-drs/client/indexd/indexd_client.go:436.16,438.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:441.2,447.16 4 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:441.2,447.16 4 18 github.com/calypr/git-drs/client/indexd/indexd_client.go:447.16,449.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:451.2,452.16 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:451.2,452.16 2 18 github.com/calypr/git-drs/client/indexd/indexd_client.go:452.16,454.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:455.2,456.16 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:455.2,456.16 2 18 github.com/calypr/git-drs/client/indexd/indexd_client.go:456.16,458.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:459.2,459.28 1 6 -github.com/calypr/git-drs/client/indexd/indexd_client.go:459.28,461.17 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:459.2,459.28 1 18 +github.com/calypr/git-drs/client/indexd/indexd_client.go:459.28,461.17 2 18 github.com/calypr/git-drs/client/indexd/indexd_client.go:461.17,463.4 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:466.2,466.44 1 6 -github.com/calypr/git-drs/client/indexd/indexd_client.go:466.44,469.17 3 3 +github.com/calypr/git-drs/client/indexd/indexd_client.go:466.2,466.44 1 18 +github.com/calypr/git-drs/client/indexd/indexd_client.go:466.44,469.17 3 9 github.com/calypr/git-drs/client/indexd/indexd_client.go:469.17,471.4 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:472.8,486.17 3 3 +github.com/calypr/git-drs/client/indexd/indexd_client.go:472.8,486.17 3 9 github.com/calypr/git-drs/client/indexd/indexd_client.go:486.17,488.4 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:490.2,490.23 1 6 -github.com/calypr/git-drs/client/indexd/indexd_client.go:494.84,495.22 1 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:490.2,490.23 1 18 +github.com/calypr/git-drs/client/indexd/indexd_client.go:494.84,495.22 1 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:495.22,497.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:498.2,498.39 1 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:498.2,498.39 1 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:498.39,501.3 2 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:502.2,504.16 3 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:502.2,504.16 3 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:504.16,507.3 2 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:508.2,508.25 1 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:508.2,508.25 1 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:508.25,510.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:512.2,513.16 2 12 -github.com/calypr/git-drs/client/indexd/indexd_client.go:513.16,516.3 2 6 -github.com/calypr/git-drs/client/indexd/indexd_client.go:517.2,518.18 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:512.2,513.16 2 36 +github.com/calypr/git-drs/client/indexd/indexd_client.go:513.16,516.3 2 18 +github.com/calypr/git-drs/client/indexd/indexd_client.go:517.2,518.18 2 18 github.com/calypr/git-drs/client/indexd/indexd_client.go:521.70,527.16 4 0 github.com/calypr/git-drs/client/indexd/indexd_client.go:527.16,529.3 1 0 github.com/calypr/git-drs/client/indexd/indexd_client.go:531.2,532.16 2 0 @@ -360,19 +360,19 @@ github.com/calypr/git-drs/client/indexd/indexd_client.go:633.4,633.13 1 0 github.com/calypr/git-drs/client/indexd/indexd_client.go:637.3,637.26 1 0 github.com/calypr/git-drs/client/indexd/indexd_client.go:637.26,639.4 1 0 github.com/calypr/git-drs/client/indexd/indexd_client.go:642.2,642.17 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:647.95,654.16 3 18 +github.com/calypr/git-drs/client/indexd/indexd_client.go:647.95,654.16 3 54 github.com/calypr/git-drs/client/indexd/indexd_client.go:654.16,656.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:658.2,666.16 5 18 +github.com/calypr/git-drs/client/indexd/indexd_client.go:658.2,666.16 5 54 github.com/calypr/git-drs/client/indexd/indexd_client.go:666.16,668.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:670.2,675.16 4 18 +github.com/calypr/git-drs/client/indexd/indexd_client.go:670.2,675.16 4 54 github.com/calypr/git-drs/client/indexd/indexd_client.go:675.16,677.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:679.2,681.16 3 18 +github.com/calypr/git-drs/client/indexd/indexd_client.go:679.2,681.16 3 54 github.com/calypr/git-drs/client/indexd/indexd_client.go:681.16,683.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:684.2,688.42 3 18 -github.com/calypr/git-drs/client/indexd/indexd_client.go:688.42,691.3 2 6 -github.com/calypr/git-drs/client/indexd/indexd_client.go:692.2,696.16 3 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:684.2,688.42 3 54 +github.com/calypr/git-drs/client/indexd/indexd_client.go:688.42,691.3 2 18 +github.com/calypr/git-drs/client/indexd/indexd_client.go:692.2,696.16 3 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:696.16,698.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:699.2,699.20 1 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:699.2,699.20 1 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:703.62,706.16 2 0 github.com/calypr/git-drs/client/indexd/indexd_client.go:706.16,708.3 1 0 github.com/calypr/git-drs/client/indexd/indexd_client.go:711.2,713.16 3 0 @@ -386,28 +386,28 @@ github.com/calypr/git-drs/client/indexd/indexd_client.go:729.31,731.21 2 0 github.com/calypr/git-drs/client/indexd/indexd_client.go:731.21,733.4 1 0 github.com/calypr/git-drs/client/indexd/indexd_client.go:734.3,735.99 2 0 github.com/calypr/git-drs/client/indexd/indexd_client.go:737.2,737.12 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:741.86,746.16 4 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:741.86,746.16 4 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:746.16,749.3 2 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:750.2,753.16 3 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:750.2,753.16 3 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:753.16,755.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:756.2,760.16 3 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:756.2,760.16 3 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:760.16,762.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:763.2,765.38 2 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:763.2,765.38 2 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:765.38,768.3 2 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:771.2,773.16 3 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:771.2,773.16 3 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:773.16,775.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:777.2,782.31 3 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:777.2,782.31 3 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:782.31,784.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:786.2,788.41 2 12 -github.com/calypr/git-drs/client/indexd/indexd_client.go:788.41,791.34 2 36 -github.com/calypr/git-drs/client/indexd/indexd_client.go:791.34,792.25 1 36 -github.com/calypr/git-drs/client/indexd/indexd_client.go:792.25,794.10 2 12 -github.com/calypr/git-drs/client/indexd/indexd_client.go:797.3,797.13 1 36 -github.com/calypr/git-drs/client/indexd/indexd_client.go:797.13,798.12 1 24 -github.com/calypr/git-drs/client/indexd/indexd_client.go:801.3,802.17 2 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:786.2,788.41 2 36 +github.com/calypr/git-drs/client/indexd/indexd_client.go:788.41,791.34 2 108 +github.com/calypr/git-drs/client/indexd/indexd_client.go:791.34,792.25 1 108 +github.com/calypr/git-drs/client/indexd/indexd_client.go:792.25,794.10 2 36 +github.com/calypr/git-drs/client/indexd/indexd_client.go:797.3,797.13 1 108 +github.com/calypr/git-drs/client/indexd/indexd_client.go:797.13,798.12 1 72 +github.com/calypr/git-drs/client/indexd/indexd_client.go:801.3,802.17 2 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:802.17,804.4 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:805.3,805.29 1 12 -github.com/calypr/git-drs/client/indexd/indexd_client.go:808.2,808.17 1 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:805.3,805.29 1 36 +github.com/calypr/git-drs/client/indexd/indexd_client.go:808.2,808.17 1 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:813.96,814.16 1 0 github.com/calypr/git-drs/client/indexd/indexd_client.go:814.16,816.3 1 0 github.com/calypr/git-drs/client/indexd/indexd_client.go:818.2,822.16 3 0 @@ -480,23 +480,23 @@ github.com/calypr/git-drs/client/indexd/indexd_client.go:1053.38,1056.3 2 0 github.com/calypr/git-drs/client/indexd/indexd_client.go:1058.2,1059.72 2 0 github.com/calypr/git-drs/client/indexd/indexd_client.go:1059.72,1061.3 1 0 github.com/calypr/git-drs/client/indexd/indexd_client.go:1063.2,1063.20 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:1066.121,1068.18 2 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1066.121,1068.18 2 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:1068.18,1070.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:1073.2,1076.16 3 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1073.2,1076.16 3 36 github.com/calypr/git-drs/client/indexd/indexd_client.go:1076.16,1078.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:1079.2,1093.21 3 12 -github.com/calypr/git-drs/client/indexd/indexd_client.go:1097.79,1101.16 3 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1079.2,1093.21 3 36 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1097.79,1101.16 3 18 github.com/calypr/git-drs/client/indexd/indexd_client.go:1101.16,1103.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:1105.2,1106.16 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1105.2,1106.16 2 18 github.com/calypr/git-drs/client/indexd/indexd_client.go:1106.16,1108.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:1109.2,1112.16 3 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1109.2,1112.16 3 18 github.com/calypr/git-drs/client/indexd/indexd_client.go:1112.16,1114.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:1115.2,1117.38 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1115.2,1117.38 2 18 github.com/calypr/git-drs/client/indexd/indexd_client.go:1117.38,1120.3 2 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:1122.2,1123.72 2 6 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1122.2,1123.72 2 18 github.com/calypr/git-drs/client/indexd/indexd_client.go:1123.72,1125.3 1 0 -github.com/calypr/git-drs/client/indexd/indexd_client.go:1127.2,1127.20 1 6 -github.com/calypr/git-drs/client/indexd/records.go:90.62,102.2 1 12 +github.com/calypr/git-drs/client/indexd/indexd_client.go:1127.2,1127.20 1 18 +github.com/calypr/git-drs/client/indexd/records.go:90.62,102.2 1 36 github.com/calypr/git-drs/client/indexd/utils.go:21.174,23.23 1 0 github.com/calypr/git-drs/client/indexd/utils.go:23.23,25.3 1 0 github.com/calypr/git-drs/client/indexd/utils.go:27.2,28.16 2 0 @@ -515,8 +515,8 @@ github.com/calypr/git-drs/client/indexd/utils.go:55.58,56.50 1 0 github.com/calypr/git-drs/client/indexd/utils.go:56.50,58.4 1 0 github.com/calypr/git-drs/client/indexd/utils.go:59.3,59.72 1 0 github.com/calypr/git-drs/client/indexd/utils.go:62.2,62.17 1 0 -github.com/calypr/git-drs/cmd/root.go:30.61,32.3 0 126 -github.com/calypr/git-drs/cmd/root.go:35.13,58.2 21 126 +github.com/calypr/git-drs/cmd/root.go:30.61,32.3 0 378 +github.com/calypr/git-drs/cmd/root.go:35.13,58.2 21 378 github.com/calypr/git-drs/cmd/addref/add-ref.go:21.54,30.17 6 0 github.com/calypr/git-drs/cmd/addref/add-ref.go:30.17,32.4 1 0 github.com/calypr/git-drs/cmd/addref/add-ref.go:34.3,35.17 2 0 @@ -533,7 +533,7 @@ github.com/calypr/git-drs/cmd/addref/add-ref.go:55.19,57.4 1 0 github.com/calypr/git-drs/cmd/addref/add-ref.go:58.3,60.25 3 0 github.com/calypr/git-drs/cmd/addref/add-ref.go:60.25,63.4 1 0 github.com/calypr/git-drs/cmd/addref/add-ref.go:65.3,66.13 2 0 -github.com/calypr/git-drs/cmd/addref/add-ref.go:70.13,72.2 1 126 +github.com/calypr/git-drs/cmd/addref/add-ref.go:70.13,72.2 1 378 github.com/calypr/git-drs/cmd/addurl/main.go:21.54,22.21 1 0 github.com/calypr/git-drs/cmd/addurl/main.go:22.21,25.4 2 0 github.com/calypr/git-drs/cmd/addurl/main.go:26.3,26.13 1 0 @@ -556,7 +556,7 @@ github.com/calypr/git-drs/cmd/addurl/main.go:79.17,81.4 1 0 github.com/calypr/git-drs/cmd/addurl/main.go:82.3,82.77 1 0 github.com/calypr/git-drs/cmd/addurl/main.go:82.77,84.4 1 0 github.com/calypr/git-drs/cmd/addurl/main.go:85.3,86.13 2 0 -github.com/calypr/git-drs/cmd/addurl/main.go:90.13,96.2 5 126 +github.com/calypr/git-drs/cmd/addurl/main.go:90.13,96.2 5 378 github.com/calypr/git-drs/cmd/addurl/main.go:98.80,103.66 2 0 github.com/calypr/git-drs/cmd/addurl/main.go:103.66,105.3 1 0 github.com/calypr/git-drs/cmd/addurl/main.go:108.2,108.77 1 0 @@ -614,7 +614,7 @@ github.com/calypr/git-drs/cmd/delete/main.go:87.129,89.5 1 0 github.com/calypr/git-drs/cmd/delete/main.go:93.3,94.17 2 0 github.com/calypr/git-drs/cmd/delete/main.go:94.17,96.4 1 0 github.com/calypr/git-drs/cmd/delete/main.go:98.3,99.13 2 0 -github.com/calypr/git-drs/cmd/delete/main.go:103.13,106.2 2 126 +github.com/calypr/git-drs/cmd/delete/main.go:103.13,106.2 2 378 github.com/calypr/git-drs/cmd/deleteproject/main.go:26.54,31.17 4 0 github.com/calypr/git-drs/cmd/deleteproject/main.go:31.17,33.4 1 0 github.com/calypr/git-drs/cmd/deleteproject/main.go:35.3,36.17 2 0 @@ -639,7 +639,7 @@ github.com/calypr/git-drs/cmd/deleteproject/main.go:85.154,87.5 1 0 github.com/calypr/git-drs/cmd/deleteproject/main.go:91.3,93.17 3 0 github.com/calypr/git-drs/cmd/deleteproject/main.go:93.17,95.4 1 0 github.com/calypr/git-drs/cmd/deleteproject/main.go:97.3,98.13 2 0 -github.com/calypr/git-drs/cmd/deleteproject/main.go:102.13,105.2 2 126 +github.com/calypr/git-drs/cmd/deleteproject/main.go:102.13,105.2 2 378 github.com/calypr/git-drs/cmd/download/main.go:25.54,26.21 1 0 github.com/calypr/git-drs/cmd/download/main.go:26.21,29.4 2 0 github.com/calypr/git-drs/cmd/download/main.go:30.3,30.13 1 0 @@ -662,7 +662,7 @@ github.com/calypr/git-drs/cmd/download/main.go:70.17,72.4 1 0 github.com/calypr/git-drs/cmd/download/main.go:74.3,74.17 1 0 github.com/calypr/git-drs/cmd/download/main.go:74.17,76.4 1 0 github.com/calypr/git-drs/cmd/download/main.go:78.3,80.13 2 0 -github.com/calypr/git-drs/cmd/download/main.go:84.13,87.2 2 126 +github.com/calypr/git-drs/cmd/download/main.go:84.13,87.2 2 378 github.com/calypr/git-drs/cmd/fetch/main.go:16.54,17.20 1 0 github.com/calypr/git-drs/cmd/fetch/main.go:17.20,20.4 2 0 github.com/calypr/git-drs/cmd/fetch/main.go:21.3,21.13 1 0 @@ -677,62 +677,62 @@ github.com/calypr/git-drs/cmd/fetch/main.go:43.17,46.4 2 0 github.com/calypr/git-drs/cmd/fetch/main.go:48.3,49.17 2 0 github.com/calypr/git-drs/cmd/fetch/main.go:49.17,51.4 1 0 github.com/calypr/git-drs/cmd/fetch/main.go:53.3,53.13 1 0 -github.com/calypr/git-drs/cmd/initialize/main.go:29.54,30.21 1 12 +github.com/calypr/git-drs/cmd/initialize/main.go:29.54,30.21 1 36 github.com/calypr/git-drs/cmd/initialize/main.go:30.21,33.4 2 0 -github.com/calypr/git-drs/cmd/initialize/main.go:34.3,34.13 1 12 -github.com/calypr/git-drs/cmd/initialize/main.go:36.54,41.17 3 12 +github.com/calypr/git-drs/cmd/initialize/main.go:34.3,34.13 1 36 +github.com/calypr/git-drs/cmd/initialize/main.go:36.54,41.17 3 36 github.com/calypr/git-drs/cmd/initialize/main.go:41.17,43.4 1 0 -github.com/calypr/git-drs/cmd/initialize/main.go:46.3,47.17 2 12 +github.com/calypr/git-drs/cmd/initialize/main.go:46.3,47.17 2 36 github.com/calypr/git-drs/cmd/initialize/main.go:47.17,49.4 1 0 -github.com/calypr/git-drs/cmd/initialize/main.go:52.3,53.17 2 12 +github.com/calypr/git-drs/cmd/initialize/main.go:52.3,53.17 2 36 github.com/calypr/git-drs/cmd/initialize/main.go:53.17,56.4 2 0 -github.com/calypr/git-drs/cmd/initialize/main.go:59.3,63.45 4 12 -github.com/calypr/git-drs/cmd/initialize/main.go:63.45,64.64 1 36 +github.com/calypr/git-drs/cmd/initialize/main.go:59.3,63.45 4 36 +github.com/calypr/git-drs/cmd/initialize/main.go:63.45,64.64 1 108 github.com/calypr/git-drs/cmd/initialize/main.go:64.64,66.5 1 0 -github.com/calypr/git-drs/cmd/initialize/main.go:70.3,72.17 3 12 +github.com/calypr/git-drs/cmd/initialize/main.go:70.3,72.17 3 36 github.com/calypr/git-drs/cmd/initialize/main.go:72.17,74.4 1 0 -github.com/calypr/git-drs/cmd/initialize/main.go:75.3,75.22 1 12 -github.com/calypr/git-drs/cmd/initialize/main.go:75.22,77.4 1 6 -github.com/calypr/git-drs/cmd/initialize/main.go:77.9,79.4 1 6 -github.com/calypr/git-drs/cmd/initialize/main.go:82.3,83.49 2 12 +github.com/calypr/git-drs/cmd/initialize/main.go:75.3,75.22 1 36 +github.com/calypr/git-drs/cmd/initialize/main.go:75.22,77.4 1 18 +github.com/calypr/git-drs/cmd/initialize/main.go:77.9,79.4 1 18 +github.com/calypr/git-drs/cmd/initialize/main.go:82.3,83.49 2 36 github.com/calypr/git-drs/cmd/initialize/main.go:83.49,85.4 1 0 -github.com/calypr/git-drs/cmd/initialize/main.go:89.3,90.17 2 12 +github.com/calypr/git-drs/cmd/initialize/main.go:89.3,90.17 2 36 github.com/calypr/git-drs/cmd/initialize/main.go:90.17,92.4 1 0 -github.com/calypr/git-drs/cmd/initialize/main.go:95.3,96.17 2 12 +github.com/calypr/git-drs/cmd/initialize/main.go:95.3,96.17 2 36 github.com/calypr/git-drs/cmd/initialize/main.go:96.17,98.4 1 0 -github.com/calypr/git-drs/cmd/initialize/main.go:101.3,103.13 3 12 -github.com/calypr/git-drs/cmd/initialize/main.go:107.28,118.31 2 12 -github.com/calypr/git-drs/cmd/initialize/main.go:118.31,120.46 2 72 +github.com/calypr/git-drs/cmd/initialize/main.go:101.3,103.13 3 36 +github.com/calypr/git-drs/cmd/initialize/main.go:107.28,118.31 2 36 +github.com/calypr/git-drs/cmd/initialize/main.go:118.31,120.46 2 216 github.com/calypr/git-drs/cmd/initialize/main.go:120.46,122.4 1 0 -github.com/calypr/git-drs/cmd/initialize/main.go:125.2,125.12 1 12 -github.com/calypr/git-drs/cmd/initialize/main.go:128.13,130.2 1 126 -github.com/calypr/git-drs/cmd/initialize/main.go:132.51,135.16 3 12 +github.com/calypr/git-drs/cmd/initialize/main.go:125.2,125.12 1 36 +github.com/calypr/git-drs/cmd/initialize/main.go:128.13,130.2 1 378 +github.com/calypr/git-drs/cmd/initialize/main.go:132.51,135.16 3 36 github.com/calypr/git-drs/cmd/initialize/main.go:135.16,137.3 1 0 -github.com/calypr/git-drs/cmd/initialize/main.go:138.2,140.52 3 12 +github.com/calypr/git-drs/cmd/initialize/main.go:138.2,140.52 3 36 github.com/calypr/git-drs/cmd/initialize/main.go:140.52,142.3 1 0 -github.com/calypr/git-drs/cmd/initialize/main.go:144.2,164.16 5 12 -github.com/calypr/git-drs/cmd/initialize/main.go:164.16,169.73 3 6 +github.com/calypr/git-drs/cmd/initialize/main.go:144.2,164.16 5 36 +github.com/calypr/git-drs/cmd/initialize/main.go:164.16,169.73 3 18 github.com/calypr/git-drs/cmd/initialize/main.go:169.73,171.4 1 0 -github.com/calypr/git-drs/cmd/initialize/main.go:172.3,172.45 1 6 +github.com/calypr/git-drs/cmd/initialize/main.go:172.3,172.45 1 18 github.com/calypr/git-drs/cmd/initialize/main.go:172.45,174.4 1 0 -github.com/calypr/git-drs/cmd/initialize/main.go:175.3,175.75 1 6 -github.com/calypr/git-drs/cmd/initialize/main.go:178.2,178.39 1 12 +github.com/calypr/git-drs/cmd/initialize/main.go:175.3,175.75 1 18 +github.com/calypr/git-drs/cmd/initialize/main.go:178.2,178.39 1 36 github.com/calypr/git-drs/cmd/initialize/main.go:178.39,180.3 1 0 -github.com/calypr/git-drs/cmd/initialize/main.go:182.2,183.16 2 12 +github.com/calypr/git-drs/cmd/initialize/main.go:182.2,183.16 2 36 github.com/calypr/git-drs/cmd/initialize/main.go:183.16,185.3 1 0 -github.com/calypr/git-drs/cmd/initialize/main.go:186.2,187.12 2 12 -github.com/calypr/git-drs/cmd/initialize/main.go:192.77,202.16 5 36 -github.com/calypr/git-drs/cmd/initialize/main.go:202.16,205.22 3 36 -github.com/calypr/git-drs/cmd/initialize/main.go:205.22,208.48 2 180 -github.com/calypr/git-drs/cmd/initialize/main.go:208.48,210.5 1 36 -github.com/calypr/git-drs/cmd/initialize/main.go:211.4,211.31 1 180 -github.com/calypr/git-drs/cmd/initialize/main.go:213.3,213.39 1 36 +github.com/calypr/git-drs/cmd/initialize/main.go:186.2,187.12 2 36 +github.com/calypr/git-drs/cmd/initialize/main.go:192.77,202.16 5 108 +github.com/calypr/git-drs/cmd/initialize/main.go:202.16,205.22 3 108 +github.com/calypr/git-drs/cmd/initialize/main.go:205.22,208.48 2 540 +github.com/calypr/git-drs/cmd/initialize/main.go:208.48,210.5 1 108 +github.com/calypr/git-drs/cmd/initialize/main.go:211.4,211.31 1 540 +github.com/calypr/git-drs/cmd/initialize/main.go:213.3,213.39 1 108 github.com/calypr/git-drs/cmd/initialize/main.go:213.39,215.4 1 0 github.com/calypr/git-drs/cmd/initialize/main.go:216.8,216.31 1 0 github.com/calypr/git-drs/cmd/initialize/main.go:216.31,219.3 1 0 github.com/calypr/git-drs/cmd/initialize/main.go:219.8,221.3 1 0 -github.com/calypr/git-drs/cmd/initialize/main.go:223.2,223.11 1 36 -github.com/calypr/git-drs/cmd/initialize/main.go:223.11,225.3 1 36 +github.com/calypr/git-drs/cmd/initialize/main.go:223.2,223.11 1 108 +github.com/calypr/git-drs/cmd/initialize/main.go:223.11,225.3 1 108 github.com/calypr/git-drs/cmd/initialize/main.go:228.2,228.68 1 0 github.com/calypr/git-drs/cmd/initialize/main.go:228.68,230.3 1 0 github.com/calypr/git-drs/cmd/initialize/main.go:231.2,235.16 3 0 @@ -805,7 +805,7 @@ github.com/calypr/git-drs/cmd/list/main.go:170.18,172.5 1 0 github.com/calypr/git-drs/cmd/list/main.go:173.4,174.18 2 0 github.com/calypr/git-drs/cmd/list/main.go:174.18,176.5 1 0 github.com/calypr/git-drs/cmd/list/main.go:178.3,178.13 1 0 -github.com/calypr/git-drs/cmd/list/main.go:182.13,188.2 5 126 +github.com/calypr/git-drs/cmd/list/main.go:182.13,188.2 5 378 github.com/calypr/git-drs/cmd/listconfig/main.go:22.54,23.21 1 0 github.com/calypr/git-drs/cmd/listconfig/main.go:23.21,26.4 2 0 github.com/calypr/git-drs/cmd/listconfig/main.go:27.3,27.13 1 0 @@ -814,51 +814,51 @@ github.com/calypr/git-drs/cmd/listconfig/main.go:32.17,34.4 1 0 github.com/calypr/git-drs/cmd/listconfig/main.go:36.3,36.17 1 0 github.com/calypr/git-drs/cmd/listconfig/main.go:36.17,41.4 3 0 github.com/calypr/git-drs/cmd/listconfig/main.go:41.9,48.4 4 0 -github.com/calypr/git-drs/cmd/listconfig/main.go:52.13,54.2 1 126 -github.com/calypr/git-drs/cmd/prepush/main.go:24.54,27.17 2 12 +github.com/calypr/git-drs/cmd/listconfig/main.go:52.13,54.2 1 378 +github.com/calypr/git-drs/cmd/prepush/main.go:24.54,27.17 2 36 github.com/calypr/git-drs/cmd/prepush/main.go:27.17,29.4 1 0 -github.com/calypr/git-drs/cmd/prepush/main.go:31.3,34.17 3 12 +github.com/calypr/git-drs/cmd/prepush/main.go:31.3,34.17 3 36 github.com/calypr/git-drs/cmd/prepush/main.go:34.17,36.4 1 0 -github.com/calypr/git-drs/cmd/prepush/main.go:42.3,44.21 3 12 -github.com/calypr/git-drs/cmd/prepush/main.go:44.21,46.4 1 12 -github.com/calypr/git-drs/cmd/prepush/main.go:47.3,47.21 1 12 -github.com/calypr/git-drs/cmd/prepush/main.go:47.21,49.4 1 12 -github.com/calypr/git-drs/cmd/prepush/main.go:50.3,50.26 1 12 +github.com/calypr/git-drs/cmd/prepush/main.go:42.3,44.21 3 36 +github.com/calypr/git-drs/cmd/prepush/main.go:44.21,46.4 1 36 +github.com/calypr/git-drs/cmd/prepush/main.go:47.3,47.21 1 36 +github.com/calypr/git-drs/cmd/prepush/main.go:47.21,49.4 1 36 +github.com/calypr/git-drs/cmd/prepush/main.go:50.3,50.26 1 36 github.com/calypr/git-drs/cmd/prepush/main.go:50.26,52.4 1 0 -github.com/calypr/git-drs/cmd/prepush/main.go:53.3,58.17 4 12 +github.com/calypr/git-drs/cmd/prepush/main.go:53.3,58.17 4 36 github.com/calypr/git-drs/cmd/prepush/main.go:58.17,63.4 3 0 -github.com/calypr/git-drs/cmd/prepush/main.go:66.3,67.17 2 12 +github.com/calypr/git-drs/cmd/prepush/main.go:66.3,67.17 2 36 github.com/calypr/git-drs/cmd/prepush/main.go:67.17,72.4 3 0 -github.com/calypr/git-drs/cmd/prepush/main.go:74.3,75.10 2 12 +github.com/calypr/git-drs/cmd/prepush/main.go:74.3,75.10 2 36 github.com/calypr/git-drs/cmd/prepush/main.go:75.10,77.4 1 0 -github.com/calypr/git-drs/cmd/prepush/main.go:78.3,82.17 3 12 +github.com/calypr/git-drs/cmd/prepush/main.go:78.3,82.17 3 36 github.com/calypr/git-drs/cmd/prepush/main.go:82.17,85.4 2 0 -github.com/calypr/git-drs/cmd/prepush/main.go:86.3,86.16 1 12 -github.com/calypr/git-drs/cmd/prepush/main.go:86.16,89.4 2 12 -github.com/calypr/git-drs/cmd/prepush/main.go:92.3,92.51 1 12 +github.com/calypr/git-drs/cmd/prepush/main.go:86.3,86.16 1 36 +github.com/calypr/git-drs/cmd/prepush/main.go:86.16,89.4 2 36 +github.com/calypr/git-drs/cmd/prepush/main.go:92.3,92.51 1 36 github.com/calypr/git-drs/cmd/prepush/main.go:92.51,95.4 2 0 -github.com/calypr/git-drs/cmd/prepush/main.go:98.3,98.43 1 12 +github.com/calypr/git-drs/cmd/prepush/main.go:98.3,98.43 1 36 github.com/calypr/git-drs/cmd/prepush/main.go:98.43,101.4 2 0 -github.com/calypr/git-drs/cmd/prepush/main.go:104.3,105.17 2 12 +github.com/calypr/git-drs/cmd/prepush/main.go:104.3,105.17 2 36 github.com/calypr/git-drs/cmd/prepush/main.go:105.17,108.4 2 0 -github.com/calypr/git-drs/cmd/prepush/main.go:110.3,112.17 3 12 +github.com/calypr/git-drs/cmd/prepush/main.go:110.3,112.17 3 36 github.com/calypr/git-drs/cmd/prepush/main.go:112.17,115.4 2 0 -github.com/calypr/git-drs/cmd/prepush/main.go:116.3,119.13 3 12 -github.com/calypr/git-drs/cmd/prepush/main.go:126.55,130.40 1 12 +github.com/calypr/git-drs/cmd/prepush/main.go:116.3,119.13 3 36 +github.com/calypr/git-drs/cmd/prepush/main.go:126.55,130.40 1 36 github.com/calypr/git-drs/cmd/prepush/main.go:130.40,132.3 1 0 -github.com/calypr/git-drs/cmd/prepush/main.go:133.2,135.21 3 12 -github.com/calypr/git-drs/cmd/prepush/main.go:135.21,138.22 3 12 +github.com/calypr/git-drs/cmd/prepush/main.go:133.2,135.21 3 36 +github.com/calypr/git-drs/cmd/prepush/main.go:135.21,138.22 3 36 github.com/calypr/git-drs/cmd/prepush/main.go:138.22,139.12 1 0 -github.com/calypr/git-drs/cmd/prepush/main.go:141.3,143.42 3 12 -github.com/calypr/git-drs/cmd/prepush/main.go:143.42,145.20 2 12 -github.com/calypr/git-drs/cmd/prepush/main.go:145.20,147.5 1 12 -github.com/calypr/git-drs/cmd/prepush/main.go:150.2,150.38 1 12 +github.com/calypr/git-drs/cmd/prepush/main.go:141.3,143.42 3 36 +github.com/calypr/git-drs/cmd/prepush/main.go:143.42,145.20 2 36 +github.com/calypr/git-drs/cmd/prepush/main.go:145.20,147.5 1 36 +github.com/calypr/git-drs/cmd/prepush/main.go:150.2,150.38 1 36 github.com/calypr/git-drs/cmd/prepush/main.go:150.38,152.3 1 0 -github.com/calypr/git-drs/cmd/prepush/main.go:153.2,154.21 2 12 -github.com/calypr/git-drs/cmd/prepush/main.go:154.21,156.3 1 12 -github.com/calypr/git-drs/cmd/prepush/main.go:157.2,159.40 2 12 +github.com/calypr/git-drs/cmd/prepush/main.go:153.2,154.21 2 36 +github.com/calypr/git-drs/cmd/prepush/main.go:154.21,156.3 1 36 +github.com/calypr/git-drs/cmd/prepush/main.go:157.2,159.40 2 36 github.com/calypr/git-drs/cmd/prepush/main.go:159.40,161.3 1 0 -github.com/calypr/git-drs/cmd/prepush/main.go:162.2,162.22 1 12 +github.com/calypr/git-drs/cmd/prepush/main.go:162.2,162.22 1 36 github.com/calypr/git-drs/cmd/push/main.go:16.54,17.20 1 0 github.com/calypr/git-drs/cmd/push/main.go:17.20,20.4 2 0 github.com/calypr/git-drs/cmd/push/main.go:21.3,21.13 1 0 @@ -887,7 +887,7 @@ github.com/calypr/git-drs/cmd/query/main.go:46.17,48.4 1 0 github.com/calypr/git-drs/cmd/query/main.go:49.3,50.17 2 0 github.com/calypr/git-drs/cmd/query/main.go:50.17,52.4 1 0 github.com/calypr/git-drs/cmd/query/main.go:53.3,54.13 2 0 -github.com/calypr/git-drs/cmd/query/main.go:58.13,60.2 1 126 +github.com/calypr/git-drs/cmd/query/main.go:58.13,60.2 1 378 github.com/calypr/git-drs/cmd/register/main.go:20.54,21.21 1 0 github.com/calypr/git-drs/cmd/register/main.go:21.21,24.4 2 0 github.com/calypr/git-drs/cmd/register/main.go:25.3,25.13 1 0 @@ -921,7 +921,7 @@ github.com/calypr/git-drs/cmd/register/main.go:113.4,114.21 2 0 github.com/calypr/git-drs/cmd/register/main.go:118.3,121.21 2 0 github.com/calypr/git-drs/cmd/register/main.go:121.21,123.4 1 0 github.com/calypr/git-drs/cmd/register/main.go:125.3,125.13 1 0 -github.com/calypr/git-drs/cmd/register/main.go:129.13,131.2 1 126 +github.com/calypr/git-drs/cmd/register/main.go:129.13,131.2 1 378 github.com/calypr/git-drs/cmd/remote/list.go:14.54,15.21 1 0 github.com/calypr/git-drs/cmd/remote/list.go:15.21,18.4 2 0 github.com/calypr/git-drs/cmd/remote/list.go:19.3,19.13 1 0 @@ -939,7 +939,7 @@ github.com/calypr/git-drs/cmd/remote/list.go:50.4,51.21 2 0 github.com/calypr/git-drs/cmd/remote/list.go:51.21,53.5 1 0 github.com/calypr/git-drs/cmd/remote/list.go:55.4,55.72 1 0 github.com/calypr/git-drs/cmd/remote/list.go:57.3,57.13 1 0 -github.com/calypr/git-drs/cmd/remote/root.go:14.13,18.2 3 126 +github.com/calypr/git-drs/cmd/remote/root.go:14.13,18.2 3 378 github.com/calypr/git-drs/cmd/remote/set.go:15.54,16.21 1 0 github.com/calypr/git-drs/cmd/remote/set.go:16.21,19.4 2 0 github.com/calypr/git-drs/cmd/remote/set.go:20.3,20.13 1 0 @@ -960,83 +960,83 @@ github.com/calypr/git-drs/cmd/remote/add/anvil.go:26.63,28.24 1 0 github.com/calypr/git-drs/cmd/remote/add/anvil.go:28.24,41.17 4 0 github.com/calypr/git-drs/cmd/remote/add/anvil.go:41.17,43.4 1 0 github.com/calypr/git-drs/cmd/remote/add/anvil.go:47.2,47.12 1 0 -github.com/calypr/git-drs/cmd/remote/add/gen3.go:17.54,18.20 1 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:17.54,18.20 1 18 github.com/calypr/git-drs/cmd/remote/add/gen3.go:18.20,21.4 2 0 -github.com/calypr/git-drs/cmd/remote/add/gen3.go:22.3,22.13 1 6 -github.com/calypr/git-drs/cmd/remote/add/gen3.go:24.54,28.59 2 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:22.3,22.13 1 18 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:24.54,28.59 2 18 github.com/calypr/git-drs/cmd/remote/add/gen3.go:28.59,30.4 1 0 -github.com/calypr/git-drs/cmd/remote/add/gen3.go:33.3,33.19 1 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:33.3,33.19 1 18 github.com/calypr/git-drs/cmd/remote/add/gen3.go:33.19,35.4 1 0 -github.com/calypr/git-drs/cmd/remote/add/gen3.go:37.3,38.20 2 6 -github.com/calypr/git-drs/cmd/remote/add/gen3.go:38.20,40.4 1 6 -github.com/calypr/git-drs/cmd/remote/add/gen3.go:42.3,43.17 2 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:37.3,38.20 2 18 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:38.20,40.4 1 18 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:42.3,43.17 2 18 github.com/calypr/git-drs/cmd/remote/add/gen3.go:43.17,45.4 1 0 -github.com/calypr/git-drs/cmd/remote/add/gen3.go:46.3,46.13 1 6 -github.com/calypr/git-drs/cmd/remote/add/gen3.go:50.96,51.22 1 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:46.3,46.13 1 18 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:50.96,51.22 1 18 github.com/calypr/git-drs/cmd/remote/add/gen3.go:51.22,53.3 1 0 -github.com/calypr/git-drs/cmd/remote/add/gen3.go:54.2,54.35 1 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:54.2,54.35 1 18 github.com/calypr/git-drs/cmd/remote/add/gen3.go:54.35,56.3 1 0 -github.com/calypr/git-drs/cmd/remote/add/gen3.go:58.2,60.9 3 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:58.2,60.9 3 18 github.com/calypr/git-drs/cmd/remote/add/gen3.go:61.24,65.17 4 0 github.com/calypr/git-drs/cmd/remote/add/gen3.go:65.17,67.4 1 0 -github.com/calypr/git-drs/cmd/remote/add/gen3.go:69.22,71.17 2 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:69.22,71.17 2 18 github.com/calypr/git-drs/cmd/remote/add/gen3.go:71.17,73.4 1 0 -github.com/calypr/git-drs/cmd/remote/add/gen3.go:74.3,79.17 5 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:74.3,79.17 5 18 github.com/calypr/git-drs/cmd/remote/add/gen3.go:79.17,81.4 1 0 github.com/calypr/git-drs/cmd/remote/add/gen3.go:83.10,85.17 2 0 github.com/calypr/git-drs/cmd/remote/add/gen3.go:85.17,90.4 4 0 github.com/calypr/git-drs/cmd/remote/add/gen3.go:90.9,92.4 1 0 -github.com/calypr/git-drs/cmd/remote/add/gen3.go:95.2,95.23 1 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:95.2,95.23 1 18 github.com/calypr/git-drs/cmd/remote/add/gen3.go:95.23,97.3 1 0 -github.com/calypr/git-drs/cmd/remote/add/gen3.go:99.2,108.67 3 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:99.2,108.67 3 18 github.com/calypr/git-drs/cmd/remote/add/gen3.go:108.67,110.3 1 0 -github.com/calypr/git-drs/cmd/remote/add/gen3.go:111.2,124.45 3 6 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:111.2,124.45 3 18 github.com/calypr/git-drs/cmd/remote/add/gen3.go:124.45,126.3 1 0 -github.com/calypr/git-drs/cmd/remote/add/gen3.go:128.2,129.12 2 6 -github.com/calypr/git-drs/cmd/remote/add/init.go:21.13,32.2 9 126 -github.com/calypr/git-drs/cmd/transfer/main.go:47.54,59.22 8 96 +github.com/calypr/git-drs/cmd/remote/add/gen3.go:128.2,129.12 2 18 +github.com/calypr/git-drs/cmd/remote/add/init.go:21.13,32.2 9 378 +github.com/calypr/git-drs/cmd/transfer/main.go:47.54,59.22 8 288 github.com/calypr/git-drs/cmd/transfer/main.go:59.22,64.4 4 0 -github.com/calypr/git-drs/cmd/transfer/main.go:66.3,69.64 4 96 +github.com/calypr/git-drs/cmd/transfer/main.go:66.3,69.64 4 288 github.com/calypr/git-drs/cmd/transfer/main.go:69.64,73.4 3 0 -github.com/calypr/git-drs/cmd/transfer/main.go:75.3,75.30 1 96 +github.com/calypr/git-drs/cmd/transfer/main.go:75.3,75.30 1 288 github.com/calypr/git-drs/cmd/transfer/main.go:75.30,80.4 4 0 -github.com/calypr/git-drs/cmd/transfer/main.go:82.3,86.17 3 96 +github.com/calypr/git-drs/cmd/transfer/main.go:82.3,86.17 3 288 github.com/calypr/git-drs/cmd/transfer/main.go:86.17,90.4 3 0 -github.com/calypr/git-drs/cmd/transfer/main.go:93.3,94.17 2 96 +github.com/calypr/git-drs/cmd/transfer/main.go:93.3,94.17 2 288 github.com/calypr/git-drs/cmd/transfer/main.go:94.17,98.4 3 0 -github.com/calypr/git-drs/cmd/transfer/main.go:100.3,101.17 2 96 +github.com/calypr/git-drs/cmd/transfer/main.go:100.3,101.17 2 288 github.com/calypr/git-drs/cmd/transfer/main.go:101.17,105.4 3 0 -github.com/calypr/git-drs/cmd/transfer/main.go:108.3,108.87 1 96 -github.com/calypr/git-drs/cmd/transfer/main.go:108.87,111.4 2 96 +github.com/calypr/git-drs/cmd/transfer/main.go:108.3,108.87 1 288 +github.com/calypr/git-drs/cmd/transfer/main.go:108.87,111.4 2 288 github.com/calypr/git-drs/cmd/transfer/main.go:111.9,116.4 4 0 -github.com/calypr/git-drs/cmd/transfer/main.go:117.3,117.64 1 96 +github.com/calypr/git-drs/cmd/transfer/main.go:117.3,117.64 1 288 github.com/calypr/git-drs/cmd/transfer/main.go:117.64,120.4 2 0 -github.com/calypr/git-drs/cmd/transfer/main.go:122.3,122.22 1 96 -github.com/calypr/git-drs/cmd/transfer/main.go:122.22,125.18 3 120 +github.com/calypr/git-drs/cmd/transfer/main.go:122.3,122.22 1 288 +github.com/calypr/git-drs/cmd/transfer/main.go:122.22,125.18 3 360 github.com/calypr/git-drs/cmd/transfer/main.go:125.18,127.13 2 0 -github.com/calypr/git-drs/cmd/transfer/main.go:130.4,130.56 1 120 -github.com/calypr/git-drs/cmd/transfer/main.go:130.56,136.76 3 12 +github.com/calypr/git-drs/cmd/transfer/main.go:130.4,130.56 1 360 +github.com/calypr/git-drs/cmd/transfer/main.go:130.56,136.76 3 36 github.com/calypr/git-drs/cmd/transfer/main.go:136.76,140.14 4 0 -github.com/calypr/git-drs/cmd/transfer/main.go:142.5,146.19 3 12 +github.com/calypr/git-drs/cmd/transfer/main.go:142.5,146.19 3 36 github.com/calypr/git-drs/cmd/transfer/main.go:146.19,150.14 4 0 -github.com/calypr/git-drs/cmd/transfer/main.go:152.5,152.28 1 12 +github.com/calypr/git-drs/cmd/transfer/main.go:152.5,152.28 1 36 github.com/calypr/git-drs/cmd/transfer/main.go:152.28,156.14 4 0 -github.com/calypr/git-drs/cmd/transfer/main.go:160.5,161.19 2 12 +github.com/calypr/git-drs/cmd/transfer/main.go:160.5,161.19 2 36 github.com/calypr/git-drs/cmd/transfer/main.go:161.19,165.14 4 0 -github.com/calypr/git-drs/cmd/transfer/main.go:167.5,168.19 2 12 +github.com/calypr/git-drs/cmd/transfer/main.go:167.5,168.19 2 36 github.com/calypr/git-drs/cmd/transfer/main.go:168.19,172.14 4 0 -github.com/calypr/git-drs/cmd/transfer/main.go:176.5,178.70 2 12 -github.com/calypr/git-drs/cmd/transfer/main.go:180.10,180.61 1 108 -github.com/calypr/git-drs/cmd/transfer/main.go:180.61,186.74 3 12 +github.com/calypr/git-drs/cmd/transfer/main.go:176.5,178.70 2 36 +github.com/calypr/git-drs/cmd/transfer/main.go:180.10,180.61 1 324 +github.com/calypr/git-drs/cmd/transfer/main.go:180.61,186.74 3 36 github.com/calypr/git-drs/cmd/transfer/main.go:186.74,190.14 4 0 -github.com/calypr/git-drs/cmd/transfer/main.go:192.5,194.19 3 12 +github.com/calypr/git-drs/cmd/transfer/main.go:192.5,194.19 3 36 github.com/calypr/git-drs/cmd/transfer/main.go:194.19,198.14 4 0 -github.com/calypr/git-drs/cmd/transfer/main.go:201.5,202.63 2 12 -github.com/calypr/git-drs/cmd/transfer/main.go:204.10,204.64 1 96 -github.com/calypr/git-drs/cmd/transfer/main.go:204.64,206.5 1 96 -github.com/calypr/git-drs/cmd/transfer/main.go:209.3,209.39 1 96 +github.com/calypr/git-drs/cmd/transfer/main.go:201.5,202.63 2 36 +github.com/calypr/git-drs/cmd/transfer/main.go:204.10,204.64 1 288 +github.com/calypr/git-drs/cmd/transfer/main.go:204.64,206.5 1 288 +github.com/calypr/git-drs/cmd/transfer/main.go:209.3,209.39 1 288 github.com/calypr/git-drs/cmd/transfer/main.go:209.39,211.4 1 0 -github.com/calypr/git-drs/cmd/transfer/main.go:213.3,214.13 2 96 +github.com/calypr/git-drs/cmd/transfer/main.go:213.3,214.13 2 288 github.com/calypr/git-drs/cmd/transferref/main.go:32.54,42.17 6 0 github.com/calypr/git-drs/cmd/transferref/main.go:42.17,45.4 2 0 github.com/calypr/git-drs/cmd/transferref/main.go:47.3,50.22 2 0 @@ -1117,10 +1117,10 @@ github.com/calypr/git-drs/config/config.go:40.2,40.40 1 0 github.com/calypr/git-drs/config/config.go:40.40,41.32 1 0 github.com/calypr/git-drs/config/config.go:41.32,43.4 1 0 github.com/calypr/git-drs/config/config.go:46.2,46.102 1 0 -github.com/calypr/git-drs/config/config.go:68.94,70.9 2 108 +github.com/calypr/git-drs/config/config.go:68.94,70.9 2 324 github.com/calypr/git-drs/config/config.go:70.9,72.3 1 0 -github.com/calypr/git-drs/config/config.go:73.2,73.19 1 108 -github.com/calypr/git-drs/config/config.go:73.19,79.3 5 108 +github.com/calypr/git-drs/config/config.go:73.2,73.19 1 324 +github.com/calypr/git-drs/config/config.go:73.19,79.3 5 324 github.com/calypr/git-drs/config/config.go:79.8,79.27 1 0 github.com/calypr/git-drs/config/config.go:79.27,81.3 1 0 github.com/calypr/git-drs/config/config.go:82.2,82.94 1 0 @@ -1131,59 +1131,59 @@ github.com/calypr/git-drs/config/config.go:90.19,92.3 1 0 github.com/calypr/git-drs/config/config.go:92.8,92.27 1 0 github.com/calypr/git-drs/config/config.go:92.27,94.3 1 0 github.com/calypr/git-drs/config/config.go:95.2,95.12 1 0 -github.com/calypr/git-drs/config/config.go:99.52,100.27 1 108 +github.com/calypr/git-drs/config/config.go:99.52,100.27 1 324 github.com/calypr/git-drs/config/config.go:100.27,107.3 1 0 -github.com/calypr/git-drs/config/config.go:109.2,109.46 1 108 +github.com/calypr/git-drs/config/config.go:109.2,109.46 1 324 github.com/calypr/git-drs/config/config.go:109.46,116.3 1 0 -github.com/calypr/git-drs/config/config.go:118.2,118.29 1 108 +github.com/calypr/git-drs/config/config.go:118.2,118.29 1 324 github.com/calypr/git-drs/config/config.go:123.67,124.18 1 0 github.com/calypr/git-drs/config/config.go:124.18,126.3 1 0 github.com/calypr/git-drs/config/config.go:127.2,127.29 1 0 github.com/calypr/git-drs/config/config.go:131.44,133.30 2 0 github.com/calypr/git-drs/config/config.go:133.30,135.3 1 0 github.com/calypr/git-drs/config/config.go:136.2,136.14 1 0 -github.com/calypr/git-drs/config/config.go:139.38,141.16 2 138 +github.com/calypr/git-drs/config/config.go:139.38,141.16 2 414 github.com/calypr/git-drs/config/config.go:141.16,143.3 1 0 -github.com/calypr/git-drs/config/config.go:145.2,146.24 2 138 -github.com/calypr/git-drs/config/config.go:154.70,156.16 2 6 +github.com/calypr/git-drs/config/config.go:145.2,146.24 2 414 +github.com/calypr/git-drs/config/config.go:154.70,156.16 2 18 github.com/calypr/git-drs/config/config.go:156.16,158.3 1 0 -github.com/calypr/git-drs/config/config.go:161.2,161.55 1 6 +github.com/calypr/git-drs/config/config.go:161.2,161.55 1 18 github.com/calypr/git-drs/config/config.go:161.55,162.69 1 0 github.com/calypr/git-drs/config/config.go:162.69,164.4 1 0 -github.com/calypr/git-drs/config/config.go:168.2,169.16 2 6 +github.com/calypr/git-drs/config/config.go:168.2,169.16 2 18 github.com/calypr/git-drs/config/config.go:169.16,171.3 1 0 -github.com/calypr/git-drs/config/config.go:172.2,176.59 3 6 -github.com/calypr/git-drs/config/config.go:176.59,181.3 1 6 -github.com/calypr/git-drs/config/config.go:183.2,183.24 1 6 +github.com/calypr/git-drs/config/config.go:172.2,176.59 3 18 +github.com/calypr/git-drs/config/config.go:176.59,181.3 1 18 +github.com/calypr/git-drs/config/config.go:183.2,183.24 1 18 github.com/calypr/git-drs/config/config.go:183.24,185.3 1 0 -github.com/calypr/git-drs/config/config.go:188.2,188.54 1 6 -github.com/calypr/git-drs/config/config.go:188.54,190.3 1 6 -github.com/calypr/git-drs/config/config.go:192.2,197.58 4 6 +github.com/calypr/git-drs/config/config.go:188.2,188.54 1 18 +github.com/calypr/git-drs/config/config.go:188.54,190.3 1 18 +github.com/calypr/git-drs/config/config.go:192.2,197.58 4 18 github.com/calypr/git-drs/config/config.go:197.58,199.3 1 0 -github.com/calypr/git-drs/config/config.go:200.2,200.18 1 6 -github.com/calypr/git-drs/config/config.go:204.36,206.16 2 120 +github.com/calypr/git-drs/config/config.go:200.2,200.18 1 18 +github.com/calypr/git-drs/config/config.go:204.36,206.16 2 360 github.com/calypr/git-drs/config/config.go:206.16,208.3 1 0 -github.com/calypr/git-drs/config/config.go:210.2,210.55 1 120 +github.com/calypr/git-drs/config/config.go:210.2,210.55 1 360 github.com/calypr/git-drs/config/config.go:210.55,212.3 1 0 -github.com/calypr/git-drs/config/config.go:214.2,215.16 2 120 +github.com/calypr/git-drs/config/config.go:214.2,215.16 2 360 github.com/calypr/git-drs/config/config.go:215.16,217.3 1 0 -github.com/calypr/git-drs/config/config.go:218.2,221.16 3 120 +github.com/calypr/git-drs/config/config.go:218.2,221.16 3 360 github.com/calypr/git-drs/config/config.go:221.16,223.3 1 0 -github.com/calypr/git-drs/config/config.go:225.2,227.16 3 120 +github.com/calypr/git-drs/config/config.go:225.2,227.16 3 360 github.com/calypr/git-drs/config/config.go:227.16,229.3 1 0 -github.com/calypr/git-drs/config/config.go:233.2,233.55 1 120 +github.com/calypr/git-drs/config/config.go:233.2,233.55 1 360 github.com/calypr/git-drs/config/config.go:233.55,235.34 2 0 github.com/calypr/git-drs/config/config.go:235.34,237.4 1 0 github.com/calypr/git-drs/config/config.go:238.3,245.4 1 0 -github.com/calypr/git-drs/config/config.go:248.2,248.19 1 120 -github.com/calypr/git-drs/config/config.go:251.32,253.16 2 12 +github.com/calypr/git-drs/config/config.go:248.2,248.19 1 360 +github.com/calypr/git-drs/config/config.go:251.32,253.16 2 36 github.com/calypr/git-drs/config/config.go:253.16,255.3 1 0 -github.com/calypr/git-drs/config/config.go:256.2,256.55 1 12 -github.com/calypr/git-drs/config/config.go:256.55,257.69 1 6 +github.com/calypr/git-drs/config/config.go:256.2,256.55 1 36 +github.com/calypr/git-drs/config/config.go:256.55,257.69 1 18 github.com/calypr/git-drs/config/config.go:257.69,259.4 1 0 -github.com/calypr/git-drs/config/config.go:263.2,264.16 2 12 +github.com/calypr/git-drs/config/config.go:263.2,264.16 2 36 github.com/calypr/git-drs/config/config.go:264.16,266.3 1 0 -github.com/calypr/git-drs/config/config.go:267.2,269.12 2 12 +github.com/calypr/git-drs/config/config.go:267.2,269.12 2 36 github.com/calypr/git-drs/config/config.go:272.50,274.16 2 0 github.com/calypr/git-drs/config/config.go:274.16,276.3 1 0 github.com/calypr/git-drs/config/config.go:277.2,278.16 2 0 @@ -1251,26 +1251,26 @@ github.com/calypr/git-drs/drs/util.go:51.2,52.43 2 0 github.com/calypr/git-drs/drs/hash/hash.go:33.39,34.12 1 0 github.com/calypr/git-drs/drs/hash/hash.go:36.62,37.14 1 0 github.com/calypr/git-drs/drs/hash/hash.go:38.10,39.15 1 0 -github.com/calypr/git-drs/drs/hash/hash.go:44.40,46.2 1 24 -github.com/calypr/git-drs/drs/hash/hash.go:71.53,72.28 1 66 +github.com/calypr/git-drs/drs/hash/hash.go:44.40,46.2 1 72 +github.com/calypr/git-drs/drs/hash/hash.go:71.53,72.28 1 198 github.com/calypr/git-drs/drs/hash/hash.go:72.28,75.3 2 0 -github.com/calypr/git-drs/drs/hash/hash.go:77.2,78.58 2 66 -github.com/calypr/git-drs/drs/hash/hash.go:78.58,81.3 2 66 +github.com/calypr/git-drs/drs/hash/hash.go:77.2,78.58 2 198 +github.com/calypr/git-drs/drs/hash/hash.go:78.58,81.3 2 198 github.com/calypr/git-drs/drs/hash/hash.go:83.2,84.63 2 0 github.com/calypr/git-drs/drs/hash/hash.go:84.63,87.3 2 0 github.com/calypr/git-drs/drs/hash/hash.go:89.2,89.69 1 0 -github.com/calypr/git-drs/drs/hash/hash.go:92.73,95.38 2 66 -github.com/calypr/git-drs/drs/hash/hash.go:95.38,96.31 1 66 +github.com/calypr/git-drs/drs/hash/hash.go:92.73,95.38 2 198 +github.com/calypr/git-drs/drs/hash/hash.go:95.38,96.31 1 198 github.com/calypr/git-drs/drs/hash/hash.go:96.31,97.12 1 0 -github.com/calypr/git-drs/drs/hash/hash.go:100.3,100.14 1 66 +github.com/calypr/git-drs/drs/hash/hash.go:100.3,100.14 1 198 github.com/calypr/git-drs/drs/hash/hash.go:101.32,102.24 1 0 github.com/calypr/git-drs/drs/hash/hash.go:103.33,104.24 1 0 -github.com/calypr/git-drs/drs/hash/hash.go:105.35,106.27 1 66 +github.com/calypr/git-drs/drs/hash/hash.go:105.35,106.27 1 198 github.com/calypr/git-drs/drs/hash/hash.go:107.35,108.27 1 0 github.com/calypr/git-drs/drs/hash/hash.go:109.35,110.24 1 0 github.com/calypr/git-drs/drs/hash/hash.go:111.33,112.25 1 0 github.com/calypr/git-drs/drs/hash/hash.go:113.11,113.11 0 0 -github.com/calypr/git-drs/drs/hash/hash.go:117.2,117.17 1 66 +github.com/calypr/git-drs/drs/hash/hash.go:117.2,117.17 1 198 github.com/calypr/git-drs/drs/hash/hash.go:121.62,123.22 2 0 github.com/calypr/git-drs/drs/hash/hash.go:123.22,125.3 1 0 github.com/calypr/git-drs/drs/hash/hash.go:126.2,126.22 1 0 @@ -1288,30 +1288,30 @@ github.com/calypr/git-drs/drs/hash/hash.go:144.68,146.30 2 0 github.com/calypr/git-drs/drs/hash/hash.go:146.30,150.3 1 0 github.com/calypr/git-drs/drs/hash/hash.go:151.2,151.15 1 0 github.com/calypr/git-drs/drs/hash/hash.go:156.64,162.2 2 0 -github.com/calypr/git-drs/drslog/logger.go:21.72,24.20 2 138 -github.com/calypr/git-drs/drslog/logger.go:24.20,26.63 1 138 +github.com/calypr/git-drs/drslog/logger.go:21.72,24.20 2 414 +github.com/calypr/git-drs/drslog/logger.go:24.20,26.63 1 414 github.com/calypr/git-drs/drslog/logger.go:26.63,28.4 1 0 -github.com/calypr/git-drs/drslog/logger.go:30.3,30.62 1 138 -github.com/calypr/git-drs/drslog/logger.go:33.2,34.16 2 138 +github.com/calypr/git-drs/drslog/logger.go:30.3,30.62 1 414 +github.com/calypr/git-drs/drslog/logger.go:33.2,34.16 2 414 github.com/calypr/git-drs/drslog/logger.go:34.16,36.3 1 0 -github.com/calypr/git-drs/drslog/logger.go:37.2,39.17 2 138 -github.com/calypr/git-drs/drslog/logger.go:39.17,41.3 1 126 -github.com/calypr/git-drs/drslog/logger.go:43.2,55.26 8 138 -github.com/calypr/git-drs/drslog/logger.go:58.30,59.29 1 114 -github.com/calypr/git-drs/drslog/logger.go:59.29,60.26 1 114 +github.com/calypr/git-drs/drslog/logger.go:37.2,39.17 2 414 +github.com/calypr/git-drs/drslog/logger.go:39.17,41.3 1 378 +github.com/calypr/git-drs/drslog/logger.go:43.2,55.26 8 414 +github.com/calypr/git-drs/drslog/logger.go:58.30,59.29 1 342 +github.com/calypr/git-drs/drslog/logger.go:59.29,60.26 1 342 github.com/calypr/git-drs/drslog/logger.go:60.26,62.4 1 0 -github.com/calypr/git-drs/drslog/logger.go:64.2,64.21 1 114 +github.com/calypr/git-drs/drslog/logger.go:64.2,64.21 1 342 github.com/calypr/git-drs/drslog/logger.go:68.20,71.26 3 0 github.com/calypr/git-drs/drslog/logger.go:71.26,76.3 3 0 github.com/calypr/git-drs/drslog/logger.go:77.2,77.12 1 0 github.com/calypr/git-drs/drslog/logger.go:81.34,83.2 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:35.116,36.41 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:35.116,36.41 1 36 github.com/calypr/git-drs/drsmap/drs_map.go:36.41,38.3 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:41.2,53.16 10 12 +github.com/calypr/git-drs/drsmap/drs_map.go:41.2,53.16 10 36 github.com/calypr/git-drs/drsmap/drs_map.go:53.16,55.16 2 0 github.com/calypr/git-drs/drsmap/drs_map.go:55.16,57.4 1 0 github.com/calypr/git-drs/drsmap/drs_map.go:58.3,58.67 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:60.2,60.17 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:60.2,60.17 1 36 github.com/calypr/git-drs/drsmap/drs_map.go:79.82,82.16 2 0 github.com/calypr/git-drs/drsmap/drs_map.go:82.16,84.3 1 0 github.com/calypr/git-drs/drsmap/drs_map.go:87.2,88.33 2 0 @@ -1352,43 +1352,43 @@ github.com/calypr/git-drs/drsmap/drs_map.go:172.3,172.36 1 0 github.com/calypr/git-drs/drsmap/drs_map.go:172.36,176.18 3 0 github.com/calypr/git-drs/drsmap/drs_map.go:176.18,178.5 1 0 github.com/calypr/git-drs/drsmap/drs_map.go:181.2,182.12 2 0 -github.com/calypr/git-drs/drsmap/drs_map.go:185.137,191.16 3 12 +github.com/calypr/git-drs/drsmap/drs_map.go:185.137,191.16 3 36 github.com/calypr/git-drs/drsmap/drs_map.go:191.16,193.3 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:196.2,197.21 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:196.2,197.21 2 36 github.com/calypr/git-drs/drsmap/drs_map.go:197.21,199.3 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:203.2,203.32 1 12 -github.com/calypr/git-drs/drsmap/drs_map.go:203.32,206.17 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:203.2,203.32 1 36 +github.com/calypr/git-drs/drsmap/drs_map.go:203.32,206.17 2 36 github.com/calypr/git-drs/drsmap/drs_map.go:206.17,208.4 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:209.3,209.48 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:209.3,209.48 1 36 github.com/calypr/git-drs/drsmap/drs_map.go:209.48,211.12 2 0 -github.com/calypr/git-drs/drsmap/drs_map.go:217.3,222.17 3 12 +github.com/calypr/git-drs/drsmap/drs_map.go:217.3,222.17 3 36 github.com/calypr/git-drs/drsmap/drs_map.go:222.17,224.4 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:225.3,225.50 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:225.3,225.50 1 36 github.com/calypr/git-drs/drsmap/drs_map.go:225.50,227.4 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:229.3,230.17 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:229.3,230.17 2 36 github.com/calypr/git-drs/drsmap/drs_map.go:230.17,232.4 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:235.3,236.17 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:235.3,236.17 2 36 github.com/calypr/git-drs/drsmap/drs_map.go:236.17,238.4 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:239.3,239.101 1 12 -github.com/calypr/git-drs/drsmap/drs_map.go:242.2,242.12 1 12 -github.com/calypr/git-drs/drsmap/drs_map.go:245.78,248.16 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:239.3,239.101 1 36 +github.com/calypr/git-drs/drsmap/drs_map.go:242.2,242.12 1 36 +github.com/calypr/git-drs/drsmap/drs_map.go:245.78,248.16 2 36 github.com/calypr/git-drs/drsmap/drs_map.go:248.16,250.3 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:251.2,251.68 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:251.2,251.68 1 36 github.com/calypr/git-drs/drsmap/drs_map.go:251.68,253.3 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:256.2,257.16 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:256.2,257.16 2 36 github.com/calypr/git-drs/drsmap/drs_map.go:257.16,259.3 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:260.2,260.12 1 12 -github.com/calypr/git-drs/drsmap/drs_map.go:263.52,267.2 2 12 -github.com/calypr/git-drs/drsmap/drs_map.go:270.57,273.16 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:260.2,260.12 1 36 +github.com/calypr/git-drs/drsmap/drs_map.go:263.52,267.2 2 36 +github.com/calypr/git-drs/drsmap/drs_map.go:270.57,273.16 2 36 github.com/calypr/git-drs/drsmap/drs_map.go:273.16,275.3 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:277.2,278.16 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:277.2,278.16 2 36 github.com/calypr/git-drs/drsmap/drs_map.go:278.16,280.3 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:282.2,284.16 3 12 +github.com/calypr/git-drs/drsmap/drs_map.go:282.2,284.16 3 36 github.com/calypr/git-drs/drsmap/drs_map.go:284.16,286.3 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:288.2,288.24 1 12 -github.com/calypr/git-drs/drsmap/drs_map.go:291.65,293.20 1 54 +github.com/calypr/git-drs/drsmap/drs_map.go:288.2,288.24 1 36 +github.com/calypr/git-drs/drsmap/drs_map.go:291.65,293.20 1 162 github.com/calypr/git-drs/drsmap/drs_map.go:293.20,295.3 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:297.2,297.61 1 54 +github.com/calypr/git-drs/drsmap/drs_map.go:297.2,297.61 1 162 github.com/calypr/git-drs/drsmap/drs_map.go:306.66,310.16 3 0 github.com/calypr/git-drs/drsmap/drs_map.go:310.16,313.3 1 0 github.com/calypr/git-drs/drsmap/drs_map.go:316.2,316.46 1 0 @@ -1404,62 +1404,62 @@ github.com/calypr/git-drs/drsmap/drs_map.go:356.2,357.25 2 0 github.com/calypr/git-drs/drsmap/drs_map.go:360.56,364.16 3 0 github.com/calypr/git-drs/drsmap/drs_map.go:364.16,366.3 1 0 github.com/calypr/git-drs/drsmap/drs_map.go:368.2,370.22 3 0 -github.com/calypr/git-drs/drsmap/drs_map.go:373.133,374.19 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:373.133,374.19 1 36 github.com/calypr/git-drs/drsmap/drs_map.go:374.19,376.3 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:377.2,378.16 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:377.2,378.16 2 36 github.com/calypr/git-drs/drsmap/drs_map.go:378.16,380.3 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:383.2,389.25 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:383.2,389.25 2 36 github.com/calypr/git-drs/drsmap/drs_map.go:389.25,391.3 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:392.2,392.29 1 12 -github.com/calypr/git-drs/drsmap/drs_map.go:392.29,394.3 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:392.2,392.29 1 36 +github.com/calypr/git-drs/drsmap/drs_map.go:392.29,394.3 1 36 github.com/calypr/git-drs/drsmap/drs_map.go:394.8,396.3 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:398.2,400.27 3 12 -github.com/calypr/git-drs/drsmap/drs_map.go:400.27,406.17 3 12 +github.com/calypr/git-drs/drsmap/drs_map.go:398.2,400.27 3 36 +github.com/calypr/git-drs/drsmap/drs_map.go:400.27,406.17 3 36 github.com/calypr/git-drs/drsmap/drs_map.go:406.17,408.4 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:410.3,410.81 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:410.3,410.81 1 36 github.com/calypr/git-drs/drsmap/drs_map.go:410.81,412.4 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:415.2,415.24 1 12 -github.com/calypr/git-drs/drsmap/drs_map.go:418.47,419.24 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:415.2,415.24 1 36 +github.com/calypr/git-drs/drsmap/drs_map.go:418.47,419.24 1 36 github.com/calypr/git-drs/drsmap/drs_map.go:419.24,421.3 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:422.2,424.34 3 12 -github.com/calypr/git-drs/drsmap/drs_map.go:424.34,426.19 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:422.2,424.34 3 36 +github.com/calypr/git-drs/drsmap/drs_map.go:424.34,426.19 2 36 github.com/calypr/git-drs/drsmap/drs_map.go:426.19,427.12 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:429.3,430.62 2 12 -github.com/calypr/git-drs/drsmap/drs_map.go:430.62,432.4 1 12 -github.com/calypr/git-drs/drsmap/drs_map.go:433.3,433.29 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:429.3,430.62 2 36 +github.com/calypr/git-drs/drsmap/drs_map.go:430.62,432.4 1 36 +github.com/calypr/git-drs/drsmap/drs_map.go:433.3,433.29 1 36 github.com/calypr/git-drs/drsmap/drs_map.go:433.29,434.12 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:436.3,437.27 2 12 -github.com/calypr/git-drs/drsmap/drs_map.go:439.2,439.20 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:436.3,437.27 2 36 +github.com/calypr/git-drs/drsmap/drs_map.go:439.2,439.20 1 36 github.com/calypr/git-drs/drsmap/drs_map.go:439.20,441.3 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:442.2,442.13 1 12 -github.com/calypr/git-drs/drsmap/drs_map.go:445.110,447.34 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:442.2,442.13 1 36 +github.com/calypr/git-drs/drsmap/drs_map.go:445.110,447.34 1 36 github.com/calypr/git-drs/drsmap/drs_map.go:447.34,450.3 2 0 -github.com/calypr/git-drs/drsmap/drs_map.go:453.2,455.75 2 12 -github.com/calypr/git-drs/drsmap/drs_map.go:455.75,457.17 2 18 +github.com/calypr/git-drs/drsmap/drs_map.go:453.2,455.75 2 36 +github.com/calypr/git-drs/drsmap/drs_map.go:455.75,457.17 2 54 github.com/calypr/git-drs/drsmap/drs_map.go:457.17,458.12 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:460.3,461.21 2 18 +github.com/calypr/git-drs/drsmap/drs_map.go:460.3,461.21 2 54 github.com/calypr/git-drs/drsmap/drs_map.go:461.21,462.12 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:464.3,468.33 3 18 +github.com/calypr/git-drs/drsmap/drs_map.go:464.3,468.33 3 54 github.com/calypr/git-drs/drsmap/drs_map.go:468.33,470.12 2 0 -github.com/calypr/git-drs/drsmap/drs_map.go:474.3,474.124 1 18 -github.com/calypr/git-drs/drsmap/drs_map.go:474.124,476.12 2 6 -github.com/calypr/git-drs/drsmap/drs_map.go:480.3,480.86 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:474.3,474.124 1 54 +github.com/calypr/git-drs/drsmap/drs_map.go:474.124,476.12 2 18 +github.com/calypr/git-drs/drsmap/drs_map.go:480.3,480.86 1 36 github.com/calypr/git-drs/drsmap/drs_map.go:480.86,482.4 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:483.3,485.45 3 12 -github.com/calypr/git-drs/drsmap/drs_map.go:485.45,487.4 1 12 -github.com/calypr/git-drs/drsmap/drs_map.go:488.3,488.48 1 12 -github.com/calypr/git-drs/drsmap/drs_map.go:488.48,490.4 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:483.3,485.45 3 36 +github.com/calypr/git-drs/drsmap/drs_map.go:485.45,487.4 1 36 +github.com/calypr/git-drs/drsmap/drs_map.go:488.3,488.48 1 36 +github.com/calypr/git-drs/drsmap/drs_map.go:488.48,490.4 1 36 github.com/calypr/git-drs/drsmap/drs_map.go:490.9,492.101 2 0 github.com/calypr/git-drs/drsmap/drs_map.go:492.101,494.5 1 0 github.com/calypr/git-drs/drsmap/drs_map.go:495.4,495.12 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:500.3,500.30 1 12 -github.com/calypr/git-drs/drsmap/drs_map.go:500.30,501.61 1 6 -github.com/calypr/git-drs/drsmap/drs_map.go:501.61,503.112 2 6 +github.com/calypr/git-drs/drsmap/drs_map.go:500.3,500.30 1 36 +github.com/calypr/git-drs/drsmap/drs_map.go:500.30,501.61 1 18 +github.com/calypr/git-drs/drsmap/drs_map.go:501.61,503.112 2 18 github.com/calypr/git-drs/drsmap/drs_map.go:503.112,505.127 2 0 github.com/calypr/git-drs/drsmap/drs_map.go:505.127,507.7 1 0 github.com/calypr/git-drs/drsmap/drs_map.go:508.6,508.14 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:513.3,519.4 1 12 -github.com/calypr/git-drs/drsmap/drs_map.go:523.2,523.12 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:513.3,519.4 1 36 +github.com/calypr/git-drs/drsmap/drs_map.go:523.2,523.12 1 36 github.com/calypr/git-drs/drsmap/drs_map.go:529.63,531.66 2 0 github.com/calypr/git-drs/drsmap/drs_map.go:531.66,533.3 1 0 github.com/calypr/git-drs/drsmap/drs_map.go:534.2,538.25 3 0 @@ -1468,16 +1468,16 @@ github.com/calypr/git-drs/drsmap/drs_map.go:539.15,541.9 2 0 github.com/calypr/git-drs/drsmap/drs_map.go:544.2,544.20 1 0 github.com/calypr/git-drs/drsmap/drs_map.go:544.20,546.3 1 0 github.com/calypr/git-drs/drsmap/drs_map.go:547.2,549.54 3 0 -github.com/calypr/git-drs/drsmap/drs_map.go:554.92,555.23 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:554.92,555.23 1 36 github.com/calypr/git-drs/drsmap/drs_map.go:555.23,557.3 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:560.2,561.16 2 12 +github.com/calypr/git-drs/drsmap/drs_map.go:560.2,561.16 2 36 github.com/calypr/git-drs/drsmap/drs_map.go:561.16,563.3 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:567.2,567.33 1 12 -github.com/calypr/git-drs/drsmap/drs_map.go:567.33,568.47 1 48 -github.com/calypr/git-drs/drsmap/drs_map.go:568.47,570.36 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:567.2,567.33 1 36 +github.com/calypr/git-drs/drsmap/drs_map.go:567.33,568.47 1 144 +github.com/calypr/git-drs/drsmap/drs_map.go:568.47,570.36 1 36 github.com/calypr/git-drs/drsmap/drs_map.go:570.36,572.5 1 0 -github.com/calypr/git-drs/drsmap/drs_map.go:573.4,573.52 1 12 -github.com/calypr/git-drs/drsmap/drs_map.go:573.52,575.5 1 12 +github.com/calypr/git-drs/drsmap/drs_map.go:573.4,573.52 1 36 +github.com/calypr/git-drs/drsmap/drs_map.go:573.52,575.5 1 36 github.com/calypr/git-drs/drsmap/drs_map.go:579.2,579.17 1 0 github.com/calypr/git-drs/drsmap/lfs_utils.go:11.64,13.22 2 0 github.com/calypr/git-drs/drsmap/lfs_utils.go:13.22,15.3 1 0 @@ -1491,20 +1491,20 @@ github.com/calypr/git-drs/drsmap/lfs_utils.go:36.16,38.3 1 0 github.com/calypr/git-drs/drsmap/lfs_utils.go:40.2,40.12 1 0 github.com/calypr/git-drs/lfs/messages.go:82.85,91.2 2 0 github.com/calypr/git-drs/lfs/messages.go:93.93,104.2 2 0 -github.com/calypr/git-drs/lfs/messages.go:106.84,114.2 2 24 -github.com/calypr/git-drs/s3_utils/download.go:12.64,15.16 2 12 +github.com/calypr/git-drs/lfs/messages.go:106.84,114.2 2 72 +github.com/calypr/git-drs/s3_utils/download.go:12.64,15.16 2 36 github.com/calypr/git-drs/s3_utils/download.go:15.16,17.3 1 0 -github.com/calypr/git-drs/s3_utils/download.go:18.2,21.46 2 12 +github.com/calypr/git-drs/s3_utils/download.go:18.2,21.46 2 36 github.com/calypr/git-drs/s3_utils/download.go:21.46,23.17 2 0 github.com/calypr/git-drs/s3_utils/download.go:23.17,25.4 1 0 github.com/calypr/git-drs/s3_utils/download.go:26.3,26.119 1 0 -github.com/calypr/git-drs/s3_utils/download.go:30.2,31.16 2 12 +github.com/calypr/git-drs/s3_utils/download.go:30.2,31.16 2 36 github.com/calypr/git-drs/s3_utils/download.go:31.16,33.3 1 0 -github.com/calypr/git-drs/s3_utils/download.go:36.2,37.16 2 12 +github.com/calypr/git-drs/s3_utils/download.go:36.2,37.16 2 36 github.com/calypr/git-drs/s3_utils/download.go:37.16,39.3 1 0 -github.com/calypr/git-drs/s3_utils/download.go:40.2,44.16 3 12 +github.com/calypr/git-drs/s3_utils/download.go:40.2,44.16 3 36 github.com/calypr/git-drs/s3_utils/download.go:44.16,46.3 1 0 -github.com/calypr/git-drs/s3_utils/download.go:48.2,48.12 1 12 +github.com/calypr/git-drs/s3_utils/download.go:48.2,48.12 1 36 github.com/calypr/git-drs/s3_utils/s3.go:49.96,53.2 1 0 github.com/calypr/git-drs/s3_utils/s3.go:66.51,67.33 1 0 github.com/calypr/git-drs/s3_utils/s3.go:67.33,69.3 1 0 @@ -1519,9 +1519,9 @@ github.com/calypr/git-drs/s3_utils/validate.go:16.23,18.3 1 0 github.com/calypr/git-drs/s3_utils/validate.go:20.2,20.52 1 0 github.com/calypr/git-drs/s3_utils/validate.go:20.52,22.3 1 0 github.com/calypr/git-drs/s3_utils/validate.go:24.2,24.12 1 0 -github.com/calypr/git-drs/utils/common.go:12.56,13.37 1 36 +github.com/calypr/git-drs/utils/common.go:12.56,13.37 1 108 github.com/calypr/git-drs/utils/common.go:13.37,15.3 1 0 -github.com/calypr/git-drs/utils/common.go:16.2,17.77 2 36 +github.com/calypr/git-drs/utils/common.go:16.2,17.77 2 108 github.com/calypr/git-drs/utils/common.go:22.59,27.32 2 0 github.com/calypr/git-drs/utils/common.go:27.32,29.3 1 0 github.com/calypr/git-drs/utils/common.go:31.2,31.29 1 0 @@ -1593,22 +1593,22 @@ github.com/calypr/git-drs/utils/lfs-track.go:175.31,180.35 2 0 github.com/calypr/git-drs/utils/lfs-track.go:180.35,184.5 2 0 github.com/calypr/git-drs/utils/lfs-track.go:184.10,189.5 3 0 github.com/calypr/git-drs/utils/lfs-track.go:194.2,194.13 1 0 -github.com/calypr/git-drs/utils/util.go:14.36,16.16 2 150 +github.com/calypr/git-drs/utils/util.go:14.36,16.16 2 450 github.com/calypr/git-drs/utils/util.go:16.16,18.3 1 0 -github.com/calypr/git-drs/utils/util.go:19.2,19.44 1 150 -github.com/calypr/git-drs/utils/util.go:22.47,24.16 2 150 +github.com/calypr/git-drs/utils/util.go:19.2,19.44 1 450 +github.com/calypr/git-drs/utils/util.go:22.47,24.16 2 450 github.com/calypr/git-drs/utils/util.go:24.16,26.3 1 0 -github.com/calypr/git-drs/utils/util.go:27.2,29.28 3 150 +github.com/calypr/git-drs/utils/util.go:27.2,29.28 3 450 github.com/calypr/git-drs/utils/util.go:32.36,34.16 2 0 github.com/calypr/git-drs/utils/util.go:34.16,36.3 1 0 github.com/calypr/git-drs/utils/util.go:37.2,37.42 1 0 -github.com/calypr/git-drs/utils/util.go:42.46,44.16 2 12 +github.com/calypr/git-drs/utils/util.go:42.46,44.16 2 36 github.com/calypr/git-drs/utils/util.go:44.16,46.3 1 0 -github.com/calypr/git-drs/utils/util.go:47.2,50.16 3 12 +github.com/calypr/git-drs/utils/util.go:47.2,50.16 3 36 github.com/calypr/git-drs/utils/util.go:50.16,52.3 1 0 -github.com/calypr/git-drs/utils/util.go:53.2,55.86 2 12 -github.com/calypr/git-drs/utils/util.go:55.86,57.3 1 6 -github.com/calypr/git-drs/utils/util.go:59.2,59.78 1 6 +github.com/calypr/git-drs/utils/util.go:53.2,55.86 2 36 +github.com/calypr/git-drs/utils/util.go:55.86,57.3 1 18 +github.com/calypr/git-drs/utils/util.go:59.2,59.78 1 18 github.com/calypr/git-drs/utils/util.go:62.62,65.16 3 0 github.com/calypr/git-drs/utils/util.go:65.16,67.3 1 0 github.com/calypr/git-drs/utils/util.go:68.2,69.9 2 0 @@ -1618,13 +1618,13 @@ github.com/calypr/git-drs/utils/util.go:73.9,75.3 1 0 github.com/calypr/git-drs/utils/util.go:76.2,77.9 2 0 github.com/calypr/git-drs/utils/util.go:77.9,79.3 1 0 github.com/calypr/git-drs/utils/util.go:80.2,80.18 1 0 -github.com/calypr/git-drs/utils/util.go:83.68,86.16 3 6 +github.com/calypr/git-drs/utils/util.go:83.68,86.16 3 18 github.com/calypr/git-drs/utils/util.go:86.16,88.3 1 0 -github.com/calypr/git-drs/utils/util.go:89.2,90.9 2 6 +github.com/calypr/git-drs/utils/util.go:89.2,90.9 2 18 github.com/calypr/git-drs/utils/util.go:90.9,92.3 1 0 -github.com/calypr/git-drs/utils/util.go:93.2,94.16 2 6 +github.com/calypr/git-drs/utils/util.go:93.2,94.16 2 18 github.com/calypr/git-drs/utils/util.go:94.16,96.3 1 0 -github.com/calypr/git-drs/utils/util.go:97.2,97.70 1 6 +github.com/calypr/git-drs/utils/util.go:97.2,97.70 1 18 github.com/calypr/git-drs/utils/util.go:100.55,102.41 2 0 github.com/calypr/git-drs/utils/util.go:102.41,104.3 1 0 github.com/calypr/git-drs/utils/util.go:105.2,107.54 3 0 diff --git a/tests/coverage-test.sh b/tests/coverage-test.sh index 79011fbb..0fe80b14 100755 --- a/tests/coverage-test.sh +++ b/tests/coverage-test.sh @@ -183,8 +183,12 @@ go tool covdata textfmt -i="${INTEGRATION_COV_DIR}" -o "${INTEGRATION_PROFILE}" echo "Integration coverage profile saved to ${INTEGRATION_PROFILE}" # unit tests +rm git-drs || true which git-drs -export GOCOVERDIR=coverage/unit/raw -go test -cover -covermode=atomic -coverpkg=./... ./... || { echo "error: unit tests failed" >&2; exit 1; } +#export GOCOVERDIR=coverage/unit/raw +#go test -cover -covermode=atomic -coverpkg=./... ./... || { echo "error: unit tests failed" >&2; exit 1; } + +mkdir -p coverage/unit/raw +go test -v -race -coverprofile=coverage/unit/raw/coverage.out -covermode=atomic -coverpkg=./... $(go list ./... | grep -vE 'tests/integration/calypr|client/indexd/tests') || { echo "unit tests failed" >&2; exit 1; } From e940e98fc67fc478d9e1ea53a1039dba7239db4d Mon Sep 17 00:00:00 2001 From: Brian Walsh Date: Wed, 21 Jan 2026 20:25:10 -0800 Subject: [PATCH 40/40] fix/simple-oid-bucket-key #171 --- client/indexd/indexd_client.go | 2 +- client/indexd/indexd_client_test.go | 4 ++-- tests/scripts/utils/delete-s3-by-sha256.sh | 3 +-- tests/scripts/utils/list-s3-by-sha256.sh | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/client/indexd/indexd_client.go b/client/indexd/indexd_client.go index f087dcbe..dcf33931 100644 --- a/client/indexd/indexd_client.go +++ b/client/indexd/indexd_client.go @@ -1070,7 +1070,7 @@ func (cl *IndexDClient) BuildDrsObj(fileName string, checksum string, size int64 } //TODO: support other storage backends - fileURL := fmt.Sprintf("s3://%s", filepath.Join(bucket, drsId, checksum)) + fileURL := fmt.Sprintf("s3://%s", filepath.Join(bucket, checksum)) authzStr, err := utils.ProjectToResource(cl.GetProjectId()) if err != nil { diff --git a/client/indexd/indexd_client_test.go b/client/indexd/indexd_client_test.go index e5b2c044..536e9a92 100644 --- a/client/indexd/indexd_client_test.go +++ b/client/indexd/indexd_client_test.go @@ -314,7 +314,7 @@ func TestIndexdClient_BuildDrsObj(t *testing.T) { if obj.Id != "did-1" || obj.Checksums.SHA256 != "sha-256" { t.Fatalf("unexpected drs object: %+v", obj) } - if len(obj.AccessMethods) != 1 || !strings.Contains(obj.AccessMethods[0].AccessURL.URL, filepath.Join("bucket", "did-1", "sha-256")) { + if len(obj.AccessMethods) != 1 || !strings.Contains(obj.AccessMethods[0].AccessURL.URL, filepath.Join("bucket", "sha-256")) { t.Fatalf("unexpected access URL: %+v", obj.AccessMethods) } } @@ -490,7 +490,7 @@ func TestBuildDrsObj_Success(t *testing.T) { if len(obj.AccessMethods) != 1 { t.Fatalf("expected 1 access method, got %d", len(obj.AccessMethods)) } - if !strings.Contains(obj.AccessMethods[0].AccessURL.URL, filepath.Join("bucket", "did-1", "sha-256")) { + if !strings.Contains(obj.AccessMethods[0].AccessURL.URL, filepath.Join("bucket", "sha-256")) { t.Fatalf("unexpected access URL: %s", obj.AccessMethods[0].AccessURL.URL) } if obj.AccessMethods[0].Type != "s3" { diff --git a/tests/scripts/utils/delete-s3-by-sha256.sh b/tests/scripts/utils/delete-s3-by-sha256.sh index a71bee4d..c1775436 100755 --- a/tests/scripts/utils/delete-s3-by-sha256.sh +++ b/tests/scripts/utils/delete-s3-by-sha256.sh @@ -74,7 +74,7 @@ while IFS=$' \t' read -r did hash file_name resource; do continue fi - object_key="${PREFIX}${did}/${hash}" + object_key="${PREFIX}${hash}" s3_url="s3://${BUCKET}/${object_key}" if [[ "${DEBUG:-}" == "1" ]]; then @@ -90,4 +90,3 @@ while IFS=$' \t' read -r did hash file_name resource; do done - diff --git a/tests/scripts/utils/list-s3-by-sha256.sh b/tests/scripts/utils/list-s3-by-sha256.sh index 8af6d4ae..3bec4535 100755 --- a/tests/scripts/utils/list-s3-by-sha256.sh +++ b/tests/scripts/utils/list-s3-by-sha256.sh @@ -77,7 +77,7 @@ while IFS=$' \t' read -r did hash file_name resource; do continue fi - object_key="${PREFIX}${did}/${hash}" + object_key="${PREFIX}${hash}" s3_url="s3://${BUCKET}/${object_key}" # capture mc ls output, append file_name on success; on failure print error plus file_name