diff --git a/.editorconfig b/.editorconfig index 6423822..0f624b4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,7 +9,7 @@ end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true indent_style = space -indent_size = 3 +indent_size = 2 [*.{bat,cmd}] end_of_line = crlf diff --git a/.gitignore b/.gitignore index f295547..007e8f2 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,6 @@ nb-configuration.xml # patch *.orig *.rej + +# nektos/act +.actrc diff --git a/build-image.sh b/build-image.sh index 8778b4a..0b23c4a 100644 --- a/build-image.sh +++ b/build-image.sh @@ -5,65 +5,73 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-ArtifactOfProjectHomePage: https://github.com/vegardit/docker-gitea-act-runner -function curl() { - command curl -sSfL --connect-timeout 10 --max-time 30 --retry 3 --retry-all-errors "$@" -} - shared_lib="$(dirname "${BASH_SOURCE[0]}")/.shared" -[[ -e $shared_lib ]] || curl "https://raw.githubusercontent.com/vegardit/docker-shared/v1/download.sh?_=$(date +%s)" | bash -s v1 "$shared_lib" || exit 1 +[[ -e $shared_lib ]] || curl -sSfL "https://raw.githubusercontent.com/vegardit/docker-shared/v1/download.sh?_=$(date +%s)" | bash -s v1 "$shared_lib" || exit 1 # shellcheck disable=SC1091 # Not following: $shared_lib/lib/build-image-init.sh was not specified as input source "$shared_lib/lib/build-image-init.sh" ################################################# -# specify target image repo/tag +# declare image meta ################################################# gitea_act_runner_version=${GITEA_ACT_RUNNER_VERSION:-latest} -base_image_name=${DOCKER_BASE_IMAGE:-debian:stable-slim} +base_image=${DOCKER_BASE_IMAGE:-debian:stable-slim} image_repo=${DOCKER_IMAGE_REPO:-vegardit/gitea-act-runner} +platforms="linux/amd64,linux/arm64/v8,linux/arm/v7" + +declare -A image_meta=( + [authors]="Vegard IT GmbH (vegardit.com)" + [title]="$image_repo" + [description]="Docker image based on debian:stable-slim to run Gitea's act_runner as a Docker container" + [source]="$(git config --get remote.origin.url)" + [revision]="$(git rev-parse --short HEAD)" + [version]="$(git rev-parse --short HEAD)" + [created]="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" +) ################################################# # resolve gitea act runner version ################################################# case $gitea_act_runner_version in - latest) gitea_act_runner_effective_version=$(curl https://gitea.com/gitea/act_runner/releases.rss | grep -oP "releases/tag/v\K\d\.\d\.\d\d?" | head -n 1) ;; - *) gitea_act_runner_effective_version=$gitea_act_runner_version ;; + latest) gitea_act_runner_effective_version=$(curl https://gitea.com/gitea/act_runner/releases.rss | grep -oP "releases/tag/v\K\d\.\d\.\d\d?" | head -n 1) ;; + *) gitea_act_runner_effective_version=$gitea_act_runner_version ;; esac -################################################# -# calculate tags -################################################# declare -a tags=() -tags+=("$image_repo:${DOCKER_IMAGE_TAG_PREFIX:-}$gitea_act_runner_version") -tags+=("$image_repo:${DOCKER_IMAGE_TAG_PREFIX:-}$gitea_act_runner_effective_version") - -tag_args=() -for t in "${tags[@]}"; do - tag_args+=( --tag "$t" ) -done - -image_name=${tags[0]} +tags+=("${DOCKER_IMAGE_TAG_PREFIX:-}$gitea_act_runner_version") +tags+=("${DOCKER_IMAGE_TAG_PREFIX:-}$gitea_act_runner_effective_version") ################################################# -# build the image +# decide if multi-arch build ################################################# -log INFO "Building docker image [$image_name]..." -if [[ $OSTYPE == "cygwin" || $OSTYPE == "msys" ]]; then - project_root=$(cygpath -w "$project_root") +if [[ ${DOCKER_PUSH:-} == "true" || ${DOCKER_PUSH_GHCR:-} == "true" ]]; then + build_multi_arch="true" fi + +################################################# +# prepare docker +################################################# +run_step -- docker version + # https://github.com/docker/buildx/#building-multi-platform-images -set -x +run_step -- docker buildx version # ensures buildx is enabled -docker --version export DOCKER_BUILDKIT=1 -export DOCKER_CLI_EXPERIMENTAL=1 # prevents "docker: 'buildx' is not a docker command." +export DOCKER_CLI_EXPERIMENTAL=1 # prevents "docker: 'buildx' is not a docker command." in older Docker versions -# Register QEMU emulators for all architectures so Docker can run and build multi-arch images -docker run --privileged --rm ghcr.io/dockerhub-mirror/tonistiigi__binfmt --install all +if [[ ${build_multi_arch:-} == "true" ]]; then + # Use a temporary local registry to work around Docker/Buildx/BuildKit quirks, + # enabling us to build/test multiarch images locally before pushing. + run_step -- start_docker_registry LOCAL_REGISTRY + + # Register QEMU emulators so Docker can run and build multi-arch images + run_step "Install QEMU emulators" -- \ + docker run --privileged --rm ghcr.io/dockerhub-mirror/tonistiigi__binfmt --install all +fi # https://docs.docker.com/build/buildkit/configure/#resource-limiting echo " @@ -71,47 +79,71 @@ echo " max-parallelism = 3 " | sudo tee /etc/buildkitd.toml -docker buildx version # ensures buildx is enabled -docker buildx create --config /etc/buildkitd.toml --use # prevents: error: multiple platforms feature is currently not supported for docker driver. Please switch to a different driver (eg. "docker buildx create --use") -trap 'docker buildx stop' EXIT -# shellcheck disable=SC2154,SC2046 # base_layer_cache_key is referenced but not assigned / Quote this to prevent word splitting -docker buildx build "$project_root" \ - --file "image/Dockerfile" \ - --progress=plain \ - --pull \ - --build-arg INSTALL_SUPPORT_TOOLS="${INSTALL_SUPPORT_TOOLS:-0}" \ - `# using the current date as value for BASE_LAYER_CACHE_KEY, i.e. the base layer cache (that holds system packages with security updates) will be invalidate once per day` \ - --build-arg BASE_LAYER_CACHE_KEY="$base_layer_cache_key" \ - --build-arg BASE_IMAGE="$base_image_name" \ - --build-arg BUILD_DATE="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \ - --build-arg GIT_BRANCH="${GIT_BRANCH:-$(git rev-parse --abbrev-ref HEAD)}" \ - --build-arg GIT_COMMIT_DATE="$(date -d "@$(git log -1 --format='%at')" --utc +'%Y-%m-%d %H:%M:%S UTC')" \ - --build-arg GIT_COMMIT_HASH="$(git rev-parse --short HEAD)" \ - --build-arg GIT_REPO_URL="$(git config --get remote.origin.url)" \ - --build-arg GITEA_ACT_RUNNER_VERSION="$gitea_act_runner_effective_version" \ - --build-arg FLAVOR="$DOCKER_IMAGE_FLAVOR" \ - $(if [[ ${ACT:-} == "true" || ${DOCKER_PUSH:-} != "true" ]]; then \ - echo -n "--load --output type=docker"; \ - else \ - echo -n "--platform linux/amd64,linux/arm64,linux/arm/v7"; \ - fi) \ - "${tag_args[@]}" \ - $(if [[ ${DOCKER_PUSH:-} == "true" ]]; then echo -n "--push"; fi) \ - "$@" -set +x +builder_name="bx-$(date +%s)-$RANDOM" +run_step "Configure buildx builder" -- docker buildx create \ + --name "$builder_name" \ + --bootstrap \ + --config /etc/buildkitd.toml \ + --driver-opt network=host `# required for buildx to access the temporary registry` \ + --driver docker-container \ + --driver-opt image=ghcr.io/dockerhub-mirror/moby__buildkit:latest +trap 'docker buildx rm --force "$builder_name"' EXIT -if [[ ${DOCKER_PUSH:-} == "true" ]]; then - docker image pull "$image_name" + +################################################# +# build the image +################################################# +image_name=image_repo:${tags[0]} + +build_opts=( + --file "image/Dockerfile" + --builder "$builder_name" + --progress=plain + --pull + # using the current date as value for BASE_LAYER_CACHE_KEY, i.e. the base layer cache (that holds system packages with security updates) will be invalidate once per day + --build-arg BASE_LAYER_CACHE_KEY="$base_layer_cache_key" + --build-arg BASE_IMAGE="$base_image" + --build-arg GIT_BRANCH="${GIT_BRANCH:-$(git rev-parse --abbrev-ref HEAD)}" + --build-arg GIT_COMMIT_DATE="$(date -d "@$(git log -1 --format='%at')" --utc +'%Y-%m-%d %H:%M:%S UTC')" + --build-arg GITEA_ACT_RUNNER_VERSION="$gitea_act_runner_effective_version" + --build-arg FLAVOR="$DOCKER_IMAGE_FLAVOR" + --build-arg INSTALL_SUPPORT_TOOLS="${INSTALL_SUPPORT_TOOLS:-0}" +) + +for key in "${!image_meta[@]}"; do + build_opts+=(--build-arg "OCI_${key}=${image_meta[$key]}") + if [[ ${build_multi_arch:-} == "true" ]]; then + build_opts+=(--annotation "index:org.opencontainers.image.${key}=${image_meta[$key]}") + fi +done + +if [[ ${build_multi_arch:-} == "true" ]]; then + build_opts+=(--push) + build_opts+=(--sbom=true) # https://docs.docker.com/build/metadata/attestations/sbom/#create-sbom-attestations + build_opts+=(--platform "$platforms") + build_opts+=(--tag "$LOCAL_REGISTRY/$image_name") +else + build_opts+=(--output "type=docker,load=true") + build_opts+=(--tag "$image_name") fi +if [[ $OSTYPE == "cygwin" || $OSTYPE == "msys" ]]; then + project_root=$(cygpath -w "$project_root") +fi + +run_step "Building docker image [$image_name]..." -- \ + docker buildx build "${build_opts[@]}" "$project_root" + ################################################# -# test image +# load image into local docker daemon for testing ################################################# -echo -log INFO "Testing docker image [$image_name]..." -(set -x; docker run --rm "$image_name" act_runner --version) -echo +if [[ ${build_multi_arch:-} == "true" ]]; then + run_step "Load image into local daemon for testing" @@ " + docker pull '$LOCAL_REGISTRY/$image_name'; + docker tag '$LOCAL_REGISTRY/$image_name' '$image_name' + " +fi ################################################# @@ -119,21 +151,39 @@ echo ################################################# # TODO see https://gitea.com/gitea/act_runner/issues/513 if [[ ${DOCKER_AUDIT_IMAGE:-1} == "1" && $GITEA_ACT_RUNNER_VERSION == "nightly" ]]; then - bash "$shared_lib/cmd/audit-image.sh" "$image_name" + run_step "Auditing docker image [$image_name]" -- \ + bash "$shared_lib/cmd/audit-image.sh" "$image_name" fi ################################################# -# push image to ghcr.io +# test image ################################################# +run_step "Testing docker image [$image_name]" -- \ + docker run --pull=never --rm "$image_name" act_runner --version + + +################################################# +# push image +################################################# +function regctl() { + run_step "regctl ${*}" -- \ + docker run --rm \ + -u "$(id -u):$(id -g)" -e HOME -v "$HOME:$HOME" \ + -v /etc/docker/certs.d:/etc/docker/certs.d:ro \ + --network host `# required to access the temporary registry` \ + ghcr.io/regclient/regctl:latest \ + --host "reg=$LOCAL_REGISTRY,tls=disabled" \ + "${@}" +} + +if [[ ${DOCKER_PUSH:-} == "true" ]]; then + for tag in "${tags[@]}"; do + regctl image copy --referrers "$LOCAL_REGISTRY/$image_name" "docker.io/$image_repo:$tag" + done +fi if [[ ${DOCKER_PUSH_GHCR:-} == "true" ]]; then - for tag in "${tags[@]}"; do - set -x - docker run --rm \ - -u "$(id -u):$(id -g)" -e HOME -v "$HOME:$HOME" \ - -v /etc/docker/certs.d:/etc/docker/certs.d:ro \ - ghcr.io/regclient/regctl:latest \ - image copy "$tag" "ghcr.io/$tag" - set +x - done + for tag in "${tags[@]}"; do + regctl image copy --referrers "$LOCAL_REGISTRY/$image_name" "ghcr.io/$image_repo:$tag" + done fi diff --git a/image/Dockerfile b/image/Dockerfile index b6d0b58..978f0e7 100644 --- a/image/Dockerfile +++ b/image/Dockerfile @@ -1,6 +1,5 @@ #syntax=docker/dockerfile:1 -# see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/syntax.md#user-content-syntax -# see https://docs.docker.com/build/dockerfile/frontend/ +# see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md # see https://docs.docker.com/engine/reference/builder/#syntax # # SPDX-FileCopyrightText: © Vegard IT GmbH (https://vegardit.com) @@ -11,27 +10,24 @@ # https://hub.docker.com/_/debian/tags?name=stable-slim ARG BASE_IMAGE=debian:stable-slim +############################################################# +# build final image +############################################################# + # https://github.com/hadolint/hadolint/wiki/DL3006 Always tag the version of an image explicitly # hadolint ignore=DL3006 -FROM ${BASE_IMAGE} - -LABEL maintainer="Vegard IT GmbH (vegardit.com)" - -# https://github.com/hadolint/hadolint/wiki/DL3002 Last USER should not be root -# hadolint ignore=DL3002 -USER root - -SHELL ["/bin/bash", "-euo", "pipefail", "-c"] +FROM ${BASE_IMAGE} as final ARG DEBIAN_FRONTEND=noninteractive ARG LC_ALL=C +SHELL ["/bin/bash", "-euo", "pipefail", "-c"] + ARG INSTALL_SUPPORT_TOOLS=0 +ARG BASE_LAYER_CACHE_KEY ARG UPX_COMPRESS=true -ARG BASE_LAYER_CACHE_KEY - # dood|dind|dind-rootless ARG FLAVOR ARG GITEA_ACT_RUNNER_VERSION @@ -179,19 +175,6 @@ RUN --mount=type=bind,source=.shared,target=/mnt/shared </opt/build_info +GIT_REPO: $OCI_source GIT_BRANCH: $GIT_BRANCH -GIT_COMMIT: $GIT_COMMIT_HASH @ $GIT_COMMIT_DATE -IMAGE_BUILD: $BUILD_DATE" >/opt/build_info +GIT_COMMIT: $OCI_revision @ $GIT_COMMIT_DATE +IMAGE_BUILD: $OCI_created +EOT cat /opt/build_info EOF