@@ -221,54 +221,142 @@ 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 {
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+ // ResolveIdentity 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 ) ResolveIdentity (
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 ) {
234270 // Case 1: Explicit upstream commit hash specified per-component
235271 if upstreamCommit != "" {
236- slog . Info ( "Using explicit upstream commit hash" ,
237- "commitHash" , upstreamCommit )
272+ return g . resolveEffectiveCommitHash ( ctx , "" , upstreamCommit , slog . LevelDebug )
273+ }
238274
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 )
275+ // Cases 2 & 3: need a metadata-only clone to resolve snapshot or HEAD commit.
276+ tempDir , err := fileutils .MkdirTempInTempDir (g .fs , "azldev-identity-snapshot-" )
277+ if err != nil {
278+ return "" , fmt .Errorf ("creating temp directory for snapshot clone:\n %w" , err )
279+ }
280+
281+ defer func () {
282+ if removeErr := g .fs .RemoveAll (tempDir ); removeErr != nil {
283+ slog .Debug ("Failed to clean up snapshot clone temp directory" ,
284+ "path" , tempDir , "error" , removeErr )
241285 }
286+ }()
242287
243- return nil
288+ // Clone a single branch to resolve the snapshot commit. We use a full
289+ // (non-shallow) clone because not all git servers support --shallow-since
290+ // (e.g., Pagure returns "the remote end hung up unexpectedly").
291+ err = retry .Do (ctx , g .retryConfig , func () error {
292+ _ = g .fs .RemoveAll (tempDir )
293+ _ = fileutils .MkdirAll (g .fs , tempDir )
294+
295+ return g .gitProvider .Clone (ctx , gitRepoURL , tempDir ,
296+ git .WithGitBranch (g .distroGitBranch ),
297+ git .WithMetadataOnly (),
298+ git .WithQuiet (),
299+ )
300+ })
301+ if err != nil {
302+ return "" , fmt .Errorf ("partial clone for identity of %#q:\n %w" , upstreamName , err )
303+ }
304+
305+ commitHash , err := g .resolveEffectiveCommitHash (ctx , tempDir , "" , slog .LevelDebug )
306+ if err != nil {
307+ return "" , fmt .Errorf ("resolving commit for %#q:\n %w" , upstreamName , err )
308+ }
309+
310+ return commitHash , nil
311+ }
312+
313+ // resolveEffectiveCommitHash is the single source of truth for which commit a
314+ // component should use from a cloned repository.
315+ //
316+ // Priority:
317+ // 1. Explicit upstream commit hash (pinned per-component).
318+ // 2. Snapshot time — commit immediately before the snapshot date.
319+ // 3. Default — current HEAD.
320+ func (g * FedoraSourcesProviderImpl ) resolveEffectiveCommitHash (
321+ ctx context.Context ,
322+ repoDir string ,
323+ upstreamCommit string ,
324+ logLevel slog.Level ,
325+ ) (string , error ) {
326+ // Case 1: Explicit upstream commit hash specified per-component.
327+ if upstreamCommit != "" {
328+ slog .Log (ctx , logLevel , "Using explicit upstream commit hash" , "commitHash" , upstreamCommit )
329+
330+ return upstreamCommit , nil
244331 }
245332
246- // Case 2: Provider has a snapshot time configured from the resolved distro
333+ // Case 2: Provider has a snapshot time configured from the resolved distro.
247334 if g .snapshotTime != "" {
248335 snapshotDateTime , err := time .Parse (time .RFC3339 , g .snapshotTime )
249336 if err != nil {
250- return fmt .Errorf ("invalid snapshot time %#q:\n %w" , g .snapshotTime , err )
337+ return "" , fmt .Errorf ("invalid snapshot time %#q:\n %w" , g .snapshotTime , err )
251338 }
252339
253340 commitHash , err := g .gitProvider .GetCommitHashBeforeDate (ctx , repoDir , snapshotDateTime )
254341 if err != nil {
255- return fmt .Errorf ("failed to get commit hash for snapshot time %s:\n %w" ,
342+ return "" , fmt .Errorf ("resolving commit for snapshot time %s:\n %w" ,
256343 snapshotDateTime .Format (time .RFC3339 ), err )
257344 }
258345
259- slog .Info ( "Using upstream distro snapshot time" ,
346+ slog .Log ( ctx , logLevel , "Using upstream distro snapshot time" ,
260347 "snapshotDateTime" , snapshotDateTime .Format (time .RFC3339 ),
261348 "commitHash" , commitHash )
262349
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- }
350+ return commitHash , nil
351+ }
266352
267- return nil
353+ // Case 3: Default — use current HEAD.
354+ commitHash , err := g .gitProvider .GetCurrentCommit (ctx , repoDir )
355+ if err != nil {
356+ return "" , fmt .Errorf ("resolving current HEAD commit:\n %w" , err )
268357 }
269358
270- // Case 3: Default - use current HEAD (already checked out by clone)
271- slog .Info ("Using current HEAD (no snapshot time configured)" )
359+ slog .Log (ctx , logLevel , "Using current HEAD" , "commitHash" , commitHash )
272360
273- return nil
361+ return commitHash , nil
274362}
0 commit comments