Files
plex-playlist/.gitea/workflows/docker-build-main.yaml
copilotcoder 2d7e360eac
All checks were successful
CICD Start / Sanity and Base Decision (pull_request) Successful in 1m1s
More CICD fixes again again again again again again again again again again again again again again again again again again again again again again again again again again
Signed-off-by: copilotcoder <copilotcoder@darkhelm.org>
2026-06-10 09:52:02 -04:00

346 lines
13 KiB
YAML

name: Docker Build Main
on:
workflow_dispatch:
inputs:
head_sha:
description: Commit SHA to process
required: false
base_hash:
description: Immutable base hash to use for CICD base image
required: false
source_workflow:
description: Upstream workflow name
required: false
base_needed:
description: Whether base rebuild was required
required: false
trace_id:
description: Correlation id propagated across CICD dispatch chain
required: false
env:
GITEA_SSH_HOST: kankali.darkhelm.lan
GITEA_SSH_PORT: "2222"
GITEA_REPO_SSH_URL: ssh://git@kankali.darkhelm.lan:2222/DarkHelm.org/plex-playlist.git
GITEA_REGISTRY: kankali.darkhelm.lan:3001
GITEA_REGISTRY_IP: 10.18.75.2
GITEA_REGISTRY_HOST: kankali.darkhelm.lan
defaults:
run:
shell: bash
concurrency:
group: main-build-${{ github.sha }}
cancel-in-progress: true
jobs:
startup-audit:
name: Main Workflow Startup Audit
# Guaranteed early logs on a lightweight runner, even if heavy runner setup stalls.
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Emit startup diagnostics
shell: sh
env:
EVENT_NAME: ${{ github.event_name }}
SOURCE_WORKFLOW: ${{ github.event.inputs.source_workflow }}
BASE_NEEDED: ${{ github.event.inputs.base_needed }}
HEAD_SHA_INPUT: ${{ github.event.inputs.head_sha }}
HEAD_SHA_FALLBACK: ${{ github.sha }}
BASE_HASH_INPUT: ${{ github.event.inputs.base_hash }}
REF: ${{ github.ref }}
REF_NAME: ${{ github.ref_name }}
HEAD_REF: ${{ github.head_ref }}
TRACE_ID_INPUT: ${{ github.event.inputs.trace_id }}
TARGET_LABEL: ubuntu-act-8gb
run: |
RESOLVED_HEAD_SHA="${HEAD_SHA_INPUT:-${HEAD_SHA_FALLBACK}}"
TRACE_SUFFIX="$(printf '%s' "${RESOLVED_HEAD_SHA}" | cut -c1-8)"
TRACE_ID="${TRACE_ID_INPUT:-cicd-main-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-${TRACE_SUFFIX}}"
echo "=== Main Workflow Startup Audit ==="
echo "event_name=${EVENT_NAME}"
echo "source_workflow=${SOURCE_WORKFLOW}"
echo "base_needed=${BASE_NEEDED}"
echo "head_sha_input=${HEAD_SHA_INPUT}"
echo "head_sha=${RESOLVED_HEAD_SHA}"
echo "base_hash_input=${BASE_HASH_INPUT}"
echo "ref=${REF}"
echo "ref_name=${REF_NAME}"
echo "head_ref=${HEAD_REF}"
echo "trace_id=${TRACE_ID}"
echo "target_runner_label=${TARGET_LABEL}"
echo "timestamp_utc=$(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo "startup_audit=ok"
- name: Check control-plane reachability
shell: sh
run: |
SERVER_URL="${GITHUB_SERVER_URL:-}"
if command -v curl >/dev/null 2>&1; then
if [ -n "${SERVER_URL}" ] && curl -fsS --connect-timeout 5 --max-time 10 "${SERVER_URL%/}/api/v1/version" >/tmp/gitea-version.json 2>/dev/null; then
echo "gitea_api_reachable=true"
cat /tmp/gitea-version.json || true
else
echo "gitea_api_reachable=false"
fi
else
echo "curl unavailable; skipping reachability check"
fi
build:
name: Build and Push CICD Complete Image
# Heavy Docker build: schedule only on 8GB Pi runners.
runs-on: ubuntu-act-8gb
needs: startup-audit
timeout-minutes: 60
outputs:
head_sha: ${{ steps.meta.outputs.head_sha }}
steps:
- name: Audit trigger context
env:
EVENT_NAME: ${{ github.event_name }}
SOURCE_WORKFLOW: ${{ github.event.inputs.source_workflow }}
BASE_NEEDED: ${{ github.event.inputs.base_needed }}
HEAD_SHA_INPUT: ${{ github.event.inputs.head_sha }}
HEAD_SHA_FALLBACK: ${{ github.sha }}
BASE_HASH_INPUT: ${{ github.event.inputs.base_hash }}
REF: ${{ github.ref }}
REF_NAME: ${{ github.ref_name }}
HEAD_REF: ${{ github.head_ref }}
TRACE_ID_INPUT: ${{ github.event.inputs.trace_id }}
run: |
RESOLVED_HEAD_SHA="${HEAD_SHA_INPUT:-${HEAD_SHA_FALLBACK}}"
TRACE_ID="${TRACE_ID_INPUT:-cicd-main-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-${RESOLVED_HEAD_SHA:0:8}}"
echo "=== Dispatch Audit: CICD Main Build ==="
echo "event_name=${EVENT_NAME}"
echo "source_workflow=${SOURCE_WORKFLOW}"
echo "base_needed=${BASE_NEEDED}"
echo "head_sha_input=${HEAD_SHA_INPUT}"
echo "head_sha=${RESOLVED_HEAD_SHA}"
echo "base_hash_input=${BASE_HASH_INPUT}"
echo "ref=${REF}"
echo "ref_name=${REF_NAME}"
echo "head_ref=${HEAD_REF}"
echo "trace_id=${TRACE_ID}"
- name: Resolve head SHA
id: meta
env:
HEAD_SHA_INPUT: ${{ github.event.inputs.head_sha }}
HEAD_SHA_FALLBACK: ${{ github.sha }}
run: |
RESOLVED_HEAD_SHA="${HEAD_SHA_INPUT:-${HEAD_SHA_FALLBACK}}"
echo "head_sha=${RESOLVED_HEAD_SHA}" >> "$GITHUB_OUTPUT"
- name: Minimal checkout for build inputs
env:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
HEAD_SHA: ${{ steps.meta.outputs.head_sha }}
run: |
set -e
umask 077
trap 'rm -f ~/.ssh/id_rsa' EXIT
if ! grep -q "${GITEA_REGISTRY_HOST}" /etc/hosts; then
echo "${GITEA_REGISTRY_IP} ${GITEA_REGISTRY_HOST}" >> /etc/hosts
fi
mkdir -p ~/.ssh
echo "${SSH_PRIVATE_KEY}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -p "${GITEA_SSH_PORT}" "${GITEA_SSH_HOST}" >> ~/.ssh/known_hosts 2>/dev/null
GIT_SSH_COMMAND="ssh -i ~/.ssh/id_rsa -o IdentitiesOnly=yes -o StrictHostKeyChecking=no" \
git clone --depth 1 --no-checkout "${GITEA_REPO_SSH_URL}" .
GIT_SSH_COMMAND="ssh -i ~/.ssh/id_rsa -o IdentitiesOnly=yes -o StrictHostKeyChecking=no" \
git fetch --depth 1 origin "${HEAD_SHA}" >/dev/null 2>&1 || true
git checkout FETCH_HEAD -- Dockerfile.cicd Dockerfile.cicd-base .dockerignore scripts/compute-cicd-base-hash.sh
chmod +x scripts/compute-cicd-base-hash.sh
- name: Build and push complete CICD image
env:
PACKAGE_ACCESS_TOKEN: ${{ secrets.PACKAGE_ACCESS_TOKEN }}
REGISTRY_USER: ${{ github.actor }}
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
HEAD_SHA: ${{ steps.meta.outputs.head_sha }}
BASE_HASH_INPUT: ${{ github.event.inputs.base_hash }}
run: |
set -e
umask 077
trap 'rm -f /tmp/ssh_key' EXIT
if ! grep -q "${GITEA_REGISTRY_HOST}" /etc/hosts; then
echo "${GITEA_REGISTRY_IP} ${GITEA_REGISTRY_HOST}" >> /etc/hosts
fi
echo "${PACKAGE_ACCESS_TOKEN}" | docker login "http://${GITEA_REGISTRY}" -u "${REGISTRY_USER}" --password-stdin
if [ -n "${BASE_HASH_INPUT}" ]; then
BASE_HASH="${BASE_HASH_INPUT}"
echo "Using provided immutable base hash from upstream workflow dispatch: ${BASE_HASH}"
else
BASE_HASH=$(./scripts/compute-cicd-base-hash.sh)
echo "No base_hash input provided; computed base hash locally: ${BASE_HASH}"
fi
BASE_IMAGE="${GITEA_REGISTRY}/darkhelm.org/plex-playlist-cicd-base:${BASE_HASH}"
verify_base_image() {
# Fast path: base image already present locally.
if docker image inspect "${BASE_IMAGE}" >/dev/null 2>&1; then
return 0
fi
# Manifest inspect unreliable on these runners; use pull as truth.
timeout 1800 docker pull "${BASE_IMAGE}" >/tmp/base-image-pull.log 2>&1
}
max_attempts=2
sleep_seconds=15
for i in $(seq 1 "${max_attempts}"); do
echo "Base availability check ${i}/${max_attempts}..."
if verify_base_image; then
echo "✓ Base image available and hash matched: ${BASE_IMAGE}"
break
else
pull_exit=$?
echo "Base pull attempt ${i} failed with exit code ${pull_exit}"
if [ "${pull_exit}" -eq 124 ]; then
echo "Pull timed out after 1800s while downloading base image"
fi
if [ -s /tmp/base-image-pull.log ]; then
tail -n 40 /tmp/base-image-pull.log || true
fi
if [ "${i}" -eq "${max_attempts}" ]; then
echo "❌ Required immutable base image is not available or mismatched: ${BASE_IMAGE}"
exit 1
fi
sleep "${sleep_seconds}"
fi
done
echo "✓ Base image ready: ${BASE_IMAGE}"
echo "${SSH_PRIVATE_KEY}" > /tmp/ssh_key
chmod 600 /tmp/ssh_key
export DOCKER_BUILDKIT=1
docker build -f Dockerfile.cicd \
--secret id=ssh_private_key,src=/tmp/ssh_key \
--add-host "${GITEA_SSH_HOST}:${GITEA_REGISTRY_IP}" \
--build-arg GITHUB_SHA="${HEAD_SHA}" \
--build-arg CICD_BASE_IMAGE="${BASE_IMAGE}" \
-t cicd:latest .
docker tag cicd:latest "${GITEA_REGISTRY}/darkhelm.org/plex-playlist-cicd:latest"
docker tag cicd:latest "${GITEA_REGISTRY}/darkhelm.org/plex-playlist-cicd:${HEAD_SHA}"
docker push "${GITEA_REGISTRY}/darkhelm.org/plex-playlist-cicd:latest"
docker push "${GITEA_REGISTRY}/darkhelm.org/plex-playlist-cicd:${HEAD_SHA}"
dispatch-checks:
name: Dispatch CICD Checks
runs-on: ubuntu-act
timeout-minutes: 15
needs: build
if: needs.build.result == 'success'
steps:
- name: Dispatch checks workflow
env:
ACTIONS_TRIGGER_TOKEN: ${{ secrets.ACTIONS_TRIGGER_TOKEN }}
PACKAGE_ACCESS_TOKEN: ${{ secrets.PACKAGE_ACCESS_TOKEN }}
HEAD_SHA: ${{ needs.build.outputs.head_sha }}
REPO_FULL: ${{ github.repository }}
HEAD_REF: ${{ github.head_ref }}
REF_NAME: ${{ github.ref_name }}
TRACE_ID_INPUT: ${{ github.event.inputs.trace_id }}
run: |
set -e
DISPATCH_TOKEN="${ACTIONS_TRIGGER_TOKEN:-${PACKAGE_ACCESS_TOKEN:-}}"
if [ -z "${DISPATCH_TOKEN}" ]; then
echo "❌ Missing dispatch token. Set ACTIONS_TRIGGER_TOKEN (repo write scope) or ensure PACKAGE_ACCESS_TOKEN has Actions workflow-dispatch permissions."
exit 1
fi
if ! grep -q "${GITEA_REGISTRY_HOST}" /etc/hosts; then
echo "${GITEA_REGISTRY_IP} ${GITEA_REGISTRY_HOST}" >> /etc/hosts
fi
REPO_OWNER="${REPO_FULL%/*}"
REPO_NAME="${REPO_FULL#*/}"
TARGET_REF="${HEAD_REF:-${REF_NAME}}"
TRACE_ID="${TRACE_ID_INPUT:-cicd-main-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-${HEAD_SHA:0:8}}"
echo "trace_id=${TRACE_ID}"
echo "target_ref=${TARGET_REF}"
CANDIDATE_API_BASES=()
if [ -n "${GITHUB_SERVER_URL:-}" ]; then
CANDIDATE_API_BASES+=("${GITHUB_SERVER_URL%/}/api/v1")
fi
CANDIDATE_API_BASES+=("http://${GITEA_REGISTRY_IP}:3001/api/v1")
CANDIDATE_API_BASES+=("http://${GITEA_REGISTRY_HOST}:3001/api/v1")
ensure_curl() {
if command -v curl >/dev/null 2>&1; then
return 0
fi
if command -v apt-get >/dev/null 2>&1; then
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq curl ca-certificates
fi
command -v curl >/dev/null 2>&1
}
ensure_curl || { echo "❌ curl unavailable for dispatch"; exit 1; }
HELPER_PATH="/tmp/dispatch-workflow.sh"
fetch_dispatch_helper() {
local helper_ref="$1"
local api_base
for api_base in "${CANDIDATE_API_BASES[@]}"; do
helper_url="${api_base}/repos/${REPO_OWNER}/${REPO_NAME}/raw/scripts/dispatch-workflow.sh?ref=${helper_ref}"
if curl -fsS --connect-timeout 5 --max-time 20 \
-H "Authorization: token ${DISPATCH_TOKEN}" \
-H "User-Agent: plex-playlist-cicd-main" \
-o "${HELPER_PATH}" \
"${helper_url}"; then
chmod +x "${HELPER_PATH}"
return 0
fi
done
return 1
}
if ! fetch_dispatch_helper "${TARGET_REF}" && ! fetch_dispatch_helper "${HEAD_SHA}"; then
echo "❌ Failed to fetch scripts/dispatch-workflow.sh from repository"
exit 1
fi
DISPATCH_ARGS=(
--token "${DISPATCH_TOKEN}"
--repo "${REPO_FULL}"
--workflow "cicd-checks.yaml"
--ref "${TARGET_REF}"
--head-sha "${HEAD_SHA}"
--source-workflow "CICD Main Build"
--trace-id "${TRACE_ID}"
)
for API_BASE in "${CANDIDATE_API_BASES[@]}"; do
DISPATCH_ARGS+=(--api-base "${API_BASE}")
done
"${HELPER_PATH}" "${DISPATCH_ARGS[@]}"