# syntax=docker/dockerfile:1
#
# The `gg` CLI, packaged for the two families the forge's own registries
# serve.
#
# docker buildx build --target packages \
# --output type=local,dest=./cli-dist --file cli/Dockerfile .
#
# The build context is the REPOSITORY ROOT, not cli/: the copyright file
# in both packages is the repo's own LICENSE.md.
#
# One source tree, two builds, because a Rust binary is linked against
# the glibc of whatever compiled it. The .deb is built on trixie, the
# oldest Debian this forge packages itself for; the .rpm on EL9, the
# oldest glibc in the rpm world worth shipping to. Building both from
# one base would produce a package that installs cleanly and then dies
# with `version GLIBC_2.41 not found`.
#
# Each stage compiles the dependency tree from Cargo.toml + Cargo.lock
# alone before the source is copied in, so that layer survives in the
# registry build cache across commits — the same staging the server
# image does with mix.exs and mix.lock. Cache mounts would be faster
# still and are deliberately not used: `setup-buildx-action` hands the
# job a fresh builder, so a mount cache starts empty on every run while
# a layer cache does not.
#
# Both stages want a C toolchain and CMake: aws-lc-sys, under rustls, is
# compiled rather than vendored as a binary.
ARG RUST_VERSION=1.98
# The version stamp, as CI computes it. The defaults are what a bare
# `docker buildx build` (or `cargo deb` by hand) produces.
ARG PKG_VERSION=
ARG RPM_RELEASE=1
ARG MAINTAINER=
ARG SUITES="trixie forky"
# ── .deb, on Debian trixie ───────────────────────────────────────────
FROM docker.io/rust:${RUST_VERSION}-trixie AS deb-builder
# dpkg-dev provides dpkg-shlibdeps, which is what cargo-deb's `$auto`
# dependency resolution shells out to. Without it the package would
# declare no libc dependency at all — quietly, which is why the build
# checks for one before it finishes.
# The rust images carry a minimal toolchain, so the two components the
# check step needs are added explicitly rather than assumed.
RUN apt-get update \
&& apt-get install -y --no-install-recommends cmake perl pkg-config dpkg-dev \
&& rm -rf /var/lib/apt/lists/* \
&& rustup component add rustfmt clippy \
&& cargo install --locked cargo-deb
WORKDIR /src/cli
# Dependencies first, against a stub binary: everything but this crate.
COPY cli/Cargo.toml cli/Cargo.lock ./
RUN mkdir -p src \
&& echo 'fn main() {}' > src/main.rs \
&& cargo build --release --locked \
&& rm -rf src target/release/gg target/release/deps/gg-*
COPY LICENSE.md /src/LICENSE.md
COPY cli ./
ARG PKG_VERSION
ARG MAINTAINER
ARG SUITES
ARG SOURCE_DATE_EPOCH
# The CLI's own checks run here rather than in a job of their own: this
# stage already has the toolchain and the compiled dependency tree, and
# a package isn't worth building from a tree that doesn't pass them.
#
# One artifact per suite. They are the same bytes with a different
# version suffix — a trixie-built binary runs on forky, and two
# filenames are what lets both sit in the pool with each `Packages`
# index naming its own. Each is copied out inside the loop, because
# cargo-deb empties target/debian on every run.
RUN set -eux; \
mkdir -p /out; \
cargo fmt --check; \
cargo clippy --workspace --all-targets --locked -- -D warnings; \
cargo test --locked; \
for suite in $SUITES; do \
set --; \
if [ -n "$PKG_VERSION" ]; then set -- --deb-version "${PKG_VERSION}+${suite}"; fi; \
if [ -n "$MAINTAINER" ]; then set -- "$@" --maintainer "$MAINTAINER"; fi; \
cargo deb "$@"; \
cp target/debian/*.deb /out/; \
done; \
for package in /out/*.deb; do \
dpkg-deb -f "$package" Depends | grep -q libc6 \
|| { echo "$package declares no library dependencies — is dpkg-dev installed?" >&2; exit 1; }; \
done; \
ls -l /out
# ── .rpm, on EL9 ─────────────────────────────────────────────────────
# The EL9 stage does not download its own toolchain, because the `curl`
# binary in that image cannot load a CA file on the forge's runner:
#
# curl: (77) error setting certificate file: <any path>
#
# Not the file and not the network. In the same RUN, one command
# earlier, dnf pulls 50 MB of metadata and 279 packages over HTTPS
# through libcurl using this very bundle; the same image does it
# happily on a developer's machine. So the toolchain is copied in whole
# from the Debian stage instead — the official x86_64-unknown-linux-gnu
# builds target glibc 2.17, so rustc and cargo run here unmodified, and
# cargo's own libcurl is not the binary that's broken.
FROM docker.io/rust:${RUST_VERSION}-trixie AS toolchain
FROM docker.io/rockylinux/rockylinux:9 AS rpm-builder
ENV RUSTUP_HOME=/usr/local/rustup \
CARGO_HOME=/usr/local/cargo \
PATH=/usr/local/cargo/bin:${PATH}
COPY --from=toolchain /usr/local/rustup /usr/local/rustup
COPY --from=toolchain /usr/local/cargo /usr/local/cargo
# A CA bundle from an image that demonstrably reaches the network on
# that host, for everything here that still fetches: dnf, and cargo
# pulling crates.
COPY --from=toolchain /etc/ssl/certs/ca-certificates.crt /etc/pki/ca-trust/gg-ca-bundle.pem
ENV CURL_CA_BUNDLE=/etc/pki/ca-trust/gg-ca-bundle.pem \
SSL_CERT_FILE=/etc/pki/ca-trust/gg-ca-bundle.pem \
CARGO_HTTP_CAINFO=/etc/pki/ca-trust/gg-ca-bundle.pem
# The curl line is a probe, not a dependency: it records in the build
# log whether this host still has the fault, and nothing below needs it
# to pass. (Comments can't live inside the RUN — a Dockerfile joins
# continued lines into one, and a `#` would swallow the rest.)
RUN set -eux; \
dnf -y install gcc gcc-c++ make cmake perl; \
dnf clean all; \
rustc --version; \
cargo --version; \
{ curl -sSf -o /dev/null https://static.rust-lang.org/ \
&& echo "probe: the curl binary works here" \
|| echo "probe: the curl binary cannot load a CA file here"; }; \
cargo install --locked cargo-generate-rpm
WORKDIR /src/cli
COPY cli/Cargo.toml cli/Cargo.lock ./
RUN mkdir -p src \
&& echo 'fn main() {}' > src/main.rs \
&& cargo build --release --locked \
&& rm -rf src target/release/gg target/release/deps/gg-*
COPY LICENSE.md /src/LICENSE.md
COPY cli ./
ARG RPM_RELEASE
# Read straight out of the environment by cargo-generate-rpm, which
# clamps every file's mtime and the package's build time to it.
ARG SOURCE_DATE_EPOCH
# RPM has no `~` convention to lean on for pre-release ordering the way
# Debian does, so the stamp goes in the release instead: `0.ci…` sorts
# before `1`, and a later tagged 0.1.0-1 upgrades over it.
RUN set -eux; \
cargo build --release --locked; \
cargo generate-rpm --set-metadata "release = \"${RPM_RELEASE}\""; \
mkdir -p /out; \
cp target/generate-rpm/*.rpm /out/; \
ls -l /out
# ── what CI collects ─────────────────────────────────────────────────
FROM scratch AS packages
COPY --from=deb-builder /out/ /
COPY --from=rpm-builder /out/ /
neiam
/gitgud
Git Gud
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
1
Log in to mark this repository.
7.1 KiB · text
History
26334f6