Skip to content

UX: Running chisel cut with slices as single arg? (or sourced from a file arg) #220

Open

Description

@polarathene

TL;DR: Instead of providing slices via a list of args, there are some scenarios where a single arg is more convenient, be that a string of slices or a file config.


When experimenting with chisel, I noticed that I could technically set:

# NOTE: `SHELL` is not an OCI spec compatible instruction,
# thus won't work by default with Podman or other alternatives to Docker.
SHELL ["/chisel", "cut", "--release", "ubuntu-24.04", "--root", "/"]

And then a RUN instruction would only be the slices. However that is presently only compatible in the JSON RUN syntax since each slice is expected as a separate arg:

# JSON syntax (separate args):
RUN ["base-files_base", "bash_bins"]

# SHELL syntax (string):
RUN base-files_base bash_bins
# Equivalent to JSON syntax:
RUN ["base-files_base bash_bins"]

This fails as chisel will see that as a single slice (although I don't think spaces would be valid in a slice name?). Perhaps with a single arg for the slices it could split that into the actual slices? Or a delimiter like , could be supported?

Another option would be to source the slices via a separate config file instead of as CLI args?

  • This file could contain other CLI args such as the release channel, install root, and opt-in/out of Ubuntu Pro, etc.
  • It could also technically group slices by their suffix, such as a list of slices under base: like base-files, and bins: like bash, which might be nicer/practical than the repetitive slice suffixes?

I thought this concern was worth seeking some feedback on, as it might be nice to support running chisel without requiring a shell as demonstrated below in the reference section.

When I initially opened this report I had a misunderstanding that I couldn't use a shell for --root / on FROM scratch, requiring instead --root /root-fs (and the creation of that location, plus another stage in the build to copy from /root-fs to /). That was a user error on my part as I had mounted Nushell to /bin/nu instead of /usr/bin/local/nu.

Reference

Base image to use chisel from

Ideally in future there would be an official chisel image instead of extra steps to acquire the binary to run chisel:

FROM alpine AS chisel
ARG CHISEL_VERSION=1.1.0
ARG TARGETARCH
ARG CHISEL_RELEASE="https://github.com/canonical/chisel/releases/download/v${CHISEL_VERSION}/chisel_v${CHISEL_VERSION}_linux_${TARGETARCH}.tar.gz"
# NOTE: `--no-same-owner` used as `chisel` release has ownership of `1001:128`
RUN wget -qO - "${CHISEL_RELEASE}" | tar -xz --no-same-owner -C /usr/local/bin chisel

FROM scratch + Shell

Here's one way to then run with chisel on a scratch base (this is ok, the issue was originally created before I realized this approach also worked with --root /):

FROM scratch
ENV XDG_CACHE_HOME=/cache
SHELL ["/usr/local/bin/nu", "-c"]
RUN --mount=type=cache,target=${XDG_CACHE_HOME}/chisel,id="chisel-cache" \
    --mount=type=bind,from=chisel,source=/etc/ssl/certs/ca-certificates.crt,target=/etc/ssl/certs/ca-certificates.crt \
    --mount=type=bind,from=chisel,source=/usr/local/bin/chisel,target=/chisel \
    --mount=type=bind,from=ghcr.io/nushell/nushell,source=/usr/bin/nu,target=/usr/local/bin/nu \
    /chisel cut --release ubuntu-24.04 --root / \
      base-files_base bash_bins procps_bins grep_bins gawk_bins sed_bins coreutils_bins busybox_bins openssl_bins curl_bins

NOTES:

  • /etc/ssl/certs/ca-certificates.crt (or similar) is required.
    • This location is not configurable like some other tools (eg: curl) offer, perhaps it should be? (or better, chisel could embed a fallback? This file is about 220KB on Alpine)
    • If this ca-certificates.crt file would be modified by chisel due to slices installed, a bind mount here is probably not appropriate, you'd need an earlier COPY instead and lose some image weight efficiency (fallback or alternative path would avoid this concern).
  • Nushell has been used as the shell instead of bash, only because this easily allows for a shell that works on scratch as it's a single self-contained binary like chisel.
    • While Nushell could use additional commands, note that \ cannot be used for multi-line splitting (valid for bash) if providing RUN a HereDoc string (multi-line input, akin to an actual script, which then encodes the \ as part of that string, not as part of the RUN instruction itself that is parsed). Hence additional commands in the same RUN require familiarity with Nushell's syntax differences from Bash (&& => ;, \ => ( ... )).
  • If not using --root / with scratch, WORKDIR /root-fs + --root /root-fs or similar would be required as chisel does not yet create the --root location when it doesn't exist. A separate stage to then copy /root-fs to / would be required as chisel may fail when existing base image conflicts (such as an existing /bin that would otherwise be symlinked to /usr/bin).

FROM scratch without a shell

Alternatively, a shell can be avoided by using the RUN JSON syntax (tad awkward, but avoids running via defined SHELL):

FROM scratch
ENV XDG_CACHE_HOME=/cache
RUN --mount=type=cache,target=${XDG_CACHE_HOME}/chisel,id="chisel-cache" \
    --mount=type=bind,from=chisel,source=/etc/ssl/certs/ca-certificates.crt,target=/etc/ssl/certs/ca-certificates.crt \
    --mount=type=bind,from=chisel,source=/usr/local/bin/chisel,target=/chisel \
    [ \
      "/chisel", "cut", "--release", "ubuntu-24.04", "--root", "/", \
      "base-files_base", "bash_bins", "procps_bins", "grep_bins", "gawk_bins", "sed_bins", "coreutils_bins", "busybox_bins", "openssl_bins", "curl_bins" \
    ]

FROM scratch with shell provided by earlier chisel RUN

You could also use a mix of the two to first get a shell provisioned, then install the rest of the slices:

FROM scratch
ENV XDG_CACHE_HOME=/cache
# Required for default `SHELL ["/bin/sh", "-c"]`
# Provision `/bin/sh` (`base-files_base` creates `/bin` -> `/usr/bin` symlink, `dash_bins` creates `/usr/bin/sh` -> `/usr/bin/dash` symlink)
# Alternatively use `bash_bins` and then set `SHELL ["/usr/bin/bash", "-c"]` or create a `/bin/sh` -> `/usr/bin/bash` symlink (requires `coreutils_bins` for `ln`)
RUN --mount=type=cache,target=${XDG_CACHE_HOME}/chisel,id="chisel-cache" \
    --mount=type=bind,from=chisel,source=/etc/ssl/certs/ca-certificates.crt,target=/etc/ssl/certs/ca-certificates.crt \
    --mount=type=bind,from=chisel,source=/usr/local/bin/chisel,target=/chisel \
    [ "/chisel", "cut", "--release", "ubuntu-24.04", "--root", "/", "base-files_base", "dash_bins"]

# Now you can use `SHELL` syntax `RUN`:
RUN --mount=type=cache,target=${XDG_CACHE_HOME}/chisel,id="chisel-cache" \
    --mount=type=bind,from=chisel,source=/etc/ssl/certs/ca-certificates.crt,target=/etc/ssl/certs/ca-certificates.crt \
    --mount=type=bind,from=chisel,source=/usr/local/bin/chisel,target=/chisel \
    /chisel cut --release ubuntu-24.04 --root / base-files_base bash_bins procps_bins grep_bins gawk_bins sed_bins coreutils_bins busybox_bins openssl_bins curl_bins

NOTE: The 2nd RUN presently incurs layer inefficiency with chisel installing existing slices, about 5MB wasted in this example (48MB image instead of 43MB). This should be resolved in a future release.

Activity

  1. cjdcordeiro commented on Apr 28, 2025

    @cjdcordeiro
    Collaborator

    This is an interesting case @polarathene . As for #222 , we'll keep this one open here for gathering community interest and for later evaluation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions