diff --git a/ansible/roles/repository/templates/Jenkinsfile.j2 b/ansible/roles/repository/templates/Jenkinsfile.j2 index e384036a..cab8e66a 100644 --- a/ansible/roles/repository/templates/Jenkinsfile.j2 +++ b/ansible/roles/repository/templates/Jenkinsfile.j2 @@ -1,3 +1,44 @@ +{# bash function retrying ghcr pulls and builds on rate limit, written to the workspace once and inlined on remote nodes #} +{% set retry_ghcr_fn %} +retry_ghcr() { + local max_attempts=7 + local base_delay=10 + local max_delay=90 + local attempt=1 + local errfile exit_code delay pause + errfile=$(mktemp) + while true; do + # run inside an if so a caller's set -e cannot abort before we get to retry + if "$@" 2> >(tee "${errfile}" >&2); then + exit_code=0 + else + exit_code=$? + fi + # make sure tee has finished writing before we read the file + wait $! 2>/dev/null || true + if [[ "${exit_code}" == "0" ]]; then + rm -f "${errfile}" + return 0 + elif ! grep -q -i -E "retry-after:|toomanyrequests|too many requests" "${errfile}"; then + rm -f "${errfile}" + return "${exit_code}" + elif (( attempt >= max_attempts )); then + rm -f "${errfile}" + echo "retry: $* failed after ${attempt} attempts (exit ${exit_code})" >&2 + return "${exit_code}" + else + # exponential delay: base * 2^(attempt-1), capped at max_delay + delay=$(( base_delay * 2 ** (attempt - 1) )) + (( delay > max_delay )) && delay="${max_delay}" + # jitter between half and full delay so parallel builds spread out + pause=$(( delay / 2 + RANDOM % (delay / 2 + 1) )) + echo "retry: attempt ${attempt}/${max_attempts} failed (exit ${exit_code}); waiting ${pause}s..." >&2 + sleep "${pause}" + ((attempt++)) + fi + done +} +{% endset %} pipeline { agent { label 'X86-64-MULTI' @@ -37,6 +78,19 @@ pipeline { ''' } } + /* ############################ + Bash Function For GHCR Retry + ############################ */ + // Create a reusable script for a ghcr retry bash function + stage("Create Bash Function"){ + steps{ + sh '''#! /bin/bash + # create our retry function + {{ retry_ghcr_fn | indent(14) }} + declare -f retry_ghcr > ${WORKSPACE}/retry_ghcr.sh + ''' + } + } // Setup all the basic environment variables needed for the build stage("Set ENV Variables base"){ steps{ @@ -64,7 +118,9 @@ pipeline { env.CI_TEST_ATTEMPTED = '' env.PUSH_ATTEMPTED = '' env.LS_RELEASE = sh( - script: '''docker run --rm quay.io/skopeo/stable:v1 inspect docker://ghcr.io/${LS_USER}/${CONTAINER_NAME}:{{ release_tag }} 2>/dev/null | jq -r '.Labels.build_version' | awk '{print $3}' | grep '\\-ls' || : ''', + script: '''#! /bin/bash + source ${WORKSPACE}/retry_ghcr.sh + retry_ghcr docker run --rm quay.io/skopeo/stable:v1 inspect docker://ghcr.io/${LS_USER}/${CONTAINER_NAME}:{{ release_tag }} | jq -r '.Labels.build_version' | awk '{print $3}' | grep '\\-ls' || : ''', returnStdout: true).trim() env.LS_RELEASE_NOTES = sh( script: '''cat readme-vars.yml | awk -F \\" '/date: "[0-9][0-9].[0-9][0-9].[0-9][0-9]:/ {print $4;exit;}' | sed -E ':a;N;$!ba;s/\\r{0,1}\\n/\\\\n/g' ''', @@ -85,8 +141,12 @@ pipeline { if ( env.SYFT_IMAGE_TAG == null ) { env.SYFT_IMAGE_TAG = 'latest' } + if ( env.GITLAB_TIMEOUT == null ) { + env.GITLAB_TIMEOUT = '600' + } } echo "Using syft image tag ${SYFT_IMAGE_TAG}" + echo "Using gitlab registry timeout ${GITLAB_TIMEOUT}" sh '''#! /bin/bash echo "The default github branch detected as ${GH_DEFAULT_BRANCH}" ''' script{ @@ -210,7 +270,9 @@ pipeline { // If this is a deb repo release calculate a hash for the package version stage("Set EXT tag deb repo"){ steps{ - sh '''docker pull ${DIST_IMAGE}:${DIST_TAG}''' + sh '''#! /bin/bash + source ${WORKSPACE}/retry_ghcr.sh + retry_ghcr docker pull ${DIST_IMAGE}:${DIST_TAG}''' script{ env.EXT_RELEASE = sh( script: '''docker run --rm ${DIST_IMAGE}:${DIST_TAG} bash -c\ @@ -426,21 +488,24 @@ pipeline { // Build Docker container local templating CI runs stage('Build-Jenkins-Builder') { steps { - sh "docker buildx build \ - --label \"org.opencontainers.image.created=${GITHUB_DATE}\" \ - --label \"org.opencontainers.image.authors={{ lsio_project_name }}\" \ - --label \"org.opencontainers.image.url={{ project_github_repo_url }}/packages\" \ - --label \"org.opencontainers.image.documentation={{ lsio_docs_url }}/images/docker-{{ project_name }}\" \ - --label \"org.opencontainers.image.source={{ project_github_repo_url }}\" \ - --label \"org.opencontainers.image.version=${EXT_RELEASE_CLEAN}-ls${LS_TAG_NUMBER}\" \ - --label \"org.opencontainers.image.revision=${COMMIT_SHA}\" \ - --label \"org.opencontainers.image.vendor={{ lsio_project_name }}\" \ - --label \"org.opencontainers.image.licenses=GPL-3.0-only\" \ - --label \"org.opencontainers.image.ref.name=${COMMIT_SHA}\" \ - --label \"org.opencontainers.image.title={{ project_name|capitalize }}\" \ - --label \"org.opencontainers.image.description={% if project_blurb is defined %}{{ project_blurb | replace('"', '') | replace('\n', ' ') }}{% else %}{{ project_name }} image by {{ lsio_project_name }}{% endif %}\" \ - --provenance=false --sbom=false --builder=default \ - --no-cache --pull -t jenkinslocal:${COMMIT_SHA}-${BUILD_NUMBER} --platform=linux/amd64 ." + sh '''#! /bin/bash + source ${WORKSPACE}/retry_ghcr.sh + retry_ghcr docker buildx build \ + --label "org.opencontainers.image.created=${GITHUB_DATE}" \ + --label "org.opencontainers.image.authors={{ lsio_project_name }}" \ + --label "org.opencontainers.image.url={{ project_github_repo_url }}/packages" \ + --label "org.opencontainers.image.documentation={{ lsio_docs_url }}/images/docker-{{ project_name }}" \ + --label "org.opencontainers.image.source={{ project_github_repo_url }}" \ + --label "org.opencontainers.image.version=${EXT_RELEASE_CLEAN}-ls${LS_TAG_NUMBER}" \ + --label "org.opencontainers.image.revision=${COMMIT_SHA}" \ + --label "org.opencontainers.image.vendor={{ lsio_project_name }}" \ + --label "org.opencontainers.image.licenses=GPL-3.0-only" \ + --label "org.opencontainers.image.ref.name=${COMMIT_SHA}" \ + --label "org.opencontainers.image.title={{ project_name|capitalize }}" \ + --label "org.opencontainers.image.description={% if project_blurb is defined %}{{ project_blurb | replace('"', '') | replace('\n', ' ') }}{% else %}{{ project_name }} image by {{ lsio_project_name }}{% endif %}" \ + --provenance=false --sbom=false --builder=default \ + --no-cache --pull -t jenkinslocal:${COMMIT_SHA}-${BUILD_NUMBER} --platform=linux/amd64 . + ''' } } {% endif %} @@ -459,6 +524,8 @@ pipeline { } sh '''curl -sL https://raw.githubusercontent.com/linuxserver/docker-jenkins-builder/master/checkrun.sh | /bin/bash''' sh '''#! /bin/bash + source ${WORKSPACE}/retry_ghcr.sh + retry_ghcr docker pull ghcr.io/linuxserver/baseimage-alpine:3.23 docker run --rm \ -v ${WORKSPACE}:/mnt \ -e AWS_ACCESS_KEY_ID=\"${S3_KEY}\" \ @@ -485,8 +552,9 @@ pipeline { sh '''#! /bin/bash set -e TEMPDIR=$(mktemp -d) + source ${WORKSPACE}/retry_ghcr.sh {% if project_repo_name != "docker-jenkins-builder" %} - docker pull ghcr.io/linuxserver/jenkins-builder:latest + retry_ghcr docker pull ghcr.io/linuxserver/jenkins-builder:latest {% endif %} # Cloned repo paths for templating: # ${TEMPDIR}/docker-${CONTAINER_NAME}: Cloned branch {{ ls_branch }} of ${LS_USER}/${LS_REPO} for running the jenkins builder on @@ -550,6 +618,7 @@ pipeline { sed -i 's|^changelogs:|# init diagram\\ninit_diagram:\\n\\n# changelog\\nchangelogs:|' readme-vars.yml fi mkdir -p ${TEMPDIR}/d2 + retry_ghcr docker pull ghcr.io/linuxserver/d2-builder:latest docker run --rm -v ${TEMPDIR}/d2:/output -e PUID=$(id -u) -e PGID=$(id -g) -e RAW="true" ghcr.io/linuxserver/d2-builder:latest ${CONTAINER_NAME}:{{ release_tag }} ls -al ${TEMPDIR}/d2 yq -ei ".init_diagram |= load_str(\\"${TEMPDIR}/d2/${CONTAINER_NAME}-{{ release_tag }}.d2\\")" readme-vars.yml @@ -798,22 +867,25 @@ pipeline { {% if "docker-baseimage" not in project_repo_name %} sh "sed -r -i 's|(^FROM .*)|\\1\\n\\nENV LSIO_FIRST_PARTY=true|g' Dockerfile" {% endif %} - sh "docker buildx build \ - --label \"org.opencontainers.image.created=${GITHUB_DATE}\" \ - --label \"org.opencontainers.image.authors={{ lsio_project_name }}\" \ - --label \"org.opencontainers.image.url={{ project_github_repo_url }}/packages\" \ - --label \"org.opencontainers.image.documentation={{ lsio_docs_url }}/images/docker-{{ project_name }}\" \ - --label \"org.opencontainers.image.source={{ project_github_repo_url }}\" \ - --label \"org.opencontainers.image.version=${EXT_RELEASE_CLEAN}-ls${LS_TAG_NUMBER}\" \ - --label \"org.opencontainers.image.revision=${COMMIT_SHA}\" \ - --label \"org.opencontainers.image.vendor={{ lsio_project_name }}\" \ - --label \"org.opencontainers.image.licenses=GPL-3.0-only\" \ - --label \"org.opencontainers.image.ref.name=${COMMIT_SHA}\" \ - --label \"org.opencontainers.image.title={{ project_name|capitalize }}\" \ - --label \"org.opencontainers.image.description={% if project_blurb is defined %}{{ project_blurb | replace('"', '') | replace('\n', ' ') }}{% else %}{{ project_name }} image by {{ lsio_project_name }}{% endif %}\" \ - --no-cache --pull -t ${IMAGE}:${META_TAG} --platform=linux/amd64 \ - --provenance={{ image_provenance | lower }} --sbom={{ image_sbom | lower }} --builder={{ image_builder }} --load \ - --build-arg ${BUILD_VERSION_ARG}=${EXT_RELEASE} --build-arg VERSION=\"${VERSION_TAG}\" --build-arg BUILD_DATE=${GITHUB_DATE} ." + sh '''#! /bin/bash + source ${WORKSPACE}/retry_ghcr.sh + retry_ghcr docker buildx build \ + --label "org.opencontainers.image.created=${GITHUB_DATE}" \ + --label "org.opencontainers.image.authors={{ lsio_project_name }}" \ + --label "org.opencontainers.image.url={{ project_github_repo_url }}/packages" \ + --label "org.opencontainers.image.documentation={{ lsio_docs_url }}/images/docker-{{ project_name }}" \ + --label "org.opencontainers.image.source={{ project_github_repo_url }}" \ + --label "org.opencontainers.image.version=${EXT_RELEASE_CLEAN}-ls${LS_TAG_NUMBER}" \ + --label "org.opencontainers.image.revision=${COMMIT_SHA}" \ + --label "org.opencontainers.image.vendor={{ lsio_project_name }}" \ + --label "org.opencontainers.image.licenses=GPL-3.0-only" \ + --label "org.opencontainers.image.ref.name=${COMMIT_SHA}" \ + --label "org.opencontainers.image.title={{ project_name|capitalize }}" \ + --label "org.opencontainers.image.description={% if project_blurb is defined %}{{ project_blurb | replace('"', '') | replace('\n', ' ') }}{% else %}{{ project_name }} image by {{ lsio_project_name }}{% endif %}" \ + --no-cache --pull -t ${IMAGE}:${META_TAG} --platform=linux/amd64 \ + --provenance={{ image_provenance | lower }} --sbom={{ image_sbom | lower }} --builder={{ image_builder }} --load \ + --build-arg ${BUILD_VERSION_ARG}=${EXT_RELEASE} --build-arg VERSION="${VERSION_TAG}" --build-arg BUILD_DATE=${GITHUB_DATE} . + ''' sh '''#! /bin/bash set -e IFS=',' read -ra CACHE <<< "$BUILDCACHE" @@ -841,11 +913,16 @@ pipeline { declare -A pids IFS=',' read -ra CACHE <<< "$BUILDCACHE" for i in "${CACHE[@]}"; do - docker push ${i}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} & + if [[ "${i}" == *"registry.gitlab.com"* ]]; then + TIMEOUT_CMD="timeout ${GITLAB_TIMEOUT}" + else + TIMEOUT_CMD="" + fi + ${TIMEOUT_CMD} docker push ${i}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} & pids[$!]="$i" done for p in "${!pids[@]}"; do - wait "$p" || { [[ "${pids[$p]}" != *"quay.io"* && "${pids[$p]}" != *"registry.gitlab.com"* ]] && exit 1; } + wait "$p" || { if [[ "${pids[$p]}" != *"quay.io"* && "${pids[$p]}" != *"registry.gitlab.com"* ]]; then exit 1; fi; } done fi ''' @@ -871,22 +948,25 @@ pipeline { {% if "docker-baseimage" not in project_repo_name %} sh "sed -r -i 's|(^FROM .*)|\\1\\n\\nENV LSIO_FIRST_PARTY=true|g' Dockerfile" {% endif %} - sh "docker buildx build \ - --label \"org.opencontainers.image.created=${GITHUB_DATE}\" \ - --label \"org.opencontainers.image.authors={{ lsio_project_name }}\" \ - --label \"org.opencontainers.image.url={{ project_github_repo_url }}/packages\" \ - --label \"org.opencontainers.image.documentation={{ lsio_docs_url }}/images/docker-{{ project_name }}\" \ - --label \"org.opencontainers.image.source={{ project_github_repo_url }}\" \ - --label \"org.opencontainers.image.version=${EXT_RELEASE_CLEAN}-ls${LS_TAG_NUMBER}\" \ - --label \"org.opencontainers.image.revision=${COMMIT_SHA}\" \ - --label \"org.opencontainers.image.vendor={{ lsio_project_name }}\" \ - --label \"org.opencontainers.image.licenses=GPL-3.0-only\" \ - --label \"org.opencontainers.image.ref.name=${COMMIT_SHA}\" \ - --label \"org.opencontainers.image.title={{ project_name|capitalize }}\" \ - --label \"org.opencontainers.image.description={% if project_blurb is defined %}{{ project_blurb | replace('"', '') | replace('\n', ' ') }}{% else %}{{ project_name }} image by {{ lsio_project_name }}{% endif %}\" \ - --no-cache --pull -t ${IMAGE}:amd64-${META_TAG} --platform=linux/amd64 \ - --provenance={{ image_provenance | lower }} --sbom={{ image_sbom | lower }} --builder={{ image_builder }} --load \ - --build-arg ${BUILD_VERSION_ARG}=${EXT_RELEASE} --build-arg VERSION=\"${VERSION_TAG}\" --build-arg BUILD_DATE=${GITHUB_DATE} ." + sh '''#! /bin/bash + source ${WORKSPACE}/retry_ghcr.sh + retry_ghcr docker buildx build \ + --label "org.opencontainers.image.created=${GITHUB_DATE}" \ + --label "org.opencontainers.image.authors={{ lsio_project_name }}" \ + --label "org.opencontainers.image.url={{ project_github_repo_url }}/packages" \ + --label "org.opencontainers.image.documentation={{ lsio_docs_url }}/images/docker-{{ project_name }}" \ + --label "org.opencontainers.image.source={{ project_github_repo_url }}" \ + --label "org.opencontainers.image.version=${EXT_RELEASE_CLEAN}-ls${LS_TAG_NUMBER}" \ + --label "org.opencontainers.image.revision=${COMMIT_SHA}" \ + --label "org.opencontainers.image.vendor={{ lsio_project_name }}" \ + --label "org.opencontainers.image.licenses=GPL-3.0-only" \ + --label "org.opencontainers.image.ref.name=${COMMIT_SHA}" \ + --label "org.opencontainers.image.title={{ project_name|capitalize }}" \ + --label "org.opencontainers.image.description={% if project_blurb is defined %}{{ project_blurb | replace('"', '') | replace('\n', ' ') }}{% else %}{{ project_name }} image by {{ lsio_project_name }}{% endif %}" \ + --no-cache --pull -t ${IMAGE}:amd64-${META_TAG} --platform=linux/amd64 \ + --provenance={{ image_provenance | lower }} --sbom={{ image_sbom | lower }} --builder={{ image_builder }} --load \ + --build-arg ${BUILD_VERSION_ARG}=${EXT_RELEASE} --build-arg VERSION="${VERSION_TAG}" --build-arg BUILD_DATE=${GITHUB_DATE} . + ''' sh '''#! /bin/bash set -e IFS=',' read -ra CACHE <<< "$BUILDCACHE" @@ -914,11 +994,16 @@ pipeline { declare -A pids IFS=',' read -ra CACHE <<< "$BUILDCACHE" for i in "${CACHE[@]}"; do - docker push ${i}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} & + if [[ "${i}" == *"registry.gitlab.com"* ]]; then + TIMEOUT_CMD="timeout ${GITLAB_TIMEOUT}" + else + TIMEOUT_CMD="" + fi + ${TIMEOUT_CMD} docker push ${i}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} & pids[$!]="$i" done for p in "${!pids[@]}"; do - wait "$p" || { [[ "${pids[$p]}" != *"quay.io"* && "${pids[$p]}" != *"registry.gitlab.com"* ]] && exit 1; } + wait "$p" || { if [[ "${pids[$p]}" != *"quay.io"* && "${pids[$p]}" != *"registry.gitlab.com"* ]]; then exit 1; fi; } done fi ''' @@ -939,22 +1024,26 @@ pipeline { {% if "docker-baseimage" not in project_repo_name %} sh "sed -r -i 's|(^FROM .*)|\\1\\n\\nENV LSIO_FIRST_PARTY=true|g' Dockerfile.aarch64" {% endif %} - sh "docker buildx build \ - --label \"org.opencontainers.image.created=${GITHUB_DATE}\" \ - --label \"org.opencontainers.image.authors={{ lsio_project_name }}\" \ - --label \"org.opencontainers.image.url={{ project_github_repo_url }}/packages\" \ - --label \"org.opencontainers.image.documentation={{ lsio_docs_url }}/images/docker-{{ project_name }}\" \ - --label \"org.opencontainers.image.source={{ project_github_repo_url }}\" \ - --label \"org.opencontainers.image.version=${EXT_RELEASE_CLEAN}-ls${LS_TAG_NUMBER}\" \ - --label \"org.opencontainers.image.revision=${COMMIT_SHA}\" \ - --label \"org.opencontainers.image.vendor={{ lsio_project_name }}\" \ - --label \"org.opencontainers.image.licenses=GPL-3.0-only\" \ - --label \"org.opencontainers.image.ref.name=${COMMIT_SHA}\" \ - --label \"org.opencontainers.image.title={{ project_name|capitalize }}\" \ - --label \"org.opencontainers.image.description={% if project_blurb is defined %}{{ project_blurb | replace('"', '') | replace('\n', ' ') }}{% else %}{{ project_name }} image by {{ lsio_project_name }}{% endif %}\" \ - --no-cache --pull -f Dockerfile.aarch64 -t ${IMAGE}:arm64v8-${META_TAG} --platform=linux/arm64 \ - --provenance={{ image_provenance | lower }} --sbom={{ image_sbom | lower }} --builder={{ image_builder }} --load \ - --build-arg ${BUILD_VERSION_ARG}=${EXT_RELEASE} --build-arg VERSION=\"${VERSION_TAG}\" --build-arg BUILD_DATE=${GITHUB_DATE} ." + sh '''#! /bin/bash + # create our retry function + {{ retry_ghcr_fn | indent(18) }} + retry_ghcr docker buildx build \ + --label "org.opencontainers.image.created=${GITHUB_DATE}" \ + --label "org.opencontainers.image.authors={{ lsio_project_name }}" \ + --label "org.opencontainers.image.url={{ project_github_repo_url }}/packages" \ + --label "org.opencontainers.image.documentation={{ lsio_docs_url }}/images/docker-{{ project_name }}" \ + --label "org.opencontainers.image.source={{ project_github_repo_url }}" \ + --label "org.opencontainers.image.version=${EXT_RELEASE_CLEAN}-ls${LS_TAG_NUMBER}" \ + --label "org.opencontainers.image.revision=${COMMIT_SHA}" \ + --label "org.opencontainers.image.vendor={{ lsio_project_name }}" \ + --label "org.opencontainers.image.licenses=GPL-3.0-only" \ + --label "org.opencontainers.image.ref.name=${COMMIT_SHA}" \ + --label "org.opencontainers.image.title={{ project_name|capitalize }}" \ + --label "org.opencontainers.image.description={% if project_blurb is defined %}{{ project_blurb | replace('"', '') | replace('\n', ' ') }}{% else %}{{ project_name }} image by {{ lsio_project_name }}{% endif %}" \ + --no-cache --pull -f Dockerfile.aarch64 -t ${IMAGE}:arm64v8-${META_TAG} --platform=linux/arm64 \ + --provenance={{ image_provenance | lower }} --sbom={{ image_sbom | lower }} --builder={{ image_builder }} --load \ + --build-arg ${BUILD_VERSION_ARG}=${EXT_RELEASE} --build-arg VERSION="${VERSION_TAG}" --build-arg BUILD_DATE=${GITHUB_DATE} . + ''' sh '''#! /bin/bash set -e IFS=',' read -ra CACHE <<< "$BUILDCACHE" @@ -981,11 +1070,16 @@ pipeline { declare -A pids IFS=',' read -ra CACHE <<< "$BUILDCACHE" for i in "${CACHE[@]}"; do - docker push ${i}:arm64v8-${COMMIT_SHA}-${BUILD_NUMBER} & + if [[ "${i}" == *"registry.gitlab.com"* ]]; then + TIMEOUT_CMD="timeout ${GITLAB_TIMEOUT}" + else + TIMEOUT_CMD="" + fi + ${TIMEOUT_CMD} docker push ${i}:arm64v8-${COMMIT_SHA}-${BUILD_NUMBER} & pids[$!]="$i" done for p in "${!pids[@]}"; do - wait "$p" || { [[ "${pids[$p]}" != *"quay.io"* && "${pids[$p]}" != *"registry.gitlab.com"* ]] && exit 1; } + wait "$p" || { if [[ "${pids[$p]}" != *"quay.io"* && "${pids[$p]}" != *"registry.gitlab.com"* ]]; then exit 1; fi; } done fi ''' @@ -1015,22 +1109,26 @@ pipeline { {% if "docker-baseimage" not in project_repo_name %} sh "sed -r -i 's|(^FROM .*)|\\1\\n\\nENV LSIO_FIRST_PARTY=true|g' Dockerfile.riscv64" {% endif %} - sh "docker buildx build \ - --label \"org.opencontainers.image.created=${GITHUB_DATE}\" \ - --label \"org.opencontainers.image.authors={{ lsio_project_name }}\" \ - --label \"org.opencontainers.image.url={{ project_github_repo_url }}/packages\" \ - --label \"org.opencontainers.image.documentation={{ lsio_docs_url }}/images/docker-{{ project_name }}\" \ - --label \"org.opencontainers.image.source={{ project_github_repo_url }}\" \ - --label \"org.opencontainers.image.version=${EXT_RELEASE_CLEAN}-ls${LS_TAG_NUMBER}\" \ - --label \"org.opencontainers.image.revision=${COMMIT_SHA}\" \ - --label \"org.opencontainers.image.vendor={{ lsio_project_name }}\" \ - --label \"org.opencontainers.image.licenses=GPL-3.0-only\" \ - --label \"org.opencontainers.image.ref.name=${COMMIT_SHA}\" \ - --label \"org.opencontainers.image.title={{ project_name|capitalize }}\" \ - --label \"org.opencontainers.image.description={% if project_blurb is defined %}{{ project_blurb | replace('"', '') | replace('\n', ' ') }}{% else %}{{ project_name }} image by {{ lsio_project_name }}{% endif %}\" \ - --no-cache --pull -f Dockerfile.riscv64 -t ${IMAGE}:riscv64-${META_TAG} --platform=linux/riscv64 \ - --provenance={{ image_provenance | lower }} --sbom={{ image_sbom | lower }} --builder={{ image_builder }} --load \ - --build-arg ${BUILD_VERSION_ARG}=${EXT_RELEASE} --build-arg VERSION=\"${VERSION_TAG}\" --build-arg BUILD_DATE=${GITHUB_DATE} ." + sh '''#! /bin/bash + # create our retry function + {{ retry_ghcr_fn | indent(18) }} + retry_ghcr docker buildx build \ + --label "org.opencontainers.image.created=${GITHUB_DATE}" \ + --label "org.opencontainers.image.authors={{ lsio_project_name }}" \ + --label "org.opencontainers.image.url={{ project_github_repo_url }}/packages" \ + --label "org.opencontainers.image.documentation={{ lsio_docs_url }}/images/docker-{{ project_name }}\" \ + --label "org.opencontainers.image.source={{ project_github_repo_url }}" \ + --label "org.opencontainers.image.version=${EXT_RELEASE_CLEAN}-ls${LS_TAG_NUMBER}" \ + --label "org.opencontainers.image.revision=${COMMIT_SHA}" \ + --label "org.opencontainers.image.vendor={{ lsio_project_name }}" \ + --label "org.opencontainers.image.licenses=GPL-3.0-only" \ + --label "org.opencontainers.image.ref.name=${COMMIT_SHA}" \ + --label "org.opencontainers.image.title={{ project_name|capitalize }}" \ + --label "org.opencontainers.image.description={% if project_blurb is defined %}{{ project_blurb | replace('"', '') | replace('\n', ' ') }}{% else %}{{ project_name }} image by {{ lsio_project_name }}{% endif %}" \ + --no-cache --pull -f Dockerfile.riscv64 -t ${IMAGE}:riscv64-${META_TAG} --platform=linux/riscv64 \ + --provenance={{ image_provenance | lower }} --sbom={{ image_sbom | lower }} --builder={{ image_builder }} --load \ + --build-arg ${BUILD_VERSION_ARG}=${EXT_RELEASE} --build-arg VERSION="${VERSION_TAG}" --build-arg BUILD_DATE=${GITHUB_DATE} . + ''' sh '''#! /bin/bash set -e IFS=',' read -ra CACHE <<< "$BUILDCACHE" @@ -1058,11 +1156,16 @@ pipeline { declare -A pids IFS=',' read -ra CACHE <<< "$BUILDCACHE" for i in "${CACHE[@]}"; do - docker push ${i}:riscv64-${COMMIT_SHA}-${BUILD_NUMBER} & + if [[ "${i}" == *"registry.gitlab.com"* ]]; then + TIMEOUT_CMD="timeout ${GITLAB_TIMEOUT}" + else + TIMEOUT_CMD="" + fi + ${TIMEOUT_CMD} docker push ${i}:riscv64-${COMMIT_SHA}-${BUILD_NUMBER} & pids[$!]="$i" done for p in "${!pids[@]}"; do - wait "$p" || { [[ "${pids[$p]}" != *"quay.io"* && "${pids[$p]}" != *"registry.gitlab.com"* ]] && exit 1; } + wait "$p" || { if [[ "${pids[$p]}" != *"quay.io"* && "${pids[$p]}" != *"registry.gitlab.com"* ]]; then exit 1; fi; } done fi ''' @@ -1099,6 +1202,8 @@ pipeline { LOCAL_CONTAINER=${IMAGE}:${META_TAG} fi touch ${TEMPDIR}/package_versions.txt + source ${WORKSPACE}/retry_ghcr.sh + retry_ghcr docker pull ghcr.io/anchore/syft:${SYFT_IMAGE_TAG} docker run --rm \ -v /var/run/docker.sock:/var/run/docker.sock:ro \ -v ${TEMPDIR}:/tmp \ @@ -1191,12 +1296,13 @@ pipeline { CI_DOCKERENV="LSIO_FIRST_PARTY=true" fi fi - docker pull ghcr.io/linuxserver/ci:${CITEST_IMAGETAG} + source ${WORKSPACE}/retry_ghcr.sh + retry_ghcr docker pull ghcr.io/linuxserver/ci:${CITEST_IMAGETAG} if [ "${MULTIARCH}" == "true" ]; then - docker pull ghcr.io/linuxserver/lsiodev-buildcache:arm64v8-${COMMIT_SHA}-${BUILD_NUMBER} --platform=arm64 + retry_ghcr docker pull ghcr.io/linuxserver/lsiodev-buildcache:arm64v8-${COMMIT_SHA}-${BUILD_NUMBER} --platform=arm64 docker tag ghcr.io/linuxserver/lsiodev-buildcache:arm64v8-${COMMIT_SHA}-${BUILD_NUMBER} ${IMAGE}:arm64v8-${META_TAG} {% if build_riscv64 %} - docker pull ghcr.io/linuxserver/lsiodev-buildcache:riscv64-${COMMIT_SHA}-${BUILD_NUMBER} --platform=riscv64 + retry_ghcr docker pull ghcr.io/linuxserver/lsiodev-buildcache:riscv64-${COMMIT_SHA}-${BUILD_NUMBER} --platform=riscv64 docker tag ghcr.io/linuxserver/lsiodev-buildcache:riscv64-${COMMIT_SHA}-${BUILD_NUMBER} ${IMAGE}:riscv64-${META_TAG} {% endif %} fi @@ -1256,6 +1362,11 @@ pipeline { sh '''#! /bin/bash set -e for PUSHIMAGE in "${IMAGE}" "${GITLABIMAGE}" "${GITHUBIMAGE}" "${QUAYIMAGE}"; do + if [[ "${PUSHIMAGE}" == "${GITLABIMAGE}" ]]; then + TIMEOUT_CMD="timeout ${GITLAB_TIMEOUT}" + else + TIMEOUT_CMD="" + fi [[ ${PUSHIMAGE%%/*} =~ \\. ]] && PUSHIMAGEPLUS="${PUSHIMAGE}" || PUSHIMAGEPLUS="docker.io/${PUSHIMAGE}" IFS=',' read -ra CACHE <<< "$BUILDCACHE" for i in "${CACHE[@]}"; do @@ -1263,14 +1374,14 @@ pipeline { CACHEIMAGE=${i} fi done - docker buildx imagetools create --prefer-index=false -t ${PUSHIMAGE}:${META_TAG} -t ${PUSHIMAGE}:{{ release_tag }} -t ${PUSHIMAGE}:${EXT_RELEASE_TAG} ${CACHEIMAGE}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} || \ + ${TIMEOUT_CMD} docker buildx imagetools create --prefer-index=false -t ${PUSHIMAGE}:${META_TAG} -t ${PUSHIMAGE}:{{ release_tag }} -t ${PUSHIMAGE}:${EXT_RELEASE_TAG} ${CACHEIMAGE}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} || \ { if [[ "${PUSHIMAGE}" != "${QUAYIMAGE}" && "${PUSHIMAGE}" != "${GITLABIMAGE}" ]]; then exit 1; fi; } if [ -n "${SEMVER}" ]; then - docker buildx imagetools create --prefer-index=false -t ${PUSHIMAGE}:${SEMVER} ${CACHEIMAGE}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} || \ + ${TIMEOUT_CMD} docker buildx imagetools create --prefer-index=false -t ${PUSHIMAGE}:${SEMVER} ${CACHEIMAGE}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} || \ { if [[ "${PUSHIMAGE}" != "${QUAYIMAGE}" && "${PUSHIMAGE}" != "${GITLABIMAGE}" ]]; then exit 1; fi; } fi {% if project_deprecation_status %} - docker buildx imagetools create -t ${PUSHIMAGE}:{{ release_tag }} ghcr.io/linuxserver/jenkins-builder:empty || true + ${TIMEOUT_CMD} docker buildx imagetools create -t ${PUSHIMAGE}:{{ release_tag }} ghcr.io/linuxserver/jenkins-builder:empty || true {% endif %} done ''' @@ -1291,6 +1402,11 @@ pipeline { sh '''#! /bin/bash set -e for MANIFESTIMAGE in "${IMAGE}" "${GITLABIMAGE}" "${GITHUBIMAGE}" "${QUAYIMAGE}"; do + if [[ "${MANIFESTIMAGE}" == "${GITLABIMAGE}" ]]; then + TIMEOUT_CMD="timeout ${GITLAB_TIMEOUT}" + else + TIMEOUT_CMD="" + fi if [[ "${MANIFESTIMAGE%%/*}" =~ \\. ]]; then MANIFESTIMAGEPLUS="${MANIFESTIMAGE}" else @@ -1302,38 +1418,43 @@ pipeline { CACHEIMAGE=${i} fi done - docker buildx imagetools create --prefer-index=false -t ${MANIFESTIMAGE}:amd64-${META_TAG} -t ${MANIFESTIMAGE}:amd64-{{ release_tag }} -t ${MANIFESTIMAGE}:amd64-${EXT_RELEASE_TAG} ${CACHEIMAGE}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} || \ + ${TIMEOUT_CMD} docker buildx imagetools create --prefer-index=false -t ${MANIFESTIMAGE}:amd64-${META_TAG} -t ${MANIFESTIMAGE}:amd64-{{ release_tag }} -t ${MANIFESTIMAGE}:amd64-${EXT_RELEASE_TAG} ${CACHEIMAGE}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} || \ { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" && "${MANIFESTIMAGE}" != "${GITLABIMAGE}" ]]; then exit 1; fi; } - docker buildx imagetools create --prefer-index=false -t ${MANIFESTIMAGE}:arm64v8-${META_TAG} -t ${MANIFESTIMAGE}:arm64v8-{{ release_tag }} -t ${MANIFESTIMAGE}:arm64v8-${EXT_RELEASE_TAG} ${CACHEIMAGE}:arm64v8-${COMMIT_SHA}-${BUILD_NUMBER} || \ + ${TIMEOUT_CMD} docker buildx imagetools create --prefer-index=false -t ${MANIFESTIMAGE}:arm64v8-${META_TAG} -t ${MANIFESTIMAGE}:arm64v8-{{ release_tag }} -t ${MANIFESTIMAGE}:arm64v8-${EXT_RELEASE_TAG} ${CACHEIMAGE}:arm64v8-${COMMIT_SHA}-${BUILD_NUMBER} || \ { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" && "${MANIFESTIMAGE}" != "${GITLABIMAGE}" ]]; then exit 1; fi; } {% if build_riscv64 %} - docker buildx imagetools create --prefer-index=false -t ${MANIFESTIMAGE}:riscv64-${META_TAG} -t ${MANIFESTIMAGE}:riscv64-{{ release_tag }} -t ${MANIFESTIMAGE}:riscv64-${EXT_RELEASE_TAG} ${CACHEIMAGE}:riscv64-${COMMIT_SHA}-${BUILD_NUMBER} || \ + ${TIMEOUT_CMD} docker buildx imagetools create --prefer-index=false -t ${MANIFESTIMAGE}:riscv64-${META_TAG} -t ${MANIFESTIMAGE}:riscv64-{{ release_tag }} -t ${MANIFESTIMAGE}:riscv64-${EXT_RELEASE_TAG} ${CACHEIMAGE}:riscv64-${COMMIT_SHA}-${BUILD_NUMBER} || \ { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" && "${MANIFESTIMAGE}" != "${GITLABIMAGE}" ]]; then exit 1; fi; } {% endif %} if [ -n "${SEMVER}" ]; then - docker buildx imagetools create --prefer-index=false -t ${MANIFESTIMAGE}:amd64-${SEMVER} ${CACHEIMAGE}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} || \ + ${TIMEOUT_CMD} docker buildx imagetools create --prefer-index=false -t ${MANIFESTIMAGE}:amd64-${SEMVER} ${CACHEIMAGE}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} || \ { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" && "${MANIFESTIMAGE}" != "${GITLABIMAGE}" ]]; then exit 1; fi; } - docker buildx imagetools create --prefer-index=false -t ${MANIFESTIMAGE}:arm64v8-${SEMVER} ${CACHEIMAGE}:arm64v8-${COMMIT_SHA}-${BUILD_NUMBER} || \ + ${TIMEOUT_CMD} docker buildx imagetools create --prefer-index=false -t ${MANIFESTIMAGE}:arm64v8-${SEMVER} ${CACHEIMAGE}:arm64v8-${COMMIT_SHA}-${BUILD_NUMBER} || \ { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" && "${MANIFESTIMAGE}" != "${GITLABIMAGE}" ]]; then exit 1; fi; } {% if build_riscv64 %} - docker buildx imagetools create --prefer-index=false -t ${MANIFESTIMAGE}:riscv64-${SEMVER} ${CACHEIMAGE}:riscv64-${COMMIT_SHA}-${BUILD_NUMBER} || \ + ${TIMEOUT_CMD} docker buildx imagetools create --prefer-index=false -t ${MANIFESTIMAGE}:riscv64-${SEMVER} ${CACHEIMAGE}:riscv64-${COMMIT_SHA}-${BUILD_NUMBER} || \ { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" && "${MANIFESTIMAGE}" != "${GITLABIMAGE}" ]]; then exit 1; fi; } {% endif %} fi done for MANIFESTIMAGE in "${IMAGE}" "${GITLABIMAGE}" "${GITHUBIMAGE}" "${QUAYIMAGE}"; do + if [[ "${MANIFESTIMAGE}" == "${GITLABIMAGE}" ]]; then + TIMEOUT_CMD="timeout ${GITLAB_TIMEOUT}" + else + TIMEOUT_CMD="" + fi {% if project_deprecation_status %} - docker buildx imagetools create -t ${MANIFESTIMAGE}:{{ release_tag }} -t ${MANIFESTIMAGE}:amd64-{{ release_tag }} -t ${MANIFESTIMAGE}:arm64v8-{{ release_tag }}{% if build_riscv64 %} -t ${MANIFESTIMAGE}:riscv64-{{ release_tag }}{% endif %} ghcr.io/linuxserver/jenkins-builder:empty || true + ${TIMEOUT_CMD} docker buildx imagetools create -t ${MANIFESTIMAGE}:{{ release_tag }} -t ${MANIFESTIMAGE}:amd64-{{ release_tag }} -t ${MANIFESTIMAGE}:arm64v8-{{ release_tag }}{% if build_riscv64 %} -t ${MANIFESTIMAGE}:riscv64-{{ release_tag }}{% endif %} ghcr.io/linuxserver/jenkins-builder:empty || true {% else %} - docker buildx imagetools create -t ${MANIFESTIMAGE}:{{ release_tag }} ${MANIFESTIMAGE}:amd64-{{ release_tag }}{% if build_riscv64 %} ${MANIFESTIMAGE}:riscv64-{{ release_tag }}{% endif %} ${MANIFESTIMAGE}:arm64v8-{{ release_tag }} || \ + ${TIMEOUT_CMD} docker buildx imagetools create -t ${MANIFESTIMAGE}:{{ release_tag }} ${MANIFESTIMAGE}:amd64-{{ release_tag }}{% if build_riscv64 %} ${MANIFESTIMAGE}:riscv64-{{ release_tag }}{% endif %} ${MANIFESTIMAGE}:arm64v8-{{ release_tag }} || \ { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" && "${MANIFESTIMAGE}" != "${GITLABIMAGE}" ]]; then exit 1; fi; } {% endif %} - docker buildx imagetools create -t ${MANIFESTIMAGE}:${META_TAG} ${MANIFESTIMAGE}:amd64-${META_TAG}{% if build_riscv64 %} ${MANIFESTIMAGE}:riscv64-${META_TAG}{% endif %} ${MANIFESTIMAGE}:arm64v8-${META_TAG} || \ + ${TIMEOUT_CMD} docker buildx imagetools create -t ${MANIFESTIMAGE}:${META_TAG} ${MANIFESTIMAGE}:amd64-${META_TAG}{% if build_riscv64 %} ${MANIFESTIMAGE}:riscv64-${META_TAG}{% endif %} ${MANIFESTIMAGE}:arm64v8-${META_TAG} || \ { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" && "${MANIFESTIMAGE}" != "${GITLABIMAGE}" ]]; then exit 1; fi; } - docker buildx imagetools create -t ${MANIFESTIMAGE}:${EXT_RELEASE_TAG} ${MANIFESTIMAGE}:amd64-${EXT_RELEASE_TAG}{% if build_riscv64 %} ${MANIFESTIMAGE}:riscv64-${EXT_RELEASE_TAG}{% endif %} ${MANIFESTIMAGE}:arm64v8-${EXT_RELEASE_TAG} || \ + ${TIMEOUT_CMD} docker buildx imagetools create -t ${MANIFESTIMAGE}:${EXT_RELEASE_TAG} ${MANIFESTIMAGE}:amd64-${EXT_RELEASE_TAG}{% if build_riscv64 %} ${MANIFESTIMAGE}:riscv64-${EXT_RELEASE_TAG}{% endif %} ${MANIFESTIMAGE}:arm64v8-${EXT_RELEASE_TAG} || \ { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" && "${MANIFESTIMAGE}" != "${GITLABIMAGE}" ]]; then exit 1; fi; } if [ -n "${SEMVER}" ]; then - docker buildx imagetools create -t ${MANIFESTIMAGE}:${SEMVER} ${MANIFESTIMAGE}:amd64-${SEMVER}{% if build_riscv64 %} ${MANIFESTIMAGE}:riscv64-${SEMVER}{% endif %} ${MANIFESTIMAGE}:arm64v8-${SEMVER} || \ + ${TIMEOUT_CMD} docker buildx imagetools create -t ${MANIFESTIMAGE}:${SEMVER} ${MANIFESTIMAGE}:amd64-${SEMVER}{% if build_riscv64 %} ${MANIFESTIMAGE}:riscv64-${SEMVER}{% endif %} ${MANIFESTIMAGE}:arm64v8-${SEMVER} || \ { if [[ "${MANIFESTIMAGE}" != "${QUAYIMAGE}" && "${MANIFESTIMAGE}" != "${GITLABIMAGE}" ]]; then exit 1; fi; } fi done