ci: keep gitea workflows only, harden and speed up pipelines
Drop duplicated .github/workflows (helm blocks ported to .gitea deploy first). Add ci concurrency with cancel on branches, pin checkout to SHA and prettier to 3.9.8, registry layer cache for image builds. renovate-run gains config validation and dry-run input.
This commit is contained in:
1 parent
7f0bd5f609
commit
ab386fc436
6 files changed
+76
-561
No files matched your search
+33
-13
@@ -7,6 +7,11 @@ on:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
# Fast pushes on a branch cancel superseded runs; main is never cancelled.
|
||||
concurrency:
|
||||
group: ci-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||
|
||||
env:
|
||||
REGISTRY: gcr.forust.xyz
|
||||
|
||||
@@ -15,7 +20,7 @@ jobs:
|
||||
runs-on: [self-hosted, linux, arch, homelab]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||||
|
||||
- name: Check formatting with Prettier
|
||||
shell: bash
|
||||
@@ -35,13 +40,13 @@ jobs:
|
||||
-v "$PWD:/work" \
|
||||
-w /work \
|
||||
node:22-alpine \
|
||||
sh -lc 'npx --yes prettier@3 --check --ignore-unknown "$@"' sh "${prettier_files[@]}"
|
||||
sh -lc 'npx --yes prettier@3.9.8 --check --ignore-unknown "$@"' sh "${prettier_files[@]}"
|
||||
|
||||
lint-ruff:
|
||||
runs-on: [self-hosted, linux, arch, homelab]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||||
|
||||
- name: Lint Python with Ruff
|
||||
shell: bash
|
||||
@@ -56,7 +61,7 @@ jobs:
|
||||
runs-on: [self-hosted, linux, arch, homelab]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||||
|
||||
- name: Lint YAML syntax
|
||||
shell: bash
|
||||
@@ -82,7 +87,7 @@ jobs:
|
||||
runs-on: [self-hosted, linux, arch, homelab]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||||
|
||||
- name: Lint Dockerfiles
|
||||
shell: bash
|
||||
@@ -107,7 +112,7 @@ jobs:
|
||||
runs-on: [self-hosted, linux, arch, homelab]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||||
|
||||
- name: Validate Kubernetes manifests
|
||||
shell: bash
|
||||
@@ -139,7 +144,7 @@ jobs:
|
||||
services: ${{ steps.services.outputs.services }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -230,7 +235,10 @@ jobs:
|
||||
for tag in "${tags[@]}"; do
|
||||
build_args+=(-t "${image}:${tag}")
|
||||
done
|
||||
docker build "${build_args[@]}" dtek_notif
|
||||
docker build \
|
||||
--cache-from "type=registry,ref=${image}:buildcache" \
|
||||
--cache-to "type=registry,ref=${image}:buildcache,mode=max" \
|
||||
"${build_args[@]}" dtek_notif
|
||||
for tag in "${tags[@]}"; do
|
||||
docker push "${image}:${tag}"
|
||||
done
|
||||
@@ -250,7 +258,10 @@ jobs:
|
||||
for tag in "${tags[@]}"; do
|
||||
build_args+=(-t "${image}:${tag}")
|
||||
done
|
||||
docker build "${build_args[@]}" errorpages
|
||||
docker build \
|
||||
--cache-from "type=registry,ref=${image}:buildcache" \
|
||||
--cache-to "type=registry,ref=${image}:buildcache,mode=max" \
|
||||
"${build_args[@]}" errorpages
|
||||
for tag in "${tags[@]}"; do
|
||||
docker push "${image}:${tag}"
|
||||
done
|
||||
@@ -280,7 +291,10 @@ jobs:
|
||||
for tag in "${tags[@]}"; do
|
||||
build_args+=(-t "${image}:${tag}")
|
||||
done
|
||||
docker build "${build_args[@]}" "$context"
|
||||
docker build \
|
||||
--cache-from "type=registry,ref=${image}:buildcache" \
|
||||
--cache-to "type=registry,ref=${image}:buildcache,mode=max" \
|
||||
"${build_args[@]}" "$context"
|
||||
for tag in "${tags[@]}"; do
|
||||
docker push "${image}:${tag}"
|
||||
done
|
||||
@@ -309,7 +323,10 @@ jobs:
|
||||
for tag in "${tags[@]}"; do
|
||||
build_args+=(-t "${image}:${tag}")
|
||||
done
|
||||
docker build "${build_args[@]}" -f "homepages/Dockerfile.${service}" homepages
|
||||
docker build \
|
||||
--cache-from "type=registry,ref=${image}:buildcache" \
|
||||
--cache-to "type=registry,ref=${image}:buildcache,mode=max" \
|
||||
"${build_args[@]}" -f "homepages/Dockerfile.${service}" homepages
|
||||
for tag in "${tags[@]}"; do
|
||||
docker push "${image}:${tag}"
|
||||
done
|
||||
@@ -340,7 +357,10 @@ jobs:
|
||||
for tag in "${tags[@]}"; do
|
||||
build_args+=(-t "${image}:${tag}")
|
||||
done
|
||||
docker build "${build_args[@]}" "$context"
|
||||
docker build \
|
||||
--cache-from "type=registry,ref=${image}:buildcache" \
|
||||
--cache-to "type=registry,ref=${image}:buildcache,mode=max" \
|
||||
"${build_args[@]}" "$context"
|
||||
for tag in "${tags[@]}"; do
|
||||
docker push "${image}:${tag}"
|
||||
done
|
||||
@@ -355,7 +375,7 @@ jobs:
|
||||
runs-on: [self-hosted, linux, arch, homelab, prod]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||||
|
||||
- name: Apply and roll out userbot panel
|
||||
shell: bash
|
||||
|
||||
@@ -130,6 +130,30 @@ jobs:
|
||||
echo " namespaces first: ${ns_files[*]}"
|
||||
kubectl apply -f "${ns_files[@]}"
|
||||
fi
|
||||
if [ -f "$repo/prometheus-stack/k8s/active" ]; then
|
||||
echo "== Upgrading kube-prometheus-stack =="
|
||||
helm upgrade --install prometheus-stack prometheus-community/kube-prometheus-stack \
|
||||
--namespace prometheus \
|
||||
--version 86.2.3 \
|
||||
--values "$repo/prometheus-stack/k8s/grafana-values.yaml" \
|
||||
--wait
|
||||
fi
|
||||
if [ -f "$repo/loki/k8s/active" ]; then
|
||||
echo "== Upgrading loki/alloy =="
|
||||
helm repo add grafana https://grafana.github.io/helm-charts >/dev/null 2>&1 || true
|
||||
helm repo update grafana >/dev/null 2>&1 || true
|
||||
helm upgrade --install loki grafana/loki \
|
||||
--version 7.3.0 \
|
||||
--namespace prometheus \
|
||||
--values "$repo/loki/k8s/loki-values.yaml" \
|
||||
--wait
|
||||
helm upgrade --install alloy grafana/alloy \
|
||||
--version 1.12.1 \
|
||||
--namespace prometheus \
|
||||
--values "$repo/loki/k8s/alloy-values.yaml" \
|
||||
--wait
|
||||
fi
|
||||
|
||||
if [ "${#other_files[@]}" -gt 0 ]; then
|
||||
echo " resources: ${other_files[*]}"
|
||||
kubectl apply "${prune_opts[@]}" -f "${other_files[@]}"
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: [self-hosted, linux, arch, homelab]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||||
|
||||
- name: Validate Renovate Compose draft
|
||||
shell: bash
|
||||
|
||||
@@ -15,6 +15,11 @@ on:
|
||||
options:
|
||||
- info
|
||||
- debug
|
||||
dry_run:
|
||||
description: "Plan only, do not open or update PRs"
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
concurrency:
|
||||
group: renovate-run
|
||||
@@ -25,7 +30,17 @@ jobs:
|
||||
runs-on: [self-hosted, linux, arch, homelab]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||||
|
||||
- name: Validate Renovate config
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
docker run --rm \
|
||||
-v "$PWD/renovate/config.js:/opt/renovate/config.js:ro" \
|
||||
-e RENOVATE_CONFIG_FILE=/opt/renovate/config.js \
|
||||
renovate/renovate:44.97.2 \
|
||||
renovate-config-validator
|
||||
|
||||
- name: Run Renovate
|
||||
shell: bash
|
||||
@@ -33,6 +48,7 @@ jobs:
|
||||
RENOVATE_TOKEN: ${{ secrets.RENOVATE_TOKEN }}
|
||||
RENOVATE_GITHUB_COM_TOKEN: ${{ secrets.RENOVATE_GITHUB_COM_TOKEN }}
|
||||
RENOVATE_REPOSITORIES: ${{ inputs.repositories }}
|
||||
RENOVATE_DRY_RUN: ${{ inputs.dry_run && 'full' || '' }}
|
||||
LOG_LEVEL: ${{ inputs.log_level }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@@ -46,6 +62,7 @@ jobs:
|
||||
-e RENOVATE_TOKEN="$RENOVATE_TOKEN" \
|
||||
-e RENOVATE_GITHUB_COM_TOKEN="${RENOVATE_GITHUB_COM_TOKEN:-}" \
|
||||
-e RENOVATE_REPOSITORIES="${RENOVATE_REPOSITORIES:-forust/homelab}" \
|
||||
-e RENOVATE_DRY_RUN="${RENOVATE_DRY_RUN:-}" \
|
||||
-e RENOVATE_CONFIG_FILE=/opt/renovate/config.js \
|
||||
-e RENOVATE_BASE_DIR=/tmp/renovate \
|
||||
-e LOG_LEVEL="${LOG_LEVEL:-info}" \
|
||||
|
||||
@@ -1,370 +0,0 @@
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "**"
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
REGISTRY: gcr.forust.xyz
|
||||
|
||||
jobs:
|
||||
lint-prettier:
|
||||
runs-on: [self-hosted, linux, arch, homelab]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Check formatting with Prettier
|
||||
shell: bash
|
||||
run: |
|
||||
mapfile -t prettier_files < <(
|
||||
git ls-files \
|
||||
| grep -E '\.(md|json|ya?ml|html|css)$' \
|
||||
| grep -Ev '^(\.docs/|\.zed/|errorpages/html/|homepages/(forust_files|xdfnx_files)/)'
|
||||
)
|
||||
|
||||
if [ "${#prettier_files[@]}" -eq 0 ]; then
|
||||
echo "No Prettier-managed files found."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
docker run --rm \
|
||||
-v "$PWD:/work" \
|
||||
-w /work \
|
||||
node:22-alpine \
|
||||
sh -lc 'npx --yes prettier@3 --check --ignore-unknown "$@"' sh "${prettier_files[@]}"
|
||||
|
||||
lint-ruff:
|
||||
runs-on: [self-hosted, linux, arch, homelab]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Lint Python with Ruff
|
||||
shell: bash
|
||||
run: |
|
||||
docker run --rm \
|
||||
-v "$PWD:/work" \
|
||||
-w /work \
|
||||
ghcr.io/astral-sh/ruff:latest \
|
||||
check .
|
||||
|
||||
lint-yaml:
|
||||
runs-on: [self-hosted, linux, arch, homelab]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Lint YAML syntax
|
||||
shell: bash
|
||||
run: |
|
||||
mapfile -t yaml_files < <(
|
||||
git ls-files '*.yaml' '*.yml' \
|
||||
':!node_modules/**' \
|
||||
':!**/.venv/**'
|
||||
)
|
||||
|
||||
if [ "${#yaml_files[@]}" -eq 0 ]; then
|
||||
echo "No YAML files found."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
docker run --rm \
|
||||
-v "$PWD:/work" \
|
||||
-w /work \
|
||||
cytopia/yamllint:latest \
|
||||
-c .yamllint "${yaml_files[@]}"
|
||||
|
||||
lint-dockerfiles:
|
||||
runs-on: [self-hosted, linux, arch, homelab]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Lint Dockerfiles
|
||||
shell: bash
|
||||
run: |
|
||||
mapfile -t dockerfiles < <(
|
||||
git ls-files ':(glob)**/Dockerfile' ':(glob)**/Dockerfile.*'
|
||||
)
|
||||
|
||||
if [ "${#dockerfiles[@]}" -eq 0 ]; then
|
||||
echo "No Dockerfiles found."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
docker run --rm \
|
||||
-v "$PWD:/work" \
|
||||
-w /work \
|
||||
--entrypoint hadolint \
|
||||
hadolint/hadolint:latest-debian \
|
||||
-c .hadolint.yaml "${dockerfiles[@]}"
|
||||
|
||||
validate:
|
||||
runs-on: [self-hosted, linux, arch, homelab]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Validate Kubernetes manifests
|
||||
shell: bash
|
||||
run: |
|
||||
mapfile -t manifests < <(
|
||||
git ls-files ':(glob)**/k8s/**/*.yaml' ':(glob)**/k8s/**/*.yml' \
|
||||
| grep -Ev '(^|/)(kustomization\.ya?ml|.*\.example\.ya?ml|.*values\.ya?ml|patch-.*\.ya?ml)$'
|
||||
)
|
||||
|
||||
if [ "${#manifests[@]}" -eq 0 ]; then
|
||||
echo "No Kubernetes manifests found."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
docker run --rm \
|
||||
-v "$PWD:/work" \
|
||||
-w /work \
|
||||
ghcr.io/yannh/kubeconform:latest \
|
||||
-strict \
|
||||
-ignore-missing-schemas \
|
||||
-summary \
|
||||
"${manifests[@]}"
|
||||
|
||||
build:
|
||||
needs: [lint-prettier, lint-ruff, lint-yaml, lint-dockerfiles, validate]
|
||||
if: github.event_name != 'pull_request' && (github.ref_name == 'main' || github.ref_name == 'dev')
|
||||
runs-on: [self-hosted, linux, arch, homelab]
|
||||
outputs:
|
||||
services: ${{ steps.services.outputs.services }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Detect changed docker-built services
|
||||
id: services
|
||||
shell: bash
|
||||
run: |
|
||||
base="${{ github.event.before }}"
|
||||
if [ -z "$base" ] || [ "$base" = "0000000000000000000000000000000000000000" ]; then
|
||||
base="$(git rev-list --max-parents=0 HEAD)"
|
||||
fi
|
||||
|
||||
mapfile -t changed_files < <(git diff --name-only "$base" "${GITHUB_SHA}")
|
||||
|
||||
services=()
|
||||
|
||||
add_service() {
|
||||
local name="$1"
|
||||
local seen=0
|
||||
for existing in "${services[@]}"; do
|
||||
if [ "$existing" = "$name" ]; then
|
||||
seen=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ "$seen" -eq 0 ]; then
|
||||
services+=("$name")
|
||||
fi
|
||||
}
|
||||
|
||||
for file in "${changed_files[@]}"; do
|
||||
case "$file" in
|
||||
dtek_notif/*)
|
||||
add_service dtek_notif
|
||||
;;
|
||||
errorpages/*)
|
||||
add_service errorpages
|
||||
;;
|
||||
userbot/*)
|
||||
add_service userbot
|
||||
;;
|
||||
homepages/*)
|
||||
add_service homepages
|
||||
;;
|
||||
edu_master/phpsessid-bot/*|edu_master/webinar-checker/*|edu_master/compose.yaml)
|
||||
add_service edu_master
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "${#services[@]}" -eq 0 ]; then
|
||||
echo "No docker-built services changed."
|
||||
echo "services=" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
printf '%s\n' "${services[@]}" | tee /tmp/services.txt
|
||||
echo "services=$(paste -sd, /tmp/services.txt)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Log in to registry
|
||||
if: steps.services.outputs.services != ''
|
||||
shell: bash
|
||||
run: |
|
||||
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login "${REGISTRY}" \
|
||||
-u "${{ secrets.REGISTRY_USERNAME }}" \
|
||||
--password-stdin
|
||||
|
||||
- name: Build and push changed images
|
||||
if: steps.services.outputs.services != ''
|
||||
shell: bash
|
||||
run: |
|
||||
IFS=, read -r -a services <<< "${{ steps.services.outputs.services }}"
|
||||
|
||||
for service in "${services[@]}"; do
|
||||
case "$service" in
|
||||
dtek_notif)
|
||||
image="${REGISTRY}/forust/dtek-notif"
|
||||
tags=("latest")
|
||||
case "${GITHUB_REF_NAME}" in
|
||||
main)
|
||||
tags+=("main" "prod")
|
||||
;;
|
||||
dev)
|
||||
tags+=("dev")
|
||||
;;
|
||||
esac
|
||||
build_args=()
|
||||
for tag in "${tags[@]}"; do
|
||||
build_args+=(-t "${image}:${tag}")
|
||||
done
|
||||
docker build "${build_args[@]}" dtek_notif
|
||||
for tag in "${tags[@]}"; do
|
||||
docker push "${image}:${tag}"
|
||||
done
|
||||
;;
|
||||
errorpages)
|
||||
image="${REGISTRY}/forust/error-pages"
|
||||
tags=("latest")
|
||||
case "${GITHUB_REF_NAME}" in
|
||||
main)
|
||||
tags+=("main" "prod")
|
||||
;;
|
||||
dev)
|
||||
tags+=("dev")
|
||||
;;
|
||||
esac
|
||||
build_args=()
|
||||
for tag in "${tags[@]}"; do
|
||||
build_args+=(-t "${image}:${tag}")
|
||||
done
|
||||
docker build "${build_args[@]}" errorpages
|
||||
for tag in "${tags[@]}"; do
|
||||
docker push "${image}:${tag}"
|
||||
done
|
||||
;;
|
||||
userbot)
|
||||
tags=("latest")
|
||||
case "${GITHUB_REF_NAME}" in
|
||||
main)
|
||||
tags+=("main" "prod")
|
||||
;;
|
||||
dev)
|
||||
tags+=("dev")
|
||||
;;
|
||||
esac
|
||||
for target in runtime panel; do
|
||||
case "$target" in
|
||||
runtime)
|
||||
context="userbot"
|
||||
image="${REGISTRY}/forust/userbot"
|
||||
;;
|
||||
panel)
|
||||
context="userbot/panel"
|
||||
image="${REGISTRY}/forust/userbot-panel"
|
||||
;;
|
||||
esac
|
||||
build_args=()
|
||||
for tag in "${tags[@]}"; do
|
||||
build_args+=(-t "${image}:${tag}")
|
||||
done
|
||||
docker build "${build_args[@]}" "$context"
|
||||
for tag in "${tags[@]}"; do
|
||||
docker push "${image}:${tag}"
|
||||
done
|
||||
done
|
||||
;;
|
||||
homepages)
|
||||
for service in forust xdfnx; do
|
||||
case "$service" in
|
||||
forust)
|
||||
image="${REGISTRY}/forust/forust-homepage"
|
||||
;;
|
||||
xdfnx)
|
||||
image="${REGISTRY}/forust/xdfnx-homepage"
|
||||
;;
|
||||
esac
|
||||
tags=("latest")
|
||||
case "${GITHUB_REF_NAME}" in
|
||||
main)
|
||||
tags+=("main" "prod")
|
||||
;;
|
||||
dev)
|
||||
tags+=("dev")
|
||||
;;
|
||||
esac
|
||||
build_args=()
|
||||
for tag in "${tags[@]}"; do
|
||||
build_args+=(-t "${image}:${tag}")
|
||||
done
|
||||
docker build "${build_args[@]}" -f "homepages/Dockerfile.${service}" homepages
|
||||
for tag in "${tags[@]}"; do
|
||||
docker push "${image}:${tag}"
|
||||
done
|
||||
done
|
||||
;;
|
||||
edu_master)
|
||||
for service in session-keeper webinar-checker; do
|
||||
case "$service" in
|
||||
session-keeper)
|
||||
context="edu_master/phpsessid-bot"
|
||||
image="${REGISTRY}/forust/session-keeper"
|
||||
;;
|
||||
webinar-checker)
|
||||
context="edu_master/webinar-checker"
|
||||
image="${REGISTRY}/forust/webinar-checker"
|
||||
;;
|
||||
esac
|
||||
tags=("latest")
|
||||
case "${GITHUB_REF_NAME}" in
|
||||
main)
|
||||
tags+=("main" "prod")
|
||||
;;
|
||||
dev)
|
||||
tags+=("dev")
|
||||
;;
|
||||
esac
|
||||
build_args=()
|
||||
for tag in "${tags[@]}"; do
|
||||
build_args+=(-t "${image}:${tag}")
|
||||
done
|
||||
docker build "${build_args[@]}" "$context"
|
||||
for tag in "${tags[@]}"; do
|
||||
docker push "${image}:${tag}"
|
||||
done
|
||||
done
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
deploy-userbot-panel:
|
||||
needs: build
|
||||
if: github.ref_name == 'main' && contains(needs.build.outputs.services, 'userbot')
|
||||
runs-on: [self-hosted, linux, arch, homelab, prod]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Apply and roll out userbot panel
|
||||
shell: bash
|
||||
run: |
|
||||
kubectl apply -f userbot/k8s/base/panel.yaml
|
||||
kubectl get secret userbot-common-secrets -n default -o json \
|
||||
| jq 'del(.metadata.annotations,.metadata.creationTimestamp,.metadata.resourceVersion,.metadata.uid,.metadata.managedFields) | .metadata.namespace = "userbot"' \
|
||||
| kubectl apply -f -
|
||||
# Keep legacy deployments (forust/anna) in sync with manifests; they have no replicas field, so apply leaves scaling to the user manager only.
|
||||
kubectl apply -f userbot/k8s/base/userbots.yaml
|
||||
kubectl rollout restart deployment/userbot-panel -n userbot
|
||||
kubectl rollout status deployment/userbot-panel -n userbot --timeout=180s
|
||||
@@ -1,176 +0,0 @@
|
||||
name: deploy
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: deploy-main
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
redeploy:
|
||||
runs-on: [self-hosted, linux, arch, homelab, prod]
|
||||
steps:
|
||||
- name: Redeploy workstation
|
||||
shell: bash
|
||||
env:
|
||||
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
|
||||
DEPLOY_PORT: ${{ secrets.DEPLOY_PORT }}
|
||||
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
|
||||
DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }}
|
||||
DEPLOY_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||
# Set APPLY_PRUNE=true to enable kubectl apply --prune. Requires every
|
||||
# manifest to carry label app.kubernetes.io/managed-by=homelab-deploy,
|
||||
# otherwise previously applied resources get deleted on the next run.
|
||||
APPLY_PRUNE: ${{ vars.APPLY_PRUNE }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
: "${DEPLOY_HOST:?missing DEPLOY_HOST}"
|
||||
: "${DEPLOY_USER:?missing DEPLOY_USER}"
|
||||
: "${DEPLOY_KEY:?missing DEPLOY_SSH_KEY}"
|
||||
|
||||
deploy_port="${DEPLOY_PORT:-22}"
|
||||
deploy_path="${DEPLOY_PATH:-/srv/homelab}"
|
||||
|
||||
ssh_key="$RUNNER_TEMP/deploy_key"
|
||||
mkdir -p "$RUNNER_TEMP"
|
||||
printf '%s\n' "$DEPLOY_KEY" > "$ssh_key"
|
||||
chmod 600 "$ssh_key"
|
||||
|
||||
ssh_opts=(
|
||||
-i "$ssh_key"
|
||||
-p "$deploy_port"
|
||||
-o BatchMode=yes
|
||||
-o StrictHostKeyChecking=accept-new
|
||||
)
|
||||
|
||||
ssh "${ssh_opts[@]}" "${DEPLOY_USER}@${DEPLOY_HOST}" \
|
||||
"DEPLOY_PATH=$(printf '%q' \"$deploy_path\") APPLY_PRUNE=$(printf '%q' \"${APPLY_PRUNE:-false}\") bash -se" <<'EOF'
|
||||
set -euo pipefail
|
||||
|
||||
repo="${DEPLOY_PATH:-/srv/homelab}"
|
||||
|
||||
if [ ! -d "$repo/.git" ]; then
|
||||
echo "Repository not found at $repo"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git -C "$repo" fetch origin main
|
||||
git -C "$repo" reset --hard origin/main
|
||||
|
||||
# Runtime selection: a service is k8s-managed when $SERVICE/k8s/active
|
||||
# exists. Otherwise it is compose-managed, and only k8s/routing/*
|
||||
# manifests (external Services / EndpointSlices / ServersTransport /
|
||||
# Ingresses that route to docker backends) are applied.
|
||||
# migrate: touch SERVICE/k8s/active (+ move routing files up)
|
||||
# rollback: rm SERVICE/k8s/active
|
||||
collect_k8s() {
|
||||
find "$1" -type f \( -name '*.yaml' -o -name '*.yml' \) \
|
||||
! -path '*/routing/*' ! -path '*/overlays/*' \
|
||||
! -name 'kustomization.y*ml' ! -name '*.example.y*ml' \
|
||||
! -name '*values.y*ml' ! -name 'patch-*.y*ml' \
|
||||
| sort
|
||||
}
|
||||
|
||||
collect_k8s_inactive() {
|
||||
find "$1" -type f \( -name '*.yaml' -o -name '*.yml' \) \
|
||||
\( -name 'namespace.y*ml' -o -path '*/routing/*' \) \
|
||||
! -path '*/overlays/*' ! -name '*.example.y*ml' \
|
||||
| sort
|
||||
}
|
||||
|
||||
mapfile -t compose_stacks < <(
|
||||
find "$repo" -type f \( -name 'compose.yaml' -o -name 'compose.yml' \) | sort
|
||||
)
|
||||
|
||||
mapfile -t k8s_manifests < <(
|
||||
for kd in $(find "$repo" -type d -name k8s ! -path '*/.git/*' | sort); do
|
||||
if [ -f "$kd/active" ]; then
|
||||
collect_k8s "$kd"
|
||||
else
|
||||
collect_k8s_inactive "$kd"
|
||||
fi
|
||||
done
|
||||
)
|
||||
|
||||
echo "== Validate compose stacks =="
|
||||
for cf in "${compose_stacks[@]}"; do
|
||||
dir=$(dirname "$cf")
|
||||
if [ -f "$dir/k8s/active" ]; then
|
||||
echo " skip (k8s-managed): $dir"
|
||||
continue
|
||||
fi
|
||||
echo " config: $cf"
|
||||
docker compose -f "$cf" config --quiet
|
||||
done
|
||||
|
||||
echo "== Validate k8s manifests (kubectl dry-run) =="
|
||||
for m in "${k8s_manifests[@]}"; do
|
||||
echo " apply --dry-run=client $m"
|
||||
kubectl apply --dry-run=client -f "$m" >/dev/null
|
||||
done
|
||||
|
||||
echo "== Applying Kubernetes manifests =="
|
||||
ns_files=()
|
||||
other_files=()
|
||||
for m in "${k8s_manifests[@]}"; do
|
||||
case "$m" in
|
||||
*/namespace.y?ml) ns_files+=("$m") ;;
|
||||
*) other_files+=("$m") ;;
|
||||
esac
|
||||
done
|
||||
|
||||
prune_opts=()
|
||||
if [ "${APPLY_PRUNE:-false}" = "true" ]; then
|
||||
prune_opts=(--prune -l app.kubernetes.io/managed-by=homelab-deploy)
|
||||
fi
|
||||
|
||||
if [ "${#ns_files[@]}" -gt 0 ]; then
|
||||
echo " namespaces first: ${ns_files[*]}"
|
||||
kubectl apply -f "${ns_files[@]}"
|
||||
fi
|
||||
if [ -f "$repo/prometheus-stack/k8s/active" ]; then
|
||||
echo "== Upgrading kube-prometheus-stack =="
|
||||
helm upgrade --install prometheus-stack prometheus-community/kube-prometheus-stack \
|
||||
--namespace prometheus \
|
||||
--version 86.2.3 \
|
||||
--values "$repo/prometheus-stack/k8s/grafana-values.yaml" \
|
||||
--wait
|
||||
fi
|
||||
if [ -f "$repo/loki/k8s/active" ]; then
|
||||
echo "== Upgrading loki/alloy =="
|
||||
helm repo add grafana https://grafana.github.io/helm-charts >/dev/null 2>&1 || true
|
||||
helm repo update grafana >/dev/null 2>&1 || true
|
||||
helm upgrade --install loki grafana/loki \
|
||||
--version 7.3.0 \
|
||||
--namespace prometheus \
|
||||
--values "$repo/loki/k8s/loki-values.yaml" \
|
||||
--wait
|
||||
helm upgrade --install alloy grafana/alloy \
|
||||
--version 1.12.1 \
|
||||
--namespace prometheus \
|
||||
--values "$repo/loki/k8s/alloy-values.yaml" \
|
||||
--wait
|
||||
fi
|
||||
|
||||
if [ "${#other_files[@]}" -gt 0 ]; then
|
||||
echo " resources: ${other_files[*]}"
|
||||
kubectl apply "${prune_opts[@]}" -f "${other_files[@]}"
|
||||
fi
|
||||
|
||||
echo "== Redeploying docker compose stacks =="
|
||||
for cf in "${compose_stacks[@]}"; do
|
||||
dir=$(dirname "$cf")
|
||||
if [ -f "$dir/k8s/active" ]; then
|
||||
echo " skip (k8s-managed): $dir"
|
||||
continue
|
||||
fi
|
||||
echo " compose: $dir"
|
||||
if grep -Eq '^\s+pull_policy:\s*build\b' "$cf"; then
|
||||
docker compose -f "$cf" build
|
||||
docker compose -f "$cf" push
|
||||
fi
|
||||
docker compose -f "$cf" up -d --pull always --remove-orphans
|
||||
done
|
||||
EOF
|
||||
Reference in new issue
Block a user