From 7869c96d800b5a2fcd9101d622e1487f48c4a326 Mon Sep 17 00:00:00 2001 From: Kiran Challa Date: Wed, 8 May 2019 13:47:12 -0700 Subject: [PATCH] Enable supplying commands directly to the pre and post build script envrionment variables (#124) --- .../BaseBashBuildScript.sh.tpl | 16 ++-- .../BaseBashBuildScriptProperties.cs | 16 ++-- .../DefaultBuildScriptGenerator.cs | 15 +++- .../DefaultEnvironmentSettingsProvider.cs | 40 +++++----- .../DotNetCoreBashBuildSnippet.sh.tpl | 12 +-- .../DotnetCoreBashBuildSnippetProperties.cs | 4 +- .../DotNetCore/DotnetCorePlatform.cs | 21 +++++- .../EnvironmentSettings.cs | 6 ++ .../EnvironmentSettingsKeys.cs | 13 +++- .../PreAndPostCommandScriptHelper.cs | 74 +++++++++++++++++++ .../Commands/BuildCommand.cs | 8 +- .../BaseBashBuildScriptTests.cs | 8 +- .../DefaultEnvironmentSettingsProviderTest.cs | 47 ++++++++++++ .../DotNetCoreSampleAppsTest.cs | 43 +++++++++++ .../PythonSampleAppsTest.cs | 42 +++++++++++ tests/Oryx.Tests.Common/EndToEndTestHelper.cs | 59 ++++++++++++--- 16 files changed, 354 insertions(+), 70 deletions(-) create mode 100644 src/BuildScriptGenerator/PreAndPostCommandScriptHelper.cs diff --git a/src/BuildScriptGenerator/BaseBashBuildScript.sh.tpl b/src/BuildScriptGenerator/BaseBashBuildScript.sh.tpl index 18b15e623..cd398986c 100644 --- a/src/BuildScriptGenerator/BaseBashBuildScript.sh.tpl +++ b/src/BuildScriptGenerator/BaseBashBuildScript.sh.tpl @@ -67,12 +67,12 @@ fi export SOURCE_DIR export DESTINATION_DIR -{{ if PreBuildScriptPath | IsNotBlank }} +{{ if PreBuildCommand | IsNotBlank }} # Make sure to cd to the source directory so that the pre-build script runs from there cd "$SOURCE_DIR" -echo "{{ PreBuildScriptPrologue }}" -"{{ PreBuildScriptPath }}" -echo "{{ PreBuildScriptEpilogue }}" +echo "{{ PreBuildCommandPrologue }}" +{{ PreBuildCommand }} +echo "{{ PreBuildCommandEpilogue }}" {{ end }} {{ for Snippet in BuildScriptSnippets }} @@ -81,12 +81,12 @@ cd "$SOURCE_DIR" {{~ Snippet }} {{ end }} -{{ if PostBuildScriptPath | IsNotBlank }} +{{ if PostBuildCommand | IsNotBlank }} # Make sure to cd to the source directory so that the post-build script runs from there cd $SOURCE_DIR -echo "{{ PostBuildScriptPrologue }}" -"{{ PostBuildScriptPath }}" -echo "{{ PostBuildScriptEpilogue }}" +echo "{{ PostBuildCommandPrologue }}" +{{ PostBuildCommand }} +echo "{{ PostBuildCommandEpilogue }}" {{ end }} if [ "$SOURCE_DIR" != "$DESTINATION_DIR" ] diff --git a/src/BuildScriptGenerator/BaseBashBuildScriptProperties.cs b/src/BuildScriptGenerator/BaseBashBuildScriptProperties.cs index 2a0d33b5b..7044fb80a 100644 --- a/src/BuildScriptGenerator/BaseBashBuildScriptProperties.cs +++ b/src/BuildScriptGenerator/BaseBashBuildScriptProperties.cs @@ -9,11 +9,11 @@ namespace Microsoft.Oryx.BuildScriptGenerator { public class BaseBashBuildScriptProperties { - public const string PreBuildScriptPrologue = "Executing pre-build script..."; - public const string PreBuildScriptEpilogue = "Finished executing pre-build script."; + public const string PreBuildCommandPrologue = "Executing pre-build command..."; + public const string PreBuildCommandEpilogue = "Finished executing pre-build command."; - public const string PostBuildScriptPrologue = "Executing post-build script..."; - public const string PostBuildScriptEpilogue = "Finished executing post-build script."; + public const string PostBuildCommandPrologue = "Executing post-build command..."; + public const string PostBuildCommandEpilogue = "Finished executing post-build command."; /// /// Gets or sets the collection of build script snippets. @@ -21,9 +21,9 @@ namespace Microsoft.Oryx.BuildScriptGenerator public IEnumerable BuildScriptSnippets { get; set; } /// - /// Gets or sets the path to the pre build script. + /// Gets or sets the the pre build script content /// - public string PreBuildScriptPath { get; set; } + public string PreBuildCommand { get; set; } /// /// Gets or sets the argument to the benv command. @@ -31,9 +31,9 @@ namespace Microsoft.Oryx.BuildScriptGenerator public string BenvArgs { get; set; } /// - /// Gets or sets the path to the post build script. + /// Gets or sets the path to the post build script content. /// - public string PostBuildScriptPath { get; set; } + public string PostBuildCommand { get; set; } public IEnumerable DirectoriesToExcludeFromCopyToBuildOutputDir { get; set; } diff --git a/src/BuildScriptGenerator/DefaultBuildScriptGenerator.cs b/src/BuildScriptGenerator/DefaultBuildScriptGenerator.cs index af02d8e25..400b71f37 100644 --- a/src/BuildScriptGenerator/DefaultBuildScriptGenerator.cs +++ b/src/BuildScriptGenerator/DefaultBuildScriptGenerator.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using Microsoft.Extensions.Logging; using Microsoft.Oryx.BuildScriptGenerator.Exceptions; @@ -69,6 +70,7 @@ namespace Microsoft.Oryx.BuildScriptGenerator directoriesToExcludeFromCopyToBuildOutputDir.Add(".git"); script = BuildScriptFromSnippets( + context.SourceRepo, snippets, toolsToVersion, directoriesToExcludeFromCopyToIntermediateDir, @@ -249,6 +251,7 @@ namespace Microsoft.Oryx.BuildScriptGenerator /// /// Finalized build script as a string. private string BuildScriptFromSnippets( + ISourceRepo sourceRepo, IList snippets, Dictionary toolsToVersion, List directoriesToExcludeFromCopyToIntermediateDir, @@ -263,20 +266,24 @@ namespace Microsoft.Oryx.BuildScriptGenerator .SelectMany(s => s.BuildProperties) .ToDictionary(p => p.Key, p => p.Value); + (var preBuildCommand, var postBuildCommand) = PreAndPostBuildCommandHelper.GetPreAndPostBuildCommands( + sourceRepo, + environmentSettings); + var buildScriptProps = new BaseBashBuildScriptProperties() { BuildScriptSnippets = snippets.Select(s => s.BashBuildScriptSnippet), BenvArgs = benvArgs, - PreBuildScriptPath = environmentSettings?.PreBuildScriptPath, - PostBuildScriptPath = environmentSettings?.PostBuildScriptPath, + PreBuildCommand = preBuildCommand, + PostBuildCommand = postBuildCommand, DirectoriesToExcludeFromCopyToIntermediateDir = directoriesToExcludeFromCopyToIntermediateDir, DirectoriesToExcludeFromCopyToBuildOutputDir = directoriesToExcludeFromCopyToBuildOutputDir, ManifestFileName = Constants.ManifestFileName, BuildProperties = buildProperties }; - LogScriptIfGiven("pre-build", buildScriptProps.PreBuildScriptPath); - LogScriptIfGiven("post-build", buildScriptProps.PostBuildScriptPath); + LogScriptIfGiven("pre-build", buildScriptProps.PreBuildCommand); + LogScriptIfGiven("post-build", buildScriptProps.PostBuildCommand); script = TemplateHelpers.Render( TemplateHelpers.TemplateResource.BaseBashScript, diff --git a/src/BuildScriptGenerator/DefaultEnvironmentSettingsProvider.cs b/src/BuildScriptGenerator/DefaultEnvironmentSettingsProvider.cs index e821d7a88..4430b5da4 100644 --- a/src/BuildScriptGenerator/DefaultEnvironmentSettingsProvider.cs +++ b/src/BuildScriptGenerator/DefaultEnvironmentSettingsProvider.cs @@ -139,39 +139,36 @@ namespace Microsoft.Oryx.BuildScriptGenerator { var environmentSettings = new EnvironmentSettings { - PreBuildScriptPath = GetPath(EnvironmentSettingsKeys.PreBuildScriptPath), - PostBuildScriptPath = GetPath(EnvironmentSettingsKeys.PostBuildScriptPath) + PreBuildScriptPath = GetScriptAbsolutePath(TrimValue(EnvironmentSettingsKeys.PreBuildScriptPath)), + PostBuildScriptPath = GetScriptAbsolutePath(TrimValue(EnvironmentSettingsKeys.PostBuildScriptPath)) }; - if (!string.IsNullOrEmpty(environmentSettings.PreBuildScriptPath)) - { - environmentSettings.PreBuildScriptPath = GetScriptAbsolutePath(environmentSettings.PreBuildScriptPath); - } - - if (!string.IsNullOrEmpty(environmentSettings.PostBuildScriptPath)) - { - environmentSettings.PostBuildScriptPath = GetScriptAbsolutePath( - environmentSettings.PostBuildScriptPath); - } + environmentSettings.PreBuildCommand = TrimValue(EnvironmentSettingsKeys.PreBuildCommand); + environmentSettings.PostBuildCommand = TrimValue(EnvironmentSettingsKeys.PostBuildCommand); return environmentSettings; - string GetPath(string name) + string TrimValue(string key) { - var path = GetValue(name); - if (string.IsNullOrEmpty(path)) + var value = GetValue(key); + if (string.IsNullOrEmpty(value)) { return null; } - path = path.Trim(); + value = value.Trim(); var quote = '"'; - if (path.StartsWith(quote) && path.EndsWith(quote)) + + if (value.Length > 1 && value.StartsWith(quote) && value.EndsWith(quote)) { - return path.Trim(quote); + value = value.Remove(0, 1); + if (value.Length > 0) + { + value = value.Remove(value.Length - 1); + } } - return path; + return value; } string GetValue(string name) @@ -194,6 +191,11 @@ namespace Microsoft.Oryx.BuildScriptGenerator string GetScriptAbsolutePath(string path) { + if (string.IsNullOrEmpty(path)) + { + return null; + } + if (!Path.IsPathFullyQualified(path)) { path = Path.Combine(_sourceRepo.RootPath, path); diff --git a/src/BuildScriptGenerator/DotNetCore/DotNetCoreBashBuildSnippet.sh.tpl b/src/BuildScriptGenerator/DotNetCore/DotNetCoreBashBuildSnippet.sh.tpl index 5b5e78e5b..60b68b45d 100644 --- a/src/BuildScriptGenerator/DotNetCore/DotNetCoreBashBuildSnippet.sh.tpl +++ b/src/BuildScriptGenerator/DotNetCore/DotNetCoreBashBuildSnippet.sh.tpl @@ -92,10 +92,10 @@ fi # Make sure to create the destination dir so that pre-build script has access to it mkdir -p "$DESTINATION_DIR" -{{ if PreBuildScriptPath | IsNotBlank }} +{{ if PreBuildCommand | IsNotBlank }} # Make sure to cd to the source directory so that the pre-build script runs from there cd "$SOURCE_DIR" -"{{ PreBuildScriptPath }}" +{{ PreBuildCommand }} {{ end }} echo @@ -112,10 +112,10 @@ dotnet restore "{{ ProjectFile }}" {{ if ZipAllOutput }} publishToDirectory "$tmpDestinationPublishDir" - {{ if PostBuildScriptPath | IsNotBlank }} + {{ if PostBuildCommand | IsNotBlank }} # Make sure to cd to the source directory so that the post-build script runs from there cd "$SOURCE_DIR" - "{{ PostBuildScriptPath }}" + {{ PostBuildCommand }} {{ end }} # Zip only the contents and not the parent directory @@ -129,10 +129,10 @@ dotnet restore "{{ ProjectFile }}" {{ else }} publishToDirectory "$DESTINATION_DIR" - {{ if PostBuildScriptPath | IsNotBlank }} + {{ if PostBuildCommand | IsNotBlank }} # Make sure to cd to the source directory so that the post-build script runs from there cd $SOURCE_DIR - "{{ PostBuildScriptPath }}" + {{ PostBuildCommand }} {{ end }} {{ end }} diff --git a/src/BuildScriptGenerator/DotNetCore/DotnetCoreBashBuildSnippetProperties.cs b/src/BuildScriptGenerator/DotNetCore/DotnetCoreBashBuildSnippetProperties.cs index b26fa13f3..c2571b5e8 100644 --- a/src/BuildScriptGenerator/DotNetCore/DotnetCoreBashBuildSnippetProperties.cs +++ b/src/BuildScriptGenerator/DotNetCore/DotnetCoreBashBuildSnippetProperties.cs @@ -20,9 +20,9 @@ namespace Microsoft.Oryx.BuildScriptGenerator.DotNetCore public string BenvArgs { get; set; } - public string PreBuildScriptPath { get; set; } + public string PreBuildCommand { get; set; } - public string PostBuildScriptPath { get; set; } + public string PostBuildCommand { get; set; } public bool ZipAllOutput { get; set; } diff --git a/src/BuildScriptGenerator/DotNetCore/DotnetCorePlatform.cs b/src/BuildScriptGenerator/DotNetCore/DotnetCorePlatform.cs index 951fe9c74..3f010d6f9 100644 --- a/src/BuildScriptGenerator/DotNetCore/DotnetCorePlatform.cs +++ b/src/BuildScriptGenerator/DotNetCore/DotnetCorePlatform.cs @@ -76,6 +76,10 @@ namespace Microsoft.Oryx.BuildScriptGenerator.DotNetCore _environmentSettingsProvider.TryGetAndLoadSettings(out var environmentSettings); + (var preBuildCommand, var postBuildCommand) = PreAndPostBuildCommandHelper.GetPreAndPostBuildCommands( + context.SourceRepo, + environmentSettings); + var templateProperties = new DotNetCoreBashBuildSnippetProperties { ProjectFile = projectFile, @@ -84,8 +88,8 @@ namespace Microsoft.Oryx.BuildScriptGenerator.DotNetCore BenvArgs = $"dotnet={context.DotnetCoreVersion}", DirectoriesToExcludeFromCopyToIntermediateDir = GetDirectoriesToExcludeFromCopyToIntermediateDir( context), - PreBuildScriptPath = environmentSettings?.PreBuildScriptPath, - PostBuildScriptPath = environmentSettings?.PostBuildScriptPath, + PreBuildCommand = preBuildCommand, + PostBuildCommand = postBuildCommand, ManifestFileName = Constants.ManifestFileName, ZipAllOutput = zipAllOutput, }; @@ -177,5 +181,18 @@ namespace Microsoft.Oryx.BuildScriptGenerator.DotNetCore DotnetCoreConstants.OryxOutputPublishDirectory); return (projectFile, publishDir); } + + private string GetCommandOrScript(string commandOrScript) + { + if (!string.IsNullOrEmpty(commandOrScript)) + { + if (File.Exists(commandOrScript)) + { + return $"\"{commandOrScript}\""; + } + } + + return commandOrScript; + } } } \ No newline at end of file diff --git a/src/BuildScriptGenerator/EnvironmentSettings.cs b/src/BuildScriptGenerator/EnvironmentSettings.cs index 8093f5140..c598d3de6 100644 --- a/src/BuildScriptGenerator/EnvironmentSettings.cs +++ b/src/BuildScriptGenerator/EnvironmentSettings.cs @@ -7,8 +7,14 @@ namespace Microsoft.Oryx.BuildScriptGenerator { public class EnvironmentSettings { + // Note: The following two properties exist so that we do not break + // existing users who might still be using them public string PreBuildScriptPath { get; set; } public string PostBuildScriptPath { get; set; } + + public string PreBuildCommand { get; set; } + + public string PostBuildCommand { get; set; } } } diff --git a/src/BuildScriptGenerator/EnvironmentSettingsKeys.cs b/src/BuildScriptGenerator/EnvironmentSettingsKeys.cs index 8bc69d161..5790d7d7c 100644 --- a/src/BuildScriptGenerator/EnvironmentSettingsKeys.cs +++ b/src/BuildScriptGenerator/EnvironmentSettingsKeys.cs @@ -7,10 +7,21 @@ namespace Microsoft.Oryx.BuildScriptGenerator { public static class EnvironmentSettingsKeys { + // Note: The following two constants exist so that we do not break + // existing users who might still be using them public const string PreBuildScriptPath = "PRE_BUILD_SCRIPT_PATH"; - public const string PostBuildScriptPath = "POST_BUILD_SCRIPT_PATH"; + /// + /// Represents an line script or a path to a file + /// + public const string PreBuildCommand = "PRE_BUILD_COMMAND"; + + /// + /// Represents an line script or a path to a file + /// + public const string PostBuildCommand = "POST_BUILD_COMMAND"; + public const string DotnetCoreDefaultVersion = "ORYX_DOTNETCORE_DEFAULT_VERSION"; public const string DotnetCoreSupportedVersions = "DOTNETCORE_SUPPORTED_VERSIONS"; diff --git a/src/BuildScriptGenerator/PreAndPostCommandScriptHelper.cs b/src/BuildScriptGenerator/PreAndPostCommandScriptHelper.cs new file mode 100644 index 000000000..f3e670710 --- /dev/null +++ b/src/BuildScriptGenerator/PreAndPostCommandScriptHelper.cs @@ -0,0 +1,74 @@ +// -------------------------------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +// -------------------------------------------------------------------------------------------- + +using System.IO; + +namespace Microsoft.Oryx.BuildScriptGenerator +{ + internal static class PreAndPostBuildCommandHelper + { + public static (string preBuildCommand, string postBuildCommand) GetPreAndPostBuildCommands( + ISourceRepo sourceRepo, + EnvironmentSettings settings) + { + if (settings == null) + { + return (null, null); + } + + string preBuildCommand = null; + string postBuildCommand = null; + + if (!string.IsNullOrEmpty(settings.PreBuildScriptPath)) + { + preBuildCommand = $"\"{settings.PreBuildScriptPath}\""; + } + else if (!string.IsNullOrEmpty(settings.PreBuildCommand)) + { + preBuildCommand = GetCommandOrFilePath(sourceRepo, settings.PreBuildCommand); + } + + if (!string.IsNullOrEmpty(settings.PostBuildScriptPath)) + { + postBuildCommand = $"\"{settings.PostBuildScriptPath}\""; + } + else if (!string.IsNullOrEmpty(settings.PostBuildCommand)) + { + postBuildCommand = GetCommandOrFilePath(sourceRepo, settings.PostBuildCommand); + } + + return (preBuildCommand: preBuildCommand, postBuildCommand: postBuildCommand); + } + + private static string GetCommandOrFilePath(ISourceRepo sourceRepo, string commandOrScriptPath) + { + if (string.IsNullOrEmpty(commandOrScriptPath)) + { + return null; + } + + string fullyQualifiedPath = null; + if (Path.IsPathFullyQualified(commandOrScriptPath)) + { + fullyQualifiedPath = commandOrScriptPath; + } + else + { + fullyQualifiedPath = Path.Combine(sourceRepo.RootPath, commandOrScriptPath); + } + + if (fullyQualifiedPath != null) + { + var fullPath = Path.GetFullPath(fullyQualifiedPath); + if (File.Exists(Path.GetFullPath(fullPath))) + { + return $"\"{fullPath}\""; + } + } + + return commandOrScriptPath; + } + } +} diff --git a/src/BuildScriptGeneratorCli/Commands/BuildCommand.cs b/src/BuildScriptGeneratorCli/Commands/BuildCommand.cs index d709e8662..1d0225ec5 100644 --- a/src/BuildScriptGeneratorCli/Commands/BuildCommand.cs +++ b/src/BuildScriptGeneratorCli/Commands/BuildCommand.cs @@ -27,12 +27,12 @@ namespace Microsoft.Oryx.BuildScriptGeneratorCli { new TextSpan( "RunPreBuildScript", - BaseBashBuildScriptProperties.PreBuildScriptPrologue, - BaseBashBuildScriptProperties.PreBuildScriptEpilogue), + BaseBashBuildScriptProperties.PreBuildCommandPrologue, + BaseBashBuildScriptProperties.PreBuildCommandEpilogue), new TextSpan( "RunPostBuildScript", - BaseBashBuildScriptProperties.PostBuildScriptPrologue, - BaseBashBuildScriptProperties.PostBuildScriptEpilogue) + BaseBashBuildScriptProperties.PostBuildCommandPrologue, + BaseBashBuildScriptProperties.PostBuildCommandEpilogue) }; [Argument(0, Description = "The source directory.")] diff --git a/tests/BuildScriptGenerator.Tests/BaseBashBuildScriptTests.cs b/tests/BuildScriptGenerator.Tests/BaseBashBuildScriptTests.cs index 2e4f99fcd..9bb9f07d5 100644 --- a/tests/BuildScriptGenerator.Tests/BaseBashBuildScriptTests.cs +++ b/tests/BuildScriptGenerator.Tests/BaseBashBuildScriptTests.cs @@ -42,17 +42,17 @@ namespace Microsoft.Oryx.BuildScriptGenerator.Tests const string script2 = "hijklmn"; var scriptProps = new BaseBashBuildScriptProperties() { - PreBuildScriptPath = script1, - PostBuildScriptPath = script2 + PreBuildCommand = script1, + PostBuildCommand = script2 }; // Act var script = TemplateHelpers.Render(TemplateHelpers.TemplateResource.BaseBashScript, scriptProps); // Assert - Assert.Contains("Executing pre-build script", script); + Assert.Contains("Executing pre-build command", script); Assert.Contains(script1, script); - Assert.Contains("Executing post-build script", script); + Assert.Contains("Executing post-build command", script); Assert.Contains(script2, script); } } diff --git a/tests/BuildScriptGenerator.Tests/DefaultEnvironmentSettingsProviderTest.cs b/tests/BuildScriptGenerator.Tests/DefaultEnvironmentSettingsProviderTest.cs index 067598d7d..9dcf1b8dc 100644 --- a/tests/BuildScriptGenerator.Tests/DefaultEnvironmentSettingsProviderTest.cs +++ b/tests/BuildScriptGenerator.Tests/DefaultEnvironmentSettingsProviderTest.cs @@ -22,6 +22,53 @@ namespace Microsoft.Oryx.BuildScriptGenerator.Tests _tempDirRoot = tempDirFixture.RootDirPath; } + [Fact] + public void TryGetAndLoadSettings_TrimsQuotesAndWhitespace() + { + // Arrange + var sourceDir = CreateNewDir(); + var scriptFile = Path.Combine(sourceDir, "a b.sh"); + File.Create(scriptFile); + var testEnvironment = new TestEnvironment(); + testEnvironment.SetEnvironmentVariable(EnvironmentSettingsKeys.PreBuildCommand, " \" a b c \" "); + testEnvironment.SetEnvironmentVariable( + EnvironmentSettingsKeys.PreBuildScriptPath, + $" \"{scriptFile}\" "); + testEnvironment.SetEnvironmentVariable(EnvironmentSettingsKeys.PostBuildCommand, " \" a b c \" "); + testEnvironment.SetEnvironmentVariable( + EnvironmentSettingsKeys.PostBuildScriptPath, + $" \"{scriptFile}\" "); + var provider = CreateProvider(sourceDir, testEnvironment); + + // Act + provider.TryGetAndLoadSettings(out var settings); + + // Assert + Assert.Equal(" a b c ", settings.PreBuildCommand); + Assert.Equal(scriptFile, settings.PreBuildScriptPath); + Assert.Equal(" a b c ", settings.PostBuildCommand); + Assert.Equal(scriptFile, settings.PostBuildScriptPath); + } + + [Theory] + [InlineData("\"")] + [InlineData("a\"")] + [InlineData("a\"\"")] + public void TryGetAndLoadSettings_TrimsOnlyWhenMatchingQuotesAreFound(string value) + { + // Arrange + var sourceDir = CreateNewDir(); + var testEnvironment = new TestEnvironment(); + testEnvironment.SetEnvironmentVariable(EnvironmentSettingsKeys.PreBuildCommand, value); + var provider = CreateProvider(sourceDir, testEnvironment); + + // Act + provider.TryGetAndLoadSettings(out var settings); + + // Assert + Assert.Equal(value, settings.PreBuildCommand); + } + [Fact] public void TryGetAndLoadSettings_PrefersPrefixedName_IfPresent() { diff --git a/tests/Oryx.BuildImage.Tests/DotNetCoreSampleAppsTest.cs b/tests/Oryx.BuildImage.Tests/DotNetCoreSampleAppsTest.cs index b781bfb8a..dbc9227e5 100644 --- a/tests/Oryx.BuildImage.Tests/DotNetCoreSampleAppsTest.cs +++ b/tests/Oryx.BuildImage.Tests/DotNetCoreSampleAppsTest.cs @@ -472,6 +472,49 @@ namespace Microsoft.Oryx.BuildImage.Tests result.GetDebugInfo()); } + [Fact] + public void Build_Executes_InlinePreAndPostBuildCommands() + { + // Arrange + var appName = "NetCoreApp21WebApp"; + var volume = CreateSampleAppVolume(appName); + using (var sw = File.AppendText(Path.Combine(volume.MountedHostDir, "build.env"))) + { + sw.NewLine = "\n"; + sw.WriteLine("PRE_BUILD_COMMAND=\"echo from pre-build command\""); + sw.WriteLine("POST_BUILD_COMMAND=\"echo from post-build command\""); + } + + var appDir = volume.ContainerDir; + var tempOutputDir = "/tmp/output"; + var script = new ShellScriptBuilder() + .AddBuildCommand($"{appDir} -o {tempOutputDir} -l dotnet --language-version 2.1") + .ToString(); + + // Act + var result = _dockerCli.Run( + Settings.BuildImageName, + SampleAppsTestBase.CreateAppNameEnvVar(appName), + volume, + commandToExecuteOnRun: "/bin/bash", + commandArguments: + new[] + { + "-c", + script + }); + + // Assert + RunAsserts( + () => + { + Assert.True(result.IsSuccess); + Assert.Contains("from pre-build command", result.StdOut); + Assert.Contains("from post-build command", result.StdOut); + }, + result.GetDebugInfo()); + } + [Fact] public void Build_CopiesContentCreatedByPostBuildScript_ToExplicitOutputDirectory_AndOutpuIsZipped() { diff --git a/tests/Oryx.BuildImage.Tests/PythonSampleAppsTest.cs b/tests/Oryx.BuildImage.Tests/PythonSampleAppsTest.cs index f7228a66a..ea5864100 100644 --- a/tests/Oryx.BuildImage.Tests/PythonSampleAppsTest.cs +++ b/tests/Oryx.BuildImage.Tests/PythonSampleAppsTest.cs @@ -1072,6 +1072,48 @@ namespace Microsoft.Oryx.BuildImage.Tests result.GetDebugInfo()); } + [Fact] + public void Build_Executes_InlinePreAndPostBuildCommands() + { + // Arrange + var appName = "flask-app"; + var volume = CreateSampleAppVolume("flask-app"); + using (var sw = File.AppendText(Path.Combine(volume.MountedHostDir, "build.env"))) + { + sw.NewLine = "\n"; + sw.WriteLine("PRE_BUILD_COMMAND=\"echo from pre-build command\""); + sw.WriteLine("POST_BUILD_COMMAND=\"echo from post-build command\""); + } + + var appDir = volume.ContainerDir; + var script = new ShellScriptBuilder() + .AddBuildCommand($"{appDir} -o /tmp/output") + .ToString(); + + // Act + var result = _dockerCli.Run( + Settings.BuildImageName, + SampleAppsTestBase.CreateAppNameEnvVar(appName), + volume, + commandToExecuteOnRun: "/bin/bash", + commandArguments: + new[] + { + "-c", + script + }); + + // Assert + RunAsserts( + () => + { + Assert.True(result.IsSuccess); + Assert.Contains("from pre-build command", result.StdOut); + Assert.Contains("from post-build command", result.StdOut); + }, + result.GetDebugInfo()); + } + [Fact] public void Django_CollectStaticFailure_DoesNotFailBuild() { diff --git a/tests/Oryx.Tests.Common/EndToEndTestHelper.cs b/tests/Oryx.Tests.Common/EndToEndTestHelper.cs index 222dc3266..c1e5c64ca 100644 --- a/tests/Oryx.Tests.Common/EndToEndTestHelper.cs +++ b/tests/Oryx.Tests.Common/EndToEndTestHelper.cs @@ -58,7 +58,9 @@ namespace Microsoft.Oryx.Tests.Common string[] runArgs, Func assertAction) { - var AppNameEnvVariable = new EnvironmentVariable(LoggingConstants.AppServiceAppNameEnvironmentVariableName, appName); + var AppNameEnvVariable = new EnvironmentVariable( + LoggingConstants.AppServiceAppNameEnvironmentVariableName, + appName); environmentVariables.Add(AppNameEnvVariable); return BuildRunAndAssertAppAsync( output, @@ -68,7 +70,7 @@ namespace Microsoft.Oryx.Tests.Common runtimeImageName, environmentVariables, portMapping, - link:null, + link: null, runCmd, runArgs, assertAction); @@ -146,19 +148,29 @@ namespace Microsoft.Oryx.Tests.Common output); // Run - await RunAndAssertAppAsync(runtimeImageName, output, volumes, environmentVariables, portMapping, link, runCmd, runArgs, assertAction, dockerCli); + await RunAndAssertAppAsync( + runtimeImageName, + output, + volumes, + environmentVariables, + portMapping, + link, + runCmd, + runArgs, + assertAction, + dockerCli); } public static async Task RunAndAssertAppAsync( string imageName, ITestOutputHelper output, - List volumes, - List environmentVariables, - string portMapping, - string link, - string runCmd, - string[] runArgs, - Func assertAction, + List volumes, + List environmentVariables, + string portMapping, + string link, + string runCmd, + string[] runArgs, + Func assertAction, DockerCli dockerCli) { DockerRunCommandProcessResult runResult = null; @@ -204,11 +216,34 @@ namespace Microsoft.Oryx.Tests.Common break; } - catch (Exception ex) when (ex.InnerException is IOException || ex.InnerException is SocketException) + catch (Exception ex) when (ex.InnerException is IOException || + ex.InnerException is SocketException) { if (i == MaxRetryCount - 1) { - output.WriteLine(runResult.GetDebugInfo()); + string debugInfo = string.Empty; + + // ToString() on StringBuilder is throwing an exception probably because of a + // multithreading issue where the container is still writing data into it and we are trying + // to retrieve the content out of it. + try + { + // TO i + // System.ArgumentOutOfRangeException : Index was out of range. Must be non-negative + // and less than the size of the collection. + // Parameter name: chunkLength + // Stack Trace: + // at System.Text.StringBuilder.ToString() + debugInfo = runResult.GetDebugInfo(); + output.WriteLine(debugInfo); + } + catch (Exception debugInfoException) + { + output.WriteLine( + "An error occurred while trying to get data from the output and error " + + "streams of the container. Exception: " + debugInfoException.ToString()); + } + throw; } }