Skip to content

ci(ct): detect necessary rebuilds and calculate revision number #11

ci(ct): detect necessary rebuilds and calculate revision number

ci(ct): detect necessary rebuilds and calculate revision number #11

---
name: Container Images Scheduled Maintenance
on:
# TODO: remove "push" in final PR
push:
branches:
- '10478-version-base-img'
paths:
- 'modules/container-base/**'
- 'modules/dataverse-parent/pom.xml'
- '.github/workflows/container_maintenance.yml'
# Allow manual workflow triggers in case we need to repair images on Docker Hub (build and replace)
workflow_dispatch:
schedule:
- cron: '23 3 * * 0' # Run for 'develop' every Sunday at 03:23 UTC
env:
PLATFORMS: linux/amd64,linux/arm64
NUM_PAST_RELEASES: 3
# TODO: change to "develop" in final PR
DEVELOP_BRANCH: 10478-version-base-img
jobs:
discover:
name: Discover Release Matrix
runs-on: ubuntu-latest
permissions:
contents: read
packages: read
# TODO: re-enable for final PR
# Only run in upstream repo - avoid unnecessary runs in forks and only for scheduled
#if: ${{ github.repository_owner == 'IQSS' }}
outputs:
branches: ${{ steps.matrix.outputs.branches }}
current_release: ${{ steps.matrix.outputs.current_release }}
steps:
- name: Build branch matrix options
id: matrix
run: |
echo "branches=$(curl -f -sS https://api.github.com/repos/IQSS/dataverse/releases | \
jq '[ .[0:${{ env.NUM_PAST_RELEASES }}] | .[].tag_name, "${{ env.DEVELOP_BRANCH }}" ]')" | tr -d "\n" | tr -s " " | \
tee -a "$GITHUB_OUTPUT"
echo "current_release=$(curl -f -sS https://api.github.com/repos/IQSS/dataverse/releases | jq '.[0].tag_name' )" | tee -a "$GITHUB_OUTPUT"
build:
name: Build image
runs-on: ubuntu-latest
permissions:
contents: read
packages: read
needs: discover
strategy:
fail-fast: false
matrix:
branch: ${{ fromJson(needs.discover.outputs.branches) }}
# TODO: re-enable for final PR
# Only run in upstream repo - avoid unnecessary runs in forks
#if: ${{ github.repository_owner == 'IQSS' }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ matrix.branch }}
- name: Determine Java version from Parent POM
run: |
echo "JAVA_VERSION=$(grep '<target.java.version>' modules/dataverse-parent/pom.xml | cut -f2 -d'>' | cut -f1 -d'<')" >> ${GITHUB_ENV}
- name: Set up JDK ${{ env.JAVA_VERSION }}
id: setup-java
uses: actions/setup-java@v4
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: 'temurin'
cache: 'maven'
cache-dependency-path: |
modules/container-base/pom.xml
- name: Download common cache on branch cache miss
if: ${{ steps.setup-java.outputs.cache-hit != 'true' }}
uses: actions/cache/restore@v4
with:
key: dataverse-maven-cache
path: ~/.m2/repository
# Note: Accessing, pushing tags etc. to DockerHub will only succeed in upstream and
# on events in context of upstream because secrets. PRs run in context of forks by default!
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up QEMU for multi-arch builds
uses: docker/setup-qemu-action@v3
with:
platforms: ${{ env.PLATFORMS }}
# Try to retrieve backport patches for this git ref (but don't fail if there aren't any)
# and try to apply them if present
- name: Get and apply backported patches
# There might be no patches - ignore errors
continue-on-error: true
run: |
mkdir -p "${GITHUB_WORKSPACE}/patches"
curl -sSL "https://github.com/${GITHUB_REPOSITORY}/archive/${DEVELOP_BRANCH}.tar.gz" | \
tar -zxf - -C "${GITHUB_WORKSPACE}/patches" --wildcards "*/modules/container-base/src/backports/${{ matrix.branch }}" --strip-components=6
find "${GITHUB_WORKSPACE}/patches" -type f -name '*.patch' -print0 | xargs -0 -n1 patch -p1 -s -i
# Figure out if a rebuild is necessary because either there is an updated Java image or our installed packages need updates
- name: Check for recent Temurin image updates
id: temurin-check
run: |
JAVA_IMAGE="$( mvn help:evaluate -Pct -f modules/container-base -Dexpression=java.image -q -DforceStdout )"
JAVA_IMAGE_NS="library"
JAVA_IMAGE_REPO="$( echo "$JAVA_IMAGE" | cut -f1 -d: )"
JAVA_IMAGE_TAG="$( echo "$JAVA_IMAGE" | cut -f2 -d: )"
JAVA_IMAGE_LAST_UPDATE="$( curl -sS "https://hub.docker.com/v2/namespaces/${JAVA_IMAGE_NS}/repositories/${JAVA_IMAGE_REPO}/tags/${JAVA_IMAGE_TAG}" | jq -r .last_updated )"
if [[ "$JAVA_IMAGE_LAST_UPDATE" = "null" ]]; then
echo "::error title='Invalid Java Image'::Could not find ${JAVA_IMAGE} in the registry"
exit 1
fi
BASE_IMAGE="$( mvn help:evaluate -Pct -f modules/container-base -Dexpression=base.image -q -DforceStdout )"
BASE_IMAGE_NS="$( echo "$BASE_IMAGE" | cut -f1 -d/ )"
BASE_IMAGE_REPO="$( echo "$BASE_IMAGE" | cut -f1 -d: | cut -f2 -d/ )"
BASE_IMAGE_TAG="$( echo "$BASE_IMAGE" | cut -f2 -d: )"
BASE_IMAGE_LAST_UPDATE="$( curl -sS "https://hub.docker.com/v2/namespaces/${BASE_IMAGE_NS}/repositories/${BASE_IMAGE_REPO}/tags/${BASE_IMAGE_TAG}" | jq -r .last_updated )"
if [[ "$BASE_IMAGE_LAST_UPDATE" = "null" || "$BASE_IMAGE_LAST_UPDATE" < "$JAVA_IMAGE_LAST_UPDATE" ]]; then
echo "Java image $JAVA_IMAGE has a newer release ($JAVA_IMAGE_LAST_UPDATE), which is more recent than $BASE_IMAGE ($BASE_IMAGE_LAST_UPDATE)"
echo "newer_java_image=true" >> "${GITHUB_OUTPUT}"
else
echo "Java image $JAVA_IMAGE ($JAVA_IMAGE_LAST_UPDATE) is older than $BASE_IMAGE ($BASE_IMAGE_LAST_UPDATE)"
echo "newer_java_image=false" >> "${GITHUB_OUTPUT}"
fi
# TODO: if we introduce more flavors as a matrix, we need to adapt the install command to check for updates
- name: Check for package updates in base image
id: package-check
if: ${{ steps.temurin-check.outputs.newer_java_image == 'false' }}
run: |
BASE_IMAGE="$( mvn help:evaluate -Pct -f modules/container-base -Dexpression=base.image -q -DforceStdout )"
PKGS="$( grep "ARG PKGS" modules/container-base/src/main/docker/Dockerfile | cut -f2 -d= | tr -d '"' )"
if [[ ! $( docker run -it --rm -u 0 "${BASE_IMAGE}" sh -c "apt install -s ${PKGS}" | grep "0 upgraded" ) ]]; then
echo "Base image $BASE_IMAGE needs package updates"
echo "newer_packages=true" >> "${GITHUB_OUTPUT}"
else
echo "Base image $BASE_IMAGE has no package updates"
echo "newer_packages=false" >> "${GITHUB_OUTPUT}"
fi
- name: Calculate revision number for immutable tag
run: |
BASE_IMAGE="$( mvn help:evaluate -Pct -f modules/container-base -Dexpression=base.image -q -DforceStdout )"
BASE_IMAGE_NS_REPO="$( echo "$BASE_IMAGE" | cut -d: -f1 )"
BASE_IMAGE_TAG=""$( echo "$BASE_IMAGE" | cut -d: -f2 )""
function get_all_tags() {
ref="$1"
case "$ref" in
*/*) :;; # namespace/repository syntax, leave as is
*) ref="library/$ref";; # bare repository name (docker official image); must convert to namespace/repository syntax
esac
token=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${ref}:pull" | jq -r '.token' )
i=0
while [ $? == 0 ]; do
i=$((i+1))
curl -sS -H "Authorization: Bearer $token" "https://registry.hub.docker.com/v2/repositories/${ref}/tags/?page=$i&page_size=100" | jq -r '."results"[]["name"]' 2>/dev/null
done
}
CURRENT=$( get_all_tags "${BASE_IMAGE_NS_REPO}" | grep "${BASE_IMAGE_TAG}-r" | sed -e "s#${BASE_IMAGE_TAG}-r##" | sort -h | tail -n1 )
# If there is a current number, increment it - otherwise this is the initial version, set to 0
if [[ "$CURRENT" ]]; then
echo "REVISION_OPTION=-Dbase.image.revision=$((CURRENT+1))" | tee -a "${GITHUB_ENV}"
else
echo "REVISION_OPTION=-Dbase.image.revision=0" | tee -a "${GITHUB_ENV}"
fi
- name: Configure update of "latest" tag for development branch
if: ${{ matrix.branch == env.DEVELOP_BRANCH }}
run: |
echo "DOCKER_TAGS=-Ddocker.imagePropertyConfiguration=override -Ddocker.tags.develop=latest" | tee -a "${GITHUB_ENV}"
- name: Deploy multi-arch base container image to Docker Hub
if: ${{ steps.temurin-check.outputs.newer_java_image == 'true' || steps.package-check.outputs.newer_packages == 'true' }}
id: build
run: mvn -f modules/container-base -Pct deploy -Ddocker.noCache ${DOCKER_TAGS} ${REVISION_OPTION} -Ddocker.platforms=${{ env.PLATFORMS }}
# - if: always()
# name: Save status (workaround for matrix outputs)
# run: |
# # steps.build.outcome is the status BEFORE continue-on-error
# echo "STATUS_$( echo "${{ matrix.branch }}" | tr ".:;,-/ " "_" )=${{ steps.build.outcome }}" | tee -a "${GITHUB_ENV}"
#push-app-img:
# name: "Rebase & Publish App Image"
# permissions:
# contents: read
# packages: write
# pull-requests: write
# secrets: inherit
# needs:
# - discover
# - build
# strategy:
# fail-fast: false
# matrix:
# branch: ${{ fromJson(needs.discover.outputs.branches) }}
# uses: ./.github/workflows/container_app_push.yml
# with:
# branch: ${{ matrix.branch }}
# TODO: job to update the docker hub description with supported tags and all
# - name: Push description to DockerHub
# uses: peter-evans/dockerhub-description@v3
# with:
# username: ${{ secrets.DOCKERHUB_USERNAME }}
# password: ${{ secrets.DOCKERHUB_TOKEN }}
# repository: gdcc/base
# short-description: "Dataverse Base Container image providing Payara application server and optimized configuration"
# readme-filepath: ./modules/container-base/README.md