@@ -221,54 +221,143 @@ func (g *FedoraSourcesProviderImpl) renameSpecIfNeeded(dir, upstreamName, compon
221221 return nil
222222}
223223
224- // checkoutTargetCommit determines the appropriate commit to use and checks it out.
225- // Priority order:
226- // 1. Explicit upstream commit hash - specified per-component via upstream-commit
227- // 2. Upstream distro snapshot - snapshot time from the provider's resolved distro
228- // 3. Default - use current HEAD (no checkout needed)
224+ // checkoutTargetCommit resolves the effective commit via [resolveEffectiveCommitHash]
225+ // and checks it out in the cloned repository.
229226func (g * FedoraSourcesProviderImpl ) checkoutTargetCommit (
230227 ctx context.Context ,
231228 upstreamCommit string ,
232229 repoDir string ,
233230) error {
234- // Case 1: Explicit upstream commit hash specified per-component
231+ commitHash , err := g .resolveEffectiveCommitHash (ctx , repoDir , upstreamCommit , slog .LevelInfo )
232+ if err != nil {
233+ return err
234+ }
235+
236+ if err := g .gitProvider .Checkout (ctx , repoDir , commitHash ); err != nil {
237+ return fmt .Errorf ("failed to checkout commit %#q:\n %w" , commitHash , err )
238+ }
239+
240+ return nil
241+ }
242+
243+ // ResolveSourceIdentity implements [SourceIdentityProvider] by resolving the upstream
244+ // commit hash for the component. All resolution priority logic is in
245+ // [resolveEffectiveCommitHash], called via [resolveCommit].
246+ func (g * FedoraSourcesProviderImpl ) ResolveSourceIdentity (
247+ ctx context.Context ,
248+ component components.Component ,
249+ ) (string , error ) {
250+ if component .GetName () == "" {
251+ return "" , errors .New ("component name cannot be empty" )
252+ }
253+
254+ upstreamName := component .GetConfig ().Spec .UpstreamName
255+ if upstreamName == "" {
256+ upstreamName = component .GetName ()
257+ }
258+
259+ gitRepoURL := strings .ReplaceAll (g .distroGitBaseURI , "$pkg" , upstreamName )
260+
261+ return g .resolveCommit (ctx , gitRepoURL , upstreamName , component .GetConfig ().Spec .UpstreamCommit )
262+ }
263+
264+ // resolveCommit determines the effective commit via [resolveEffectiveCommitHash].
265+ // For pinned commits (case 1), it returns immediately without cloning. For snapshot
266+ // and HEAD cases, it performs a metadata-only clone to resolve the commit hash.
267+ func (g * FedoraSourcesProviderImpl ) resolveCommit (
268+ ctx context.Context , gitRepoURL string , upstreamName string , upstreamCommit string ,
269+ ) (string , error ) {
270+ // Case 1: pinned commit — no clone needed. Delegate to resolveEffectiveCommitHash
271+ // so the priority logic remains in one place.
235272 if upstreamCommit != "" {
236- slog . Info ( "Using explicit upstream commit hash" ,
237- "commitHash" , upstreamCommit )
273+ return g . resolveEffectiveCommitHash ( ctx , "" , upstreamCommit , slog . LevelDebug )
274+ }
238275
239- if err := g .gitProvider .Checkout (ctx , repoDir , upstreamCommit ); err != nil {
240- return fmt .Errorf ("failed to checkout upstream commit %#q:\n %w" , upstreamCommit , err )
276+ // Cases 2 & 3: need a metadata-only clone to resolve snapshot or HEAD commit.
277+ tempDir , err := fileutils .MkdirTempInTempDir (g .fs , "azldev-identity-snapshot-" )
278+ if err != nil {
279+ return "" , fmt .Errorf ("creating temp directory for snapshot clone:\n %w" , err )
280+ }
281+
282+ defer func () {
283+ if removeErr := g .fs .RemoveAll (tempDir ); removeErr != nil {
284+ slog .Debug ("Failed to clean up snapshot clone temp directory" ,
285+ "path" , tempDir , "error" , removeErr )
241286 }
287+ }()
242288
243- return nil
289+ // Clone a single branch to resolve the snapshot commit. We use a full
290+ // (non-shallow) clone because not all git servers support --shallow-since
291+ // (e.g., Pagure returns "the remote end hung up unexpectedly").
292+ err = retry .Do (ctx , g .retryConfig , func () error {
293+ _ = g .fs .RemoveAll (tempDir )
294+ _ = fileutils .MkdirAll (g .fs , tempDir )
295+
296+ return g .gitProvider .Clone (ctx , gitRepoURL , tempDir ,
297+ git .WithGitBranch (g .distroGitBranch ),
298+ git .WithMetadataOnly (),
299+ git .WithQuiet (),
300+ )
301+ })
302+ if err != nil {
303+ return "" , fmt .Errorf ("partial clone for identity of %#q:\n %w" , upstreamName , err )
304+ }
305+
306+ commitHash , err := g .resolveEffectiveCommitHash (ctx , tempDir , "" , slog .LevelDebug )
307+ if err != nil {
308+ return "" , fmt .Errorf ("resolving commit for %#q:\n %w" , upstreamName , err )
309+ }
310+
311+ return commitHash , nil
312+ }
313+
314+ // resolveEffectiveCommitHash is the single source of truth for which commit a
315+ // component should use from a cloned repository.
316+ //
317+ // Priority:
318+ // 1. Explicit upstream commit hash (pinned per-component).
319+ // 2. Snapshot time — commit immediately before the snapshot date.
320+ // 3. Default — current HEAD.
321+ func (g * FedoraSourcesProviderImpl ) resolveEffectiveCommitHash (
322+ ctx context.Context ,
323+ repoDir string ,
324+ upstreamCommit string ,
325+ logLevel slog.Level ,
326+ ) (string , error ) {
327+ // Case 1: Explicit upstream commit hash specified per-component.
328+ if upstreamCommit != "" {
329+ slog .Log (ctx , logLevel , "Using explicit upstream commit hash" , "commitHash" , upstreamCommit )
330+
331+ return upstreamCommit , nil
244332 }
245333
246- // Case 2: Provider has a snapshot time configured from the resolved distro
334+ // Case 2: Provider has a snapshot time configured from the resolved distro.
247335 if g .snapshotTime != "" {
248336 snapshotDateTime , err := time .Parse (time .RFC3339 , g .snapshotTime )
249337 if err != nil {
250- return fmt .Errorf ("invalid snapshot time %#q:\n %w" , g .snapshotTime , err )
338+ return "" , fmt .Errorf ("invalid snapshot time %#q:\n %w" , g .snapshotTime , err )
251339 }
252340
253341 commitHash , err := g .gitProvider .GetCommitHashBeforeDate (ctx , repoDir , snapshotDateTime )
254342 if err != nil {
255- return fmt .Errorf ("failed to get commit hash for snapshot time %s:\n %w" ,
343+ return "" , fmt .Errorf ("resolving commit for snapshot time %s:\n %w" ,
256344 snapshotDateTime .Format (time .RFC3339 ), err )
257345 }
258346
259- slog .Info ( "Using upstream distro snapshot time" ,
347+ slog .Log ( ctx , logLevel , "Using upstream distro snapshot time" ,
260348 "snapshotDateTime" , snapshotDateTime .Format (time .RFC3339 ),
261349 "commitHash" , commitHash )
262350
263- if err := g .gitProvider .Checkout (ctx , repoDir , commitHash ); err != nil {
264- return fmt .Errorf ("failed to checkout snapshot commit %#q:\n %w" , commitHash , err )
265- }
351+ return commitHash , nil
352+ }
266353
267- return nil
354+ // Case 3: Default — use current HEAD.
355+ commitHash , err := g .gitProvider .GetCurrentCommit (ctx , repoDir )
356+ if err != nil {
357+ return "" , fmt .Errorf ("resolving current HEAD commit:\n %w" , err )
268358 }
269359
270- // Case 3: Default - use current HEAD (already checked out by clone)
271- slog .Info ("Using current HEAD (no snapshot time configured)" )
360+ slog .Log (ctx , logLevel , "Using current HEAD" , "commitHash" , commitHash )
272361
273- return nil
362+ return commitHash , nil
274363}
0 commit comments