Skip to content

Commit 94b1bc9

Browse files
committed
Generation of Dockerfile.
1 parent 08b8332 commit 94b1bc9

23 files changed

+664
-74
lines changed

src/PostSharp.Engineering.BuildTools/Build/Model/Product.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,10 @@ public ParametrizedDependency[] ParametrizedDependencies
207207

208208
public IBumpStrategy BumpStrategy { get; init; } = new DefaultBumpStrategy();
209209

210-
internal bool UseDocker => this.ResolvedBuildAgentRequirements.IsDockerHost;
210+
internal bool UseDocker => this.ResolvedBuildAgentRequirements.IsDockerized;
211211

212212
internal DockerSpec? DockerSpec
213-
=> this.ResolvedBuildAgentRequirements.IsDockerHost
213+
=> this.ResolvedBuildAgentRequirements.IsDockerized
214214
? new DockerSpec( $"{this.ProductNameWithoutDot}-{this.ProductFamily.Version}".ToLowerInvariant() )
215215
: null;
216216

@@ -1456,7 +1456,7 @@ private static bool TryGenerateNuGetConfig( BuildContext context, DependenciesOv
14561456

14571457
continue;
14581458
}
1459-
1459+
14601460
var dependencyDefinition = product.GetDependencyDefinition( dependencySource.Key );
14611461
var parametrizedDependency = product.ParametrizedDependencies.Single( d => d.Name == dependencySource.Key );
14621462
var dependencyDirectory = Path.GetDirectoryName( dependencySource.Value.VersionFile )!;

src/PostSharp.Engineering.BuildTools/ContinuousIntegration/GenerateCiScriptsCommand.cs

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
using JetBrains.Annotations;
44
using PostSharp.Engineering.BuildTools.Build;
5+
using PostSharp.Engineering.BuildTools.Docker;
6+
using PostSharp.Engineering.BuildTools.Utilities;
57
using System;
68
using System.IO;
79
using System.Linq;
@@ -32,32 +34,17 @@ protected override bool ExecuteCore( BuildContext context, CommonCommandSettings
3234

3335
if ( product.UseDocker )
3436
{
35-
ExtractScript( "DockerBuild.ps1", "" );
36-
ExtractScript( "ReadSecrets.ps1", Path.Combine( product.EngineeringDirectory, "docker-context" ) );
37-
}
38-
39-
return true;
40-
41-
void ExtractScript( string fileName, string targetDirectory )
42-
{
43-
var targetPath = Path.Combine( context.RepoDirectory, targetDirectory, fileName );
44-
45-
using var resource = this.GetType().Assembly.GetManifestResourceStream( $"PostSharp.Engineering.BuildTools.Resources.{fileName}" )
46-
?? throw new InvalidOperationException( $"Cannot find the resource {fileName}." );
47-
48-
using var reader = new StreamReader( resource );
49-
var script = reader.ReadToEnd();
37+
EmbeddedResourceHelper.ExtractScript( context, "DockerBuild.ps1", "" );
38+
var image = (ContainerRequirements) product.OverriddenBuildAgentRequirements!;
5039

51-
script = script
52-
.Replace( "<ENG_PATH>", product.EngineeringDirectory, StringComparison.Ordinal )
53-
.Replace( "<ENVIRONMENT_VARIABLES>", string.Join( ",", EnvironmentVariableNames.All.OrderBy( x => x ) ), StringComparison.Ordinal );
54-
55-
if ( !File.Exists( targetPath ) || File.ReadAllText( targetPath ) != script )
40+
if ( !image.Prepare( context ) )
5641
{
57-
context.Console.WriteMessage( $"Writing '{targetPath}'." );
58-
59-
File.WriteAllText( targetPath, script );
42+
return false;
6043
}
6144
}
45+
46+
context.Console.WriteSuccess( "Generating build scripts was successful." );
47+
48+
return true;
6249
}
6350
}
Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
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 JetBrains.Annotations;
4+
using PostSharp.Engineering.BuildTools.Docker;
5+
using System;
46
using System.Collections.Generic;
57
using System.Linq;
68

@@ -9,26 +11,24 @@ namespace PostSharp.Engineering.BuildTools.ContinuousIntegration.Model;
911
public record BuildAgentRequirement( string Name, string Value );
1012

1113
[PublicAPI]
12-
public sealed record BuildAgentRequirements
14+
public record BuildAgentRequirements
1315
{
1416
public BuildAgentRequirements( params BuildAgentRequirement[] items )
1517
{
1618
this.Items = items;
1719
}
1820

1921
public static BuildAgentRequirements Empty { get; } = new();
20-
22+
2123
public static BuildAgentRequirements Default { get; } = SelfHosted( "caravela04cloud" );
22-
23-
public static BuildAgentRequirements WindowsDockerHost { get; } = SelfHosted( "docker-win-x64-md", true );
2424

25-
public static BuildAgentRequirements SelfHosted( string name, bool isDockerHost = false ) => new( new BuildAgentRequirement( "env.BuildAgentType", name ) ) { IsDockerHost = isDockerHost };
25+
public static BuildAgentRequirements SelfHosted( string name ) => new( new BuildAgentRequirement( "env.BuildAgentType", name ) );
2626

2727
public static BuildAgentRequirements JetBrainsHosted( string name ) => new( new BuildAgentRequirement( "teamcity.agent.name", name ) );
2828

2929
public IReadOnlyList<BuildAgentRequirement> Items { get; init; }
3030

3131
public BuildAgentRequirements Combine( BuildAgentRequirements other ) => new( this.Items.Concat( other.Items ).ToArray() );
32-
33-
public bool IsDockerHost { get; init; }
32+
33+
public virtual bool IsDockerized => false;
3434
}

src/PostSharp.Engineering.BuildTools/ContinuousIntegration/TeamCityHelper.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ public static class TeamCityHelper
2929
public const string TeamCityApiRunningBuildsPath = "/app/rest/builds?locator=state:running";
3030
public const string TeamCityApiFinishedBuildsPath = "/app/rest/builds?locator=state:finished";
3131

32-
public static bool IsTeamCityBuild( CommonCommandSettings settings )
33-
=> settings.SimulateContinuousIntegration
34-
|| Environment.GetEnvironmentVariable( EnvironmentVariableNames.IsTeamCityAgent )?.ToLowerInvariant() == "true";
32+
public static bool IsTeamCityBuild( CommonCommandSettings? settings = null )
33+
=> settings?.SimulateContinuousIntegration == true
34+
|| Environment.GetEnvironmentVariable( EnvironmentVariableNames.IsTeamCityAgent )?.ToLowerInvariant() is "true" or "1";
3535

3636
public static ImmutableDictionary<string, string?> GetSimulatedContinuousIntegrationEnvironmentVariables( CommonCommandSettings settings )
3737
{
@@ -653,7 +653,7 @@ private static void WriteIfDiffers( BuildContext context, Action<TextWriter> wri
653653
resultMessage = $"File '{filePath}' was up to date.";
654654
}
655655

656-
context.Console.WriteSuccess( $"{name} generated. {resultMessage}" );
656+
context.Console.WriteMessage( $"{name} generated. {resultMessage}" );
657657
}
658658

659659
internal static bool TryGenerateConsolidatedTeamcityConfiguration( BuildContext context )
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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 System.IO;
4+
5+
namespace PostSharp.Engineering.BuildTools.Docker;
6+
7+
public class AzureCliComponent : ContainerComponent
8+
{
9+
public override string Name => "Install Azure CLI";
10+
11+
public override ContainerComponentKind Kind => ContainerComponentKind.AzureCli;
12+
13+
public override void WriteDockerfile( StreamWriter writer )
14+
{
15+
writer.WriteLine(
16+
"""
17+
RUN Invoke-WebRequest -Uri https://aka.ms/installazurecliwindowsx64 -OutFile AzureCLI.msi; `
18+
$process = Start-Process msiexec.exe -Wait -PassThru -ArgumentList '/I AzureCLI.msi /quiet'; `
19+
if ($process.ExitCode -ne 0) { exit $process.ExitCode }; `
20+
Remove-Item AzureCLI.msi
21+
""" );
22+
}
23+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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 PostSharp.Engineering.BuildTools.Build;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.IO;
7+
8+
namespace PostSharp.Engineering.BuildTools.Docker;
9+
10+
public abstract class ContainerComponent : IComparable<ContainerComponent>
11+
{
12+
public abstract string Name { get; }
13+
14+
public abstract ContainerComponentKind Kind { get; }
15+
16+
public abstract void WriteDockerfile( StreamWriter writer );
17+
18+
public virtual void AddRequirements( IReadOnlyList<ContainerComponent> components, Action<ContainerComponent> add ) { }
19+
20+
public virtual bool Validate( BuildContext context, string contextDirectory ) => true;
21+
22+
public virtual int CompareTo( ContainerComponent? other )
23+
{
24+
if ( ReferenceEquals( this, other ) )
25+
{
26+
return 0;
27+
}
28+
29+
if ( other == null )
30+
{
31+
return 1;
32+
}
33+
34+
return ((int) this.Kind).CompareTo( (int) other.Kind );
35+
}
36+
37+
public virtual void PopulateContextDirectory( BuildContext context, string directory ) { }
38+
39+
public override string ToString() => this.Kind.ToString();
40+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details.
2+
3+
namespace PostSharp.Engineering.BuildTools.Docker;
4+
5+
public enum ContainerComponentKind
6+
{
7+
// Order matters and determines the order of execution in Dockerfile.
8+
Prolog,
9+
Git,
10+
Powershell,
11+
AzureCli,
12+
DotNetInstaller,
13+
DotNet,
14+
VsBuildTools,
15+
Epilogue
16+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details.
2+
3+
namespace PostSharp.Engineering.BuildTools.Docker;
4+
5+
public enum ContainerHostKind
6+
{
7+
Windows
8+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
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 PostSharp.Engineering.BuildTools.Build;
4+
using PostSharp.Engineering.BuildTools.ContinuousIntegration.Model;
5+
using System;
6+
using System.Collections.Generic;
7+
using System.IO;
8+
using System.Linq;
9+
10+
namespace PostSharp.Engineering.BuildTools.Docker;
11+
12+
public record ContainerRequirements : BuildAgentRequirements
13+
{
14+
public ContainerRequirements( ContainerHostKind hostKind ) : base( new BuildAgentRequirement( "env.BuildAgentType", GetBuildAgentType( hostKind ) ) ) { }
15+
16+
public ContainerComponent[] Components { get; init; } = [];
17+
18+
private static string GetBuildAgentType( ContainerHostKind hostKind )
19+
=> hostKind switch
20+
{
21+
ContainerHostKind.Windows => "docker-win-x64-md",
22+
_ => throw new ArgumentOutOfRangeException( nameof(hostKind) )
23+
};
24+
25+
public override bool IsDockerized => true;
26+
27+
public bool Prepare( BuildContext context )
28+
{
29+
var contextDirectory = Path.Combine( context.RepoDirectory, context.Product.EngineeringDirectory, "docker-context" );
30+
31+
Directory.CreateDirectory( contextDirectory );
32+
33+
// Add components.
34+
var allComponents = new List<ContainerComponent>();
35+
allComponents.Add( new PrologComponent() );
36+
allComponents.Add( new PowershellComponent() );
37+
allComponents.Add( new GitComponent() );
38+
allComponents.Add( new EpilogueComponent() );
39+
allComponents.AddRange( this.Components );
40+
41+
// Add required components.
42+
foreach ( var component in allComponents.ToList() )
43+
{
44+
void Add( ContainerComponent c )
45+
{
46+
allComponents.Add( c );
47+
c.AddRequirements( allComponents, Add );
48+
}
49+
50+
component.AddRequirements( allComponents, Add );
51+
}
52+
53+
// Validate components.
54+
foreach ( var component in allComponents )
55+
{
56+
if ( !component.Validate( context, contextDirectory ) )
57+
{
58+
return false;
59+
}
60+
}
61+
62+
// Order components.
63+
var orderedComponents = allComponents.OrderBy( x => x ).ToList();
64+
65+
var dockerfilePath = Path.Combine( context.RepoDirectory, "Dockerfile" );
66+
context.Console.WriteMessage( $"Writing '{dockerfilePath}'." );
67+
using var dockerfile = File.CreateText( dockerfilePath );
68+
69+
foreach ( var component in orderedComponents )
70+
{
71+
context.Console.WriteMessage( $"Processing container component '{component.Name}'." );
72+
73+
if ( component.Kind != ContainerComponentKind.Prolog )
74+
{
75+
dockerfile.WriteLine();
76+
dockerfile.WriteLine();
77+
dockerfile.WriteLine( $"# {component.Name}" );
78+
}
79+
80+
component.PopulateContextDirectory( context, contextDirectory );
81+
component.WriteDockerfile( dockerfile );
82+
}
83+
84+
return true;
85+
}
86+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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 System;
4+
using System.Collections.Generic;
5+
using System.IO;
6+
using System.Linq;
7+
8+
namespace PostSharp.Engineering.BuildTools.Docker;
9+
10+
public class DotNetComponent : ContainerComponent
11+
{
12+
public string Version { get; }
13+
14+
public DotNetComponentKind DotNetComponentKind { get; }
15+
16+
public DotNetComponent( string version, DotNetComponentKind dotNetComponentKind )
17+
{
18+
this.Version = version;
19+
this.DotNetComponentKind = dotNetComponentKind;
20+
}
21+
22+
public override string Name => $"Install .NET {this.DotNetComponentKind} {this.Version}";
23+
24+
public override ContainerComponentKind Kind => ContainerComponentKind.DotNet;
25+
26+
public override void AddRequirements( IReadOnlyList<ContainerComponent> components, Action<ContainerComponent> add )
27+
{
28+
if ( !components.OfType<DotNetInstallerComponent>().Any() )
29+
{
30+
add( new DotNetInstallerComponent() );
31+
}
32+
}
33+
34+
public override void WriteDockerfile( StreamWriter writer )
35+
{
36+
if ( this.DotNetComponentKind == DotNetComponentKind.Sdk )
37+
{
38+
writer.WriteLine(
39+
$"""
40+
RUN powershell -ExecutionPolicy Bypass -File dotnet-install.ps1 -Version {this.Version} -InstallDir 'C:\Program Files\dotnet';
41+
""" );
42+
}
43+
else
44+
{
45+
var runtime = this.DotNetComponentKind switch
46+
{
47+
DotNetComponentKind.Runtime => "dotnet",
48+
_ => throw new InvalidOperationException()
49+
};
50+
51+
writer.WriteLine(
52+
$"""
53+
RUN powershell -ExecutionPolicy Bypass -File dotnet-install.ps1 -Version {this.Version} -Runtime {runtime} -InstallDir 'C:\Program Files\dotnet';
54+
""" );
55+
}
56+
}
57+
58+
public override string ToString() => $"{this.Kind} {this.DotNetComponentKind} {this.Version}";
59+
60+
61+
public override int CompareTo( ContainerComponent? other )
62+
{
63+
var compareBase = base.CompareTo( other );
64+
65+
if ( compareBase != 0 )
66+
{
67+
return compareBase;
68+
}
69+
70+
var otherDotNetComponent = (DotNetComponent) other!;
71+
72+
return string.Compare( this.Version, otherDotNetComponent.Version, StringComparison.Ordinal );
73+
}
74+
}

0 commit comments

Comments
 (0)