oci2git

repository·main·Indexed 19 days ago

https://github.com/virviil/oci2git

A Rust application that converts OCI container images into Git repositories. It represents each container layer as a Git commit, allowing developers to use git diff, blame, and history tools to analyze filesystem evolution. It includes a 'convert' command for repository creation and an 'fsbom' command to generate YAML-formatted filesystem bills of materials.

Tokens
12K
Snippets
52
Records
60
Agent score
64%

What's inside oci2git

  1. Understand the oci2git repository structure

    main

    When you convert an image using oci2git, the resulting Git repository follows this structure:

    • .git/: The standard Git metadata directory.
    • Image.md: A Markdown file containing the complete metadata of the original OCI image.
    • rootfs/: A directory containing the actual filesystem content extracted from the container.
  2. Analyze multiple related images in one repository

    main

    OCI2Git supports multi-image analysis. When you convert multiple images into the same output directory, OCI2Git intelligently creates branches only where the images diverge. If images share common layers, they will share a common Git history (trunk), with separate branches for unique layers.

    This is useful for comparing different versions of a base image or analyzing how different variants of an image relate to each other.

    # Convert first image to create the base repository
    oci2git postgres:16.9-alpine3.21 -o alp
    
    # Convert second image to the same output folder
    oci2git nginx:1.28.0-alpine-slim -o alp
  3. Generate a Filesystem Bill of Materials (fsbom)

    main

    The fsbom command generates a YAML report listing every file introduced or modified in each layer of an OCI image. This is useful for auditing the provenance and lifecycle of files within an image.

    Each entry in the YAML output includes:

    • type: The entry type (file, hardlink, symlink, or directory).
    • stat: The status of the entry, where n:uid:gid indicates a new file and m:uid:gid indicates a modified file.
    • path, size, mode, and target (for links).

    Note: Deleted files (OCI whiteouts) are excluded from the report.

    # Generate fsbom from a Docker image
    oci2git fsbom ubuntu:latest -o ubuntu.yml
    
    # Generate fsbom from a tarball
    oci2git fsbom -e tar image.tar -o image-bom.yml
  4. Install oci2git via Debian packages (.deb)

    main

    For Ubuntu or Debian-based systems, you can manually download and install the .deb packages from the GitHub releases page.

    For amd64 (x86_64):

    wget https://github.com/virviil/oci2git/releases/download/v0.2.5/oci2git_0.2.5_amd64.deb
    sudo dpkg -i oci2git_0.2.5_amd64.deb

    For arm64:

    wget https://github.com/virviil/oci2git/releases/download/v0.2.5/oci2git_0.2.5_arm64.deb
    sudo dpkg -i oci2git_0.2.5_arm64.deb
    # For amd64 (x86_64)
    wget https://github.com/virviil/oci2git/releases/download/v0.2.5/oci2git_0.2.5_amd64.deb
    sudo dpkg -i oci2git_0.2.5_amd64.deb
    
    # For arm64
    wget https://github.com/virviil/oci2git/releases/download/v0.2.5/oci2git_0.2.5_arm64.deb
    sudo dpkg -i oci2git_0.2.5_arm64.deb
  5. Install oci2git via Binary Tarballs

    main

    You can manually install the pre-compiled binaries for Linux, macOS, or Windows by downloading the appropriate .tar.gz archive from GitHub Releases.

    Example for Linux x86_64:

    wget https://github.com/virviil/oci2git/releases/download/v0.2.5/oci2git-linux-x86_64.tar.gz
    tar xzf oci2git-linux-x86_64.tar.gz
    sudo mv oci2git-linux-x86_64 /usr/local/bin/oci2git
    chmod +x /usr/local/bin/oci2git
  6. Track file history using Git commands

    main

    Once an image is converted to a Git repository, you can use standard Git commands to track the lifecycle of files within the /rootfs directory of the repository.

    TaskCommand
    Full history (including renames)git log --follow -- /rootfs/path/to/file
    First appearance (creation)git log --diff-filter=A -- /rootfs/path/to/file
    All changes with diffsgit log -p --follow -- /rootfs/path/to/file
    When file was deletedgit log --diff-filter=D -- /rootfs/path/to/file
    Short commit infogit log --follow --oneline -- /rootfs/path/to/file
    # Full history of a file (including renames)
    git log --follow -- /rootfs/my/file/path
    
    # First appearance (i.e. creation) - see which layer introduced the file
    git log --diff-filter=A -- /rootfs/my/file/path
    
    # All changes made to the file (with diffs)
    git log -p --follow -- /rootfs/my/file/path
    
    # When the file was deleted
    git log --diff-filter=D -- /rootfs/my/file/path
    
    # Show short commit info (concise layer history)
    git log --follow --oneline -- /rootfs/my/file/path
  7. Install OCI2Git

    main

    You can install OCI2Git using several methods depending on your operating system and preference:

    Homebrew (macOS / Linux)

    brew tap virviil/oci2git
    brew install oci2git

    Debian / Ubuntu

    Download the .deb package from the latest releases and install via dpkg:

    # Example for amd64
    wget https://github.com/Virviil/oci2git/releases/latest/download/oci2git_VERSION_amd64.deb
    sudo dpkg -i oci2git_VERSION_amd64.deb

    Arch Linux (AUR)

    yay -S oci2git-bin
    # or
    paru -S oci2git-bin

    Rust/Cargo

    cargo install oci2git
    brew tap virviil/oci2git
    brew install oci2git
  8. Install oci2git via AUR (Arch Linux)

    main

    You can install the oci2git-bin package from the Arch User Repository (AUR) using an AUR helper like yay or paru.

    Using yay:

    yay -S oci2git-bin

    Using paru:

    paru -S oci2git-bin

    Manual installation:

    git clone https://aur.archlinux.org/oci2git-bin.git
    cd oci2git-bin
    makepkg -si
    yay -S oci2git-bin
    # or
    paru -S oci2git-bin
  9. Requirements for oci2git

    main

    To use oci2git, ensure the following are installed on your system:

    • Rust 2021 edition (for building/running from source)
    • Docker CLI (required if you want to use the default Docker engine support)
    • Git (required for repository operations)
  10. Understand TarSource branch name generation

    main

    When using TarSource, branch names are constructed using a specific pattern to ensure uniqueness and traceability. The format is:

    {sanitized_filename}#{os_arch}#{short_digest}

    1. Filename: The filename is extracted (removing the extension) and sanitized to be Git-compatible (removing spaces and special characters).
    2. Architecture: The os_arch string is appended with a # separator.
    3. Digest: If the image_digest contains a sha256: prefix, it is truncated to a short version. If no prefix is present, the full digest is used.

    Examples of filename sanitization:

    • /path/to/my-image.tar $\rightarrow$ my-image
    • ubuntu 20.04.tar $\rightarrow$ ubuntu-20-04
    • file with spaces & symbols!.tar $\rightarrow$ file-with-spaces-symbols
    // Example of how a branch name is resolved:
    // image_path: "/path/to/my-image.tar"
    // os_arch: "linux-amd64"
    // image_digest: "sha256:1234567890abcdef"
    // Result: "my-image#linux-amd64#1234567890ab"
  11. Manage container image layer lineage with DigestTracker

    main

    The DigestTracker struct is used to track and validate the lineage of container image layers. It maintains a sequential record of layer metadata, allowing you to compare current image layers against previously recorded history (typically loaded from an Image.md file).

    Key Capabilities

    • Sequential Recording: Layers must be added in build order. The add_layer method enforces that the position argument matches the current number of recorded layers.
    • History Loading: You can reconstruct a tracker by parsing an existing Image.md file using load_from_file.
    • Layer Validation: Use layer_matches to verify if a specific layer in an extracted image matches a recorded entry in the tracker. A match requires:
      • Matching is_empty status.
      • Matching creation timestamps (normalized to handle Z or +00:00 formats).
      • For non-empty layers: Matching digest.
      • For empty layers: Matching command.
    • Digest Extraction: Provides utilities to derive sha256: prefixed digests from layer IDs or tarball file paths.
    // Example: Creating a tracker and adding layers sequentially
    let mut tracker = DigestTracker::new();
    
    tracker.add_layer(
        0,                                // position must be current length
        "sha256:abc123".to_string(),     // digest
        "FROM alpine".to_string(),      // command
        "2023-01-01T00:00:00Z".to_string(), // created
        false,                            // is_empty
        None,                             // comment
    );