From 14cced157cddff508c0ae612ac39caced1a05f78 Mon Sep 17 00:00:00 2001 From: Raphanus Lo Date: Wed, 12 Feb 2025 17:44:05 +0800 Subject: [PATCH] feat: self-contained container image build Signed-off-by: Raphanus Lo --- .github/workflows/build.yml | 94 ++++++++++++++++----------------- Makefile | 23 ++++++++- package/Dockerfile | 24 ++++++--- scripts/package | 100 +++++++++++++++++++++++++++--------- 4 files changed, 159 insertions(+), 82 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 089f6b8a..a7979c98 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,10 +11,48 @@ jobs: build: name: Build binaries runs-on: ubuntu-22.04 + outputs: + version_major: ${{ steps.build_info.outputs.version_major }} + version_minor: ${{ steps.build_info.outputs.version_minor }} + version_patch: ${{ steps.build_info.outputs.version_patch }} + image_tag: ${{ steps.build_info.outputs.image_tag }} + steps: - name: Checkout code uses: actions/checkout@v4 + - id: build_info + name: Declare build info + run: | + version_major='' + version_minor='' + version_patch='' + image_tag='' + + branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}} + ref=${{ github.ref }} + if [[ "$ref" =~ 'refs/tags/' ]]; then + version=$(sed -E 's/^v([0-9]*\.[0-9]*\.[0-9]*).*$/\1/' <<<$ref ) + version_major=$(cut -d. -f1 <<<$version) + version_minor=$(cut -d. -f2 <<<$version) + version_patch=$(cut -d. -f3 <<<$version) + image_tag=${{ github.ref_name }} + elif [[ "$ref" =~ 'refs/heads/' ]]; then + image_tag="${branch}-head" + fi + + echo "version_major=${version_major}" >>$GITHUB_OUTPUT + echo "version_minor=${version_minor}" >>$GITHUB_OUTPUT + echo "version_patch=${version_patch}" >>$GITHUB_OUTPUT + echo "image_tag=${image_tag}" >>$GITHUB_OUTPUT + + cat <> "$GITHUB_ENV" - - name: Login to Docker Hub uses: docker/login-action@v3 with: @@ -71,24 +79,8 @@ jobs: password: ${{ secrets.DOCKER_PASSWORD }} # longhornio/longhorn-share-manager image - - name: docker-publish - if: ${{ startsWith(github.ref, 'refs/heads/') }} - uses: docker/build-push-action@v5 - with: - context: ./ - push: true - platforms: linux/amd64,linux/arm64 - tags: longhornio/longhorn-share-manager:${{ env.branch }}-head - file: package/Dockerfile - sbom: true - - - name: docker-publish-with-tag - if: ${{ startsWith(github.ref, 'refs/tags/') }} - uses: docker/build-push-action@v5 - with: - context: ./ - push: true - platforms: linux/amd64,linux/arm64 - tags: longhornio/longhorn-share-manager:${{ github.ref_name }} - file: package/Dockerfile - sbom: true + - name: Build and publish image + env: + REPO: docker.io/longhornio + TAG: ${{ needs.build.outputs.image_tag }} + run: make workflow-image-build-push diff --git a/Makefile b/Makefile index 973a11e4..268d4101 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,11 @@ TARGETS := $(shell ls scripts) +MACHINE := longhorn +# Define the target platforms that can be used across the ecosystem. +# Note that what would actually be used for a given project will be +# defined in TARGET_PLATFORMS, and must be a subset of the below: +DEFAULT_PLATFORMS := linux/amd64,linux/arm64 -export SRC_BRANCH := $(shell bash -c 'wget -q "https://raw.githubusercontent.com/longhorn/dep-versions/main/scripts/common.sh" -O build-common.sh && source build-common.sh && get_branch') +export SRC_BRANCH := $(shell bash -c 'source <(curl -s "https://raw.githubusercontent.com/longhorn/dep-versions/main/scripts/common.sh") && get_branch') export SRC_TAG := $(shell git tag --points-at HEAD | head -n 1) .dapper: @@ -13,6 +18,22 @@ export SRC_TAG := $(shell git tag --points-at HEAD | head -n 1) $(TARGETS): .dapper ./.dapper $@ +.PHONY: buildx-machine +buildx-machine: + @docker buildx create --name=$(MACHINE) --platform=$(DEFAULT_PLATFORMS) 2>/dev/null || true + docker buildx inspect $(MACHINE) + +# variables needed from GHA caller: +# - REPO: image repo, include $registry/$repo_path +# - TAG: image tag +# - TARGET_PLATFORMS: optional, to be passed for buildx's --platform option +# - IID_FILE_FLAG: optional, options to generate image ID file +.PHONY: workflow-image-build-push workflow-image-build-push-secure +workflow-image-build-push: buildx-machine + MACHINE=$(MACHINE) PUSH='true' bash scripts/package +workflow-image-build-push-secure: buildx-machine + MACHINE=$(MACHINE) PUSH='true' IS_SECURE=true bash scripts/package + .DEFAULT_GOAL := ci .PHONY: $(TARGETS) diff --git a/package/Dockerfile b/package/Dockerfile index 3d14b978..afc1156e 100644 --- a/package/Dockerfile +++ b/package/Dockerfile @@ -1,6 +1,18 @@ # syntax=docker/dockerfile:1.13.0 +FROM registry.suse.com/bci/golang:1.23 AS app_builder -FROM registry.suse.com/bci/bci-base:15.6 AS build +WORKDIR /app + +# Copy the build script and source code +COPY . /app + +# Make the build script executable +RUN chmod +x /app/scripts/build + +# Run the build script +RUN /app/scripts/build + +FROM registry.suse.com/bci/bci-base:15.6 AS lib_builder ARG SRC_BRANCH=main ARG SRC_TAG @@ -33,14 +45,14 @@ RUN export REPO_OVERRIDE="" && \ bash /usr/src/dep-versions/scripts/build-nfs-ganesha.sh "${REPO_OVERRIDE}" "${COMMIT_ID_OVERRIDE}" -FROM registry.suse.com/bci/bci-base:15.6 +FROM registry.suse.com/bci/bci-base:15.6 AS release ARG TARGETPLATFORM RUN if [ "$TARGETPLATFORM" != "linux/amd64" ] && [ "$TARGETPLATFORM" != "linux/arm64" ]; then \ echo "Error: Unsupported TARGETPLATFORM: $TARGETPLATFORM" && \ exit 1; \ fi -ENV ARCH ${TARGETPLATFORM#linux/} +ENV ARCH=${TARGETPLATFORM#linux/} RUN zypper -n ref && \ zypper update -y @@ -64,9 +76,9 @@ RUN sed -i s/systemd// /etc/nsswitch.conf # ganesha reads /etc/mtab for mounted volumes RUN ln -sf /proc/self/mounts /etc/mtab -COPY --from=build /usr/local /usr/local/ -COPY --from=build /ganesha-extra / -COPY package/bin/longhorn-share-manager-${ARCH} /longhorn-share-manager +COPY --from=lib_builder /usr/local /usr/local/ +COPY --from=lib_builder /ganesha-extra / +COPY --from=app_builder /app/bin/longhorn-share-manager-${ARCH} /longhorn-share-manager # run ldconfig after libs have been copied RUN ldconfig diff --git a/scripts/package b/scripts/package index a7c3ce1b..e4080cc8 100755 --- a/scripts/package +++ b/scripts/package @@ -5,39 +5,91 @@ source $(dirname $0)/version cd $(dirname $0)/.. -PROJECT=`basename "$PWD"` +PROJECT=$(basename "${PWD}") -if [ ! -x ./bin/longhorn ]; then - ./scripts/build +command -v buildx >/dev/null && BUILD_CMD=(buildx) || BUILD_CMD=(docker buildx) + +# read configurable parameters +REPO=${REPO:-longhornio} +IMAGE_NAME=${IMAGE_NAME:-${PROJECT}} +TAG=${TAG:-''} +PUSH=${PUSH:-'false'} +IS_SECURE=${IS_SECURE:-'false'} +MACHINE=${MACHINE:-''} +TARGET_PLATFORMS=${TARGET_PLATFORMS:-''} +IID_FILE=${IID_FILE:-''} +IID_FILE_FLAG=${IID_FILE_FLAG:-''} +SRC_BRANCH=${SRC_BRANCH:-''} +SRC_TAG=${SRC_TAG:-''} + +BUILDER_ARGS=() +[[ $MACHINE ]] && BUILDER_ARGS+=('--builder' "$MACHINE") + +BUILDX_ARGS=() + +if [[ ${TARGET_PLATFORMS} ]] ; then + IFS='/' read -r OS ARCH <<<"${TARGET_PLATFORMS}" + BUILDX_ARGS+=('--platform' "${TARGET_PLATFORMS}") +else + case $(uname -m) in + aarch64 | arm64) + ARCH=arm64 + ;; + x86_64) + ARCH=amd64 + ;; + *) + echo "$(uname -a): unsupported architecture" + exit 1 + esac + BUILDX_ARGS+=('--platform' "linux/${ARCH}") fi -cp -r bin package/ +if [[ -z $TAG ]]; then + if API_VERSION=$(./bin/longhorn-share-manager-"${ARCH}" version --client-only | jq ".clientVersion.apiVersion"); then + TAG="v${API_VERSION}_$(date -u +%Y%m%d)" + else + TAG="${VERSION}" + fi +fi + +IMAGE="${REPO}/${IMAGE_NAME}:${TAG}" + +IFS=' ' read -r -a IID_FILE_ARGS <<<"${IID_FILE_FLAG}" +[[ -n "${IID_FILE}" && ${#IID_FILE_ARGS} == 0 ]] && IID_FILE_ARGS=('--iidfile' "${IID_FILE}") -arch=$(uname -m) -if [ "$arch" == "aarch64" ]; then - ARCH="arm64" +if [[ "${PUSH}" == 'true' ]]; then + BUILDX_ARGS+=('--push') else - ARCH="amd64" + BUILDX_ARGS+=('--load') fi -APIVERSION=`./bin/longhorn-share-manager-"$ARCH" version --client-only|jq ".clientVersion.apiVersion"` -TAG=${TAG:-"v${APIVERSION}_`date -u +%Y%m%d`"} -REPO=${REPO:-longhornio} -IMAGE=${REPO}/${PROJECT}:${TAG} +[[ $IS_SECURE == 'true' ]] && BUILDX_ARGS+=('--sbom=true' '--attest' 'type=provenance,mode=max') + +IMAGE_ARGS=(--build-arg ARCH="${ARCH}") +[[ -n "${SRC_BRANCH}" ]] && IMAGE_ARGS+=(--build-arg SRC_BRANCH="${SRC_BRANCH}") +[[ -n "${SRC_TAG}" ]] && IMAGE_ARGS+=(--build-arg SRC_TAG="${SRC_TAG}") -# update base image to get latest changes -BASE_IMAGE=`grep FROM package/Dockerfile | grep -v AS | awk '{print $2}'` -docker pull ${BASE_IMAGE} +# update base IMAGE to get latest changes +grep 'FROM.*/' package/Dockerfile | awk '{print $2}' | while read -r STAGE_BASE_IMAGE +do + docker pull "${STAGE_BASE_IMAGE}" +done +BASE_IMAGE=$(grep 'FROM.*AS release' package/Dockerfile | awk '{print $2}') echo "Building image ${IMAGE} based on ${BASE_IMAGE} with ARCH=${ARCH} SRC_BRANCH=${SRC_BRANCH} SRC_TAG=${SRC_TAG}" -buildx build \ - --load \ - --build-arg ARCH="${ARCH}" \ - --build-arg SRC_BRANCH="${SRC_BRANCH}" \ - --build-arg SRC_TAG="${SRC_TAG}" \ - -t "${IMAGE}" \ - -f package/Dockerfile . +IMAGE_BUILD_CMD_ARGS=( + build --no-cache \ + "${BUILDER_ARGS[@]}" \ + "${IID_FILE_ARGS[@]}" \ + "${BUILDX_ARGS[@]}" \ + "${IMAGE_ARGS[@]}" \ + -t "${IMAGE}" -f package/Dockerfile . +) +echo "${BUILD_CMD[@]}" "${IMAGE_BUILD_CMD_ARGS[@]}" +"${BUILD_CMD[@]}" "${IMAGE_BUILD_CMD_ARGS[@]}" -echo Built ${IMAGE} +echo "Built ${IMAGE}" -echo ${IMAGE} > ./bin/latest_image +mkdir ./bin || true +echo "${IMAGE}" > ./bin/latest_image