@@ -13,6 +13,7 @@ import (
13
13
"sync"
14
14
15
15
"github.com/go-git/go-git/v5"
16
+ "github.com/go-git/go-git/v5/plumbing"
16
17
"github.com/google/wire"
17
18
"github.com/samber/lo"
18
19
"golang.org/x/xerrors"
@@ -56,7 +57,8 @@ type Artifact struct {
56
57
57
58
artifactOption artifact.Option
58
59
59
- commitHash string // only set when the git repository is clean
60
+ isClean bool // whether git repository is clean (for caching)
61
+ repoMetadata artifact.RepoMetadata // git repository metadata
60
62
}
61
63
62
64
func NewArtifact (rootPath string , c cache.ArtifactCache , w Walker , opt artifact.Option ) (artifact.Artifact , error ) {
@@ -86,10 +88,14 @@ func NewArtifact(rootPath string, c cache.ArtifactCache, w Walker, opt artifact.
86
88
art .logger .Debug ("Analyzing..." , log .String ("root" , art .rootPath ),
87
89
lo .Ternary (opt .Original != "" , log .String ("original" , opt .Original ), log .Nil ))
88
90
89
- // Check if the directory is a git repository and clean
90
- if hash , err := gitCommitHash (art .rootPath ); err == nil {
91
- art .logger .Debug ("Using the latest commit hash for calculating cache key" , log .String ("commit_hash" , hash ))
92
- art .commitHash = hash
91
+ // Check if the directory is a git repository and extract metadata
92
+ if art .isClean , art .repoMetadata , err = extractGitInfo (art .rootPath ); err == nil {
93
+ if art .isClean {
94
+ art .logger .Debug ("Using the latest commit hash for calculating cache key" ,
95
+ log .String ("commit_hash" , art .repoMetadata .Commit ))
96
+ } else {
97
+ art .logger .Debug ("Repository is dirty, random cache key will be used" )
98
+ }
93
99
} else if ! errors .Is (err , git .ErrRepositoryNotExists ) {
94
100
// Only log if the file path is a git repository
95
101
art .logger .Debug ("Random cache key will be used" , log .Err (err ))
@@ -98,36 +104,72 @@ func NewArtifact(rootPath string, c cache.ArtifactCache, w Walker, opt artifact.
98
104
return art , nil
99
105
}
100
106
101
- // gitCommitHash returns the latest commit hash if the git repository is clean, otherwise returns an error
102
- func gitCommitHash (dir string ) (string , error ) {
107
+ // extractGitInfo extracts git repository information including clean status and metadata
108
+ // Returns clean status (for caching), metadata, and error
109
+ func extractGitInfo (dir string ) (bool , artifact.RepoMetadata , error ) {
110
+ var metadata artifact.RepoMetadata
111
+
103
112
repo , err := git .PlainOpen (dir )
104
113
if err != nil {
105
- return "" , xerrors .Errorf ("failed to open git repository: %w" , err )
114
+ return false , metadata , xerrors .Errorf ("failed to open git repository: %w" , err )
106
115
}
107
116
108
- // Get the working tree
109
- worktree , err := repo .Worktree ()
117
+ // Get HEAD commit
118
+ head , err := repo .Head ()
110
119
if err != nil {
111
- return "" , xerrors .Errorf ("failed to get worktree : %w" , err )
120
+ return false , metadata , xerrors .Errorf ("failed to get HEAD : %w" , err )
112
121
}
113
122
114
- // Get the current status
115
- status , err := worktree .Status ()
123
+ commit , err := repo .CommitObject (head .Hash ())
116
124
if err != nil {
117
- return "" , xerrors .Errorf ("failed to get status : %w" , err )
125
+ return false , metadata , xerrors .Errorf ("failed to get commit object : %w" , err )
118
126
}
119
127
120
- if ! status .IsClean () {
121
- return "" , xerrors .New ("repository is dirty" )
128
+ // Extract basic commit metadata
129
+ metadata .Commit = head .Hash ().String ()
130
+ metadata .CommitMsg = strings .TrimSpace (commit .Message )
131
+ metadata .Author = commit .Author .String ()
132
+ metadata .Committer = commit .Committer .String ()
133
+
134
+ // Get branch name
135
+ if head .Name ().IsBranch () {
136
+ metadata .Branch = head .Name ().Short ()
122
137
}
123
138
124
- // Get the HEAD commit hash
125
- head , err := repo .Head ()
139
+ // Get all tag names that point to HEAD
140
+ if tags , err := repo .Tags (); err == nil {
141
+ var headTags []string
142
+ _ = tags .ForEach (func (tag * plumbing.Reference ) error {
143
+ if tag .Hash () == head .Hash () {
144
+ headTags = append (headTags , tag .Name ().Short ())
145
+ }
146
+ return nil
147
+ })
148
+ metadata .Tags = headTags
149
+ }
150
+
151
+ // Get repository URL - prefer upstream, fallback to origin
152
+ remoteConfig , err := repo .Remote ("upstream" )
153
+ if err != nil {
154
+ remoteConfig , err = repo .Remote ("origin" )
155
+ }
156
+ if err == nil && len (remoteConfig .Config ().URLs ) > 0 {
157
+ metadata .RepoURL = remoteConfig .Config ().URLs [0 ]
158
+ }
159
+
160
+ // Check if repository is clean for caching purposes
161
+ worktree , err := repo .Worktree ()
162
+ if err != nil {
163
+ return false , metadata , xerrors .Errorf ("failed to get worktree: %w" , err )
164
+ }
165
+
166
+ status , err := worktree .Status ()
126
167
if err != nil {
127
- return "" , xerrors .Errorf ("failed to get HEAD : %w" , err )
168
+ return false , metadata , xerrors .Errorf ("failed to get status : %w" , err )
128
169
}
129
170
130
- return head .Hash ().String (), nil
171
+ // Return clean status and metadata
172
+ return status .IsClean (), metadata , nil
131
173
}
132
174
133
175
func (a Artifact ) Inspect (ctx context.Context ) (artifact.Reference , error ) {
@@ -138,7 +180,7 @@ func (a Artifact) Inspect(ctx context.Context) (artifact.Reference, error) {
138
180
}
139
181
140
182
// Check if the cache exists only when it's a clean git repository
141
- if a .commitHash != "" {
183
+ if a .isClean && a . repoMetadata . Commit != "" {
142
184
_ , missingBlobs , err := a .cache .MissingBlobs (cacheKey , []string {cacheKey })
143
185
if err != nil {
144
186
return artifact.Reference {}, xerrors .Errorf ("unable to get missing blob: %w" , err )
@@ -231,10 +273,11 @@ func (a Artifact) Inspect(ctx context.Context) (artifact.Reference, error) {
231
273
}
232
274
233
275
return artifact.Reference {
234
- Name : hostName ,
235
- Type : a .artifactOption .Type ,
236
- ID : cacheKey , // use a cache key as pseudo artifact ID
237
- BlobIDs : []string {cacheKey },
276
+ Name : hostName ,
277
+ Type : a .artifactOption .Type ,
278
+ ID : cacheKey , // use a cache key as pseudo artifact ID
279
+ BlobIDs : []string {cacheKey },
280
+ RepoMetadata : a .repoMetadata ,
238
281
}, nil
239
282
}
240
283
@@ -295,16 +338,16 @@ func (a Artifact) analyzeWithTraversal(ctx context.Context, root, relativePath s
295
338
296
339
func (a Artifact ) Clean (reference artifact.Reference ) error {
297
340
// Don't delete cache if it's a clean git repository
298
- if a .commitHash != "" {
341
+ if a .isClean && a . repoMetadata . Commit != "" {
299
342
return nil
300
343
}
301
344
return a .cache .DeleteBlobs (reference .BlobIDs )
302
345
}
303
346
304
347
func (a Artifact ) calcCacheKey () (string , error ) {
305
348
// If this is a clean git repository, use the commit hash as cache key
306
- if a .commitHash != "" {
307
- return cache .CalcKey (a .commitHash , artifactVersion , a .analyzer .AnalyzerVersions (), a .handlerManager .Versions (), a .artifactOption )
349
+ if a .isClean && a . repoMetadata . Commit != "" {
350
+ return cache .CalcKey (a .repoMetadata . Commit , artifactVersion , a .analyzer .AnalyzerVersions (), a .handlerManager .Versions (), a .artifactOption )
308
351
}
309
352
310
353
// For non-git repositories or dirty git repositories, use UUID as cache key
0 commit comments