[714233] Add --output parameter to build-script command (#381)

* [714233] Add --output parameter to build-script command

* Resolve review feedback

* Resolve review feedback

* Trim trailing slashes in file path
This commit is contained in:
Cormac McCarthy 2019-10-10 12:29:53 -07:00 коммит произвёл GitHub
Родитель 0e12f14346
Коммит ba03de8dd6
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 213 добавлений и 19 удалений

Просмотреть файл

@ -10,6 +10,7 @@ using System.IO;
using System.Linq;
using Microsoft.Extensions.Logging;
using Microsoft.Oryx.Common;
using Microsoft.Oryx.Common.Extensions;
namespace Microsoft.Oryx.BuildScriptGenerator
{
@ -41,29 +42,12 @@ namespace Microsoft.Oryx.BuildScriptGenerator
{
if (searchSubDirectories)
{
return SafeEnumerateFiles(RootPath, searchPattern);
return RootPath.SafeEnumerateFiles(searchPattern);
}
return Directory.EnumerateFiles(RootPath, searchPattern);
}
/// <summary>
/// This method will recursively enumerate the files under a given path since the
/// Directory.EnumerateFiles call does not check to see if a directory exists before
/// enumerating it.
/// </summary>
/// <param name="path">The directory to recursively enumerate the files in.</param>
/// <param name="searchPattern">The search string to match against the names of files in path.</param>
/// <returns>All files that are accessible under the given directory.</returns>
public IEnumerable<string> SafeEnumerateFiles(string path, string searchPattern)
{
var fileResult = Directory.EnumerateFiles(path, searchPattern);
var directoryResult = Directory.EnumerateDirectories(path)
.Where(Directory.Exists)
.SelectMany(d => SafeEnumerateFiles(d, searchPattern));
return fileResult.Concat(directoryResult);
}
public string ReadFile(params string[] paths)
{
var path = ResolvePath(paths);

Просмотреть файл

@ -10,6 +10,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.Oryx.BuildScriptGenerator;
using Microsoft.Oryx.Common;
using Microsoft.Oryx.Common.Extensions;
namespace Microsoft.Oryx.BuildScriptGeneratorCli
{
@ -18,6 +19,13 @@ namespace Microsoft.Oryx.BuildScriptGeneratorCli
{
public const string Name = "build-script";
[Option(
"--output",
CommandOptionType.SingleValue,
Description = "The path that the build script will be written to. " +
"If not specified, the result will be written to STDOUT.")]
public string OutputPath { get; set; }
internal override int Execute(IServiceProvider serviceProvider, IConsole console)
{
var scriptGenerator = new BuildScriptGenerator(
@ -31,7 +39,18 @@ namespace Microsoft.Oryx.BuildScriptGeneratorCli
return ProcessConstants.ExitFailure;
}
console.WriteLine(generatedScript);
if (string.IsNullOrEmpty(OutputPath))
{
console.WriteLine(generatedScript);
}
else
{
OutputPath.SafeWriteAllText(generatedScript);
console.WriteLine($"Script written to '{OutputPath}'");
// Try making the script executable
ProcessHelper.TrySetExecutableMode(OutputPath);
}
return ProcessConstants.ExitSuccess;
}

Просмотреть файл

@ -0,0 +1,57 @@
// --------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
// --------------------------------------------------------------------------------------------
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Microsoft.Oryx.Common.Extensions
{
/// <summary>
/// A set of extension methods to help with operations involving the file system.
/// </summary>
public static class FileExtensions
{
/// <summary>
/// This method will write the given contents to the path provided and create any non-existent directories
/// along the path since File.WriteAllText will throw if one of the directories in the path does not exist.
/// </summary>
/// <param name="outputPath">The file path to write the contents to.</param>
/// <param name="contents">The contents to be written to the given file path.</param>
public static void SafeWriteAllText(this string outputPath, string contents)
{
if (string.IsNullOrEmpty(outputPath))
{
return;
}
outputPath = Path.GetFullPath(outputPath).TrimEnd('/').TrimEnd('\\');
var parentPath = Directory.GetParent(outputPath).FullName;
if (!Directory.Exists(parentPath))
{
Directory.CreateDirectory(parentPath);
}
File.WriteAllText(outputPath, contents);
}
/// <summary>
/// This method will recursively enumerate the files under a given path since the Directory.EnumerateFiles
/// call does not check to see if a directory exists, or if the directory is accessible, before
/// enumerating it.
/// </summary>
/// <param name="path">The directory to recursively enumerate the files in.</param>
/// <param name="searchPattern">The search string to match against the names of files in path.</param>
/// <returns>All files that are accessible under the given directory.</returns>
public static IEnumerable<string> SafeEnumerateFiles(this string path, string searchPattern)
{
var fileResult = Directory.EnumerateFiles(path, searchPattern);
var directoryResult = Directory.EnumerateDirectories(path)
.Where(Directory.Exists)
.SelectMany(d => d.SafeEnumerateFiles(searchPattern));
return fileResult.Concat(directoryResult);
}
}
}

Просмотреть файл

@ -16,10 +16,12 @@ namespace Microsoft.Oryx.BuildScriptGeneratorCli.Tests
{
public class ScriptCommandTest : IClassFixture<TestTempDirTestFixture>
{
private static TestTempDirTestFixture _testDir;
private static string _testDirPath;
public ScriptCommandTest(TestTempDirTestFixture testFixture)
{
_testDir = testFixture;
_testDirPath = testFixture.RootDirPath;
}
@ -78,6 +80,33 @@ namespace Microsoft.Oryx.BuildScriptGeneratorCli.Tests
Assert.Equal(string.Empty, testConsole.StdError);
}
[Fact]
public void ScriptOnly_OnSuccess_Execute_WritesOnlyScriptContent_ToFile()
{
// Arrange
const string scriptContent = "script content only";
var serviceProvider = CreateServiceProvider(
new TestProgrammingPlatform("test", new[] { "1.0.0" }, true, scriptContent, new TestLanguageDetector()),
scriptOnly: true);
var scriptCommand = new BuildScriptCommand();
var testConsole = new TestConsole(newLineCharacter: string.Empty);
var outputDirectory = _testDir.CreateChildDir();
var scriptPath = Path.Join(outputDirectory, "build.sh");
scriptCommand.OutputPath = scriptPath;
// Act
var exitCode = scriptCommand.Execute(serviceProvider, testConsole);
// Assert
Assert.Equal(0, exitCode);
Assert.Contains("Script written to", testConsole.StdOutput);
Assert.Equal(string.Empty, testConsole.StdError);
var outputFileContent = File.ReadAllText(scriptPath);
Assert.Contains(scriptContent, outputFileContent);
}
[Fact]
public void ScriptOnly_OnSuccess_GeneratesScript_ReplacingCRLF_WithLF()
{
@ -98,6 +127,33 @@ namespace Microsoft.Oryx.BuildScriptGeneratorCli.Tests
Assert.Equal(string.Empty, testConsole.StdError);
}
[Fact]
public void ScriptOnly_OnSuccess_GenerateScript_ReplacingCRLF_WithLF_ToFile()
{
// Arrange
const string scriptContentWithCRLF = "#!/bin/bash\r\necho Hello\r\necho World\r\n";
var serviceProvider = CreateServiceProvider(
new TestProgrammingPlatform("test", new[] { "1.0.0" }, true, scriptContentWithCRLF, new TestLanguageDetector()),
scriptOnly: true);
var scriptCommand = new BuildScriptCommand();
var testConsole = new TestConsole(newLineCharacter: string.Empty);
var outputDirectory = _testDir.CreateChildDir();
var scriptPath = Path.Join(outputDirectory, "build.sh");
scriptCommand.OutputPath = scriptPath;
// Act
var exitCode = scriptCommand.Execute(serviceProvider, testConsole);
// Assert
Assert.Equal(0, exitCode);
Assert.Contains("Script written to", testConsole.StdOutput);
Assert.Equal(string.Empty, testConsole.StdError);
var outputFileContent = File.ReadAllText(scriptPath);
Assert.Contains(scriptContentWithCRLF.Replace("\r\n", "\n"), outputFileContent);
}
private IServiceProvider CreateServiceProvider(TestProgrammingPlatform generator, bool scriptOnly)
{
var sourceCodeFolder = Path.Combine(_testDirPath, "src");

Просмотреть файл

@ -0,0 +1,78 @@
// --------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
// --------------------------------------------------------------------------------------------
using Microsoft.Oryx.Tests.Common;
using System.IO;
using Xunit;
namespace Microsoft.Oryx.Common.Extensions
{
public class FileExtensionsTest : IClassFixture<TestTempDirTestFixture>
{
private TestTempDirTestFixture _testDir;
private string _testDirPath;
public FileExtensionsTest(TestTempDirTestFixture testFixture)
{
_testDir = testFixture;
_testDirPath = testFixture.RootDirPath;
}
[Fact]
public void SafeWriteAllText_Validate_EmptyPath()
{
var contents = "Test content";
// No failure for empty string
string.Empty.SafeWriteAllText(contents);
}
[Fact]
public void SafeWriteAllText_Validate_ExistingParentDirectory()
{
var contents = "Test content";
// Existing parent directory, no file
var outputPath = Path.Combine(_testDirPath, "test.txt");
outputPath.SafeWriteAllText(contents);
Assert.True(File.Exists(outputPath));
Assert.Equal(File.ReadAllText(outputPath), contents);
}
[Fact]
public void SafeWriteAllText_Validate_ExistingFile()
{
var contents = "Test content";
var overwrittenContents = "Overwritten test contents";
// No failure for empty string
string.Empty.SafeWriteAllText(contents);
// Existing parent directory, no file
var outputPath = Path.Combine(_testDirPath, "test.txt");
outputPath.SafeWriteAllText(contents);
Assert.True(File.Exists(outputPath));
Assert.Equal(File.ReadAllText(outputPath), contents);
// Existing parent directory, overwrites file
outputPath.SafeWriteAllText(overwrittenContents);
Assert.Equal(File.ReadAllText(outputPath), overwrittenContents);
}
[Fact]
public void SafeWriteAllText_Validate_NonExistentParentDirectory()
{
var contents = "Test content";
// Non-existent parent directory
var outputPath = Path.Combine(_testDirPath, "test.txt");
var nonExistentDirectory = _testDir.GenerateRandomChildDirPath();
outputPath = Path.Combine(nonExistentDirectory, "test.txt");
outputPath.SafeWriteAllText(contents);
Assert.True(Directory.Exists(nonExistentDirectory));
Assert.Equal(File.ReadAllText(outputPath), contents);
}
}
}