Files
base-image-debian-rdp-ldap/.gitea/workflows/build-and-push.yaml
T
pi-farm d25f1697c5
build-and-push.yaml / release-and-build (push) Canceled after 1m44s
fix build and cleanup steps if Dockerfile.aarch64 does not exist
2026-10-07 10:31:35 +00:00

498 lines
23 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
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:
- 'dev'
tags:
- '*'
schedule:
- cron: '0 5 * * 0'
workflow_dispatch:
permissions:
contents: write
packages: write
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:
token: ${{ secrets.RUNNER_TOKEN }}
fetch-depth: 0
- name: Prepare Environment and Read Config
id: prep
run: |
export TZ=Europe/Berlin
# FIX 1: Stellt sicher, dass die Datei mit einem Zeilenumbruch endet.
# Das verhindert, dass Bash die letzte Zeile beim Einlesen verschluckt.
sed -i -e '$a\' buildargs.env
set -a
source <(grep -v '^#' buildargs.env | sed 's/\r$//')
set +a
echo "push_targets=$PUSH" >> $GITHUB_OUTPUT
ARGS_HASH=$(grep "^BUILD_" buildargs.env | sha256sum | cut -d' ' -f1)
echo "build_args_hash=$ARGS_HASH" >> $GITHUB_OUTPUT
echo "event_name=${{ gitea.event_name }}" >> $GITHUB_OUTPUT
# FIX 2: Kugelsicheres Auslesen des Tags direkt aus der Datei.
# Trimm-Befehle (tr) entfernen eventuelle unsichtbare Zeichen oder Anführungszeichen.
SAFE_BUILD_TAG=$(grep "^BUILD_TAG=" buildargs.env | cut -d'=' -f2 | tr -d '\r' | tr -d '"' | xargs)
if [[ "${{ gitea.ref }}" == refs/tags/* ]]; then
CLEAN_TAG=${{ gitea.ref_name }}
echo "docker_tag=${CLEAN_TAG#v}" >> $GITHUB_OUTPUT
else
# Nutzt jetzt garantiert den direkt extrahierten Wert
echo "docker_tag=${SAFE_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 "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.RUNNER_TOKEN }}" | docker login git.pi-farm.de -u "${{ gitea.actor }}" --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 "🔍 Check Remote-Registry..."
if [[ "${{ steps.prep.outputs.event_name }}" != "schedule" ]]; then
echo "🚀 Start: Build forced."
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 not readable. Build process started."
else
RAW_INDEX=$(docker buildx imagetools inspect "$IMAGE:$TAG" --raw 2>/dev/null || echo "")
if [ -z "$RAW_INDEX" ]; then
echo "🆕 Image doesn't exist. Build required."
else
USED_BASE_SHA=$(echo "$RAW_INDEX" | grep "\"pi_farm.base_digest\":" | sed -E 's/.*: "([^"]+)".*/\1/' || echo "none")
USED_ARGS_HASH=$(echo "$RAW_INDEX" | grep "\"pi_farm.args_hash\":" | sed -E 's/.*: "([^"]+)".*/\1/' || echo "none")
if [ "$REMOTE_BASE_SHA" == "$USED_BASE_SHA" ] && [ "$LOCAL_ARGS_HASH" == "$USED_ARGS_HASH" ]; then
echo "😴 All identic. Build not neccessary."
SHOULD_BUILD="false"
else
echo "✅ Build neccessary."
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
if: contains(steps.prep.outputs.push_targets, 'dockerhub')
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 }}
DOCKERHUB_REPO="${{ secrets.DOCKERHUB_USERNAME }}/$REPO_PURE"
set -a
source <(grep -v '^#' buildargs.env | sed 's/\r$//')
set +a
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=$(docker buildx imagetools inspect $BASE --format '{{json .Manifest.Digest}}' 2>/dev/null | tr -d '"' || echo "unknown")
# AMD64 ist immer das primäre Target
AMD_TAGS=("-t" "$IMAGE_GITEA:tmp-amd64")
GITEA_SOURCES=("$IMAGE_GITEA:tmp-amd64")
if [[ "$PUSH" == *"dockerhub"* ]]; then
AMD_TAGS+=("-t" "$DOCKERHUB_REPO:tmp-amd64")
DH_SOURCES=("$DOCKERHUB_REPO:tmp-amd64")
fi
# 1. Build AMD64 & Push
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 .
# 2. Prüfung auf ARM64 Dockerfile
if [ -f "Dockerfile.aarch64" ]; then
echo "⚙️ Dockerfile.aarch64 gefunden. Baue ARM64 Image..."
ARM_TAGS=("-t" "$IMAGE_GITEA:tmp-arm64")
GITEA_SOURCES+=("$IMAGE_GITEA:tmp-arm64")
if [[ "$PUSH" == *"dockerhub"* ]]; then
ARM_TAGS+=("-t" "$DOCKERHUB_REPO:tmp-arm64")
DH_SOURCES+=("$DOCKERHUB_REPO:tmp-arm64")
fi
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 .
else
echo "ℹ️ Keine Dockerfile.aarch64 gefunden. ARM64 Build wird übersprungen."
fi
# 3. Manifest auf Gitea erstellen (nutzt nur die tatsächlich gebauten Temp-Tags)
GITEA_MANIFEST_TAGS=("-t" "$IMAGE_GITEA:$TAG")
if [[ "$BUILD_TAG_LATEST" == "y" ]]; then
GITEA_MANIFEST_TAGS+=("-t" "$IMAGE_GITEA:latest")
fi
docker buildx imagetools create \
--annotation "index:pi_farm.base_digest=$BASE_SHA" --annotation "index:pi_farm.args_hash=$ARGS_HASH" \
"${GITEA_MANIFEST_TAGS[@]}" "${GITEA_SOURCES[@]}"
# 4. Manifest auf Docker Hub erstellen
if [[ "$PUSH" == *"dockerhub"* ]]; then
DH_MANIFEST_TAGS=("-t" "$DOCKERHUB_REPO:$TAG")
if [[ "$BUILD_TAG_LATEST" == "y" ]]; then
DH_MANIFEST_TAGS+=("-t" "$DOCKERHUB_REPO:latest")
fi
docker buildx imagetools create \
--annotation "index:pi_farm.base_digest=$BASE_SHA" --annotation "index:pi_farm.args_hash=$ARGS_HASH" \
"${DH_MANIFEST_TAGS[@]}" "${DH_SOURCES[@]}"
fi
- name: Update Documentation and Compose
id: update_doc
if: steps.check_changes.outputs.should_build == 'true'
run: |
set -x
export TZ=Europe/Berlin
CURRENT_TIME=$(date '+%d.%m.%Y %H:%M')
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 "buildargs.env" ]; then echo "❌ buildargs.env missing!"; exit 1; fi
grep -v '^#' buildargs.env | sed 's/\r$//' > cleaned_env.sh
set -a
source ./cleaned_env.sh
set +a
if [ -f "Dockerfile.aarch64" ]; then ARM_STATUS="✅ Active"; else ARM_STATUS="❌ Not supported"; fi
cat << 'EOF' > commit_msg.txt
${{ gitea.event.head_commit.message }}
EOF
COMMIT_MSG=$(sed 's/\[skip ci\]//g' commit_msg.txt | xargs)
# 1. Authentifizierter Download der Templates via curl (-f bricht bei HTTP-Fehlern ab)
TOKEN="${{ secrets.RUNNER_TOKEN }}"
curl -sSLf -H "Authorization: token $TOKEN" \
"https://git.pi-farm.de/pi-farm/templates/raw/branch/dev/README.template" \
-o README.template || echo "Warnung: README Template konnte nicht geladen werden"
curl -sSLf -H "Authorization: token $TOKEN" \
"https://git.pi-farm.de/pi-farm/templates/raw/branch/dev/docker-compose.template" \
-o docker-compose.template || echo "Warnung: Compose Template konnte nicht geladen werden"
# 2. Sicherheitsprüfung: Falls Gitea trotz -f eine HTML-Login-Seite ausgibt, Datei sofort verwerfen
for t_file in README.template docker-compose.template; do
if [ -f "$t_file" ] && grep -qi "<!DOCTYPE html>" "$t_file"; then
echo "❌ FEHLER: $t_file ist eine HTML-Loginseite! Download verworfen."
rm -f "$t_file"
fi
done
NEW_ROW="| **$BUILD_TAG** | $CURRENT_TIME | $COMMIT_MSG ✅ |"
if [ -f "VERSION.history" ]; then
grep -v "| **$BUILD_TAG** |" VERSION.history > VERSION.history.tmp || true
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)
ENV_BLOCK_CONTENT=""
env_vars=$(grep '^ENV_' buildargs.env | grep -v '^#' | tr -d '\r' || true)
if [ -n "$env_vars" ]; then
ENV_BLOCK_CONTENT=" environment:\n"
while read -r line; do
[ -z "$line" ] && continue
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 <<< "$env_vars"
fi
PORTS_BLOCK_CONTENT=""
port_vars=$(grep '^PORT_' buildargs.env | grep -v '^#' | tr -d '\r' || true)
if [ -n "$port_vars" ]; then
PORTS_BLOCK_CONTENT=" ports:\n"
while read -r line; do
[ -z "$line" ] && continue
val=$(echo "$line" | cut -d= -f2-); PORTS_BLOCK_CONTENT="${PORTS_BLOCK_CONTENT} - ${val}\n"
done <<< "$port_vars"
fi
VOL_BLOCK_CONTENT=""
vol_vars=$(grep '^VOL_' buildargs.env | grep -v '^#' | tr -d '\r' || true)
if [ -n "$vol_vars" ]; then
VOL_BLOCK_CONTENT=" volumes:\n"
while read -r line; do
[ -z "$line" ] && continue
val=$(echo "$line" | cut -d= -f2-); VOL_BLOCK_CONTENT="${VOL_BLOCK_CONTENT} - ${val}\n"
done <<< "$vol_vars"
fi
{
echo "docker run -d \\"
echo " --name $REPO_PURE \\"
echo " --restart unless-stopped \\"
all_params=$(grep -E '^(PORT_|ENV_|VOL_)' buildargs.env | grep -v '^#' | sed 's/\r$//' || true)
if [ -n "$all_params" ]; then
while read -r line; do
[ -z "$line" ] && continue
if [[ "$line" =~ ^PORT_ ]]; then
val=$(echo "$line" | cut -d= -f2-)
echo " -p ${val} \\"
elif [[ "$line" =~ ^ENV_ ]]; then
key=$(echo "$line" | cut -d= -f1); clean_key=${key#ENV_}; val=$(echo "$line" | cut -d= -f2-)
echo " -e ${clean_key}=${val} \\"
elif [[ "$line" =~ ^VOL_ ]]; then
val=$(echo "$line" | cut -d= -f2-)
echo " -v ${val} \\"
fi
done <<< "$all_params"
fi
echo " $FULL_URL:$BUILD_TAG"
} > docker_run_block.txt
DOCKERHUB_LINK_CONTENT=""
if [[ "${{ steps.prep.outputs.push_targets }}" == *"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
# Template-Verarbeitung via Python (verhindert awk-Abstürze bei Zeilenumbrüchen)
process_template() {
local template=$1; local output=$2
[ ! -f "$template" ] && return
cp "$template" "$output"
export REPO_PURE FULL_URL BUILD_TAG BASE_IMAGE ARM_STATUS CURRENT_TIME \
HISTORY_CONTENT DOCKERHUB_LINK_CONTENT ENV_BLOCK_CONTENT \
PORTS_BLOCK_CONTENT VOL_BLOCK_CONTENT DESCRIPTION
python3 -c '
import os, sys
output = sys.argv[1]
with open(output, "r", encoding="utf-8") as f:
content = f.read()
content = content.replace("__REPO_NAME__", os.environ.get("REPO_PURE", ""))
content = content.replace("__FULL_URL__", os.environ.get("FULL_URL", ""))
content = content.replace("__BUILD_TAG__", os.environ.get("BUILD_TAG", ""))
content = content.replace("__BASE_IMAGE__", os.environ.get("BASE_IMAGE", ""))
content = content.replace("__ARM_STATUS__", os.environ.get("ARM_STATUS", ""))
content = content.replace("__CURRENT_DATE__", os.environ.get("CURRENT_TIME", ""))
content = content.replace("__HISTORY_CONTENT__", os.environ.get("HISTORY_CONTENT", ""))
content = content.replace("__DOCKERHUB_LINK__", os.environ.get("DOCKERHUB_LINK_CONTENT", ""))
content = content.replace("__ENV_BLOCK__", os.environ.get("ENV_BLOCK_CONTENT", "").replace("\\n", "\n"))
content = content.replace("__PORTS_BLOCK__", os.environ.get("PORTS_BLOCK_CONTENT", "").replace("\\n", "\n"))
content = content.replace("__VOL_BLOCK__", os.environ.get("VOL_BLOCK_CONTENT", "").replace("\\n", "\n"))
content = content.replace("__DESCRIPTION__", os.environ.get("DESCRIPTION", "Keine Beschreibung."))
if "__DOCKER_RUN__" in content and os.path.exists("docker_run_block.txt"):
with open("docker_run_block.txt", "r", encoding="utf-8") as f:
content = content.replace("__DOCKER_RUN__", f.read())
if "__COMPOSE_BLOCK__" in content and os.path.exists("docker-compose.yml"):
with open("docker-compose.yml", "r", encoding="utf-8") as f:
content = content.replace("__COMPOSE_BLOCK__", f.read())
with open(output, "w", encoding="utf-8") as f:
f.write(content)
' "$output"
}
process_template "docker-compose.template" "docker-compose.yml"
process_template "README.template" "README.md"
echo "FINAL_MSG=$COMMIT_MSG" >> $GITHUB_ENV
echo "DESCRIPTION<<EOF" >> $GITHUB_ENV
echo -e "$DESCRIPTION" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
- name: Push README to Docker Hub
if: steps.check_changes.outputs.should_build == 'true' && contains(steps.prep.outputs.push_targets, 'dockerhub')
run: |
if ! command -v jq &> /dev/null; then
apt-get update && apt-get install -y jq || apk add --no-cache jq || true
fi
echo "🚀 Starting update README on Docker Hub..."
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" ] || [ -z "$TOKEN" ]; then
echo "❌ Error: No login-token available. Check your credentials!"
exit 1
fi
REPO_PURE="${{ steps.prep.outputs.repo_pure }}"
DH_USER="${{ secrets.DOCKERHUB_USERNAME }}"
SHORT_DESC=$(echo -e "${{ env.DESCRIPTION }}" | head -n 1 | cut -c 1-100)
[ -z "$SHORT_DESC" ] && SHORT_DESC="Docker Image for $REPO_PURE"
echo "📦 Processing README.md (${REPO_PURE})..."
jq -n \
--arg desc "$SHORT_DESC" \
--rawfile full_desc README.md \
'{description: $desc, full_description: $full_desc}' > payload.json
echo "📤 Sending data to Docker Hub API..."
RESPONSE=$(curl -s -w "\n%{http_code}" -X PATCH \
"https://hub.docker.com/v2/repositories/${DH_USER}/${REPO_PURE}/" \
-H "Authorization: JWT ${TOKEN}" \
-H "Content-Type: application/json" \
-d @payload.json)
HTTP_STATUS=$(echo "$RESPONSE" | tail -n 1)
BODY=$(echo "$RESPONSE" | head -n -1)
if [ "$HTTP_STATUS" -eq 200 ]; then
echo "✅ README update successfull (HTTP 200)."
else
echo "❌ Error on update! HTTP Status: $HTTP_STATUS"
echo "Answer from Docker Hub: $BODY"
exit 1
fi
- 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]"
TARGET_TAG="${{ steps.prep.outputs.docker_tag }}"
git tag -f "$TARGET_TAG"
if [[ "${{ gitea.ref }}" == refs/tags/* ]]; then
echo "🏷️ Build aus Tag getriggert: $TARGET_TAG"
git push -f origin "$TARGET_TAG"
else
echo "🌿 Build aus Branch/Schedule getriggert: ${{ gitea.ref_name }}"
git push origin HEAD:${{ gitea.ref_name }}
git push -f origin "$TARGET_TAG"
fi
- name: Cleanup Temporary Tags on Docker Hub
if: steps.check_changes.outputs.should_build == 'true' && contains(steps.prep.outputs.push_targets, 'dockerhub')
run: |
if ! command -v jq &> /dev/null; then
(apt-get update && apt-get install -y jq) || (apk add --no-cache jq) || true
fi
echo "🧹 Cleanup temp. Docker Hub tags..."
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 || echo "")
if [ "$TOKEN" != "null" ] && [ -n "$TOKEN" ]; then
REPO_PURE=${{ steps.prep.outputs.repo_pure }}
DH_USER="${{ secrets.DOCKERHUB_USERNAME }}"
# Nur existierende Tags löschen
CLEAN_TAGS=("tmp-amd64")
if [ -f "Dockerfile.aarch64" ]; then
CLEAN_TAGS+=("tmp-arm64")
fi
for t in "${CLEAN_TAGS[@]}"; do
echo "Deleting tag $t..."
STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X DELETE \
"https://hub.docker.com/v2/repositories/${DH_USER}/${REPO_PURE}/tags/$t/" \
-H "Authorization: JWT ${TOKEN}" || echo "000")
echo "Status: $STATUS"
done
echo "✅ Temp. tags deleted."
fi
- name: Cleanup Temporary Registry Tags (Gitea)
if: steps.check_changes.outputs.should_build == 'true'
run: |
TOKEN="${{ secrets.RUNNER_TOKEN }}"
ORG_NAME=$(echo "${{ gitea.repository }}" | cut -d'/' -f1)
REPO_NAME=$(echo "${{ gitea.repository }}" | cut -d'/' -f2)
CLEAN_TAGS=("tmp-amd64")
if [ -f "Dockerfile.aarch64" ]; then
CLEAN_TAGS+=("tmp-arm64")
fi
for t in "${CLEAN_TAGS[@]}"; do
curl -s -X DELETE "https://git.pi-farm.de/api/v1/packages/$ORG_NAME/container/$REPO_NAME/$t" -H "Authorization: token $TOKEN" || true
curl -s -X DELETE "https://git.pi-farm.de/api/v1/packages/${ORG_NAME,,}/container/${REPO_NAME,,}/$t" -H "Authorization: token $TOKEN" || true
done
- 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 }}"