2023-09-28 11:31:09 +02:00
using CodexPlugin ;
using Logging ;
using System.Diagnostics ;
namespace CodexNetDeployer
{
public class LocalCodexBuilder
{
private readonly ILog log ;
private readonly string? repoPath ;
private readonly string? dockerUsername ;
public LocalCodexBuilder ( ILog log , string? repoPath , string? dockerUsername )
{
2023-09-29 10:43:21 +02:00
this . log = new LogPrefixer ( log , "(LocalCodexBuilder) " ) ;
2023-09-28 11:31:09 +02:00
this . repoPath = repoPath ;
this . dockerUsername = dockerUsername ;
}
public LocalCodexBuilder ( ILog log , string? repoPath )
: this ( log , repoPath , Environment . GetEnvironmentVariable ( "DOCKERUSERNAME" ) )
{
}
public LocalCodexBuilder ( ILog log )
: this ( log , Environment . GetEnvironmentVariable ( "CODEXREPOPATH" ) )
{
}
public void Intialize ( )
{
if ( ! IsEnabled ( ) ) return ;
if ( string . IsNullOrEmpty ( dockerUsername ) ) throw new Exception ( "Docker username required. (Pass to constructor or set 'DOCKERUSERNAME' environment variable.)" ) ;
if ( string . IsNullOrEmpty ( repoPath ) ) throw new Exception ( "Codex repo path required. (Pass to constructor or set 'CODEXREPOPATH' environment variable.)" ) ;
if ( ! Directory . Exists ( repoPath ) ) throw new Exception ( $"Path '{repoPath}' does not exist." ) ;
var files = Directory . GetFiles ( repoPath ) ;
if ( ! files . Any ( f = > f . ToLowerInvariant ( ) . EndsWith ( "codex.nim" ) ) ) throw new Exception ( $"Path '{repoPath}' does not appear to be the Codex repo root." ) ;
Log ( $"Codex docker image will be built in path '{repoPath}'." ) ;
Log ( "Please note this can take several minutes. If you're not trying to use a Codex image with local code changes," ) ;
Log ( "Consider using the default test image or consider setting the 'CODEXDOCKERIMAGE' environment variable to use an already built image." ) ;
2025-04-18 15:47:44 +02:00
CodexDockerImage . Override = $"Using docker image locally built in path '{repoPath}'." ;
2023-09-28 11:31:09 +02:00
}
public void Build ( )
{
if ( ! IsEnabled ( ) ) return ;
Log ( "Docker login..." ) ;
DockerLogin ( ) ;
Log ( $"Logged in. Building Codex image in path '{repoPath}'..." ) ;
var customImage = GenerateImageName ( ) ;
Docker ( $"build" , "-t" , customImage , "-f" , "./codex.Dockerfile" ,
"--build-arg=\"MAKE_PARALLEL=4\"" ,
"--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\"" ,
"--build-arg=\"NAT_IP_AUTO=true\"" ,
".." ) ;
Log ( $"Image '{customImage}' built successfully. Pushing..." ) ;
Docker ( "push" , customImage ) ;
2025-04-18 15:47:44 +02:00
CodexDockerImage . Override = customImage ;
2023-09-28 11:31:09 +02:00
Log ( "Image pushed. Good to go!" ) ;
}
private void DockerLogin ( )
{
var dockerPassword = Environment . GetEnvironmentVariable ( "DOCKERPASSWORD" ) ;
2023-09-28 12:22:35 +02:00
try
2023-09-28 11:31:09 +02:00
{
2023-09-28 12:22:35 +02:00
if ( string . IsNullOrEmpty ( dockerUsername ) | | string . IsNullOrEmpty ( dockerPassword ) )
{
Log ( "Environment variable 'DOCKERPASSWORD' not provided." ) ;
Log ( "Trying system default..." ) ;
Docker ( "login" ) ;
}
else
{
Docker ( "login" , "-u" , dockerUsername , "-p" , dockerPassword ) ;
}
2023-09-28 11:31:09 +02:00
}
2023-09-28 12:22:35 +02:00
catch
2023-09-28 11:31:09 +02:00
{
2023-09-28 12:22:35 +02:00
Log ( "Docker login failed." ) ;
Log ( "Please check the docker username and password provided by the constructor arguments and/or" ) ;
Log ( "set by 'DOCKERUSERNAME' and 'DOCKERPASSWORD' environment variables." ) ;
Log ( "Note: You can use a docker access token as DOCKERPASSWORD." ) ;
throw ;
2023-09-28 11:31:09 +02:00
}
}
private string GenerateImageName ( )
{
2023-09-28 12:22:35 +02:00
var tag = Environment . GetEnvironmentVariable ( "DOCKERTAG" ) ;
if ( string . IsNullOrEmpty ( tag ) ) return $"{dockerUsername!}/nim-codex-autoimage:{Guid.NewGuid().ToString().ToLowerInvariant()}" ;
return $"{dockerUsername}/nim-codex-autoimage:{tag}" ;
2023-09-28 11:31:09 +02:00
}
private void Docker ( params string [ ] args )
{
var dockerPath = Path . Combine ( repoPath ! , "docker" ) ;
var startInfo = new ProcessStartInfo ( )
{
FileName = "docker" ,
Arguments = string . Join ( " " , args ) ,
WorkingDirectory = dockerPath ,
} ;
var process = Process . Start ( startInfo ) ;
if ( process = = null ) throw new Exception ( "Failed to start docker process." ) ;
if ( ! process . WaitForExit ( TimeSpan . FromMinutes ( 10 ) ) ) throw new Exception ( "Docker processed timed out after 10 minutes." ) ;
if ( process . ExitCode ! = 0 ) throw new Exception ( "Docker process exited with error." ) ;
}
private bool IsEnabled ( )
{
return ! string . IsNullOrEmpty ( repoPath ) ;
}
private void Log ( string msg )
{
log . Log ( msg ) ;
}
}
}