in src/BuildScriptGeneratorCli/Commands/BuildCommand.cs [118:295]
internal override int Execute(IServiceProvider serviceProvider, IConsole console)
{
var environment = serviceProvider.GetRequiredService<IEnvironment>();
var logger = serviceProvider.GetRequiredService<ILogger<BuildCommand>>();
var buildOperationId = logger.StartOperation(BuildOperationName(environment));
var sourceRepo = serviceProvider.GetRequiredService<ISourceRepoProvider>().GetSourceRepo();
var sourceRepoCommitId = GetSourceRepoCommitId(environment, sourceRepo, logger);
var oryxVersion = Program.GetVersion();
var oryxCommitId = Program.GetMetadataValue(Program.GitCommit);
var oryxReleaseTagName = Program.GetMetadataValue(Program.ReleaseTagName);
var buildEventProps = new Dictionary<string, string>()
{
{ "oryxVersion", oryxVersion },
{ "oryxCommitId", oryxCommitId },
{ "oryxReleaseTagName", oryxReleaseTagName },
{
"oryxCommandLine",
string.Join(' ', environment.GetCommandLineArgs())
},
{ "sourceRepoCommitId", sourceRepoCommitId },
};
logger.LogEvent("BuildRequested", buildEventProps);
var options = serviceProvider.GetRequiredService<IOptions<BuildScriptGeneratorOptions>>().Value;
var beginningOutputLog = GetBeginningCommandOutputLog();
console.WriteLine(beginningOutputLog);
var buildInfo = new DefinitionListFormatter();
buildInfo.AddDefinition("Build Operation ID", buildOperationId);
if (!string.IsNullOrWhiteSpace(sourceRepoCommitId))
{
buildInfo.AddDefinition("Repository Commit", sourceRepoCommitId);
}
console.WriteLine(buildInfo.ToString());
// Generate build script
string scriptContent;
Exception exception;
using (var stopwatch = logger.LogTimedEvent("GenerateBuildScript"))
{
var checkerMessages = new List<ICheckerMessage>();
var scriptGenerator = new BuildScriptGenerator(
serviceProvider, console, checkerMessages, buildOperationId);
var generated = scriptGenerator.TryGenerateScript(out scriptContent, out exception);
stopwatch.AddProperty("generateSucceeded", generated.ToString());
if (checkerMessages.Count > 0)
{
var messageFormatter = new DefinitionListFormatter();
checkerMessages.ForEach(msg => messageFormatter.AddDefinition(msg.Level.ToString(), msg.Content));
console.WriteLine(messageFormatter.ToString());
}
else
{
logger.LogDebug("No checker messages emitted");
}
if (!generated)
{
if (exception != null)
{
return ProcessExitCodeHelper.GetExitCodeForException(exception);
}
return ProcessConstants.ExitFailure;
}
}
// Get the path where the generated script should be written into.
var tempDirectoryProvider = serviceProvider.GetRequiredService<ITempDirectoryProvider>();
var buildScriptPath = Path.Combine(tempDirectoryProvider.GetTempDirectory(), "build.sh");
// Write build script to selected path
File.WriteAllText(buildScriptPath, scriptContent);
logger.LogTrace("Build script written to file");
if (DebugMode)
{
console.WriteLine($"Build script content:\n{scriptContent}");
}
// Merge the earlier build event properties
buildEventProps = new Dictionary<string, string>(buildEventProps)
{
{ "scriptPath", buildScriptPath },
{ "envVars", string.Join(",", GetEnvVarNames(environment)) },
};
var buildScriptOutput = new StringBuilder();
var stdOutEventLoggers = new ITextStreamProcessor[]
{
new TextSpanEventLogger(logger, _measurableStdOutSpans),
new PipDownloadEventLogger(logger),
};
DataReceivedEventHandler stdOutBaseHandler = (sender, args) =>
{
string line = args.Data;
if (line == null)
{
return;
}
console.WriteLine(line);
buildScriptOutput.AppendLine(line);
foreach (var processor in stdOutEventLoggers)
{
// Catch any exception and log them instead of failing this build since whatever these processors
// do are not really relevant to the actual build of the app.
try
{
processor.ProcessLine(line);
}
catch (Exception ex)
{
logger.LogError(
ex,
$"An error occurred when trying to process the line '{line}' from standard " +
$"out using the '{processor.GetType()}' processor.");
}
}
};
DataReceivedEventHandler stdErrBaseHandler = (sender, args) =>
{
string line = args.Data;
if (line == null)
{
return;
}
// Not using IConsole.WriteErrorLine intentionally, to keep the child's error stream intact
console.Error.WriteLine(line);
buildScriptOutput.AppendLine(line);
};
// Run the generated script
int exitCode;
using (var timedEvent = logger.LogTimedEvent("RunBuildScript", buildEventProps))
{
console.WriteLine();
exitCode = serviceProvider.GetRequiredService<IScriptExecutor>().ExecuteScript(
buildScriptPath,
new[]
{
sourceRepo.RootPath,
options.DestinationDir ?? string.Empty,
options.IntermediateDir ?? string.Empty,
},
workingDirectory: sourceRepo.RootPath,
stdOutBaseHandler,
stdErrBaseHandler);
timedEvent.AddProperty("exitCode", exitCode.ToString());
}
if (exitCode != ProcessConstants.ExitSuccess)
{
logger.LogLongMessage(
LogLevel.Error,
header: "Error running build script",
buildScriptOutput.ToString(),
new Dictionary<string, object>
{
["buildExitCode"] = exitCode,
["oryxVersion"] = oryxVersion,
["oryxReleaseTagName"] = oryxReleaseTagName,
});
return exitCode;
}
return ProcessConstants.ExitSuccess;
}