From fceae0dfe50c5e7d5214f3218e0ac2a41c99b92d Mon Sep 17 00:00:00 2001 From: Michael Lynch Date: Sat, 9 Nov 2024 23:20:18 -0500 Subject: [PATCH] Build binaries in CI (#369) --- .circleci/config.yml | 91 ++++++++++++++++++++--------- Dockerfile | 3 + dev-scripts/enable-multiarch-docker | 33 +++++++++++ dev-scripts/package-binaries | 49 ++++++++++++++++ 4 files changed, 150 insertions(+), 26 deletions(-) create mode 100755 dev-scripts/enable-multiarch-docker create mode 100755 dev-scripts/package-binaries diff --git a/.circleci/config.yml b/.circleci/config.yml index a98cd376..304ccb8e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,13 +1,15 @@ version: 2.1 executors: + base: + docker: + - image: cimg/base:stable go: docker: # cimg version wasn't available at upgrade time. - image: golang:1.23.3 jobs: check_whitespace: - docker: - - image: cimg/base:stable + executor: go resource_class: small steps: - checkout @@ -113,9 +115,62 @@ jobs: command: ./dev-scripts/run-e2e-tests --skip-build - store_artifacts: path: playwright-report + package_release: + executor: base + steps: + - checkout + - setup_remote_docker: + version: docker24 + docker_layer_caching: true + - run: + name: Enable multiarch builds with QEMU + command: ./dev-scripts/enable-multiarch-docker + - run: + name: Build binaries + command: | + set -eux + if [[ "${CIRCLE_TAG-''}" =~ ^[0-9]+(\.[0-9]+){2}.* || \ + "${CIRCLE_BRANCH}" == 'master' ]]; then + readonly BUILD_TARGETS='linux/arm/v7,linux/arm64,linux/amd64' + else + readonly BUILD_TARGETS='linux/amd64' + fi + docker buildx build \ + --platform "${BUILD_TARGETS}" \ + --target=artifact \ + --output "type=local,dest=$(pwd)/bin/" \ + . + - store_artifacts: + path: bin + - run: + name: Install compress utility for gzip compression + command: | + sudo apt-get update + sudo apt-get install --yes ncompress + - run: + name: Package binaries for distribution + command: | + set -eux + VERSION='' + if [[ "${CIRCLE_TAG-''}" =~ ^[0-9]+(\.[0-9]+){2}.* ]]; then + VERSION="${CIRCLE_TAG}" + elif [[ "${CIRCLE_BRANCH}" == 'master' ]]; then + VERSION="${CIRCLE_SHA1}" + fi + readonly VERSION + + if [[ -n "${VERSION}" ]]; then + ./dev-scripts/package-binaries "${VERSION}" + else + echo "Skipping packaging step" + circleci-agent step halt + fi + - persist_to_workspace: + root: ./ + paths: + - ./dist build_docker_images: - docker: - - image: cimg/base:stable + executor: go environment: BUILD_TARGETS: "linux/arm/v7,linux/arm64,linux/amd64" steps: @@ -130,26 +185,7 @@ jobs: docker login --username "${DOCKERHUB_USERNAME}" --password-stdin - run: name: Enable multiarch builds with QEMU - command: | - docker run \ - --rm \ - --privileged \ - multiarch/qemu-user-static \ - --reset \ - -p yes - - run: - name: Create multiarch build context - command: docker context create builder - - run: - name: Create multiplatform builder - command: | - docker buildx create builder \ - --name builder \ - --driver docker-container \ - --use - - run: - name: Ensure builder has booted - command: docker buildx inspect --bootstrap + command: ./dev-scripts/enable-multiarch-docker - run: name: Build docker images command: | @@ -159,8 +195,7 @@ jobs: --tag mtlynch/screenjournal:latest \ . deploy: - docker: - - image: cimg/base:stable + executor: go resource_class: small environment: # The flyctl changes too much to use a specific version, so use the latest for the @@ -215,6 +250,10 @@ workflows: only: /.*/ requires: - build_backend + - package_release: + filters: + tags: + only: /.*/ - build_docker_images: requires: - check_whitespace diff --git a/Dockerfile b/Dockerfile index d19b6ca5..56c187b4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,6 +20,9 @@ WORKDIR /app RUN TARGETPLATFORM="${TARGETPLATFORM}" \ ./dev-scripts/build-backend prod +FROM scratch AS artifact +COPY --from=builder /app/bin/screenjournal ./ + FROM debian:stable-20240311-slim AS litestream_downloader ARG TARGETPLATFORM diff --git a/dev-scripts/enable-multiarch-docker b/dev-scripts/enable-multiarch-docker new file mode 100755 index 00000000..bc8cd73b --- /dev/null +++ b/dev-scripts/enable-multiarch-docker @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +# Configure Docker to support multiarch builds, allowing it to use QEMU to build +# images targeting different CPU architectures. + +# Exit script on first failure. +set -e + +# Echo commands before executing them, by default to stderr. +set -x + +# Exit on unset variable. +set -u + +# Enable multiarch builds with QEMU. +docker run \ + --rm \ + --privileged \ + multiarch/qemu-user-static \ + --reset \ + -p yes + +# Create multiarch build context. +docker context create builder + +# Create multiplatform builder. +docker buildx create builder \ + --name builder \ + --driver docker-container \ + --use + +# Ensure builder has booted. +docker buildx inspect --bootstrap diff --git a/dev-scripts/package-binaries b/dev-scripts/package-binaries new file mode 100755 index 00000000..aac97904 --- /dev/null +++ b/dev-scripts/package-binaries @@ -0,0 +1,49 @@ +#!/usr/bin/env bash + +# Exit script on first failure. +set -e + +# Echo commands before executing them, by default to stderr. +set -x + +VERSION="$1" +if [[ -z "${VERSION}" ]]; then + >&2 echo "Must specify a version number like 1.2.3" + exit 1 +fi +readonly VERSION + +# Exit on unset variable. +set -u + +# Change directory to repository root. +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +readonly SCRIPT_DIR +cd "${SCRIPT_DIR}/.." + +cd bin + +readonly OUTPUT_DIR="${PWD}/../dist" +mkdir -p "${OUTPUT_DIR}" + +for d in ./*_*; do + FOLDER_NAME="$(basename "$d")" + + # Split FOLDER_NAME into an array with underscore as a delimiter. + IFS="_" read -r -a FOLDER_PARTS <<< "${FOLDER_NAME}" + + OS="${FOLDER_PARTS[0]}" + + # Join remaining parts and remove spaces. + FOLDER_PARTS=("${FOLDER_PARTS[@]:1}") + ARCH="${FOLDER_PARTS[*]}" + ARCH="${ARCH//[[:blank:]]}" + + pushd "$d" + tar \ + --create \ + --compress \ + --file="${OUTPUT_DIR}/screenjournal-v${VERSION}-${OS}-${ARCH}.tar.gz" \ + screenjournal + popd +done