@@ -5,6 +5,9 @@ import type { BuildManifest, BuildRuntime } from "@trigger.dev/core/v3/schemas";
55import { networkInterfaces } from "os" ;
66import { join } from "path" ;
77import { safeReadJSONFile } from "../utilities/fileSystem.js" ;
8+ import { mkdir , writeFile } from "node:fs/promises" ;
9+ import { homedir } from "os" ;
10+ import { dirname } from "path" ;
811import { readFileSync } from "fs" ;
912
1013import { isLinux } from "std-env" ;
@@ -474,6 +477,12 @@ async function localBuildImage(options: SelfHostedBuildImageOptions): Promise<Bu
474477
475478 const errors : string [ ] = [ ] ;
476479
480+ const writeRegistryAuthFile = process . env . TRIGGER_REGISTRY_AUTH === "file" ;
481+ const dockerConfigPath = join (
482+ process . env . DOCKER_CONFIG ?? join ( homedir ( ) , ".docker" ) ,
483+ "config.json"
484+ ) ;
485+
477486 let cloudRegistryHost : string | undefined ;
478487 if ( push && options . authenticateToRegistry ) {
479488 cloudRegistryHost =
@@ -499,32 +508,48 @@ async function localBuildImage(options: SelfHostedBuildImageOptions): Promise<Bu
499508 } ;
500509 }
501510
502- logger . debug ( `Logging in to docker registry: ${ cloudRegistryHost } ` ) ;
511+ if ( writeRegistryAuthFile ) {
512+ logger . debug ( `Writing registry auth for ${ cloudRegistryHost } to ${ dockerConfigPath } ` ) ;
513+
514+ const [ writeError ] = await tryCatch (
515+ upsertRegistryAuth ( dockerConfigPath , cloudRegistryHost , credentials )
516+ ) ;
503517
504- const loginProcess = x (
505- "docker" ,
506- [ "login" , "--username" , credentials . username , "--password-stdin" , cloudRegistryHost ] ,
507- {
508- nodeOptions : {
509- cwd : options . cwd ,
510- } ,
518+ if ( writeError ) {
519+ return {
520+ ok : false as const ,
521+ error : `Failed to write registry auth for ${ cloudRegistryHost } : ${ writeError . message } ` ,
522+ logs : "" ,
523+ } ;
511524 }
512- ) ;
525+ } else {
526+ logger . debug ( `Logging in to docker registry: ${ cloudRegistryHost } ` ) ;
527+
528+ const loginProcess = x (
529+ "docker" ,
530+ [ "login" , "--username" , credentials . username , "--password-stdin" , cloudRegistryHost ] ,
531+ {
532+ nodeOptions : {
533+ cwd : options . cwd ,
534+ } ,
535+ }
536+ ) ;
513537
514- loginProcess . process ?. stdin ?. write ( credentials . password ) ;
515- loginProcess . process ?. stdin ?. end ( ) ;
538+ loginProcess . process ?. stdin ?. write ( credentials . password ) ;
539+ loginProcess . process ?. stdin ?. end ( ) ;
516540
517- for await ( const line of loginProcess ) {
518- errors . push ( line ) ;
519- logger . debug ( line ) ;
520- }
541+ for await ( const line of loginProcess ) {
542+ errors . push ( line ) ;
543+ logger . debug ( line ) ;
544+ }
521545
522- if ( loginProcess . exitCode !== 0 ) {
523- return {
524- ok : false as const ,
525- error : `Failed to login to registry: ${ cloudRegistryHost } ` ,
526- logs : extractLogs ( errors ) ,
527- } ;
546+ if ( loginProcess . exitCode !== 0 ) {
547+ return {
548+ ok : false as const ,
549+ error : `Failed to login to registry: ${ cloudRegistryHost } ` ,
550+ logs : extractLogs ( errors ) ,
551+ } ;
552+ }
528553 }
529554
530555 options . onLog ?.( `Successfully logged in to the remote registry` ) ;
@@ -610,8 +635,7 @@ async function localBuildImage(options: SelfHostedBuildImageOptions): Promise<Bu
610635
611636 if ( buildProcess . exitCode !== 0 ) {
612637 if ( cloudRegistryHost ) {
613- logger . debug ( `Logging out from docker registry: ${ cloudRegistryHost } ` ) ;
614- await x ( "docker" , [ "logout" , cloudRegistryHost ] ) ;
638+ await clearRegistryAuth ( cloudRegistryHost , writeRegistryAuthFile , dockerConfigPath ) ;
615639 }
616640
617641 return {
@@ -660,8 +684,7 @@ async function localBuildImage(options: SelfHostedBuildImageOptions): Promise<Bu
660684 }
661685
662686 if ( cloudRegistryHost ) {
663- logger . debug ( `Logging out from docker registry: ${ cloudRegistryHost } ` ) ;
664- await x ( "docker" , [ "logout" , cloudRegistryHost ] ) ;
687+ await clearRegistryAuth ( cloudRegistryHost , writeRegistryAuthFile , dockerConfigPath ) ;
665688 }
666689
667690 return {
@@ -672,6 +695,41 @@ async function localBuildImage(options: SelfHostedBuildImageOptions): Promise<Bu
672695 } ;
673696}
674697
698+ async function upsertRegistryAuth (
699+ configPath : string ,
700+ host : string ,
701+ credentials : { username : string ; password : string }
702+ ) {
703+ const config = ( ( await safeReadJSONFile ( configPath ) ) ?? { } ) as {
704+ auths ?: Record < string , { auth ?: string } > ;
705+ } ;
706+ config . auths = {
707+ ...config . auths ,
708+ [ host ] : {
709+ auth : Buffer . from ( `${ credentials . username } :${ credentials . password } ` ) . toString ( "base64" ) ,
710+ } ,
711+ } ;
712+ await mkdir ( dirname ( configPath ) , { recursive : true } ) ;
713+ await writeFile ( configPath , JSON . stringify ( config ) , { mode : 0o600 } ) ;
714+ }
715+
716+ async function clearRegistryAuth ( host : string , viaAuthFile : boolean , configPath : string ) {
717+ if ( ! viaAuthFile ) {
718+ logger . debug ( `Logging out from docker registry: ${ host } ` ) ;
719+ await x ( "docker" , [ "logout" , host ] ) ;
720+ return ;
721+ }
722+
723+ const config = ( await safeReadJSONFile ( configPath ) ) as
724+ | { auths ?: Record < string , unknown > }
725+ | undefined ;
726+ if ( ! config ?. auths ?. [ host ] ) {
727+ return ;
728+ }
729+ delete config . auths [ host ] ;
730+ await writeFile ( configPath , JSON . stringify ( config ) , { mode : 0o600 } ) ;
731+ }
732+
675733function extractLogs ( outputs : string [ ] ) {
676734 // Remove empty lines
677735 const cleanedOutputs = outputs . map ( ( line ) => line . trim ( ) ) . filter ( ( line ) => line !== "" ) ;
0 commit comments