Added infrastructure to support an Oryx Buildpack (#55)

This commit is contained in:
Dor 2019-04-12 10:11:35 -07:00 коммит произвёл GitHub
Родитель cdc0ced106
Коммит 5a2330d7c5
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
35 изменённых файлов: 486 добавлений и 113 удалений

6
.gitignore поставляемый
Просмотреть файл

@ -18,7 +18,6 @@
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
@ -272,11 +271,10 @@ images/runtime/dotnetcore/*.*/
!images/runtime/dotnetcore/2.2/
images/runtime/python/*.*/
# Golang packages, if oryx is added to GOPATH (which it should)
# Go packages, if Oryx is added to GOPATH (which it should)
pkg/
bin/
# Go packages from github
# Go packages from GitHub
src/github.com/
src/startupscriptgenerator/vendor/
src/golang.org/

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

@ -12,6 +12,8 @@ declare -r BUILD_RUNTIMEIMAGES_USING_NOCACHE="$BUILD_RUNTIMEIMAGES_USING_NOCACHE
declare -r BUILD_IMAGES_BUILD_CONTEXT_DIR="$__REPO_DIR/"
declare -r BUILD_IMAGES_DOCKERFILE="$__REPO_DIR/images/build/Dockerfile"
declare -r BUILDER_BASE_IMAGE_DOCKERFILE="$__REPO_DIR/images/pack-builder/builder-base.Dockerfile"
declare -r PACK_IMAGE_DOCKERFILE="$__REPO_DIR/images/pack-builder/pack-runner.Dockerfile"
declare -r ORYXTESTS_BUILDIMAGE_DOCKERFILE="$__REPO_DIR/tests/images/build/Dockerfile"
declare -r RUNTIME_IMAGES_SRC_DIR="$__REPO_DIR/images/runtime"
declare -r SOURCES_SRC_DIR="$__REPO_DIR/src"
@ -24,9 +26,12 @@ declare -r RUNTIME_IMAGES_ARTIFACTS_FILE="$ARTIFACTS_DIR/images/runtime-images.t
declare -r ACR_BUILD_IMAGES_ARTIFACTS_FILE="$ARTIFACTS_DIR/images/build-images-acr.txt"
declare -r ACR_RUNTIME_IMAGES_ARTIFACTS_FILE="$ARTIFACTS_DIR/images/runtime-images-acr.txt"
declare -r DOCKER_BUILD_IMAGES_REPO="oryxdevms/build"
declare -r DOCKER_DEV_REPO_BASE='oryxdevms'
declare -r DOCKER_BUILD_IMAGES_REPO="$DOCKER_DEV_REPO_BASE/build"
declare -r DOCKER_PACK_IMAGE_REPO="$DOCKER_DEV_REPO_BASE/pack"
declare -r DOCKER_BUILDER_BASE_IMAGE_REPO="$DOCKER_DEV_REPO_BASE/builder-base"
declare -r ORYXTESTS_BUILDIMAGE_REPO="oryxtests/build"
declare -r DOCKER_RUNTIME_IMAGES_REPO="oryxdevms"
declare -r DOCKER_RUNTIME_IMAGES_REPO=$DOCKER_DEV_REPO_BASE
declare -r ACR_DEV_NAME="oryxdevmcr.azurecr.io"
declare -r ACR_BUILD_IMAGES_REPO="$ACR_DEV_NAME/public/oryx/build"
declare -r ACR_RUNTIME_IMAGES_REPO="$ACR_DEV_NAME/public/oryx"

0
build/build-buildimage-bases.sh Normal file → Executable file
Просмотреть файл

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

@ -31,7 +31,7 @@ fi
if [ "$EMBED_BUILDCONTEXT_IN_IMAGES" == "true" ]
then
ctxArgs="--build-arg GIT_COMMIT=$GIT_COMMIT --build-arg BUILD_NUMBER=$BUILD_NUMBER"
echo "build context: "$ctxArgs
echo "Build context args: $ctxArgs"
fi
function BuildAndTagStage()

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

@ -0,0 +1,36 @@
#!/bin/bash
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license.
# --------------------------------------------------------------------------------------------
set -e
declare -r REPO_DIR=$( cd $( dirname "$0" ) && cd .. && pwd )
source $REPO_DIR/build/__variables.sh
# Build base image for builder
cd "$BUILD_IMAGES_BUILD_CONTEXT_DIR"
docker build -f "$BUILDER_BASE_IMAGE_DOCKERFILE" -t $DOCKER_BUILDER_BASE_IMAGE_REPO:latest .
cd /tmp
$REPO_DIR/images/pack-builder/install-pack.sh
# Create builder
builderName="$DOCKER_DEV_REPO_BASE/pack-builder"
./pack create-builder $builderName \
--builder-config $REPO_DIR/images/pack-builder/builder.toml \
--no-pull
# Remove pack & everything that was added by it
rm -f ./pack
rm -rf ~/.pack
# Build an image that runs `pack`
cd "$BUILD_IMAGES_BUILD_CONTEXT_DIR"
docker build -f "$PACK_IMAGE_DOCKERFILE" \
--build-arg BUILD_NUMBER='0.2.0' \
--build-arg BUILDPACK_BUILDER_NAME="$builderName" \
-t $DOCKER_PACK_IMAGE_REPO:latest \
.

9
doc/buildpack.md Normal file
Просмотреть файл

@ -0,0 +1,9 @@
# Oryx Buildpack
Oryx provides a buildpack that runs it, so that Oryx can also be used via the [pack][] tool.
## Related images
WIP
[pack]: https://github.com/buildpack/pack

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

@ -243,7 +243,9 @@ ARG AGENTBUILD=${AGENTBUILD}
ARG BUILD_NUMBER=unspecified
ENV GIT_COMMIT=${GIT_COMMIT}
ENV BUILD_NUMBER=${BUILD_NUMBER}
RUN if [ -z "$AGENTBUILD" ]; then dotnet publish -r linux-x64 -o /opt/buildscriptgen/ -c Release BuildScriptGeneratorCli/BuildScriptGeneratorCli.csproj; fi
RUN if [ -z "$AGENTBUILD" ]; then \
dotnet publish -r linux-x64 -o /opt/buildscriptgen/ -c Release BuildScriptGeneratorCli/BuildScriptGeneratorCli.csproj; \
fi
# This stage is only when building in devops agents
FROM main AS copybuildscriptbinaries

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

@ -0,0 +1,14 @@
FROM oryxdevms/build:latest
LABEL io.buildpacks.stack.id=com.microsoft.oryx.stack
# Configure non-root user
RUN groupadd -g 1002 oryx_group && \
useradd -u 1001 -g oryx_group oryx_user && \
chown -R oryx_user:oryx_group /tmp && \
mkdir -p /home/oryx_user && \
chmod -R 777 /home/oryx_user
ENV CNB_USER_ID=1001 CNB_GROUP_ID=1002
COPY --from=packs/samples:rc /lifecycle /lifecycle

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

@ -0,0 +1,13 @@
[stack]
id = "com.microsoft.oryx.stack"
build-image = "oryxdevms/builder-base"
run-image = "oryxdevms/builder-base"
[[buildpacks]]
id = "com.microsoft.oryx.buildpack"
uri = "oryx-buildpack"
[[groups]]
[[groups.buildpacks]]
id = "com.microsoft.oryx.buildpack"
version = "0.0.1"

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

@ -0,0 +1,23 @@
#!/bin/bash
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license.
# --------------------------------------------------------------------------------------------
declare -r PACK_VERSION='0.1.0'
if [[ "$OSTYPE" == "linux-gnu" ]]; then
packPlatform='linux';
elif [[ "$OSTYPE" == "darwin"* ]]; then
packPlatform='macos';
elif [[ "$OSTYPE" == "cygwin" || "$OSTYPE" == "msys" ]]; then
echo 'ERROR: `pack create-builder` is not implemented on Windows.'
exit 1
else
echo 'ERROR: Could not detect compatible pack binary platform.'
exit 1
fi
wget -nv "https://github.com/buildpack/pack/releases/download/v$PACK_VERSION/pack-v$PACK_VERSION-$packPlatform.tgz"
tar -xvf "pack-v$PACK_VERSION-$packPlatform.tgz"
# `./pack` is now available for use

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

@ -0,0 +1,15 @@
#!/usr/bin/env bash
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license.
# --------------------------------------------------------------------------------------------
# usage: bin/build <layers dir> <platform dir> <plan path>
set -eo pipefail
echo "# Running 'oryx build .' in '`pwd`'..."
oryx build .
oryxExitStatus=$?
echo "# Oryx exited with $oryxExitStatus..."
exit $oryxExitStatus

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

@ -0,0 +1,13 @@
#!/usr/bin/env bash
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license.
# --------------------------------------------------------------------------------------------
# usage: bin/detect <platform-dir> <plan-path>
set -eo pipefail
sourceDir=`pwd`
echo "# Running 'oryx buildpack-detect $sourceDir --platform-dir $1 --plan-path $2'..."
exec oryx buildpack-detect $sourceDir --platform-dir $1 --plan-path $2

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

@ -0,0 +1,7 @@
[buildpack]
id = "com.microsoft.oryx.buildpack"
version = "0.0.1"
name = "Microsoft Oryx Buildpack"
[[stacks]]
id = ["com.microsoft.oryx.stack"]

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

@ -0,0 +1,11 @@
FROM buildpack-deps:stable
WORKDIR /tmp
COPY images/buildpack-builder/install-pack.sh install-pack.sh
RUN ./install-pack.sh && mv pack /usr/local/bin
ARG BUILDPACK_BUILDER_NAME
RUN pack set-default-builder $BUILDPACK_BUILDER_NAME
ENTRYPOINT ["/usr/local/bin/pack"]

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

@ -36,7 +36,7 @@ namespace Microsoft.Oryx.BuildScriptGenerator
script = null;
var toolsToVersion = new Dictionary<string, string>();
List<BuildScriptSnippet> snippets;
IList<BuildScriptSnippet> snippets;
var directoriesToExcludeFromCopyToIntermediateDir = new List<string>();
var directoriesToExcludeFromCopyToBuildOutputDir = new List<string>();
@ -70,42 +70,31 @@ namespace Microsoft.Oryx.BuildScriptGenerator
}
}
private static string GetBenvArgs(Dictionary<string, string> benvArgsMap)
{
var listOfBenvArgs = benvArgsMap.Select(t => $"{t.Key}={t.Value}");
var benvArgs = string.Join(' ', listOfBenvArgs);
return benvArgs;
}
private List<BuildScriptSnippet> GetBuildSnippets(
BuildScriptGeneratorContext context,
Dictionary<string, string> toolsToVersion,
List<string> directoriesToExcludeFromCopyToIntermediateDir,
List<string> directoriesToExlcudeFromCopyToBuildOutputDir)
public IList<Tuple<IProgrammingPlatform, string>> GetCompatiblePlatforms(BuildScriptGeneratorContext ctx)
{
bool providedLanguageFound = false;
var snippets = new List<BuildScriptSnippet>();
var resultPlatforms = new List<Tuple<IProgrammingPlatform, string>>();
foreach (var platform in _programmingPlatforms)
{
if (!platform.IsEnabled(context))
if (!platform.IsEnabled(ctx))
{
_logger.LogDebug("{platformName} has been disabled", platform.Name);
continue;
}
bool usePlatform = false;
var currPlatformMatchesProvided = !string.IsNullOrEmpty(context.Language) &&
string.Equals(context.Language, platform.Name, StringComparison.OrdinalIgnoreCase);
var currPlatformMatchesProvided = !string.IsNullOrEmpty(ctx.Language) &&
string.Equals(ctx.Language, platform.Name, StringComparison.OrdinalIgnoreCase);
string targetVersionSpec = null;
if (currPlatformMatchesProvided)
{
providedLanguageFound = true;
targetVersionSpec = context.LanguageVersion;
targetVersionSpec = ctx.LanguageVersion;
usePlatform = true;
}
else if (context.DisableMultiPlatformBuild && !string.IsNullOrEmpty(context.Language))
else if (ctx.DisableMultiPlatformBuild && !string.IsNullOrEmpty(ctx.Language))
{
_logger.LogDebug(
"Multi platform build is disabled and platform was specified. " +
@ -117,7 +106,7 @@ namespace Microsoft.Oryx.BuildScriptGenerator
if (!currPlatformMatchesProvided || string.IsNullOrEmpty(targetVersionSpec))
{
_logger.LogDebug("Detecting platform using {platformName}", platform.Name);
var detectionResult = platform.Detect(context.SourceRepo);
var detectionResult = platform.Detect(ctx.SourceRepo);
if (detectionResult != null)
{
_logger.LogDebug(
@ -136,6 +125,33 @@ namespace Microsoft.Oryx.BuildScriptGenerator
if (usePlatform)
{
resultPlatforms.Add(Tuple.Create(platform, targetVersionSpec));
}
}
// Even if a language was detected, we throw an error if the user provided
// an unsupported language as target.
if (!string.IsNullOrEmpty(ctx.Language) && !providedLanguageFound)
{
ThrowInvalidLanguageProvided(ctx);
}
return resultPlatforms;
}
private IList<BuildScriptSnippet> GetBuildSnippets(
BuildScriptGeneratorContext context,
Dictionary<string, string> toolsToVersion,
List<string> directoriesToExcludeFromCopyToIntermediateDir,
List<string> directoriesToExlcudeFromCopyToBuildOutputDir)
{
var snippets = new List<BuildScriptSnippet>();
var platformsToUse = GetCompatiblePlatforms(context);
foreach (Tuple<IProgrammingPlatform, string> platformAndVersion in platformsToUse)
{
var (platform, targetVersionSpec) = platformAndVersion;
var excludedDirs = platform.GetDirectoriesToExcludeFromCopyToIntermediateDir(context);
if (excludedDirs.Any())
{
@ -166,14 +182,6 @@ namespace Microsoft.Oryx.BuildScriptGenerator
_logger.LogDebug("Script generator {scriptGenType} cannot be used", platform.GetType());
}
}
}
// Even if a language was detected, we throw an error if the user provided
// an unsupported language as target.
if (!string.IsNullOrEmpty(context.Language) && !providedLanguageFound)
{
ThrowInvalidLanguageProvided(context);
}
return snippets;
}
@ -195,12 +203,19 @@ namespace Microsoft.Oryx.BuildScriptGenerator
}
}
private static string GetBenvArgs(Dictionary<string, string> benvArgsMap)
{
var listOfBenvArgs = benvArgsMap.Select(t => $"{t.Key}={t.Value}");
var benvArgs = string.Join(' ', listOfBenvArgs);
return benvArgs;
}
/// <summary>
/// Builds the full build script from the list of snippets for each platform.
/// </summary>
/// <returns>Finalized build script as a string.</returns>
private string BuildScriptFromSnippets(
List<BuildScriptSnippet> snippets,
IList<BuildScriptSnippet> snippets,
Dictionary<string, string> toolsToVersion,
List<string> directoriesToExcludeFromCopyToIntermediateDir,
List<string> directoriesToExcludeFromCopyToBuildOutputDir)

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

@ -30,15 +30,15 @@ namespace Microsoft.Oryx.BuildScriptGenerator
return null;
}
public string GetEnvironmentVariable(string name)
public string GetEnvironmentVariable(string name, string defaultValue = null)
{
return Environment.GetEnvironmentVariable(name);
return Environment.GetEnvironmentVariable(name) ?? defaultValue;
}
public IList<string> GetEnvironmentVariableAsList(string name)
{
IList<string> ret = null;
var values = Environment.GetEnvironmentVariable(name);
var values = GetEnvironmentVariable(name);
if (!string.IsNullOrWhiteSpace(values))
{
ret = values.Split(",");

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

@ -3,6 +3,7 @@
// Licensed under the MIT license.
// --------------------------------------------------------------------------------------------
using System.IO;
using Microsoft.Extensions.Options;
namespace Microsoft.Oryx.BuildScriptGenerator.DotNetCore
@ -10,7 +11,7 @@ namespace Microsoft.Oryx.BuildScriptGenerator.DotNetCore
internal class DotnetCoreScriptGeneratorOptionsSetup : IConfigureOptions<DotnetCoreScriptGeneratorOptions>
{
internal const string DefaultVersion = DotNetCoreVersions.DotNetCore21Version;
internal const string InstalledVersionsDir = "/opt/dotnet/";
internal const string InstalledVersionsDir = "/opt/dotnet/"; // TODO: remove hard-coded path
private readonly IEnvironment _environment;

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

@ -3,6 +3,9 @@
// Licensed under the MIT license.
// --------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
namespace Microsoft.Oryx.BuildScriptGenerator
{
public interface IBuildScriptGenerator
@ -10,9 +13,20 @@ namespace Microsoft.Oryx.BuildScriptGenerator
/// <summary>
/// Tries to generate a bash script to build an application.
/// </summary>
/// <param name="scriptGeneratorContext">The <see cref="BuildScriptGeneratorContext"/> with parameters for the script.</param>
/// <param name="scriptGeneratorContext">
/// The <see cref="BuildScriptGeneratorContext"/> with parameters for the script.
/// </param>
/// <param name="script">The generated script if the operation was successful.</param>
/// <returns><c>true</c> if the operation was successful, <c>false</c> otherwise.</returns>
bool TryGenerateBashScript(BuildScriptGeneratorContext scriptGeneratorContext, out string script);
/// <summary>
/// Determines which platforms can be used to build the given application.
/// </summary>
/// <param name="scriptGeneratorContext">
/// The <see cref="BuildScriptGeneratorContext"/> with parameters for check.
/// </param>
/// <returns>a list of platform and version pairs.</returns>
IList<Tuple<IProgrammingPlatform, string>> GetCompatiblePlatforms(BuildScriptGeneratorContext ctx);
}
}

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

@ -14,14 +14,13 @@ namespace Microsoft.Oryx.BuildScriptGenerator
public interface IEnvironment
{
/// <summary>
/// Gets values of an environment variable.
/// Gets the value of an environment variable.
/// </summary>
/// <param name="name">Name of the environment variable. Is case-sensitive.</param>
/// <param name="name">Name of the environment variable. Case-sensitive.</param>
/// <returns>
/// The value of the environment variable specified by variable,
/// or null if the environment variable is not found.
/// The value of the given environment variable, or null if the environment variable isn't found.
/// </returns>
string GetEnvironmentVariable(string name);
string GetEnvironmentVariable(string name, string defaultValue = null);
/// <summary>
/// Gets the value of an environment variable as a boolean, if found. The check

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

@ -3,6 +3,7 @@
// Licensed under the MIT license.
// --------------------------------------------------------------------------------------------
using System.IO;
using Microsoft.Extensions.Options;
namespace Microsoft.Oryx.BuildScriptGenerator.Node
@ -15,7 +16,7 @@ namespace Microsoft.Oryx.BuildScriptGenerator.Node
internal const string NpmSupportedVersionsEnvVariable = "NPM_SUPPORTED_VERSIONS";
internal const string LegacyZipNodeModules = "ENABLE_NODE_MODULES_ZIP";
internal const string NodeLtsVersion = "8.11.2";
internal const string InstalledNodeVersionsDir = "/opt/nodejs/";
internal const string InstalledNodeVersionsDir = "/opt/nodejs/"; // TODO: remove hard-coded paths
internal const string InstalledNpmVersionsDir = "/opt/npm/";
private readonly IEnvironment _environment;
@ -35,8 +36,10 @@ namespace Microsoft.Oryx.BuildScriptGenerator.Node
options.NodeJsDefaultVersion = defaultVersion;
options.NpmDefaultVersion = _environment.GetEnvironmentVariable(NpmDefaultVersion);
options.InstalledNodeVersionsDir = InstalledNodeVersionsDir;
options.InstalledNpmVersionsDir = InstalledNpmVersionsDir;
options.SupportedNodeVersions = _environment.GetEnvironmentVariableAsList(
NodeSupportedVersionsEnvVariable);
options.SupportedNpmVersions = _environment.GetEnvironmentVariableAsList(NpmSupportedVersionsEnvVariable);

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

@ -3,6 +3,7 @@
// Licensed under the MIT license.
// --------------------------------------------------------------------------------------------
using System.IO;
using Microsoft.Extensions.Options;
namespace Microsoft.Oryx.BuildScriptGenerator.Python
@ -28,7 +29,6 @@ namespace Microsoft.Oryx.BuildScriptGenerator.Python
options.PythonDefaultVersion = defaultVersion;
options.InstalledPythonVersionsDir = PythonConstants.InstalledPythonVersionsDir;
// Providing the supported versions through an environment variable allows us to use the tool in
// other environments, e.g. our local machines for debugging.
options.SupportedPythonVersions = _environment.GetEnvironmentVariableAsList(

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

@ -26,6 +26,25 @@ namespace Microsoft.Oryx.BuildScriptGeneratorCli
_logger = _serviceProvider.GetRequiredService<ILogger<BuildScriptGenerator>>();
}
public static BuildScriptGeneratorContext CreateContext(
BuildScriptGeneratorOptions options,
CliEnvironmentSettings envSettings,
ISourceRepo sourceRepo)
{
return new BuildScriptGeneratorContext
{
SourceRepo = sourceRepo,
Language = options.Language,
LanguageVersion = options.LanguageVersion,
Properties = options.Properties,
EnableDotNetCore = !envSettings.DisableDotNetCore,
EnableNodeJs = !envSettings.DisableNodeJs,
EnablePython = !envSettings.DisablePython,
EnablePhp = !envSettings.DisablePhp,
DisableMultiPlatformBuild = envSettings.DisableMultiPlatformBuild
};
}
public bool TryGenerateScript(out string generatedScript)
{
generatedScript = null;
@ -37,18 +56,7 @@ namespace Microsoft.Oryx.BuildScriptGeneratorCli
var sourceRepoProvider = _serviceProvider.GetRequiredService<ISourceRepoProvider>();
var environment = _serviceProvider.GetRequiredService<CliEnvironmentSettings>();
var sourceRepo = sourceRepoProvider.GetSourceRepo();
var scriptGeneratorContext = new BuildScriptGeneratorContext
{
SourceRepo = sourceRepo,
Language = options.Language,
LanguageVersion = options.LanguageVersion,
Properties = options.Properties,
EnableDotNetCore = !environment.DisableDotNetCore,
EnableNodeJs = !environment.DisableNodeJs,
EnablePython = !environment.DisablePython,
EnablePhp = !environment.DisablePhp,
DisableMultiPlatformBuild = environment.DisableMultiPlatformBuild
};
var scriptGeneratorContext = CreateContext(options, environment, sourceRepo);
// Try generating a script
if (!scriptGenerator.TryGenerateBashScript(scriptGeneratorContext, out generatedScript))

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

@ -20,7 +20,7 @@ using Microsoft.Oryx.Common;
namespace Microsoft.Oryx.BuildScriptGeneratorCli
{
[Command("build", Description = "Generate and run build scripts.")]
internal class BuildCommand : BaseCommand
internal class BuildCommand : CommandBase
{
// Beginning and ending markers for build script output spans that should be time measured
private readonly TextSpan[] _measurableStdOutSpans =

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

@ -14,7 +14,7 @@ using Microsoft.Oryx.Common;
namespace Microsoft.Oryx.BuildScriptGeneratorCli
{
[Command("build-script", Description = "Generate build script to standard output.")]
internal class BuildScriptCommand : BaseCommand
internal class BuildScriptCommand : CommandBase
{
[Argument(0, Description = "The source directory.")]
public string SourceDir { get; set; }

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

@ -0,0 +1,107 @@
// --------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
// --------------------------------------------------------------------------------------------
using System;
using System.IO;
using System.Linq;
using McMaster.Extensions.CommandLineUtils;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Oryx.BuildScriptGenerator;
using Microsoft.Oryx.Common;
namespace Microsoft.Oryx.BuildScriptGeneratorCli
{
[Command("buildpack-detect", Description = "Determines whether Oryx can be applied as a buildpack to " +
"an app in the current working directory.")]
internal class BuildpackDetectCommand : CommandBase
{
// CodeDetectFail @ https://github.com/buildpack/lifecycle/blob/master/detector.go
public const int DetectorFailCode = 100;
[Argument(0, Description = "The source directory.")]
public string SourceDir { get; set; }
[Option("--platform-dir <dir>", CommandOptionType.SingleValue, Description = "Platform directory path.")]
public string PlatformDir { get; set; }
[Option("--plan-path <path>", CommandOptionType.SingleValue, Description = "Build plan TOML path.")]
public string PlanPath { get; set; }
internal override bool IsValidInput(IServiceProvider serviceProvider, IConsole console)
{
var result = true;
var options = serviceProvider.GetRequiredService<IOptions<BuildScriptGeneratorOptions>>().Value;
var logger = serviceProvider.GetService<ILogger<BuildpackDetectCommand>>();
// Set from ConfigureBuildScriptGeneratorOptions
if (!Directory.Exists(options.SourceDir))
{
logger.LogError("Could not find the source directory {srcDir}", options.SourceDir);
console.Error.WriteLine($"Error: Could not find the source directory '{options.SourceDir}'.");
result = false;
}
if (!string.IsNullOrWhiteSpace(PlanPath))
{
PlanPath = Path.GetFullPath(PlanPath);
if (!File.Exists(PlanPath))
{
logger?.LogError("Could not find build plan file {planPath}", PlanPath);
console.Error.WriteLine($"Error: Could not find build plan file '{PlanPath}'.");
result = false;
}
}
if (!string.IsNullOrWhiteSpace(PlatformDir))
{
PlatformDir = Path.GetFullPath(PlatformDir);
if (!Directory.Exists(PlatformDir))
{
logger?.LogError("Could not find platform directory {platformDir}", PlatformDir);
console.Error.WriteLine($"Error: Could not find platform directory '{PlatformDir}'.");
result = false;
}
}
return result;
}
internal override void ConfigureBuildScriptGeneratorOptions(BuildScriptGeneratorOptions options)
{
BuildScriptGeneratorOptionsHelper.ConfigureBuildScriptGeneratorOptions(
options,
SourceDir,
destinationDir: null,
intermediateDir: null,
language: null,
languageVersion: null,
scriptOnly: false,
properties: null);
}
internal override int Execute(IServiceProvider serviceProvider, IConsole console)
{
var generator = serviceProvider.GetRequiredService<IBuildScriptGenerator>();
var options = serviceProvider.GetRequiredService<IOptions<BuildScriptGeneratorOptions>>().Value;
var env = serviceProvider.GetRequiredService<CliEnvironmentSettings>();
var repo = serviceProvider.GetRequiredService<ISourceRepoProvider>().GetSourceRepo();
var ctx = BuildScriptGenerator.CreateContext(options, env, repo);
var compatPlats = generator.GetCompatiblePlatforms(ctx);
if (compatPlats != null && compatPlats.Any())
{
console.WriteLine("# Detected platforms:");
console.WriteLine(string.Join(' ', compatPlats.Select(pair => $"{pair.Item1.Name}=\"{pair.Item2}\"")));
return ProcessConstants.ExitSuccess;
}
return DetectorFailCode;
}
}
}

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

@ -13,7 +13,7 @@ using Microsoft.Oryx.Common;
namespace Microsoft.Oryx.BuildScriptGeneratorCli
{
internal abstract class BaseCommand
internal abstract class CommandBase
{
private IServiceProvider _serviceProvider = null;
@ -33,6 +33,8 @@ namespace Microsoft.Oryx.BuildScriptGeneratorCli
try
{
_serviceProvider = GetServiceProvider();
_serviceProvider?.GetRequiredService<ILogger<CommandBase>>()?.LogInformation(
"Oryx command line: {cmdLine}", Environment.CommandLine);
if (!IsValidInput(_serviceProvider, console))
{
return ProcessConstants.ExitFailure;
@ -52,7 +54,7 @@ namespace Microsoft.Oryx.BuildScriptGeneratorCli
}
catch (Exception exc)
{
_serviceProvider?.GetRequiredService<ILogger<BaseCommand>>()?.LogError(exc, "Exception caught");
_serviceProvider?.GetRequiredService<ILogger<CommandBase>>()?.LogError(exc, "Exception caught");
console.Error.WriteLine(Constants.GenericErrorMessage);
if (ShowStackTrace)
@ -83,10 +85,7 @@ namespace Microsoft.Oryx.BuildScriptGeneratorCli
internal virtual IServiceProvider GetServiceProvider()
{
var serviceProviderBuilder = new ServiceProviderBuilder(LogFilePath)
.ConfigureScriptGenerationOptions(o =>
{
ConfigureBuildScriptGeneratorOptions(o);
});
.ConfigureScriptGenerationOptions(opts => ConfigureBuildScriptGeneratorOptions(opts));
return serviceProviderBuilder.Build();
}

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

@ -19,7 +19,7 @@ namespace Microsoft.Oryx.BuildScriptGeneratorCli
[Command(
"languages",
Description = "Show the list of supported platforms and other information like versions, properties etc.")]
internal class LanguagesCommand : BaseCommand
internal class LanguagesCommand : CommandBase
{
[Option("--json", Description = "Output the supported platform data in JSON format.")]
public bool OutputJson { get; set; }
@ -73,10 +73,9 @@ namespace Microsoft.Oryx.BuildScriptGeneratorCli
var defs = new DefinitionListFormatter();
defs.AddDefinition("Platform", platform.Name);
if (platform.Versions != null && platform.Versions.Any())
{
defs.AddDefinition("Versions", string.Join(Environment.NewLine, platform.Versions));
}
defs.AddDefinition("Versions",
(platform.Versions != null && platform.Versions.Any()) ?
string.Join(Environment.NewLine, platform.Versions) : "N/A");
if (platform.Properties != null && platform.Properties.Any())
{

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

@ -14,7 +14,7 @@ using Microsoft.Oryx.Common;
namespace Microsoft.Oryx.BuildScriptGeneratorCli
{
[Command("run-script", Description = "Generate startup script.")]
internal class RunScriptCommand : BaseCommand
internal class RunScriptCommand : CommandBase
{
[Option(
"--platform",

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

@ -15,6 +15,7 @@ namespace Microsoft.Oryx.BuildScriptGeneratorCli
[Subcommand("languages", typeof(LanguagesCommand))]
[Subcommand("build-script", typeof(BuildScriptCommand))]
[Subcommand("run-script", typeof(RunScriptCommand))]
[Subcommand("buildpack-detect", typeof(BuildpackDetectCommand))]
internal class Program
{
[Option(CommandOptionType.NoValue, Description = "Print version information.")]

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

@ -5,6 +5,7 @@
using System;
using System.IO;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Oryx.BuildScriptGenerator;
@ -50,10 +51,7 @@ namespace Microsoft.Oryx.BuildScriptGeneratorCli
public ServiceProviderBuilder ConfigureScriptGenerationOptions(Action<BuildScriptGeneratorOptions> configure)
{
_serviceCollection.Configure<BuildScriptGeneratorOptions>(options =>
{
configure(options);
});
_serviceCollection.Configure<BuildScriptGeneratorOptions>(opts => configure(opts));
return this;
}
@ -62,7 +60,7 @@ namespace Microsoft.Oryx.BuildScriptGeneratorCli
return _serviceCollection.BuildServiceProvider();
}
private LoggingConfiguration BuildNLogConfiguration(string logPath)
private LoggingConfiguration BuildNLogConfiguration([CanBeNull] string logPath)
{
var config = new LoggingConfiguration();

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

@ -3,9 +3,7 @@
// Licensed under the MIT license.
// --------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Microsoft.Oryx.BuildScriptGenerator.Exceptions;
@ -99,7 +97,10 @@ namespace Microsoft.Oryx.BuildScriptGenerator.Tests.Php
var options = new PhpScriptGeneratorOptions();
optionsSetup.Configure(options);
return new PhpLanguageDetector(Options.Create(options), new TestPhpVersionProvider(supportedPhpVersions), NullLogger<PhpLanguageDetector>.Instance);
return new PhpLanguageDetector(
Options.Create(options),
new TestPhpVersionProvider(supportedPhpVersions),
NullLogger<PhpLanguageDetector>.Instance);
}
private class TestPhpVersionProvider : IPhpVersionProvider

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

@ -239,8 +239,7 @@ namespace BuildScriptGeneratorCli.Tests
[Theory]
[MemberData(nameof(DestinationDirectoryPathData))]
public void IsValidInput_IsTrue_IfDestinationDirIsNotEmpty(
string destinationDir)
public void IsValidInput_IsTrue_IfDestinationDirIsNotEmpty(string destinationDir)
{
// Arrange
var serviceProvider = new ServiceProviderBuilder()

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

@ -0,0 +1,76 @@
// --------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
// --------------------------------------------------------------------------------------------
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System;
using System.IO;
using Xunit;
using Microsoft.Oryx.Tests.Common;
using Microsoft.Oryx.BuildScriptGenerator.Node;
using Microsoft.Oryx.BuildScriptGenerator;
using Microsoft.Oryx.BuildScriptGeneratorCli;
using Microsoft.Oryx.Common;
namespace BuildScriptGeneratorCli.Tests
{
public class BuildpackDetectCommandTest : IClassFixture<TestTempDirTestFixture>
{
private readonly string _testDirPath;
public BuildpackDetectCommandTest(TestTempDirTestFixture testFixture)
{
_testDirPath = testFixture.RootDirPath;
}
[Fact]
public void Execute_Returns100_WhenSourceDirIsEmpty()
{
// Arrange
var srcDir = Path.Combine(_testDirPath, "emptydir");
Directory.CreateDirectory(srcDir);
var cmd = new BuildpackDetectCommand { SourceDir = srcDir };
// Act & Assert
Assert.Equal(
BuildpackDetectCommand.DetectorFailCode,
cmd.Execute(GetServiceProvider(cmd), new TestConsole()));
}
[Fact]
public void Execute_OutputsNode_WhenPackageJsonExists()
{
// Arrange
var srcDir = Path.Combine(_testDirPath, "nodeappdir");
Directory.CreateDirectory(srcDir);
File.WriteAllText(Path.Combine(srcDir, NodeConstants.PackageJsonFileName), "\n");
var cmd = new BuildpackDetectCommand { SourceDir = srcDir };
var console = new TestConsole();
// Act
int exitCode = cmd.Execute(GetServiceProvider(cmd), console);
// Assert
Assert.Equal(ProcessConstants.ExitSuccess, exitCode);
Assert.Contains(
$"{NodeConstants.NodeJsName}=\"{NodeScriptGeneratorOptionsSetup.NodeLtsVersion}\"",
console.StdOutput);
}
private static IServiceProvider GetServiceProvider(BuildpackDetectCommand cmd)
{
var env = new TestEnvironment();
env.SetEnvironmentVariable("NODE_SUPPORTED_VERSIONS", NodeScriptGeneratorOptionsSetup.NodeLtsVersion);
var svcProvider = new ServiceProviderBuilder()
.ConfigureServices(svcs => svcs.Replace(ServiceDescriptor.Singleton(typeof(IEnvironment), env)))
.ConfigureScriptGenerationOptions(opts => cmd.ConfigureBuildScriptGeneratorOptions(opts))
.Build();
return svcProvider;
}
}
}

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

@ -453,7 +453,6 @@ namespace Microsoft.Oryx.RuntimeImage.Tests
{
// Arrange
var appDir = "/app";
var scriptLocation = "/tmp/run.sh";
var script = new ShellScriptBuilder()
.AddCommand($"mkdir -p {appDir}") // no .csproj file
.AddCommand($"oryx -sourcePath {appDir} -defaultAppFilePath /tmp/doesnotexist.dll")
@ -569,7 +568,6 @@ namespace Microsoft.Oryx.RuntimeImage.Tests
var webApp2Dir = $"{repoDir}/src/Apps/WebApp2";
var defaultWebAppFile = "/tmp/defaultwebapp.dll";
var expectedStartupCommand = $"dotnet \"{defaultWebAppFile}\"";
var scriptLocation = "/tmp/run.sh";
var script = new ShellScriptBuilder()
.AddCommand($"mkdir -p {webApp1Dir}")
.AddCommand($"mkdir -p {webApp2Dir}")

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

@ -6,6 +6,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Oryx.BuildScriptGenerator;
namespace Microsoft.Oryx.Tests.Common
@ -28,18 +29,26 @@ namespace Microsoft.Oryx.Tests.Common
return null;
}
public string GetEnvironmentVariable(string name)
public string GetEnvironmentVariable(string name, string defaultValue = null)
{
if (Variables.TryGetValue(name, out var value))
{
return value;
}
return null;
return defaultValue;
}
public IList<string> GetEnvironmentVariableAsList(string name)
{
return null;
IList<string> ret = null;
var values = GetEnvironmentVariable(name);
if (!string.IsNullOrWhiteSpace(values))
{
ret = values.Split(",");
ret = ret.Select(s => s.Trim()).ToList();
}
return ret;
}
public IDictionary GetEnvironmentVariables()