Skip to content

Docker Image CI

Docker Image CI #107

Workflow file for this run

name: Docker Image CI
on:
#push:
# branches:
# - main # Run on push to the branch
schedule:
- cron: "20 4 * * *" # Run every night 4:20
workflow_dispatch: # This enables manual triggering
concurrency:
group: main-build # Name for the group of workflows to control concurrency
cancel-in-progress: true # Cancel any currently running workflow if a new one starts
jobs:
build:
runs-on: ubuntu-22.04
steps:
# Checkout the repository
- name: Checkout code
uses: actions/checkout@v2
# Set up Python environment
- name: Set up Python 3.x
uses: actions/setup-python@v4
with:
python-version: '3.x' # 3.x or specific version like '3.9'
# Set up Docker Buildx (for multi-platform builds)
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
# Log in to Docker Hub using the secrets
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
# Log in to ghcr.io using the secrets
- name: Log in to ghcr.io
run: |
echo $GITHUB_TOKEN | docker login ghcr.io -u ${{ vars.GHCR_USER }} --password-stdin
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Automatically provided by GitHub
- name: "Cache prepare"
run: |
current_time=$(date +%s)
# increment this uniq id to reset all caches
uniq_id_to_reset_cache=100
# one week cache
versions_max_cache_duration_seconds=$((7 * 24 * 60 * 60))
versions_cache_id_time_part=$((current_time / versions_max_cache_duration_seconds))
CACHE_ID_VERSIONS=${{ github.repository }}--${uniq_id_to_reset_cache}--${{ runner.os }}-versions-${versions_cache_id_time_part}
BUILD_CACHE_FOLDER_VERSIONS=/tmp/build-cache/versions
mkdir -p $BUILD_CACHE_FOLDER_VERSIONS
echo "CACHE_ID_VERSIONS=$CACHE_ID_VERSIONS" >> $GITHUB_ENV
echo "BUILD_CACHE_FOLDER_VERSIONS=$BUILD_CACHE_FOLDER_VERSIONS" >> $GITHUB_ENV
echo "'versions' cache id: $CACHE_ID_VERSIONS; Cache folder: $BUILD_CACHE_FOLDER_VERSIONS"
# one month cache
full_max_cache_duration_seconds=$((31 * 24 * 60 * 60))
full_cache_id_time_part=$((current_time / full_max_cache_duration_seconds))
CACHE_ID_FULL=${{ github.repository }}--${uniq_id_to_reset_cache}--${{ runner.os }}-full-${cache_id_time_part}
BUILD_CACHE_FOLDER_FULL=/tmp/build-cache/full
mkdir -p $BUILD_CACHE_FOLDER_FULL
echo "CACHE_ID_FULL=$CACHE_ID_FULL" >> $GITHUB_ENV
echo "BUILD_CACHE_FOLDER_FULL=$BUILD_CACHE_FOLDER_FULL" >> $GITHUB_ENV
echo "'full' cache id: $CACHE_ID_FULL; Cache folder: $BUILD_CACHE_FOLDER_FULL"
- name: "Cache download (versions)"
id: cache-docker-layers
uses: actions/cache@v3
with:
path: ${{ env.BUILD_CACHE_FOLDER_VERSIONS }}
key: ${{ env.CACHE_ID_VERSIONS }}
- name: Get latest release version
id: get_version
run: |
HTTP_CODE=$(curl -s -w "%{http_code}" -o /tmp/last_release_info "https://api.github.com/repos/${{ github.repository }}/releases/latest")
if [[ "$HTTP_CODE" -ne 200 && "$HTTP_CODE" -ne 404 ]]; then
echo "Error requesting latest version: $HTTP_CODE"
exit 1
fi
PREVIOUS_VERSION=$(cat /tmp/last_release_info | jq -r .tag_name) || PREVIOUS_VERSION=null
VERSION_INCREMENT_REQUIRED=1
BASE_VERSION=$(cat VERSION)
NEW_VERSION=$PREVIOUS_VERSION
HAVE_PREVIOUS_RELEASE=1
if [ "$PREVIOUS_VERSION" == "null" ]; then
PREVIOUS_VERSION=0.0.0
NEW_VERSION=$PREVIOUS_VERSION
VERSION_INCREMENT_REQUIRED=0
HAVE_PREVIOUS_RELEASE=0
fi
if [[ "$PREVIOUS_VERSION" != "$BASE_VERSION."* ]]; then
NEW_VERSION="${BASE_VERSION}.0"
VERSION_INCREMENT_REQUIRED=0
fi
echo "Previous version was $PREVIOUS_VERSION. New version base: $NEW_VERSION. Increment required: $VERSION_INCREMENT_REQUIRED"
echo "PREVIOUS_VERSION=$PREVIOUS_VERSION" >> $GITHUB_ENV
echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV
echo "VERSION_INCREMENT_REQUIRED=$VERSION_INCREMENT_REQUIRED" >> $GITHUB_ENV
if [[ "$HAVE_PREVIOUS_RELEASE" == "1" ]]; then
HTTP_CODE=$(curl -L -s -w "%{http_code}" -o prev-software-list.txt "https://github.com/${{ github.repository }}/releases/download/$PREVIOUS_VERSION/software-list.txt")
if [[ "$HTTP_CODE" -ne 200 ]]; then
echo "Error requesting previous software versions: $HTTP_CODE"
exit 1
fi
PREV_SOFTWARE_LIST_HASH=$(sha256sum ./prev-software-list.txt | awk '{ print $1 }')
else
touch prev-software-list.txt
PREV_SOFTWARE_LIST_HASH="none"
fi
echo "PREV_SOFTWARE_LIST_HASH=$PREV_SOFTWARE_LIST_HASH" >> $GITHUB_ENV
echo "Previous hash of the software list file: $PREV_SOFTWARE_LIST_HASH"
- name: Increment version
id: increment_version
run: |
IFS='.' read -r major minor patch <<< "${{ env.NEW_VERSION }}"
if [[ "$VERSION_INCREMENT_REQUIRED" == "1" ]]; then
patch=$((patch + 1)) # Increment patch version
fi
NEW_VERSION="$major.$minor.$patch"
echo "New version will be $NEW_VERSION"
echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV
echo "VERSION_CHECK_ONLY_BUILD_VERSION=$NEW_VERSION-$(date +%s)" >> $GITHUB_ENV
# Build Docker image with software versions only (light image without software installation)
- name: Build docker image to get current versions of software
uses: docker/build-push-action@v4
with:
context: .
push: false
load: true
cache-from: type=local,src=${{ env.BUILD_CACHE_FOLDER_VERSIONS }}/.buildx-cache
cache-to: type=local,dest=${{ env.BUILD_CACHE_FOLDER_VERSIONS }}/.buildx-cache,mode=max
build-args: |
DOCKER_IMAGE_BUILD_VERSION=${{ env.VERSION_CHECK_ONLY_BUILD_VERSION }}
DOCKER_IMAGE_BUILD_MODE=versions
tags: temp-versions
# Pull and extract file from Docker image
- name: Extract Software List from the Docker Image
run: |
IMAGE=temp-versions
# Create a container from the image but don't start it
CONTAINER_ID=$(docker create $IMAGE)
# Copy the file from the container to the host system
docker cp $CONTAINER_ID:/usr/local/jddlab/software-list.txt ./software-list.txt
# Remove the temporary container
docker rm $CONTAINER_ID
# Calculate the hash of the extracted file
SOFTWARE_LIST_HASH=$(sha256sum ./software-list.txt | awk '{ print $1 }')
echo "Current hash of the software list file: $SOFTWARE_LIST_HASH"
echo "SOFTWARE_LIST_HASH=$SOFTWARE_LIST_HASH" >> $GITHUB_ENV
if [ "$PREV_SOFTWARE_LIST_HASH" == "$SOFTWARE_LIST_HASH" ]; then
echo "Software list is the same => Stopping"
echo "CONTINUE_BUILD=false" >> $GITHUB_ENV
fi
- name: "Cache download (full)"
if: env.CONTINUE_BUILD != 'false'
uses: actions/cache@v3
with:
path: ${{ env.BUILD_CACHE_FOLDER_FULL }}
key: ${{ env.CACHE_ID_FULL }}
# Build and push the Docker image for both amd64 and arm64 platforms
- name: Build and Push Docker image
if: env.CONTINUE_BUILD != 'false'
uses: docker/build-push-action@v4
with:
context: .
push: true
platforms: linux/amd64,linux/arm64
cache-from: type=local,src=${{ env.BUILD_CACHE_FOLDER_FULL }}/.buildx-cache
cache-to: type=local,dest=${{ env.BUILD_CACHE_FOLDER_FULL }}/.buildx-cache,mode=max
build-args: |
DOCKER_IMAGE_BUILD_VERSION=${{ env.NEW_VERSION }}
tags: ${{ vars.DOCKER_IMAGE }}:${{ env.NEW_VERSION }}-temp
# Pull and extract file from current Docker image
- name: Pull and Extract Software List from the Docker Image
if: env.CONTINUE_BUILD != 'false'
run: |
echo "TEMP_DOCKER_IMAGE_PREPARED=true" >> $GITHUB_ENV
IMAGE=${{ vars.DOCKER_IMAGE }}:${{ env.NEW_VERSION }}-temp
echo "Pulling Docker image: $IMAGE"
docker pull $IMAGE
# Create a container from the image but don't start it
CONTAINER_ID=$(docker create $IMAGE)
# Copy the file from the container to the host system
docker cp $CONTAINER_ID:/usr/local/jddlab/software-list.txt ./software-list.txt
# Remove the temporary container
docker rm $CONTAINER_ID
# Calculate the hash of the extracted file
SOFTWARE_LIST_HASH=$(sha256sum ./software-list.txt | awk '{ print $1 }')
echo "Current hash of the software list file: $SOFTWARE_LIST_HASH"
echo "SOFTWARE_LIST_HASH=$SOFTWARE_LIST_HASH" >> $GITHUB_ENV
if [ "$PREV_SOFTWARE_LIST_HASH" == "$SOFTWARE_LIST_HASH" ]; then
echo "Software list is the same => Stopping"
echo "CONTINUE_BUILD=false" >> $GITHUB_ENV
fi
# Preparing software changelog
python scripts/workflow/create-software-list-diff.py ./prev-software-list.txt ./software-list.txt >software.changelog.txt
cat software.changelog.txt
# Install gettext to use envsubst
- name: Install gettext for envsubst
if: env.CONTINUE_BUILD != 'false'
run: sudo apt-get install gettext
# Load template and dynamically populate it
- name: Prepare Release Body
if: env.CONTINUE_BUILD != 'false'
id: prepare_release_body
run: |
NEW_VERSION="${{ env.NEW_VERSION }}"
DOCKER_IMAGE="${{ vars.DOCKER_IMAGE }}"
# Substituting variables in template
export NEW_VERSION DOCKER_IMAGE
# Adding header to release_body.md
envsubst <scripts/workflow/templates/release.template.header.md | sed -e 's/§/$/g' >release_body.md
# Adding software changelog
cat software.changelog.txt >>release_body.md
# Adding footer to release_body.md
envsubst <scripts/workflow/templates/release.template.footer.md | sed -e 's/§/$/g' >>release_body.md
echo "release_body<<EOF" >> $GITHUB_ENV
cat release_body.md >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
# Create a GitHub release
- name: Create GitHub Release
if: env.CONTINUE_BUILD != 'false'
id: create_release
uses: actions/create-release@v1.1.0
with:
tag_name: ${{ env.NEW_VERSION }}
release_name: jddlab ${{ env.NEW_VERSION }}
body: ${{ env.release_body }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Automatically provided by GitHub
- name: Upload Release Asset
if: env.CONTINUE_BUILD != 'false'
uses: actions/upload-release-asset@v1
with:
upload_url: ${{ steps.create_release.outputs.upload_url }} # Upload URL from create_release step
asset_path: ./software-list.txt # Path to the file you want to upload
asset_name: software-list.txt # Name of the asset as it will appear on GitHub
asset_content_type: text/plain # Content type of the asset
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Use the token to authenticate
# Tag the image as 'latest' and remove temp
- name: Push docker image
if: env.CONTINUE_BUILD != 'false'
run: |
TMP_IMAGE=${{ vars.DOCKER_IMAGE }}:${{ env.NEW_VERSION }}-temp
docker buildx imagetools create -t ${{ vars.DOCKER_IMAGE }}:${{ env.NEW_VERSION }} $TMP_IMAGE
docker buildx imagetools create -t ${{ vars.DOCKER_IMAGE }}:latest $TMP_IMAGE
docker buildx imagetools create -t ${{ vars.GHCR_IMAGE }}:${{ env.NEW_VERSION }} $TMP_IMAGE
docker buildx imagetools create -t ${{ vars.GHCR_IMAGE }}:latest $TMP_IMAGE
# Remove image which is not required anymore
- name: Removing temp image
if: env.TEMP_DOCKER_IMAGE_PREPARED == 'true'
continue-on-error: true # This ensures the workflow continues even if this step fails
run: |
echo Removing temp image
curl -f -X DELETE -H "Authorization: Bearer ${{ secrets.DOCKER_PASSWORD }}" \
"https://hub.docker.com/v2/repositories/${{ vars.DOCKER_IMAGE }}/tags/${{ env.NEW_VERSION }}-temp"
# Log out of Docker Hub
- name: Log out of Docker Hub
if: always() # Ensures this step always runs
continue-on-error: true # This ensures the workflow continues even if this step fails
run: docker logout