···1515 ?password:string ->
1616 Image.t ->
1717 files:(string * string) list ->
1818- unit
1818+ Spec.Descriptor.t
1919(** [push ~client image ~files] pushes [files] as OCI artifact layers to the
2020- registry. *)
2020+ registry and returns the manifest descriptor. *)
2121+2222+val push_index :
2323+ client:Fetch.client ->
2424+ ?username:string ->
2525+ ?password:string ->
2626+ Image.t ->
2727+ Spec.Index.t ->
2828+ Spec.Descriptor.t
2929+(** [push_index ~client image index] pushes an OCI image index to the registry
3030+ and returns the index descriptor. *)
21312232val fetch :
2333 ?show_progress:bool ->
+43-12
src/push.ml
···6060 Log.info (fun l -> l "Blob %a already exists, skipping" Digest.pp digest)
6161 else upload_blob client ~token image ~digest ~content
62626363-let put_manifest (Fetch.Client { net; clock }) ~token image ~tag manifest =
6363+let put_json (Fetch.Client { net; clock }) ~token image ~tag ~media_type_str
6464+ json =
6465 let base = Fmt.str "https://%s" (Image.registry image) in
6566 let name = Image.repository image in
6667 let url = Fmt.str "%s/v2/%s/manifests/%s" base name tag in
6767- let manifest_json = Manifest.OCI.to_string manifest in
6868 Log.debug (fun l -> l "PUT %s" url);
6969 Eio.Switch.run @@ fun sw ->
7070- let manifest_media_type = "application/vnd.oci.image.manifest.v1+json" in
7171- let mime = Requests.Mime.of_string manifest_media_type in
7070+ let mime = Requests.Mime.of_string media_type_str in
7271 let headers =
7372 Requests.Headers.(
7473 empty |> bearer token |> content_type mime
7575- |> content_length (Int64.of_int (String.length manifest_json)))
7474+ |> content_length (Int64.of_int (String.length json)))
7675 in
7777- let body = Requests.Body.of_string mime manifest_json in
7676+ let body = Requests.Body.of_string mime json in
7877 let resp = Requests.One.put ~sw ~clock ~net ~headers ~body url in
7978 let status = Requests.Response.status_code resp in
8079 if status <> 201 then
8181- Fmt.failwith "manifest push failed: %d %s" status
8282- (Requests.Response.text resp);
8383- let digest_str =
8080+ Fmt.failwith "push failed: %d %s" status (Requests.Response.text resp);
8181+ let digest =
8482 match Requests.Response.header_string "Docker-Content-Digest" resp with
8585- | Some d -> d
8686- | None -> Digest.to_string (Digest.hash SHA256 manifest_json)
8383+ | Some d -> (
8484+ match Digest.of_string d with
8585+ | Ok d -> d
8686+ | Error (`Msg _) -> Digest.hash SHA256 json)
8787+ | None -> Digest.hash SHA256 json
8888+ in
8989+ let size = Int64.of_int (String.length json) in
9090+ let media_type =
9191+ match Media_type.of_string media_type_str with
9292+ | Ok m -> m
9393+ | Error (`Msg e) -> Fmt.failwith "media_type: %s" e
8794 in
8888- Log.info (fun l -> l "Manifest pushed: %s" digest_str)
9595+ let desc = Descriptor.v ~media_type ~size digest in
9696+ Log.info (fun l -> l "Pushed: %a" Digest.pp digest);
9797+ desc
9898+9999+let put_manifest client ~token image ~tag manifest =
100100+ let json = Manifest.OCI.to_string manifest in
101101+ put_json client ~token image ~tag
102102+ ~media_type_str:"application/vnd.oci.image.manifest.v1+json" json
103103+104104+let put_index client ~token image ~tag index =
105105+ let json = Index.to_string index in
106106+ put_json client ~token image ~tag
107107+ ~media_type_str:"application/vnd.oci.image.index.v1+json" json
8910890109let run ~client ?username ?password image ~files =
91110 let credentials =
···139158 ~config:config_descriptor layer_descriptors
140159 in
141160 put_manifest client ~token image ~tag manifest
161161+162162+let run_index ~client ?username ?password image index =
163163+ let credentials =
164164+ match (username, password) with
165165+ | Some u, Some p -> Some { Fetch.API.username = u; password = p }
166166+ | _ -> None
167167+ in
168168+ let token =
169169+ Fetch.API.get_token client ?credentials ~scope_actions:"push,pull" image
170170+ in
171171+ let tag = match Image.tag image with Some t -> t | None -> "latest" in
172172+ put_index client ~token image ~tag index
+16-4
src/push.mli
···11(** Push OCI artifacts to remote container registries. *)
2233+open Oci_spec
44+35val run :
46 client:Fetch.client ->
57 ?username:string ->
68 ?password:string ->
79 Image.t ->
810 files:(string * string) list ->
99- unit
1111+ Descriptor.t
1012(** [run ~client ?username ?password image ~files] pushes [files] as OCI
1111- artifact layers to the registry. Each entry in [files] is [(name, path)]
1212- where [name] is a label and [path] is the local file path. The image
1313- reference must include a tag. *)
1313+ artifact layers to the registry and returns the manifest descriptor. Each
1414+ entry in [files] is [(name, path)] where [name] is a label and [path] is the
1515+ local file path. The image reference must include a tag. *)
1616+1717+val run_index :
1818+ client:Fetch.client ->
1919+ ?username:string ->
2020+ ?password:string ->
2121+ Image.t ->
2222+ Index.t ->
2323+ Descriptor.t
2424+(** [run_index ~client ?username ?password image index] pushes an OCI image
2525+ index to the registry and returns the index descriptor. *)
+4-2
src/spec/descriptor.ml
···5555 d.artifact_type)
5656 |> Jsont.Object.finish
57575858-let v ?platform ?data ~media_type ~size digest =
5858+let v ?platform ?data ?(annotations = []) ~media_type ~size digest =
5959 let data =
6060 match data with None -> None | Some d -> Some (Base64.encode d)
6161 in
···6464 size;
6565 digest;
6666 urls = [];
6767- annotations = [];
6767+ annotations;
6868 data;
6969 platform;
7070 artifact_type = None;
7171 }
7272+7373+let annotations t = t.annotations
72747375let of_yojson json =
7476 match Jsont_bytesrw.decode_string jsont (json_to_string json) with
+6-1
src/spec/descriptor.mli
···1717val v :
1818 ?platform:Platform.t ->
1919 ?data:string ->
2020+ ?annotations:(Annotation.t * string) list ->
2021 media_type:Media_type.t ->
2122 size:z ->
2223 Oci_spec__Digest.t ->
2324 t
2424-(** [v ?platform ?data ~media_type ~size digest] constructs a descriptor. *)
2525+(** [v ?platform ?data ?annotations ~media_type ~size digest] constructs a
2626+ descriptor. *)
2727+2828+val annotations : t -> (Annotation.t * string) list
2929+(** Return the annotations. *)
25302631val pp : t Fmt.t
2732(** Pretty-print a descriptor. *)
+5
src/spec/index.ml
···61616262let pp ppf t = pp_json ppf (to_yojson t)
6363let to_string = Fmt.to_to_string pp
6464+6565+let v ?artifact_type ?subject ?(annotations = []) ?platform manifests =
6666+ { version = V2; artifact_type; manifests; platform; subject; annotations }
6767+6468let manifests t = t.manifests
6569let platform t = t.platform
7070+let annotations t = t.annotations
+13
src/spec/index.mli
···1818val to_string : t -> string
1919(** Serialize an image index to its JSON string representation. *)
20202121+val v :
2222+ ?artifact_type:string ->
2323+ ?subject:Descriptor.t ->
2424+ ?annotations:(Annotation.t * string) list ->
2525+ ?platform:Platform.t ->
2626+ Descriptor.t list ->
2727+ t
2828+(** [v ?artifact_type ?subject ?annotations ?platform manifests] constructs an
2929+ OCI image index. *)
3030+2131val manifests : t -> Descriptor.t list
2232(** Return the list of manifest descriptors. *)
23332434val platform : t -> Platform.t option
2535(** Return the optional platform constraint of the index. *)
3636+3737+val annotations : t -> (Annotation.t * string) list
3838+(** Return the annotations. *)
+1-1
test/spec/test_oci.ml
···4444 ?password:string ->
4545 Oci.Image.t ->
4646 files:(string * string) list ->
4747- unit)
4747+ Oci.Spec.Descriptor.t)
48484949let test_submodule_reexports () =
5050 (* Verify all submodules are accessible through Oci *)