Docker Image CI #109
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
# change this uniq id to reset all caches | |
uniq_id_to_reset_cache=${{ github.sha }} | |
# 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 |