Skip to content
name: Build and Push Docker Images to GHCR (User-Specified or Auto-Increment Version)
on:
workflow_dispatch:
inputs:
version:
description: "Specify a custom version (e.g., 1.5). Leave empty for auto-increment."
required: false
default: ""
push:
branches:
- main
paths:
- "**/**" # Detect changes at any path
permissions:
contents: read
packages: write
jobs:
build-and-push:
runs-on: ubuntu-latest
env:
DOCKER_CONFIG: /tmp/docker-config # ephemeral Docker config folder
steps:
##################################################################
# 1) CHECK OUT CODE
##################################################################

Check failure on line 31 in .github/workflows/build-and-push.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/build-and-push.yml

Invalid workflow file

You have an error in your yaml syntax on line 31
- name: Checkout Repository
uses: actions/checkout@v3
with:
fetch-depth: 2 # Ensure we have a previous commit if it exists
##################################################################
# 2) PREPARE EPHEMERAL DOCKER CONFIG
##################################################################
- name: Prepare Ephemeral Docker Config
run: mkdir -p /tmp/docker-config
##################################################################
# 3) CONVERT REPO OWNER TO LOWERCASE
##################################################################
- name: Convert Repository Owner to Lowercase
run: echo "OWNER=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
##################################################################
# 4) DETECT CHANGED DIRECTORIES
##################################################################
- name: Detect Changed Directories
id: detect
run: |
echo "=== Detecting changed top-level directories ==="
# If there's a previous commit, find changed directories
if git rev-parse HEAD^ >/dev/null 2>&1; then
CHANGED_DIRS=$(git diff --name-only HEAD^ HEAD -- | awk -F/ '{print $1}' | sort -u | grep -v '^\.' || true)
else
# If first commit, consider all top-level directories
CHANGED_DIRS=$(ls -d */ | cut -f1 -d'/' | grep -v '^\.' || true)
fi
echo "Raw changed directories: $CHANGED_DIRS"
# Convert to space-separated for looping
echo "CHANGED_DIRS=$(echo "$CHANGED_DIRS" | tr '\n' ' ')" >> $GITHUB_ENV
##################################################################
# 5) LOG IN TO GHCR (SECURELY, WITH EPHEMERAL CONFIG)
##################################################################
- name: Log in to GitHub Container Registry
run: |
echo "${{ secrets.GHCR_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
echo "Successfully logged in using ephemeral Docker config at $DOCKER_CONFIG"
##################################################################
# 6) BUILD AND PUSH DOCKER IMAGES
##################################################################
- name: Build and Push Docker Images
run: |
# If no directories changed, skip
if [ -z "${{ env.CHANGED_DIRS }}" ]; then
echo "No top-level directories changed. Skipping build."
exit 0
fi
echo "Directories to process: ${{ env.CHANGED_DIRS }}"
# Check if user provided a custom version
USER_SPECIFIED_VERSION="${{ github.event.inputs.version }}"
echo "User-specified version: $USER_SPECIFIED_VERSION (empty = auto)"
for dir in ${{ env.CHANGED_DIRS }}; do
if [ ! -d "$dir" ]; then
echo "Skipping '$dir' (not a directory)."
continue
fi
echo "=== Processing directory: $dir ==="
# If user specified a version, use it; otherwise auto-detect
if [ -n "$USER_SPECIFIED_VERSION" ]; then
NEW_VERSION="$USER_SPECIFIED_VERSION"
echo "Using user-specified version: $NEW_VERSION"
else
##################################################################
# AUTO-INCREMENT LOGIC
##################################################################
PACKAGE_URL="https://api.github.com/orgs/${{ env.OWNER }}/packages/container/$dir/versions"
echo "Fetching GHCR versions from: $PACKAGE_URL"
API_RESPONSE=$(curl -s \
-H "Authorization: Bearer ${{ secrets.GHCR_TOKEN }}" \
-H "Accept: application/vnd.github.v3+json" \
"$PACKAGE_URL")
echo "GHCR API Response: $API_RESPONSE"
# If package not found => start at 1.0
if echo "$API_RESPONSE" | grep -q '"message": "Package not found."'; then
NEW_VERSION="1.0"
echo "Package '$dir' not found. Starting at version 1.0."
else
# Check if GHCR returned a valid array
IS_ARRAY=$(echo "$API_RESPONSE" | jq -r 'type' 2>/dev/null || true)
if [ "$IS_ARRAY" != "array" ]; then
NEW_VERSION="1.0"
echo "GHCR response not an array. Starting at 1.0."
else
# Regex allows optional 'v' prefix: ^v?[0-9]+\.[0-9]+$
VERSION_TAGS=$(echo "$API_RESPONSE" | jq -r '
[.[].metadata.container.tags? // [] | .[]]
| map(select(test("^v?[0-9]+\\.[0-9]+$") and . != "latest"))
| sort
| last
')
if [ -z "$VERSION_TAGS" ] || [ "$VERSION_TAGS" = "null" ]; then
NEW_VERSION="1.0"
echo "No valid versions found for '$dir'. Starting at 1.0."
else
# Strip leading 'v' if present, then increment minor
CLEAN_VERSION=$(echo "$VERSION_TAGS" | sed 's/^v//')
NEW_VERSION=$(awk -F. '{print $1 "." $2+1}' <<< "$CLEAN_VERSION")
echo "Latest version: $CLEAN_VERSION => Incremented to: $NEW_VERSION"
fi
fi
fi
fi
echo "Final version for '$dir': $NEW_VERSION"
IMAGE_NAME="ghcr.io/${{ env.OWNER }}/$dir"
echo "Docker Image Name: $IMAGE_NAME"
echo "=== Building Docker image: $IMAGE_NAME:v$NEW_VERSION ==="
docker build -t "$IMAGE_NAME:v$NEW_VERSION" -t "$IMAGE_NAME:latest" "$dir/"
docker push "$IMAGE_NAME:v$NEW_VERSION"
docker push "$IMAGE_NAME:latest"
##############################################################################
# AFTER PUSH: MAKE THE PACKAGE PUBLIC
##############################################################################
- name: Make GHCR Package Public
run: |
# We do NOT use ${{ env.OWNER }} here.
# Instead, we call GitHub's actual repository_owner as returned (possibly uppercase).
REAL_OWNER="${{ github.repository_owner }}"
echo "Making the GHCR package '$dir' public under org: $REAL_OWNER"
curl -X PATCH "https://api.github.com/orgs/$REAL_OWNER/packages/container/$dir/visibility" \
-H "Authorization: Bearer ${{ secrets.GHCR_TOKEN }}" \
-H "Accept: application/vnd.github.v3+json" \
-d '{"visibility":"public"}'