From 513e365542f1598c9c2fd6e16079a6562e8570bd Mon Sep 17 00:00:00 2001 From: Stevan Andjelkovic Date: Mon, 21 Oct 2024 15:36:34 +0200 Subject: [PATCH] ci: improve dockerfile for static build --- release/Dockerfile | 114 +++++++++++++++++++++++++++++------------- release/Makefile | 39 +++++++++++---- release/entrypoint.sh | 17 ++++++- 3 files changed, 121 insertions(+), 49 deletions(-) diff --git a/release/Dockerfile b/release/Dockerfile index 81ad87c..88be2a9 100644 --- a/release/Dockerfile +++ b/release/Dockerfile @@ -1,43 +1,85 @@ -# Inspired by https://entropicthoughts.com/deploying-single-binary-haskell-web-app +ARG GHC_VERSION=9.6.6 +ARG CABAL_VERSION=3.12.1.0 -FROM docker.io/library/alpine:3.20.3 +FROM docker.io/library/alpine:3.20.3 AS build -RUN apk update \ - && apk add --no-cache \ - autoconf automake bash binutils-gold curl dpkg fakeroot file \ - findutils g++ gcc git make perl shadow tar xz \ +ARG GHC_VERSION +ARG CABAL_VERSION + +LABEL org.opencontainers.image.source=https://github.com/spex-lang/spex +LABEL org.opencontainers.image.description="GHC musl" +LABEL org.opencontainers.image.licenses=BSD-2-Clause + +# Install system dependencies needed for ghcup. +# https://www.haskell.org/ghcup/install/#system-requirements +RUN apk upgrade --no-cache \ && apk add --no-cache \ - brotli brotli-static \ - bzip2 bzip2-dev bzip2-static \ - curl libcurl curl-static \ - freetype freetype-dev freetype-static \ - gmp-dev \ - libffi libffi-dev \ - libpng libpng-static \ - ncurses-dev ncurses-static \ - openssl-dev openssl-libs-static \ - pcre pcre-dev \ - pcre2 pcre2-dev \ - sdl2 sdl2-dev \ - sdl2_image sdl2_image-dev \ - sdl2_mixer sdl2_mixer-dev \ - sdl2_ttf sdl2_ttf-dev \ - xz xz-dev \ - zlib zlib-dev zlib-static \ - && ln -s /usr/lib/libncursesw.so.6 /usr/lib/libtinfo.so.6 - -ENV GHCUP_INSTALL_BASE_PREFIX=/usr/local - -RUN curl --fail --output /bin/ghcup \ - 'https://downloads.haskell.org/ghcup/x86_64-linux-ghcup' \ + binutils-gold \ + curl \ + gcc \ + g++ \ + gmp-dev \ + gpg \ + gpg-agent \ + libc-dev \ + libffi-dev \ + make \ + musl-dev \ + ncurses-dev \ + perl \ + tar \ + xz + +# Install ghcup as per: +# https://www.haskell.org/ghcup/install/#manual-installation +RUN curl --proto '=https' --tlsv1.2 --silent --show-error --fail \ + --output /bin/ghcup \ + 'https://downloads.haskell.org/ghcup/x86_64-linux-ghcup' \ && chmod 0755 /bin/ghcup \ - && ghcup upgrade --target /bin/ghcup \ - && ghcup install cabal --set \ - && /usr/local/.ghcup/bin/cabal update + && gpg --batch --keyserver keyserver.ubuntu.com \ + --recv-keys 7D1E8AFD1D4A16D71FADA2F2CCC85C0E40C06A8C \ + && gpg --batch --keyserver keyserver.ubuntu.com \ + --recv-keys FE5AB6C91FEA597C3B31180B73EDE9E8CFBAEF01 \ + && gpg --batch --keyserver keyserver.ubuntu.com \ + --recv-keys 88B57FCF7DB53B4DB3BFA4B1588764FBE22D19C4 \ + && gpg --batch --keyserver keyserver.ubuntu.com \ + --recv-keys EAF2A9A722C0C96F2B431CA511AAD8CEDEE0CAEF \ + && ghcup install cabal $CABAL_VERSION --set \ + && ghcup install ghc $GHC_VERSION --set + +FROM docker.io/library/alpine:3.20.3 + +ARG GHC_VERSION +ARG CABAL_VERSION + +# Copy in the bare minimum possible (I think) from .ghcup (which is massive). +COPY --from=build \ + /root/.ghcup/ghc/$GHC_VERSION/lib/ghc-$GHC_VERSION/lib/x86_64-linux-ghc-$GHC_VERSION/*.so \ + /root/.ghcup/ghc/$GHC_VERSION/lib/ghc-$GHC_VERSION/lib/x86_64-linux-ghc-$GHC_VERSION/ +COPY --from=build \ + /root/.ghcup/bin/cabal /root/.ghcup/bin/cabal +COPY --from=build \ + /root/.ghcup/ghc/$GHC_VERSION/lib/ghc-$GHC_VERSION/bin/ \ + /root/.ghcup/ghc/$GHC_VERSION/lib/ghc-$GHC_VERSION/bin/ +COPY --from=build \ + /root/.ghcup/ghc/$GHC_VERSION/lib/ghc-$GHC_VERSION/lib/settings \ + /root/.ghcup/ghc/$GHC_VERSION/lib/ghc-$GHC_VERSION/lib/settings +COPY --from=build \ + /root/.ghcup/ghc/$GHC_VERSION/lib/ghc-$GHC_VERSION/lib/package.conf.d \ + /root/.ghcup/ghc/$GHC_VERSION/lib/ghc-$GHC_VERSION/lib/package.conf.d + +# Install system dependencies for cabal (curl), ghc (gmp and ncurses) and spex +# (zlib). +RUN apk upgrade --no-cache \ + && apk add --no-cache \ + curl \ + gmp-dev \ + libffi-dev \ + ncurses-dev \ + zlib-dev -ENV PATH="/usr/local/.ghcup/bin:$PATH" +ENV PATH="/root/.ghcup/bin:$PATH" -RUN ghcup install cabal 3.12.1.0 --set -RUN ghcup install ghc 9.6.6 --set +COPY --chmod=0755 entrypoint.sh /entrypoint.sh -ENTRYPOINT [ "/mnt/release/entrypoint.sh" ] +ENTRYPOINT [ "/entrypoint.sh" ] diff --git a/release/Makefile b/release/Makefile index e6d3e41..aa2e9da 100644 --- a/release/Makefile +++ b/release/Makefile @@ -1,21 +1,35 @@ -all: bin/spex +all: bin/spex bin/spex-demo-petstore -bin/spex bin/spex-demo-petstore: build-image - mkdir -p "cache/cabal-cache-musl" \ - "cache/cabal-store-musl" \ - "cache/dist-newstyle-musl" +# We can't use cabal list-bin here, because it thinks the binary is in +# ../dist-newstyle rather than cache/dist-newstyle-musl. +bin/spex: static-build + @mkdir -p bin + cp "$(shell find cache/dist-newstyle-musl \ + -name spex -type f -executable)" bin + +bin/spex-demo-petstore: static-build + @mkdir -p bin + cp "$(shell find cache/dist-newstyle-musl \ + -name spex-demo-petstore -type f -executable)" bin + +static-build: build-image + mkdir -p cache/cabal-cache-musl \ + cache/cabal-store-musl \ + cache/dist-newstyle-musl docker run \ -v "${PWD}/..":/mnt/:ro \ -v "${PWD}/cache/cabal-cache-musl":/root/.cache/cabal \ -v "${PWD}/cache/cabal-store-musl":/root/.local/state/cabal/store \ -v "${PWD}/cache/dist-newstyle-musl":/mnt/dist-newstyle \ - muslghc - mkdir -p bin - cp "$(shell cabal list-bin spex)" bin - cp "$(shell cabal list-bin petstore)" bin + ghcr.io/spex-lang/static-build:latest build-image: Dockerfile - docker build --tag muslghc . + docker build --tag ghcr.io/spex-lang/static-build . + +push-image: build-image + @echo ${GITHUB_TOKEN} | \ + docker login ghrc.io --username spex-lang --password-stdin + docker push ghcr.io/spex-lang/static-build:latest bin/spex.upx: bin/spex upx --best -q bin/spex -o bin/spex.upx @@ -25,4 +39,7 @@ clean: rm -f bin/spex.upx rm -f bin/spex-demo-petstore -.PHONY: all build-image clean +distclean: clean + rm -rf cache + +.PHONY: all static-build build-image push-image clean distclean diff --git a/release/entrypoint.sh b/release/entrypoint.sh index fc35be4..c65ad4b 100755 --- a/release/entrypoint.sh +++ b/release/entrypoint.sh @@ -1,5 +1,18 @@ #!/bin/sh +# To avoid cabal update always refetching the latest packages, we pin it to a +# specific date. This can be removed once the following bug is fixed: +# https://github.com/haskell/cabal/issues/8572 + cd /mnt \ - && cabal update \ - && cabal build exe:spex exe:spex-demo-petstore --enable-executable-static + && cabal update hackage.haskell.org,2024-10-17T07:25:36Z \ + && cabal build exe:spex exe:spex-demo-petstore --enable-executable-static + + # We can't use cabal install here, because that causes --version to break + # due to the following bug: https://github.com/acfoltzer/gitrev/issues/23 + # + # && cabal install exe:spex exe:spex-demo-petstore \ + # --enable-executable-static \ + # --install-method=copy \ + # --overwrite-policy=always \ + # --installdir=/mnt/bin