Skip to content

Move to buildah + skopeo#43

Merged
mergify[bot] merged 6 commits intotinkerbell:mainfrom
jacobweinstock:fix-untagged-images
Mar 7, 2026
Merged

Move to buildah + skopeo#43
mergify[bot] merged 6 commits intotinkerbell:mainfrom
jacobweinstock:fix-untagged-images

Conversation

@jacobweinstock
Copy link
Copy Markdown
Member

Description

crane operates directly on the registry for every mutation — each append, mutate, or edit-config call creates a new manifest, leaving the previous one as an untagged image. Over time this accumulated significant registry garbage that GHCR's permission model made impossible to clean up automatically.

buildah builds images locally and pushes the finished manifest in a single operation, producing zero intermediate untagged manifests. skopeo handles read-side operations (inspect, copy/retag, export).

Split the CI publish job into per-arch and combined stages so the combined image can depend on per-arch images already in the registry. Add --force flag to allow overwriting existing images when needed.

See the design doc for more details.

Fixes: #

How Has This Been Tested?

How are existing users impacted? What migration steps/scripts do we need?

Checklist:

I have:

  • updated the documentation and/or roadmap (if required)
  • added unit or e2e tests
  • provided instructions on how to upgrade

crane operates directly on the registry for every mutation — each
append, mutate, or edit-config call creates a new manifest, leaving
the previous one as an untagged image. Over time this accumulated
significant registry garbage that GHCR's permission model made
impossible to clean up automatically.

buildah builds images locally and pushes the finished manifest in a
single operation, producing zero intermediate untagged manifests.
skopeo handles read-side operations (inspect, copy/retag, export).

Split the CI publish job into per-arch and combined stages so the
combined image can depend on per-arch images already in the registry.
Add --force flag to allow overwriting existing images when needed.

Signed-off-by: Jacob Weinstock <jakobweinstock@gmail.com>
Documents the progression from ORAS to crane to buildah+skopeo and
the specific problems that motivated each migration.

Signed-off-by: Jacob Weinstock <jakobweinstock@gmail.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates CaptainOS OCI artifact publishing/tagging/pulling away from crane to a local-build workflow using buildah (write-side image construction) and skopeo (read-side inspect/copy/export), aiming to eliminate intermediate untagged registry manifests in GHCR and reduce registry garbage.

Changes:

  • Replaces crane-based OCI mutation with buildah-built images pushed in a single operation; introduces a skopeo wrapper for inspect/copy/export.
  • Updates release tooling/dependency checks and the release container image to include buildah+skopeo.
  • Refactors CI publishing into per-arch and combined stages and adds a --force flag to overwrite existing images when needed.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
docs/design-decisions/oci-tooling-buildah-skopeo.md Adds design rationale and history for moving to buildah+skopeo.
captain/util.py Updates native release dependency checks to require buildah+skopeo.
captain/skopeo.py Adds skopeo wrapper for existence checks, digest inspection, retagging, and export/extraction.
captain/buildah.py Adds buildah wrapper for local image construction and manifest list management.
captain/oci.py Reimplements publish/pull/tag flows using buildah+skopeo; adds combined/per-arch publish helpers and force behavior.
captain/docker.py Updates release container runner to support buildah execution (adds privileged + isolation env).
captain/crane.py Removes crane wrapper module.
captain/cli.py Adds --force to release publish CLI and forwards it through docker/native execution paths.
Dockerfile.release Rebuilds release image to include buildah+skopeo and related configuration.
.github/workflows/release.yml Removes crane installation step from release workflow.
.github/workflows/ci.yml Splits publishing into per-arch and combined jobs and removes crane installation steps.
Comments suppressed due to low confidence (1)

.github/workflows/release.yml:37

  • This workflow removed crane installation but doesn't install the new release dependencies (buildah/skopeo). ./build.py release pull and ./build.py release tag will fail in native mode without them. Add an explicit install step (apt) before installing Python deps / running the release commands.
      - name: Load shared config
        run: cat .github/config.env >> "$GITHUB_ENV"

      - name: Install Python dependencies
        run: pip install -r requirements.txt

      - name: Pull release artifacts (both)
        env:
          VERSION_EXCLUDE: ${{ github.ref_name }}
        run: ./build.py release pull --target both --pull-output artifacts/both


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread captain/docker.py
Comment thread captain/util.py Outdated
Comment thread Dockerfile.release Outdated
Comment thread captain/skopeo.py Outdated
Comment thread captain/skopeo.py Outdated
Comment thread .github/workflows/ci.yml
Comment thread .github/workflows/ci.yml
- Remove unused fuse-overlayfs from Dockerfile.release (vfs driver)
- Drop tar from release dependency check (no longer used)
- Raise error on missing layer blob instead of warn+continue
- Replace _safe_tar_extract with safe_extractall (rejects symlinks/devices)
- Make checksum writing idempotent (skip write if content unchanged)
- Rename --target 'both' to 'combined' across CLI, OCI, CI, and release

Signed-off-by: Jacob Weinstock <jakobweinstock@gmail.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 11 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread captain/docker.py
Comment thread captain/oci.py Outdated
Comment thread captain/oci.py
Comment thread captain/skopeo.py Outdated
Comment thread captain/skopeo.py Outdated
Comment thread captain/oci.py
Comment thread README.md
Comment thread captain/docker.py
Comment thread .github/workflows/ci.yml
Comment thread .github/workflows/release.yml
- Fix _build_platform_image docstring (timestamp applies to all commits)
- Remove unused arch param from _publish_single_arch
- Skip publish recap when combined image already exists
- Fix copy() docstring: refs don't include docker:// prefix
- Fix blob lookup order: try verbatim digest first
- Forward registry auth env vars into release container
- Update README: replace crane references with buildah/skopeo

Signed-off-by: Jacob Weinstock <jakobweinstock@gmail.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread captain/skopeo.py Outdated
Comment thread .github/workflows/ci.yml
Comment thread captain/docker.py
Comment thread captain/docker.py
Signed-off-by: Jacob Weinstock <jakobweinstock@gmail.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

.github/workflows/release.yml:50

  • This workflow logs into GHCR with docker/login-action, but build.py release pull/tag execute skopeo in the release container (default RELEASE_MODE=docker). Without forwarding an auth file into that container, pulls/retags against GHCR may fail (especially for private packages). Consider wiring REGISTRY_AUTH_FILE into the release container or performing an explicit skopeo login within it.
      - name: Log in to GHCR
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Load shared config
        run: cat .github/config.env >> "$GITHUB_ENV"

      - name: Install Python dependencies
        run: pip install -r requirements.txt

      - name: Pull release artifacts (combined)
        env:
          VERSION_EXCLUDE: ${{ github.ref_name }}
        run: ./build.py release pull --target combined --pull-output artifacts/combined

      - name: Create GitHub Release
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          gh release create "${{ github.ref_name }}" \
            artifacts/combined/* \
            --generate-notes \
            --title "${{ github.ref_name }}"

      - name: Tag OCI artifacts with version
        env:
          VERSION_EXCLUDE: ${{ github.ref_name }}
        run: ./build.py release tag ${{ github.ref_name }}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread captain/oci.py Outdated
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread captain/oci.py
Comment thread .github/workflows/ci.yml
Use a temporary local manifest name to avoid collisions on repeated
publishes, and remove local manifests and intermediate images in a
finally block after push completes.

Signed-off-by: Jacob Weinstock <jakobweinstock@gmail.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .github/workflows/ci.yml
@jacobweinstock jacobweinstock added the ready-to-merge Signal Mergify to merge the PR label Mar 7, 2026
@mergify mergify bot added the queued label Mar 7, 2026
mergify bot added a commit that referenced this pull request Mar 7, 2026
@mergify
Copy link
Copy Markdown
Contributor

mergify bot commented Mar 7, 2026

Merge Queue Status

Rule: default


This pull request spent 5 minutes 11 seconds in the queue, including 5 minutes 2 seconds running CI.

Required conditions to merge
  • #changes-requested-reviews-by=0
  • base=main
  • check-success=DCO
  • check-success=build-initramfs (amd64)
  • check-success=build-initramfs (arm64)
  • check-success=build-iso (amd64)
  • check-success=build-iso (arm64)
  • check-success=build-kernel (amd64)
  • check-success=build-kernel (arm64)
  • check-success=download-tools (amd64)
  • check-success=download-tools (arm64)
  • label!=do-not-merge
  • label=ready-to-merge
  • queue-position>=0
  • any of:
  • any of [🛡 GitHub repository ruleset rule main]:
    • check-success = build-initramfs (amd64)
    • check-neutral = build-initramfs (amd64)
    • check-skipped = build-initramfs (amd64)
  • any of [🛡 GitHub repository ruleset rule main]:
    • check-success = build-initramfs (arm64)
    • check-neutral = build-initramfs (arm64)
    • check-skipped = build-initramfs (arm64)
  • any of [🛡 GitHub repository ruleset rule main]:
    • check-success = build-kernel (amd64)
    • check-neutral = build-kernel (amd64)
    • check-skipped = build-kernel (amd64)
  • any of [🛡 GitHub repository ruleset rule main]:
    • check-success = build-kernel (arm64)
    • check-neutral = build-kernel (arm64)
    • check-skipped = build-kernel (arm64)
  • any of [🛡 GitHub repository ruleset rule main]:
    • check-success = download-tools (amd64)
    • check-neutral = download-tools (amd64)
    • check-skipped = download-tools (amd64)
  • any of [🛡 GitHub repository ruleset rule main]:
    • check-success = download-tools (arm64)
    • check-neutral = download-tools (arm64)
    • check-skipped = download-tools (arm64)
  • any of [🛡 GitHub repository ruleset rule main]:
    • check-success = build-iso (amd64)
    • check-neutral = build-iso (amd64)
    • check-skipped = build-iso (amd64)
  • any of [🛡 GitHub repository ruleset rule main]:
    • check-success = build-iso (arm64)
    • check-neutral = build-iso (arm64)
    • check-skipped = build-iso (arm64)

@mergify mergify bot merged commit 2dcad22 into tinkerbell:main Mar 7, 2026
20 checks passed
@mergify mergify bot removed the queued label Mar 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-merge Signal Mergify to merge the PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants