Skip to content

feat(cli): Add component identity command#48

Draft
dmcilvaney wants to merge 1 commit intomicrosoft:mainfrom
dmcilvaney:damcilva/component_change_detection_parts/6
Draft

feat(cli): Add component identity command#48
dmcilvaney wants to merge 1 commit intomicrosoft:mainfrom
dmcilvaney:damcilva/component_change_detection_parts/6

Conversation

@dmcilvaney
Copy link
Copy Markdown
Contributor

No description provided.

Copilot AI review requested due to automatic review settings March 31, 2026 00:54
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new azldev component identity subcommand intended to compute deterministic “identity fingerprints” for selected components, to support detecting rebuild needs between commits.

Changes:

  • Introduces a new component identity Cobra command and wires it into the component command tree.
  • Implements parallel per-component identity computation (including source identity + “Affects:” commit counting inputs).
  • Regenerates auto-generated CLI reference docs to include the new subcommand.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.

File Description
internal/app/azldev/cmds/component/identity.go New CLI command + implementation for computing component identity fingerprints in parallel.
internal/app/azldev/cmds/component/component.go Registers the new identity subcommand under azldev component.
docs/user/reference/cli/azldev_component.md Auto-generated CLI index updated to list component identity.
docs/user/reference/cli/azldev_component_identity.md Auto-generated CLI doc page for the new subcommand.

Comment on lines +11 to +16
"github.com/microsoft/azure-linux-dev-tools/internal/app/azldev"
"github.com/microsoft/azure-linux-dev-tools/internal/app/azldev/core/components"
"github.com/microsoft/azure-linux-dev-tools/internal/app/azldev/core/sources"
"github.com/microsoft/azure-linux-dev-tools/internal/fingerprint"
"github.com/microsoft/azure-linux-dev-tools/internal/projectconfig"
"github.com/microsoft/azure-linux-dev-tools/internal/providers/sourceproviders"
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file imports github.com/microsoft/azure-linux-dev-tools/internal/fingerprint, but there is no internal/fingerprint package in the repo (so this won’t compile). Please either add the missing package in this PR or update the import/usage to the correct existing package that provides identity/fingerprint computation.

Copilot uses AI. Check for mistakes.
Comment on lines +43 to +44
Use this with 'component diff-identity' to determine which components need
rebuilding between two commits.`,
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The help text references component diff-identity, but there is no such command in the codebase (the only match is this string). Either add that command in the same feature set, or adjust the wording to reference an existing workflow/command so users aren’t directed to a non-existent subcommand.

Suggested change
Use this with 'component diff-identity' to determine which components need
rebuilding between two commits.`,
Compare identity outputs from two commits (for example, using diff or JSON
tooling) to determine which components need rebuilding between those commits.`,

Copilot uses AI. Check for mistakes.
Comment on lines +258 to +262

repo, err := sources.OpenProjectRepo(configFile.SourcePath())
if err != nil {
slog.Debug("Could not open project repo for Affects commits; defaulting to 0",
"component", componentName, "error", err)
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sources.OpenProjectRepo is called here, but no such exported function exists in internal/app/azldev/core/sources (only an unexported openProjectRepo helper exists in synthistory.go). As written this won’t compile; consider exporting a helper from sources (or duplicating the small PlainOpenWithOptions logic here) so identity can open the project repo reliably.

Copilot uses AI. Check for mistakes.
Comment on lines +250 to +276
// countAffectsCommits counts the number of "Affects: <componentName>" commits in the
// project repo. Returns 0 if the count cannot be determined (e.g., no git repo).
func countAffectsCommits(config *projectconfig.ComponentConfig, componentName string,
) int {
configFile := config.SourceConfigFile
if configFile == nil || configFile.SourcePath() == "" {
return 0
}

repo, err := sources.OpenProjectRepo(configFile.SourcePath())
if err != nil {
slog.Debug("Could not open project repo for Affects commits; defaulting to 0",
"component", componentName, "error", err)

return 0
}

commits, err := sources.FindAffectsCommits(repo, componentName)
if err != nil {
slog.Debug("Could not count Affects commits; defaulting to 0",
"component", componentName, "error", err)

return 0
}

return len(commits)
}
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

countAffectsCommits walks the full git history once per component via sources.FindAffectsCommits, making this command O(#components × #commits). On large repos/components this can become very slow. Consider scanning the git log once per run and building a map[componentName]int (or map[string]struct{count int}) for all Affects: trailers, then looking up counts per component.

Copilot uses AI. Check for mistakes.
Comment on lines +279 to +303
// component's source by delegating to [sourceproviders.SourceManager.ResolveSourceIdentity].
func resolveSourceIdentityForComponent(
env *azldev.Env, comp components.Component,
) (string, error) {
distro, err := sourceproviders.ResolveDistro(env, comp)
if err != nil {
return "", fmt.Errorf("resolving distro for component %#q:\n%w",
comp.GetName(), err)
}

// A new source manager is created per component because each may reference a different
// upstream distro.
srcManager, err := sourceproviders.NewSourceManager(env, distro)
if err != nil {
return "", fmt.Errorf("creating source manager for component %#q:\n%w",
comp.GetName(), err)
}

identity, err := srcManager.ResolveSourceIdentity(env.Context(), comp)
if err != nil {
return "", fmt.Errorf("resolving source identity for %#q:\n%w",
comp.GetName(), err)
}

return identity, nil
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sourceproviders.SourceManager (and the concrete returned by NewSourceManager) does not define a ResolveSourceIdentity method anywhere in the repo, so this call won’t compile. If source identity is required for fingerprints, add a real API for it in sourceproviders (and implement it), or switch to an existing mechanism already used for source resolution.

Suggested change
// component's source by delegating to [sourceproviders.SourceManager.ResolveSourceIdentity].
func resolveSourceIdentityForComponent(
env *azldev.Env, comp components.Component,
) (string, error) {
distro, err := sourceproviders.ResolveDistro(env, comp)
if err != nil {
return "", fmt.Errorf("resolving distro for component %#q:\n%w",
comp.GetName(), err)
}
// A new source manager is created per component because each may reference a different
// upstream distro.
srcManager, err := sourceproviders.NewSourceManager(env, distro)
if err != nil {
return "", fmt.Errorf("creating source manager for component %#q:\n%w",
comp.GetName(), err)
}
identity, err := srcManager.ResolveSourceIdentity(env.Context(), comp)
if err != nil {
return "", fmt.Errorf("resolving source identity for %#q:\n%w",
comp.GetName(), err)
}
return identity, nil
// component's source. Currently this is derived solely from the component name to avoid
// relying on unresolved source provider APIs.
func resolveSourceIdentityForComponent(
env *azldev.Env, comp components.Component,
) (string, error) {
if comp == nil {
return "", fmt.Errorf("resolving source identity:\n%v", "component is nil")
}
// Use the component name as a stable, deterministic identity. This avoids depending on
// a non-existent [sourceproviders.SourceManager.ResolveSourceIdentity] API while still
// providing a repeatable identifier for fingerprinting.
return comp.GetName(), nil

Copilot uses AI. Check for mistakes.
Comment on lines +78 to +100
// ComputeComponentIdentities computes fingerprints for all selected components.
func ComputeComponentIdentities(
env *azldev.Env, options *IdentityComponentOptions,
) ([]ComponentIdentityResult, error) {
resolver := components.NewResolver(env)

comps, err := resolver.FindComponents(&options.ComponentFilter)
if err != nil {
return nil, fmt.Errorf("failed to resolve components:\n%w", err)
}

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

// Resolve the distro definition (fills in default version for the fingerprint).
distroRef, err = resolveDistroForIdentity(env, distroRef)
if err != nil {
slog.Debug("Could not resolve distro", "error", err)
}

return computeIdentitiesParallel(
env, comps.Components(), distroRef,
)
}
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new component identity behavior is substantial (parallel execution, source identity resolution, git-history-derived inputs) but there are no unit tests added alongside it. Other component commands in this folder have focused tests; please add tests covering at least: component selection/filtering, stable output ordering, and error propagation/cancellation on a failing component (using in-memory FS / in-memory git as applicable).

Copilot generated this review using guidance from repository custom instructions.
@dmcilvaney dmcilvaney marked this pull request as draft March 31, 2026 01:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants