Skip to content

Commit 708b0b7

Browse files
committed
wip 3
1 parent 7731e28 commit 708b0b7

File tree

11 files changed

+519
-239
lines changed

11 files changed

+519
-239
lines changed

docs/user/reference/cli/azldev_component_identity.md

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/app/azldev/cmds/component/identity.go

Lines changed: 30 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,14 @@ import (
1313
"github.com/microsoft/azure-linux-dev-tools/internal/app/azldev/core/sources"
1414
"github.com/microsoft/azure-linux-dev-tools/internal/fingerprint"
1515
"github.com/microsoft/azure-linux-dev-tools/internal/projectconfig"
16-
"github.com/microsoft/azure-linux-dev-tools/internal/utils/git"
17-
"github.com/microsoft/azure-linux-dev-tools/internal/utils/retry"
16+
"github.com/microsoft/azure-linux-dev-tools/internal/providers/sourceproviders"
1817
"github.com/spf13/cobra"
1918
)
2019

2120
// Options for computing component identity fingerprints.
2221
type IdentityComponentOptions struct {
2322
// Standard filter for selecting components.
2423
ComponentFilter components.ComponentFilter
25-
// Strict causes the command to fail if any upstream commit cannot be resolved.
26-
Strict bool
2724
}
2825

2926
func identityOnAppInit(_ *azldev.App, parentCmd *cobra.Command) {
@@ -66,9 +63,6 @@ rebuilding between two commits.`,
6663
azldev.ExportAsMCPTool(cmd)
6764
components.AddComponentFilterOptionsToCommand(cmd, &options.ComponentFilter)
6865

69-
cmd.Flags().BoolVar(&options.Strict, "strict", false,
70-
"fail if any upstream commit cannot be resolved (recommended for CI)")
71-
7266
return cmd
7367
}
7468

@@ -95,23 +89,20 @@ func ComputeComponentIdentities(
9589

9690
distroRef := env.Config().Project.DefaultDistro
9791

98-
// Set up upstream resolver for floating upstream specs.
99-
gitProvider, err := git.NewGitProviderImpl(env, env)
92+
// Resolve the distro definition (fills in default version).
93+
distroRef, distro, err := resolveDistroForIdentity(env, distroRef)
10094
if err != nil {
101-
return nil, fmt.Errorf("creating git provider:\n%w", err)
95+
slog.Debug("Could not resolve distro", "error", err)
10296
}
10397

104-
upstreamResolver := fingerprint.NewUpstreamResolver(gitProvider)
105-
106-
// Resolve the upstream distro definition for upstream commit resolution.
107-
distroRef, distroDef, distroVersionDef, err := resolveDistroForIdentity(env, distroRef)
98+
// Create a source manager — it handles provider construction and dispatch.
99+
srcManager, err := sourceproviders.NewSourceManager(env, distro)
108100
if err != nil {
109-
slog.Debug("Could not resolve distro for upstream commit resolution",
110-
"error", err)
101+
return nil, fmt.Errorf("creating source manager:\n%w", err)
111102
}
112103

113104
return computeIdentitiesParallel(
114-
env, comps.Components(), distroRef, distroDef, distroVersionDef, upstreamResolver, options.Strict,
105+
env, comps.Components(), distroRef, srcManager,
115106
)
116107
}
117108

@@ -120,15 +111,12 @@ func ComputeComponentIdentities(
120111
const maxConcurrentIdentity = 32
121112

122113
// computeIdentitiesParallel computes fingerprints for all components concurrently,
123-
// including upstream commit resolution, affects count, and file hashing.
114+
// including source identity resolution, affects count, and overlay file hashing.
124115
func computeIdentitiesParallel(
125116
env *azldev.Env,
126117
comps []components.Component,
127118
distroRef projectconfig.DistroReference,
128-
distroDef projectconfig.DistroDefinition,
129-
distroVersionDef projectconfig.DistroVersionDefinition,
130-
upstreamResolver *fingerprint.UpstreamResolver,
131-
strict bool,
119+
srcManager sourceproviders.SourceManager,
132120
) ([]ComponentIdentityResult, error) {
133121
progressEvent := env.StartEvent("Computing component identities",
134122
"count", len(comps))
@@ -142,7 +130,6 @@ func computeIdentitiesParallel(
142130

143131
resultsChan := make(chan indexedResult, len(comps))
144132
semaphore := make(chan struct{}, maxConcurrentIdentity)
145-
retryCfg := retry.DefaultConfig()
146133

147134
var waitGroup sync.WaitGroup
148135

@@ -163,7 +150,7 @@ func computeIdentitiesParallel(
163150
}
164151

165152
result, computeErr := computeSingleIdentity(
166-
env, comp, distroRef, distroDef, distroVersionDef, upstreamResolver, retryCfg, strict,
153+
env, comp, distroRef, srcManager,
167154
)
168155

169156
resultsChan <- indexedResult{index: compIdx, result: result, err: computeErr}
@@ -197,16 +184,12 @@ func computeIdentitiesParallel(
197184
}
198185

199186
// computeSingleIdentity computes the identity for a single component, including
200-
// upstream commit resolution (with retry) and affects commit counting.
187+
// source identity resolution, affects commit counting, and overlay file hashing.
201188
func computeSingleIdentity(
202189
env *azldev.Env,
203190
comp components.Component,
204191
distroRef projectconfig.DistroReference,
205-
distroDef projectconfig.DistroDefinition,
206-
distroVersionDef projectconfig.DistroVersionDefinition,
207-
upstreamResolver *fingerprint.UpstreamResolver,
208-
retryCfg retry.Config,
209-
strict bool,
192+
srcManager sourceproviders.SourceManager,
210193
) (ComponentIdentityResult, error) {
211194
config := comp.GetConfig()
212195
componentName := comp.GetName()
@@ -215,20 +198,16 @@ func computeSingleIdentity(
215198
AffectsCommitCount: countAffectsCommits(config, componentName),
216199
}
217200

218-
// Resolve upstream commit for upstream specs (with retry for transient failures).
219-
if config.Spec.SourceType == projectconfig.SpecSourceTypeUpstream {
220-
resolvedCommit, resolveErr := resolveUpstreamWithRetry(
221-
env, upstreamResolver, componentName, config, distroDef, distroVersionDef, retryCfg,
222-
)
223-
if resolveErr != nil && strict {
224-
return ComponentIdentityResult{}, fmt.Errorf(
225-
"upstream commit resolution failed for %#q (--strict mode):\n%w",
226-
componentName, resolveErr)
227-
}
228-
229-
identityOpts.ResolvedUpstreamCommit = resolvedCommit
201+
// Resolve source identity via the source manager (handles local/upstream dispatch + retry).
202+
sourceIdentity, resolveErr := srcManager.ResolveSourceIdentity(env, comp)
203+
if resolveErr != nil {
204+
return ComponentIdentityResult{}, fmt.Errorf(
205+
"source identity resolution failed for %#q:\n%w",
206+
componentName, resolveErr)
230207
}
231208

209+
identityOpts.SourceIdentity = sourceIdentity
210+
232211
identity, err := fingerprint.ComputeIdentity(env.FS(), *config, distroRef, identityOpts)
233212
if err != nil {
234213
return ComponentIdentityResult{}, fmt.Errorf("computing identity for component %#q:\n%w",
@@ -242,53 +221,14 @@ func computeSingleIdentity(
242221
}, nil
243222
}
244223

245-
// resolveUpstreamWithRetry resolves an upstream commit with retry logic, returning
246-
// the resolved commit or empty string and an error on failure.
247-
func resolveUpstreamWithRetry(
248-
env *azldev.Env,
249-
resolver *fingerprint.UpstreamResolver,
250-
componentName string,
251-
config *projectconfig.ComponentConfig,
252-
distroDef projectconfig.DistroDefinition,
253-
distroVersionDef projectconfig.DistroVersionDefinition,
254-
retryCfg retry.Config,
255-
) (string, error) {
256-
var resolvedCommit string
257-
258-
retryErr := retry.Do(env, retryCfg, func() error {
259-
var resolveErr error
260-
261-
resolvedCommit, resolveErr = resolver.ResolveCommit(
262-
env,
263-
componentName,
264-
config.Spec,
265-
resolveUpstreamDistro(config.Spec, env.Config(), distroDef),
266-
resolveUpstreamDistroVersion(config.Spec, env.Config(), distroVersionDef),
267-
)
268-
if resolveErr != nil {
269-
return fmt.Errorf("resolving commit:\n%w", resolveErr)
270-
}
271-
272-
return nil
273-
})
274-
if retryErr != nil {
275-
slog.Warn("Could not resolve upstream commit; fingerprint may be incomplete",
276-
"component", componentName, "error", retryErr)
277-
278-
return "", fmt.Errorf("resolving upstream commit for %#q:\n%w", componentName, retryErr)
279-
}
280-
281-
return resolvedCommit, nil
282-
}
283-
284-
// resolveDistroForIdentity resolves the default distro reference for upstream commit resolution.
285-
// Returns the distro ref with version populated if it was initially empty.
224+
// resolveDistroForIdentity resolves the default distro reference and returns a
225+
// [sourceproviders.ResolvedDistro] for constructing a source manager.
286226
func resolveDistroForIdentity(
287227
env *azldev.Env, distroRef projectconfig.DistroReference,
288-
) (projectconfig.DistroReference, projectconfig.DistroDefinition, projectconfig.DistroVersionDefinition, error) {
228+
) (projectconfig.DistroReference, sourceproviders.ResolvedDistro, error) {
289229
distroDef, distroVersionDef, err := env.ResolveDistroRef(distroRef)
290230
if err != nil {
291-
return distroRef, distroDef, distroVersionDef,
231+
return distroRef, sourceproviders.ResolvedDistro{},
292232
fmt.Errorf("resolving distro %#q:\n%w", distroRef.Name, err)
293233
}
294234

@@ -297,53 +237,11 @@ func resolveDistroForIdentity(
297237
distroRef.Version = distroDef.DefaultVersion
298238
}
299239

300-
return distroRef, distroDef, distroVersionDef, nil
301-
}
302-
303-
// resolveUpstreamDistro returns the distro definition for the component's upstream distro,
304-
// falling back to the default project distro if the component doesn't specify one.
305-
func resolveUpstreamDistro(
306-
spec projectconfig.SpecSource,
307-
config *projectconfig.ProjectConfig,
308-
defaultDef projectconfig.DistroDefinition,
309-
) projectconfig.DistroDefinition {
310-
if spec.UpstreamDistro.Name == "" {
311-
return defaultDef
312-
}
313-
314-
if upstreamDef, found := config.Distros[spec.UpstreamDistro.Name]; found {
315-
return upstreamDef
316-
}
317-
318-
return defaultDef
319-
}
320-
321-
// resolveUpstreamDistroVersion returns the distro version definition for the upstream distro,
322-
// falling back to the default if not found.
323-
func resolveUpstreamDistroVersion(
324-
spec projectconfig.SpecSource,
325-
config *projectconfig.ProjectConfig,
326-
defaultVersionDef projectconfig.DistroVersionDefinition,
327-
) projectconfig.DistroVersionDefinition {
328-
if spec.UpstreamDistro.Name == "" {
329-
return defaultVersionDef
330-
}
331-
332-
upstreamDef, found := config.Distros[spec.UpstreamDistro.Name]
333-
if !found {
334-
return defaultVersionDef
335-
}
336-
337-
version := spec.UpstreamDistro.Version
338-
if version == "" {
339-
version = upstreamDef.DefaultVersion
340-
}
341-
342-
if versionDef, vFound := upstreamDef.Versions[version]; vFound {
343-
return versionDef
344-
}
345-
346-
return defaultVersionDef
240+
return distroRef, sourceproviders.ResolvedDistro{
241+
Ref: distroRef,
242+
Definition: distroDef,
243+
Version: distroVersionDef,
244+
}, nil
347245
}
348246

349247
// countAffectsCommits counts the number of "Affects: <componentName>" commits in the

internal/fingerprint/fingerprint.go

Lines changed: 11 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,11 @@ type ComponentIdentity struct {
3535
type ComponentInputs struct {
3636
// ConfigHash is the hash of the resolved component config fields (uint64 from hashstructure).
3737
ConfigHash uint64 `json:"configHash"`
38-
// SpecContentHash is the SHA256 of the spec file content (local specs only).
39-
SpecContentHash string `json:"specContentHash,omitempty"`
38+
// SourceIdentity is the opaque identity string for the component's source.
39+
// For local specs this is a content hash; for upstream specs this is a commit hash.
40+
SourceIdentity string `json:"sourceIdentity,omitempty"`
4041
// OverlayFileHashes maps overlay source file paths to their SHA256 hashes.
4142
OverlayFileHashes map[string]string `json:"overlayFileHashes,omitempty"`
42-
// ResolvedUpstreamCommit is the concrete upstream commit hash for upstream specs.
43-
// For pinned specs this is the pinned value; for floating specs it is resolved at runtime.
44-
ResolvedUpstreamCommit string `json:"resolvedUpstreamCommit,omitempty"`
4543
// AffectsCommitCount is the number of "Affects: <component>" commits in the project repo.
4644
AffectsCommitCount int `json:"affectsCommitCount"`
4745
// Distro is the effective distro name.
@@ -55,9 +53,8 @@ type ComponentInputs struct {
5553
type IdentityOptions struct {
5654
// AffectsCommitCount is the number of "Affects: <component>" commits.
5755
AffectsCommitCount int
58-
// ResolvedUpstreamCommit is the concrete upstream commit hash for upstream specs.
59-
// Leave empty for local specs.
60-
ResolvedUpstreamCommit string
56+
// SourceIdentity is the opaque identity string from a [sourceproviders.SourceIdentityProvider].
57+
SourceIdentity string
6158
}
6259

6360
// ComputeIdentity computes the fingerprint for a component from its resolved config
@@ -70,10 +67,10 @@ func ComputeIdentity(
7067
opts IdentityOptions,
7168
) (*ComponentIdentity, error) {
7269
inputs := ComponentInputs{
73-
AffectsCommitCount: opts.AffectsCommitCount,
74-
ResolvedUpstreamCommit: opts.ResolvedUpstreamCommit,
75-
Distro: distroRef.Name,
76-
DistroVersion: distroRef.Version,
70+
AffectsCommitCount: opts.AffectsCommitCount,
71+
SourceIdentity: opts.SourceIdentity,
72+
Distro: distroRef.Name,
73+
DistroVersion: distroRef.Version,
7774
}
7875

7976
// 1. Hash the resolved config struct (excluding fingerprint:"-" fields).
@@ -86,20 +83,7 @@ func ComputeIdentity(
8683

8784
inputs.ConfigHash = configHash
8885

89-
// 2. Hash spec file content for local specs.
90-
if component.Spec.SourceType == projectconfig.SpecSourceTypeLocal ||
91-
component.Spec.SourceType == projectconfig.SpecSourceTypeUnspecified {
92-
if component.Spec.Path != "" {
93-
specHash, fileErr := fileutils.ComputeFileHash(fs, fileutils.HashTypeSHA256, component.Spec.Path)
94-
if fileErr != nil {
95-
return nil, fmt.Errorf("hashing spec file %#q:\n%w", component.Spec.Path, fileErr)
96-
}
97-
98-
inputs.SpecContentHash = specHash
99-
}
100-
}
101-
102-
// 3. Hash overlay source file contents.
86+
// 2. Hash overlay source file contents.
10387
overlayHashes, err := hashOverlayFiles(fs, component.Overlays)
10488
if err != nil {
10589
return nil, fmt.Errorf("hashing overlay files:\n%w", err)
@@ -145,8 +129,7 @@ func combineInputs(inputs ComponentInputs) string {
145129

146130
// Write each input in a fixed order with field labels for domain separation.
147131
writeField(hasher, "config_hash", strconv.FormatUint(inputs.ConfigHash, 10))
148-
writeField(hasher, "spec_content_hash", inputs.SpecContentHash)
149-
writeField(hasher, "resolved_upstream_commit", inputs.ResolvedUpstreamCommit)
132+
writeField(hasher, "source_identity", inputs.SourceIdentity)
150133
writeField(hasher, "affects_commit_count", strconv.Itoa(inputs.AffectsCommitCount))
151134
writeField(hasher, "distro", inputs.Distro)
152135
writeField(hasher, "distro_version", inputs.DistroVersion)

0 commit comments

Comments
 (0)