Skip to content

Dependencies: add consumer-side Alias for multi-version references#109

Open
gfraiteur wants to merge 2 commits intodevelop/2023.2from
topic/2023.2/dependency-aliases
Open

Dependencies: add consumer-side Alias for multi-version references#109
gfraiteur wants to merge 2 commits intodevelop/2023.2from
topic/2023.2/dependency-aliases

Conversation

@gfraiteur
Copy link
Copy Markdown
Member

Summary

  • Add per-use-site Alias and ArtifactPickup (Snapshot/LastSuccessful) on ParametrizedDependency so a referencing product can list the same logical dependency under two ProductFamily versions without MSBuild property-name collisions.
  • Plumb the consumer-side Key (alias-aware) through file generators (DependenciesConfigurationFile, VersionFile, ArtifactManifestFile) and CI generators (ConfigurationProperties, PowershellAdditionalCiBuildConfiguration, TeamCitySettingsFile). For unaliased dependencies, Key == Name and behavior is unchanged (regression-verified by running b prepare on PostSharp.Engineering itself: byte-identical generated files).
  • Add a fetch-time XML transform that projects the producer''s {Name}.version.props into a {Alias}.version.props with prefix-substituted element names. Uses a curated suffix list so transitive Feed dep properties (e.g. <MetalamaCompilerVersion>) are not renamed. Relative <Import Project=".."/> paths are absolutized so the relocated copy still resolves. Applies uniformly to BuildServer, RestoredDependency, and Local source kinds.
  • Resolve transitive dependencies starting from the direct dep''s ProductFamily, falling back to the consumer''s Product only if not found. Required for aliased deps: a Metalama 2026.0 reference''s transitive Metalama.Compiler now correctly routes to V2026_0.MetalamaCompiler instead of V2026_1.MetalamaCompiler.
  • MetalamaVsxDependencies.V2026_1 now references MetalamaDependencies.V2026_0.Metalama alongside V2026_1, mapped to BuildConfiguration.Public with WithLastSuccessfulOnly() (artifact-only TeamCity dependency, no snapshot).
  • New PostSharp.Engineering.BuildTools.Tests project (xUnit) with 13 tests covering alias semantics and the version.props transform.

Backward compatibility

The alias is purely additive: when no alias is set, Key == Name and every code path is byte-identical to before. A regression run of b prepare on PostSharp.Engineering produced an identical eng/Versions.Debug.g.props modulo environment-specific lines (version comment, user data dir).

Notes

  • DependencyConfiguration got custom Equals/GetHashCode to keep (Definition, Configuration) as the equality key — the auto-generated record equality would have included the new Parametrized property, breaking HashSet-based deduplication in GetAllDependencies.

🤖 Generated with Claude Code

Adds a per-use-site `Alias` and `ArtifactPickup` (Snapshot/LastSuccessful)
on `ParametrizedDependency` so a referencing product can list the same
logical dependency under two different `ProductFamily` versions without
MSBuild property-name collisions. The alias drives the `dependencies/{Key}/`
directory layout, the generated MSBuild property prefix, and the TeamCity
artifact-rule destination. A fetch-time XML transform projects the
producer's `<{Name}.version.props>` into a `<{Alias}.version.props>` whose
producer-prefixed property/item names are renamed (using a curated
suffix list to avoid renaming transitive Feed dep properties). Transitive
dependency resolution now starts from the direct dep's family, so an
aliased Metalama 2026.0 routes its transitive `Metalama.Compiler` to
the V2026_0 definition rather than the consumer's V2026_1 family.

`Metalama.Vsx` 2026.1 now references Metalama 2026.0 alongside 2026.1,
mapped to BuildConfiguration.Public with `LastSuccessful` artifact pickup
(no snapshot dependency).

Includes a new `PostSharp.Engineering.BuildTools.Tests` project with
13 tests covering the alias semantics and the version.props transform.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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 consumer-side dependency aliasing (plus artifact pickup mode) so a product can reference multiple versions of the same logical dependency without MSBuild/config key collisions, and plumbs the alias-aware key through dependency resolution, file generation, and TeamCity CI generation.

Changes:

  • Introduce ParametrizedDependency.Alias/Key/KeyWithoutDot and DependencyArtifactPickup (Snapshot vs LastSuccessful) and propagate through dependency configuration models.
  • Update dependency fetch/restore/local resolution to support aliased {Key}.version.props via an XML transform of producer {Name}.version.props.
  • Add PostSharp.Engineering.BuildTools.Tests (xUnit) covering alias semantics and the version.props transform.

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/PostSharp.Engineering.BuildTools/PostSharp.Engineering.BuildTools.csproj Exposes internals to the new test assembly.
src/PostSharp.Engineering.BuildTools/Dependencies/Model/ParametrizedDependency.cs Adds alias-aware keying and artifact pickup mode + fluent helpers.
src/PostSharp.Engineering.BuildTools/Dependencies/Model/DependencySource.cs Adds alias-aware restored-dependency creation overload.
src/PostSharp.Engineering.BuildTools/Dependencies/Model/DependencyDefinition.cs Carries parametrized info into recursive dependency enumeration; adds WithAlias.
src/PostSharp.Engineering.BuildTools/Dependencies/Model/DependencyConfiguration.cs Adds alias-aware Key/KeyWithoutDot/ArtifactPickup and custom equality.
src/PostSharp.Engineering.BuildTools/Dependencies/Model/DependencyArtifactPickup.cs New enum controlling snapshot vs last-successful artifact pickup.
src/PostSharp.Engineering.BuildTools/Dependencies/DependenciesHelper.cs Plumbs alias keys through fetch/restore/local resolution and adds version.props transform.
src/PostSharp.Engineering.BuildTools/Dependencies/Definitions/MetalamaVsxDependencies.V2026_1.cs Demonstrates dual Metalama refs (V2026_1 + aliased V2026_0, last-successful).
src/PostSharp.Engineering.BuildTools/ContinuousIntegration/TeamCity/Generation/TeamCitySettingsFile.cs Excludes non-snapshot deps from deploy/upstream-merge snapshot dependency sets.
src/PostSharp.Engineering.BuildTools/ContinuousIntegration/TeamCity/Generation/ConfigurationProperties.cs Generates artifact rules keyed by alias and supports last-successful artifact dependencies.
src/PostSharp.Engineering.BuildTools/ContinuousIntegration/Model/PowershellAdditionalCiBuildConfiguration.cs Uses alias-aware keys in artifact rules and version import paths.
src/PostSharp.Engineering.BuildTools/Build/Model/Product.cs Updates dependency lookup to consider alias-aware keys.
src/PostSharp.Engineering.BuildTools/Build/Files/VersionFile.cs Reads/validates dependency versions using alias-aware keys.
src/PostSharp.Engineering.BuildTools/Build/Files/DependenciesConfigurationFile.cs Writes/reads dependency imports and version properties using alias-aware keys.
src/PostSharp.Engineering.BuildTools/Build/Files/ArtifactManifestFile.cs Emits dependency lists keyed by alias-aware keys in producer manifests.
src/PostSharp.Engineering.BuildTools.Tests/runtimeconfig.template.json Test runtime roll-forward configuration.
src/PostSharp.Engineering.BuildTools.Tests/TransformVersionPropsForAliasTests.cs Tests for the version.props alias transform behavior.
src/PostSharp.Engineering.BuildTools.Tests/PostSharp.Engineering.BuildTools.Tests.csproj New xUnit test project definition.
src/PostSharp.Engineering.BuildTools.Tests/ParametrizedDependencyAliasTests.cs Tests alias key semantics, pickup mode, and config equality behavior.
PostSharp.Engineering.sln Adds the test project and extra solution configurations.
Directory.Packages.props Adds package versions for test dependencies.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 281 to 283
dependency = this.ParametrizedDependencies.SingleOrDefault( d => d.Key == name )
?? this.ParametrizedDependencies.SingleOrDefault( d => d.Name == name );

Comment on lines 301 to 305
public bool TryGetDependencyDefinition( string name, [NotNullWhen( true )] out DependencyDefinition? dependencyDefinition )
{
dependencyDefinition = this.ParametrizedDependencies.SingleOrDefault( d => d.Name == name )?.Definition;
dependencyDefinition = (this.ParametrizedDependencies.SingleOrDefault( d => d.Key == name )
?? this.ParametrizedDependencies.SingleOrDefault( d => d.Name == name ))?.Definition;

Comment on lines +524 to +535
var aliasDirectory = TeamCityHelper.GetRestoredDependencyDirectory( context.RepoDirectory, dependency.Key );
var producerRestoredPath = Path.Combine( aliasDirectory, dependency.Dependency.Name + ".version.props" );
var aliasedVersionPropsPath = Path.Combine( aliasDirectory, dependency.Key + ".version.props" );

if ( File.Exists( producerRestoredPath ) && !File.Exists( aliasedVersionPropsPath ) )
{
TransformVersionPropsForAlias(
producerRestoredPath,
aliasedVersionPropsPath,
dependency.Dependency.NameWithoutDot,
dependency.KeyWithoutDot );
}
Three correctness fixes from Copilot review:

- Product.TryGetDependency / TryGetDependencyDefinition: drop the
  ambiguous Definition.Name fallback that would throw whenever two
  ParametrizedDependency entries declare different aliases for the
  same Definition.Name. Lookup is now strictly by Key (which equals
  Name for unaliased entries, preserving legacy callers).

- DependenciesHelper.ResolveBuildNumbersFromBranches: use
  ResolvedDependency.Parametrized directly instead of looking it up
  by Definition.Name. The Name-based lookup returned the wrong
  ParametrizedDependency (and its ConfigurationMapping) when the
  consumer had two refs to the same Definition under different
  aliases. The path is only reachable for direct deps, where
  Parametrized is always populated.

- DependenciesHelper.ResolveRestoredDependencies: always re-transform
  the producer's restored {Name}.version.props rather than skipping
  when {Key}.version.props already exists. The previous gate left a
  stale file when TeamCity restored a fresher version over an
  existing dependencies/{Key}/ directory.

Adds a test covering the throw-avoidance scenario (two aliased refs
to the same Definition.Name).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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.

3 participants