diff --git a/.ci/build-all.sh b/.ci/build-all.sh new file mode 100755 index 0000000..c3bfe3d --- /dev/null +++ b/.ci/build-all.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# SPDX-License-Identifier: Apache-2.0 +# (c) 2024, Konstantin Demin +set -ef + +[ -z "${CI_DEBUG}" ] || set -xv + +r=0 + +TARGET_PLATFORMS=$(printf '%s' "${TARGET_PLATFORMS:?}" | tr ',' ' ') +for TARGET_PLATFORM in ${TARGET_PLATFORMS} ; do + export TARGET_PLATFORM + + . .ci/envsh.build + .ci/build.sh || r=$? + [ "$r" = 0 ] || break +done + +make ci-clean + +exit "$r" diff --git a/.ci/build.sh b/.ci/build.sh new file mode 100755 index 0000000..3145a44 --- /dev/null +++ b/.ci/build.sh @@ -0,0 +1,20 @@ +#!/bin/sh +# SPDX-License-Identifier: Apache-2.0 +# (c) 2024, Konstantin Demin +set -ef + +[ -z "${CI_DEBUG}" ] || set -xv + +mkdir -p dist +OUTDIR=dist +OUTSFX='-'$(printf '%s' "${TARGET_PLATFORM:?}" | tr '/' '-') + +export OUTDIR OUTSFX + +idle() { + nice -n +40 \ + chrt -i 0 \ + ionice -c 3 \ + "$@" +} +idle make clean build || make clean build diff --git a/.ci/envsh.build b/.ci/envsh.build new file mode 100644 index 0000000..b3ab1cc --- /dev/null +++ b/.ci/envsh.build @@ -0,0 +1,44 @@ +#!/bin/sh +# SPDX-License-Identifier: Apache-2.0 +# (c) 2024, Konstantin Demin +set -ef + +unset GOAMD64 GOARM GOPPC64 GOMIPS GOMIPS64 + +## produce GOOS and GOARCH from TARGET_PLATFORM +unset GOOS GOARCH _variant +IFS=/ read -r GOOS GOARCH _variant <<-EOF +${TARGET_PLATFORM:?} +EOF +## verify that GOOS and GOARCH are not empty +: "${GOOS:?}" "${GOARCH:?}" +export GOOS GOARCH +## fill env with Go-related variables +if [ -n "${_variant}" ] ; then + case "${GOARCH}" in + amd64 ) + export GOAMD64="${_variant}" ;; + arm ) + export GOARM="${_variant#v}" ;; + ppc64 | ppc64le ) + export GOPPC64="${_variant}" ;; + mips | mipsle ) + export GOMIPS="${_variant}" ;; + mips64 | mips64le ) + export GOMIPS64="${_variant}" ;; + esac +fi +unset _variant + +unset RELMODE +while : ; do + [ -n "${CI_COMMIT_BRANCH}" ] || break + [ -n "${CI_REPO_DEFAULT_BRANCH}" ] || break + + ## RELMODE is for default branch only + [ "${CI_COMMIT_BRANCH}" = "${CI_REPO_DEFAULT_BRANCH}" ] || break + export RELMODE=1 + + break +done +[ -z "${CI_COMMIT_TAG}" ] || export RELMODE=1 diff --git a/.ci/envsh.common b/.ci/envsh.common new file mode 100644 index 0000000..252055d --- /dev/null +++ b/.ci/envsh.common @@ -0,0 +1,66 @@ +#!/bin/sh +# SPDX-License-Identifier: Apache-2.0 +# (c) 2024, Konstantin Demin +set -ef + +## shifty-nifty shell goodies :) + +## do same thing as GitLab does for CI_COMMIT_REF_SLUG: +## 1. lowercase string +## 2. replace not allowed chars with '-' (squeezing repeats) +## allowed chars are: `0-9`, `a-z` and '-' +## 3. remove leading and trailing '-' (if any) +## 4. truncate string up to 63 chars +## 5. remove trailing '-' (if any) +ref_slug() { + printf '%s' "${1:?}" \ + | sed -Ez 's/^(.+)$/\L\1/;s/[^0-9a-z]+/-/g;s/^-//;s/-$//;s/^(.{1,63}).*$/\1/;s/-$//' \ + | tr -d '\0' +} + +## normalize image tag +## performs like ref_slug() except: +## - symbols '_' and '.' are allowed too +## - truncate string up to 96 chars +## - squeeze symbol sequences: +## - '-' has higher priority than surrounding (leading and trailing) symbols +## - first symbol in sequence has higher priority than following symbols +## NB: implementation is rather demonstrative than effective +image_tag_norm() { + printf '%s' "${1:?}" \ + | sed -Ez 's/^(.+)$/\L\1/;s/[^0-9a-z_.]+/-/g' \ + | sed -Ez 's/\.+/./g;s/_+/_/g;s/[_.]+-/-/g;s/-[_.]+/-/g;s/([_.])[_.]+/\1/g' \ + | sed -Ez 's/^[_.-]//;s/[_.-]$//;s/^(.{1,95}).*$/\1/;s/[_.-]$//' \ + | tr -d '\0' +} + +## misc CI things +# CI_COMMIT_SHORT_SHA="${CI_COMMIT_SHA:0:8}" +CI_COMMIT_SHORT_SHA=$(printf '%s' "${CI_COMMIT_SHA}" | cut -c 1-8) +CI_COMMIT_REF_SLUG="$(ref_slug "${CI_COMMIT_BRANCH}")" +if [ -n "${CI_COMMIT_SOURCE_BRANCH}" ] ; then + CI_COMMIT_REF_SLUG="$(ref_slug "${CI_COMMIT_SOURCE_BRANCH}")" +fi + +## image tag(s) +IMAGE_TAG="${CI_COMMIT_SHORT_SHA}-b${CI_PIPELINE_NUMBER}-${CI_COMMIT_REF_SLUG}" +EXTRA_TAGS=$(image_tag_norm "branch-${CI_COMMIT_BRANCH}") +if [ -n "${CI_COMMIT_TAG}" ] ; then + IMAGE_TAG="${CI_COMMIT_TAG}" + unset EXTRA_TAGS + ## TODO: think about "latest" tag: it should be error-prone for "backward tag push" + # EXTRA_TAGS='latest' +else + if [ -n "${CI_COMMIT_SOURCE_BRANCH}" ] ; then + echo "Running on branch '${CI_COMMIT_SOURCE_BRANCH}'" + else + if [ "${CI_COMMIT_BRANCH}" != "${CI_REPO_DEFAULT_BRANCH}" ] ; then + echo "Running on branch '${CI_COMMIT_BRANCH}'" + else + IMAGE_TAG="${CI_COMMIT_SHORT_SHA}" + fi + fi +fi +IMAGE_TAG=$(image_tag_norm "${IMAGE_TAG}") + +export CI_COMMIT_SHORT_SHA CI_COMMIT_REF_SLUG IMAGE_TAG EXTRA_TAGS diff --git a/.ci/envsh.registry b/.ci/envsh.registry new file mode 100644 index 0000000..4033b69 --- /dev/null +++ b/.ci/envsh.registry @@ -0,0 +1,14 @@ +#!/bin/sh +# SPDX-License-Identifier: Apache-2.0 +# (c) 2024, Konstantin Demin +set -ef + +## setup image registry authentication +export REGISTRY_AUTH_FILE="${PWD}/.ci/.auth.json" +if ! [ -s "${REGISTRY_AUTH_FILE}" ] ; then + if [ -z "${REGISTRY_AUTH}" ] ; then + echo 'REGISTRY_AUTH is missing' + exit 1 + fi + printf '%s' "${REGISTRY_AUTH}" > "${REGISTRY_AUTH_FILE}" +fi diff --git a/.ci/image-all.sh b/.ci/image-all.sh new file mode 100755 index 0000000..4813b17 --- /dev/null +++ b/.ci/image-all.sh @@ -0,0 +1,50 @@ +#!/bin/sh +# SPDX-License-Identifier: Apache-2.0 +# (c) 2024, Konstantin Demin +set -ef + +[ -z "${CI_DEBUG}" ] || set -xv + +: "${TARGET_PLATFORMS:?}" + +. .ci/envsh.common +. .ci/envsh.registry + +: "${IMAGE_NAME:?}" "${IMAGE_TAG:?}" +IMAGE="${IMAGE_NAME}:${IMAGE_TAG}" + +## used by .ci/image.sh +export IMAGE_MANIFEST="${IMAGE}" + +if buildah manifest exists "${IMAGE}" ; then + buildah manifest rm "${IMAGE}" +fi +buildah manifest create "${IMAGE}" + +r=0 + +TARGET_PLATFORMS=$(printf '%s' "${TARGET_PLATFORMS}" | tr ',' ' ') +for TARGET_PLATFORM in ${TARGET_PLATFORMS} ; do + export TARGET_PLATFORM + + . .ci/envsh.build + + PLATFORM_SUFFIX='-'$(printf '%s' "${TARGET_PLATFORM}" | tr '/' '-') + export PLATFORM_SUFFIX + + .ci/image.sh || r=$? + [ "$r" = 0 ] || break + + buildah manifest add "${IMAGE}" "${IMAGE}${PLATFORM_SUFFIX}" +done + +[ "$r" = 0 ] || exit "$r" + +## list built image(s) +echo +echo 'IMAGES:' +echo +buildah images --all --noheading --format 'table {{.ID}} {{.Name}}:{{.Tag}} {{.Size}} {{.CreatedAtRaw}}' --filter "reference=${IMAGE_NAME}" +echo + +buildah manifest push --all "${IMAGE}" "docker://${IMAGE}" diff --git a/.ci/image.sh b/.ci/image.sh new file mode 100755 index 0000000..fe4397d --- /dev/null +++ b/.ci/image.sh @@ -0,0 +1,22 @@ +#!/bin/sh +# SPDX-License-Identifier: Apache-2.0 +# (c) 2024, Konstantin Demin +set -ef + +. .ci/envsh.registry + +[ -z "${CI_DEBUG}" ] || set -xv + +## produce _real_ BASE_IMAGE because "static-debian12:debug-nonroot" is not multiarch image (yet) +export BASE_IMAGE="${BASE_IMAGE:?}-${GOARCH:?}" + +## build image +buildah bud \ + -t "${IMAGE_NAME}:${IMAGE_TAG}${PLATFORM_SUFFIX}" \ + -f ./Dockerfile.ci \ + ${IMAGE_MANIFEST:+ --manifest "${IMAGE_MANIFEST}" } \ + --platform "${TARGET_PLATFORM}" \ + --build-arg "TARGET_PLATFORM=${TARGET_PLATFORM}" \ + --build-arg "PLATFORM_SUFFIX=${PLATFORM_SUFFIX}" \ + --build-arg "BASE_IMAGE=${BASE_IMAGE}" \ + --network=host diff --git a/.ci/registry-login.sh b/.ci/registry-login.sh new file mode 100755 index 0000000..e6d77e2 --- /dev/null +++ b/.ci/registry-login.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# SPDX-License-Identifier: Apache-2.0 +# (c) 2024, Konstantin Demin +set -ef + +[ -z "${CI_DEBUG}" ] || set -xv + +unset _bin +for i in podman buildah skopeo ; do + if command -V "$i" >/dev/null ; then + _bin=$i + break + fi +done +: "${_bin:?}" + +. .ci/envsh.registry + +for i ; do + "${_bin}" login "$i"