@@ -178,6 +178,9 @@ func (h *handler) errHandler(hfe HandleFuncE) http.HandlerFunc {
178178func (h * handler ) renderResponse (w http.ResponseWriter , r * http.Request ) error {
179179 qs := r .URL .Query ()
180180
181+ if image := qs .Get ("history" ); image != "" {
182+ return h .renderHistory (w , r , strings .TrimPrefix (strings .TrimSpace (image ), "https://" ))
183+ }
181184 if image := qs .Get ("image" ); image != "" {
182185 return h .renderManifest (w , r , strings .TrimPrefix (strings .TrimSpace (image ), "https://" ))
183186 }
@@ -572,6 +575,105 @@ func (h *handler) renderManifest(w http.ResponseWriter, r *http.Request, image s
572575 return nil
573576}
574577
578+ func (h * handler ) tagHistory (w http.ResponseWriter , r * http.Request , ref name.Reference , u string ) ([]byte , error ) {
579+ repo := ref .Context ().String ()
580+
581+ auth := authn .Anonymous
582+ if h .keychain != nil {
583+ ref , err := name .NewRepository (repo )
584+ if err == nil {
585+ maybeAuth , err := h .keychain .Resolve (ref )
586+ if err == nil {
587+ auth = maybeAuth
588+ } else {
589+ logs .Debug .Printf ("Resolve(%q) = %v" , repo , err )
590+ }
591+ } else {
592+ logs .Debug .Printf ("NewRepository(%q) = %v" , repo , err )
593+ }
594+ }
595+ tr , err := h .transportFromCookie (w , r , repo , auth )
596+ if err != nil {
597+ return nil , err
598+ }
599+
600+ req , err := http .NewRequest (http .MethodGet , u , nil )
601+ if err != nil {
602+ return nil , err
603+ }
604+
605+ req .Header .Set ("User-Agent" , h .userAgent )
606+
607+ resp , err := tr .RoundTrip (req )
608+ if err != nil {
609+ return nil , err
610+ }
611+ defer resp .Body .Close ()
612+
613+ if resp .StatusCode != http .StatusOK {
614+ return nil , fmt .Errorf ("unexpected status: %d" , resp .StatusCode )
615+ }
616+
617+ return io .ReadAll (resp .Body )
618+ }
619+
620+ // Render CGR history.
621+ func (h * handler ) renderHistory (w http.ResponseWriter , r * http.Request , image string ) error {
622+ ref , err := name .ParseReference (image , name .WeakValidation )
623+ if err != nil {
624+ return err
625+ }
626+
627+ if ref .Context ().RegistryStr () != "cgr.dev" {
628+ return fmt .Errorf ("not a cgr.dev image: %s" , image )
629+ }
630+
631+ u := fmt .Sprintf ("https://%s/v2/%s/_chainguard/history/%s" , ref .Context ().Registry , ref .Context ().RepositoryStr (), ref .Identifier ())
632+
633+ // TODO: Do we need to cache this?
634+ th , err := h .tagHistory (w , r , ref , u )
635+ if err != nil {
636+ return err
637+ }
638+
639+ header := HeaderData {
640+ Repo : ref .Context ().String (),
641+ Reference : ref .String (),
642+ JQ : `curl -H "$(crane auth token -H ` + ref .Context ().String () + `)" ` + u ,
643+ }
644+
645+ if err := headerTmpl .Execute (w , TitleData {image }); err != nil {
646+ return fmt .Errorf ("headerTmpl: %w" , err )
647+ }
648+
649+ copied := * r .URL
650+ output := & jsonOutputter {
651+ w : w ,
652+ u : & copied ,
653+ fresh : []bool {},
654+ repo : ref .Context ().String (),
655+ mt : r .URL .Query ().Get ("mt" ),
656+ }
657+
658+ // Mutates header for bodyTmpl.
659+ b , err := h .jq (output , th , r , & header )
660+ if err != nil {
661+ return fmt .Errorf ("h.jq: %w" , err )
662+ }
663+
664+ if err := bodyTmpl .Execute (w , header ); err != nil {
665+ return fmt .Errorf ("bodyTmpl: %w" , err )
666+ }
667+
668+ if err := h .renderContent (w , r , ref , b , output , copied ); err != nil {
669+ return err
670+ }
671+
672+ fmt .Fprintf (w , footer )
673+
674+ return nil
675+ }
676+
575677func (h * handler ) renderReferrers (w http.ResponseWriter , r * http.Request , src string ) error {
576678 ref , err := name .NewDigest (src )
577679 if err != nil {
@@ -1444,6 +1546,12 @@ func headerData(ref name.Reference, desc v1.Descriptor) *HeaderData {
14441546 if strings .Contains (handler , "?" ) {
14451547 sep = "&"
14461548 }
1549+
1550+ if _ , ok := ref .(name.Tag ); ok {
1551+ if ref .Context ().RegistryStr () == "cgr.dev" {
1552+ handler = "?history="
1553+ }
1554+ }
14471555 return & HeaderData {
14481556 Repo : ref .Context ().String (),
14491557 Reference : ref .String (),
0 commit comments