Skip to content
Closed
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
110 changes: 110 additions & 0 deletions .github/workflows/build-verified-garbage-images.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
name: verified-garbage Image Builder

on:
workflow_dispatch:
schedule:
# Run once a week on Fridays
- cron: "0 0 * * FRI"
pull_request:
paths:
- '.github/workflows/build-verified-garbage-images.yml'
- 'verified-garbage/**'
push:
branches:
- main
paths:
- '.github/workflows/build-verified-garbage-images.yml'
- 'verified-garbage/**'

jobs:
# The 32-bit userspaces verified-garbage's CI runs its x86 and ARMv7 jobs
# in, each natively on a 64-bit runner of its architecture.
build:
runs-on: ${{ matrix.IMAGE.RUNNER }}
timeout-minutes: 20
permissions:
contents: read
packages: write
id-token: write
attestations: write

strategy:
fail-fast: false
matrix:
IMAGE:
- {TAG_NAME: "verified-garbage-runner:i686", DOCKER_PLATFORM: "linux/386", RUNNER: "ubuntu-latest"}
- {TAG_NAME: "verified-garbage-runner:armv7l", DOCKER_PLATFORM: "linux/arm/v7", RUNNER: "ubuntu-24.04-arm"}

name: "${{ matrix.IMAGE.TAG_NAME }}"
steps:
- uses: actions/checkout@v7.0.1
with:
persist-credentials: false
# Pull the previous image, but if it fails return true anyway.
# Sometimes we add new docker images and if they've never been pushed
# they can't be pulled.
- name: Pull existing image
run: docker pull --platform ${{ matrix.IMAGE.DOCKER_PLATFORM }} ghcr.io/pyca/${{ matrix.IMAGE.TAG_NAME }} || true
if: github.event_name != 'schedule' && github.event_name != 'workflow_dispatch'

- name: Get image name
id: image-name
run: |
ATTEST_IMAGE=$(echo "${{ matrix.IMAGE.TAG_NAME }}" | cut -d ':' -f 1)
echo "ATTEST_IMAGE=${ATTEST_IMAGE}" | tee -a "$GITHUB_OUTPUT"

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@594f3bf4285d9ea8dc53c9a0c9c4092420091003 # v4.4.0
with:
cache-binary: false
- name: Login to ghcr.io
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
if: (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && github.ref == 'refs/heads/main'
- name: Build (and push if main) docker image
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
id: docker-build
with:
context: verified-garbage
cache-from: ghcr.io/pyca/${{ matrix.IMAGE.TAG_NAME }}
tags: ghcr.io/pyca/${{ matrix.IMAGE.TAG_NAME }}
platforms: ${{ matrix.IMAGE.DOCKER_PLATFORM }}
pull: true
outputs: ${{ ((github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && github.ref == 'refs/heads/main') && 'type=registry,compression=zstd' || 'type=docker' }}
# What verified-garbage's jobs need: the runner's Node (which GitHub
# Actions injects into the container for JS actions), a toolchain
# that builds and runs for the host and each installed target, and one
# that rustup can update in place (exercising the uninstall path even if
# stable has not changed since the image was built; this container is
# disposable).
- name: Check the image
run: |
docker run --rm --platform ${{ matrix.IMAGE.DOCKER_PLATFORM }} \
--pull=${{ github.ref == 'refs/heads/main' && github.event_name != 'pull_request' && 'always' || 'never' }} \
-v "$(readlink -f "$(command -v node)")":/runner-node:ro \
ghcr.io/pyca/${{ matrix.IMAGE.TAG_NAME }} \
sh -euxc '
/runner-node -e "console.log(process.version, process.arch)"
git --version
python3 -V
openssl version
pkg-config --exists openssl
rustc -vV
cargo clippy -V
cargo fmt --version
command -v "$(rustc --print sysroot)/lib/rustlib/$(rustc -vV | sed -n "s/^host: //p")/bin/llvm-cov"
cd /tmp && cargo new -q hello && cd hello
for target in $(rustup target list --installed); do
cargo run -q --target "$target"
done
rustup component remove cargo clippy llvm-tools rustc rust-std rustfmt
'
- uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
with:
subject-name: "ghcr.io/pyca/${{ steps.image-name.outputs.ATTEST_IMAGE }}"
subject-digest: ${{ steps.docker-build.outputs.digest }}
push-to-registry: true
if: (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && github.ref == 'refs/heads/main'
105 changes: 105 additions & 0 deletions verified-garbage/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# The 32-bit Linux userspaces verified-garbage's CI runs its x86 (i386) and
# ARMv7 (armhf) jobs in, natively on a 64-bit runner: build it for
# linux/386 on an x86-64 runner and for linux/arm/v7 on an ARM64 one.
FROM debian:trixie-slim

# Increment this to blow away the docker cache
ENV CACHE_BUSTER=1

# This is needed because otherwise `sys.getfilesystemencoding()` returns
# "ANSI_X3.4-1968".
ENV LANG=C.UTF-8

# Docker overlay filesystems can reject directory renames during toolchain
# updates with EXDEV. Allow rustup to copy the files in that case.
ENV RUSTUP_PERMIT_COPY_RENAME=1

# Don't unpack things nothing in CI uses: static OpenSSL, gcc's LTO backend,
# and the sanitizer runtimes (hard deps of libgcc-dev).
RUN printf '%s\n' \
'path-exclude=/usr/lib/*/libcrypto.a' \
'path-exclude=/usr/lib/*/libssl.a' \
'path-exclude=/usr/libexec/gcc/*/*/lto1' \
'path-exclude=/usr/bin/*-lto-dump-*' \
'path-exclude=/usr/lib/*/libasan*' \
'path-exclude=/usr/lib/*/libhwasan*' \
'path-exclude=/usr/lib/*/liblsan*' \
'path-exclude=/usr/lib/*/libtsan*' \
'path-exclude=/usr/lib/*/libubsan*' \
'path-exclude=/usr/lib/gcc/*/*/libasan*' \
'path-exclude=/usr/lib/gcc/*/*/libhwasan*' \
'path-exclude=/usr/lib/gcc/*/*/liblsan*' \
'path-exclude=/usr/lib/gcc/*/*/libtsan*' \
'path-exclude=/usr/lib/gcc/*/*/libubsan*' \
> /etc/dpkg/dpkg.cfg.d/excludes-unused-dev-files

# Only what the jobs use:
# * the runner's (64-bit) libc and libstdc++, so that the Node binary GitHub
# Actions injects into the container for JS actions can run;
# * gcc and libc6-dev, rustc's linker (with binutils' readelf);
# * git and ca-certificates, for actions/checkout;
# * python3, for the coverage export and the benchmark comparison;
# * zstd, for actions/cache (through Swatinem/rust-cache);
# * libssl-dev, pkg-config and openssl, for the benchmarks, which link
# OpenSSL (rust-openssl) and record its version.
RUN case "$(dpkg --print-architecture)" in \
i386) runner_arch=amd64 ;; \
armhf) runner_arch=arm64 ;; \
*) echo "unsupported architecture: $(dpkg --print-architecture)" >&2; exit 1 ;; \
esac && \
dpkg --add-architecture "$runner_arch" && \
apt-get -qq update && apt-get install -qq -y --no-install-recommends \
"libc6:$runner_arch" \
"libstdc++6:$runner_arch" \
gcc \
libc6-dev \
git \
ca-certificates \
python3 \
libssl-dev \
pkg-config \
openssl \
zstd && \
apt-get clean && \
rm -rf /var/lib/apt/lists

# A complete, unstripped toolchain in a fixed home (the jobs' HOME is
# /github/home), so that the jobs' `rustup toolchain install` can update it
# in place. The host is the userspace's, not the kernel's, and the stable
# toolchain has every component and target the jobs install, so that they
# download nothing until stable changes. Deduplicate libLLVM and strip the
# big binaries (with llvm-strip; GNU strip breaks them), as the other runner
# images do. Keep all files recorded in rustup's component manifests:
# deleting even unused files prevents rustup from uninstalling or updating
# the toolchain.
ENV RUSTUP_HOME=/usr/local/rustup \
CARGO_HOME=/usr/local/cargo \
PATH=/usr/local/cargo/bin:$PATH
RUN case "$(dpkg --print-architecture)" in \
i386) host=i686-unknown-linux-gnu; targets= ;; \
armhf) host=armv7-unknown-linux-gnueabihf; targets=thumbv7neon-unknown-linux-gnueabihf ;; \
esac && \
url="https://static.rust-lang.org/rustup/dist/$host/rustup-init" && \
python3 -c 'import sys, urllib.request; urllib.request.urlretrieve(sys.argv[1], sys.argv[2])' "$url" /tmp/rustup-init && \
python3 -c 'import sys, urllib.request; sys.stdout.write(urllib.request.urlopen(sys.argv[1]).read().decode().split()[0] + " /tmp/rustup-init\n")' "$url.sha256" | sha256sum -c - && \
chmod +x /tmp/rustup-init && \
/tmp/rustup-init -y --no-modify-path --profile minimal --default-host "$host" \
--default-toolchain stable --component clippy,rustfmt,llvm-tools \
${targets:+--target "$targets"} && \
rm /tmp/rustup-init && \
sysroot="$(rustc --print sysroot)" && \
cd "$sysroot/lib/rustlib/$host" && \
for lib in lib/libLLVM*; do \
if [ ! -L "$lib" ] && cmp -s "$lib" "$sysroot/lib/${lib#lib/}"; then \
ln -sf "../../../${lib#lib/}" "$lib"; \
fi; \
done && \
for f in "$sysroot/bin/cargo" "$sysroot/bin/rustc" \
"$sysroot/bin/clippy-driver" "$sysroot/bin/cargo-clippy" \
"$sysroot/bin/rustfmt" "$sysroot/bin/cargo-fmt" \
"$sysroot"/lib/librustc_driver-*.so "$sysroot"/lib/libLLVM.so.* \
"$CARGO_HOME/bin/rustup" \
bin/rust-lld bin/llvm-cov bin/llvm-profdata; do \
if [ -f "$f" ]; then bin/llvm-strip --strip-all "$f"; fi; \
done && \
rustc -vV && cargo -V
Loading