Skip to content

Commit 8ee7eda

Browse files
gfraiteurclaude
andcommitted
TeamCity: Add transitive dependency support and fix artifact restoration paths.
- PowershellAdditionalCiBuildConfiguration now recursively resolves all transitive dependencies and creates snapshot dependencies for them - Generate eng/Versions.g.props with imports for main product and all transitive dependency version props files - Fix RestoredArtifactsNuGetConfigGenerator to use correct paths (TeamCity artifact restoration flattens structure to dependencies/{Name}) - Fix PowerShellScriptBuildStep to properly handle UseWsl by using ScriptPath and ScriptArguments - Mark ContainerHostKind.Wsl as obsolete and add HostKind property to ContainerHostRequirements Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 563b474 commit 8ee7eda

File tree

7 files changed

+115
-30
lines changed

7 files changed

+115
-30
lines changed

src/PostSharp.Engineering.BuildTools/Build/Files/NuGet/RestoredArtifactsNuGetConfigGenerator.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,12 @@ protected override string GetDependencyDirectory(
3636
DependencyDefinition dependencyDefinition,
3737
BuildConfiguration configuration )
3838
{
39-
// For restored artifacts, dependencies are always under dependencies/<name>/{PrivateArtifactsDirectory}
40-
// regardless of the source kind (this is the TeamCity artifact restoration structure)
39+
// For restored artifacts, dependencies are always under dependencies/<name>
40+
// TeamCity artifact rule +:{PrivateArtifactsDir}/**/* => dependencies/{name} copies the contents directly
4141
return Path.Combine(
4242
context.RepoDirectory,
4343
"dependencies",
44-
dependencyKey,
45-
dependencyDefinition.GetPrivateArtifactsDirectory( configuration ) );
44+
dependencyKey );
4645
}
4746

4847
protected override bool ShouldIncludeDependency( DependencyDefinition dependencyDefinition )

src/PostSharp.Engineering.BuildTools/ContinuousIntegration/Model/PowershellAdditionalCiBuildConfiguration.cs

Lines changed: 64 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
using PostSharp.Engineering.BuildTools.Docker;
99
using System;
1010
using System.Collections.Generic;
11+
using System.IO;
12+
using System.Linq;
1113

1214
namespace PostSharp.Engineering.BuildTools.ContinuousIntegration.Model;
1315

@@ -24,44 +26,96 @@ public PowershellAdditionalCiBuildConfiguration( string id, string name, string
2426

2527
public string Arguments { get; }
2628

29+
public bool UseWsl { get; init; }
30+
2731
internal override TeamCityBuildConfiguration TeamCityBuildConfiguration(
2832
ProductProperties productProperties,
2933
IReadOnlyDictionary<BuildConfiguration, TeamCityBuildConfiguration> teamCityBuildBuildConfigurations )
3034
{
3135
var product = productProperties.Product;
3236

3337
var buildSteps = new List<BuildStep>();
34-
TeamCityBuildConfiguration? buildConfiguration = null;
35-
string? buildArtifactsDirectory = null;
38+
List<TeamCitySnapshotDependency>? snapshotDependencies = null;
3639

3740
// Handle snapshot dependencies.
3841
if ( this.BuildSnapshotDependency != null )
3942
{
40-
if ( !teamCityBuildBuildConfigurations.TryGetValue( this.BuildSnapshotDependency.Value, out buildConfiguration ) )
43+
if ( !teamCityBuildBuildConfigurations.TryGetValue( this.BuildSnapshotDependency.Value, out var buildConfiguration ) )
4144
{
4245
throw new KeyNotFoundException( $"Cannot find the TeamCity build configuration for '{this.BuildSnapshotDependency.Value}'." );
4346
}
4447

45-
buildArtifactsDirectory = productProperties.Product.GetPrivateArtifactsRelativeDirectory( this.BuildSnapshotDependency.Value ).Replace( "\\", "/", StringComparison.Ordinal );
46-
48+
var buildArtifactsDirectory = productProperties.Product.GetPrivateArtifactsRelativeDirectory( this.BuildSnapshotDependency.Value )
49+
.Replace( "\\", "/", StringComparison.Ordinal );
50+
51+
// Get all transitive dependencies for the build configuration
52+
var dependencies = product.DependencyDefinition.GetAllDependencies( this.BuildSnapshotDependency.Value )
53+
.Where( d => d.Definition.GenerateSnapshotDependency )
54+
.ToList();
55+
56+
// Create snapshot dependencies for all transitive dependencies
57+
snapshotDependencies =
58+
[new TeamCitySnapshotDependency( buildConfiguration.ObjectName, false, $"+:{buildArtifactsDirectory}/**/*=>{buildArtifactsDirectory}" )];
59+
60+
snapshotDependencies.AddRange(
61+
dependencies.Select( d => new TeamCitySnapshotDependency(
62+
d.Definition.CiConfiguration.BuildTypes[d.Configuration],
63+
true,
64+
$"+:{d.Definition.GetPrivateArtifactsDirectory( d.Configuration ).Replace( Path.DirectorySeparatorChar, '/' )}/**/*=>dependencies/{d.Definition.Name}" ) ) );
65+
4766
// If we have a build snapshot dependency, copy nuget.restored.config to nuget.config
67+
var copyNuGetConfigCommand = $@"Copy-Item -Path ""{buildArtifactsDirectory}/nuget.restored.config"" -Destination ""nuget.config"" -Force;";
68+
69+
if ( product.AddWslSupport )
70+
{
71+
copyNuGetConfigCommand += $@"Copy-Item -Path ""{buildArtifactsDirectory}/nuget.restored.config"" -Destination ""nuget.wsl.config"" -Force;";
72+
}
73+
4874
buildSteps.Add(
4975
new PowerShellCommandBuildStep(
5076
"CopyNuGetConfig",
5177
"Copy nuget.restored.config to nuget.config",
52-
$@"Copy-Item -Path ""{buildArtifactsDirectory}/nuget.restored.config"" -Destination ""nuget.config"" -Force",
78+
copyNuGetConfigCommand,
79+
null ) );
80+
81+
// Create an MSBuild project that imports the restored version props file and all dependency version props
82+
// Paths are relative to eng/Versions.g.props, so need ../ prefix
83+
var versionImports = $"<Import Project=`\"../{buildArtifactsDirectory}/{product.ProductName}.version.props`\" />";
84+
85+
foreach ( var dependency in dependencies )
86+
{
87+
versionImports +=
88+
$"<Import Project=`\"../dependencies/{dependency.Definition.Name}/{dependency.Definition.Name}.version.props`\" />";
89+
}
90+
91+
var createVersionsFileCommand =
92+
$@"New-Item -Path ""{product.EngineeringDirectory}/Versions.g.props"" -ItemType File -Force -Value ""<Project>{versionImports}</Project>"" | Out-Null;";
93+
94+
buildSteps.Add(
95+
new PowerShellCommandBuildStep(
96+
"CreateVersionsFile",
97+
"Create eng/Versions.g.props",
98+
createVersionsFileCommand,
5399
null ) );
54100
}
55-
101+
56102
// Add the main execution step
57103
buildSteps.Add(
58104
new PowerShellScriptBuildStep(
59105
"Exec",
60106
$"Execute {this.Script}",
61107
this.Script,
62108
this.Arguments,
63-
this.BuildAgentRequirements == null ? product.DockerSpec : this.BuildAgentRequirements.IsDockerized ? new DockerSpec( $"{productProperties.Product.ProductNameWithoutDot}-{productProperties.Product.ProductFamily.Version}-{this.Id}".ToLowerInvariant() ) : null,
64-
true ) );
109+
this.BuildAgentRequirements == null ? product.DockerSpec :
110+
this.BuildAgentRequirements.IsDockerized ? new DockerSpec(
111+
$"{productProperties.Product.ProductNameWithoutDot}-{productProperties.Product.ProductFamily.Version}-{this.Id}".ToLowerInvariant() ) :
112+
null,
113+
true )
114+
{
115+
#pragma warning disable CS0612 // Type or member is obsolete
116+
UseWsl = this.UseWsl || this.BuildAgentRequirements is ContainerHostRequirements { HostKind: ContainerHostKind.Wsl }
117+
#pragma warning restore CS0612 // Type or member is obsolete
118+
} );
65119

66120
// Build the configuration.
67121
var downstreamMergeConfiguration = new TeamCityBuildConfiguration(
@@ -82,9 +136,7 @@ internal override TeamCityBuildConfiguration TeamCityBuildConfiguration(
82136
SourceDependenciesRequirements.Full => productProperties.SourceDependencies,
83137
_ => throw new ArgumentOutOfRangeException()
84138
},
85-
SnapshotDependencies = buildConfiguration == null
86-
? null
87-
: [new TeamCitySnapshotDependency( buildConfiguration.ObjectName, false, $"+:{buildArtifactsDirectory}/**/*=>{buildArtifactsDirectory}" )]
139+
SnapshotDependencies = snapshotDependencies?.ToArray()
88140
};
89141

90142
return downstreamMergeConfiguration;

src/PostSharp.Engineering.BuildTools/ContinuousIntegration/TeamCity/BuildSteps/PowerShellCommandBuildStep.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details.
22

33
using PostSharp.Engineering.BuildTools.Docker;
4-
using System;
54
using System.IO;
65

76
namespace PostSharp.Engineering.BuildTools.ContinuousIntegration.TeamCity.BuildSteps;

src/PostSharp.Engineering.BuildTools/ContinuousIntegration/TeamCity/BuildSteps/PowerShellScriptBuildStep.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ internal class PowerShellScriptBuildStep : BuildStep
1818

1919
public string? WorkingDirectory { get; init; }
2020

21+
public bool UseWsl { get; init; }
22+
2123
public PowerShellScriptBuildStep(
2224
string id,
2325
string name,
@@ -57,7 +59,21 @@ public PowerShellScriptBuildStep(
5759

5860
public override string GenerateTeamCityCode()
5961
{
60-
return $@" powerShell {{
62+
if ( this.UseWsl )
63+
{
64+
return $@" powerShell {{
65+
name = ""{KotlinHelper.EscapeString( this.Name )}""
66+
id = ""{this.Id}""{(this.WorkingDirectory == null ? "" : $@"
67+
workingDir = ""{this.WorkingDirectory.Replace( Path.DirectorySeparatorChar, '/' )}""")}
68+
scriptMode = script {{
69+
content = ""wsl pwsh {KotlinHelper.EscapeString( this.ScriptPath )} {KotlinHelper.EscapeString( this.ScriptArguments )}""
70+
}}
71+
noProfile = false
72+
}}";
73+
}
74+
else
75+
{
76+
return $@" powerShell {{
6177
name = ""{KotlinHelper.EscapeString( this.Name )}""
6278
id = ""{this.Id}""{(this.WorkingDirectory == null ? "" : $@"
6379
workingDir = ""{this.WorkingDirectory.Replace( Path.DirectorySeparatorChar, '/' )}""")}
@@ -67,5 +83,6 @@ public override string GenerateTeamCityCode()
6783
noProfile = false
6884
scriptArgs = ""{KotlinHelper.EscapeString( this.ScriptArguments )}""
6985
}}";
86+
}
7087
}
7188
}

src/PostSharp.Engineering.BuildTools/Docker/ContainerHelper.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details.
2+
13
using System;
24

35
namespace PostSharp.Engineering.BuildTools.Docker;
@@ -8,7 +10,11 @@ public static string GetBuildAgentType( ContainerHostKind hostKind )
810
=> hostKind switch
911
{
1012
ContainerHostKind.Windows => "docker-win-x64-md",
13+
14+
#pragma warning disable CS0612 // Type or member is obsolete
1115
ContainerHostKind.Wsl => "docker-wsl-x64-md",
16+
#pragma warning restore CS0612 // Type or member is obsolete
17+
1218
_ => throw new ArgumentOutOfRangeException( nameof(hostKind) )
1319
};
1420
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details.
22

3+
using System;
4+
35
namespace PostSharp.Engineering.BuildTools.Docker;
46

57
public enum ContainerHostKind
68
{
79
Windows,
10+
11+
[Obsolete]
812
Wsl
913
}
Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
1-
using JetBrains.Annotations;
2-
using PostSharp.Engineering.BuildTools.ContinuousIntegration.Model;
3-
4-
namespace PostSharp.Engineering.BuildTools.Docker;
5-
6-
/// <summary>
7-
/// Indicates that the build script must execute on the Docker host (presumably, the script itself then initializes the container).
8-
/// </summary>
9-
[PublicAPI]
10-
public record ContainerHostRequirements : BuildAgentRequirements
11-
{
12-
public ContainerHostRequirements( ContainerHostKind hostKind ) : base( new BuildAgentRequirement( "env.BuildAgentType", ContainerHelper.GetBuildAgentType( hostKind ) ) ) { }
1+
// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details.
2+
3+
using JetBrains.Annotations;
4+
using PostSharp.Engineering.BuildTools.ContinuousIntegration.Model;
5+
6+
namespace PostSharp.Engineering.BuildTools.Docker;
7+
8+
/// <summary>
9+
/// Indicates that the build script must execute on the Docker host (presumably, the script itself then initializes the container).
10+
/// </summary>
11+
[PublicAPI]
12+
public record ContainerHostRequirements : BuildAgentRequirements
13+
{
14+
public ContainerHostKind HostKind { get; }
15+
16+
public ContainerHostRequirements( ContainerHostKind hostKind ) : base(
17+
new BuildAgentRequirement( "env.BuildAgentType", ContainerHelper.GetBuildAgentType( hostKind ) ) )
18+
{
19+
this.HostKind = hostKind;
20+
}
1321
}

0 commit comments

Comments
 (0)