Files
openrabbit/.gitea/workflows/docker.yml
latte 8cadb2d216
Some checks failed
Docker / docker (push) Successful in 6s
Security / security (push) Successful in 6s
Deploy / deploy-local-runner (push) Has been cancelled
CI / ci (push) Successful in 1m42s
Deploy / deploy-ssh (push) Successful in 7s
Add Gitea Actions workflows, CI config, and docs
2026-02-28 20:40:14 +01:00

285 lines
11 KiB
YAML

# =============================================================================
# Docker Workflow — Build & Push to Gitea Container Registry
# =============================================================================
#
# PURPOSE:
# Build Docker images generically; optionally push to the Gitea Container
# Registry at git.hiddenden.cafe.
#
# TRIGGERS:
# - pull_request → build only (never push)
# - push to main → build; push only if ENABLE_DOCKER=true AND DOCKER_PUSH=true
# - tag v* → build; push only if DOCKER_PUSH=true AND DOCKER_PUSH_ON_TAG=true
#
# DETECTION:
# - Dockerfile exists → docker build
# - docker-compose.yml exists → docker compose build
# - Neither → exit 0 gracefully
#
# NAMING (Gitea convention):
# Image ref: ${REGISTRY_HOST}/${IMAGE_OWNER}/${IMAGE_NAME}:${TAG}
# Example: git.hiddenden.cafe/myorg/myrepo:1.2.3
#
# AUTHENTICATION:
# Uses PAT-based secrets (recommended for Gitea Actions):
# - REGISTRY_USERNAME — your Gitea username or bot account
# - REGISTRY_TOKEN — a Personal Access Token with package:write scope
# Set these in: Repository Settings → Secrets (or Organization Secrets).
# NEVER echo secrets in logs.
#
# CONFIG:
# All settings loaded from .ci/config.env. See docs/DOCKER.md.
#
# =============================================================================
name: Docker
on:
push:
branches:
- main
tags:
- "v*"
pull_request:
jobs:
docker:
runs-on: ubuntu-latest
steps:
# -----------------------------------------------------------------------
# Step 1: Checkout
# -----------------------------------------------------------------------
- name: Checkout
uses: actions/checkout@v4
# -----------------------------------------------------------------------
# Step 2: Load configuration
# -----------------------------------------------------------------------
- name: Load config
run: |
if [ -f .ci/config.env ]; then
set -a
source .ci/config.env
set +a
echo "Config loaded from .ci/config.env"
else
echo "WARNING: .ci/config.env not found, using defaults"
fi
# Export with defaults
echo "ENABLE_DOCKER=${ENABLE_DOCKER:-true}" >> "$GITHUB_ENV"
echo "DOCKER_PUSH=${DOCKER_PUSH:-false}" >> "$GITHUB_ENV"
echo "DOCKER_PUSH_ON_BRANCH=${DOCKER_PUSH_ON_BRANCH:-true}" >> "$GITHUB_ENV"
echo "DOCKER_PUSH_ON_TAG=${DOCKER_PUSH_ON_TAG:-true}" >> "$GITHUB_ENV"
echo "REGISTRY_HOST=${REGISTRY_HOST:-git.hiddenden.cafe}" >> "$GITHUB_ENV"
echo "IMAGE_OWNER_CFG=${IMAGE_OWNER:-auto}" >> "$GITHUB_ENV"
echo "IMAGE_NAME_CFG=${IMAGE_NAME:-auto}" >> "$GITHUB_ENV"
echo "DOCKER_TAG_STRATEGY=${DOCKER_TAG_STRATEGY:-semver+latest}" >> "$GITHUB_ENV"
echo "DEFAULT_BRANCH=${DEFAULT_BRANCH:-main}" >> "$GITHUB_ENV"
# -----------------------------------------------------------------------
# Step 3: Check if Docker is enabled
# -----------------------------------------------------------------------
- name: Check if Docker is enabled
run: |
if [ "$ENABLE_DOCKER" != "true" ]; then
echo "Docker is disabled (ENABLE_DOCKER=$ENABLE_DOCKER). Exiting."
# We still exit 0 — graceful skip.
echo "SKIP_DOCKER=true" >> "$GITHUB_ENV"
fi
# -----------------------------------------------------------------------
# Step 4: Detect Dockerfile or docker-compose.yml
# -----------------------------------------------------------------------
- name: Detect Docker files
if: env.SKIP_DOCKER != 'true'
run: |
if [ -f Dockerfile ]; then
echo "DOCKER_MODE=dockerfile" >> "$GITHUB_ENV"
echo "Detected: Dockerfile"
elif [ -f docker-compose.yml ] || [ -f docker-compose.yaml ]; then
echo "DOCKER_MODE=compose" >> "$GITHUB_ENV"
echo "Detected: docker-compose.yml"
else
echo "No Dockerfile or docker-compose.yml found. Skipping."
echo "SKIP_DOCKER=true" >> "$GITHUB_ENV"
fi
# -----------------------------------------------------------------------
# Step 5: Derive image owner and name dynamically
#
# Logic:
# FULL_REPO is derived from (in priority order):
# 1. $GITEA_REPOSITORY (Gitea native env var)
# 2. github.repository (Gitea Actions compatibility fallback)
# Format: "owner/repo"
#
# If IMAGE_OWNER=auto → use the owner part
# If IMAGE_NAME=auto → use the repo part
# Otherwise, use the explicit config values.
# -----------------------------------------------------------------------
- name: Derive image naming
if: env.SKIP_DOCKER != 'true'
run: |
# Determine FULL_REPO (owner/repo)
# Gitea Actions sets GITEA_REPOSITORY natively in some versions.
# It also maps github.repository for compatibility.
FULL_REPO="${GITEA_REPOSITORY:-${{ github.repository }}}"
if [ -z "$FULL_REPO" ]; then
echo "ERROR: Could not determine repository (GITEA_REPOSITORY and github.repository are both empty)"
exit 1
fi
# Split into OWNER and REPO
OWNER="$(echo "$FULL_REPO" | cut -d'/' -f1)"
REPO="$(echo "$FULL_REPO" | cut -d'/' -f2)"
echo "Derived FULL_REPO=$FULL_REPO OWNER=$OWNER REPO=$REPO"
# Apply config overrides
if [ "$IMAGE_OWNER_CFG" = "auto" ]; then
FINAL_OWNER="$OWNER"
else
FINAL_OWNER="$IMAGE_OWNER_CFG"
fi
if [ "$IMAGE_NAME_CFG" = "auto" ]; then
FINAL_NAME="$REPO"
else
FINAL_NAME="$IMAGE_NAME_CFG"
fi
# Construct the full image reference (without tag)
IMAGE_REF="${REGISTRY_HOST}/${FINAL_OWNER}/${FINAL_NAME}"
echo "Image reference: ${IMAGE_REF}:<tag>"
echo "IMAGE_REF=${IMAGE_REF}" >> "$GITHUB_ENV"
# -----------------------------------------------------------------------
# Step 6: Determine tags based on trigger and strategy
#
# Tag rules:
# - PR: build only, tag = "pr-<number>" (local only)
# - Push to branch: tag = branch name (e.g., "main")
# - Tag v1.2.3: tag = "1.2.3"; also "latest" if strategy includes it
# -----------------------------------------------------------------------
- name: Determine tags
if: env.SKIP_DOCKER != 'true'
run: |
TAGS=""
SHOULD_PUSH=false
REF="${GITHUB_REF:-}"
echo "Event: ${{ github.event_name }}"
echo "Ref: ${REF}"
# --- Pull Request: build only ---
if [ "${{ github.event_name }}" = "pull_request" ]; then
TAGS="${IMAGE_REF}:pr-${{ github.event.number }}"
SHOULD_PUSH=false
echo "PR build — will NOT push"
# --- Tag push (v*) ---
elif echo "${REF}" | grep -qE '^refs/tags/v'; then
# Extract version: refs/tags/v1.2.3 → 1.2.3
VERSION="$(echo "${REF}" | sed 's|refs/tags/v||')"
TAGS="${IMAGE_REF}:${VERSION}"
# Optionally add :latest
if echo "$DOCKER_TAG_STRATEGY" | grep -qi 'latest'; then
TAGS="${TAGS},${IMAGE_REF}:latest"
fi
if [ "$DOCKER_PUSH" = "true" ] && [ "$DOCKER_PUSH_ON_TAG" = "true" ]; then
SHOULD_PUSH=true
fi
echo "Tag push — version=${VERSION}, push=${SHOULD_PUSH}"
# --- Branch push ---
elif echo "${REF}" | grep -q '^refs/heads/'; then
BRANCH="$(echo "${REF}" | sed 's|refs/heads/||')"
TAGS="${IMAGE_REF}:${BRANCH}"
if [ "$DOCKER_PUSH" = "true" ] && [ "$DOCKER_PUSH_ON_BRANCH" = "true" ]; then
SHOULD_PUSH=true
fi
echo "Branch push — branch=${BRANCH}, push=${SHOULD_PUSH}"
else
echo "Unknown ref type: ${REF}. Building with tag 'dev'."
TAGS="${IMAGE_REF}:dev"
SHOULD_PUSH=false
fi
echo "DOCKER_TAGS=${TAGS}" >> "$GITHUB_ENV"
echo "SHOULD_PUSH=${SHOULD_PUSH}" >> "$GITHUB_ENV"
echo "Final tags: ${TAGS}"
echo "Will push: ${SHOULD_PUSH}"
# -----------------------------------------------------------------------
# Step 7: Build the Docker image
# -----------------------------------------------------------------------
- name: Build Docker image
if: env.SKIP_DOCKER != 'true'
run: |
if [ "$DOCKER_MODE" = "dockerfile" ]; then
# Build with the first tag; additional tags are added after
PRIMARY_TAG="$(echo "$DOCKER_TAGS" | cut -d',' -f1)"
echo ">>> docker build -t ${PRIMARY_TAG} ."
docker build -t "${PRIMARY_TAG}" .
# Tag additional images if present
IFS=',' read -ra TAG_ARRAY <<< "$DOCKER_TAGS"
for tag in "${TAG_ARRAY[@]:1}"; do
echo ">>> docker tag ${PRIMARY_TAG} ${tag}"
docker tag "${PRIMARY_TAG}" "${tag}"
done
elif [ "$DOCKER_MODE" = "compose" ]; then
echo ">>> docker compose build"
docker compose build
fi
# -----------------------------------------------------------------------
# Step 8: Login to registry (only when pushing)
#
# Uses PAT-based auth. Requires secrets:
# REGISTRY_USERNAME — Gitea username or bot
# REGISTRY_TOKEN — PAT with package:write scope
# -----------------------------------------------------------------------
- name: Login to container registry
if: env.SKIP_DOCKER != 'true' && env.SHOULD_PUSH == 'true'
run: |
echo "Logging in to ${REGISTRY_HOST}..."
# Use --password-stdin to avoid leaking the token in process list
echo "${{ secrets.REGISTRY_TOKEN }}" | \
docker login "${REGISTRY_HOST}" \
-u "${{ secrets.REGISTRY_USERNAME }}" \
--password-stdin
# -----------------------------------------------------------------------
# Step 9: Push Docker image(s)
# -----------------------------------------------------------------------
- name: Push Docker image
if: env.SKIP_DOCKER != 'true' && env.SHOULD_PUSH == 'true'
run: |
IFS=',' read -ra TAG_ARRAY <<< "$DOCKER_TAGS"
for tag in "${TAG_ARRAY[@]}"; do
echo ">>> docker push ${tag}"
docker push "${tag}"
done
# -----------------------------------------------------------------------
# Step 10: Summary
# -----------------------------------------------------------------------
- name: Docker Summary
if: always()
run: |
echo "=============================="
echo " Docker Workflow Complete"
echo " Mode: ${DOCKER_MODE:-skipped}"
echo " Tags: ${DOCKER_TAGS:-none}"
echo " Pushed: ${SHOULD_PUSH:-false}"
echo "=============================="