@@ -12,7 +12,7 @@ import {
1212 fallbackDockerfileName ,
1313} from 'src/docker/constants'
1414import { configOption , pathOption , teamOption } from 'src/options'
15- import { getUserConfig } from 'src/user'
15+ import { getUserConfig , USER_CONFIG_PATH } from 'src/user'
1616import { getRoot } from 'src/utils/filesystem'
1717import { wait } from 'src/utils/wait'
1818import * as stripAnsi from 'strip-ansi'
@@ -28,13 +28,15 @@ import {
2828 asTypescript ,
2929 withDelimiter ,
3030} from '../../utils/format'
31- import { buildWithProxy } from './buildWithProxy'
3231
3332const templateCheckInterval = 500 // 0.5 sec
3433
3534// Custom image URI is used for Bring Your Own Compute with self-hosted Docker registry
3635export const imageUriMask = process . env . UCLOUD_SANDBOX_IMAGE_URI_MASK
3736
37+ // UHub Docker Registry
38+ const UHUB_REGISTRY = 'uhub.service.ucloud.cn'
39+
3840async function getTemplateBuildLogs ( {
3941 templateID,
4042 buildID,
@@ -87,18 +89,36 @@ async function requestTemplateRebuild(
8789 } )
8890}
8991
90- async function triggerTemplateBuild ( templateID : string , buildID : string ) {
92+ async function triggerTemplateBuild (
93+ templateID : string ,
94+ buildID : string ,
95+ fromImage ?: string ,
96+ registryCredentials ?: { username : string ; password : string }
97+ ) {
9198 let res
9299 const maxRetries = 3
93100 for ( let i = 0 ; i < maxRetries ; i ++ ) {
94101 try {
102+ const body : Record < string , unknown > = { }
103+ if ( fromImage ) {
104+ body . fromImage = fromImage
105+ }
106+ if ( registryCredentials ) {
107+ body . fromImageRegistry = {
108+ type : 'registry' ,
109+ username : registryCredentials . username ,
110+ password : registryCredentials . password ,
111+ }
112+ }
113+
95114 res = await client . api . POST ( '/templates/{templateID}/builds/{buildID}' , {
96115 params : {
97116 path : {
98117 templateID,
99118 buildID,
100119 } ,
101120 } ,
121+ body : body as any ,
102122 } )
103123
104124 break
@@ -197,6 +217,8 @@ export const buildCommand = new commander.Command('build')
197217 noCache ?: boolean
198218 }
199219 ) => {
220+ let uhubRepoPath = ''
221+ let uhubCreds : { username : string ; password : string } | undefined
200222 try {
201223 // Display deprecation warning
202224 const deprecationMessage = `${ asBold ( 'DEPRECATION WARNING' ) }
@@ -241,7 +263,7 @@ Migration guide: ${asPrimary('https://docs.ucloud.cn/modelverse/README')}`
241263 } )
242264 }
243265
244- const accessToken = ensureAccessToken ( )
266+ ensureAccessToken ( )
245267 process . stdout . write ( '\n' )
246268
247269 const newName = opts . name ?. trim ( )
@@ -374,20 +396,85 @@ Migration guide: ${asPrimary('https://docs.ucloud.cn/modelverse/README')}`
374396 )
375397
376398 if ( imageUriMask == undefined ) {
399+ // Login to UHub registry — reuse saved credentials or prompt
400+ const inquirer = await import ( 'inquirer' )
401+ let savedConfig = getUserConfig ( )
402+ let uhubUsername = savedConfig ?. uhubUsername || ''
403+ let uhubPassword = savedConfig ?. uhubPassword || ''
404+
405+ if ( uhubUsername && uhubPassword ) {
406+ console . log ( `Using saved UHub credentials (username: ${ uhubUsername } )` )
407+ } else {
408+ console . log ( `Logging in to UHub registry (${ UHUB_REGISTRY } )...` )
409+ const answers = await inquirer . default . prompt ( [
410+ {
411+ name : 'uhubUsername' ,
412+ type : 'input' ,
413+ message : 'UHub username:' ,
414+ } ,
415+ {
416+ name : 'uhubPassword' ,
417+ type : 'password' ,
418+ message : 'UHub password (token):' ,
419+ mask : '*' ,
420+ } ,
421+ ] )
422+ uhubUsername = answers . uhubUsername
423+ uhubPassword = answers . uhubPassword
424+ }
425+
377426 try {
378427 child_process . execSync (
379- `echo " ${ accessToken } " | docker login docker. ${ connectionConfig . domain } -u _sandbox_access_token --password-stdin` ,
428+ `docker login ${ UHUB_REGISTRY } -u ${ uhubUsername } --password-stdin` ,
380429 {
381- stdio : ' inherit',
430+ stdio : [ 'pipe' , ' inherit', 'inherit' ] ,
382431 cwd : root ,
432+ input : uhubPassword ,
383433 }
384434 )
435+ uhubCreds = { username : uhubUsername , password : uhubPassword }
385436 } catch ( err : any ) {
386437 console . error (
387- ' Docker login failed. Please try to log in with `ucloud-sandbox-cli auth login` and try again.'
438+ ` Docker login to ${ UHUB_REGISTRY } failed. Please check your UHub credentials and try again.`
388439 )
440+ // Clear saved credentials on failure
441+ if ( savedConfig ) {
442+ const { uhubUsername : _u , uhubPassword : _p , uhubRepo : _r , ...rest } = savedConfig
443+ fs . writeFileSync ( USER_CONFIG_PATH , JSON . stringify ( rest , null , 2 ) )
444+ }
389445 process . exit ( 1 )
390446 }
447+
448+ // Prompt for UHub namespace and repo name, reuse saved or ask
449+ let uhubRepo = savedConfig ?. uhubRepo || ''
450+ if ( uhubRepo ) {
451+ console . log ( `Using saved UHub repo: ${ uhubRepo } ` )
452+ } else {
453+ const repoAnswer = await inquirer . default . prompt ( [
454+ {
455+ name : 'uhubNamespace' ,
456+ type : 'input' ,
457+ message : 'UHub namespace (e.g. ucloud-uhub):' ,
458+ validate : ( input : string ) => input ? true : 'Namespace is required' ,
459+ } ,
460+ {
461+ name : 'uhubRepoName' ,
462+ type : 'input' ,
463+ message : `UHub repo name:` ,
464+ default : templateID ,
465+ validate : ( input : string ) => input ? true : 'Repo name is required' ,
466+ } ,
467+ ] )
468+ uhubRepo = `${ repoAnswer . uhubNamespace } /${ repoAnswer . uhubRepoName } `
469+ }
470+
471+ // Save all UHub settings to config
472+ if ( savedConfig ) {
473+ const updatedConfig = { ...savedConfig , uhubUsername, uhubPassword, uhubRepo }
474+ fs . writeFileSync ( USER_CONFIG_PATH , JSON . stringify ( updatedConfig , null , 2 ) )
475+ }
476+
477+ uhubRepoPath = uhubRepo
391478 }
392479
393480 process . stdout . write ( '\n' )
@@ -402,7 +489,8 @@ Migration guide: ${asPrimary('https://docs.ucloud.cn/modelverse/README')}`
402489 templateID ,
403490 template . buildID ,
404491 connectionConfig . domain ,
405- imageUriMask
492+ imageUriMask ,
493+ uhubRepoPath
406494 )
407495 if ( imageUriMask != undefined ) {
408496 console . log ( 'Using custom docker image URI:' , imageUrl )
@@ -444,18 +532,15 @@ Migration guide: ${asPrimary('https://docs.ucloud.cn/modelverse/README')}`
444532 cwd : root ,
445533 } )
446534 } catch ( err : any ) {
447- await buildWithProxy (
448- userConfig ,
449- connectionConfig ,
450- accessToken ,
451- template ,
452- root
535+ console . error (
536+ `Docker push to ${ UHUB_REGISTRY } failed. Please check your UHub credentials and repository permissions.`
453537 )
538+ process . exit ( 1 )
454539 }
455540 console . log ( '> Docker image pushed.\n' )
456541
457542 console . log ( 'Triggering build...' )
458- await triggerBuild ( templateID , template . buildID )
543+ await triggerBuild ( templateID , template . buildID , imageUrl , uhubCreds )
459544
460545 console . log (
461546 `> Triggered build for the sandbox template ${ asFormattedSandboxTemplate (
@@ -656,20 +741,26 @@ async function requestBuildTemplate(
656741 return res . data
657742}
658743
659- async function triggerBuild ( templateID : string , buildID : string ) {
660- await triggerTemplateBuild ( templateID , buildID )
744+ async function triggerBuild (
745+ templateID : string ,
746+ buildID : string ,
747+ fromImage ?: string ,
748+ registryCredentials ?: { username : string ; password : string }
749+ ) {
750+ await triggerTemplateBuild ( templateID , buildID , fromImage , registryCredentials )
661751
662752 return
663753}
664754
665755function dockerImageUrl (
666756 templateID : string ,
667757 buildID : string ,
668- defaultDomain : string ,
669- imageUrlMask ?: string
758+ _defaultDomain : string ,
759+ imageUrlMask ?: string ,
760+ uhubRepo ?: string
670761) : string {
671762 if ( imageUrlMask == undefined ) {
672- return `docker. ${ defaultDomain } /sandbox/custom-envs/ ${ templateID } :${ buildID } `
763+ return `${ UHUB_REGISTRY } / ${ uhubRepo } :${ buildID } `
673764 }
674765
675766 return imageUrlMask
0 commit comments