@@ -3,6 +3,7 @@ package pull
33import (
44 "context"
55 "fmt"
6+ "strings"
67
78 "connectrpc.com/connect"
89 depotapi "github.com/depot/cli/pkg/api"
@@ -18,19 +19,23 @@ import (
1819 "golang.org/x/sync/errgroup"
1920)
2021
22+ const (
23+ depotRegistry = "registry.depot.dev"
24+ )
25+
2126func NewCmdPull () * cobra.Command {
2227 var (
2328 token string
2429 projectID string
2530 platform string
26- buildID string
31+ param string
2732 progress string
2833 userTags []string
2934 targets []string
3035 )
3136
3237 cmd := & cobra.Command {
33- Use : "pull [flags] [buildID]" ,
38+ Use : "pull [flags] [buildID|tag ]" ,
3439 Short : "Pull a project's build from the Depot registry" ,
3540 Args : cli .RequiresMaxArgs (1 ),
3641 RunE : func (cmd * cobra.Command , args []string ) error {
@@ -40,7 +45,7 @@ func NewCmdPull() *cobra.Command {
4045 }
4146
4247 if len (args ) > 0 {
43- buildID = args [0 ]
48+ param = args [0 ]
4449 }
4550 _ , isCI := ci .Provider ()
4651 if progress == prog .PrinterModeAuto && isCI {
@@ -49,7 +54,7 @@ func NewCmdPull() *cobra.Command {
4954
5055 ctx := cmd .Context ()
5156
52- token , err : = helpers .ResolveProjectAuth (ctx , token )
57+ token , err = helpers .ResolveProjectAuth (ctx , token )
5358 if err != nil {
5459 return err
5560 }
@@ -58,7 +63,7 @@ func NewCmdPull() *cobra.Command {
5863 return fmt .Errorf ("missing API token, please run `depot login`" )
5964 }
6065
61- if buildID == "" {
66+ if param == "" {
6267 var selectedProject * helpers.SelectedProject
6368 projectID = helpers .ResolveProjectID (projectID )
6469 if projectID == "" { // No locally saved depot.json.
@@ -85,34 +90,53 @@ func NewCmdPull() *cobra.Command {
8590 return fmt .Errorf ("build ID must be specified" )
8691 }
8792
88- buildID , err = helpers .SelectBuildID (ctx , token , projectID , client )
93+ param , err = helpers .SelectBuildID (ctx , token , projectID , client )
8994 if err != nil {
9095 return err
9196 }
9297
93- if buildID == "" {
94- return fmt .Errorf ("build ID must be specified" )
98+ if param == "" {
99+ return fmt .Errorf ("build ID or tag must be specified" )
95100 }
96101 }
97102
103+ // Check if the buildID is actually a registry reference or tag
104+ if strings .HasPrefix (param , depotRegistry + "/" ) {
105+ // Extract project ID and tag from the reference
106+ projectID , tag := extractProjectIDAndTag (param )
107+ return pullByTag (ctx , dockerCli , token , projectID , tag , userTags , platform , progress )
108+ }
109+
110+ // Check if the param is in the format "projectID:tag"
111+ if strings .Contains (param , ":" ) && ! strings .HasPrefix (param , depotRegistry + "/" ) {
112+ parts := strings .SplitN (param , ":" , 2 )
113+ if len (parts ) == 2 {
114+ projectID = parts [0 ]
115+ tag := parts [1 ]
116+ return pullByTag (ctx , dockerCli , token , projectID , tag , userTags , platform , progress )
117+ }
118+ }
119+
120+ // Try to get build info first (build ID approach)
98121 client := depotapi .NewBuildClient ()
99- req := & cliv1.GetPullInfoRequest {BuildId : buildID }
122+ req := & cliv1.GetPullInfoRequest {BuildId : param }
100123 res , err := client .GetPullInfo (ctx , depotapi .WithAuthentication (connect .NewRequest (req ), token ))
101124 if err != nil {
102- return err
125+ // If GetPullInfo fails, try the direct tag approach
126+ return pullByTag (ctx , dockerCli , token , projectID , param , userTags , platform , progress )
103127 }
104128
105129 buildOptions := res .Msg .Options
106130 savedForLoad := res .Msg .SaveForLoad
107131 if len (buildOptions ) > 0 && ! isSavedBuild (buildOptions , savedForLoad ) {
108- return fmt .Errorf ("build %s is not a saved build. To use the registry use --save when building" , buildID )
132+ return fmt .Errorf ("build %s is not a saved build. To use the registry use --save when building" , param )
109133 }
110134
111135 if isBake (buildOptions ) {
112136 return pullBake (ctx , dockerCli , res .Msg , targets , userTags , platform , progress )
113- } else {
114- return pullBuild (ctx , dockerCli , res .Msg , userTags , platform , progress )
115137 }
138+
139+ return pullBuild (ctx , dockerCli , res .Msg , userTags , platform , progress )
116140 },
117141 }
118142
@@ -126,6 +150,68 @@ func NewCmdPull() *cobra.Command {
126150 return cmd
127151}
128152
153+ // extractProjectIDAndTag extracts project ID and tag from a registry reference
154+ func extractProjectIDAndTag (reference string ) (projectID string , tag string ) {
155+ // Remove the registry prefix
156+ projectAndTag := strings .TrimPrefix (reference , depotRegistry + "/" )
157+
158+ // Split on colon to separate project ID and tag
159+ parts := strings .SplitN (projectAndTag , ":" , 2 )
160+ if len (parts ) != 2 {
161+ return "" , reference
162+ }
163+
164+ return parts [0 ], parts [1 ]
165+ }
166+
167+ // pullByTag handles pulling an image directly by tag when build ID approach fails
168+ func pullByTag (ctx context.Context , dockerCli command.Cli , token , projectID , tag string , userTags []string , platform , progress string ) error {
169+ client := depotapi .NewBuildClient ()
170+
171+ // Get pull token for the project
172+ req := & cliv1.GetPullTokenRequest {ProjectId : & projectID }
173+ res , err := client .GetPullToken (ctx , depotapi .WithAuthentication (connect .NewRequest (req ), token ))
174+ if err != nil {
175+ return fmt .Errorf ("failed to get pull token for project %s: %w" , projectID , err )
176+ }
177+
178+ // Construct the full image reference
179+ imageName := fmt .Sprintf ("%s/%s:%s" , depotRegistry , projectID , tag )
180+
181+ // Set up pull options
182+ serverAddress := depotRegistry
183+ username := "x-token"
184+ opts := load.PullOptions {
185+ UserTags : userTags ,
186+ Quiet : progress == prog .PrinterModeQuiet ,
187+ KeepImage : true ,
188+ Username : & username ,
189+ Password : & res .Msg .Token ,
190+ ServerAddress : & serverAddress ,
191+ }
192+ if platform != "" {
193+ opts .Platform = & platform
194+ }
195+
196+ // Create a simple pull struct
197+ pull := & pull {
198+ imageName : imageName ,
199+ pullOptions : opts ,
200+ }
201+
202+ // Set up printer
203+ printer , cancel , err := buildPrinter (ctx , pull , progress )
204+ if err != nil {
205+ return err
206+ }
207+ defer func () {
208+ cancel ()
209+ _ = printer .Wait ()
210+ }()
211+
212+ return load .PullImages (ctx , dockerCli .Client (), pull .imageName , pull .pullOptions , printer )
213+ }
214+
129215func pullBuild (ctx context.Context , dockerCli command.Cli , msg * cliv1.GetPullInfoResponse , userTags []string , platform string , progress string ) error {
130216 pull := buildPullOpt (msg , userTags , platform , progress )
131217 printer , cancel , err := buildPrinter (ctx , pull , progress )
0 commit comments