Skip to content

Commit

Permalink
Add docker support
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanbrandenburg committed Sep 12, 2017
1 parent f7dbf22 commit 778b1a5
Show file tree
Hide file tree
Showing 10 changed files with 261 additions and 9 deletions.
8 changes: 5 additions & 3 deletions files/KoreBuild/KoreBuild.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ __korebuild_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$__korebuild_dir/scripts/common.sh"

# functions
default_tools_source='https://aspnetcore.blob.core.windows.net/buildtools'

set_korebuildsettings() {
tools_source=$1
Expand All @@ -13,7 +14,7 @@ set_korebuildsettings() {
local config_file="${4:-}" # optional. Not used yet.

[ -z "${dot_net_home:-}" ] && dot_net_home="$HOME/.dotnet"
[ -z "${tools_source:-}" ] && tools_source='https://aspnetcore.blob.core.windows.net/buildtools'
[ -z "${tools_source:-}" ] && tools_source="$default_tools_source"

return 0
}
Expand Down Expand Up @@ -71,10 +72,11 @@ install_tools() {

# Instructs MSBuild where to find .NET Framework reference assemblies
export ReferenceAssemblyRoot="$tools_home/netfx/$netfx_version"

# Call "sync" between "chmod" and execution to prevent "text file busy" error in Docker (aufs)
chmod +x "$__korebuild_dir/scripts/get-netfx.sh"; sync
"$__korebuild_dir/scripts/get-netfx.sh" $verbose_flag $netfx_version "$tools_source" "$ReferenceAssemblyRoot" \
# we don't include netfx in the BuildTools artifacts currently, it ends up on the blob store through other means, so we'll only look for it in the default_tools_source
"$__korebuild_dir/scripts/get-netfx.sh" $verbose_flag $netfx_version "$default_tools_source" "$ReferenceAssemblyRoot" \
|| return 1

# Call "sync" between "chmod" and execution to prevent "text file busy" error in Docker (aufs)
Expand Down
2 changes: 1 addition & 1 deletion files/KoreBuild/scripts/KoreBuild.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ function Invoke-KoreBuildCommand(
--tools-source $global:KoreBuildSettings.ToolsSource `
--dotnet-home $global:KoreBuildSettings.DotNetHome `
--repo-path $global:KoreBuildSettings.RepoPath `
$Arguments
@Arguments
}
}

Expand Down
19 changes: 19 additions & 0 deletions test/KoreBuild.FunctionalTests/SimpleRepoTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.IO;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using Xunit;
using Xunit.Abstractions;

Expand Down Expand Up @@ -55,5 +56,23 @@ public async Task BuildShouldReturnNonZeroCode()
Assert.Same(task, build);
Assert.NotEqual(0, build.Result);
}

[Fact]
public async Task DockerSuccessful()
{
var app = _fixture.CreateTestApp("SimpleRepo");
var platform = "jessie";
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
platform = "winservercore";
}

var build = app.ExecuteRun(_output, "docker-build", platform, "-Path", app.WorkingDirectory, "/p:BuildNumber=0001");
var task = await Task.WhenAny(build, Task.Delay(TimeSpan.FromMinutes(5)));

Assert.Same(task, build);

Assert.Equal(0, build.Result);
}
}
}
20 changes: 15 additions & 5 deletions test/KoreBuild.FunctionalTests/Utilities/TestApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,17 @@ public TestApp(string templateDir, string toolsSource, string source, string wor

public string WorkingDirectory { get; }

public async Task<int> ExecuteRun(ITestOutputHelper output, params string[] args)
{
return await ExecuteScript(output, "run", args);
}

public async Task<int> ExecuteBuild(ITestOutputHelper output, params string[] args)
{
return await ExecuteScript(output, "build", args);
}

private async Task<int> ExecuteScript(ITestOutputHelper output, string script, params string[] args)
{
output.WriteLine("Starting in " + WorkingDirectory);
void Write(object sender, DataReceivedEventArgs e)
Expand All @@ -41,22 +51,22 @@ void Write(object sender, DataReceivedEventArgs e)
{
cmd = "cmd.exe";
arguments.Add("/C");
arguments.Add(@".\build.cmd");
arguments.Add($@".\{script}.cmd");
}
else
{
cmd = "bash";
arguments.Add("./build.sh");
arguments.Add($"./{script}.sh");
}

arguments.AddRange(new[]
{
"-ToolsSource", _toolsSource,
"-Update"
"-s", _toolsSource,
"-u"
});

arguments.Add("/v:n");
arguments.AddRange(args);
arguments.Add("/v:n");

var process = new Process
{
Expand Down
182 changes: 182 additions & 0 deletions tools/KoreBuild.Console/Commands/DockerBuildCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using Microsoft.Extensions.CommandLineUtils;

namespace KoreBuild.Console.Commands
{
internal class DockerBuildCommand : SubCommandBase
{
private const string DockerIgnore = ".dockerignore";
private const string DockerfileExtension = ".dockerfile";
private const string Owner = "aspnetbuild";
private const string ImageName = "korebuild";

public CommandArgument ImageVariant { get; set; }

public List<string> Arguments {get; set; }

public string Tag => $@"{Owner}/{ImageName}:{ImageVariant.Value}";

public override void Configure(CommandLineApplication application)
{
ImageVariant = application.Argument("image", "The docker image to run on.");
Arguments = application.RemainingArguments;

base.Configure(application);
}

protected override bool IsValid()
{
if(string.IsNullOrEmpty(ImageVariant?.Value))
{
Reporter.Error("Image is a required argument.");
return false;
}

return true;
}

protected override int Execute()
{
var dockerFileName = GetDockerFileName(ImageVariant.Value);
var dockerFileSource = GetDockerFileSource(dockerFileName);
var dockerFileDestination = Path.Combine(RepoPath, GetDockerFileName(ImageVariant.Value));

File.Copy(dockerFileSource, dockerFileDestination, overwrite: true);

var dockerIgnoreSource = GetDockerFileSource(DockerIgnore);
var dockerIgnoreDestination = Path.Combine(RepoPath, DockerIgnore);

File.Copy(dockerIgnoreSource, dockerIgnoreDestination, overwrite: true);

// If our ToolSource isn't http copy it to the docker context
var dockerToolsSource = ToolsSource;
string toolsSourceDestination = null;
if (!ToolsSource.StartsWith("http"))
{
dockerToolsSource = "ToolsSource";
toolsSourceDestination = Path.Combine(RepoPath, dockerToolsSource);
DirectoryCopy(ToolsSource, toolsSourceDestination);
}

try
{
var buildArgs = new List<string> { "build" };

buildArgs.AddRange(new string[] { "-t", Tag, "-f", dockerFileDestination, RepoPath });
var buildResult = RunDockerCommand(buildArgs);

if (buildResult != 0)
{
return buildResult;
}

var containerName = $"{Owner}_{DateTime.Now.ToString("yyyyMMddHHmmss")}";

var runArgs = new List<string> { "run", "--rm", "-it", "--name", containerName, Tag };

runArgs.AddRange(new[] { "-ToolsSource", dockerToolsSource });

if (Arguments?.Count > 0)
{
runArgs.AddRange(Arguments);
}

Reporter.Verbose($"Running in container '{containerName}'");
return RunDockerCommand(runArgs);
}
finally{
// Clean up the stuff we dumped there in order to get it in the docker context.
File.Delete(dockerFileDestination);
File.Delete(dockerIgnoreDestination);
if(toolsSourceDestination != null)
{
Directory.Delete(toolsSourceDestination, recursive: true);
}
}
}

private string GetDockerFileSource(string fileName)
{
var executingDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var source = Path.Combine(executingDir, "Commands", "DockerFiles", fileName);

if(!File.Exists(source))
{
Reporter.Error($"DockerFile '{source}' doesn't exist.");
throw new FileNotFoundException();
}

return source;
}

private string GetDockerFileName(string platform)
{
return $"{platform}{DockerfileExtension}";
}

private int RunDockerCommand(List<string> arguments)
{
var args = ArgumentEscaper.EscapeAndConcatenate(arguments.ToArray());
Reporter.Verbose($"Running 'docker {args}'");

var psi = new ProcessStartInfo
{
FileName = "docker",
Arguments = args,
RedirectStandardError = true
};

var process = Process.Start(psi);
process.WaitForExit();

if(process.ExitCode != 0)
{
Reporter.Error(process.StandardError.ReadToEnd());
}

return process.ExitCode;
}

private static void DirectoryCopy(string sourceDirName, string destDirName)
{
// Get the subdirectories for the specified directory.
DirectoryInfo dir = new DirectoryInfo(sourceDirName);

if (!dir.Exists)
{
throw new DirectoryNotFoundException(
"Source directory does not exist or could not be found: "
+ sourceDirName);
}

DirectoryInfo[] dirs = dir.GetDirectories();
// If the destination directory doesn't exist, create it.
if (!Directory.Exists(destDirName))
{
Directory.CreateDirectory(destDirName);
}

// Get the files in the directory and copy them to the new location.
FileInfo[] files = dir.GetFiles();
foreach (FileInfo file in files)
{
string temppath = Path.Combine(destDirName, file.Name);
file.CopyTo(temppath, overwrite: true);
}

// Copy subdirectories and their contents to the new location.
foreach (DirectoryInfo subdir in dirs)
{
string temppath = Path.Combine(destDirName, subdir.Name);
DirectoryCopy(subdir.FullName, temppath);
}
}
}
}
4 changes: 4 additions & 0 deletions tools/KoreBuild.Console/Commands/DockerFiles/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
korebuild-lock.txt
**/bin
**/obj
artifacts
16 changes: 16 additions & 0 deletions tools/KoreBuild.Console/Commands/DockerFiles/jessie.dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM microsoft/dotnet:2.0-runtime-deps-jessie

RUN apt-get update \
&& apt-get install -y --no-install-recommends \
git \
# KoreBuild dependencies
curl \
unzip \
apt-transport-https \
&& rm -rf /var/lib/apt/lists/*

ADD . .

ENV DOTNET_SKIP_FIRST_TIME_EXPERIENCE=true

ENTRYPOINT ["./build.sh"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
FROM microsoft/aspnet:4.6.2

WORKDIR c:\\repo

# DevPack returns exit 0 immediately, but it's not done, so we wait.
# A more correct thing would be to block on a registry key existing or similar.
RUN \
Invoke-WebRequest https://download.microsoft.com/download/F/1/D/F1DEB8DB-D277-4EF9-9F48-3A65D4D8F965/NDP461-DevPack-KB3105179-ENU.exe -OutFile .\\net461dev.exe ; \
.\\net461dev.exe /Passive /NoRestart ; \
Start-Sleep -s 10; \
Remove-Item .\\net461dev.exe -Force ;

ADD . .

ENV DOTNET_SKIP_FIRST_TIME_EXPERIENCE=true

ENTRYPOINT ["build.cmd"]
1 change: 1 addition & 0 deletions tools/KoreBuild.Console/Commands/RootCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public override void Configure(CommandLineApplication application)

application.Command("install-tools", new InstallToolsCommand().Configure, throwOnUnexpectedArg:false);
application.Command("msbuild", new MSBuildCommand().Configure, throwOnUnexpectedArg:false);
application.Command("docker-build", new DockerBuildCommand().Configure, throwOnUnexpectedArg: false);

application.VersionOption("--version", GetVersion);

Expand Down
1 change: 1 addition & 0 deletions tools/KoreBuild.Console/KoreBuild.Console.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

<ItemGroup>
<Content Include="Commands\DockerFiles\*.dockerfile" CopyToPublishDirectory="PreserveNewest" CopyToOutputDirectory="PreserveNewest" />
<Content Include="Commands\DockerFiles\.dockerignore" CopyToPublishDirectory="PreserveNewest" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

</Project>

0 comments on commit 778b1a5

Please sign in to comment.