Monorepo for Tangled

knotmirror: classify git error message

Signed-off-by: Seongmin Lee <git@boltless.me>

boltless.me 301e81a1 a81412ef

verified
+40 -3
+40 -3
knotmirror/git.go
··· 5 5 "errors" 6 6 "fmt" 7 7 "os/exec" 8 + "regexp" 9 + "strings" 8 10 9 11 "github.com/go-git/go-git/v5" 10 12 gitconfig "github.com/go-git/go-git/v5/config" ··· 21 23 var _ GitMirrorClient = new(CliGitMirrorClient) 22 24 23 25 func (c *CliGitMirrorClient) Clone(ctx context.Context, path, url string) error { 24 - cmd := exec.Command("git", "clone", "--mirror", url, path) 26 + cmd := exec.CommandContext(ctx, "git", "clone", "--mirror", url, path) 25 27 if out, err := cmd.CombinedOutput(); err != nil { 26 - return fmt.Errorf("cloning repo: %w\n%s", err, string(out)) 28 + if ctx.Err() != nil { 29 + return ctx.Err() 30 + } 31 + msg := string(out) 32 + if classification := classifyError(msg); classification != nil { 33 + return classification 34 + } 35 + return fmt.Errorf("cloning repo: %w\n%s", err, msg) 27 36 } 28 37 return nil 29 38 } 30 39 31 40 func (c *CliGitMirrorClient) Fetch(ctx context.Context, path, url string) error { 32 - cmd := exec.Command("git", "-C", path, "fetch", "--prune", url) 41 + cmd := exec.CommandContext(ctx, "git", "-C", path, "fetch", "--prune", url) 33 42 if out, err := cmd.CombinedOutput(); err != nil { 43 + if ctx.Err() != nil { 44 + return ctx.Err() 45 + } 34 46 return fmt.Errorf("fetching repo: %w\n%s", err, string(out)) 47 + } 48 + return nil 49 + } 50 + 51 + var ( 52 + ErrDNSFailure = errors.New("git: dns failure (could not resolve host)") 53 + ErrCertExpired = errors.New("git: certificate has expired") 54 + ErrRepoNotFound = errors.New("git: repository not found") 55 + ) 56 + 57 + var ( 58 + reDNS = regexp.MustCompile(`Could not resolve host:`) 59 + reCertExpired = regexp.MustCompile(`SSL certificate OpenSSL verify result: certificate has expired`) 60 + reRepoNotFound = regexp.MustCompile(`repository '.*' not found`) 61 + ) 62 + 63 + func classifyError(stderr string) error { 64 + msg := strings.TrimSpace(stderr) 65 + switch { 66 + case reDNS.MatchString(msg): 67 + return ErrDNSFailure 68 + case reCertExpired.MatchString(msg): 69 + return ErrCertExpired 70 + case reRepoNotFound.MatchString(msg): 71 + return ErrRepoNotFound 35 72 } 36 73 return nil 37 74 }