public override async Task ExecuteAsync(OutputContext output, Application application, ServiceEntry service) { if (SkipWithoutProject(output, service, out var project)) { return; } if (SkipWithoutContainerInfo(output, service, out var container)) { return; } if (container.UseMultiphaseDockerfile == false) { throw new CommandException("Generated Dockerfile workflow does not support single-phase Dockerfiles."); } container.UseMultiphaseDockerfile ??= true; var dockerFilePath = Path.Combine(application.GetProjectDirectory(project), "Dockerfile"); if (File.Exists(dockerFilePath) && !Force) { throw new CommandException("'Dockerfile' already exists for project. use '--force' to overwrite."); } File.Delete(dockerFilePath); await DockerfileGenerator.WriteDockerfileAsync(output, application, service, project, container, dockerFilePath); output.WriteInfoLine($"Generated Dockerfile at '{dockerFilePath}'."); }
private static async Task <Application> InferApplicationForProject(OutputContext output, FileInfo projectFile) { var globals = new ApplicationGlobals(); globals.Name = Names.NormalizeToDns(Path.GetFileNameWithoutExtension(projectFile.Name)); var services = new List <ServiceEntry>(); var name = Path.GetFileNameWithoutExtension(projectFile.Name); var project = new Project(projectFile.FullName); var service = new ServiceEntry(new Service(name), name); service.Service.Source = project; services.Add(service); await ProjectReader.ReadProjectDetailsAsync(output, projectFile, project); var container = new ContainerInfo(); service.Service.GeneratedAssets.Container = container; var application = new GroveledApplication(globals, projectFile.DirectoryName, services); DockerfileGenerator.ApplyContainerDefaults(application, service, project, container); return(application); }
public static async Task BuildContainerImageAsync(OutputContext output, Application application, ContainerStep container) { if (output is null) { throw new ArgumentNullException(nameof(output)); } if (application is null) { throw new ArgumentNullException(nameof(application)); } if (container is null) { throw new ArgumentNullException(nameof(container)); } DockerfileGenerator.ApplyContainerDefaults(application, container); using var tempFile = TempFile.Create(); var dockerFilePath = Path.Combine(application.ProjectDirectory, "Dockerfile"); if (File.Exists(dockerFilePath)) { output.WriteDebugLine("using existing dockerfile"); } else { await DockerfileGenerator.WriteDockerfileAsync(output, application, container, tempFile.FilePath); dockerFilePath = tempFile.FilePath; } output.WriteDebugLine("running docker build"); output.WriteDebug($"> docker build . -t {container.ImageName}:{container.ImageTag} -f \"{dockerFilePath}\""); var capture = output.Capture(); var exitCode = await Process.ExecuteAsync( $"docker", $"build . -t {container.ImageName}:{container.ImageTag} -f \"{dockerFilePath}\"", application.ProjectDirectory, stdOut : capture.StdOut, stdErr : capture.StdErr); output.WriteDebugLine($"done running docker build exit code:{exitCode}"); if (exitCode != 0) { throw new CommandException("Docker build failed."); } }
private static async Task <Application> InferApplicationForSolution(OutputContext output, FileInfo solutionFile, SolutionFile solution) { var globals = new ApplicationGlobals(); globals.Name = Names.NormalizeToDns(Path.GetFileNameWithoutExtension(solutionFile.Name)); var services = new List <ServiceEntry>(); for (var i = 0; i < solution.ProjectsInOrder.Count; i++) { // The library we're using doesn't translate Windows style paths automatically. var solutionProject = solution.ProjectsInOrder[i]; if (solutionProject.AbsolutePath.EndsWith(".csproj", StringComparison.Ordinal)) { var projectFilePath = solutionProject.RelativePath.Replace('\\', Path.DirectorySeparatorChar); var projectFile = new FileInfo(projectFilePath); var name = Path.GetFileNameWithoutExtension(projectFile.Name); var project = new Project(projectFile.FullName); var service = new ServiceEntry(new Service(name), name); service.Service.Source = project; services.Add(service); await ProjectReader.ReadProjectDetailsAsync(output, projectFile, project); var container = new ContainerInfo(); service.Service.GeneratedAssets.Container = container; } } var application = new GroveledApplication(globals, solutionFile.DirectoryName, services); foreach (var service in application.Services) { DockerfileGenerator.ApplyContainerDefaults(application, service, (Project)service.Service.Source !, service.Service.GeneratedAssets.Container !); } return(application); }
public static async Task BuildContainerImageAsync(OutputContext output, Application application, ServiceEntry service, Project project, ContainerInfo container) { if (output is null) { throw new ArgumentNullException(nameof(output)); } if (application is null) { throw new ArgumentNullException(nameof(application)); } if (service is null) { throw new ArgumentNullException(nameof(service)); } if (project is null) { throw new ArgumentNullException(nameof(project)); } if (container is null) { throw new ArgumentNullException(nameof(container)); } using var tempFile = TempFile.Create(); var dockerFilePath = Path.Combine(application.GetProjectDirectory(project), Path.GetDirectoryName(project.RelativeFilePath) !, "Dockerfile"); if (File.Exists(dockerFilePath)) { output.WriteDebugLine($"Using existing dockerfile '{dockerFilePath}'."); } else { await DockerfileGenerator.WriteDockerfileAsync(output, application, service, project, container, tempFile.FilePath); dockerFilePath = tempFile.FilePath; } output.WriteDebugLine("Running 'docker build'."); output.WriteCommandLine("docker", $"build . -t {container.ImageName}:{container.ImageTag} -f \"{dockerFilePath}\""); var capture = output.Capture(); var exitCode = await Process.ExecuteAsync( $"docker", $"build . -t {container.ImageName}:{container.ImageTag} -f \"{dockerFilePath}\"", application.GetProjectDirectory(project), stdOut : capture.StdOut, stdErr : capture.StdErr); output.WriteDebugLine($"Done running 'docker build' exit code: {exitCode}"); if (exitCode != 0) { throw new CommandException("'docker build' failed."); } output.WriteInfoLine($"Created Docker Image: {container.ImageName}:{container.ImageTag}"); service.Outputs.Add(new DockerImageOutput(container.ImageName !, container.ImageTag !)); }
public override async Task <object?> ExecuteAsync() { if (callbacks.Count == 0) { return(Task.FromResult <object?>(null)); } if (callbacks.Count > 1) { throw new InvalidOperationException("More than one application type is not supported."); } var kvp = callbacks.Single(); var type = kvp.Key; var delegates = kvp.Value; output.WriteDebugLine($"Creating instance of application type '{type}'."); var application = Activator.CreateInstance(type); output.WriteDebugLine($"Done creating instance of application type '{type}'."); var wrapper = new ApplicationWrapper(application !, rootDirectory); wrapper.Globals.Name ??= name; foreach (var service in wrapper.Services) { output.WriteDebugLine($"Found service '{service.FriendlyName} {{ Name: {service.Service.Name} }}'."); string?projectRelativeFilePath = null; string?projectFilePath = null; if (solution != null) { var project = FindProjectInSolution(solution, service.FriendlyName); if (project == null) { output.WriteDebugLine($"Could not find project for service '{service.FriendlyName}'."); continue; } output.WriteDebugLine($"Found project '{project.RelativePath}' for service '{service.FriendlyName}'."); projectRelativeFilePath = project.RelativePath.Replace('\\', Path.DirectorySeparatorChar); projectFilePath = project.AbsolutePath.Replace('\\', Path.DirectorySeparatorChar); } else if (projectFile != null) { var normalized = Names.NormalizeToFriendly(Path.GetFileNameWithoutExtension(projectFile.Name)); if (!string.Equals(normalized, service.FriendlyName)) { output.WriteDebugLine($"Skipping service '{service.FriendlyName}'."); continue; } projectRelativeFilePath = projectFile.FullName; projectFilePath = projectFile.FullName; } if (projectFilePath != null) { var project = new Project(projectRelativeFilePath !); await ProjectReader.ReadProjectDetailsAsync(output, new FileInfo(projectFilePath), project); service.Service.Source = project; // Apply defaults to everything that has a project. var container = new ContainerInfo(); service.Service.GeneratedAssets.Container = container; DockerfileGenerator.ApplyContainerDefaults(wrapper, service, project, container); } } output.WriteDebugLine($"Running {delegates.Count} customization callbacks."); for (var i = 0; i < delegates.Count; i++) { delegates[i].DynamicInvoke(application); } output.WriteDebugLine($"Done running {delegates.Count} customization callbacks."); return(application); }
private static async Task ExecuteAsync(OutputContext output, FileInfo projectFile, List <string> outputs, bool force) { var config = await OpulenceConfigFactory.ReadConfigAsync(output, projectFile.DirectoryName); if (config == null) { // Allow operating without config for now. output.WriteInfoLine("config was not found, using defaults"); config = new OpulenceConfig() { Container = new ContainerConfig() { Registry = new RegistryConfig(), } }; } var application = ApplicationFactory.CreateDefault(config, projectFile); await ProjectReader.InitializeAsync(output, application); await ScriptRunner.RunProjectScriptAsync(output, application); for (var i = 0; i < application.Steps.Count; i++) { var step = application.Steps[i]; if (step is ContainerStep container) { if (!outputs.Contains("container")) { // We should still apply the defaults here because they'll be used by // the helm step. DockerfileGenerator.ApplyContainerDefaults(application, container); output.WriteDebugLine("skipping container"); continue; } output.WriteInfoLine("generating dockerfile"); var dockerFilePath = Path.Combine(application.ProjectDirectory, "Dockerfile"); if (File.Exists(dockerFilePath) && !force) { throw new CommandException("'Dockerfile' already exists for project. use --force to overwrite"); } // force multi-phase dockerfile - this makes much more sense in the workflow // where you're going to maintain the dockerfile yourself. container.UseMultiphaseDockerfile = true; File.Delete(dockerFilePath); await DockerfileGenerator.WriteDockerfileAsync(output, application, container, dockerFilePath); } else if (step is HelmChartStep chart) { if (!outputs.Contains("chart")) { output.WriteDebugLine("skipping helm chart"); continue; } output.WriteInfoLine("generating helm charts"); var chartDirectory = Path.Combine(application.ProjectDirectory, "charts"); if (Directory.Exists(chartDirectory) && !force) { throw new CommandException("'charts' directory already exists for project. use --force to overwrite"); } await HelmChartGenerator.GenerateAsync( output, application, application.Steps.Get <ContainerStep>() !, chart, new DirectoryInfo(chartDirectory)); } } }