LocalCodexBuilder.cs
1 using CodexPlugin; 2 using Logging; 3 using System.Diagnostics; 4 5 namespace CodexNetDeployer 6 { 7 public class LocalCodexBuilder 8 { 9 private readonly ILog log; 10 private readonly string? repoPath; 11 private readonly string? dockerUsername; 12 13 public LocalCodexBuilder(ILog log, string? repoPath, string? dockerUsername) 14 { 15 this.log = new LogPrefixer(log, "(LocalCodexBuilder) "); 16 this.repoPath = repoPath; 17 this.dockerUsername = dockerUsername; 18 } 19 20 public LocalCodexBuilder(ILog log, string? repoPath) 21 : this(log, repoPath, Environment.GetEnvironmentVariable("DOCKERUSERNAME")) 22 { 23 } 24 25 public LocalCodexBuilder(ILog log) 26 : this(log, Environment.GetEnvironmentVariable("CODEXREPOPATH")) 27 { 28 } 29 30 public void Intialize() 31 { 32 if (!IsEnabled()) return; 33 34 if (string.IsNullOrEmpty(dockerUsername)) throw new Exception("Docker username required. (Pass to constructor or set 'DOCKERUSERNAME' environment variable.)"); 35 if (string.IsNullOrEmpty(repoPath)) throw new Exception("Codex repo path required. (Pass to constructor or set 'CODEXREPOPATH' environment variable.)"); 36 if (!Directory.Exists(repoPath)) throw new Exception($"Path '{repoPath}' does not exist."); 37 var files = Directory.GetFiles(repoPath); 38 if (!files.Any(f => f.ToLowerInvariant().EndsWith("codex.nim"))) throw new Exception($"Path '{repoPath}' does not appear to be the Codex repo root."); 39 40 Log($"Codex docker image will be built in path '{repoPath}'."); 41 Log("Please note this can take several minutes. If you're not trying to use a Codex image with local code changes,"); 42 Log("Consider using the default test image or consider setting the 'CODEXDOCKERIMAGE' environment variable to use an already built image."); 43 CodexDockerImage.Override = $"Using docker image locally built in path '{repoPath}'."; 44 } 45 46 public void Build() 47 { 48 if (!IsEnabled()) return; 49 Log("Docker login..."); 50 DockerLogin(); 51 52 Log($"Logged in. Building Codex image in path '{repoPath}'..."); 53 54 var customImage = GenerateImageName(); 55 Docker($"build", "-t", customImage, "-f", "./codex.Dockerfile", 56 "--build-arg=\"MAKE_PARALLEL=4\"", 57 "--build-arg=\"NIMFLAGS=-d:disableMarchNative -d:codex_enable_api_debug_peers=true -d:codex_enable_api_debug_fetch=true -d:codex_enable_simulated_proof_failures\"", 58 "--build-arg=\"NAT_IP_AUTO=true\"", 59 ".."); 60 61 Log($"Image '{customImage}' built successfully. Pushing..."); 62 63 Docker("push", customImage); 64 65 CodexDockerImage.Override = customImage; 66 Log("Image pushed. Good to go!"); 67 } 68 69 private void DockerLogin() 70 { 71 var dockerPassword = Environment.GetEnvironmentVariable("DOCKERPASSWORD"); 72 73 try 74 { 75 if (string.IsNullOrEmpty(dockerUsername) || string.IsNullOrEmpty(dockerPassword)) 76 { 77 Log("Environment variable 'DOCKERPASSWORD' not provided."); 78 Log("Trying system default..."); 79 Docker("login"); 80 } 81 else 82 { 83 Docker("login", "-u", dockerUsername, "-p", dockerPassword); 84 } 85 } 86 catch 87 { 88 Log("Docker login failed."); 89 Log("Please check the docker username and password provided by the constructor arguments and/or"); 90 Log("set by 'DOCKERUSERNAME' and 'DOCKERPASSWORD' environment variables."); 91 Log("Note: You can use a docker access token as DOCKERPASSWORD."); 92 throw; 93 } 94 } 95 96 private string GenerateImageName() 97 { 98 var tag = Environment.GetEnvironmentVariable("DOCKERTAG"); 99 if (string.IsNullOrEmpty(tag)) return $"{dockerUsername!}/nim-codex-autoimage:{Guid.NewGuid().ToString().ToLowerInvariant()}"; 100 return $"{dockerUsername}/nim-codex-autoimage:{tag}"; 101 } 102 103 private void Docker(params string[] args) 104 { 105 var dockerPath = Path.Combine(repoPath!, "docker"); 106 107 var startInfo = new ProcessStartInfo() 108 { 109 FileName = "docker", 110 Arguments = string.Join(" ", args), 111 WorkingDirectory = dockerPath, 112 }; 113 var process = Process.Start(startInfo); 114 if (process == null) throw new Exception("Failed to start docker process."); 115 if (!process.WaitForExit(TimeSpan.FromMinutes(10))) throw new Exception("Docker processed timed out after 10 minutes."); 116 if (process.ExitCode != 0) throw new Exception("Docker process exited with error."); 117 } 118 119 private bool IsEnabled() 120 { 121 return !string.IsNullOrEmpty(repoPath); 122 } 123 124 private void Log(string msg) 125 { 126 log.Log(msg); 127 } 128 } 129 }