зеркало из https://github.com/microsoft/Oryx.git
[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:
Родитель
0e12f14346
Коммит
ba03de8dd6
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче