Files
docker-baseimage-alpine/.gitea/workflows/build-and-push.yaml
pi-farm bb5589da0d
Some checks failed
/ release-and-build (push) Failing after 56s
fix push to Dockerhub
2026-02-14 00:20:56 +01:00

451 lines
20 KiB
YAML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
on:
push:
branches:
- 'main'
tags:
- '*'
schedule:
- cron: '0 5 * * 0'
workflow_dispatch:
jobs:
release-and-build:
runs-on: buildx-multiarch
if: "!contains(gitea.event.head_commit.message, '[skip ci]')"
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Prepare Environment and Read Config
id: prep
run: |
export TZ=Europe/Berlin
# 1. Env laden
set -a
source <(grep -v '^#' buildargs.env | sed 's/\r$//')
set +a
# 2. Hash der BUILD_ Variablen berechnen (für Change-Detection)
# Wir nehmen nur Zeilen, die mit BUILD_ beginnen
ARGS_HASH=$(grep "^BUILD_" buildargs.env | sha256sum | cut -d' ' -f1)
echo "build_args_hash=$ARGS_HASH" >> $GITHUB_OUTPUT
# ... (Rest deiner Logik für Tags, Owner etc. bleibt gleich) ...
echo "event_name=${{ gitea.event_name }}" >> $GITHUB_OUTPUT
if [[ "${{ gitea.ref }}" == refs/tags/* ]]; then
CLEAN_TAG=${{ gitea.ref_name }}
echo "docker_tag=${CLEAN_TAG#v}" >> $GITHUB_OUTPUT
else
# Fallback falls BUILD_TAG nicht gesetzt ist
echo "docker_tag=${BUILD_TAG:-latest}" >> $GITHUB_OUTPUT
fi
OWNER=$(echo "${{ gitea.repository }}" | cut -d'/' -f1 | tr '[:upper:]' '[:lower:]')
REPO_NAME=$(echo "${{ gitea.repository }}" | cut -d'/' -f2 | tr '[:upper:]' '[:lower:]')
echo "repo_pure=$REPO_NAME" >> $GITHUB_OUTPUT
echo "docker_tag=$BUILD_TAG" >> $GITHUB_OUTPUT
echo "image_name=git.pi-farm.de/$OWNER/$REPO_NAME" >> $GITHUB_OUTPUT
echo "base_image=$BUILD_BASE_IMAGE" >> $GITHUB_OUTPUT
- name: Login to Gitea Registry
run: |
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login \
git.pi-farm.de -u ${{ secrets.REGISTRY_USER }} --password-stdin
- name: Check for Real Changes
id: check_changes
shell: bash
run: |
SHOULD_BUILD="true"
IMAGE="${{ steps.prep.outputs.image_name }}"
TAG="${{ steps.prep.outputs.docker_tag }}"
BASE="${{ steps.prep.outputs.base_image }}"
LOCAL_ARGS_HASH="${{ steps.prep.outputs.build_args_hash }}"
echo "🔍 Prüfe Remote-Registry..."
echo " Lokaler Args-Hash: $LOCAL_ARGS_HASH"
if [[ "${{ steps.prep.outputs.event_name }}" != "schedule" ]]; then
echo "🚀 Manueller Start: Build erzwungen."
else
REMOTE_BASE_SHA=$(docker buildx imagetools inspect "$BASE" --format '{{json .Manifest.Digest}}' 2>/dev/null | tr -d '"' || echo "")
if [ -z "$REMOTE_BASE_SHA" ]; then
echo "⚠️ Base-Image SHA nicht lesbar. Build wird gestartet."
else
echo " Base-Image SHA (Remote): $REMOTE_BASE_SHA"
RAW_INDEX=$(docker buildx imagetools inspect "$IMAGE:$TAG" --raw 2>/dev/null || echo "")
if [ -z "$RAW_INDEX" ]; then
echo "🆕 Image existiert noch nicht. Build erforderlich."
else
# 1. Base Digest aus Annotation holen
USED_BASE_SHA=$(echo "$RAW_INDEX" | grep "\"pi_farm.base_digest\":" | sed -E 's/.*: "([^"]+)".*/\1/' || echo "none")
# 2. Args Hash aus Annotation holen (NEU!)
USED_ARGS_HASH=$(echo "$RAW_INDEX" | grep "\"pi_farm.args_hash\":" | sed -E 's/.*: "([^"]+)".*/\1/' || echo "none")
echo " Remote Base SHA: $USED_BASE_SHA"
echo " Remote Args Hash: $USED_ARGS_HASH"
# Logik: Build wenn Base anders ODER Args anders
if [ "$REMOTE_BASE_SHA" == "$USED_BASE_SHA" ] && [ "$LOCAL_ARGS_HASH" == "$USED_ARGS_HASH" ]; then
echo "😴 Alles identisch. Kein Build nötig."
SHOULD_BUILD="false"
else
echo "✅ Update nötig (Base oder Args geändert)."
SHOULD_BUILD="true"
fi
fi
fi
fi
echo "should_build=$SHOULD_BUILD" >> $GITHUB_OUTPUT
- name: Set up QEMU
if: steps.check_changes.outputs.should_build == 'true'
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
if: steps.check_changes.outputs.should_build == 'true'
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: https://github.com/docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and Push Docker Image
if: steps.check_changes.outputs.should_build == 'true'
run: |
IMAGE_GITEA=${{ steps.prep.outputs.image_name }}
TAG=${{ steps.prep.outputs.docker_tag }}
ARGS_HASH=${{ steps.prep.outputs.build_args_hash }}
BASE=${{ steps.prep.outputs.base_image }}
REPO_PURE=${{ steps.prep.outputs.repo_pure }}
set -a
source <(grep -v '^#' buildargs.env | sed 's/\r$//')
set +a
# 1. Gemeinsame Build-Args in ein Array
DOCKER_ARGS=()
keys=$(grep -v '^#' buildargs.env | cut -d'=' -f1 | tr -d '\r')
for k in $keys; do
DOCKER_ARGS+=("--build-arg" "$k=${!k}")
done
# Base SHA ermitteln
BASE_SHA=$(docker buildx imagetools inspect $BASE --format '{{json .Manifest.Digest}}' 2>/dev/null | tr -d '"' || echo "unknown")
# 2. Arrays für die Tags (AMD64 und ARM64 getrennt)
AMD_TAGS=("-t" "$IMAGE_GITEA:tmp-amd64")
ARM_TAGS=("-t" "$IMAGE_GITEA:tmp-arm64")
# Docker Hub hinzufügen, falls aktiviert
if [[ "$PUSH" == *"dockerhub"* ]]; then
DOCKERHUB_REPO="${{ secrets.DOCKERHUB_USERNAME }}/$REPO_PURE"
echo "📢 Docker Hub Push aktiviert für: $DOCKERHUB_REPO"
AMD_TAGS+=("-t" "$DOCKERHUB_REPO:tmp-amd64")
ARM_TAGS+=("-t" "$DOCKERHUB_REPO:tmp-arm64")
fi
# 3. Multi-Arch Build (jetzt mit sauberen Arrays)
# AMD64
docker buildx build "${DOCKER_ARGS[@]}" "${AMD_TAGS[@]}" \
--pull --platform linux/amd64 -f Dockerfile \
--label "pi_farm.base_digest=$BASE_SHA" \
--label "pi_farm.args_hash=$ARGS_HASH" \
--push .
# ARM64
docker buildx build "${DOCKER_ARGS[@]}" "${ARM_TAGS[@]}" \
--pull --platform linux/arm64 -f Dockerfile.aarch64 \
--label "pi_farm.base_digest=$BASE_SHA" \
--label "pi_farm.args_hash=$ARGS_HASH" \
--push .
# 4. Manifeste finalisieren
# Gitea
docker buildx imagetools create \
--annotation "index:pi_farm.base_digest=$BASE_SHA" \
--annotation "index:pi_farm.args_hash=$ARGS_HASH" \
-t $IMAGE_GITEA:$TAG -t $IMAGE_GITEA:latest $IMAGE_GITEA:tmp-amd64 $IMAGE_GITEA:tmp-arm64
# Docker Hub (Manifest für die finalen Tags erstellen)
if [[ "$PUSH" == *"dockerhub"* ]]; then
DOCKERHUB_REPO="${{ secrets.DOCKERHUB_USERNAME }}/$REPO_PURE"
docker buildx imagetools create \
--annotation "index:pi_farm.base_digest=$BASE_SHA" \
--annotation "index:pi_farm.args_hash=$ARGS_HASH" \
-t $DOCKERHUB_REPO:$TAG -t $DOCKERHUB_REPO:latest $IMAGE_GITEA:tmp-amd64 $IMAGE_GITEA:tmp-arm64
fi
- name: Push README to Docker Hub
if: steps.check_changes.outputs.should_build == 'true' && contains(env.PUSH, 'dockerhub')
run: |
# Token holen
TOKEN=$(curl -s -X POST "https://hub.docker.com/v2/users/login/" \
-H "Content-Type: application/json" \
-d "{\"username\": \"${{ secrets.DOCKERHUB_USERNAME }}\", \"password\": \"${{ secrets.DOCKERHUB_TOKEN }}\"}" | jq -r .token)
if [ "$TOKEN" != "null" ]; then
REPO_PURE=${{ steps.prep.outputs.repo_pure }}
DH_USER="${{ secrets.DOCKERHUB_USERNAME }}"
# Kurzbeschreibung für die API säubern (nur erste Zeile, max 100 Zeichen)
SHORT_DESC=$(echo -e "${{ env.DESCRIPTION }}" | head -n 1 | cut -c 1-100)
echo "📤 Übertrage README und Metadata zu Docker Hub..."
curl -s -X PATCH "https://hub.docker.com/v2/repositories/${DH_USER}/${REPO_PURE}/" \
-H "Authorization: JWT ${TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"description\": \"${SHORT_DESC}\",
\"full_description\": $(jq -Rs . < README.md)
}"
echo "✅ Docker Hub erfolgreich aktualisiert."
fi
- name: Cleanup Temporary Registry Tags
if: steps.check_changes.outputs.should_build == 'true'
run: |
TOKEN="${{ secrets.GIT_TOKEN }}"
# Wir extrahieren den Original-Owner und Namen direkt aus dem Repo-Pfad
# Gitea ist bei der API oft empfindlich, was Groß/Kleinschreibung angeht
ORG_NAME=$(echo "${{ gitea.repository }}" | cut -d'/' -f1)
REPO_NAME=$(echo "${{ gitea.repository }}" | cut -d'/' -f2)
echo "🗑️ Bereinige temporäre Tags für $ORG_NAME/$REPO_NAME..."
for t in tmp-amd64 tmp-arm64; do
echo "Versuche Löschung von Tag: $t"
# Versuch 1: Original-Schreibweise
STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X 'DELETE' \
"https://git.pi-farm.de/api/v1/packages/$ORG_NAME/container/$REPO_NAME/$t" \
-H "Authorization: token $TOKEN")
if [ "$STATUS" -eq 204 ]; then
echo "✅ Tag $t gelöscht (Original-Schreibweise)."
else
# Versuch 2: Kleingeschriebene Version (falls Gitea das intern umwandelt)
ORG_LOWER=$(echo "$ORG_NAME" | tr '[:upper:]' '[:lower:]')
REPO_LOWER=$(echo "$REPO_NAME" | tr '[:upper:]' '[:lower:]')
STATUS_LOWER=$(curl -s -o /dev/null -w "%{http_code}" -X 'DELETE' \
"https://git.pi-farm.de/api/v1/packages/$ORG_LOWER/container/$REPO_LOWER/$t" \
-H "Authorization: token $TOKEN")
if [ "$STATUS_LOWER" -eq 204 ]; then
echo "✅ Tag $t gelöscht (Kleinschreibung)."
else
echo "⚠️ Tag $t konnte nicht gelöscht werden (HTTP $STATUS / $STATUS_LOWER)."
echo "Prüfe bitte: Hat der Token 'write:package' Rechte und ist der User in der Org?"
fi
fi
done
- name: Update Documentation and Compose
id: update_doc
if: steps.check_changes.outputs.should_build == 'true'
run: |
# --- 1. VARIABLEN VORBEREITEN ---
export TZ=Europe/Berlin
CURRENT_TIME=$(date '+%d.%m.%Y %H:%M')
BUILD_DATE_ONLY=$(date '+%d.%m.%Y')
BUILD_TAG=${{ steps.prep.outputs.docker_tag }}
FULL_URL=${{ steps.prep.outputs.image_name }}
REPO_PURE=${{ steps.prep.outputs.repo_pure }}
BASE_IMAGE=${{ steps.prep.outputs.base_image }}
if [ -f "Dockerfile.aarch64" ]; then
ARM_STATUS="✅ Aktiv (eigenes Dockerfile)"
else
ARM_STATUS="❌ Nicht unterstützt"
fi
# Umgebungsvariablen laden
set -a
source <(grep -v '^#' buildargs.env | sed 's/\r$//')
set +a
COMMIT_MSG=$(echo "${{ github.event.head_commit.message }}" | sed 's/\[skip ci\]//g' | xargs)
# --- 2. TEMPLATES LADEN ---
wget -q https://git.pi-farm.de/pi-farm/templates/raw/branch/main/README.template -O README.template
wget -q https://git.pi-farm.de/pi-farm/templates/raw/branch/main/docker-compose.template -O docker-compose.template
# --- 3. VERSION HISTORY UPDATE ---
NEW_ROW="| **$BUILD_TAG** | $CURRENT_TIME | $COMMIT_MSG ✅ |"
if [ -f "VERSION.history" ]; then
grep -v "| **$BUILD_TAG** |" VERSION.history > VERSION.history.tmp
echo "$NEW_ROW" > VERSION.history
cat VERSION.history.tmp >> VERSION.history
rm VERSION.history.tmp
else
echo "$NEW_ROW" > VERSION.history
fi
HISTORY_CONTENT=$(cat VERSION.history)
# --- 4. ENV / PORTS / VOL BLÖCKE GENERIEREN ---
ENV_BLOCK_CONTENT=""
env_vars=$(grep '^ENV_' buildargs.env | grep -v '^#' | tr -d '\r')
if [ ! -z "$env_vars" ]; then
ENV_BLOCK_CONTENT=" environment:\n"
for line in $env_vars; do
key=$(echo $line | cut -d= -f1)
val=$(echo $line | cut -d= -f2-)
clean_key=${key#ENV_}
ENV_BLOCK_CONTENT="${ENV_BLOCK_CONTENT} - ${clean_key}=${val}\n"
done
fi
PORTS_BLOCK_CONTENT=""
port_vars=$(grep '^PORT_' buildargs.env | grep -v '^#' | tr -d '\r' || true)
if [ ! -z "$port_vars" ]; then
PORTS_BLOCK_CONTENT=" ports:\n"
for line in $port_vars; do
val=$(echo $line | cut -d= -f2-)
PORTS_BLOCK_CONTENT="${PORTS_BLOCK_CONTENT} - ${val}\n"
done
fi
VOL_BLOCK_CONTENT=""
vol_vars=$(grep '^VOL_' buildargs.env | grep -v '^#' | tr -d '\r' || true)
if [ ! -z "$vol_vars" ]; then
VOL_BLOCK_CONTENT=" volumes:\n"
for line in $vol_vars; do
val=$(echo $line | cut -d= -f2-)
VOL_BLOCK_CONTENT="${VOL_BLOCK_CONTENT} - ${val}\n"
done
fi
# --- 5. DOCKER RUN BEFEHL GENERIEREN ---
RUN_CMD="docker run -d \\ \n --name $REPO_PURE \\ \n --restart unless-stopped"
# Environment Args für Run Command
while read -r line; do
if [[ "$line" =~ ^PORT_ ]]; then
val=$(echo $line | cut -d= -f2-)
RUN_CMD="${RUN_CMD} \\ \n -p ${val}"
elif [[ "$line" =~ ^ENV_ ]]; then
key=$(echo $line | cut -d= -f1)
clean_key=${key#ENV_}
val=$(echo $line | cut -d= -f2-)
RUN_CMD="${RUN_CMD} \\ \n -e ${clean_key}=${val}"
elif [[ "$line" =~ ^VOL_ ]]; then
val=$(echo $line | cut -d= -f2-)
RUN_CMD="${RUN_CMD} \\ \n -v ${val}"
fi
done < <(grep -E '^(PORT_|ENV_|VOL_)' buildargs.env | grep -v '^#' | sed 's/\r$//')
RUN_CMD="${RUN_CMD} \\ \n $FULL_URL:$BUILD_TAG"
DOCKER_RUN_FINAL=$(echo -e "$RUN_CMD")
# --- 6. DOCKER HUB LINK GENERATOR ---
DOCKERHUB_LINK_CONTENT=""
if [[ "$PUSH" == *"dockerhub"* ]]; then
DH_USER="${{ secrets.DOCKERHUB_USERNAME }}"
DOCKERHUB_LINK_CONTENT="[![Docker Hub](https://img.shields.io/badge/docker-hub-blue?logo=docker&logoColor=white)](https://hub.docker.com/r/${DH_USER}/${REPO_PURE})"
fi
# --- 7. TEMPLATE ENGINE FUNKTION ---
process_template() {
local template=$1; local output=$2
if [ -f "$template" ]; then
> "$output"
while IFS= read -r line || [ -n "$line" ]; do
line="${line//__REPO_NAME__/$REPO_PURE}"
line="${line//__FULL_URL__/$FULL_URL}"
line="${line//__BUILD_TAG__/$BUILD_TAG}"
line="${line//__BASE_IMAGE__/$BASE_IMAGE}"
line="${line//__ARM_STATUS__/$ARM_STATUS}"
line="${line//__CURRENT_DATE__/$CURRENT_TIME}"
line="${line//__HISTORY_CONTENT__/$HISTORY_CONTENT}"
line="${line//__DOCKER_RUN__/$DOCKER_RUN_FINAL}"
line="${line//__DOCKERHUB_LINK__/$DOCKERHUB_LINK_CONTENT}"
if [[ "$line" == *"__DESCRIPTION__"* ]]; then
echo -e "${DESCRIPTION:-Keine Beschreibung.}" >> "$output"
elif [[ "$line" == *"__ENV_BLOCK__"* ]]; then
[ -n "$ENV_BLOCK_CONTENT" ] && echo -e "${ENV_BLOCK_CONTENT}" >> "$output"
elif [[ "$line" == *"__PORTS_BLOCK__"* ]]; then
[ -n "$PORTS_BLOCK_CONTENT" ] && echo -e "${PORTS_BLOCK_CONTENT}" >> "$output"
elif [[ "$line" == *"__VOL_BLOCK__"* ]]; then
[ -n "$VOL_BLOCK_CONTENT" ] && echo -e "${VOL_BLOCK_CONTENT}" >> "$output"
elif [[ "$line" == *"__COMPOSE_BLOCK__"* ]]; then
[ -f "docker-compose.yml" ] && cat docker-compose.yml >> "$output"
else
echo "$line" >> "$output"
fi
done < "$template"
fi
}
# Templates verarbeiten (Compose ZUERST!)
process_template "docker-compose.template" "docker-compose.yml"
process_template "README.template" "README.md"
# --- 8. EXPORTS FÜR NÄCHSTE STEPS ---
echo "FINAL_MSG=$COMMIT_MSG" >> $GITHUB_ENV
# Multiline Export für DESCRIPTION
echo "DESCRIPTION<<EOF" >> $GITHUB_ENV
echo -e "$DESCRIPTION" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
- name: Commit, Tag and Push Changes
if: steps.check_changes.outputs.should_build == 'true'
run: |
git config --local user.email "action@pi-farm.de"
git config --local user.name "Gitea Action"
git add VERSION.history README.md docker-compose.yml
git diff --quiet && git diff --staged --quiet || git commit -m "${{ env.FINAL_MSG }} [skip ci]"
git tag -f "v${{ steps.prep.outputs.docker_tag }}"
git push origin main
git push -f origin "v${{ steps.prep.outputs.docker_tag }}"
- name: Cleanup Temporary Tags on Docker Hub
if: steps.check_changes.outputs.should_build == 'true' && contains(env.PUSH, 'dockerhub')
run: |
echo "🧹 Aufräumen temporärer Docker Hub Tags..."
# 1. Login Token holen
TOKEN=$(curl -s -X POST "https://hub.docker.com/v2/users/login/" \
-H "Content-Type: application/json" \
-d "{\"username\": \"${{ secrets.DOCKERHUB_USERNAME }}\", \"password\": \"${{ secrets.DOCKERHUB_TOKEN }}\"}" | jq -r .token)
if [ "$TOKEN" != "null" ]; then
REPO_PURE=${{ steps.prep.outputs.repo_pure }}
DH_USER="${{ secrets.DOCKERHUB_USERNAME }}"
# 2. Lösche tmp-amd64
echo "Lösche $DH_USER/$REPO_PURE:tmp-amd64 ..."
curl -s -X DELETE "https://hub.docker.com/v2/repositories/${DH_USER}/${REPO_PURE}/tags/tmp-amd64/" \
-H "Authorization: JWT ${TOKEN}"
# 3. Lösche tmp-arm64
echo "Lösche $DH_USER/$REPO_PURE:tmp-arm64 ..."
curl -s -X DELETE "https://hub.docker.com/v2/repositories/${DH_USER}/${REPO_PURE}/tags/tmp-arm64/" \
-H "Authorization: JWT ${TOKEN}"
echo "✅ Temporäre Tags entfernt."
else
echo "⚠️ Konnte kein Token für Cleanup erhalten. Tags bleiben bestehen."
fi
- name: Cleanup Docker Artifacts
if: always()
run: docker image prune -f
- name: Workflow Summary
if: always()
run: |
echo "Check completed. Build was: ${{ steps.check_changes.outputs.should_build }}"