{"id":28899312,"url":"https://github.com/containers/olot","last_synced_at":"2026-02-16T14:17:29.020Z","repository":{"id":267189746,"uuid":"900350846","full_name":"containers/olot","owner":"containers","description":null,"archived":false,"fork":false,"pushed_at":"2025-07-03T07:02:04.000Z","size":4023,"stargazers_count":8,"open_issues_count":2,"forks_count":7,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-03T08:20:04.179Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/containers.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-12-08T15:00:42.000Z","updated_at":"2025-07-03T07:02:09.000Z","dependencies_parsed_at":null,"dependency_job_id":"d015610d-44c5-4ee4-aa68-85f8a386f4d9","html_url":"https://github.com/containers/olot","commit_stats":null,"previous_names":["tarilabs/olot","containers/olot"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/containers/olot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/containers%2Folot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/containers%2Folot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/containers%2Folot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/containers%2Folot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/containers","download_url":"https://codeload.github.com/containers/olot/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/containers%2Folot/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263489674,"owners_count":23474526,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-06-21T08:09:07.945Z","updated_at":"2026-02-16T14:17:29.014Z","avatar_url":"https://github.com/containers.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# oci layers on top\n\n`olot` is a python-based tool to append layers (files) onto an OCI-compatible image.\nIt is meant to be used in conjunction with command-line based tools to fetch and upload these images. Tools such as [skopeo](https://github.com/containers/skopeo) or [oras](https://github.com/oras-project/oras).\nIt leverages standard oci-layout format from the [OCI Image Layout Specification](https://github.com/opencontainers/image-spec/blob/main/image-layout.md).\nIt can be used either as a CLI tool, or via standard python interface. The package is published to [pypi](https://pypi.org/p/olot).\n\n## Usage\n\n### As a CLI\n\n1. use simple tool like `skopeo` ( or `oras cp`, or ...) and produce an [oci image layout](https://github.com/opencontainers/image-spec/blob/main/image-layout.md) of the _base image_ for the [Modelcar](https://kserve.github.io/website/latest/modelserving/storage/oci/#prepare-an-oci-image-with-model-data) (ie base image could be: A. `ubi-micro`, B. `busybox`, or C. even [simple base image for KServe Modelcar](https://github.com/tarilabs/demo20241108-base?tab=readme-ov-file#a-minimal-base-image-for-kserve-modelcarsidecar-puposes-that-does-nothing), etc.)\n2. (this project) use pure python way to add layers of the ML models, and any metadata like ModelCarD\n3. use simple tool from step 1 to push the resulting layout to the remote registry (i.e. `simpletool cp ... quay.io/mmortari/model:car`)\n4. ... you now have a Modelcar inside of your OCI registry that can be used with KServe and more!\n\n```sh\nIMAGE_DIR=download\nOCI_REGISTRY_SOURCE=quay.io/mmortari/hello-world-wait:latest\nOCI_REGISTRY_DESTINATION=quay.io/mmortari/demo20241208:latest\nrm -rf $IMAGE_DIR\n\n# Downloads the image `/quay.io/mmortari/hello-world-wait:latest` to the folder `download` with tag `latest`\nskopeo copy --multi-arch all docker://${OCI_REGISTRY_SOURCE} oci:${IMAGE_DIR}:latest\n\n# If using oras, you will need to also need to add write permissions\n# oras copy --to-oci-layout $OCI_REGISTRY_SOURCE ./${IMAGE_DIR}:latest\n# chmod +w ${IMAGE_DIR}/blobs/sha256/*\n\n# Appends to the image found in `download` the files `model.joblib` and as ModelCarD the `README.md`\npoetry run olot $IMAGE_DIR --modelcard tests/data/sample-model/README.md tests/data/sample-model/model.joblib\n\n# Pushes the (updated) image found in `download` folder to the registry `quay.io/mmortari/demo20241208` with tag `latest`\nskopeo copy --multi-arch all oci:${IMAGE_DIR}:latest docker://${OCI_REGISTRY_DESTINATION}\n\n# If using oras\n# oras cp --from-oci-layout ./${IMAGE_DIR}:latest $OCI_REGISTRY_DESTINATION\n```\n\nYou can now test the image to validate the files exist in it\n\n\n```bash\npodman run --rm -it $OCI_REGISTRY_DESTINATION ls -la /models/\n```\n\nExpected Output:\n\n```sh\nUnable to find image 'quay.io/mmortari/demo20241208:latest' locally\nlatest: Pulling from mmortari/demo20241208\n6163eebc9af4: Download complete \n1933e30a3373: Download complete \n3dc1903f1fc8: Download complete \nDigest: sha256:6effaec653c4ba4711c8bda5d18211014016e543e6657eb36ced186fb9aed9e4\nStatus: Downloaded newer image for quay.io/mmortari/demo20241208:latest\ntotal 20\ndrwxr-xr-x    1 root     root          4096 Feb 13 15:17 .\ndrwxr-xr-x    1 root     root          4096 Feb 13 15:17 ..\n-rw-rw-r--    1 root     root          6625 Dec 24 09:24 README.md\n-rw-rw-r--    1 root     root          3299 Dec 24 09:24 model.joblib\n```\n\nCleanup your local image\n\n```sh\npodman image rm quay.io/mmortari/demo20241208:latest\n```\n\n### Dev notes\n\nIf copying the resulting image to local filesystem oci-layout using skopeo, make sure to enable `--dest-oci-accept-uncompressed-layers` option.\n\n### As a Python Package\n\nInstall the package\n\n```sh\npip install olot\n# poetry add olot\n```\n\nImport and add layers onto a locally available model (using skopeo):\n\n```python\nfrom olot.basic import oci_layers_on_top\nfrom olot.backend.skopeo import skopeo_pull, skopeo_push\n\nmodel_dir = 'download'\noci_registry_source='quay.io/mmortari/hello-world-wait:latest'\noci_registry_destination='quay.io/mmortari/demo20241208:latest'\n\nmodel_files = [\n    'tests/data/sample-model/model.joblib',\n    'tests/data/sample-model/README.md',\n]\n\n# Download the model\nskopeo_pull(oci_registry_source, model_dir)\n\n# Add the layers\noci_layers_on_top(model_dir, model_files)\n\n# Push the model\nskopeo_push(model_dir, oci_registry_destination)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcontainers%2Folot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcontainers%2Folot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcontainers%2Folot/lists"}