@@ -14,14 +14,6 @@ import (
1414	"github.com/rs/zerolog/log" 
1515)
1616
17- type  GitCloneError  struct  {
18- 	msg  string 
19- }
20- 
21- func  (e  * GitCloneError ) Error () string  {
22- 	return  e .msg 
23- }
24- 
2517type  GitClient  struct  {
2618	Command  GitCommand 
2719}
@@ -38,19 +30,100 @@ type GitCommand interface {
3830	ReadFile (path  string ) ([]byte , error )
3931}
4032
33+ type  GitError  interface  {
34+ 	error 
35+ 	Command () string 
36+ }
37+ 
38+ type  GitCommandError  struct  {
39+ 	CommandStr  string 
40+ 	Err         error 
41+ }
42+ 
43+ func  (e  * GitCommandError ) Error () string  {
44+ 	return  fmt .Sprintf ("error running command `%s`: %v" , e .CommandStr , e .Err )
45+ }
46+ 
47+ func  (e  * GitCommandError ) Unwrap () error  {
48+ 	return  e .Err 
49+ }
50+ 
51+ func  (e  * GitCommandError ) Command () string  {
52+ 	return  e .CommandStr 
53+ }
54+ 
55+ type  GitExitError  struct  {
56+ 	CommandStr  string 
57+ 	Stderr      string 
58+ 	ExitCode    int 
59+ 	Err         error 
60+ }
61+ 
62+ func  (e  * GitExitError ) Error () string  {
63+ 	return  fmt .Sprintf ("command `%s` failed with exit code %d: %v, stderr: %s" , e .CommandStr , e .ExitCode , e .Err , e .Stderr )
64+ }
65+ 
66+ func  (e  * GitExitError ) Unwrap () error  {
67+ 	return  e .Err 
68+ }
69+ 
70+ func  (e  * GitExitError ) Command () string  {
71+ 	return  e .CommandStr 
72+ }
73+ 
74+ type  GitNotFoundError  struct  {
75+ 	CommandStr  string 
76+ }
77+ 
78+ func  (e  * GitNotFoundError ) Error () string  {
79+ 	return  fmt .Sprintf ("git binary not found for command `%s`. Please ensure Git is installed and available in your PATH." , e .CommandStr )
80+ }
81+ 
82+ func  (e  * GitNotFoundError ) Command () string  {
83+ 	return  e .CommandStr 
84+ }
85+ 
4186type  ExecGitCommand  struct {}
4287
4388func  (g  * ExecGitCommand ) Run (ctx  context.Context , cmd  string , args  []string , dir  string ) ([]byte , error ) {
4489	command  :=  exec .CommandContext (ctx , cmd , args ... )
4590	command .Dir  =  dir 
46- 	stdout , err  :=  command .Output ()
91+ 	var  stdout , stderr  strings.Builder 
92+ 	command .Stdout  =  & stdout 
93+ 	command .Stderr  =  & stderr 
94+ 
95+ 	err  :=  command .Run ()
4796	if  err  !=  nil  {
48- 		if  exitErr , ok  :=  err .(* exec.ExitError ); ok  {
49- 			return  nil , fmt .Errorf ("command `%s` returned an error: %w stderr: %s" , command .String (), err , string (bytes .TrimSpace (exitErr .Stderr )))
97+ 		var  execErr  * exec.Error 
98+ 		if  errors .As (err , & execErr ) &&  errors .Is (execErr .Err , exec .ErrNotFound ) {
99+ 			return  nil , & GitNotFoundError {
100+ 				CommandStr : command .String (),
101+ 			}
102+ 		}
103+ 
104+ 		var  exitErr  * exec.ExitError 
105+ 		if  errors .As (err , & exitErr ) {
106+ 			exitCode  :=  exitErr .ExitCode ()
107+ 			stderrMsg  :=  strings .TrimSpace (stderr .String ())
108+ 
109+ 			if  stderrMsg  ==  ""  {
110+ 				stderrMsg  =  exitErr .Error ()
111+ 			}
112+ 
113+ 			return  nil , & GitExitError {
114+ 				CommandStr : command .String (),
115+ 				Stderr :     stderrMsg ,
116+ 				ExitCode :   exitCode ,
117+ 				Err :        exitErr ,
118+ 			}
119+ 		}
120+ 		return  nil , & GitCommandError {
121+ 			CommandStr : command .String (),
122+ 			Err :        err ,
50123		}
51- 		return  nil , fmt .Errorf ("error running command: %w" , err )
52124	}
53- 	return  stdout , nil 
125+ 
126+ 	return  []byte (stdout .String ()), nil 
54127}
55128
56129func  (g  * ExecGitCommand ) ReadFile (path  string ) ([]byte , error ) {
@@ -160,15 +233,42 @@ type LocalGitClient struct {
160233}
161234
162235func  (g  * LocalGitClient ) GetRemoteOriginURL (ctx  context.Context , repoPath  string ) (string , error ) {
163- 	return  g .GitClient .GetRemoteOriginURL (ctx , repoPath )
236+ 	remoteOriginURL , err  :=  g .GitClient .GetRemoteOriginURL (ctx , repoPath )
237+ 	if  err  !=  nil  {
238+ 		var  gitErr  GitError 
239+ 		if  errors .As (err , & gitErr ) {
240+ 			log .Debug ().Err (err ).Msg ("failed to get remote origin URL for local repo" )
241+ 			return  repoPath , nil 
242+ 		}
243+ 		return  "" , err 
244+ 	}
245+ 	return  remoteOriginURL , nil 
164246}
165247
166248func  (g  * LocalGitClient ) LastCommitDate (ctx  context.Context , clonePath  string ) (time.Time , error ) {
167- 	return  g .GitClient .LastCommitDate (ctx , clonePath )
249+ 	lastCommitDate , err  :=  g .GitClient .LastCommitDate (ctx , clonePath )
250+ 	if  err  !=  nil  {
251+ 		var  gitErr  GitError 
252+ 		if  errors .As (err , & gitErr ) {
253+ 			log .Debug ().Err (err ).Msg ("failed to get last commit date for local repo" )
254+ 			return  time .Now (), nil 
255+ 		}
256+ 		return  time.Time {}, err 
257+ 	}
258+ 	return  lastCommitDate , nil 
168259}
169260
170261func  (g  * LocalGitClient ) CommitSHA (clonePath  string ) (string , error ) {
171- 	return  g .GitClient .CommitSHA (clonePath )
262+ 	commitSHA , err  :=  g .GitClient .CommitSHA (clonePath )
263+ 	if  err  !=  nil  {
264+ 		var  gitErr  GitError 
265+ 		if  errors .As (err , & gitErr ) {
266+ 			log .Debug ().Err (err ).Msg ("failed to get commit SHA for local repo" )
267+ 			return  "" , nil 
268+ 		}
269+ 		return  "" , err 
270+ 	}
271+ 	return  commitSHA , nil 
172272}
173273
174274func  (g  * LocalGitClient ) Clone (ctx  context.Context , clonePath  string , url  string , token  string , ref  string ) error  {
@@ -182,6 +282,11 @@ func (g *LocalGitClient) GetRepoHeadBranchName(ctx context.Context, repoPath str
182282
183283	output , err  :=  g .GitClient .Command .Run (ctx , cmd , args , repoPath )
184284	if  err  !=  nil  {
285+ 		var  gitErr  GitError 
286+ 		if  errors .As (err , & gitErr ) {
287+ 			log .Debug ().Err (err ).Msg ("failed to get repo head branch name for local repo" )
288+ 			return  "local" , nil 
289+ 		}
185290		return  "" , err 
186291	}
187292
0 commit comments