private ProcessStartInfo CreateContainerBuildProcess(string buildDir, ContainerBuildTask containerBuildTask) { if (!PathUtil.IsValidSubPath(containerBuildTask.Dockerfile)) { throw new Exception("Invalid Dockerfile path."); } if (!PathUtil.IsValidSubPath(containerBuildTask.ImageFileName) || containerBuildTask.ImageFileName.Contains(Path.DirectorySeparatorChar) || containerBuildTask.ImageFileName.Contains(Path.AltDirectorySeparatorChar)) { throw new Exception("Invalid image file name."); } var workspaceDir = WorkspaceDir; var psi = new ProcessStartInfo { FileName = "helium", UseShellExecute = false, RedirectStandardError = true, RedirectStandardOutput = true, ArgumentList = { "container-build", "--os", containerBuildTask.Platform.OS.ToString(), "--arch", containerBuildTask.Platform.Arch.ToString(), "--file", Path.Combine(workspaceDir, containerBuildTask.Dockerfile), "--build-context", workspaceDir, }, }; switch (containerBuildTask.ReplayMode) { case ReplayMode.Discard: break; case ReplayMode.RecordCache: psi.ArgumentList.Add("--archive"); psi.ArgumentList.Add(ReplayFile); break; default: throw new Exception("Invalid build task"); } psi.ArgumentList.Add(buildDir); psi.ArgumentList.Add(containerBuildTask.ImageFileName); return(psi); }
public async Task RunJob(BuildTaskBase?buildTaskBase) { await SetupWorkspace(); logger.LogTrace("Copied workspace"); Directory.CreateDirectory(ArtifactDir); var psi = buildTaskBase switch { BuildTask buildTask => CreateBuildProcess(buildDir, buildTask), ContainerBuildTask containerBuildTask => CreateContainerBuildProcess(buildDir, containerBuildTask), _ => throw new Exception("Invalid build task") }; cancellationToken.ThrowIfCancellationRequested(); var process = Process.Start(psi); logger.LogTrace("Running external process"); var exitTask = process.WaitForExitAsync(); var writerLock = new AsyncLock(); async Task PipeData(Stream stream) { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0) { cancellationToken.ThrowIfCancellationRequested(); using (await writerLock.LockAsync()) { cancellationToken.ThrowIfCancellationRequested(); await updateStream.WriteAsync(new BuildStatusUpdate { BuildOutput = ByteString.CopyFrom(buffer.AsSpan(0, bytesRead)), }); } cancellationToken.ThrowIfCancellationRequested(); } stream.Close(); } var outputTask = Task.Run(() => PipeData(process.StandardOutput.BaseStream), cancellationToken); var errorTask = Task.Run(() => PipeData(process.StandardError.BaseStream), cancellationToken); await Task.WhenAll(outputTask, errorTask, exitTask); var artifactDir = ArtifactDir; var artifacts = Directory.EnumerateFiles(artifactDir, "*.json", SearchOption.AllDirectories) .Select(file => new ArtifactInfo { Name = file.Substring(artifactDir.Length) .TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) }); var jobResult = new JobResult { ExitCode = process.ExitCode, }; jobResult.Artifacts.AddRange(artifacts); await updateStream.WriteAsync(new BuildStatusUpdate { JobFinished = jobResult, }); logger.LogTrace("Finished job"); }