Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions .github/workflows/Build-Bootstraper.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ jobs:
contents: read
packages: none
pull-requests: read
runs-on: ubuntu-latest
runs-on: macos-latest # Has FreeBSD date (with -j and -f [format] options)
outputs:
timestamp: ${{ steps.output_time.outputs.bootstrap-timestamp }}
mitl-timestamp: ${{ steps.output_time.outputs.mitl-timestamp }}
steps:
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
Expand All @@ -44,7 +45,8 @@ jobs:
printf "%s\n" "::group::bootstrap-MITL-env"
printf "bootstrap-timestamp=%s\n" $(git log -1 --pretty=%ct) >> "$GITHUB_OUTPUT"
printf "TIMESTAMP=%s\n" $(git log -1 --pretty=%ct) >> "$GITHUB_ENV"
printf "%s %s\n" "MITL-Bootstrap will be synced at time of:" $(git log -1 --pretty=%ct)
printf "mitl-timestamp=%s %s\n" $(date -j -f "%s" "$(git log -1 --pretty=%ct)" "+%C%y-%d-%m %H:%M:%S") >> "$GITHUB_OUTPUT"
printf "%s %s\n" "MITL-Bootstrap will be synced at time of:" $(date -j -f "%s" "$(git log -1 --pretty=%ct)" "+%C%y-%d-%m %H:%M:%S")
printf "%s\n" "::endgroup::"

build:
Expand All @@ -55,11 +57,17 @@ jobs:
packages: write
pull-requests: read
security-events: none
runs-on: ubuntu-latest
needs: seed
strategy:
matrix:
architecture: [amd64, arm64, arm]
include:
- architecture: amd64
runner: ubuntu-latest
- architecture: arm64
runner: ubuntu-24.04-arm
- architecture: arm
runner: ubuntu-24.04-arm
runs-on: ${{ matrix.runner }}

steps:
- name: Checkout code
Expand All @@ -74,7 +82,9 @@ jobs:
platform=linux/${{ matrix.architecture }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV

- name: Set up QEMU for multi-architecture builds
# QEMU setup only for armv7 emulation
- name: Set up QEMU (for armv7)
if: ${{ matrix.architecture == 'arm' }}
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0

- name: Set up Docker Buildx
Expand Down Expand Up @@ -116,6 +126,8 @@ jobs:
build-args: |
TOYBOX_VERSION=0.8.12
TARGETARCH=${{ matrix.architecture }}
MUSL_VER=1.2.5
MITL_DATE_EPOCH=${{ needs.seed.outputs.mitl-timestamp }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
attests: |
Expand All @@ -129,10 +141,13 @@ jobs:
index.org.opencontainers.image.authors=mitl-maintainers@users.noreply.github.com
index.org.opencontainers.image.description=Multi-arch MITL Bootstrap image
index.org.opencontainers.image.vendor=individual
index.org.opencontainers.image.licenses="MIT"
outputs: type=image,push-by-digest=true,name-canonical=true,push=true,annotation-index.org.opencontainers.image.description=Multi-arch MITL Bootstrap image ${{ matrix.architecture }}
env:
SOURCE_DATE_EPOCH: ${{ needs.seed.outputs.timestamp }}
MITL_DATE_EPOCH: ${{ needs.seed.outputs.mitl-timestamp }}
TOYBOX_VERSION: "0.8.12"
MUSL_VER: "1.2.5"
TARGETARCH: ${{ matrix.architecture }}
- name: Export digest
run: |
Expand Down
146 changes: 133 additions & 13 deletions dockerfile
Original file line number Diff line number Diff line change
@@ -1,14 +1,90 @@
# syntax=docker/dockerfile:1
ARG TOYBOX_VERSION=${TOYBOX_VERSION:-"0.8.12"}

# Stage 1: Build Musl
# Use MIT licensed Alpine as the base image for the build environment
# shellcheck disable=SC2154
FROM --platform="linux/${TARGETARCH}" alpine:latest AS musl-builder

# version is passed through by Docker.
# shellcheck disable=SC2154
ARG MUSL_VER=${MUSL_VER:-"1.2.5"}
ENV MUSL_VER=${MUSL_VER}
ENV MUSL_PREFIX="/usr/local/musl-llvm-staging"

RUN set -eux \
&& apk add --no-cache \
cmd:bsdtar \
clang \
llvm \
cmd:llvm-ar \
lld \
make \
binutils \
curl \
ca-certificates \
build-base \
gzip \
perl \
paxctl \
&& mkdir -p /build

WORKDIR /build
ENV CC=clang
ENV AR=llvm-ar
ENV RANLIB=llvm-ranlib
ENV LD=ld.lld
ENV LDFLAGS="-fuse-ld=lld"
# epoch is passed through by Docker.
# shellcheck disable=SC2154
ARG MITL_DATE_EPOCH
ENV MITL_DATE_EPOCH=${MITL_DATE_EPOCH}

# Download musl
RUN curl -fsSL \
--url "https://musl.libc.org/releases/musl-${MUSL_VER}.tar.gz" \
-o musl-${MUSL_VER}.tar.gz && \
bsdtar xf musl-${MUSL_VER}.tar.gz && \
mv musl-${MUSL_VER} musl

WORKDIR /build/musl

# Configure, build, and install musl with shared enabled (default) using LLVM tools
RUN mkdir -p ${MUSL_PREFIX} && \
./configure --prefix=${MUSL_PREFIX} && \
make CC=clang CFLAGS="${CFLAGS} -fno-math-errno -fPIC -fno-common" AR=llvm-ar LDFLAGS="${LDFLAGS}" -j"$(nproc)" && \
make install

# Ensure we have the dynamic loader and libs present (example paths)
RUN ls -l ${MUSL_PREFIX}/lib || true \
&& file ${MUSL_PREFIX}/lib/* || true

# Strip unneeded symbols from shared objects to save space (optional)
RUN set -eux \
&& if command -v llvm-strip >/dev/null 2>&1; then \
find ${MUSL_PREFIX}/lib -type f -name "*.so*" -exec llvm-strip --strip-unneeded {} + || true; \
else \
find ${MUSL_PREFIX}/lib -type f -name "*.so*" -exec strip --strip-unneeded {} + || true; \
fi

RUN touch -d ${MITL_DATE_EPOCH} ${MUSL_PREFIX}/lib/* || true \
&& touch -d ${MITL_DATE_EPOCH} ${MUSL_PREFIX}/include/* || true


# Stage 2: Build toybox based filesystem
# Use MIT licensed Alpine as the base image for the build environment
# shellcheck disable=SC2154
FROM --platform="linux/${TARGETARCH}" alpine:latest AS builder

# Set environment variables
ENV TOYBOX_VERSION=${TOYBOX_VERSION:-"0.8.12"}

# epoch is passed through by Docker.
# shellcheck disable=SC2154
ARG MITL_DATE_EPOCH
ENV MITL_DATE_EPOCH=${MITL_DATE_EPOCH}
# version is passed through by Docker.
# shellcheck disable=SC2154
ARG TOYBOX_VERSION=${TOYBOX_VERSION:-"0.8.12"}
ENV TOYBOX_VERSION=${TOYBOX_VERSION}
ENV PATH="/usr/local/bin:$PATH"
ENV CC=clang
ENV CXX=clang++
Expand All @@ -17,6 +93,11 @@ ENV RANLIB=llvm-ranlib
ENV LDFLAGS="-fuse-ld=lld"
ENV BSD=/usr/include/bsd
ENV LINUX=/usr/include/linux
# version is passed through by Docker.
# shellcheck disable=SC2154
ARG MUSL_VER=${MUSL_VER:-"1.2.5"}
ENV MUSL_VER=${MUSL_VER}
ENV MUSL_PREFIX="/usr/local/musl-llvm-staging"

# Install necessary packages
# llvm - LLVM-apache-2
Expand All @@ -29,7 +110,7 @@ ENV LINUX=/usr/include/linux
# libbsd-dev - BSD-3-Clause
# bash - GPL-3.0 - do not bundle - only until toolbox bash is compiled to run bootstrap scripts
# curl - curl License / MIT
# tar - GPL-3.0-or-later - do not bundle - used to unarchive during bootstrap
# bsdtar - BSD-2 - used to unarchive during bootstrap (transient)
# openssl-dev - Apache-2.0
# zlib-dev - zlib license

Expand All @@ -44,7 +125,7 @@ RUN --mount=type=cache,target=/var/cache/apk,sharing=locked --network=default \
linux-headers \
bash \
curl \
tar \
cmd:bsdtar \
openssl-dev \
libbsd-dev \
lld \
Expand All @@ -56,7 +137,7 @@ RUN mkdir -p /opt && \
curl -fsSL \
--url "https://github.com/landley/toybox/archive/refs/tags/${TOYBOX_VERSION}.tar.gz" \
-o toybox-${TOYBOX_VERSION}.tar.gz && \
tar -xzf toybox-${TOYBOX_VERSION}.tar.gz && \
bsdtar -xzf toybox-${TOYBOX_VERSION}.tar.gz && \
rm toybox-${TOYBOX_VERSION}.tar.gz && \
mv /opt/toybox-${TOYBOX_VERSION} /opt/toybox

Expand Down Expand Up @@ -111,32 +192,71 @@ ENV DESTDIR="/output/fs"

# Minimal etc
RUN /usr/bin/mkroot.bash && \
printf "root:x:0:0:root:/root:/bin/sh\n" > /output/fs/etc/passwd && \
printf "/dev/sda / ext4 defaults 0 1\n" > /output/fs/etc/fstab
printf "root:x:0:0:root:/root:/bin/sh\n" > "${DESTDIR}"/etc/passwd && \
printf "/dev/sda / ext4 defaults 0 1\n" > "${DESTDIR}"/etc/fstab && \
printf "# List of acceptable shells for chpass(1).\n\n/bin/sh\n/bin/bash\n" > "${DESTDIR}"/etc/shells && \
touch -d ${MITL_DATE_EPOCH} "${DESTDIR}"/etc/* || true;

# Stage 2: Create the final image
# Copy musl runtime artifacts from builder:
# - dynamic loader (ld-musl-*.so.1)
# - libmusl shared object(s) (libc.so.*)
# - crt*.o (for static linking if needed)
# - headers
COPY --from=musl-builder ${MUSL_PREFIX}/lib/ld-musl-*.so.* "${DESTDIR}"/lib/
COPY --from=musl-builder ${MUSL_PREFIX}/lib/crt*.o "${DESTDIR}"/lib/
COPY --from=musl-builder ${MUSL_PREFIX}/lib/libc.so* "${DESTDIR}"/usr/lib/
COPY --from=musl-builder ${MUSL_PREFIX}/include "${DESTDIR}"/usr/include

RUN touch -d ${MITL_DATE_EPOCH} "${DESTDIR}"/* || true \
&& touch -d ${MITL_DATE_EPOCH} "${DESTDIR}"/usr/include/* || true \
&& touch -d ${MITL_DATE_EPOCH} "${DESTDIR}"/usr/lib/* || true

# Some systems expect /lib64 -> /lib for x86_64. Create symlink if appropriate (unsupported by musl).
RUN set -eux; \
if [ "$(uname -m)" = "x86_64" ]; then \
[ -d "${DESTDIR}"/lib64 ] || ln -s /lib "${DESTDIR}"/lib64; \
fi

# Ensure loader has canonical name (example: /lib/ld-musl-x86_64.so.1)
RUN set -eux \
&& for f in "${DESTDIR}"/lib/ld-musl-*; do \
ln -fns "$f" "${DESTDIR}"/lib/ld-musl.so.1 || true; \
done || true

# Stage 3: Create the final image
# shellcheck disable=SC2154
FROM --platform="linux/${TARGETARCH}" scratch AS mitl-bootstrap

# set inherited values
LABEL version="0.7"
LABEL version="0.9"
LABEL maintainer="mitl-maintainers@users.noreply.github.com"
LABEL org.opencontainers.image.title="MITL-bootstrap"
LABEL org.opencontainers.image.description="Custom Bootstrap MITL image with toybox installed."
LABEL org.opencontainers.image.vendor="individual"
LABEL org.opencontainers.image.licenses="MIT"
LABEL org.opencontainers.image.authors="mitl-maintainers@users.noreply.github.com"
LABEL maintainer="mitl-maintainers@users.noreply.github.com"


# Copy built files
COPY --from=builder /output/fs /

# Ensure toybox is reachable at /bin/toybox (symlink if needed)
COPY --from=builder /output/fs/usr/bin/toybox /bin/toybox

SHELL [ "/bin/bash", "--norc", "-l", "-c" ]
SHELL [ "/bin/bash", "--norc", "-c" ]

# Set the entry point to Toybox
ENTRYPOINT ["/usr/bin/toybox"]
# Set the default path to a reasonable value
ENV PATH='/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/libexec'
# musl libc checks TZ
# format is
# [SUS/POSIX](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03)
# Set TZ to UTC
ENV TZ='UTC+0'
# Set PS1 to something
ENV PS1="(\A \u@\h \W) > "
# Set bash to toybox bash
ENV BASH='/bin/bash'
ENV HOSTNAME="base-builder"
CMD [ "/bin/bash", "--norc", "-l", "-c", "'exec -a bash /bin/bash -il'" ]
ENV HOSTNAME="MITL-bootstrap"
CMD [ "/bin/bash", "--norc", "-c", "'exec -a bash /bin/bash -i'" ]
20 changes: 17 additions & 3 deletions mkroot.bash
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,11 @@ HOST_TOOLCHAIN_PATH=${HOST_TOOLCHAIN_PATH}
# MacOS/Darwin host might be different like:
# HOST_TOOLCHAIN_PATH=$(xcode-select --print-path)

# allow reproducable builds based on EPOCH

MITL_DATE_EPOCH=${MITL_DATE_EPOCH}

# primitive stage0 make root script
PREFIX_STUB=${HOST_TOOLCHAIN_PATH}${PREFIX:-/usr}
BASH_CMD=$(which bash)
test -x "${BASH_CMD}" || exit 126 ; # need host bash

Expand Down Expand Up @@ -118,9 +121,11 @@ fn_host_do_cmd() {

#export -f fn_host_do_cmd ;

for FILE in bin lib sbin tmp usr usr/bin usr/libexec usr/local usr/share var Users ; do
for FILE in bin lib sbin tmp usr usr/bin usr/libexec usr/local usr/share usr/include var Users ; do
fn_host_do_cmd mkdir -p "${DESTDIR}/${FILE}" ;
fn_host_do_cmd chmod 755 "${DESTDIR}/${FILE}" || true ;
fn_host_do_cmd touch -d ${MITL_DATE_EPOCH} "${DESTDIR}/${FILE}" || true ;
fn_host_do_cmd touch -mad ${MITL_DATE_EPOCH} "${DESTDIR}/${FILE}" 2>/dev/null || true ;
done ;

# basic sym-links
Expand All @@ -135,9 +140,13 @@ fn_host_do_cmd rm "${HOST_TOOLCHAIN_PATH}/etc/os-release" 2>/dev/null || true ;

for FILE in dev etc init usr/lib mnt ; do
fn_host_do_cmd mv "${HOST_TOOLCHAIN_PATH}/${FILE}" "${DESTDIR}/${FILE}" ;
fn_host_do_cmd touch -d ${MITL_DATE_EPOCH} "${DESTDIR}/${FILE}" || true ;
fn_host_do_cmd touch -mad ${MITL_DATE_EPOCH} "${DESTDIR}/${FILE}" 2>/dev/null || true ;
done ;

fn_host_do_cmd cp -vf "${HOST_TOOLCHAIN_PATH}/usr/bin/toybox" "${DESTDIR}/usr/bin/toybox" 2>/dev/null || true ;
fn_host_do_cmd cp -f "${HOST_TOOLCHAIN_PATH}/usr/bin/toybox" "${DESTDIR}/usr/bin/toybox" 2>/dev/null || true ;
fn_host_do_cmd touch -d ${MITL_DATE_EPOCH} "${DESTDIR}/usr/bin/toybox" || true ;
fn_host_do_cmd touch -mad ${MITL_DATE_EPOCH} "${DESTDIR}/usr/bin/toybox" 2>/dev/null || true ;

for FILE in "bash" "basename" "cat" "chgrp" "chmod" "chown" "cp" "date" "dirname" "find" "grep" "head" "halt" "ls" "ln" "mkdir" "mv" "printf" "rm" "sed" ; do
fn_host_do_cmd ln -s "toybox" "${DESTDIR}/usr/bin/${FILE}" 2>/dev/null || true ;
Expand All @@ -146,6 +155,11 @@ done ;
fn_host_do_cmd ln -s "../usr/bin/toybox" "${DESTDIR}/bin/sh" 2>/dev/null || true ;
fn_host_do_cmd ln -s "../usr/bin/toybox" "${DESTDIR}/bin/bash" 2>/dev/null || true ;

for FILE in bin lib sbin tmp usr usr/bin usr/libexec usr/local usr/share usr/include var Users ; do
fn_host_do_cmd touch -d ${MITL_DATE_EPOCH} "${DESTDIR}/${FILE}"/* || true ;
fn_host_do_cmd touch -mad ${MITL_DATE_EPOCH} "${DESTDIR}/${FILE}"/* 2>/dev/null || true ;
done ;

# fn_host_do_cmd sha256sum "${DESTDIR}/${FILE}" || true ;

exit 0 ;