1 Commits

Author SHA1 Message Date
dee6fe03fa chore(deps): update gethomepage/homepage to v1.11.0
All checks were successful
lint-test-helm / lint-helm (pull_request) Successful in 14s
render-manifests-automerge / render-manifests-automerge (pull_request) Has been skipped
render-manifests-merge / render-manifests-merge (pull_request) Successful in 36s
2026-03-14 20:05:41 +00:00
257 changed files with 3597 additions and 2848 deletions

View File

@@ -14,21 +14,21 @@ on:
- 'hosts/**'
env:
BASE_BRANCH: "origin/${{ github.base_ref }}"
BASE_BRANCH: "origin/${{ gitea.base_ref }}"
jobs:
lint-docker-compose:
runs-on: ubuntu-js
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Check Branch Exists
id: check-branch-exists
if: github.event_name == 'pull_request'
uses: GuillaumeFalourd/branch-exists@650358876c774d6ccbd581b5553eb636dab79a97 # v1.2
uses: GuillaumeFalourd/branch-exists@v1.1
with:
branch: "${{ github.base_ref }}"
@@ -36,22 +36,21 @@ jobs:
id: branch-exists
if: github.event_name == 'push' || steps.check-branch-exists.outputs.exists == 'true' && github.event_name == 'pull_request'
run: |
if [ "${{ github.event_name }}" == "push" ]; then
if [ ${{ github.event_name == 'push' }} ]; then
echo ">> Action is from a push event, will continue with linting"
else
echo ">> Branch ${{ github.base_ref }} exists, will continue with linting"
echo ">> Branch ${{ gitea.base_ref }} exists, will continue with linting"
fi
echo ""
echo "----"
echo "exists=true" >> $GITHUB_OUTPUT
echo "exists=true" >> $GITEA_OUTPUT
- name: Set Up Node.js
- name: Set up Node.js
if: steps.branch-exists.outputs.exists == 'true'
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
uses: actions/setup-node@v6
with:
node-version: '24'
@@ -59,48 +58,58 @@ jobs:
id: check-dir-changes
if: steps.branch-exists.outputs.exists == 'true'
run: |
CHANGED_COMPOSE=()
echo ">> Target branch for diff is: ${BASE_BRANCH}"
if [ "${{ github.event_name }}" == "pull_request" ]; then
DIFF_TARGET="${BASE_BRANCH}"
echo ""
echo ">> Checking for changes in a pull request ..."
GIT_DIFF=$(git diff --name-only "${BASE_BRANCH}" | xargs -I {} dirname {} | sort -u)
else
DIFF_TARGET="${{ github.event.before }}..HEAD"
echo ""
echo ">> Checking for changes from a push ..."
GIT_DIFF=$(git diff --name-only ${{ gitea.event.before }}..HEAD | xargs -I {} dirname {} | sort -u)
fi
CHANGED_COMPOSE=$(git diff --name-only "${DIFF_TARGET}" | grep -E "^hosts/[^/]+/[^/]+/" | cut -d/ -f1,2,3 | sort -u || true)
if [ -n "${GIT_DIFF}" ]; then
echo ""
echo ">> Changes detected:"
echo "$GIT_DIFF"
for path in $GIT_DIFF; do
if echo "$path" | grep -q -E "hosts/[^/]+/[^/]+"; then
echo ""
echo ">> Adding path: $path"
CHANGED_COMPOSE+=$(echo "$path")
CHANGED_COMPOSE+=$(echo " ")
fi
done
else
echo ""
echo ">> No changes detected"
fi
if [ -n "${CHANGED_COMPOSE}" ]; then
echo ""
echo ">> Compose to Lint:"
echo ""
echo "${CHANGED_COMPOSE}"
echo "$(echo "${CHANGED_COMPOSE}" | sort -u)"
CHANGED_COMPOSE_CSV=$(echo "$CHANGED_COMPOSE" | paste -sd ',' -)
echo ""
echo "----"
echo "changes-detected=true" >> $GITHUB_OUTPUT
echo "compose-dir-csv=${CHANGED_COMPOSE_CSV}" >> $GITHUB_OUTPUT
echo "compose-dir<<EOF" >> $GITHUB_OUTPUT
echo "${CHANGED_COMPOSE}" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
echo "changes-detected=true" >> $GITEA_OUTPUT
echo "compose-dir<<EOF" >> $GITEA_OUTPUT
echo "$(echo "${CHANGED_COMPOSE}" | sort -u)" >> $GITEA_OUTPUT
echo "EOF" >> $GITEA_OUTPUT
else
echo ""
echo ">> Did not find any docker compose files to lint"
echo ""
echo "----"
echo "changes-detected=false" >> $GITHUB_OUTPUT
echo "changes-detected=false" >> $GITEA_OUTPUT
fi
- name: Lint Docker Compose
@@ -108,27 +117,25 @@ jobs:
env:
CHANGED_COMPOSE: ${{ steps.check-dir-changes.outputs.compose-dir }}
run: |
echo ">> Running dclint on changed compose files ..."
for COMPOSE in $CHANGED_COMPOSE; do
echo ">> Linting ${COMPOSE} ..."
npx dclint ${COMPOSE}
echo ">> Running dclint on changed compose files:"
echo "$CHANGED_COMPOSE"
for compose in $CHANGED_COMPOSE; do
echo ">> Linting $compose ..."
npx dclint $compose
done
echo ""
echo "----"
- name: ntfy Failed
uses: niniyas/ntfy-action@96acac57fdc91d4c4f50b78486c1ed6f03f9f61c # master
uses: niniyas/ntfy-action@master
if: failure()
with:
url: '${{ secrets.NTFY_URL }}'
topic: '${{ secrets.NTFY_TOPIC }}'
title: 'Docker Compose Test Failure'
title: 'Test Failure - Infrastructure'
priority: 3
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,failed
details: "Docker linting for compose dirs: ${{ steps.check-dir-changes.outputs.compose-dir-csv }}"
details: 'Docker linting on Pull Request for Infrastructure has failed!'
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
actions: '[{"action": "view", "label": "View Logs", "url": "${{ vars.USER_URL }}/${{ github.repository }}/actions/runs/${{ github.run_id }}", "clear": true}]'
actions: '[{"action": "view", "label": "Open Gitea", "url": "https://gitea.alexlebens.dev/alexlebens/infrastructure/actions?workflow=lint-test-docker-pull.yaml", "clear": true}]'
image: true

View File

@@ -15,113 +15,104 @@ on:
env:
CLUSTER: cl01tl
BASE_BRANCH: "origin/${{ github.base_ref }}"
# renovate: datasource=github-releases depName=yannh/kubeconform
KUBECONFORM_VERSION: "v0.6.7"
BASE_BRANCH: "origin/${{ gitea.base_ref }}"
jobs:
lint-helm:
runs-on: ubuntu-js
outputs:
chart-dir: ${{ steps.check-dir-changes.outputs.chart-dir }}
chart-dir-csv: ${{ steps.check-dir-changes.outputs.chart-dir-csv }}
changes-detected: ${{ steps.check-dir-changes.outputs.changes-detected }}
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Check Branch Exists
id: check-branch-exists
if: github.event_name == 'pull_request'
uses: GuillaumeFalourd/branch-exists@650358876c774d6ccbd581b5553eb636dab79a97 # v1.2
uses: GuillaumeFalourd/branch-exists@v1.1
with:
branch: ${{ github.base_ref }}
branch: ${{ gitea.base_ref }}
- name: Report Branch Exists
id: branch-exists
if: github.event_name == 'push' || steps.check-branch-exists.outputs.exists == 'true' && github.event_name == 'pull_request'
run: |
if [ "${{ github.event_name }}" == "push" ]; then
if [ ${{ github.event_name == 'push' }} ]; then
echo ">> Action is from a push event, will continue with linting"
else
echo ">> Branch ${{ github.base_ref }} exists, will continue with linting"
echo ">> Branch ${{ gitea.base_ref }} exists, will continue with linting"
fi
echo ""
echo "----"
echo "exists=true" >> $GITHUB_OUTPUT
echo "exists=true" >> $GITEA_OUTPUT
- name: Set Up Helm
- name: Set up Helm
if: steps.branch-exists.outputs.exists == 'true'
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5
uses: azure/setup-helm@v4
with:
token: ${{ secrets.GITEA_TOKEN }}
# renovate: datasource=github-releases depName=helm/helm
version: v4.1.3
version: v3.19.2
cache: true
- name: Cache Helm Dependencies
if: steps.branch-exists.outputs.exists == 'true'
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: |
~/.cache/helm
~/.config/helm
key: helm-cache-${{ runner.os }}-${{ hashFiles('infrastructure/clusters/cl01tl/helm/**/Chart.yaml', 'infrastructure/clusters/cl01tl/helm/**/Chart.lock') }}
restore-keys: |
helm-cache-${{ runner.os }}-
- name: Check Directories for Changes
id: check-dir-changes
if: steps.branch-exists.outputs.exists == 'true'
run: |
CHANGED_CHARTS=()
echo ">> Target branch for diff is: ${BASE_BRANCH}"
if [ "${{ github.event_name }}" == "pull_request" ]; then
DIFF_TARGET="${BASE_BRANCH}"
echo ""
echo ">> Checking for changes in a pull request ..."
GIT_DIFF=$(git diff --name-only "${BASE_BRANCH}" | xargs -I {} dirname {} | sort -u)
else
DIFF_TARGET="${{ github.event.before }}..HEAD"
echo ""
echo ">> Checking for changes from a push ..."
GIT_DIFF=$(git diff --name-only ${{ gitea.event.before }}..HEAD | xargs -I {} dirname {} | sort -u)
fi
CHANGED_CHARTS=$(git diff --name-only "${DIFF_TARGET}" | grep -E "^clusters/${CLUSTER}/helm/" | awk -F '/' '{print $4}' | sort -u || true)
if [ -n "${GIT_DIFF}" ]; then
echo ""
echo ">> Changes detected:"
echo "$GIT_DIFF"
for path in $GIT_DIFF; do
if echo "$path" | grep -q -E "clusters/[^/]+/helm/[^/]+"; then
echo ""
echo ">> Adding path: $path"
CHANGED_CHARTS+=$(echo "$path" | awk -F '/' '{print $4}')
CHANGED_CHARTS+=$(echo "\n")
fi
done
else
echo ""
echo ">> No changes detected"
fi
if [ -n "${CHANGED_CHARTS}" ]; then
echo ""
echo ">> Chart to Lint:"
echo ""
echo "${CHANGED_CHARTS}"
echo "$(echo "${CHANGED_CHARTS}" | sort -u)"
CHANGED_CHARTS_CSV=$(echo "$CHANGED_CHARTS" | paste -sd ',' -)
echo ""
echo "----"
echo "changes-detected=true" >> $GITHUB_OUTPUT
echo "chart-dir-csv=${CHANGED_CHARTS_CSV}" >> $GITHUB_OUTPUT
echo "chart-dir<<EOF" >> $GITHUB_OUTPUT
echo "${CHANGED_CHARTS}" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
echo "changes-detected=true" >> $GITEA_OUTPUT
echo "chart-dir<<EOF" >> $GITEA_OUTPUT
echo "$(echo "${CHANGED_CHARTS}" | sort -u)" >> $GITEA_OUTPUT
echo "EOF" >> $GITEA_OUTPUT
else
echo ""
echo ">> Did not find any helm charts files to lint"
echo ""
echo "----"
echo "changes-detected=false" >> $GITHUB_OUTPUT
echo "changes-detected=false" >> $GITEA_OUTPUT
fi
- name: Add Repositories
@@ -130,238 +121,68 @@ jobs:
CHANGED_CHARTS: ${{ steps.check-dir-changes.outputs.chart-dir }}
run: |
echo ">> Adding repositories for chart dependencies ..."
echo ""
for DIR in ${CHANGED_CHARTS}; do
helm dependency list --max-col-width 120 clusters/${CLUSTER}/helm/${DIR} 2> /dev/null \
| tail -n +2 \
| awk 'NF > 0 { print $1, $3 }' \
| while read -r REPO_NAME REPO_URL; do
if [[ "${REPO_URL}" == oci://* ]]; then
echo ">> Ignoring OCI repo: ${REPO_URL}"
elif [[ -n "${REPO_NAME}" && -n "${REPO_URL}" ]]; then
helm repo add "${REPO_NAME}" "${REPO_URL}"
for dir in ${CHANGED_CHARTS}; do
helm dependency list --max-col-width 120 clusters/${CLUSTER}/helm/$dir 2> /dev/null \
| tail +2 | head -n -1 \
| awk '{ print "helm repo add " $1 " " $3 }' \
| while read cmd; do
if [[ "$cmd" == "*oci://*" ]]; then
echo ">> Ignoring OCI repo"
else
echo ">> Command: $cmd"
echo "$cmd" | sh;
fi
done || true
done
if helm repo list > /dev/null 2>&1; then
if helm repo list | tail +2 | read -r; then
echo ""
echo ">> Update repository cache ..."
helm repo update
fi
echo ""
echo "----"
- name: Lint Helm Chart
id: lint
if: steps.check-dir-changes.outputs.changes-detected == 'true'
env:
CHANGED_CHARTS: ${{ steps.check-dir-changes.outputs.chart-dir }}
run: |
EXIT_CODE=0
FAILED_CHARTS=""
echo ">> Running linting on changed charts ..."
for DIR in ${CHANGED_CHARTS}; do
CHART_PATH="clusters/${CLUSTER}/helm/${DIR}"
CHART_NAME=$(basename "${CHART_PATH}")
for dir in ${CHANGED_CHARTS}; do
chart_path=clusters/${CLUSTER}/helm/$dir
chart_name=$(basename "$chart_path")
if [ -f "${CHART_PATH}/Chart.yaml" ]; then
echo ""
echo ">> Building helm dependency for ${CHART_NAME} ..."
helm dependency build "${CHART_PATH}" --skip-refresh
if [ -f "$chart_path/Chart.yaml" ]; then
cd $chart_path
echo ""
echo ">> Linting helm chart ${CHART_NAME} ..."
echo ">> Building helm dependency ..."
helm dependency build --skip-refresh
if ! helm lint "${CHART_PATH}" --namespace "default"; then
EXIT_CODE=1
if [ -z "${FAILED_CHARTS}" ]; then
FAILED_CHARTS="${DIR}"
else
FAILED_CHARTS="${FAILED_CHARTS}, ${DIR}"
fi
fi
echo ""
echo ">> Linting helm ..."
helm lint --namespace "$chart_name"
else
echo ""
echo ">> Directory ${CHART_PATH} does not contain a Chart.yaml. Skipping ..."
echo ">> Directory $chart_path does not contain a Chart.yaml. Skipping ..."
echo ""
fi
done
echo ""
echo "----"
echo "failed-charts=${FAILED_CHARTS}" >> "$GITHUB_OUTPUT"
exit $EXIT_CODE
- name: ntfy Failed
uses: niniyas/ntfy-action@96acac57fdc91d4c4f50b78486c1ed6f03f9f61c # master
uses: niniyas/ntfy-action@master
if: failure()
with:
url: '${{ secrets.NTFY_URL }}'
topic: '${{ secrets.NTFY_TOPIC }}'
title: 'Helm Test Failure'
title: 'Test Failure - Infrastructure'
priority: 3
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,failed
details: "Helm linting for cluster '${{ env.CLUSTER }}' failed on charts: ${{ steps.lint.outputs.failed-charts }}"
details: 'Helm linting on Pull Request for Infrastructure has failed!'
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
actions: '[{"action": "view", "label": "View Run", "url": "${{ vars.USER_URL }}/${{ github.repository }}/actions/runs/${{ github.run_id }}", "clear": true}]'
image: true
validate-kubeconform:
needs: lint-helm
runs-on: ubuntu-js
if: |
needs.lint-helm.result == 'success' &&
needs.lint-helm.outputs.changes-detected == 'true' &&
github.event_name == 'pull_request'
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
- name: Install Kubeconform
run: |
echo ">> Downloading Kubeconform ${{ env.KUBECONFORM_VERSION }} ..."
wget -q https://github.com/yannh/kubeconform/releases/download/${{ env.KUBECONFORM_VERSION }}/kubeconform-linux-amd64.tar.gz
echo ""
echo ">> Extracting Kubeconform ..."
tar xf kubeconform-linux-amd64.tar.gz
echo ""
echo ">> Installing Kubeconform ..."
sudo mv kubeconform /usr/local/bin/
echo ""
echo ">> Verifying installation ..."
kubeconform -v
echo ""
echo "----"
- name: Set Up Helm
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5
with:
token: ${{ secrets.GITEA_TOKEN }}
# renovate: datasource=github-releases depName=helm/helm
version: v4.1.3
cache: true
- name: Cache Helm Dependencies
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: |
~/.cache/helm
~/.config/helm
key: helm-cache-${{ runner.os }}-${{ hashFiles('infrastructure/clusters/cl01tl/helm/**/Chart.yaml', 'infrastructure/clusters/cl01tl/helm/**/Chart.lock') }}
restore-keys: |
helm-cache-${{ runner.os }}-
- name: Add Repositories
env:
CHANGED_CHARTS: ${{ needs.lint-helm.outputs.chart-dir }}
run: |
echo ">> Adding repositories for chart dependencies ..."
echo ""
for DIR in ${CHANGED_CHARTS}; do
helm dependency list --max-col-width 120 clusters/${CLUSTER}/helm/${DIR} 2> /dev/null \
| tail -n +2 \
| awk 'NF > 0 { print $1, $3 }' \
| while read -r REPO_NAME REPO_URL; do
if [[ "${REPO_URL}" == oci://* ]]; then
echo ">> Ignoring OCI repo: ${REPO_URL}"
elif [[ -n "${REPO_NAME}" && -n "${REPO_URL}" ]]; then
helm repo add "${REPO_NAME}" "${REPO_URL}"
fi
done || true
done
if helm repo list > /dev/null 2>&1; then
echo ""
echo ">> Update repository cache ..."
helm repo update
fi
echo ""
echo "----"
- name: Validate Rendered Templates
id: validate
env:
CHANGED_CHARTS: ${{ needs.lint-helm.outputs.chart-dir }}
run: |
SCHEMA_LOCATIONS="-schema-location default -schema-location https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json"
EXIT_CODE=0
FAILED_CHARTS=""
for DIR in ${CHANGED_CHARTS}; do
CHART_PATH="clusters/${CLUSTER}/helm/${DIR}"
echo ""
echo ">> Validating: ${DIR}"
helm dependency build "${CHART_PATH}" --skip-refresh
if ! helm template "${DIR}" "${CHART_PATH}" --include-crds --namespace default --api-versions "gateway.networking.k8s.io/v1/HTTPRoute" | \
kubeconform \
${SCHEMA_LOCATIONS} \
-ignore-missing-schemas \
-strict \
-summary; then
EXIT_CODE=1
if [ -z "${FAILED_CHARTS}" ]; then
FAILED_CHARTS="${DIR}"
else
FAILED_CHARTS="${FAILED_CHARTS}, ${DIR}"
fi
fi
done
echo ""
echo "----"
echo "failed-charts=${FAILED_CHARTS}" >> "$GITHUB_OUTPUT"
exit $EXIT_CODE
- name: ntfy Failed
uses: niniyas/ntfy-action@96acac57fdc91d4c4f50b78486c1ed6f03f9f61c # master
if: failure()
with:
url: '${{ secrets.NTFY_URL }}'
topic: '${{ secrets.NTFY_TOPIC }}'
title: 'Kubeconform Test Failure'
priority: 3
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,failed
details: "Kubeconform for cluster '${{ env.CLUSTER }}' failed on charts: ${{ steps.validate.outputs.failed-charts }}"
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
actions: '[{"action": "view", "label": "View Run", "url": "${{ vars.USER_URL }}/${{ github.repository }}/actions/runs/${{ github.run_id }}", "clear": true}]'
actions: '[{"action": "view", "label": "Open Gitea", "url": "https://gitea.alexlebens.dev/alexlebens/infrastructure/actions?workflow=lint-test-helm-pull.yaml", "clear": true}]'
image: true

View File

@@ -0,0 +1,440 @@
name: render-manifests-automerge
on:
pull_request:
branches:
- main
paths:
- 'clusters/cl01tl/helm/**'
types:
- closed
env:
CLUSTER: cl01tl
BASE_BRANCH: manifests
BRANCH_NAME_BASE: auto/update-manifests-automerge
MAIN_DIR: /workspace/alexlebens/infrastructure/infrastructure
MANIFEST_DIR: /workspace/alexlebens/infrastructure/infrastructure-manifests
jobs:
render-manifests-automerge:
runs-on: ubuntu-js
if: ${{ (github.event.pull_request.merged == true) && (contains(github.event.pull_request.labels.*.name, 'automerge')) }}
steps:
- name: Checkout Main
uses: actions/checkout@v6
with:
path: infrastructure
fetch-depth: 0
- name: Checkout Manifests
uses: actions/checkout@v6
with:
ref: manifests
path: infrastructure-manifests
- name: Set up Helm
uses: azure/setup-helm@v4
with:
token: ${{ secrets.GITEA_TOKEN }}
version: v3.17.2 # Pending https://github.com/helm/helm/pull/30743
cache: true
- name: Configure Kubeconfig
uses: azure/k8s-set-context@v4
with:
method: kubeconfig
kubeconfig: ${{ secrets.KUBECONFIG }}
- name: Prepare Manifest Branch
id: prepare-manifest-branch
run: |
cd ${MANIFEST_DIR}
BRANCH_NAME="${BRANCH_NAME_BASE}-$(date +%Y%m%d%H%M%S)"
echo ">> Configure git to use gitea-bot as user ..."
git config user.name "gitea-bot"
git config user.email "gitea-bot@alexlebens.net"
echo ">> Creating branch ..."
git checkout -b $BRANCH_NAME
echo "----"
echo "BRANCH_NAME=${BRANCH_NAME}" >> $GITEA_OUTPUT
- name: Check which Directories have Changes
id: check-dir-changes
run: |
cd ${MAIN_DIR}
RENDER_DIR=()
echo ">> Checking for changes from HEAD^..HEAD ..."
GIT_DIFF=$(git diff --name-only HEAD^..HEAD | xargs -I {} dirname {} | sort -u | grep -E "clusters/[^/]+/helm/[^/]+")
if [ -n "${GIT_DIFF}" ]; then
echo ">> Changes detected:"
echo "$GIT_DIFF"
for path in $GIT_DIFF; do
RENDER_DIR+=$(echo "$path" | awk -F '/' '{print $4}')
RENDER_DIR+=$(echo " ")
done
else
echo ">> No changes detected"
fi
if [ -n "${RENDER_DIR}" ]; then
echo ">> Directories to Render:"
echo "$(echo "${RENDER_DIR}" | sort -u)"
echo "----"
echo "changes-detected=true" >> $GITEA_OUTPUT
echo "render-dir<<EOF" >> $GITEA_OUTPUT
echo "$(echo "${RENDER_DIR}" | sort -u)" >> $GITEA_OUTPUT
echo "EOF" >> $GITEA_OUTPUT
else
echo "changes-detected=false" >> $GITEA_OUTPUT
fi
- name: Add Repositories
if: steps.check-dir-changes.outputs.changes-detected == 'true'
env:
RENDER_DIR: ${{ steps.check-dir-changes.outputs.render-dir }}
run: |
cd ${MAIN_DIR}
echo ">> Adding repositories for chart dependencies ..."
for dir in ${RENDER_DIR}; do
helm dependency list --max-col-width 120 ${MAIN_DIR}/clusters/${CLUSTER}/helm/$dir 2> /dev/null \
| tail +2 | head -n -1 \
| awk '{ print "helm repo add " $1 " " $3 }' \
| while read cmd; do
if [[ "$cmd" == "*oci://*" ]]; then
echo ">> Ignoring OCI repo"
else
echo "$cmd" | sh;
fi
done || true
done
if helm repo list | tail +2 | read -r; then
echo ">> Update repository cache ..."
helm repo update
fi
echo "----"
- name: Remove Changed Manifest Files
if: steps.check-dir-changes.outputs.changes-detected == 'true'
env:
RENDER_DIR: ${{ steps.check-dir-changes.outputs.render-dir }}
run: |
cd ${MANIFEST_DIR}
echo ">> Remove manfiest files and rebuild from source ..."
for dir in ${RENDER_DIR}; do
chart_path=${MANIFEST_DIR}/clusters/${CLUSTER}/manifests/$dir
echo "$chart_path"
rm -rf $chart_path/*
done
echo "----"
- name: Render Helm Manifests
id: render-manifests
if: steps.check-dir-changes.outputs.changes-detected == 'true'
env:
RENDER_DIR: ${{ steps.check-dir-changes.outputs.render-dir }}
run: |
cd ${MAIN_DIR}
echo ">> Rendering Manifests ..."
for dir in ${RENDER_DIR}; do
chart_path=${MAIN_DIR}/clusters/${CLUSTER}/helm/$dir
chart_name=$(basename "$chart_path")
echo ""
echo ""
echo ">> Rendering chart: $chart_name"
echo ">> Chart path $chart_path"
if [ -f "$chart_path/Chart.yaml" ]; then
OUTPUT_FOLDER="${MANIFEST_DIR}/clusters/${CLUSTER}/manifests/$chart_name/"
TEMPLATE=""
mkdir -p ${MANIFEST_DIR}/clusters/${CLUSTER}/manifests/$chart_name
cd $chart_path
echo ""
echo ">> Updating helm dependency ..."
helm dependency update --skip-refresh
echo ""
echo ">> Building helm dependency ..."
helm dependency build --skip-refresh
echo ""
echo ">> Linting helm ..."
helm lint --namespace "$chart_name"
echo ""
echo ">> Rendering templates ..."
case "$chart_name" in
"stack")
echo ""
echo ">> Special Rendering for stack into argocd namespace ..."
TEMPLATE=$(helm template $chart_name ./ --namespace argocd --include-crds --dry-run=server --api-versions "gateway.networking.k8s.io/v1/HTTPRoute")
;;
"cilium" | "coredns" | "metrics-server" |"prometheus-operator-crds")
echo ""
echo ">> Special Rendering for $chart_name into kube-system namespace ..."
TEMPLATE=$(helm template $chart_name ./ --namespace kube-system --include-crds --dry-run=server --api-versions "gateway.networking.k8s.io/v1/HTTPRoute")
;;
*)
echo ""
echo ">> Standard Rendering for $chart_name ..."
TEMPLATE=$(helm template "$chart_name" ./ --namespace "$chart_name" --include-crds --dry-run=server --api-versions "gateway.networking.k8s.io/v1/HTTPRoute")
;;
esac
echo ""
echo ">> Formating rendered template ..."
echo "$TEMPLATE" | yq '... comments=""' | yq 'select(. != null)' | yq -s '"'"$OUTPUT_FOLDER"'" + .kind + "-" + .metadata.name + ".yaml"'
# Strip comments again to ensure formatting correctness
for file in "$OUTPUT_FOLDER"/*; do
yq -i '... comments=""' $file
done
echo ""
echo ">> Manifests for $chart_name rendered to $OUTPUT_FOLDER"
ls $OUTPUT_FOLDER
echo ""
else
echo ""
echo ">> Directory $chart_path does not contain a Chart.yaml. Skipping ..."
echo ""
fi
done
echo "----"
- name: Check for Changes
id: check-changes
if: steps.check-dir-changes.outputs.changes-detected == 'true'
run: |
cd ${MANIFEST_DIR}
GIT_CHANGES=$(git status --porcelain)
if [ -n "$GIT_CHANGES" ]; then
echo ">> Changes detected"
git status --porcelain
echo "changes-detected=true" >> $GITEA_OUTPUT
else
echo ">> No changes detected, skipping PR creation"
fi
echo "----"
- name: Commit and Push Changes
id: commit-push
if: steps.check-changes.outputs.changes-detected == 'true'
env:
BRANCH_NAME: ${{ steps.prepare-manifest-branch.outputs.BRANCH_NAME }}
run: |
cd ${MANIFEST_DIR}
echo ">> Commiting changes to ${BRANCH_NAME} ..."
git add .
git commit -m "chore: Update manifests after automerge"
REPO_URL="${{ secrets.REPO_URL }}/${{ gitea.repository }}"
echo ">> Pushing changes to $REPO_URL ..."
git push -u "https://oauth2:${{ secrets.BOT_TOKEN }}@$(echo $REPO_URL | sed -e 's|https://||')" ${BRANCH_NAME}
echo "----"
echo "push=true" >> $GITEA_OUTPUT
- name: Create Pull Request
id: create-pull-request
if: steps.commit-push.outputs.push == 'true'
env:
GITEA_TOKEN: ${{ secrets.BOT_TOKEN }}
GITEA_URL: ${{ secrets.REPO_URL }}
BRANCH_NAME: ${{ steps.prepare-manifest-branch.outputs.BRANCH_NAME }}
run: |
cd ${MANIFEST_DIR}
API_ENDPOINT="${GITEA_URL}/api/v1/repos/${{ gitea.repository }}/pulls"
PAYLOAD=$( jq -n \
--arg head "${BRANCH_NAME}" \
--arg base "${BASE_BRANCH}" \
--arg title "Automated Manifest Update - Automerge" \
--arg body "This PR contains newly rendered Kubernetes manifests automatically generated by the CI workflow. This is expected to be automerged." \
'{head: $head, base: $base, title: $title, body: $body}' )
echo ">> Creating PR from branch ${BRANCH_NAME} into ${BASE_BRANCH}"
echo ">> With Endpoint of:"
echo "$API_ENDPOINT"
echo ">> With Payload of:"
echo "$PAYLOAD"
HTTP_STATUS=$(
curl -X POST \
--silent \
--write-out '%{http_code}' \
--output response_body.json \
--dump-header response_headers.txt \
--data "$PAYLOAD" \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"$API_ENDPOINT" 2> response_errors.txt
)
echo ">> HTTP Status Code: $HTTP_STATUS"
echo ">> Response Output ..."
echo "----"
cat response_body.json
echo "----"
cat response_headers.txt
echo "----"
cat response_errors.txt
echo "----"
if [ "$HTTP_STATUS" == "201" ]; then
echo ">> Pull Request created successfully!"
PR_URL=$(cat response_body.json | jq -r .html_url)
echo ">> Pull Request URL: $PR_URL"
echo "pull-request-url=${PR_URL}" >> $GITEA_OUTPUT
PR_NUMBER=$(cat response_body.json | jq -r .number)
echo ">> Pull Request Number: $PR_NUMBER"
echo "pull-request-number=${PR_NUMBER}" >> $GITEA_OUTPUT
echo "pull-request-operation=created" >> $GITEA_OUTPUT
elif [ "$HTTP_STATUS" == "422" ]; then
echo ">> Failed to create PR (HTTP 422: Unprocessable Entity), PR may already exist"
elif [ "$HTTP_STATUS" == "409" ]; then
echo ">> Failed to create PR (HTTP 409: Conflict), PR already exists"
else
echo ">> Failed to create PR, HTTP status code: $HTTP_STATUS"
exit 1
fi
echo "----"
- name: Merge Changes
id: merge-changes
if: steps.commit-push.outputs.push == 'true'
env:
GITEA_TOKEN: ${{ secrets.BOT_TOKEN }}
GITEA_URL: ${{ secrets.REPO_URL }}
BRANCH_NAME: ${{ steps.prepare-manifest-branch.outputs.BRANCH_NAME }}
PR_NUMBER: ${{ steps.create-pull-request.outputs.pull-request-number }}
run: |
cd ${MANIFEST_DIR}
API_ENDPOINT="${GITEA_URL}/api/v1/repos/${{ gitea.repository }}/pulls/${PR_NUMBER}/merge"
PAYLOAD=$( jq -n \
--arg Do "merge" \
'{Do: $Do}' )
echo ">> Merging PR with ID: ${PR_NUMBER}"
echo ">> With Endpoint of:"
echo "$API_ENDPOINT"
echo ">> With Payload of:"
echo "$PAYLOAD"
HTTP_STATUS=$(
curl -X POST \
--silent \
--write-out '%{http_code}' \
--output response_body.json \
--dump-header response_headers.txt \
--data "$PAYLOAD" \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"$API_ENDPOINT" 2> response_errors.txt
)
echo ">> HTTP Status Code: $HTTP_STATUS"
echo ">> Response Output ..."
echo "----"
cat response_body.json
echo "----"
cat response_headers.txt
echo "----"
cat response_errors.txt
echo "----"
if [ "$HTTP_STATUS" == "200" ]; then
echo ">> Pull Request merged successfully!"
echo "pull-request-operation=merged" >> $GITEA_OUTPUT
else
echo ">> Failed to create PR, HTTP status code: $HTTP_STATUS"
echo "pull-request-operation=failed" >> $GITEA_OUTPUT
exit 1
fi
echo "----"
- name: Cleanup Branch
if: failure()
env:
BRANCH_NAME: ${{ steps.prepare-manifest-branch.outputs.BRANCH_NAME }}
run: |
cd ${MANIFEST_DIR}
echo ">> Removing branch: ${BRANCH_NAME}"
git push origin --delete ${BRANCH_NAME}
echo "----"
- name: ntfy Merged
uses: niniyas/ntfy-action@master
if: steps.merge-changes.outputs.pull-request-operation == 'merged'
with:
url: "${{ secrets.NTFY_URL }}"
topic: "${{ secrets.NTFY_TOPIC }}"
title: "Manifest Render PR Merged - Infrastructure"
priority: 3
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,successfully,completed
details: "Automerge Manifest rendering for Infrastructure!"
icon: "https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png"
actions: '[{"action": "view", "label": "Open Gitea", "url": "${{ steps.create-pull-request.outputs.pull-request-url }}", "clear": true}]'
- name: ntfy Failed
uses: niniyas/ntfy-action@master
if: failure()
with:
url: "${{ secrets.NTFY_URL }}"
topic: "${{ secrets.NTFY_TOPIC }}"
title: "Manifest Render Failure - Infrastructure"
priority: 4
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,failed
details: "Automerge Manifest rendering for Infrastructure has failed!"
icon: "https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png"
actions: '[{"action": "view", "label": "Open Gitea", "url": "https://gitea.alexlebens.dev/alexlebens/infrastructure/actions?workflow=render-manifests-automerge.yaml", "clear": true}]'
image: true

View File

@@ -0,0 +1,390 @@
name: render-manifests-dispatch
on:
schedule:
- cron: '0 3 * * *'
workflow_dispatch:
env:
CLUSTER: cl01tl
BASE_BRANCH: manifests
BRANCH_NAME: auto/update-manifests
ASSIGNEE: alexlebens
MAIN_DIR: /workspace/alexlebens/infrastructure/infrastructure
MANIFEST_DIR: /workspace/alexlebens/infrastructure/infrastructure-manifests
jobs:
render-manifests-dispatch:
runs-on: ubuntu-js
steps:
- name: Checkout Main
uses: actions/checkout@v6
with:
path: infrastructure
fetch-depth: 0
- name: Checkout Manifests
uses: actions/checkout@v6
with:
ref: manifests
path: infrastructure-manifests
- name: Set up Helm
uses: azure/setup-helm@v4
with:
token: ${{ secrets.GITEA_TOKEN }}
version: v3.17.2 # Pending https://github.com/helm/helm/pull/30743
cache: true
- name: Configure Kubeconfig
uses: azure/k8s-set-context@v4
with:
method: kubeconfig
kubeconfig: ${{ secrets.KUBECONFIG }}
- name: Prepare Manifest Branch
run: |
cd ${MANIFEST_DIR}
echo ">> Configure git to use gitea-bot as user ..."
git config user.name "gitea-bot"
git config user.email "gitea-bot@alexlebens.net"
echo ">> Checking if PR branch exists ..."
if [[ $(git ls-remote --heads origin "${BRANCH_NAME}" | wc -l) -gt 0 ]]; then
echo ">> Branch '${BRANCH_NAME}' exists, pulling changes ..."
git fetch origin "${BRANCH_NAME}"
git checkout "${BRANCH_NAME}"
git pull --rebase
else
echo ">> Branch '${BRANCH_NAME}' does not exist, creating ..."
git checkout -b $BRANCH_NAME
fi
echo "----"
- name: Check which Directories have Changes
id: check-dir-changes
run: |
cd ${MAIN_DIR}
RENDER_DIR=()
echo ">> Triggered on dispatch, will check all paths ..."
RENDER_DIR+=$(ls clusters/cl01tl/helm/)
if [ -n "${RENDER_DIR}" ]; then
echo ">> Directories to Render:"
echo "$(echo "${RENDER_DIR}" | sort -u)"
echo "----"
echo "changes-detected=true" >> $GITEA_OUTPUT
echo "render-dir<<EOF" >> $GITEA_OUTPUT
echo "$(echo "${RENDER_DIR}" | sort -u)" >> $GITEA_OUTPUT
echo "EOF" >> $GITEA_OUTPUT
else
echo "changes-detected=false" >> $GITEA_OUTPUT
fi
- name: Add Repositories
if: steps.check-dir-changes.outputs.changes-detected == 'true'
env:
RENDER_DIR: ${{ steps.check-dir-changes.outputs.render-dir }}
run: |
cd ${MAIN_DIR}
echo ">> Adding repositories for chart dependencies ..."
for dir in ${RENDER_DIR}; do
helm dependency list --max-col-width 120 ${MAIN_DIR}/clusters/${CLUSTER}/helm/$dir 2> /dev/null \
| tail +2 | head -n -1 \
| awk '{ print "helm repo add " $1 " " $3 }' \
| while read cmd; do
if [[ "$cmd" == "*oci://*" ]]; then
echo ">> Ignoring OCI repo"
else
echo "$cmd" | sh;
fi
done || true
done
if helm repo list | tail +2 | read -r; then
echo ">> Update repository cache ..."
helm repo update
fi
echo "----"
- name: Render Helm Manifests
id: render-manifests
if: steps.check-dir-changes.outputs.changes-detected == 'true'
env:
RENDER_DIR: ${{ steps.check-dir-changes.outputs.render-dir }}
run: |
cd ${MAIN_DIR}
echo ">> Rendering Manifests ..."
for dir in ${RENDER_DIR}; do
chart_path=${MAIN_DIR}/clusters/${CLUSTER}/helm/$dir
chart_name=$(basename "$chart_path")
echo ""
echo ""
echo ">> Rendering chart: $chart_name"
echo ">> Chart path $chart_path"
if [ -f "$chart_path/Chart.yaml" ]; then
OUTPUT_FOLDER="${MANIFEST_DIR}/clusters/${CLUSTER}/manifests/$chart_name/"
TEMPLATE=""
mkdir -p ${MANIFEST_DIR}/clusters/${CLUSTER}/manifests/$chart_name
cd $chart_path
echo ""
echo ">> Updating helm dependency ..."
helm dependency update --skip-refresh
echo ""
echo ">> Building helm dependency ..."
helm dependency build --skip-refresh
echo ""
echo ">> Linting helm ..."
helm lint --namespace "$chart_name"
echo ""
echo ">> Rendering templates ..."
case "$chart_name" in
"stack")
echo ""
echo ">> Special Rendering for stack into argocd namespace ..."
TEMPLATE=$(helm template $chart_name ./ --namespace argocd --include-crds --dry-run=server --api-versions "gateway.networking.k8s.io/v1/HTTPRoute")
;;
"cilium" | "coredns" | "metrics-server" |"prometheus-operator-crds")
echo ""
echo ">> Special Rendering for $chart_name into kube-system namespace ..."
TEMPLATE=$(helm template $chart_name ./ --namespace kube-system --include-crds --dry-run=server --api-versions "gateway.networking.k8s.io/v1/HTTPRoute")
;;
*)
echo ""
echo ">> Standard Rendering for $chart_name ..."
TEMPLATE=$(helm template "$chart_name" ./ --namespace "$chart_name" --include-crds --dry-run=server --api-versions "gateway.networking.k8s.io/v1/HTTPRoute")
;;
esac
echo ""
echo ">> Formating rendered template ..."
echo "$TEMPLATE" | yq '... comments=""' | yq 'select(. != null)' | yq -s '"'"$OUTPUT_FOLDER"'" + .kind + "-" + .metadata.name + ".yaml"'
# Strip comments again to ensure formatting correctness
for file in "$OUTPUT_FOLDER"/*; do
yq -i '... comments=""' $file
done
echo ""
echo ">> Manifests for $chart_name rendered to $OUTPUT_FOLDER"
ls $OUTPUT_FOLDER
echo ""
else
echo ""
echo ">> Directory $chart_path does not contain a Chart.yaml. Skipping ..."
echo ""
fi
done
echo "----"
- name: Check for Changes
id: check-changes
if: steps.check-dir-changes.outputs.changes-detected == 'true'
run: |
cd ${MANIFEST_DIR}
GIT_CHANGES=$(git status --porcelain)
if [ -n "$GIT_CHANGES" ]; then
echo ">> Changes detected"
git status --porcelain
echo "changes-detected=true" >> $GITEA_OUTPUT
else
echo ">> No changes detected, skipping PR creation"
fi
echo "----"
- name: Commit and Push Changes
id: commit-push
if: steps.check-changes.outputs.changes-detected == 'true'
run: |
cd ${MANIFEST_DIR}
echo ">> Commiting changes to ${BRANCH_NAME} ..."
git add .
git commit -m "chore: Update manifests after change"
REPO_URL="${{ secrets.REPO_URL }}/${{ gitea.repository }}"
echo ">> Pushing changes to $REPO_URL ..."
git push -u "https://oauth2:${{ secrets.BOT_TOKEN }}@$(echo $REPO_URL | sed -e 's|https://||')" ${BRANCH_NAME}
echo "----"
echo "HEAD_BRANCH=${BRANCH_NAME}" >> $GITEA_OUTPUT
echo "push=true" >> $GITEA_OUTPUT
- name: Check for Pull Request
id: check-for-pull-requst
if: steps.commit-push.outputs.push == 'true'
env:
GITEA_TOKEN: ${{ secrets.BOT_TOKEN }}
GITEA_URL: ${{ secrets.REPO_URL }}
HEAD_BRANCH: ${{ steps.commit-push.outputs.HEAD_BRANCH }}
run: |
cd ${MANIFEST_DIR}
API_ENDPOINT="${GITEA_URL}/api/v1/repos/${{ gitea.repository }}/pulls?base_branch=${BASE_BRANCH}&state=open&page=1"
echo ">> Checking if PR from branch ${HEAD_BRANCH} into ${BASE_BRANCH}"
echo ">> With Endpoint of:"
echo "$API_ENDPOINT"
HTTP_STATUS=$(
curl -X GET \
--silent \
--write-out '%{http_code}' \
--output response_body.json \
--dump-header response_headers.txt \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"$API_ENDPOINT" 2> response_errors.txt
)
echo ">> HTTP Status Code: $HTTP_STATUS"
echo ">> Response Output ..."
echo "----"
cat response_body.json
echo "----"
cat response_headers.txt
echo "----"
cat response_errors.txt
echo "----"
if [ "$HTTP_STATUS" == "200" ] && [ "$(cat response_body.json | jq -r .[0].state)" == "open" ]; then
echo ">> Pull Request has been found open, will update"
PR_INDEX=$(cat response_body.json | jq -r .[0].number)
echo "pull-request-exists=${PR_INDEX}" >> $GITEA_OUTPUT
echo "pull-request-index=true" >> $GITEA_OUTPUT
elif [ "$HTTP_STATUS" == "200" ] && [ "$(cat response_body.json | jq -r .[0].state)" == "closed" ]; then
echo ">> Pull Request found, but was closed"
echo "pull-request-exists=false" >> $GITEA_OUTPUT
else
echo ">> Pull Request not found"
echo "pull-request-exists=false" >> $GITEA_OUTPUT
fi
echo "----"
- name: Create Pull Request
id: create-pull-request
if: steps.commit-push.outputs.push == 'true' && steps.check-for-pull-requst.outputs.pull-request-exists == 'false'
env:
GITEA_TOKEN: ${{ secrets.BOT_TOKEN }}
GITEA_URL: ${{ secrets.REPO_URL }}
HEAD_BRANCH: ${{ steps.commit-push.outputs.HEAD_BRANCH }}
run: |
cd ${MANIFEST_DIR}
API_ENDPOINT="${GITEA_URL}/api/v1/repos/${{ gitea.repository }}/pulls"
PAYLOAD=$( jq -n \
--arg head "${HEAD_BRANCH}" \
--arg base "${BASE_BRANCH}" \
--arg assignee "${ASSIGNEE}" \
--arg title "Automated Manifest Update" \
--arg body "This PR contains newly rendered Kubernetes manifests automatically generated by the CI workflow." \
'{head: $head, base: $base, assignee: $assignee, title: $title, body: $body}' )
echo ">> Creating PR from branch ${HEAD_BRANCH} into ${BASE_BRANCH}"
echo ">> With Endpoint of:"
echo "$API_ENDPOINT"
echo ">> With Payload of:"
echo "$PAYLOAD"
HTTP_STATUS=$(
curl -X POST \
--silent \
--write-out '%{http_code}' \
--output response_body.json \
--dump-header response_headers.txt \
--data "$PAYLOAD" \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"$API_ENDPOINT" 2> response_errors.txt
)
echo ">> HTTP Status Code: $HTTP_STATUS"
echo ">> Response Output ..."
echo "----"
cat response_body.json
echo "----"
cat response_headers.txt
echo "----"
cat response_errors.txt
echo "----"
if [ "$HTTP_STATUS" == "201" ]; then
echo ">> Pull Request created successfully!"
PR_URL=$(cat response_body.json | jq -r .html_url)
echo "pull-request-url=${PR_URL}" >> $GITEA_OUTPUT
PR_ID=$(cat response_body.json | jq -r .id)
echo "pull-request-id=${PR_ID}" >> $GITEA_OUTPUT
echo "pull-request-operation=created" >> $GITEA_OUTPUT
elif [ "$HTTP_STATUS" == "422" ]; then
echo ">> Failed to create PR (HTTP 422: Unprocessable Entity), PR may already exist"
elif [ "$HTTP_STATUS" == "409" ]; then
echo ">> Failed to create PR (HTTP 409: Conflict), PR already exists"
else
echo ">> Failed to create PR, HTTP status code: $HTTP_STATUS"
exit 1
fi
echo "----"
- name: ntfy Created
uses: niniyas/ntfy-action@master
if: steps.create-pull-request.outputs.pull-request-operation == 'created'
with:
url: "${{ secrets.NTFY_URL }}"
topic: "${{ secrets.NTFY_TOPIC }}"
title: "Manifest Render PR Created - Infrastructure"
priority: 3
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,successfully,completed
details: "Manifest rendering for Infrastructure has created a new Pull Request with ID: ${{ steps.create-pull-request.outputs.pull-request-id }}!"
icon: "https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png"
actions: '[{"action": "view", "label": "Open Gitea", "url": "${{ steps.create-pull-request.outputs.pull-request-url }}", "clear": true}]'
- name: ntfy Failed
uses: niniyas/ntfy-action@master
if: failure()
with:
url: "${{ secrets.NTFY_URL }}"
topic: "${{ secrets.NTFY_TOPIC }}"
title: "Manifest Render Failure - Infrastructure"
priority: 4
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,failed
details: "Manifest rendering for Infrastructure has failed!"
icon: "https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png"
actions: '[{"action": "view", "label": "Open Gitea", "url": "https://gitea.alexlebens.dev/alexlebens/infrastructure/actions?workflow=render-manifests.yaml", "clear": true}]'
image: true

View File

@@ -0,0 +1,425 @@
name: render-manifests-merge
on:
pull_request:
branches:
- main
paths:
- 'clusters/cl01tl/helm/**'
types:
- closed
env:
CLUSTER: cl01tl
BASE_BRANCH: manifests
BRANCH_NAME: auto/update-manifests
ASSIGNEE: alexlebens
MAIN_DIR: /workspace/alexlebens/infrastructure/infrastructure
MANIFEST_DIR: /workspace/alexlebens/infrastructure/infrastructure-manifests
jobs:
render-manifests-merge:
runs-on: ubuntu-js
if: ${{ (github.event.pull_request.merged == true) && !(contains(github.event.pull_request.labels.*.name, 'automerge')) }}
steps:
- name: Checkout Main
uses: actions/checkout@v6
with:
path: infrastructure
fetch-depth: 0
- name: Checkout Manifests
uses: actions/checkout@v6
with:
ref: manifests
path: infrastructure-manifests
- name: Set up Helm
uses: azure/setup-helm@v4
with:
token: ${{ secrets.GITEA_TOKEN }}
version: v3.17.2 # Pending https://github.com/helm/helm/pull/30743
cache: true
- name: Configure Kubeconfig
uses: azure/k8s-set-context@v4
with:
method: kubeconfig
kubeconfig: ${{ secrets.KUBECONFIG }}
- name: Prepare Manifest Branch
run: |
cd ${MANIFEST_DIR}
echo ">> Configure git to use gitea-bot as user ..."
git config user.name "gitea-bot"
git config user.email "gitea-bot@alexlebens.net"
echo ">> Checking if PR branch exists ..."
if [[ $(git ls-remote --heads origin "${BRANCH_NAME}" | wc -l) -gt 0 ]]; then
echo ">> Branch '${BRANCH_NAME}' exists, pulling changes ..."
git fetch origin "${BRANCH_NAME}"
git checkout "${BRANCH_NAME}"
git pull --rebase
else
echo ">> Branch '${BRANCH_NAME}' does not exist, creating ..."
git checkout -b $BRANCH_NAME
fi
echo "----"
- name: Check which Directories have Changes
id: check-dir-changes
run: |
cd ${MAIN_DIR}
RENDER_DIR=()
echo ">> Checking for changes from HEAD^..HEAD ..."
GIT_DIFF=$(git diff --name-only HEAD^..HEAD | xargs -I {} dirname {} | sort -u | grep -E "clusters/[^/]+/helm/[^/]+")
if [ -n "${GIT_DIFF}" ]; then
echo ">> Changes detected:"
echo "$GIT_DIFF"
for path in $GIT_DIFF; do
RENDER_DIR+=$(echo "$path" | awk -F '/' '{print $4}')
RENDER_DIR+=$(echo " ")
done
else
echo ">> No changes detected"
fi
if [ -n "${RENDER_DIR}" ]; then
echo ">> Directories to Render:"
echo "$(echo "${RENDER_DIR}" | sort -u)"
echo "----"
echo "changes-detected=true" >> $GITEA_OUTPUT
echo "render-dir<<EOF" >> $GITEA_OUTPUT
echo "$(echo "${RENDER_DIR}" | sort -u)" >> $GITEA_OUTPUT
echo "EOF" >> $GITEA_OUTPUT
else
echo "changes-detected=false" >> $GITEA_OUTPUT
fi
- name: Add Repositories
if: steps.check-dir-changes.outputs.changes-detected == 'true'
env:
RENDER_DIR: ${{ steps.check-dir-changes.outputs.render-dir }}
run: |
cd ${MAIN_DIR}
echo ">> Adding repositories for chart dependencies ..."
for dir in ${RENDER_DIR}; do
helm dependency list --max-col-width 120 ${MAIN_DIR}/clusters/${CLUSTER}/helm/$dir 2> /dev/null \
| tail +2 | head -n -1 \
| awk '{ print "helm repo add " $1 " " $3 }' \
| while read cmd; do
if [[ "$cmd" == "*oci://*" ]]; then
echo ">> Ignoring OCI repo"
else
echo "$cmd" | sh;
fi
done || true
done
if helm repo list | tail +2 | read -r; then
echo ">> Update repository cache ..."
helm repo update
fi
echo "----"
- name: Remove Changed Manifest Files
if: steps.check-dir-changes.outputs.changes-detected == 'true'
env:
RENDER_DIR: ${{ steps.check-dir-changes.outputs.render-dir }}
run: |
cd ${MANIFEST_DIR}
echo ">> Remove manfiest files and rebuild from source ..."
for dir in ${RENDER_DIR}; do
chart_path=${MANIFEST_DIR}/clusters/${CLUSTER}/manifests/$dir
echo "$chart_path"
rm -rf $chart_path/*
done
echo "----"
- name: Render Helm Manifests
id: render-manifests
if: steps.check-dir-changes.outputs.changes-detected == 'true'
env:
RENDER_DIR: ${{ steps.check-dir-changes.outputs.render-dir }}
run: |
cd ${MAIN_DIR}
echo ">> Rendering Manifests ..."
for dir in ${RENDER_DIR}; do
chart_path=${MAIN_DIR}/clusters/${CLUSTER}/helm/$dir
chart_name=$(basename "$chart_path")
echo ""
echo ""
echo ">> Rendering chart: $chart_name"
echo ">> Chart path $chart_path"
if [ -f "$chart_path/Chart.yaml" ]; then
OUTPUT_FOLDER="${MANIFEST_DIR}/clusters/${CLUSTER}/manifests/$chart_name/"
TEMPLATE=""
mkdir -p ${MANIFEST_DIR}/clusters/${CLUSTER}/manifests/$chart_name
cd $chart_path
echo ""
echo ">> Updating helm dependency ..."
helm dependency update --skip-refresh
echo ""
echo ">> Building helm dependency ..."
helm dependency build --skip-refresh
echo ""
echo ">> Linting helm ..."
helm lint --namespace "$chart_name"
echo ""
echo ">> Rendering templates ..."
case "$chart_name" in
"stack")
echo ""
echo ">> Special Rendering for stack into argocd namespace ..."
TEMPLATE=$(helm template $chart_name ./ --namespace argocd --include-crds --dry-run=server --api-versions "gateway.networking.k8s.io/v1/HTTPRoute")
;;
"cilium" | "coredns" | "metrics-server" |"prometheus-operator-crds")
echo ""
echo ">> Special Rendering for $chart_name into kube-system namespace ..."
TEMPLATE=$(helm template $chart_name ./ --namespace kube-system --include-crds --dry-run=server --api-versions "gateway.networking.k8s.io/v1/HTTPRoute")
;;
*)
echo ""
echo ">> Standard Rendering for $chart_name ..."
TEMPLATE=$(helm template "$chart_name" ./ --namespace "$chart_name" --include-crds --dry-run=server --api-versions "gateway.networking.k8s.io/v1/HTTPRoute")
;;
esac
echo ""
echo ">> Formating rendered template ..."
echo "$TEMPLATE" | yq '... comments=""' | yq 'select(. != null)' | yq -s '"'"$OUTPUT_FOLDER"'" + .kind + "-" + .metadata.name + ".yaml"'
# Strip comments again to ensure formatting correctness
for file in "$OUTPUT_FOLDER"/*; do
yq -i '... comments=""' $file
done
echo ""
echo ">> Manifests for $chart_name rendered to $OUTPUT_FOLDER"
ls $OUTPUT_FOLDER
echo ""
else
echo ""
echo ">> Directory $chart_path does not contain a Chart.yaml. Skipping ..."
echo ""
fi
done
echo "----"
- name: Check for Changes
id: check-changes
if: steps.check-dir-changes.outputs.changes-detected == 'true'
run: |
cd ${MANIFEST_DIR}
GIT_CHANGES=$(git status --porcelain)
if [ -n "$GIT_CHANGES" ]; then
echo ">> Changes detected"
git status --porcelain
echo "changes-detected=true" >> $GITEA_OUTPUT
else
echo ">> No changes detected, skipping PR creation"
fi
echo "----"
- name: Commit and Push Changes
id: commit-push
if: steps.check-changes.outputs.changes-detected == 'true'
run: |
cd ${MANIFEST_DIR}
echo ">> Commiting changes to ${BRANCH_NAME} ..."
git add .
git commit -m "chore: Update manifests after change"
REPO_URL="${{ secrets.REPO_URL }}/${{ gitea.repository }}"
echo ">> Pushing changes to $REPO_URL ..."
git push -u "https://oauth2:${{ secrets.BOT_TOKEN }}@$(echo $REPO_URL | sed -e 's|https://||')" ${BRANCH_NAME}
echo "----"
echo "HEAD_BRANCH=${BRANCH_NAME}" >> $GITEA_OUTPUT
echo "push=true" >> $GITEA_OUTPUT
- name: Check for Pull Request
id: check-for-pull-requst
if: steps.commit-push.outputs.push == 'true'
env:
GITEA_TOKEN: ${{ secrets.BOT_TOKEN }}
GITEA_URL: ${{ secrets.REPO_URL }}
HEAD_BRANCH: ${{ steps.commit-push.outputs.HEAD_BRANCH }}
run: |
cd ${MANIFEST_DIR}
API_ENDPOINT="${GITEA_URL}/api/v1/repos/${{ gitea.repository }}/pulls?base_branch=${BASE_BRANCH}&state=open&page=1"
echo ">> Checking if PR from branch ${HEAD_BRANCH} into ${BASE_BRANCH}"
echo ">> With Endpoint of:"
echo "$API_ENDPOINT"
HTTP_STATUS=$(
curl -X GET \
--silent \
--write-out '%{http_code}' \
--output response_body.json \
--dump-header response_headers.txt \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"$API_ENDPOINT" 2> response_errors.txt
)
echo ">> HTTP Status Code: $HTTP_STATUS"
echo ">> Response Output ..."
echo "----"
cat response_body.json
echo "----"
cat response_headers.txt
echo "----"
cat response_errors.txt
echo "----"
if [ "$HTTP_STATUS" == "200" ] && [ "$(cat response_body.json | jq -r .[0].state)" == "open" ]; then
echo ">> Pull Request has been found open, will update"
PR_INDEX=$(cat response_body.json | jq -r .[0].number)
echo "pull-request-exists=${PR_INDEX}" >> $GITEA_OUTPUT
echo "pull-request-index=true" >> $GITEA_OUTPUT
elif [ "$HTTP_STATUS" == "200" ] && [ "$(cat response_body.json | jq -r .[0].state)" == "closed" ]; then
echo ">> Pull Request found, but was closed"
echo "pull-request-exists=false" >> $GITEA_OUTPUT
else
echo ">> Pull Request not found"
echo "pull-request-exists=false" >> $GITEA_OUTPUT
fi
echo "----"
- name: Create Pull Request
id: create-pull-request
if: steps.commit-push.outputs.push == 'true' && steps.check-for-pull-requst.outputs.pull-request-exists == 'false'
env:
GITEA_TOKEN: ${{ secrets.BOT_TOKEN }}
GITEA_URL: ${{ secrets.REPO_URL }}
HEAD_BRANCH: ${{ steps.commit-push.outputs.HEAD_BRANCH }}
run: |
cd ${MANIFEST_DIR}
API_ENDPOINT="${GITEA_URL}/api/v1/repos/${{ gitea.repository }}/pulls"
PAYLOAD=$( jq -n \
--arg head "${HEAD_BRANCH}" \
--arg base "${BASE_BRANCH}" \
--arg assignee "${ASSIGNEE}" \
--arg title "Automated Manifest Update" \
--arg body "This PR contains newly rendered Kubernetes manifests automatically generated by the CI workflow." \
'{head: $head, base: $base, assignee: $assignee, title: $title, body: $body}' )
echo ">> Creating PR from branch ${HEAD_BRANCH} into ${BASE_BRANCH}"
echo ">> With Endpoint of:"
echo "$API_ENDPOINT"
echo ">> With Payload of:"
echo "$PAYLOAD"
HTTP_STATUS=$(
curl -X POST \
--silent \
--write-out '%{http_code}' \
--output response_body.json \
--dump-header response_headers.txt \
--data "$PAYLOAD" \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"$API_ENDPOINT" 2> response_errors.txt
)
echo ">> HTTP Status Code: $HTTP_STATUS"
echo ">> Response Output ..."
echo "----"
cat response_body.json
echo "----"
cat response_headers.txt
echo "----"
cat response_errors.txt
echo "----"
if [ "$HTTP_STATUS" == "201" ]; then
echo ">> Pull Request created successfully!"
PR_URL=$(cat response_body.json | jq -r .html_url)
echo "pull-request-url=${PR_URL}" >> $GITEA_OUTPUT
PR_ID=$(cat response_body.json | jq -r .id)
echo "pull-request-id=${PR_ID}" >> $GITEA_OUTPUT
echo "pull-request-operation=created" >> $GITEA_OUTPUT
elif [ "$HTTP_STATUS" == "422" ]; then
echo ">> Failed to create PR (HTTP 422: Unprocessable Entity), PR may already exist"
elif [ "$HTTP_STATUS" == "409" ]; then
echo ">> Failed to create PR (HTTP 409: Conflict), PR already exists"
else
echo ">> Failed to create PR, HTTP status code: $HTTP_STATUS"
exit 1
fi
echo "----"
- name: ntfy Created
uses: niniyas/ntfy-action@master
if: steps.create-pull-request.outputs.pull-request-operation == 'created'
with:
url: "${{ secrets.NTFY_URL }}"
topic: "${{ secrets.NTFY_TOPIC }}"
title: "Manifest Render PR Created - Infrastructure"
priority: 3
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,successfully,completed
details: "Manifest rendering for Infrastructure has created a new Pull Request with ID: ${{ steps.create-pull-request.outputs.pull-request-id }}!"
icon: "https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png"
actions: '[{"action": "view", "label": "Open Gitea", "url": "${{ steps.create-pull-request.outputs.pull-request-url }}", "clear": true}]'
- name: ntfy Failed
uses: niniyas/ntfy-action@master
if: failure()
with:
url: "${{ secrets.NTFY_URL }}"
topic: "${{ secrets.NTFY_TOPIC }}"
title: "Manifest Render Failure - Infrastructure"
priority: 4
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,failed
details: "Manifest rendering for Infrastructure has failed!"
icon: "https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png"
actions: '[{"action": "view", "label": "Open Gitea", "url": "https://gitea.alexlebens.dev/alexlebens/infrastructure/actions?workflow=render-manifests.yaml", "clear": true}]'
image: true

View File

@@ -0,0 +1,423 @@
name: render-manifests-push
on:
push:
branches:
- main
paths:
- 'clusters/cl01tl/helm/**'
env:
CLUSTER: cl01tl
BASE_BRANCH: manifests
BRANCH_NAME: auto/update-manifests
ASSIGNEE: alexlebens
MAIN_DIR: /workspace/alexlebens/infrastructure/infrastructure
MANIFEST_DIR: /workspace/alexlebens/infrastructure/infrastructure-manifests
jobs:
render-manifests-push:
runs-on: ubuntu-js
if: gitea.event.commits[0].author.username != 'renovate-bot'
steps:
- name: Checkout Main
uses: actions/checkout@v6
with:
path: infrastructure
fetch-depth: 0
- name: Checkout Manifests
uses: actions/checkout@v6
with:
ref: manifests
path: infrastructure-manifests
- name: Set up Helm
uses: azure/setup-helm@v4
with:
token: ${{ secrets.GITEA_TOKEN }}
version: v3.17.2 # Pending https://github.com/helm/helm/pull/30743
cache: true
- name: Configure Kubeconfig
uses: azure/k8s-set-context@v4
with:
method: kubeconfig
kubeconfig: ${{ secrets.KUBECONFIG }}
- name: Prepare Manifest Branch
run: |
cd ${MANIFEST_DIR}
echo ">> Configure git to use gitea-bot as user ..."
git config user.name "gitea-bot"
git config user.email "gitea-bot@alexlebens.net"
echo ">> Checking if PR branch exists ..."
if [[ $(git ls-remote --heads origin "${BRANCH_NAME}" | wc -l) -gt 0 ]]; then
echo ">> Branch '${BRANCH_NAME}' exists, pulling changes ..."
git fetch origin "${BRANCH_NAME}"
git checkout "${BRANCH_NAME}"
git pull --rebase
else
echo ">> Branch '${BRANCH_NAME}' does not exist, creating ..."
git checkout -b $BRANCH_NAME
fi
echo "----"
- name: Check which Directories have Changes
id: check-dir-changes
run: |
cd ${MAIN_DIR}
RENDER_DIR=()
echo ">> Checking for changes ..."
GIT_DIFF=$(git diff --name-only ${{ gitea.event.before }}..HEAD | xargs -I {} dirname {} | sort -u | grep -E "clusters/[^/]+/helm/[^/]+")
if [ -n "${GIT_DIFF}" ]; then
echo ">> Changes detected:"
echo "$GIT_DIFF"
for path in $GIT_DIFF; do
RENDER_DIR+=$(echo "$path" | awk -F '/' '{print $4}')
RENDER_DIR+=$(echo " ")
done
else
echo ">> No changes detected"
fi
if [ -n "${RENDER_DIR}" ]; then
echo ">> Directories to Render:"
echo "$(echo "${RENDER_DIR}" | sort -u)"
echo "----"
echo "changes-detected=true" >> $GITEA_OUTPUT
echo "render-dir<<EOF" >> $GITEA_OUTPUT
echo "$(echo "${RENDER_DIR}" | sort -u)" >> $GITEA_OUTPUT
echo "EOF" >> $GITEA_OUTPUT
else
echo "changes-detected=false" >> $GITEA_OUTPUT
fi
- name: Add Repositories
if: steps.check-dir-changes.outputs.changes-detected == 'true'
env:
RENDER_DIR: ${{ steps.check-dir-changes.outputs.render-dir }}
run: |
cd ${MAIN_DIR}
echo ">> Adding repositories for chart dependencies ..."
for dir in ${RENDER_DIR}; do
helm dependency list --max-col-width 120 ${MAIN_DIR}/clusters/${CLUSTER}/helm/$dir 2> /dev/null \
| tail +2 | head -n -1 \
| awk '{ print "helm repo add " $1 " " $3 }' \
| while read cmd; do
if [[ "$cmd" == "*oci://*" ]]; then
echo ">> Ignoring OCI repo"
else
echo "$cmd" | sh;
fi
done || true
done
if helm repo list | tail +2 | read -r; then
echo ">> Update repository cache ..."
helm repo update
fi
echo "----"
- name: Remove Changed Manifest Files
if: steps.check-dir-changes.outputs.changes-detected == 'true'
env:
RENDER_DIR: ${{ steps.check-dir-changes.outputs.render-dir }}
run: |
cd ${MANIFEST_DIR}
echo ">> Remove manfiest files and rebuild from source ..."
for dir in ${RENDER_DIR}; do
chart_path=${MANIFEST_DIR}/clusters/${CLUSTER}/manifests/$dir
echo "$chart_path"
rm -rf $chart_path/*
done
echo "----"
- name: Render Helm Manifests
id: render-manifests
if: steps.check-dir-changes.outputs.changes-detected == 'true'
env:
RENDER_DIR: ${{ steps.check-dir-changes.outputs.render-dir }}
run: |
cd ${MAIN_DIR}
echo ">> Rendering Manifests ..."
for dir in ${RENDER_DIR}; do
chart_path=${MAIN_DIR}/clusters/${CLUSTER}/helm/$dir
chart_name=$(basename "$chart_path")
echo ""
echo ""
echo ">> Rendering chart: $chart_name"
echo ">> Chart path $chart_path"
if [ -f "$chart_path/Chart.yaml" ]; then
OUTPUT_FOLDER="${MANIFEST_DIR}/clusters/${CLUSTER}/manifests/$chart_name/"
TEMPLATE=""
mkdir -p ${MANIFEST_DIR}/clusters/${CLUSTER}/manifests/$chart_name
cd $chart_path
echo ""
echo ">> Updating helm dependency ..."
helm dependency update --skip-refresh
echo ""
echo ">> Building helm dependency ..."
helm dependency build --skip-refresh
echo ""
echo ">> Linting helm ..."
helm lint --namespace "$chart_name"
echo ""
echo ">> Rendering templates ..."
case "$chart_name" in
"stack")
echo ""
echo ">> Special Rendering for stack into argocd namespace ..."
TEMPLATE=$(helm template $chart_name ./ --namespace argocd --include-crds --dry-run=server --api-versions "gateway.networking.k8s.io/v1/HTTPRoute")
;;
"cilium" | "coredns" | "metrics-server" |"prometheus-operator-crds")
echo ""
echo ">> Special Rendering for $chart_name into kube-system namespace ..."
TEMPLATE=$(helm template $chart_name ./ --namespace kube-system --include-crds --dry-run=server --api-versions "gateway.networking.k8s.io/v1/HTTPRoute")
;;
*)
echo ""
echo ">> Standard Rendering for $chart_name ..."
TEMPLATE=$(helm template "$chart_name" ./ --namespace "$chart_name" --include-crds --dry-run=server --api-versions "gateway.networking.k8s.io/v1/HTTPRoute")
;;
esac
echo ""
echo ">> Formating rendered template ..."
echo "$TEMPLATE" | yq '... comments=""' | yq 'select(. != null)' | yq -s '"'"$OUTPUT_FOLDER"'" + .kind + "-" + .metadata.name + ".yaml"'
# Strip comments again to ensure formatting correctness
for file in "$OUTPUT_FOLDER"/*; do
yq -i '... comments=""' $file
done
echo ""
echo ">> Manifests for $chart_name rendered to $OUTPUT_FOLDER"
ls $OUTPUT_FOLDER
echo ""
else
echo ""
echo ">> Directory $chart_path does not contain a Chart.yaml. Skipping ..."
echo ""
fi
done
echo "----"
- name: Check for Changes
id: check-changes
if: steps.check-dir-changes.outputs.changes-detected == 'true'
run: |
cd ${MANIFEST_DIR}
GIT_CHANGES=$(git status --porcelain)
if [ -n "$GIT_CHANGES" ]; then
echo ">> Changes detected"
git status --porcelain
echo "changes-detected=true" >> $GITEA_OUTPUT
else
echo ">> No changes detected, skipping PR creation"
fi
echo "----"
- name: Commit and Push Changes
id: commit-push
if: steps.check-changes.outputs.changes-detected == 'true'
run: |
cd ${MANIFEST_DIR}
echo ">> Commiting changes to ${BRANCH_NAME} ..."
git add .
git commit -m "chore: Update manifests after change"
REPO_URL="${{ secrets.REPO_URL }}/${{ gitea.repository }}"
echo ">> Pushing changes to $REPO_URL ..."
git push -u "https://oauth2:${{ secrets.BOT_TOKEN }}@$(echo $REPO_URL | sed -e 's|https://||')" ${BRANCH_NAME}
echo "----"
echo "HEAD_BRANCH=${BRANCH_NAME}" >> $GITEA_OUTPUT
echo "push=true" >> $GITEA_OUTPUT
- name: Check for Pull Request
id: check-for-pull-requst
if: steps.commit-push.outputs.push == 'true'
env:
GITEA_TOKEN: ${{ secrets.BOT_TOKEN }}
GITEA_URL: ${{ secrets.REPO_URL }}
HEAD_BRANCH: ${{ steps.commit-push.outputs.HEAD_BRANCH }}
run: |
cd ${MANIFEST_DIR}
API_ENDPOINT="${GITEA_URL}/api/v1/repos/${{ gitea.repository }}/pulls?base_branch=${BASE_BRANCH}&state=open&page=1"
echo ">> Checking if PR from branch ${HEAD_BRANCH} into ${BASE_BRANCH}"
echo ">> With Endpoint of:"
echo "$API_ENDPOINT"
HTTP_STATUS=$(
curl -X GET \
--silent \
--write-out '%{http_code}' \
--output response_body.json \
--dump-header response_headers.txt \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"$API_ENDPOINT" 2> response_errors.txt
)
echo ">> HTTP Status Code: $HTTP_STATUS"
echo ">> Response Output ..."
echo "----"
cat response_body.json
echo "----"
cat response_headers.txt
echo "----"
cat response_errors.txt
echo "----"
if [ "$HTTP_STATUS" == "200" ] && [ "$(cat response_body.json | jq -r .[0].state)" == "open" ]; then
echo ">> Pull Request has been found open, will update"
PR_INDEX=$(cat response_body.json | jq -r .[0].number)
echo "pull-request-exists=${PR_INDEX}" >> $GITEA_OUTPUT
echo "pull-request-index=true" >> $GITEA_OUTPUT
elif [ "$HTTP_STATUS" == "200" ] && [ "$(cat response_body.json | jq -r .[0].state)" == "closed" ]; then
echo ">> Pull Request found, but was closed"
echo "pull-request-exists=false" >> $GITEA_OUTPUT
else
echo ">> Pull Request not found"
echo "pull-request-exists=false" >> $GITEA_OUTPUT
fi
echo "----"
- name: Create Pull Request
id: create-pull-request
if: steps.commit-push.outputs.push == 'true' && steps.check-for-pull-requst.outputs.pull-request-exists == 'false'
env:
GITEA_TOKEN: ${{ secrets.BOT_TOKEN }}
GITEA_URL: ${{ secrets.REPO_URL }}
HEAD_BRANCH: ${{ steps.commit-push.outputs.HEAD_BRANCH }}
run: |
cd ${MANIFEST_DIR}
API_ENDPOINT="${GITEA_URL}/api/v1/repos/${{ gitea.repository }}/pulls"
PAYLOAD=$( jq -n \
--arg head "${HEAD_BRANCH}" \
--arg base "${BASE_BRANCH}" \
--arg assignee "${ASSIGNEE}" \
--arg title "Automated Manifest Update" \
--arg body "This PR contains newly rendered Kubernetes manifests automatically generated by the CI workflow." \
'{head: $head, base: $base, assignee: $assignee, title: $title, body: $body}' )
echo ">> Creating PR from branch ${HEAD_BRANCH} into ${BASE_BRANCH}"
echo ">> With Endpoint of:"
echo "$API_ENDPOINT"
echo ">> With Payload of:"
echo "$PAYLOAD"
HTTP_STATUS=$(
curl -X POST \
--silent \
--write-out '%{http_code}' \
--output response_body.json \
--dump-header response_headers.txt \
--data "$PAYLOAD" \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"$API_ENDPOINT" 2> response_errors.txt
)
echo ">> HTTP Status Code: $HTTP_STATUS"
echo ">> Response Output ..."
echo "----"
cat response_body.json
echo "----"
cat response_headers.txt
echo "----"
cat response_errors.txt
echo "----"
if [ "$HTTP_STATUS" == "201" ]; then
echo ">> Pull Request created successfully!"
PR_URL=$(cat response_body.json | jq -r .html_url)
echo "pull-request-url=${PR_URL}" >> $GITEA_OUTPUT
PR_ID=$(cat response_body.json | jq -r .id)
echo "pull-request-id=${PR_ID}" >> $GITEA_OUTPUT
echo "pull-request-operation=created" >> $GITEA_OUTPUT
elif [ "$HTTP_STATUS" == "422" ]; then
echo ">> Failed to create PR (HTTP 422: Unprocessable Entity), PR may already exist"
elif [ "$HTTP_STATUS" == "409" ]; then
echo ">> Failed to create PR (HTTP 409: Conflict), PR already exists"
else
echo ">> Failed to create PR, HTTP status code: $HTTP_STATUS"
exit 1
fi
echo "----"
- name: ntfy Created
uses: niniyas/ntfy-action@master
if: steps.create-pull-request.outputs.pull-request-operation == 'created'
with:
url: "${{ secrets.NTFY_URL }}"
topic: "${{ secrets.NTFY_TOPIC }}"
title: "Manifest Render PR Created - Infrastructure"
priority: 3
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,successfully,completed
details: "Manifest rendering for Infrastructure has created a new Pull Request with ID: ${{ steps.create-pull-request.outputs.pull-request-id }}!"
icon: "https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png"
actions: '[{"action": "view", "label": "Open Gitea", "url": "${{ steps.create-pull-request.outputs.pull-request-url }}", "clear": true}]'
- name: ntfy Failed
uses: niniyas/ntfy-action@master
if: failure()
with:
url: "${{ secrets.NTFY_URL }}"
topic: "${{ secrets.NTFY_TOPIC }}"
title: "Manifest Render Failure - Infrastructure"
priority: 4
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,failed
details: "Manifest rendering for Infrastructure has failed!"
icon: "https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png"
actions: '[{"action": "view", "label": "Open Gitea", "url": "https://gitea.alexlebens.dev/alexlebens/infrastructure/actions?workflow=render-manifests.yaml", "clear": true}]'
image: true

View File

@@ -1,624 +0,0 @@
name: render-manifests
on:
schedule:
- cron: '0 15 * * *'
workflow_dispatch:
pull_request:
branches:
- main
paths:
- 'clusters/cl01tl/helm/**'
types:
- closed
env:
CLUSTER: cl01tl
BASE_BRANCH: manifests
BRANCH_NAME_BASE: auto/update-manifests
ASSIGNEE: alexlebens
MAIN_DIR: /workspace/alexlebens/infrastructure/infrastructure
MANIFEST_DIR: /workspace/alexlebens/infrastructure/infrastructure-manifests
jobs:
render-manifests:
runs-on: ubuntu-js
if: >-
github.event_name == 'schedule' ||
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'pull_request' && github.event.pull_request.merged == true)
steps:
- name: Checkout Main
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
path: infrastructure
fetch-depth: 0
- name: Checkout Manifests
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: manifests
path: infrastructure-manifests
- name: Set Up Helm
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5
with:
token: ${{ secrets.GITEA_TOKEN }}
version: v3.17.2 # Pending https://github.com/helm/helm/pull/30743
cache: true
- name: Configure Kubeconfig
uses: azure/k8s-set-context@ae59a723ba9abe7a9655538854a025448dbab4aa # v4
with:
method: kubeconfig
kubeconfig: ${{ secrets.KUBECONFIG }}
- name: Cache Helm Dependencies
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: |
~/.cache/helm
~/.config/helm
key: helm-cache-${{ runner.os }}-${{ hashFiles('infrastructure/clusters/cl01tl/helm/**/Chart.yaml', 'infrastructure/clusters/cl01tl/helm/**/Chart.lock') }}
restore-keys: |
helm-cache-${{ runner.os }}-
- name: Determine Workflow Mode
id: mode
run: |
IS_AUTOMERGE="false"
RENDER_ALL="false"
DIFF_TARGET=""
if [[ "${{ github.event_name }}" == "schedule" || "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo ">> Mode: Dispatch/Schedule (Render All)"
RENDER_ALL="true"
elif [[ "${{ github.event_name }}" == "pull_request" ]]; then
if [[ "${{ contains(github.event.pull_request.labels.*.name, 'automerge') }}" == "true" ]]; then
echo ">> Mode: PR Merged (Automerge)"
IS_AUTOMERGE="true"
else
echo ">> Mode: PR Merged (Standard)"
fi
DIFF_TARGET="HEAD^..HEAD"
fi
echo ""
echo "----"
echo "is-automerge=${IS_AUTOMERGE}" >> "$GITHUB_OUTPUT"
echo "render-all=${RENDER_ALL}" >> "$GITHUB_OUTPUT"
echo "diff-target=${DIFF_TARGET}" >> "$GITHUB_OUTPUT"
- name: Prepare Manifest Branch
id: prepare-manifest-branch
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
IS_AUTOMERGE: ${{ steps.mode.outputs.is-automerge }}
run: |
cd "${MANIFEST_DIR}"
echo ">> Configure git to use gitea-bot as user ..."
git config user.name "gitea-bot"
git config user.email "gitea-bot@alexlebens.net"
if [[ "$IS_AUTOMERGE" == "true" ]]; then
BRANCH_NAME="${BRANCH_NAME_BASE}-automerge-${PR_NUMBER}"
echo ""
echo ">> Creating branch ${BRANCH_NAME} ..."
git checkout -B "$BRANCH_NAME"
else
echo ""
echo ">> Checking if PR branch exists ..."
BRANCH_NAME="${BRANCH_NAME_BASE}"
if git ls-remote --exit-code --heads origin "${BRANCH_NAME}" > /dev/null 2>&1; then
echo ""
echo ">> Branch '${BRANCH_NAME}' exists, pulling changes ..."
git fetch origin "${BRANCH_NAME}"
git checkout "${BRANCH_NAME}"
git pull --rebase
else
echo ""
echo ">> Branch '${BRANCH_NAME}' does not exist, creating ..."
git checkout -b "${BRANCH_NAME}"
fi
fi
echo ""
echo "----"
echo "branch-name=${BRANCH_NAME}" >> "$GITHUB_OUTPUT"
- name: Check Which Directories Have Changes
id: check-dir-changes
env:
RENDER_ALL: ${{ steps.mode.outputs.render-all }}
DIFF_TARGET: ${{ steps.mode.outputs.diff-target }}
run: |
cd "${MAIN_DIR}"
if [[ "$RENDER_ALL" == "true" ]]; then
echo ">> Triggered on dispatch, will check all paths ..."
RENDER_DIR=$(find "clusters/${CLUSTER}/helm" -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | sort -u)
else
echo ">> Checking for changes from ${DIFF_TARGET} ..."
RENDER_DIR=$(git diff --name-only "${DIFF_TARGET}" | grep -E "^clusters/${CLUSTER}/helm/" | awk -F '/' '{print $4}' | sort -u || true)
fi
if [ -n "${RENDER_DIR}" ]; then
echo ""
echo ">> Directories to Render:"
echo ""
echo "${RENDER_DIR}"
echo ""
echo "----"
echo "changes-detected=true" >> "$GITHUB_OUTPUT"
echo "render-dir<<EOF" >> "$GITHUB_OUTPUT"
echo "${RENDER_DIR}" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
else
echo ""
echo ">> No chart changes detected"
echo ""
echo "----"
echo "changes-detected=false" >> "$GITHUB_OUTPUT"
fi
- name: Add Repositories
if: steps.check-dir-changes.outputs.changes-detected == 'true'
env:
RENDER_DIR: ${{ steps.check-dir-changes.outputs.render-dir }}
run: |
cd "${MAIN_DIR}"
echo ">> Adding repositories for chart dependencies ..."
echo ""
for DIR in ${RENDER_DIR}; do
helm dependency list --max-col-width 120 "${MAIN_DIR}/clusters/${CLUSTER}/helm/${DIR}" 2> /dev/null \
| tail -n +2 \
| awk 'NF > 0 { print $1, $3 }' \
| while read -r REPO_NAME REPO_URL; do
if [[ "${REPO_URL}" == oci://* ]]; then
echo ">> Ignoring OCI repo: ${REPO_URL}"
elif [[ -n "${REPO_NAME}" && -n "${REPO_URL}" ]]; then
helm repo add "${REPO_NAME}" "${REPO_URL}"
fi
done || true
done
if helm repo list > /dev/null 2>&1; then
echo ""
echo ">> Update repository cache ..."
helm repo update
fi
echo ""
echo "----"
- name: Remove Changed Manifest Files
if: steps.check-dir-changes.outputs.changes-detected == 'true'
env:
RENDER_DIR: ${{ steps.check-dir-changes.outputs.render-dir }}
run: |
cd "${MANIFEST_DIR}"
echo ">> Remove manifest files and rebuild from source ..."
echo ""
for DIR in ${RENDER_DIR}; do
CHART_PATH="${MANIFEST_DIR}/clusters/${CLUSTER}/manifests/${DIR}"
echo "${CHART_PATH}"
rm -rf "${CHART_PATH}"/*
done
echo ""
echo "----"
- name: Render Helm Manifests
id: render-manifests
if: steps.check-dir-changes.outputs.changes-detected == 'true'
env:
RENDER_DIR: ${{ steps.check-dir-changes.outputs.render-dir }}
run: |
cd "${MAIN_DIR}"
echo ">> Rendering Manifests ..."
render_chart() {
local DIR="$1"
local CHART_PATH="${MAIN_DIR}/clusters/${CLUSTER}/helm/${DIR}"
local CHART_NAME=$(basename "${CHART_PATH}")
echo ""
echo ">> Rendering chart: ${CHART_NAME}"
if [ -f "${CHART_PATH}/Chart.yaml" ]; then
local OUTPUT_FOLDER="${MANIFEST_DIR}/clusters/${CLUSTER}/manifests/${CHART_NAME}/"
mkdir -p "${OUTPUT_FOLDER}"
cd "${CHART_PATH}"
helm dependency update --skip-refresh > /dev/null
helm lint --namespace "${CHART_NAME}" --quiet
local NAMESPACE="${CHART_NAME}"
case "${CHART_NAME}" in
"stack")
NAMESPACE="argocd"
echo ">> Special Rendering into 'argocd' namespace ..."
;;
"cilium" | "coredns" | "metrics-server" | "prometheus-operator-crds")
NAMESPACE="kube-system"
echo ">> Special Rendering for ${CHART_NAME} into 'kube-system' namespace ..."
;;
*)
echo ">> Standard Rendering ..."
esac
echo ">> Formating rendered template ..."
local TEMPLATE
TEMPLATE=$(helm template "${CHART_NAME}" ./ --namespace "${NAMESPACE}" --include-crds --dry-run=server --api-versions "gateway.networking.k8s.io/v1/HTTPRoute")
# Format and split rendered template
echo "${TEMPLATE}" | yq '... comments=""' | yq 'select(. != null)' | yq -s '"'"${OUTPUT_FOLDER}"'" + .kind + "-" + .metadata.name + ".yaml"'
# Strip comments again to ensure formatting correctness
for file in "$OUTPUT_FOLDER"/*; do
yq -i '... comments=""' $file
done
echo ">> Manifests for ${CHART_NAME} rendered successfully to $OUTPUT_FOLDER:"
echo ""
ls $OUTPUT_FOLDER
echo ""
else
echo ""
echo ">> Directory ${CHART_PATH} does not contain a Chart.yaml. Skipping ..."
fi
}
export -f render_chart
export MAIN_DIR CLUSTER MANIFEST_DIR
# Run rendering in parallel
for DIR in ${RENDER_DIR}; do
echo "${DIR}"
done | xargs -P 4 -I {} bash -c 'OUT=$(render_chart "$@" 2>&1); printf "%s\n" "$OUT"' _ {}
echo ""
echo "----"
- name: Check for Changes
id: check-changes
if: steps.check-dir-changes.outputs.changes-detected == 'true'
run: |
cd "${MANIFEST_DIR}"
GIT_CHANGES=$(git status --porcelain)
if [ -n "${GIT_CHANGES}" ]; then
echo ">> Changes detected"
git status --porcelain
CHANGED_CHARTS=$(echo "$GIT_CHANGES" | grep -oE "clusters/${CLUSTER}/manifests/[^/]+" | awk -F '/' '{print $4}' | sort -u | paste -sd ',' -)
echo ""
echo "----"
echo "changes-detected=true" >> "$GITHUB_OUTPUT"
echo "changed-charts-csv=${CHANGED_CHARTS}" >> "$GITHUB_OUTPUT"
else
echo ">> No changes detected, skipping PR creation"
echo ""
echo "----"
fi
- name: Commit and Push Changes
id: commit-push
if: steps.check-changes.outputs.changes-detected == 'true'
env:
BRANCH_NAME: ${{ steps.prepare-manifest-branch.outputs.branch-name }}
IS_AUTOMERGE: ${{ steps.mode.outputs.is-automerge }}
run: |
cd "${MANIFEST_DIR}"
MSG="chore: Update manifests after change"
if [[ "$IS_AUTOMERGE" == "true" ]]; then
MSG="chore: Update manifests after automerge"
fi
echo ">> Commiting changes to ${BRANCH_NAME} ..."
git add .
git commit -m "${MSG}"
REPO_URL="${{ secrets.REPO_URL }}/${{ gitea.repository }}"
echo ""
echo ">> Pushing changes to ${REPO_URL} ..."
git push -u "https://oauth2:${{ secrets.BOT_TOKEN }}@${REPO_URL#*://}" "${BRANCH_NAME}"
echo ""
echo "----"
echo "push=true" >> "$GITHUB_OUTPUT"
echo "head-branch=${BRANCH_NAME}" >> "$GITHUB_OUTPUT"
- name: Check for Pull Request
id: check-for-pull-request
if: steps.commit-push.outputs.push == 'true' && steps.mode.outputs.is-automerge == 'false'
env:
GITEA_TOKEN: ${{ secrets.BOT_TOKEN }}
GITEA_URL: ${{ secrets.REPO_URL }}
HEAD_BRANCH: ${{ steps.commit-push.outputs.head-branch }}
run: |
cd "${MANIFEST_DIR}"
API_ENDPOINT="${GITEA_URL}/api/v1/repos/${{ gitea.repository }}/pulls?base_branch=${BASE_BRANCH}&state=open&page=1"
echo ">> Checking if PR from branch ${HEAD_BRANCH} into ${BASE_BRANCH}"
echo ">> With Endpoint of:"
echo "$API_ENDPOINT"
HTTP_STATUS=$(curl -X GET -s -w '%{http_code}' -o response_body.json -H "Authorization: token ${GITEA_TOKEN}" -H "Content-Type: application/json" "$API_ENDPOINT")
if [ "$HTTP_STATUS" == "200" ] && [ "$(cat response_body.json | jq -r .[0].state)" == "open" ]; then
echo ""
echo ">> Pull Request has been found open, will update"
echo ""
echo "----"
echo "pull-request-exists=$(cat response_body.json | jq -r .[0].number)" >> "$GITHUB_OUTPUT"
else
echo ""
echo ">> Pull Request not found"
echo ""
echo "----"
echo "pull-request-exists=false" >> "$GITHUB_OUTPUT"
fi
- name: Create Pull Request
id: create-pull-request
if: steps.commit-push.outputs.push == 'true' && (steps.mode.outputs.is-automerge == 'true' || steps.check-for-pull-request.outputs.pull-request-exists == 'false')
env:
IS_AUTOMERGE: ${{ steps.mode.outputs.is-automerge }}
GITEA_TOKEN: ${{ secrets.BOT_TOKEN }}
GITEA_URL: ${{ secrets.REPO_URL }}
HEAD_BRANCH: ${{ steps.commit-push.outputs.head-branch }}
CHARTS: ${{ steps.check-changes.outputs.changed-charts-csv }}
EVENT_NAME: ${{ github.event_name }}
ACTOR: ${{ github.actor }}
SHA: ${{ github.sha }}
REF: ${{ github.ref_name }}
run: |
cd "${MANIFEST_DIR}"
API_ENDPOINT="${GITEA_URL}/api/v1/repos/${{ gitea.repository }}/pulls"
BODY=$(printf "This PR contains newly rendered Kubernetes manifests automatically generated by the CI workflow.\n\n### Details\n- **Trigger**: \`%s\` by \`@%s\`\n- **Commit**: \`%s\` (on \`%s\`)\n- **Charts Updated**: \`%s\`" "${EVENT_NAME}" "${ACTOR}" "${SHA:0:7}" "${REF}" "${CHARTS}")
if [[ "$IS_AUTOMERGE" == "true" ]]; then
TITLE="Automated Manifest Update - Automerge"
BODY=$(printf "%s\n\n_This PR is expected to be automerged._" "${BODY}")
else
TITLE="Automated Manifest Update"
fi
PAYLOAD=$(jq -n --arg head "${HEAD_BRANCH}" --arg base "${BASE_BRANCH}" --arg assignee "${ASSIGNEE}" --arg title "${TITLE}" --arg body "${BODY}" '{head: $head, base: $base, assignee: $assignee, title: $title, body: $body}')
HTTP_STATUS=$(curl -X POST -s -w '%{http_code}' -o response_body.json --data "$PAYLOAD" -H "Authorization: token ${GITEA_TOKEN}" -H "Content-Type: application/json" "$API_ENDPOINT")
if [ "$HTTP_STATUS" == "201" ]; then
echo ">> Pull Request created successfully!"
echo ""
echo "----"
echo "pull-request-id=$(jq -r .id response_body.json)" >> "$GITHUB_OUTPUT"
echo "pull-request-number=$(jq -r .number response_body.json)" >> "$GITHUB_OUTPUT"
echo "pull-request-operation=created" >> "$GITHUB_OUTPUT"
elif [[ "$HTTP_STATUS" == "422" || "$HTTP_STATUS" == "409" ]]; then
echo ""
echo ">> Failed to create PR (Already exists)"
echo ""
echo "----"
else
echo ""
echo ">> Failed to create PR, HTTP status code: $HTTP_STATUS"
echo ""
echo "----"
exit 1
fi
- name: Update Pull Request
id: update-pull-request
if: steps.commit-push.outputs.push == 'true' && steps.check-for-pull-request.outputs.pull-request-exists != 'false' && steps.mode.outputs.is-automerge == 'false'
env:
GITEA_TOKEN: ${{ secrets.BOT_TOKEN }}
GITEA_URL: ${{ secrets.REPO_URL }}
PR_NUMBER: ${{ steps.check-for-pull-request.outputs.pull-request-exists }}
CHARTS: ${{ steps.check-changes.outputs.changed-charts-csv }}
EVENT_NAME: ${{ github.event_name }}
ACTOR: ${{ github.actor }}
SHA: ${{ github.sha }}
REF: ${{ github.ref_name }}
run: |
cd "${MANIFEST_DIR}"
API_ENDPOINT="${GITEA_URL}/api/v1/repos/${{ gitea.repository }}/pulls/${PR_NUMBER}"
EXISTING_BODY=$(jq -r '.[0].body' response_body.json)
NEW_DETAILS=$(printf "### Update Details (%s)\n- **Trigger**: \`%s\` by \`@%s\`\n- **Commit**: \`%s\` (on \`%s\`)\n- **Charts Updated**: \`%s\`" "$(date -u +'%Y-%m-%d %H:%M UTC')" "${EVENT_NAME}" "${ACTOR}" "${SHA:0:7}" "${REF}" "${CHARTS}")
UPDATED_BODY=$(printf "%s\n\n%s" "${EXISTING_BODY}" "${NEW_DETAILS}")
PAYLOAD=$(jq -n --arg body "${UPDATED_BODY}" '{body: $body}')
HTTP_STATUS=$(curl -X PATCH -s -w '%{http_code}' -o update_response.json --data "$PAYLOAD" -H "Authorization: token ${GITEA_TOKEN}" -H "Content-Type: application/json" "$API_ENDPOINT")
if [ "$HTTP_STATUS" == "201" ] || [ "$HTTP_STATUS" == "200" ]; then
echo ">> Pull Request updated successfully!"
echo ""
echo "----"
echo "pull-request-operation=updated" >> "$GITHUB_OUTPUT"
else
echo ">> Failed to update PR, HTTP status code: $HTTP_STATUS"; exit 1
echo ""
echo "----"
fi
- name: Merge Changes
id: merge-changes
if: steps.commit-push.outputs.push == 'true' && steps.mode.outputs.is-automerge == 'true'
env:
GITEA_TOKEN: ${{ secrets.BOT_TOKEN }}
GITEA_URL: ${{ secrets.REPO_URL }}
PR_NUMBER: ${{ steps.create-pull-request.outputs.pull-request-number }}
run: |
cd "${MANIFEST_DIR}"
API_ENDPOINT="${GITEA_URL}/api/v1/repos/${{ gitea.repository }}/pulls/${PR_NUMBER}/merge"
PAYLOAD=$(jq -n --arg Do "merge" '{Do: $Do}')
HTTP_STATUS=$(curl -X POST -s -w '%{http_code}' -o response_body.json --data "$PAYLOAD" -H "Authorization: token ${GITEA_TOKEN}" -H "Content-Type: application/json" "$API_ENDPOINT")
if [ "$HTTP_STATUS" == "200" ]; then
echo ">> Pull Request merged successfully!"
echo ""
echo "----"
echo "pull-request-operation=merged" >> "$GITHUB_OUTPUT"
else
echo ">> Failed to merge PR, HTTP status code: $HTTP_STATUS"; exit 1
echo ""
echo "----"
fi
- name: Cleanup Branch
if: failure() && steps.mode.outputs.is-automerge == 'true'
env:
BRANCH_NAME: ${{ steps.prepare-manifest-branch.outputs.branch-name }}
run: |
cd "${MANIFEST_DIR}"
echo ">> Removing branch: ${BRANCH_NAME}"
git push origin --delete "${BRANCH_NAME}" || true
echo ""
echo "----"
- name: ntfy Created
uses: niniyas/ntfy-action@96acac57fdc91d4c4f50b78486c1ed6f03f9f61c # master
if: steps.create-pull-request.outputs.pull-request-operation == 'created' && steps.mode.outputs.is-automerge == 'false'
with:
url: "${{ secrets.NTFY_URL }}"
topic: "${{ secrets.NTFY_TOPIC }}"
title: "Manifest Render - Open PR"
priority: 3
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,successfully,completed
details: "Created renderd manifests for cluster '${{ env.CLUSTER }}' with charts: ${{ steps.check-changes.outputs.changed-charts-csv }}"
icon: "https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png"
actions: '[{"action": "view", "label": "View PR", "url": "${{ vars.USER_URL }}/${{ github.repository }}/pulls/${{ steps.create-pull-request.outputs.pull-request-number }}", "clear": true}]'
- name: ntfy Updated
uses: niniyas/ntfy-action@96acac57fdc91d4c4f50b78486c1ed6f03f9f61c # master
if: steps.commit-push.outputs.push == 'true' && steps.check-for-pull-request.outputs.pull-request-exists != 'false' && steps.mode.outputs.is-automerge == 'false'
with:
url: "${{ secrets.NTFY_URL }}"
topic: "${{ secrets.NTFY_TOPIC }}"
title: "Manifest Render - PR Updated"
priority: 3
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,successfully,completed
details: "Updated rendered manifests PR for cluster '${{ env.CLUSTER }}' with charts: ${{ steps.check-changes.outputs.changed-charts-csv }}"
icon: "https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png"
actions: '[{"action": "view", "label": "View PR", "url": "${{ vars.USER_URL }}/${{ github.repository }}/pulls/${{ steps.create-pull-request.outputs.pull-request-number }}", "clear": true}]'
- name: ntfy Merged
uses: niniyas/ntfy-action@96acac57fdc91d4c4f50b78486c1ed6f03f9f61c # master
if: steps.merge-changes.outputs.pull-request-operation == 'merged'
with:
url: "${{ secrets.NTFY_URL }}"
topic: "${{ secrets.NTFY_TOPIC }}"
title: "Manifest Render - Automerged"
priority: 3
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,successfully,completed
details: "Automerged manifest rendering for cluster '${{ env.CLUSTER }}' with charts: ${{ steps.check-changes.outputs.changed-charts-csv }}"
icon: "https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png"
actions: '[{"action": "view", "label": "View PR", "url": "${{ vars.USER_URL }}/${{ github.repository }}/pulls/${{ steps.create-pull-request.outputs.pull-request-number }}", "clear": true}]'
- name: ntfy Failed
uses: niniyas/ntfy-action@96acac57fdc91d4c4f50b78486c1ed6f03f9f61c # master
if: failure()
with:
url: "${{ secrets.NTFY_URL }}"
topic: "${{ secrets.NTFY_TOPIC }}"
title: "Manifest Render Failure"
priority: 4
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,failed
details: "Manifest rendering for Infrastructure has failed!"
icon: "https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png"
actions: '[{"action": "view", "label": "View Logs", "url": "${{ vars.USER_URL }}/${{ github.repository }}/actions/runs/${{ github.run_id }}", "clear": true}]'

View File

@@ -13,10 +13,10 @@ on:
jobs:
renovate:
runs-on: ubuntu-latest
container: ghcr.io/renovatebot/renovate:43.92.0@sha256:fc36479074628689b956475db381a71e4c7f85904e83009fe5e29ec3f1eee1d0
container: ghcr.io/renovatebot/renovate:43
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@v6
- name: Renovate
run: renovate
@@ -25,7 +25,7 @@ jobs:
RENOVATE_ENDPOINT: ${{ vars.INSTANCE_URL }}
RENOVATE_REPOSITORIES: alexlebens/infrastructure
RENOVATE_GIT_AUTHOR: Renovate Bot <renovate-bot@alexlebens.net>
LOG_LEVEL: debug
LOG_LEVEL: info
RENOVATE_TOKEN: ${{ secrets.RENOVATE_TOKEN }}
RENOVATE_GIT_PRIVATE_KEY: ${{ secrets.RENOVATE_GIT_PRIVATE_KEY }}
RENOVATE_GITHUB_COM_TOKEN: ${{ secrets.RENOVATE_GITHUB_COM_TOKEN }}

View File

@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
rev: v2.3.0
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
@@ -9,9 +9,7 @@ repos:
exclude: '^.*\/templates\/.*$'
args:
- --multi
- id: check-merge-conflict
- id: check-json
- repo: https://github.com/IamTheFij/docker-pre-commit
rev: v3.0.1
rev: v2.0.0
hooks:
- id: docker-compose-check

View File

@@ -5,12 +5,11 @@ description: Actual
keywords:
- actual
- budget
home: https://docs.alexlebens.dev/applications/actual/
home: https://wiki.alexlebens.dev/s/86192f45-94b7-45de-872c-6ef3fec7df5e
sources:
- https://github.com/actualbudget/actual
- https://github.com/actualbudget/actual/pkgs/container/actual
- https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/volsync-target
maintainers:
- name: alexlebens
dependencies:

View File

@@ -4,18 +4,20 @@ actual:
type: deployment
replicas: 1
strategy: Recreate
revisionHistoryLimit: 3
containers:
main:
image:
repository: ghcr.io/actualbudget/actual
tag: 26.3.0@sha256:eb8bc26f53025e07e464594c12d77c52c4b95840c8dadd9b95c4f0c4660f8ad2
tag: 26.3.0
pullPolicy: IfNotPresent
env:
- name: ACTUAL_PORT
value: 5006
- name: TZ
value: US/Central
resources:
requests:
cpu: 10m
memory: 50Mi
memory: 128Mi
probes:
liveness:
enabled: true
@@ -52,8 +54,11 @@ actual:
- actual.alexlebens.net
rules:
- backendRefs:
- name: actual
- group: ''
kind: Service
name: actual
port: 80
weight: 100
matches:
- path:
type: PathPrefix
@@ -64,6 +69,7 @@ actual:
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 2Gi
retain: true
advancedMounts:
main:
main:

View File

@@ -1,12 +1,12 @@
dependencies:
- name: argo-workflows
repository: https://argoproj.github.io/argo-helm
version: 1.0.6
version: 0.47.5
- name: argo-events
repository: https://argoproj.github.io/argo-helm
version: 2.4.21
version: 2.4.20
- name: postgres-cluster
repository: oci://harbor.alexlebens.net/helm-charts
version: 7.10.0
digest: sha256:5635bfe609d8a901df257ef3e6cb469396a21bdd4c6f96e7e33f84036019c52b
generated: "2026-03-24T16:59:01.228848139Z"
version: 7.9.1
digest: sha256:5b7f498040dd93f99a00c708c80fcefdb64dcdf473cfd3edcf8a94255b80b3b4
generated: "2026-03-12T13:02:52.109982708Z"

View File

@@ -7,26 +7,26 @@ keywords:
- argo-events
- workflows
- events
home: https://docs.alexlebens.dev/applications/argo-workflows/
home: https://wiki.alexlebens.dev/s/a268508f-d81d-4b4b-8bd5-9058edaea635
sources:
- https://github.com/argoproj/argo-workflows
- https://github.com/argoproj/argo-events
- https://github.com/argoproj/argo-helm/tree/main/charts/argo-workflows
- https://github.com/argoproj/argo-helm/tree/main/charts/argo-events
- https://github.com/cloudnative-pg/cloudnative-pg
- https://github.com/argoproj/argo-helm/tree/main/charts
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/postgres-cluster
maintainers:
- name: alexlebens
dependencies:
- name: argo-workflows
version: 1.0.6
version: 0.47.5
repository: https://argoproj.github.io/argo-helm
- name: argo-events
version: 2.4.21
version: 2.4.20
repository: https://argoproj.github.io/argo-helm
- name: postgres-cluster
alias: postgres-18-cluster
version: 7.10.0
version: 7.9.1
repository: oci://harbor.alexlebens.net/helm-charts
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/png/argo-cd.png
# renovate: datasource=github-releases depName=argoproj/argo-workflows
appVersion: v4.0.3
appVersion: v4.0.2

View File

@@ -14,9 +14,15 @@ spec:
data:
- secretKey: secret
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /authentik/oidc/argo-workflows
metadataPolicy: None
property: secret
- secretKey: client
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /authentik/oidc/argo-workflows
metadataPolicy: None
property: client

View File

@@ -0,0 +1,28 @@
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: argo-workflows
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: argo-workflows
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: traefik-gateway
namespace: traefik
hostnames:
- argo-workflows.alexlebens.net
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- group: ''
kind: Service
name: argo-workflows-server
port: 2746
weight: 100

View File

@@ -1,16 +1,13 @@
argo-workflows:
crds:
install: true
keep: true
full: true
upgradeJob:
image:
repository: registry.k8s.io/kubectl
tag: v1.35.3
controller:
metricsConfig:
enabled: true
persistence:
connectionPool:
maxIdleConns: 100
maxOpenConns: 0
nodeStatusOffLoad: true
archive: true
postgresql:
host: argo-workflows-postgresql-18-cluster-rw
port: 5432
@@ -25,34 +22,24 @@ argo-workflows:
ssl: false
sslMode: disable
workflowWorkers: 2
workflowTTLWorkers: 2
podCleanupWorkers: 2
cronWorkflowWorkers: 2
workflowTTLWorkers: 1
podCleanupWorkers: 1
cronWorkflowWorkers: 1
resources:
requests:
cpu: 1m
memory: 20Mi
cpu: 10m
memory: 128Mi
serviceMonitor:
enabled: true
name: workflow-controller
workflowNamespaces:
- argocd
- argo-workflows
server:
authModes:
- sso
httproute:
enabled: true
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: traefik-gateway
namespace: traefik
hostnames:
- argo-workflows.alexlebens.net
rules:
- matches:
- path:
type: PathPrefix
value: /
ingress:
enabled: false
sso:
enabled: true
issuer: https://authentik.alexlebens.net/application/o/argo-workflows/
@@ -69,15 +56,15 @@ argo-workflows:
- openid
- email
- profile
useStaticCredentials: true
artifactRepository:
archiveLogs: false
argo-events:
crds:
install: true
keep: true
controller:
resources:
requests:
cpu: 1m
memory: 32Mi
cpu: 10m
memory: 128Mi
metrics:
enabled: true
serviceMonitor:
@@ -86,8 +73,8 @@ argo-events:
enabled: true
resources:
requests:
cpu: 1m
memory: 20Mi
cpu: 10m
memory: 128Mi
postgres-18-cluster:
mode: recovery
recovery:
@@ -101,9 +88,32 @@ postgres-18-cluster:
destinationBucket: postgres-backups
externalSecretCredentialPath: /garage/home-infra/postgres-backups
isWALArchiver: true
# - name: garage-remote
# index: 1
# destinationBucket: postgres-backups
# externalSecretCredentialPath: /garage/home-infra/postgres-backups
# retentionPolicy: "90d"
# data:
# compression: bzip2
# - name: external
# index: 1
# endpointURL: https://nyc3.digitaloceanspaces.com
# destinationBucket: postgres-backups-ce540ddf106d186bbddca68a
# externalSecretCredentialPath: /garage/home-infra/postgres-backups
# isWALArchiver: false
scheduledBackups:
- name: live-backup
suspend: false
immediate: true
schedule: "0 0 14 * * *"
backupName: garage-local
# - name: weekly-backup
# suspend: true
# immediate: true
# schedule: "0 0 4 * * SAT"
# backupName: garage-remote
# - name: daily-backup
# suspend: true
# immediate: true
# schedule: "0 0 0 * * *"
# backupName: external

View File

@@ -1,6 +1,6 @@
dependencies:
- name: argo-cd
repository: https://argoproj.github.io/argo-helm
version: 9.4.16
digest: sha256:f9ecc47369d4401df61c17f55cc59c9b2d4543f57cf122653abb1a27a4f7bf35
generated: "2026-03-26T21:01:52.678525211Z"
version: 9.4.10
digest: sha256:795aad956acef3f5efb8160390caf9b9792b7b4150d3a7984f1c5edbad92dfaa
generated: "2026-03-10T18:58:35.720448421Z"

View File

@@ -4,8 +4,10 @@ version: 1.0.0
description: Argo CD
keywords:
- argo-cd
- delivery
- deployment
home: https://docs.alexlebens.dev/applications/argo-cd/
- gitops
home: https://wiki.alexlebens.dev/s/8a75cf26-b9df-437e-9cc5-2ef47e871a5f
sources:
- https://github.com/argoproj/argo-cd
- https://github.com/argoproj/argo-helm/tree/main/charts/argo-cd
@@ -13,8 +15,8 @@ maintainers:
- name: alexlebens
dependencies:
- name: argo-cd
version: 9.4.16
version: 9.4.10
repository: https://argoproj.github.io/argo-helm
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/png/argo-cd.png
# renovate: datasource=github-releases depName=argoproj/argo-cd
appVersion: v3.3.5
appVersion: v3.3.3

View File

@@ -14,11 +14,17 @@ spec:
data:
- secretKey: secret
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /authentik/oidc/argocd
metadataPolicy: None
property: secret
- secretKey: client
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /authentik/oidc/argocd
metadataPolicy: None
property: client
---
@@ -38,7 +44,10 @@ spec:
data:
- secretKey: ntfy-token
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /ntfy/user/cl01tl
metadataPolicy: None
property: token
---
@@ -58,13 +67,22 @@ spec:
data:
- secretKey: type
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/argocd/credentials/repo/infrastructure
metadataPolicy: None
property: type
- secretKey: url
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/argocd/credentials/repo/infrastructure
metadataPolicy: None
property: url
- secretKey: sshPrivateKey
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/argocd/credentials/repo/infrastructure
metadataPolicy: None
property: sshPrivateKey

View File

@@ -1,11 +1,12 @@
argo-cd:
crds:
install: true
keep: true
configs:
cm:
admin.enabled: true
accounts.homepage: apiKey
timeout.reconciliation: 100s
timeout.reconciliation.jitter: 60s
url: https://argocd.alexlebens.net
statusbadge.url: https://argocd.alexlebens.net/
statusbadge.enabled: true
@@ -32,53 +33,12 @@ argo-cd:
g, homepage, role:readonly
controller:
replicas: 1
resources:
requests:
cpu: 100m
memory: 1Gi
readinessProbe:
failureThreshold: 3
initialDelaySeconds: 60
periodSeconds: 30
successThreshold: 1
timeoutSeconds: 5
metrics:
enabled: true
serviceMonitor:
enabled: true
rules:
enabled: true
spec:
- alert: ArgoAppMissing
expr: |
absent(argocd_app_info) == 1
for: 15m
labels:
severity: critical
annotations:
summary: "[Argo CD] No reported applications"
description: >
Argo CD has not reported any applications data for the past 15 minutes which
means that it must be down or not functioning properly. This needs to be
resolved for this cloud to continue to maintain state.
- alert: ArgoAppNotSynced
expr: |
argocd_app_info{sync_status!="Synced"} == 1
for: 12h
labels:
severity: warning
annotations:
summary: "[{{`{{$labels.name}}`}}] Application not synchronized"
description: >
The application [{{`{{$labels.name}}`}} has not been synchronized for over
12 hours which means that the state of this cloud has drifted away from the
state inside Git.
dex:
enabled: true
resources:
requests:
cpu: 1m
memory: 64Mi
metrics:
enabled: true
serviceMonitor:
@@ -89,57 +49,20 @@ argo-cd:
enabled: true
redis-ha:
enabled: true
image:
repository: redis
tag: 8.6.2-alpine@sha256:81b6f81d6a6c5b9019231a2e8eb10085e3a139a34f833dcc965a8a959b040b72
persistentVolume:
enabled: true
redis:
resources:
requests:
cpu: 1000m
memory: 50Mi
haproxy:
enabled: true
image:
repository: haproxy
tag: 3.3.6-alpine@sha256:744be2dca649a44d490a4c565d36968d19482dd387f1bdd44c168f4322bc6b1e
resources:
requests:
cpu: 5m
memory: 90Mi
metrics:
enabled: true
serviceMonitor:
enabled: true
exporter:
enabled: true
image: ghcr.io/oliver006/redis_exporter
tag: v1.82.0@sha256:6a97d4dd743b533e1f950c677b87d880e44df363c61af3f406fc9e53ed65ee03
serviceMonitor:
enabled: true
prometheusRule:
enabled: true
interval: 30s
rules:
- alert: RedisPodDown
expr: |
redis_up{job="{{ include "redis-ha.fullname" . }}"} == 0
for: 5m
labels:
severity: critical
annotations:
description: Redis pod {{ "{{ $labels.pod }}" }} is down
summary: Redis pod {{ "{{ $labels.pod }}" }} is down
auth: false
redisSecretInit:
enabled: false
server:
replicas: 2
resources:
requests:
cpu: 20m
memory: 80Mi
extensions:
enabled: true
extensionList:
- name: extension-trivy
env:
- name: EXTENSION_URL
value: https://github.com/mziyabo/argocd-trivy-extension/releases/download/v0.2.0/extension-trivy.tar
- name: EXTENSION_CHECKSUM_URL
value: https://github.com/mziyabo/argocd-trivy-extension/releases/download/v0.2.0/extension-trivy_checksums.txt
metrics:
enabled: true
serviceMonitor:
@@ -153,56 +76,31 @@ argo-cd:
namespace: traefik
hostnames:
- argocd.alexlebens.net
rules:
- matches:
- path:
type: PathPrefix
value: /
repoServer:
replicas: 2
resources:
requests:
cpu: 1m
memory: 50Mi
readinessProbe:
enabled: true
failureThreshold: 3
initialDelaySeconds: 60
periodSeconds: 30
successThreshold: 1
timeoutSeconds: 5
livenessProbe:
enabled: true
failureThreshold: 3
initialDelaySeconds: 60
periodSeconds: 30
successThreshold: 1
timeoutSeconds: 5
metrics:
enabled: true
serviceMonitor:
enabled: true
applicationSet:
replicas: 2
resources:
requests:
cpu: 10m
memory: 50Mi
metrics:
enabled: true
serviceMonitor:
enabled: true
readinessProbe:
enabled: true
failureThreshold: 3
initialDelaySeconds: 60
periodSeconds: 30
successThreshold: 1
timeoutSeconds: 5
livenessProbe:
enabled: true
failureThreshold: 3
initialDelaySeconds: 60
periodSeconds: 30
successThreshold: 1
timeoutSeconds: 5
readinessProbe:
enabled: true
notifications:
argocdUrl: https://argocd.alexlebens.net
enabled: true
context:
argocdUrl: https://argocd.alexlebens.net
secret:
create: false
name: argocd-notifications-secret
@@ -216,10 +114,6 @@ argo-cd:
headers:
- name: Authorization
value: Bearer $ntfy-token
resources:
requests:
cpu: 2m
memory: 50Mi
livenessProbe:
enabled: true
readinessProbe:

View File

@@ -7,14 +7,11 @@ keywords:
- books
- podcasts
- audiobooks
home: https://docs.alexlebens.dev/applications/audiobookshelf/
home: https://wiki.alexlebens.dev/s/d4d6719f-cd1c-4b6e-b78e-2d2d7a5097d7
sources:
- https://github.com/advplyr/audiobookshelf
- https://github.com/caronc/apprise
- https://github.com/advplyr/audiobookshelf/pkgs/container/audiobookshelf
- https://github.com/caronc/apprise-api/pkgs/container/apprise
- https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/volsync-target
maintainers:
- name: alexlebens
dependencies:
@@ -32,4 +29,4 @@ dependencies:
repository: oci://harbor.alexlebens.net/helm-charts
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/png/audiobookshelf.png
# renovate: datasource=github-releases depName=advplyr/audiobookshelf
appVersion: 2.33.1
appVersion: 2.33.0

View File

@@ -14,5 +14,8 @@ spec:
data:
- secretKey: ntfy-url
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/audiobookshelf/apprise
metadataPolicy: None
property: ntfy-url

View File

@@ -4,29 +4,28 @@ audiobookshelf:
type: deployment
replicas: 1
strategy: Recreate
pod:
securityContext:
fsGroup: 1000
fsGroupChangePolicy: OnRootMismatch
revisionHistoryLimit: 3
containers:
main:
image:
repository: ghcr.io/advplyr/audiobookshelf
tag: 2.33.1@sha256:a4a5841bba093d81e5f4ad1eaedb4da3fda6dbb2528c552349da50ad1f7ae708
tag: 2.33.0
pullPolicy: IfNotPresent
env:
- name: TZ
value: America/Chicago
value: US/Central
resources:
requests:
cpu: 1m
memory: 200Mi
cpu: 10m
memory: 128Mi
apprise-api:
image:
repository: ghcr.io/caronc/apprise
tag: v1.3.3@sha256:4bfeac268ba87b8e08e308c9aa0182fe99e9501ec464027afc333d1634e65977
repository: caronc/apprise
tag: v1.3.2
pullPolicy: IfNotPresent
env:
- name: TZ
value: America/Chicago
value: US/Central
- name: PGID
value: "1000"
- name: PUID
@@ -42,6 +41,10 @@ audiobookshelf:
secretKeyRef:
name: audiobookshelf-apprise-config
key: ntfy-url
resources:
requests:
cpu: 10m
memory: 128Mi
service:
main:
controller: main
@@ -79,8 +82,11 @@ audiobookshelf:
- audiobookshelf.alexlebens.net
rules:
- backendRefs:
- name: audiobookshelf
- group: ''
kind: Service
name: audiobookshelf
port: 80
weight: 100
matches:
- path:
type: PathPrefix
@@ -91,6 +97,7 @@ audiobookshelf:
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 2Gi
retain: true
advancedMounts:
main:
main:
@@ -101,6 +108,7 @@ audiobookshelf:
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 10Gi
retain: true
advancedMounts:
main:
main:

View File

@@ -7,9 +7,9 @@ dependencies:
version: 2.4.0
- name: postgres-cluster
repository: oci://harbor.alexlebens.net/helm-charts
version: 7.10.0
version: 7.9.1
- name: valkey
repository: oci://harbor.alexlebens.net/helm-charts
version: 0.4.0
digest: sha256:8c353c5dad4c3d04d518c1445497f0d1cb64261a4201ae17a2c0874454b807a7
generated: "2026-03-15T20:04:35.99407071Z"
digest: sha256:abb34b7bb54393236e695453aa1940497cb4def3d3a56a45ca004a22f8e05648
generated: "2026-03-11T22:55:49.936164674Z"

View File

@@ -6,14 +6,17 @@ keywords:
- authentik
- sso
- oidc
- ldap
- idp
- authentication
home: https://docs.alexlebens.dev/applications/authentik/
home: https://wiki.alexlebens.dev/s/45ca5171-581f-41d2-b6fb-2b0915029a2d
sources:
- https://github.com/goauthentik/authentik
- https://github.com/cloudflare/cloudflared
- https://github.com/cloudnative-pg/cloudnative-pg
- https://github.com/goauthentik/helm
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/cloudflared
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/postgres-cluster
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/valkey
maintainers:
- name: alexlebens
dependencies:
@@ -25,7 +28,7 @@ dependencies:
version: 2.4.0
- name: postgres-cluster
alias: postgres-18-cluster
version: 7.10.0
version: 7.9.1
repository: oci://harbor.alexlebens.net/helm-charts
- name: valkey
alias: valkey

View File

@@ -14,5 +14,8 @@ spec:
data:
- secretKey: key
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/authentik/key
metadataPolicy: None
property: key

View File

@@ -30,23 +30,8 @@ authentik:
redis:
host: authentik-valkey
server:
replicas: 2
resources:
requests:
cpu: 20m
memory: 700Mi
livenessProbe:
failureThreshold: 3
initialDelaySeconds: 15
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
readinessProbe:
failureThreshold: 3
initialDelaySeconds: 15
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
name: server
replicas: 1
metrics:
enabled: true
serviceMonitor:
@@ -54,6 +39,8 @@ authentik:
route:
main:
enabled: true
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
hostnames:
- authentik.alexlebens.net
parentRefs:
@@ -61,20 +48,21 @@ authentik:
kind: Gateway
name: traefik-gateway
namespace: traefik
httpsRedirect: false
matches:
- path:
type: PathPrefix
value: /
worker:
name: worker
replicas: 2
resources:
requests:
cpu: 80m
memory: 650Mi
metrics:
enabled: true
serviceMonitor:
enabled: true
replicas: 1
prometheus:
rules:
enabled: true
postgresql:
enabled: false
redis:
enabled: false
postgres-18-cluster:
mode: recovery
recovery:
@@ -88,9 +76,32 @@ postgres-18-cluster:
destinationBucket: postgres-backups
externalSecretCredentialPath: /garage/home-infra/postgres-backups
isWALArchiver: true
# - name: garage-remote
# index: 1
# destinationBucket: postgres-backups
# externalSecretCredentialPath: /garage/home-infra/postgres-backups
# retentionPolicy: "90d"
# data:
# compression: bzip2
# - name: external
# index: 1
# endpointURL: https://nyc3.digitaloceanspaces.com
# destinationBucket: postgres-backups-ce540ddf106d186bbddca68a
# externalSecretCredentialPath: /garage/home-infra/postgres-backups
# isWALArchiver: false
scheduledBackups:
- name: live-backup
suspend: false
immediate: true
schedule: "0 5 14 * * *"
backupName: garage-local
# - name: weekly-backup
# suspend: true
# immediate: true
# schedule: "0 0 4 * * SAT"
# backupName: garage-remote
# - name: daily-backup
# suspend: true
# immediate: true
# schedule: "0 0 0 * * *"
# backupName: external

View File

@@ -5,12 +5,11 @@ description: backrest
keywords:
- backrest
- backup
home: https://docs.alexlebens.dev/applications/backrest/
home: https://wiki.alexlebens.dev/
sources:
- https://github.com/garethgeorge/backrest
- https://github.com/garethgeorge/backrest/pkgs/container/backrest
- https://hub.docker.com/r/garethgeorge/backrest
- https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/volsync-target
maintainers:
- name: alexlebens
dependencies:

View File

@@ -7,8 +7,9 @@ backrest:
containers:
main:
image:
repository: ghcr.io/garethgeorge/backrest
tag: v1.12.1@sha256:f4d34bd6fa985d13bdb6c01c5d8727e07708899afa9567d800808357d77b9fb0
repository: garethgeorge/backrest
tag: v1.12.1
pullPolicy: IfNotPresent
env:
- name: TZ
value: America/Chicago
@@ -22,8 +23,8 @@ backrest:
value: /tmp
resources:
requests:
cpu: 1m
memory: 30Mi
cpu: 10m
memory: 256Mi
service:
main:
controller: main
@@ -32,19 +33,6 @@ backrest:
port: 80
targetPort: 9898
protocol: TCP
serviceMonitor:
main:
selector:
matchLabels:
app.kubernetes.io/name: backrest
app.kubernetes.io/instance: backrest
serviceName: '{{ include "bjw-s.common.lib.chart.names.fullname" $ }}'
endpoints:
- port: http
scheme: http
path: /metrics
interval: 300s
scrapeTimeout: 15s
route:
main:
kind: HTTPRoute
@@ -57,8 +45,11 @@ backrest:
- backrest.alexlebens.net
rules:
- backendRefs:
- name: backrest
- group: ''
kind: Service
name: backrest
port: 80
weight: 100
matches:
- path:
type: PathPrefix
@@ -69,6 +60,7 @@ backrest:
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 10Gi
retain: true
advancedMounts:
main:
main:
@@ -79,6 +71,7 @@ backrest:
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 1Gi
retain: true
advancedMounts:
main:
main:

View File

@@ -4,14 +4,14 @@ version: 1.0.0
description: Bazarr
keywords:
- bazarr
- servarr
- subtitles
home: https://docs.alexlebens.dev/applications/bazarr/
home: https://wiki.alexlebens.dev/s/92784d53-1d43-42fd-b509-f42c73454226
sources:
- https://github.com/morpheus65535/bazarr
- https://github.com/linuxserver/docker-bazarr
- https://github.com/linuxserver/docker-bazarr/pkgs/container/bazarr
- https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/volsync-target
maintainers:
- name: alexlebens
dependencies:

View File

@@ -4,6 +4,7 @@ bazarr:
type: deployment
replicas: 1
strategy: Recreate
revisionHistoryLimit: 3
pod:
securityContext:
runAsUser: 1000
@@ -14,20 +15,19 @@ bazarr:
main:
image:
repository: ghcr.io/linuxserver/bazarr
tag: v1.5.6-ls342@sha256:9a631194c0dee21c85b5bff59e23610e1ae2f54594e922973949d271102e585e
tag: 1.5.6@sha256:05f9d5b24884f37120453dc1a008a47be244eebec32099ae1bd29032e75b67aa
pullPolicy: IfNotPresent
env:
- name: TZ
value: America/Chicago
value: US/Central
- name: PUID
value: 1000
- name: PGID
value: 1000
resources:
limits:
cpu: 100m
requests:
cpu: 1m
memory: 250Mi
cpu: 10m
memory: 256Mi
service:
main:
controller: main
@@ -48,8 +48,11 @@ bazarr:
- bazarr.alexlebens.net
rules:
- backendRefs:
- name: bazarr
- group: ''
kind: Service
name: bazarr
port: 80
weight: 100
matches:
- path:
type: PathPrefix
@@ -60,6 +63,7 @@ bazarr:
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 5Gi
retain: true
advancedMounts:
main:
main:

View File

@@ -5,12 +5,11 @@ description: Blocky
keywords:
- blocky
- dns
home: https://docs.alexlebens.dev/applications/blocky/
home: https://wiki.alexlebens.dev/s/cf70113d-20bc-48ad-afb8-1e22ed3fd62a
sources:
- https://github.com/0xERR0R/blocky
- https://github.com/0xERR0R/blocky/pkgs/container/blocky
- https://hub.docker.com/r/spx01/blocky
- https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/valkey
maintainers:
- name: alexlebens
dependencies:

View File

@@ -4,18 +4,20 @@ blocky:
type: deployment
replicas: 3
strategy: RollingUpdate
revisionHistoryLimit: 3
containers:
main:
image:
repository: ghcr.io/0xerr0r/blocky
tag: v0.29.0@sha256:a6d99f323d3036a99a3767a52ad612f4d8f3f31167492bfc14d4ea57b24cdfd0
pullPolicy: IfNotPresent
env:
- name: TZ
value: America/Chicago
value: US/Central
resources:
requests:
cpu: 10m
memory: 100Mi
memory: 128Mi
configMaps:
config:
enabled: true
@@ -96,7 +98,7 @@ blocky:
traefik-cl01tl IN A 10.232.1.21
blocky IN A 10.232.1.22
plex-lb IN A 10.232.1.23
cilium-cl01tl IN A 10.232.1.23
;; Application Names
@@ -108,35 +110,31 @@ blocky:
authentik IN CNAME traefik-cl01tl
backrest IN CNAME traefik-cl01tl
bazarr IN CNAME traefik-cl01tl
booklore IN CNAME traefik-cl01tl
ceph IN CNAME traefik-cl01tl
code-server IN CNAME traefik-cl01tl
dawarich IN CNAME traefik-cl01tl
directus IN CNAME traefik-cl01tl
excalidraw IN CNAME traefik-cl01tl
feishin IN CNAME traefik-cl01tl
foldergram IN CNAME traefik-cl01tl
garage-s3 IN CNAME traefik-cl01tl
garage-webui IN CNAME traefik-cl01tl
gatus IN CNAME traefik-cl01tl
gitea IN CNAME traefik-cl01tl
grafana IN CNAME traefik-cl01tl
grimmory IN CNAME traefik-cl01tl
harbor IN CNAME traefik-cl01tl
headlamp IN CNAME traefik-cl01tl
home IN CNAME traefik-cl01tl
home-assistant IN CNAME traefik-cl01tl
home-assistant-code-server IN CNAME traefik-cl01tl
houndarr IN CNAME traefik-cl01tl
hubble IN CNAME traefik-cl01tl
immich IN CNAME traefik-cl01tl
jellyfin IN CNAME traefik-cl01tl
jellystat IN CNAME traefik-cl01tl
kiwix IN CNAME traefik-cl01tl
komodo IN CNAME traefik-cl01tl
languagetool IN CNAME traefik-cl01tl
lidarr IN CNAME traefik-cl01tl
mail IN CNAME traefik-cl01tl
medialyze IN CNAME traefik-cl01tl
movie-roulette IN CNAME traefik-cl01tl
music-grabber IN CNAME traefik-cl01tl
navidrome IN CNAME traefik-cl01tl

View File

@@ -4,9 +4,12 @@ dependencies:
version: 4.6.2
- name: mariadb-cluster
repository: https://helm.mariadb.com/mariadb-operator
version: 26.3.0
version: 25.10.4
- name: volsync-target
repository: oci://harbor.alexlebens.net/helm-charts
version: 0.8.0
digest: sha256:6ee403da03c1bcc0289a9abdef0508344072d51173da996eda69b8305d5feefa
generated: "2026-03-23T20:35:19.743257-05:00"
- name: volsync-target
repository: oci://harbor.alexlebens.net/helm-charts
version: 0.8.0
digest: sha256:d4c7bf75f72f7eab4ad968bf9f55daac9392c9b2df08f8a27c5dc4f8fffb5f57
generated: "2026-03-06T01:06:05.696573273Z"

View File

@@ -0,0 +1,33 @@
apiVersion: v2
name: booklore
version: 1.0.0
description: booklore
keywords:
- booklore
- books
home: https://wiki.alexlebens.dev/
sources:
- https://github.com/booklore-app/BookLore
- https://github.com/booklore-app/booklore/pkgs/container/booklore
- https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template
maintainers:
- name: alexlebens
dependencies:
- name: app-template
alias: booklore
repository: https://bjw-s-labs.github.io/helm-charts/
version: 4.6.2
- name: mariadb-cluster
version: 25.10.4
repository: https://helm.mariadb.com/mariadb-operator
- name: volsync-target
alias: volsync-target-config
version: 0.8.0
repository: oci://harbor.alexlebens.net/helm-charts
- name: volsync-target
alias: volsync-target-data
version: 0.8.0
repository: oci://harbor.alexlebens.net/helm-charts
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/png/booklore.png
# renovate: datasource=github-releases depName=booklore-app/BookLore
appVersion: v2.2.0

View File

@@ -1,10 +1,10 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: grimmory-database-secret
name: booklore-database-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: grimmory-database-secret
app.kubernetes.io/name: booklore-database-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
@@ -14,17 +14,20 @@ spec:
data:
- secretKey: password
remoteRef:
key: /cl01tl/grimmory/database
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/booklore/database
metadataPolicy: None
property: password
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: grimmory-data-replication-secret
name: booklore-data-replication-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: grimmory-data-replication-secret
app.kubernetes.io/name: booklore-data-replication-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
@@ -34,17 +37,20 @@ spec:
data:
- secretKey: psk.txt
remoteRef:
key: /cl01tl/grimmory/replication
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/booklore/replication
metadataPolicy: None
property: psk.txt
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: grimmory-mariadb-cluster-backup-secret-external
name: booklore-mariadb-cluster-backup-secret-external
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: grimmory-mariadb-cluster-backup-secret-external
app.kubernetes.io/name: booklore-mariadb-cluster-backup-secret-external
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
@@ -54,21 +60,27 @@ spec:
data:
- secretKey: access
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/mariadb-backups
metadataPolicy: None
property: access
- secretKey: secret
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /digital-ocean/home-infra/mariadb-backups
metadataPolicy: None
property: secret
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: grimmory-mariadb-cluster-backup-secret-garage
name: booklore-mariadb-cluster-backup-secret-garage
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: grimmory-mariadb-cluster-backup-secret-garage
app.kubernetes.io/name: booklore-mariadb-cluster-backup-secret-garage
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
@@ -78,9 +90,15 @@ spec:
data:
- secretKey: access
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/mariadb-backups
metadataPolicy: None
property: access
- secretKey: secret
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/mariadb-backups
metadataPolicy: None
property: secret

View File

@@ -1,11 +1,11 @@
apiVersion: v1
kind: Namespace
metadata:
name: grimmory
name: booklore
annotations:
volsync.backube/privileged-movers: "true"
labels:
app.kubernetes.io/name: grimmory
app.kubernetes.io/name: booklore
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
pod-security.kubernetes.io/audit: privileged

View File

@@ -1,14 +1,14 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: grimmory-books-nfs-storage
name: booklore-books-nfs-storage
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: grimmory-books-nfs-storage
app.kubernetes.io/name: booklore-books-nfs-storage
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
volumeName: grimmory-books-nfs-storage
volumeName: booklore-books-nfs-storage
storageClassName: nfs-client
accessModes:
- ReadWriteMany
@@ -20,14 +20,14 @@ spec:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: grimmory-books-import-nfs-storage
name: booklore-books-import-nfs-storage
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: grimmory-books-import-nfs-storage
app.kubernetes.io/name: booklore-books-import-nfs-storage
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
volumeName: grimmory-books-import-nfs-storage
volumeName: booklore-books-import-nfs-storage
storageClassName: nfs-client
accessModes:
- ReadWriteMany

View File

@@ -1,10 +1,10 @@
apiVersion: v1
kind: PersistentVolume
metadata:
name: grimmory-books-nfs-storage
name: booklore-books-nfs-storage
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: grimmory-books-nfs-storage
app.kubernetes.io/name: booklore-books-nfs-storage
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
@@ -26,10 +26,10 @@ spec:
apiVersion: v1
kind: PersistentVolume
metadata:
name: grimmory-books-import-nfs-storage
name: booklore-books-import-nfs-storage
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: grimmory-books-import-nfs-storage
app.kubernetes.io/name: booklore-books-import-nfs-storage
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:

View File

@@ -1,18 +1,16 @@
grimmory:
booklore:
controllers:
main:
type: deployment
replicas: 1
strategy: Recreate
pod:
securityContext:
fsGroup: 1000
fsGroupChangePolicy: OnRootMismatch
revisionHistoryLimit: 3
containers:
main:
image:
repository: ghcr.io/grimmory-tools/grimmory
tag: v2.3.0@sha256:9014247f591074529894f81115ca40f899db697e89f72c2fe91ec530e3f19597
repository: ghcr.io/booklore-app/booklore
tag: v2.2.0
pullPolicy: IfNotPresent
env:
- name: TZ
value: America/Chicago
@@ -21,22 +19,22 @@ grimmory:
- name: GROUP_ID
value: 1000
- name: DATABASE_URL
value: jdbc:mariadb://grimmory-mariadb-cluster-primary.grimmory:3306/booklore
value: jdbc:mariadb://booklore-mariadb-cluster-primary.booklore:3306/booklore
- name: DATABASE_USERNAME
value: grimmory
value: booklore
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: grimmory-database-secret
name: booklore-database-secret
key: password
- name: GRIMMORY_PORT
- name: BOOKLORE_PORT
value: 6060
- name: SWAGGER_ENABLED
value: false
resources:
requests:
cpu: 10m
memory: 1Gi
cpu: 50m
memory: 128Mi
service:
main:
controller: main
@@ -54,26 +52,41 @@ grimmory:
name: traefik-gateway
namespace: traefik
hostnames:
- grimmory.alexlebens.net
- booklore.alexlebens.net
rules:
- backendRefs:
- name: grimmory
- group: ''
kind: Service
name: booklore
port: 80
weight: 100
matches:
- path:
type: PathPrefix
value: /
persistence:
config:
forceRename: grimmory-config
forceRename: booklore-config
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 5Gi
retain: true
advancedMounts:
main:
main:
- path: /app/data
readOnly: false
data:
forceRename: booklore-data
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 10Gi
retain: true
advancedMounts:
main:
main:
- path: /data
readOnly: false
books-import:
type: emptyDir
advancedMounts:
@@ -81,15 +94,8 @@ grimmory:
main:
- path: /bookdrop
readOnly: false
data:
existingClaim: grimmory-books-nfs-storage
advancedMounts:
main:
main:
- path: /data
readOnly: false
ingest:
existingClaim: grimmory-books-import-nfs-storage
existingClaim: booklore-books-import-nfs-storage
advancedMounts:
main:
main:
@@ -99,7 +105,7 @@ mariadb-cluster:
mariadb:
rootPasswordSecretKeyRef:
generate: false
name: grimmory-database-secret
name: booklore-database-secret
key: password
storage:
size: 5Gi
@@ -109,14 +115,14 @@ mariadb-cluster:
bootstrapFrom:
s3:
bucket: mariadb-backups-b230a2f5aecf080a4b372c08
prefix: cl01tl/grimmory
prefix: cl01tl/booklore
endpoint: nyc3.digitaloceanspaces.com
region: us-east-1
accessKeyIdSecretKeyRef:
name: grimmory-mariadb-cluster-backup-secret-external
name: booklore-mariadb-cluster-backup-secret-external
key: access
secretAccessKeySecretKeyRef:
name: grimmory-mariadb-cluster-backup-secret-external
name: booklore-mariadb-cluster-backup-secret-external
key: secret
tls:
enabled: true
@@ -128,22 +134,21 @@ mariadb-cluster:
cleanupPolicy: Delete
requeueInterval: 10h
users:
- name: grimmory
- name: booklore
passwordSecretKeyRef:
name: grimmory-database-secret
name: booklore-database-secret
key: password
host: '%'
maxUserConnections: 100
cleanupPolicy: Delete
requeueInterval: 10h
retryInterval: 30s
grants:
- name: grimmory
- name: booklore
privileges:
- "ALL PRIVILEGES"
database: "booklore"
table: "*"
username: grimmory
username: booklore
grantOption: true
host: '%'
cleanupPolicy: Delete
@@ -161,14 +166,14 @@ mariadb-cluster:
storage:
s3:
bucket: mariadb-backups-b230a2f5aecf080a4b372c08
prefix: cl01tl/grimmory
prefix: cl01tl/booklore
endpoint: nyc3.digitaloceanspaces.com
region: us-east-1
accessKeyIdSecretKeyRef:
name: grimmory-mariadb-cluster-backup-secret-external
name: booklore-mariadb-cluster-backup-secret-external
key: access
secretAccessKeySecretKeyRef:
name: grimmory-mariadb-cluster-backup-secret-external
name: booklore-mariadb-cluster-backup-secret-external
key: secret
tls:
enabled: true
@@ -183,14 +188,14 @@ mariadb-cluster:
storage:
s3:
bucket: mariadb-backups
prefix: cl01tl/grimmory
prefix: cl01tl/booklore
endpoint: garage-ps10rp.boreal-beaufort.ts.net:3900
region: us-east-1
accessKeyIdSecretKeyRef:
name: grimmory-mariadb-cluster-backup-secret-garage
name: booklore-mariadb-cluster-backup-secret-garage
key: access
secretAccessKeySecretKeyRef:
name: grimmory-mariadb-cluster-backup-secret-garage
name: booklore-mariadb-cluster-backup-secret-garage
key: secret
tls:
enabled: true
@@ -205,20 +210,17 @@ mariadb-cluster:
storage:
s3:
bucket: mariadb-backups
prefix: cl01tl/grimmory
prefix: cl01tl/booklore
endpoint: garage-main.garage:3900
region: us-east-1
accessKeyIdSecretKeyRef:
name: grimmory-mariadb-cluster-backup-secret-garage
name: booklore-mariadb-cluster-backup-secret-garage
key: access
secretAccessKeySecretKeyRef:
name: grimmory-mariadb-cluster-backup-secret-garage
name: booklore-mariadb-cluster-backup-secret-garage
key: secret
volsync-target-config:
pvcTarget: grimmory-config
moverSecurityContext:
fsGroup: 1000
fsGroupChangePolicy: OnRootMismatch
pvcTarget: booklore-config
local:
enabled: true
schedule: 12 8 * * *
@@ -228,3 +230,20 @@ volsync-target-config:
external:
enabled: true
schedule: 12 10 * * *
volsync-target-data:
pvcTarget: booklore-data
local:
enabled: true
schedule: 14 8 * * *
restic:
cacheCapacity: 10Gi
remote:
enabled: true
schedule: 14 9 * * *
restic:
cacheCapacity: 10Gi
external:
enabled: true
schedule: 14 10 * * *
restic:
cacheCapacity: 10Gi

View File

@@ -5,7 +5,8 @@ description: Cert Manager
keywords:
- cert-manager
- certificates
home: https://docs.alexlebens.dev/applications/cert-manager/
- kubernetes
home: https://wiki.alexlebens.dev/s/368fe718-eedb-40e0-a5a7-fad03cdc6b09
sources:
- https://github.com/cert-manager/cert-manager
- https://github.com/cert-manager/cert-manager/tree/master/deploy/charts/cert-manager
@@ -15,6 +16,6 @@ dependencies:
- name: cert-manager
version: v1.20.0
repository: https://charts.jetstack.io
icon: https://raw.githubusercontent.com/cert-manager/cert-manager/refs/heads/master/logo/logo.png
icon: https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/cert-manager.png
# renovate: datasource=github-releases depName=cert-manager/cert-manager
appVersion: v1.20.0

View File

@@ -2,11 +2,6 @@ apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-issuer
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: letsencrypt-issuer
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
acme:
email: alexanderlebens@gmail.com

View File

@@ -14,5 +14,8 @@ spec:
data:
- secretKey: api-token
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cloudflare/alexlebens.net/clusterissuer
metadataPolicy: None
property: token

View File

@@ -3,16 +3,10 @@ cert-manager:
enabled: true
keep: true
replicaCount: 2
podDisruptionBudget:
enabled: true
minAvailable: 1
extraArgs:
- --enable-gateway-api
resources:
requests:
cpu: 10m
memory: 64Mi
prometheus:
enabled: true
servicemonitor:
enabled: true
honorLabels: true

View File

@@ -1,6 +1,6 @@
dependencies:
- name: cilium
repository: https://helm.cilium.io/
version: 1.19.2
digest: sha256:11f8eef4733b70c2b9a91ce39fe3c1ea1ad3fa3c46750efb015e03ff6ea3655b
generated: "2026-03-27T06:03:34.264955338Z"
version: 1.18.6
digest: sha256:8ea328ac238524b5b423e6289f5e25d05ef64e6aa19cfd5de238f1d5dd533e9b
generated: "2026-02-05T12:00:20.15778-06:00"

View File

@@ -4,18 +4,19 @@ version: 1.0.0
description: Cilium
keywords:
- cilium
- operator
- cni
- network
home: https://docs.alexlebens.dev/applications/cilium/
- kubernetes
home: https://wiki.alexlebens.dev/s/9e6f5b17-e186-4af0-81cd-af647b162d3d
sources:
- https://github.com/cilium/cilium
- https://github.com/cilium/cilium/tree/main/install/kubernetes/cilium
- https://github.com/cilium/charts
maintainers:
- name: alexlebens
dependencies:
- name: cilium
version: 1.19.2
version: 1.18.6
repository: https://helm.cilium.io/
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/png/cilium.png
# renovate: datasource=github-releases depName=cilium/cilium
appVersion: 1.19.2
appVersion: 1.19.1

View File

@@ -25,24 +25,36 @@ cilium:
- NET_ADMIN
- SYS_ADMIN
- SYS_RESOURCE
l2announcements:
enabled: false
bgpControlPlane:
enabled: false
secretsNamespace:
name: kube-system
statusReport:
enabled: true
routerIDAllocation:
mode: "default"
bpf:
hostLegacyRouting: true
devices: end0 enp6s0
ciliumEndpointSlice:
enabled: true
ingressController:
enabled: false
gatewayAPI:
enabled: true
enableAppProtocol: true
enableAlpn: true
secretsNamespace:
create: false
name: kube-system
enableAppProtocol: true
gatewayClass:
create: auto
externalIPs:
enabled: true
socketLB:
enabled: true
hostNamespaceOnly: true
hubble:
enabled: true
metrics:
serviceMonitor:
enabled: true
@@ -56,6 +68,8 @@ cilium:
enabled: true
ui:
enabled: true
ingress:
enabled: false
ipam:
mode: "kubernetes"
ipv4:
@@ -63,11 +77,12 @@ cilium:
ipv6:
enabled: false
kubeProxyReplacement: true
l7Proxy: true
prometheus:
enabled: true
serviceMonitor:
enabled: true
trustCRDsExist: true
enabled: true
envoy:
enabled: true
securityContext:
@@ -79,11 +94,14 @@ cilium:
- PERFMON
- BPF
prometheus:
enabled: true
serviceMonitor:
enabled: true
operator:
enabled: true
rollOutPods: true
prometheus:
enabled: true
serviceMonitor:
enabled: true
cgroup:

View File

@@ -6,11 +6,10 @@ keywords:
- cloudnative-pg
- operator
- postgresql
home: https://docs.alexlebens.dev/applications/cloudnative-pg/
- kubernetes
home: https://wiki.alexlebens.dev/s/9fb10833-0278-4e64-a34c-d348d833839f
sources:
- https://github.com/cloudnative-pg/cloudnative-pg
- https://github.com/cloudnative-pg/plugin-barman-cloud
- https://github.com/cloudnative-pg/postgres-containers/pkgs/container/postgresql
- https://github.com/cloudnative-pg/charts/tree/main/charts/cloudnative-pg
- https://github.com/cloudnative-pg/charts/tree/main/charts/plugin-barman-cloud
maintainers:
@@ -22,6 +21,6 @@ dependencies:
- name: plugin-barman-cloud
version: 0.5.0
repository: https://cloudnative-pg.io/charts/
icon: https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg.github.io/refs/heads/main/assets/images/hero_image.png
icon: https://avatars.githubusercontent.com/u/100373852?s=200&v=4
# renovate: datasource=github-releases depName=cloudnative-pg/cloudnative-pg
appVersion: 1.28.1

View File

@@ -1,16 +1,16 @@
cloudnative-pg:
replicaCount: 2
resources:
requests:
cpu: 10m
memory: 100Mi
monitoring:
podMonitorEnabled: true
plugin-barman-cloud:
replicaCount: 1
image:
registry: ghcr.io
repository: cloudnative-pg/plugin-barman-cloud
tag: v0.11.0
sidecarImage:
registry: ghcr.io
repository: cloudnative-pg/plugin-barman-cloud-sidecar
tag: v0.11.0
crds:
create: true
resources:
requests:
cpu: 1m
memory: 20Mi

View File

@@ -5,14 +5,14 @@ description: Code Server
keywords:
- code-server
- code
home: https://docs.alexlebens.dev/applications/code-server/
- ide
home: https://wiki.alexlebens.dev/s/233f96bb-db70-47e4-8b22-a8efcbb0f93d
sources:
- https://github.com/coder/code-server
- https://github.com/linuxserver/docker-code-server
- https://github.com/linuxserver/docker-code-server/pkgs/container/code-server
- https://github.com/cloudflare/cloudflared
- https://hub.docker.com/r/linuxserver/code-server
- https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/cloudflared
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/volsync-target
maintainers:
- name: alexlebens
dependencies:
@@ -28,5 +28,5 @@ dependencies:
version: 0.8.0
repository: oci://harbor.alexlebens.net/helm-charts
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/png/visual-studio-code.png
# renovate: datasource=github-releases depName=coder/code-server
appVersion: 4.112.0
# renovate: datasource=github-releases depName=linuxserver/docker-code-server
appVersion: 4.108.1

View File

@@ -14,9 +14,15 @@ spec:
data:
- secretKey: PASSWORD
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/code-server/auth
metadataPolicy: None
property: PASSWORD
- secretKey: SUDO_PASSWORD
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/code-server/auth
metadataPolicy: None
property: SUDO_PASSWORD

View File

@@ -4,18 +4,16 @@ code-server:
type: deployment
replicas: 1
strategy: Recreate
pod:
securityContext:
fsGroup: 1000
fsGroupChangePolicy: OnRootMismatch
revisionHistoryLimit: 3
containers:
main:
image:
repository: ghcr.io/linuxserver/code-server
tag: 4.112.0@sha256:4bb5b8ad22268001687c047f0f04933799fb03df1eb0e1e266ba15ed2d9f4e8b
tag: 4.111.0@sha256:04107645b21f33215d1087773e2c889dfed823434f5dc4c7e9ae20df218a8ef3
pullPolicy: IfNotPresent
env:
- name: TZ
value: America/Chicago
value: US/Central
- name: PUID
value: 1000
- name: PGID
@@ -27,8 +25,8 @@ code-server:
name: codeserver-password-secret
resources:
requests:
cpu: 1m
memory: 50Mi
cpu: 10m
memory: 128Mi
service:
main:
controller: main
@@ -49,8 +47,11 @@ code-server:
- code-server.alexlebens.net
rules:
- backendRefs:
- name: code-server
- group: ''
kind: Service
name: code-server
port: 8443
weight: 100
matches:
- path:
type: PathPrefix
@@ -61,6 +62,7 @@ code-server:
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 2Gi
retain: true
advancedMounts:
main:
main:

View File

@@ -5,7 +5,9 @@ description: CoreDNS
keywords:
- coredns
- dns
home: https://docs.alexlebens.dev/applications/coredns/
- network
- kubernetes
home: https://wiki.alexlebens.dev/s/43947ec6-a034-449f-8c76-982ac493b072
sources:
- https://github.com/coredns/coredns
- https://github.com/coredns/helm
@@ -15,6 +17,6 @@ dependencies:
- name: coredns
version: 1.45.2
repository: https://coredns.github.io/helm
icon: https://raw.githubusercontent.com/coredns/coredns.io/refs/heads/master/static/images/favicon.png
icon: https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/png/coredns.png
# renovate: datasource=github-releases depName=coredns/coredns
appVersion: v1.14.2

View File

@@ -1,18 +1,23 @@
coredns:
image:
repository: registry.k8s.io/coredns/coredns
tag: v1.14.2@sha256:e7e6440cfd1e919280958f5b5a6ab2b184d385bba774c12ad2a9e1e4183f90d9
tag: v1.14.1
replicaCount: 3
resources:
limits:
cpu: null
memory: null
requests:
cpu: 30m
memory: 30Mi
cpu: 50m
memory: 128Mi
rollingUpdate:
maxUnavailable: 1
maxSurge: 25%
terminationGracePeriodSeconds: 30
serviceType: "ClusterIP"
prometheus:
service:
enabled: true
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9153"
monitor:
enabled: true
namespace: kube-system
@@ -24,7 +29,18 @@ coredns:
serviceAccount:
create: true
name: coredns
rbac:
create: true
isClusterService: true
priorityClassName: system-cluster-critical
securityContext:
capabilities:
add:
- NET_BIND_SERVICE
drop:
- ALL
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
servers:
- zones:
- zone: .
@@ -61,8 +77,6 @@ coredns:
- name: errors
- name: cache
parameters: 30
- name: prometheus
parameters: :9153
- name: forward
parameters: . 10.111.232.172
- zones:
@@ -74,8 +88,6 @@ coredns:
- name: errors
- name: cache
parameters: 30
- name: prometheus
parameters: :9153
- name: forward
parameters: . 10.97.20.219
nodeSelector:
@@ -88,4 +100,6 @@ coredns:
operator: Exists
effect: NoSchedule
deployment:
skipConfig: false
enabled: true
name: coredns

View File

@@ -4,9 +4,9 @@ dependencies:
version: 4.6.2
- name: postgres-cluster
repository: oci://harbor.alexlebens.net/helm-charts
version: 7.10.0
version: 7.9.1
- name: valkey
repository: oci://harbor.alexlebens.net/helm-charts
version: 0.4.0
digest: sha256:7584c2a1613454bbd83b66df46170fd0157df5186842844d483e2dd131398574
generated: "2026-03-15T20:04:49.68456485Z"
digest: sha256:9524709cf393c01f28b0d073ef6870a2f1afd46f3bc5f564e73c55450aba8dd0
generated: "2026-03-11T22:56:11.749729235Z"

View File

@@ -5,13 +5,10 @@ description: Dawarich
keywords:
- dawarich
- location
home: https://docs.alexlebens.dev/applications/dawarich/
home: https://wiki.alexlebens.dev/s/
sources:
- https://github.com/Freika/dawarich
- https://hub.docker.com/r/freikin/dawarich
- https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/postgres-cluster
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/valkey
maintainers:
- name: alexlebens
dependencies:
@@ -21,7 +18,7 @@ dependencies:
version: 4.6.2
- name: postgres-cluster
alias: postgres-18-cluster
version: 7.10.0
version: 7.9.1
repository: oci://harbor.alexlebens.net/helm-charts
- name: valkey
alias: valkey
@@ -29,4 +26,4 @@ dependencies:
repository: oci://harbor.alexlebens.net/helm-charts
icon: https://cdn.jsdelivr.net/gh/selfhst/icons@main/png/dawarich.png
# renovate: datasource=github-releases depName=Freika/dawarich
appVersion: 1.4.0
appVersion: 1.3.3

View File

@@ -14,7 +14,10 @@ spec:
data:
- secretKey: key
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/dawarich/key
metadataPolicy: None
property: key
---
@@ -34,9 +37,15 @@ spec:
data:
- secretKey: client
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /authentik/oidc/dawarich
metadataPolicy: None
property: client
- secretKey: secret
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /authentik/oidc/dawarich
metadataPolicy: None
property: secret

View File

@@ -4,20 +4,15 @@ dawarich:
type: deployment
replicas: 1
strategy: Recreate
revisionHistoryLimit: 3
containers:
main:
image:
repository: freikin/dawarich
tag: 1.4.0@sha256:07adb7643b00d1d8f606c675931d3604317fa3851b91b74ec503df8d50734cb8
command:
- "web-entrypoint.sh"
args:
- "bin/rails"
- "server"
- "-p"
- "3000"
- "-b"
- "::"
tag: 1.3.3
pullPolicy: IfNotPresent
command: ["web-entrypoint.sh"]
args: ["bin/rails", "server", "-p", "3000", "-b", "::"]
env:
- name: RAILS_ENV
value: production
@@ -91,14 +86,14 @@ dawarich:
value: true
probes:
liveness:
enabled: true
enabled: false
custom: true
spec:
exec:
command:
- /bin/sh
- -c
- "wget -qO - http://127.0.0.1:3000/api/v1/health | grep -q '\"status\"\\s*:\\s*\"ok\"'"
- wget -qO - http://127.0.0.1:3000/api/v1/health | grep -Eq '\"status\"\\s*:\\s*\"ok\"'
failureThreshold: 5
initialDelaySeconds: 60
periodSeconds: 10
@@ -106,16 +101,15 @@ dawarich:
timeoutSeconds: 10
resources:
requests:
cpu: 20m
memory: 750Mi
cpu: 10m
memory: 128Mi
sidekiq:
image:
repository: freikin/dawarich
tag: 1.4.0@sha256:07adb7643b00d1d8f606c675931d3604317fa3851b91b74ec503df8d50734cb8
command:
- "sidekiq-entrypoint.sh"
args:
- "sidekiq"
tag: 1.3.3
pullPolicy: IfNotPresent
command: ["sidekiq-entrypoint.sh"]
args: ["sidekiq"]
env:
- name: RAILS_ENV
value: production
@@ -191,19 +185,23 @@ dawarich:
value: true
probes:
liveness:
enabled: true
enabled: false
custom: true
spec:
exec:
command:
- pgrep
- -f
- sidekiq
- /bin/sh
- -c
- pgrep -f sidekiq
failureThreshold: 5
initialDelaySeconds: 60
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 10
resources:
requests:
cpu: 10m
memory: 128Mi
service:
main:
controller: main
@@ -240,8 +238,11 @@ dawarich:
- dawarich.alexlebens.net
rules:
- backendRefs:
- name: dawarich
- group: ""
kind: Service
name: dawarich
port: 80
weight: 100
matches:
- path:
type: PathPrefix
@@ -252,6 +253,7 @@ dawarich:
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 5Gi
retain: true
advancedMounts:
main:
main:
@@ -265,6 +267,7 @@ dawarich:
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 5Gi
retain: true
advancedMounts:
main:
main:
@@ -278,6 +281,7 @@ dawarich:
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 1Gi
retain: true
advancedMounts:
main:
main:
@@ -309,9 +313,32 @@ postgres-18-cluster:
destinationBucket: postgres-backups
externalSecretCredentialPath: /garage/home-infra/postgres-backups
isWALArchiver: true
# - name: garage-remote
# index: 1
# destinationBucket: postgres-backups
# externalSecretCredentialPath: /garage/home-infra/postgres-backups
# retentionPolicy: "90d"
# data:
# compression: bzip2
# - name: external
# index: 1
# endpointURL: https://nyc3.digitaloceanspaces.com
# destinationBucket: postgres-backups-ce540ddf106d186bbddca68a
# externalSecretCredentialPath: /garage/home-infra/postgres-backups
# isWALArchiver: false
scheduledBackups:
- name: live-backup
suspend: false
immediate: true
schedule: "0 10 14 * * *"
backupName: garage-local
# - name: weekly-backup
# suspend: true
# immediate: true
# schedule: "0 0 4 * * SAT"
# backupName: garage-remote
# - name: daily-backup
# suspend: true
# immediate: true
# schedule: "0 0 0 * * *"
# backupName: external

View File

@@ -0,0 +1,6 @@
dependencies:
- name: app-template
repository: https://bjw-s-labs.github.io/helm-charts/
version: 4.6.2
digest: sha256:548ae1f8699100a2f6bac11a4a3137402b3eea340c7a3db4d9f1813ad6a11dca
generated: "2026-02-23T22:08:42.516245-06:00"

View File

@@ -0,0 +1,20 @@
apiVersion: v2
name: decluttarr
version: 1.0.0
description: decluttarr
keywords:
- decluttarr
- servarr
home: https://wiki.alexlebens.dev/s/
sources:
- https://github.com/ManiMatter/decluttarr
- https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template
maintainers:
- name: alexlebens
dependencies:
- name: app-template
alias: decluttarr
repository: https://bjw-s-labs.github.io/helm-charts/
version: 4.6.2
# renovate: datasource=github-releases depName=ManiMatter/decluttarr
appVersion: v2.0.0

View File

@@ -0,0 +1,21 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: decluttarr-config-secret
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: decluttarr-config-secret
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
secretStoreRef:
kind: ClusterSecretStore
name: vault
data:
- secretKey: config.yaml
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/decluttarr/config
metadataPolicy: None
property: config.yaml

View File

@@ -0,0 +1,32 @@
decluttarr:
controllers:
main:
type: deployment
replicas: 1
strategy: Recreate
revisionHistoryLimit: 3
containers:
main:
image:
repository: ghcr.io/manimatter/decluttarr
tag: v2.0.0
pullPolicy: IfNotPresent
env:
- name: TZ
value: America/Chicago
resources:
requests:
cpu: 10m
memory: 128Mi
persistence:
config:
enabled: true
type: secret
name: decluttarr-config-secret
advancedMounts:
main:
main:
- path: /app/config/config.yaml
readOnly: true
mountPropagation: None
subPath: config.yaml

View File

@@ -5,7 +5,8 @@ description: Democratic CSI
keywords:
- democratic-csi-synology-iscsi
- iscsi
home: https://docs.alexlebens.dev/applications/democratic-csi-synology-iscsi/
- kubernetes
home: https://wiki.alexlebens.dev/s/0cc6ba65-024b-4489-952a-fc0f647fd099
sources:
- https://github.com/democratic-csi/democratic-csi
- https://github.com/democratic-csi/charts/tree/master/stable/democratic-csi

View File

@@ -14,5 +14,8 @@ spec:
data:
- secretKey: driver-config-file.yaml
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/democratic-csi-synology-iscsi/config
metadataPolicy: None
property: driver-config-file.yaml

View File

@@ -3,13 +3,12 @@ democratic-csi:
existingConfigSecret: synology-iscsi-config-secret
config:
driver: synology-iscsi
resources:
requests:
cpu: 1m
memory: 128Mi
csiDriver:
name: "org.democratic-csi.iscsi-synology"
controller:
enabled: true
rbac:
enabled: true
replicaCount: 2
storageClasses:
- name: synology-iscsi-delete

View File

@@ -5,7 +5,8 @@ description: Descheduler
keywords:
- descheduler
- kube-scheduler
home: https://docs.alexlebens.dev/applications/descheduler/
- kubernetes
home: https://wiki.alexlebens.dev/s/0c38b7e4-4573-487c-82b0-4eeeb00e1276
sources:
- https://github.com/kubernetes-sigs/descheduler
- https://github.com/kubernetes-sigs/descheduler/tree/master/charts/descheduler

View File

@@ -1,22 +1,27 @@
descheduler:
kind: Deployment
resources:
limits:
cpu: null
memory: null
requests:
cpu: 10m
memory: 50Mi
memory: 64Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
deschedulingInterval: 5m
replicas: 3
replicas: 1
leaderElection:
enabled: true
leaseDuration: 15s
renewDeadline: 10s
retryPeriod: 2s
resourceLock: "leases"
resourceName: "descheduler"
resourceNamespace: "descheduler"
enabled: false
command:
- "/bin/descheduler"
cmdOptions:
v: 3
deschedulerPolicyAPIVersion: "descheduler/v1alpha2"
deschedulerPolicy:
profiles:
- name: default
@@ -48,13 +53,13 @@ descheduler:
- name: LowNodeUtilization
args:
thresholds:
cpu: 20
memory: 20
pods: 20
cpu: 30
memory: 30
pods: 50
targetThresholds:
cpu: 50
memory: 50
pods: 60
cpu: 60
memory: 40
pods: 80
plugins:
balance:
enabled:

View File

@@ -4,9 +4,9 @@ dependencies:
version: 4.6.2
- name: postgres-cluster
repository: oci://harbor.alexlebens.net/helm-charts
version: 7.10.0
version: 7.9.1
- name: valkey
repository: oci://harbor.alexlebens.net/helm-charts
version: 0.4.0
digest: sha256:dfcb5d35e03ecdc4206227d206d36509319f0dcdaed54363840d71337debb3f7
generated: "2026-03-15T20:05:03.156596646Z"
digest: sha256:0b50b4938669a7210930d6ee86a9602611b54cd13774f3386dbad04b4771e7f4
generated: "2026-03-11T22:56:26.818980186Z"

View File

@@ -4,14 +4,16 @@ version: 1.0.0
description: Directus
keywords:
- directus
- content-management-system
home: https://docs.alexlebens.dev/applications/descheduler/
- cms
home: https://wiki.alexlebens.dev/s/c2d242de-dcaa-4801-86a2-c4761dc8bf9b
sources:
- https://github.com/directus/directus
- https://github.com/directus/directus/pkgs/container/directus
- https://github.com/cloudflare/cloudflared
- https://github.com/cloudnative-pg/cloudnative-pg
- https://hub.docker.com/r/directus/directus
- https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/cloudflared
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/postgres-cluster
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/valkey
maintainers:
- name: alexlebens
dependencies:
@@ -21,7 +23,7 @@ dependencies:
version: 4.6.2
- name: postgres-cluster
alias: postgres-18-cluster
version: 7.10.0
version: 7.9.1
repository: oci://harbor.alexlebens.net/helm-charts
- name: valkey
alias: valkey
@@ -29,4 +31,4 @@ dependencies:
repository: oci://harbor.alexlebens.net/helm-charts
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/png/directus.png
# renovate: datasource=github-releases depName=directus/directus
appVersion: 11.17.0
appVersion: 11.16.1

View File

@@ -14,19 +14,31 @@ spec:
data:
- secretKey: admin-email
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/directus/config
metadataPolicy: None
property: admin-email
- secretKey: admin-password
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/directus/config
metadataPolicy: None
property: admin-password
- secretKey: secret
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/directus/config
metadataPolicy: None
property: secret
- secretKey: key
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/directus/config
metadataPolicy: None
property: key
---
@@ -46,11 +58,17 @@ spec:
data:
- secretKey: OIDC_CLIENT_ID
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /authentik/oidc/directus
metadataPolicy: None
property: client
- secretKey: OIDC_CLIENT_SECRET
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /authentik/oidc/directus
metadataPolicy: None
property: secret
---
@@ -70,7 +88,10 @@ spec:
data:
- secretKey: metric-token
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/directus/metrics
metadataPolicy: None
property: metric-token
---
@@ -90,15 +111,24 @@ spec:
data:
- secretKey: ACCESS_KEY_ID
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/directus-assets
metadataPolicy: None
property: ACCESS_KEY_ID
- secretKey: ACCESS_SECRET_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/directus-assets
metadataPolicy: None
property: ACCESS_SECRET_KEY
- secretKey: ACCESS_REGION
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /garage/home-infra/directus-assets
metadataPolicy: None
property: ACCESS_REGION
---
@@ -118,13 +148,22 @@ spec:
data:
- secretKey: default
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/directus/valkey
metadataPolicy: None
property: password
- secretKey: user
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/directus/valkey
metadataPolicy: None
property: user
- secretKey: password
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/directus/valkey
metadataPolicy: None
property: password

View File

@@ -4,11 +4,12 @@ directus:
type: deployment
replicas: 1
strategy: Recreate
revisionHistoryLimit: 3
containers:
main:
image:
repository: ghcr.io/directus/directus
tag: 11.17.0@sha256:076269ccbe7d4a0c44ce5f5b7f11e2ea5f7b3e4c4f704c0f88a52805e069c1c6
repository: directus/directus
tag: 11.16.1
pullPolicy: IfNotPresent
env:
- name: PUBLIC_URL
@@ -143,7 +144,7 @@ directus:
resources:
requests:
cpu: 10m
memory: 1Gi
memory: 256Mi
service:
main:
controller: main
@@ -179,8 +180,11 @@ directus:
- directus.alexlebens.net
rules:
- backendRefs:
- name: directus
- group: ''
kind: Service
name: directus
port: 80
weight: 100
matches:
- path:
type: PathPrefix
@@ -198,12 +202,35 @@ postgres-18-cluster:
destinationBucket: postgres-backups
externalSecretCredentialPath: /garage/home-infra/postgres-backups
isWALArchiver: true
# - name: garage-remote
# index: 1
# destinationBucket: postgres-backups
# externalSecretCredentialPath: /garage/home-infra/postgres-backups
# retentionPolicy: "90d"
# data:
# compression: bzip2
# - name: external
# index: 1
# endpointURL: https://nyc3.digitaloceanspaces.com
# destinationBucket: postgres-backups-ce540ddf106d186bbddca68a
# externalSecretCredentialPath: /garage/home-infra/postgres-backups
# isWALArchiver: false
scheduledBackups:
- name: live-backup
suspend: false
immediate: true
schedule: "0 15 14 * * *"
backupName: garage-local
# - name: weekly-backup
# suspend: true
# immediate: true
# schedule: "0 0 4 * * SAT"
# backupName: garage-remote
# - name: daily-backup
# suspend: true
# immediate: true
# schedule: "0 0 0 * * *"
# backupName: external
valkey:
valkey:
auth:
@@ -212,3 +239,5 @@ valkey:
aclUsers:
default:
permissions: "~* &* +@all"
metrics:
enabled: false

View File

@@ -6,7 +6,8 @@ keywords:
- elastic-operator
- operator
- elastic-search
home: https://docs.alexlebens.dev/applications/elastic-operator/
- kubernetes
home: https://wiki.alexlebens.dev/s/
sources:
- https://github.com/elastic/cloud-on-k8s
- https://github.com/elastic/cloud-on-k8s/tree/main/deploy/eck-operator
@@ -16,6 +17,6 @@ dependencies:
- name: eck-operator
version: 3.3.1
repository: https://helm.elastic.co
icon: https://cdn.jsdelivr.net/gh/selfhst/icons@main/png/elastic.png
icon: https://helm.elastic.co/icons/eck.png
# renovate: datasource=github-releases depName=elastic/cloud-on-k8s
appVersion: v3.3.1

View File

@@ -4,13 +4,6 @@ eck-operator:
- stalwart
installCRDs: true
replicaCount: 2
resources:
limits:
cpu: null
memory: null
requests:
cpu: 2m
memory: 50Mi
telemetry:
disabled: true
config:

View File

@@ -1,9 +1,9 @@
dependencies:
- name: element-web
repository: https://ananace.gitlab.io/charts
version: 1.4.33
version: 1.4.32
- name: cloudflared
repository: oci://harbor.alexlebens.net/helm-charts
version: 2.4.0
digest: sha256:63b0e582d42fb42bcf4d96ba4b299e42c434c42f284208596808288543192fe0
generated: "2026-03-24T16:11:50.424321433Z"
digest: sha256:49d9dd45eff7cbbc11644e4a8bd3c9d3bf84716ed034a76f097f0ba1fea4c934
generated: "2026-03-11T16:04:17.556777286Z"

View File

@@ -4,22 +4,24 @@ version: 1.0.0
description: Element Web
keywords:
- element-web
- matrix-chat
home: https://docs.alexlebens.dev/applications/element-web/
- chat
- matrix
home: https://wiki.alexlebens.dev/s/e3b03481-1a1d-4b56-8cd9-e75a8dcc0f6c
sources:
- https://github.com/element-hq/element-web
- https://github.com/element-hq/element-web/pkgs/container/element-web
- https://github.com/cloudflare/cloudflared
- https://hub.docker.com/r/vectorim/element-web
- https://gitlab.com/ananace/charts/-/tree/master/charts/element-web
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/cloudflared
maintainers:
- name: alexlebens
dependencies:
- name: element-web
version: 1.4.33
version: 1.4.32
repository: https://ananace.gitlab.io/charts
- name: cloudflared
repository: oci://harbor.alexlebens.net/helm-charts
version: 2.4.0
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/png/element.png
# renovate: datasource=github-releases depName=element-hq/element-web
appVersion: v1.12.13
appVersion: v1.12.12

View File

@@ -1,8 +1,9 @@
element-web:
replicaCount: 1
image:
repository: ghcr.io/element-hq/element-web
tag: v1.12.13@sha256:5107e63026c13ed014f743e485821b7d4b56d275a41e76303859bb14f5f94eb6
repository: vectorim/element-web
tag: v1.12.12
pullPolicy: IfNotPresent
defaultServer:
url: https://matrix.alexlebens.dev
name: alexlebens.dev
@@ -17,7 +18,9 @@ element-web:
immediate: true
default_theme: dark
default_country_code: US
ingress:
enabled: false
resources:
requests:
cpu: 1m
memory: 10Mi
cpu: 10m
memory: 128Mi

View File

@@ -5,10 +5,10 @@ description: Eraser
keywords:
- eraser
- images
home: https://docs.alexlebens.dev/applications/eraser/
- kubernetes
home: https://wiki.alexlebens.dev/s/bb53ffae-0eda-4ed6-9fdd-894e672b4377
sources:
- https://github.com/eraser-dev/eraser
- https://github.com/eraser-dev/eraser/pkgs/container/eraser-manager
- https://github.com/eraser-dev/eraser/tree/main/charts/eraser
maintainers:
- name: alexlebens
@@ -16,6 +16,6 @@ dependencies:
- name: eraser
version: 1.4.1
repository: https://eraser-dev.github.io/eraser/charts
icon: https://raw.githubusercontent.com/eraser-dev/eraser/refs/heads/main/images/eraser-logo-color-1c.png
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/png/kubernetes.png
# renovate: datasource=github-releases depName=eraser-dev/eraser
appVersion: v1.4.1

View File

@@ -1,37 +1,70 @@
eraser:
runtimeConfig:
apiVersion: eraser.sh/v1alpha3
kind: EraserConfig
manager:
runtime:
name: containerd
address: unix:///run/containerd/containerd.sock
logLevel: info
scheduling:
repeatInterval: 24h
beginImmediately: true
profile:
enabled: false
port: 6060
imageJob:
successRatio: 1.0
cleanup:
delayOnSuccess: 0s
delayOnFailure: 24h
nodeFilter:
type: exclude
selectors:
- eraser.sh/cleanup.filter
- kubernetes.io/os=windows
components:
collector:
image:
repo: ghcr.io/eraser-dev/collector
tag: v1.4.1@sha256:827588ff826c3558bf2c50b1fc94f20122b054dfcf3480c3ffe6f0bae25c3dad
enabled: true
request:
cpu: 1m
memory: 20Mi
cpu: 10m
memory: 128Mi
scanner:
enabled: false
remover:
image:
repo: ghcr.io/eraser-dev/remover
tag: v1.4.1@sha256:e57592157d717588f69c011cd0b6ab783a19a53b447a5350b27e7e66aae67525
request:
cpu: 1m
memory: 20Mi
cpu: 100m
memory: 128Mi
config: "" # |
# cacheDir: /var/lib/trivy
# dbRepo: ghcr.io/aquasecurity/trivy-db
# deleteFailedImages: true
# deleteEOLImages: true
# vulnerabilities:
# ignoreUnfixed: true
# types:
# - os
# - library
# securityChecks:
# - vuln
# severities:
# - CRITICAL
# - HIGH
# - MEDIUM
# - LOW
# ignoredStatuses:
# timeout:
# total: 23h
# perImage: 1h
remover:
request:
cpu: 10m
memory: 128Mi
deploy:
image:
repo: ghcr.io/eraser-dev/eraser-manager
tag: v1.4.1@sha256:5f18fb7da4ccad93a8643ece496681f1489b0d7b0ce45e18a94774cf8b6a717d
securityContext:
allowPrivilegeEscalation: false
resources:
limits:
memory: null
requests:
cpu: 1m
memory: 20Mi
cpu: 10m
memory: 30Mi
nodeSelector:
kubernetes.io/os: linux

View File

@@ -4,8 +4,7 @@ version: 1.0.0
description: Excalidraw
keywords:
- excalidraw
- drawing
home: https://docs.alexlebens.dev/applications/eraser/
home: https://wiki.alexlebens.dev/
sources:
- https://github.com/excalidraw/excalidraw
- https://hub.docker.com/r/excalidraw/excalidraw

View File

@@ -4,11 +4,13 @@ excalidraw:
type: deployment
replicas: 1
strategy: Recreate
revisionHistoryLimit: 3
containers:
main:
image:
repository: excalidraw/excalidraw
tag: latest@sha256:3c2513e830bb6e195147c05b34ecf8393d0ba2b1cc86e93b407a5777d6135c6c
pullPolicy: IfNotPresent
env:
- name: NODE_ENV
value: production
@@ -16,8 +18,8 @@ excalidraw:
value: America/Chicago
resources:
requests:
cpu: 1m
memory: 10Mi
cpu: 10m
memory: 128Mi
service:
main:
controller: main
@@ -38,8 +40,11 @@ excalidraw:
- excalidraw.alexlebens.net
rules:
- backendRefs:
- name: excalidraw
- group: ''
kind: Service
name: excalidraw
port: 80
weight: 100
matches:
- path:
type: PathPrefix

View File

@@ -5,10 +5,11 @@ description: External DNS
keywords:
- external-dns
- dns
home: https://docs.alexlebens.dev/applications/eraser/
- unifi
- kubernetes
home: https://wiki.alexlebens.dev/s/7b50e4da-5dc1-4f62-baf9-14b5fed64552
sources:
- https://github.com/kubernetes-sigs/external-dns
- https://github.com/kashalls/external-dns-unifi-webhook
- https://github.com/kubernetes-sigs/external-dns/tree/master/charts/external-dns
maintainers:
- name: alexlebens

View File

@@ -14,5 +14,8 @@ spec:
data:
- secretKey: api-key
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /unifi/auth/cl01tl
metadataPolicy: None
property: api-key

View File

@@ -1,27 +1,25 @@
external-dns-unifi:
fullnameOverride: external-dns-unifi
resources:
requests:
cpu: 1m
memory: 80Mi
serviceMonitor:
enabled: true
interval: 360m
interval: 1m
sources:
- ingress
- crd
- gateway-httproute
- gateway-tlsroute
policy: sync
registry: txt
txtOwnerId: default
txtPrefix: k8s.
domainFilters: ["alexlebens.net"]
excludeDomains: ["alexlebens.dev"]
excludeDomains: []
provider:
name: webhook
webhook:
image:
repository: ghcr.io/kashalls/external-dns-unifi-webhook
tag: v0.8.2@sha256:7f0ddbbc83a36a2a9d762e25eef9cafcb3adf0493068a27d72ae71087eafe6f0
tag: v0.8.2
env:
- name: UNIFI_HOST
value: https://192.168.1.1
@@ -31,14 +29,18 @@ external-dns-unifi:
name: external-dns-unifi-secret
key: api-key
- name: LOG_LEVEL
value: info
value: debug
livenessProbe:
httpGet:
path: /healthz
port: http-webhook
initialDelaySeconds: 10
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /readyz
port: http-webhook
initialDelaySeconds: 10
timeoutSeconds: 5
extraArgs:
- --ignore-ingress-tls-spec

View File

@@ -1,6 +1,6 @@
dependencies:
- name: external-secrets
repository: https://charts.external-secrets.io
version: 2.2.0
digest: sha256:3894df20e1f3d56bc9789177181a84d8ae1402ef76ec6328e417ce5a568738ae
generated: "2026-03-26T19:19:15.734454-05:00"
version: 2.1.0
digest: sha256:b19563d51f1922403185979c6c442531a7bb13d302e8438b5a18d450259b7245
generated: "2026-03-07T18:02:23.908145348Z"

View File

@@ -5,17 +5,15 @@ description: External Secrets
keywords:
- external-secrets
- secrets
- operator
home: https://docs.alexlebens.dev/applications/eraser/
- vault
home: https://wiki.alexlebens.dev/s/d29044fb-0d63-4500-8853-2971964f356a
sources:
- https://github.com/external-secrets/external-secrets
- https://github.com/external-secrets/external-secrets/pkgs/container/external-secrets
- https://github.com/external-secrets/external-secrets/tree/main/deploy/charts/external-secrets
dependencies:
- name: external-secrets
alias: external-secrets
version: 2.2.0
version: 2.1.0
repository: https://charts.external-secrets.io
icon: https://raw.githubusercontent.com/external-secrets/external-secrets/refs/heads/main/assets/eso-logo-large.png
icon: https://avatars.githubusercontent.com/u/68335991?s=48&v=4
# renovate: datasource=github-releases depName=external-secrets/external-secrets
appVersion: v2.2.0
appVersion: v2.1.0

View File

@@ -1,44 +0,0 @@
external-secrets:
replicaCount: 3
image:
repository: ghcr.io/external-secrets/external-secrets
tag: v2.2.0@sha256:876e627dbee5b0edd12da49b035469d12418cd6c3c4be5e383ae6a82e8bd4565
installCRDs: true
crds:
createClusterExternalSecret: true
createClusterSecretStore: true
createSecretStore: true
createClusterGenerator: true
createClusterPushSecret: true
createPushSecret: true
leaderElect: true
extendedMetricLabels: true
resources:
requests:
cpu: 5m
memory: 50Mi
serviceMonitor:
enabled: true
livenessProbe:
enabled: true
readinessProbe:
enabled: true
podDisruptionBudget:
enabled: true
minAvailable: 1
webhook:
image:
repository: ghcr.io/external-secrets/external-secrets
tag: v2.2.0@sha256:876e627dbee5b0edd12da49b035469d12418cd6c3c4be5e383ae6a82e8bd4565
resources:
requests:
cpu: 1m
memory: 30Mi
certController:
image:
repository: ghcr.io/external-secrets/external-secrets
tag: v2.2.0@sha256:876e627dbee5b0edd12da49b035469d12418cd6c3c4be5e383ae6a82e8bd4565
resources:
requests:
cpu: 1m
memory: 60Mi

View File

@@ -1,9 +0,0 @@
dependencies:
- name: app-template
repository: https://bjw-s-labs.github.io/helm-charts/
version: 4.6.2
- name: volsync-target
repository: oci://harbor.alexlebens.net/helm-charts
version: 0.8.0
digest: sha256:59100c6fbfb829f9d703b9ee1cf869c4fd77b6ff53c63b0c644a757223027e58
generated: "2026-03-22T12:42:43.150705-05:00"

View File

@@ -1,27 +0,0 @@
apiVersion: v2
name: foldergram
version: 1.0.0
description: Foldergram
keywords:
- foldergram
- pictures
home: https://docs.alexlebens.dev/applications/foldergram/
sources:
- https://github.com/foldergram/foldergram
- https://github.com/foldergram/foldergram/pkgs/container/foldergram
- https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/volsync-target
maintainers:
- name: alexlebens
dependencies:
- name: app-template
alias: foldergram
repository: https://bjw-s-labs.github.io/helm-charts/
version: 4.6.2
- name: volsync-target
alias: volsync-target-data
version: 0.8.0
repository: oci://harbor.alexlebens.net/helm-charts
icon: https://raw.githubusercontent.com/foldergram/foldergram/refs/heads/main/client/public/icon-512.png
# renovate: datasource=github-releases depName=foldergram/foldergram
appVersion: v1.0.8

View File

@@ -1,17 +0,0 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: foldergram-pictures-nfs-storage
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: foldergram-pictures-nfs-storage
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
volumeName: foldergram-pictures-nfs-storage
storageClassName: nfs-client
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi

View File

@@ -1,23 +0,0 @@
apiVersion: v1
kind: PersistentVolume
metadata:
name: foldergram-pictures-nfs-storage
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: foldergram-pictures-nfs-storage
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Release.Name }}
spec:
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs-client
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
nfs:
path: /volume2/Storage/Pictures
server: synologybond.alexlebens.net
mountOptions:
- vers=4
- minorversion=1
- noac

View File

@@ -1,86 +0,0 @@
foldergram:
controllers:
main:
type: deployment
replicas: 1
strategy: Recreate
pod:
securityContext:
fsGroup: 1000
fsGroupChangePolicy: OnRootMismatch
containers:
main:
image:
repository: ghcr.io/foldergram/foldergram
tag: 1.0.8@sha256:3546dc1da4ec12cb27aaecbf77896d708ac7601eb0225e0f6e181d7ef35273f9
pullPolicy: IfNotPresent
env:
- name: IMAGE_DETAIL_SOURCE
value: original
- name: DERIVATIVE_MODE
value: lazy
- name: DATA_ROOT
value: ./data
- name: GALLERY_ROOT
value: /gallery
- name: CSRF_TRUSTED_ORIGINS
value: https://foldergram.alexlebens.net
resources:
requests:
cpu: 1m
memory: 230Mi
service:
main:
controller: main
ports:
http:
port: 80
targetPort: 4141
protocol: HTTP
route:
main:
kind: HTTPRoute
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: traefik-gateway
namespace: traefik
hostnames:
- foldergram.alexlebens.net
rules:
- backendRefs:
- name: foldergram
port: 80
matches:
- path:
type: PathPrefix
value: /
persistence:
cache:
forceRename: foldergram-data
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 10Gi
advancedMounts:
main:
main:
- path: /app/data
readOnly: false
pictures:
existingClaim: foldergram-pictures-nfs-storage
advancedMounts:
main:
main:
- path: /gallery/pictures
readOnly: true
volsync-target-data:
pvcTarget: foldergram-data
local:
enabled: true
schedule: 46 11 * * *
remote:
enabled: true
schedule: 46 12 * * *
external:
enabled: true
schedule: 46 13 * * *

View File

@@ -7,9 +7,9 @@ dependencies:
version: 2.4.0
- name: postgres-cluster
repository: oci://harbor.alexlebens.net/helm-charts
version: 7.10.0
version: 7.9.1
- name: volsync-target
repository: oci://harbor.alexlebens.net/helm-charts
version: 0.8.0
digest: sha256:a7bdbecd50433fedd65d3043102fe3c9e366dc98953c37eb0cfe762bce833e8e
generated: "2026-03-15T20:05:14.085780861Z"
digest: sha256:a3703e245881145524304af8a03c89d309c602479be3f7f8953c2fba120bf341
generated: "2026-03-11T22:56:41.856429843Z"

View File

@@ -5,14 +5,15 @@ description: FreshRSS
keywords:
- freshrss
- rss
home: https://docs.alexlebens.dev/applications/freshrss/
home: https://wiki.alexlebens.dev/s/251cb7cb-2797-4bbb-8597-32757aa96391
sources:
- https://github.com/FreshRSS/FreshRSS
- https://github.com/cloudflare/cloudflared
- https://github.com/cloudnative-pg/cloudnative-pg
- https://hub.docker.com/r/freshrss/freshrss
- https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/cloudflared
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/postgres-cluster
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/volsync-target
maintainers:
- name: alexlebens
dependencies:
@@ -25,7 +26,7 @@ dependencies:
version: 2.4.0
- name: postgres-cluster
alias: postgres-18-cluster
version: 7.10.0
version: 7.9.1
repository: oci://harbor.alexlebens.net/helm-charts
- name: volsync-target
alias: volsync-target-data

View File

@@ -14,15 +14,24 @@ spec:
data:
- secretKey: ADMIN_EMAIL
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/freshrss/config
metadataPolicy: None
property: ADMIN_EMAIL
- secretKey: ADMIN_PASSWORD
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/freshrss/config
metadataPolicy: None
property: ADMIN_PASSWORD
- secretKey: ADMIN_API_PASSWORD
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/freshrss/config
metadataPolicy: None
property: ADMIN_API_PASSWORD
---
@@ -42,13 +51,22 @@ spec:
data:
- secretKey: OIDC_CLIENT_ID
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /authentik/oidc/freshrss
metadataPolicy: None
property: client
- secretKey: OIDC_CLIENT_SECRET
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /authentik/oidc/freshrss
metadataPolicy: None
property: secret
- secretKey: OIDC_CLIENT_CRYPTO_KEY
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /authentik/oidc/freshrss
metadataPolicy: None
property: crypto-key

View File

@@ -4,11 +4,84 @@ freshrss:
type: deployment
replicas: 1
strategy: Recreate
revisionHistoryLimit: 3
initContainers:
init-download-extension-1:
securityContext:
runAsUser: 0
image:
repository: alpine
tag: 3.23.3
pullPolicy: IfNotPresent
command:
- /bin/sh
- -ec
- |
apk add --no-cache git;
cd /tmp;
git clone -n --depth=1 --filter=tree:0 https://github.com/cn-tools/cntools_FreshRssExtensions.git;
cd cntools_FreshRssExtensions;
git sparse-checkout set --no-cone /xExtension-YouTubeChannel2RssFeed;
git checkout;
rm -rf /var/www/FreshRSS/extensions/xExtension-YouTubeChannel2RssFeed
cp -r xExtension-YouTubeChannel2RssFeed /var/www/FreshRSS/extensions
chown -R 568:568 /var/www/FreshRSS/extensions/xExtension-YouTubeChannel2RssFeed
resources:
requests:
cpu: 10m
memory: 128Mi
init-download-extension-2:
securityContext:
runAsUser: 0
image:
repository: alpine
tag: 3.23.3
pullPolicy: IfNotPresent
command:
- /bin/sh
- -ec
- |
apk add --no-cache git;
cd /tmp;
git clone -n --depth=1 --filter=tree:0 https://github.com/FreshRSS/Extensions.git;
cd Extensions;
git sparse-checkout set --no-cone /xExtension-ImageProxy;
git checkout;
rm -rf /var/www/FreshRSS/extensions/xExtension-ImageProxy
cp -r xExtension-ImageProxy /var/www/FreshRSS/extensions
chown -R 568:568 /var/www/FreshRSS/extensions/xExtension-ImageProxy
resources:
requests:
cpu: 10m
memory: 128Mi
init-download-extension-3:
securityContext:
runAsUser: 0
image:
repository: alpine
tag: 3.23.3
pullPolicy: IfNotPresent
command:
- /bin/sh
- -ec
- |
cd /tmp;
wget https://github.com/zimmra/xExtension-karakeep-button/archive/refs/tags/v1.1.tar.gz;
tar -xvzf *.tar.gz;
rm -rf /var/www/FreshRSS/extensions/xExtension-karakeep-button
mkdir /var/www/FreshRSS/extensions/xExtension-karakeep-button
cp -r /tmp/xExtension-karakeep-button-*/* /var/www/FreshRSS/extensions/xExtension-karakeep-button
chown -R 568:568 /var/www/FreshRSS/extensions/xExtension-karakeep-button
resources:
requests:
cpu: 10m
memory: 128Mi
containers:
main:
image:
repository: freshrss/freshrss
tag: 1.28.1@sha256:9100f649f5c946f589f54cdb9be7a65996528f48f691ef90eb262a0e06e5a522
tag: 1.28.1
pullPolicy: IfNotPresent
env:
- name: PGID
value: "568"
@@ -78,7 +151,7 @@ freshrss:
name: freshrss-install-secret
resources:
requests:
cpu: 1m
cpu: 10m
memory: 128Mi
service:
main:
@@ -94,11 +167,31 @@ freshrss:
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 5Gi
retain: true
advancedMounts:
main:
main:
- path: /var/www/FreshRSS/data
readOnly: false
extensions:
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 1Gi
retain: true
advancedMounts:
main:
init-download-extension-1:
- path: /var/www/FreshRSS/extensions
readOnly: false
init-download-extension-2:
- path: /var/www/FreshRSS/extensions
readOnly: false
init-download-extension-3:
- path: /var/www/FreshRSS/extensions
readOnly: false
main:
- path: /var/www/FreshRSS/extensions
readOnly: false
postgres-18-cluster:
mode: recovery
recovery:
@@ -112,12 +205,35 @@ postgres-18-cluster:
destinationBucket: postgres-backups
externalSecretCredentialPath: /garage/home-infra/postgres-backups
isWALArchiver: true
# - name: garage-remote
# index: 1
# destinationBucket: postgres-backups
# externalSecretCredentialPath: /garage/home-infra/postgres-backups
# retentionPolicy: "90d"
# data:
# compression: bzip2
# - name: external
# index: 1
# endpointURL: https://nyc3.digitaloceanspaces.com
# destinationBucket: postgres-backups-ce540ddf106d186bbddca68a
# externalSecretCredentialPath: /garage/home-infra/postgres-backups
# isWALArchiver: false
scheduledBackups:
- name: live-backup
suspend: false
immediate: true
schedule: "0 20 14 * * *"
backupName: garage-local
# - name: weekly-backup
# suspend: true
# immediate: true
# schedule: "0 0 4 * * SAT"
# backupName: garage-remote
# - name: daily-backup
# suspend: true
# immediate: true
# schedule: "0 0 0 * * *"
# backupName: external
volsync-target-data:
pvcTarget: freshrss-data
moverSecurityContext:
@@ -125,6 +241,11 @@ volsync-target-data:
runAsGroup: 568
fsGroup: 568
fsGroupChangePolicy: OnRootMismatch
supplementalGroups:
- 44
- 100
- 109
- 65539
local:
enabled: true
schedule: 18 8 * * *

View File

@@ -4,13 +4,12 @@ version: 1.0.0
description: Garage
keywords:
- garage
- storage
- s3
home: https://docs.alexlebens.dev/applications/garage/
home: https://wiki.alexlebens.dev/s/
sources:
- https://git.deuxfleurs.fr/Deuxfleurs/garage
- https://github.com/khairul169/garage-webui
- https://hub.docker.com/r/dxflrs/garage
- https://hub.docker.com/r/khairul169/garage-webui
- https://github.com/bjw-s-labs/helm-charts/tree/main/charts/other/app-template
maintainers:
- name: alexlebens
@@ -19,6 +18,6 @@ dependencies:
alias: garage
repository: https://bjw-s-labs.github.io/helm-charts/
version: 4.6.2
icon: https://cdn.jsdelivr.net/gh/selfhst/icons@main/png/garage.png
# renovate: datasource=docker depName=dxflrs/garage
appVersion: v2.2.0
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/png/kubernetes.png
# renovate: datasource=github-releases depName=deuxfleurs-org/garage
appVersion: v2.1.0

View File

@@ -14,13 +14,22 @@ spec:
data:
- secretKey: GARAGE_RPC_SECRET
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/garage/token
metadataPolicy: None
property: rpc
- secretKey: GARAGE_ADMIN_TOKEN
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/garage/token
metadataPolicy: None
property: admin
- secretKey: GARAGE_METRICS_TOKEN
remoteRef:
conversionStrategy: Default
decodingStrategy: None
key: /cl01tl/garage/token
metadataPolicy: None
property: metric

View File

@@ -4,6 +4,7 @@ garage:
type: deployment
replicas: 1
strategy: Recreate
revisionHistoryLimit: 3
pod:
labels:
garage-type: server
@@ -21,18 +22,32 @@ garage:
main:
image:
repository: dxflrs/garage
tag: v2.2.0@sha256:45a61ce3f7c9c24fc23d9ed2b09b27ed560ab87b34605d175d5c588f539c24e4
tag: v2.2.0
pullPolicy: IfNotPresent
envFrom:
- secretRef:
name: garage-token-secret
resources:
requests:
cpu: 10m
memory: 400Mi
memory: 128Mi
debug:
image:
repository: ubuntu
tag: resolute-20260108
pullPolicy: IfNotPresent
command:
- "sleep"
- "infinity"
resources:
requests:
cpu: 10m
memory: 32Mi
server-2:
type: deployment
replicas: 1
strategy: Recreate
revisionHistoryLimit: 3
pod:
labels:
garage-type: server
@@ -50,18 +65,20 @@ garage:
main:
image:
repository: dxflrs/garage
tag: v2.2.0@sha256:45a61ce3f7c9c24fc23d9ed2b09b27ed560ab87b34605d175d5c588f539c24e4
tag: v2.2.0
pullPolicy: IfNotPresent
envFrom:
- secretRef:
name: garage-token-secret
resources:
requests:
cpu: 10m
memory: 400Mi
memory: 128Mi
server-3:
type: deployment
replicas: 1
strategy: Recreate
revisionHistoryLimit: 3
pod:
labels:
garage-type: server
@@ -79,23 +96,26 @@ garage:
main:
image:
repository: dxflrs/garage
tag: v2.2.0@sha256:45a61ce3f7c9c24fc23d9ed2b09b27ed560ab87b34605d175d5c588f539c24e4
tag: v2.2.0
pullPolicy: IfNotPresent
envFrom:
- secretRef:
name: garage-token-secret
resources:
requests:
cpu: 10m
memory: 400Mi
memory: 128Mi
webui:
type: deployment
replicas: 1
strategy: Recreate
revisionHistoryLimit: 3
containers:
main:
image:
repository: khairul169/garage-webui
tag: 1.1.0@sha256:17c793551873155065bf9a022dabcde874de808a1f26e648d4b82e168806439c
tag: 1.1.0
pullPolicy: IfNotPresent
env:
- name: API_BASE_URL
value: http://garage-main.garage:3903
@@ -108,8 +128,8 @@ garage:
key: GARAGE_ADMIN_TOKEN
resources:
requests:
cpu: 1m
memory: 10Mi
cpu: 10m
memory: 128Mi
configMaps:
config:
enabled: true
@@ -300,8 +320,11 @@ garage:
- garage-webui.alexlebens.net
rules:
- backendRefs:
- name: garage-webui
- group: ''
kind: Service
name: garage-webui
port: 3909
weight: 100
matches:
- path:
type: PathPrefix
@@ -317,8 +340,11 @@ garage:
- garage-s3.alexlebens.net
rules:
- backendRefs:
- name: garage-main
- group: ''
kind: Service
name: garage-main
port: 3900
weight: 100
matches:
- path:
type: PathPrefix
@@ -335,6 +361,11 @@ garage:
readOnly: true
mountPropagation: None
subPath: garage-1.toml
debug:
- path: /etc/garage.toml
readOnly: true
mountPropagation: None
subPath: garage-1.toml
server-2:
main:
- path: /etc/garage.toml
@@ -358,16 +389,21 @@ garage:
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 50Gi
retain: true
advancedMounts:
server-1:
main:
- path: /var/lib/garage/meta
readOnly: false
debug:
- path: /var/lib/garage/meta
readOnly: false
db-2:
forceRename: garage-db-2
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 50Gi
retain: true
advancedMounts:
server-2:
main:
@@ -378,6 +414,7 @@ garage:
storageClass: ceph-block
accessMode: ReadWriteOnce
size: 50Gi
retain: true
advancedMounts:
server-3:
main:
@@ -388,11 +425,15 @@ garage:
storageClass: synology-iscsi-delete
accessMode: ReadWriteOnce
size: 800Gi
retain: true
advancedMounts:
server-1:
main:
- path: /var/lib/garage/data
readOnly: false
debug:
- path: /var/lib/garage/data
readOnly: false
data-2:
forceRename: garage-data-2
storageClass: synology-iscsi-delete

View File

@@ -4,9 +4,9 @@ dependencies:
version: 1.5.0
- name: postgres-cluster
repository: oci://harbor.alexlebens.net/helm-charts
version: 7.10.0
version: 7.9.1
- name: volsync-target
repository: oci://harbor.alexlebens.net/helm-charts
version: 0.8.0
digest: sha256:83ec84774e0cc708f1cb5d83d657180159bfb75c9928784ebf0280e224b1cbca
generated: "2026-03-15T20:05:27.625292422Z"
digest: sha256:2fe7c088e99a11e0c6dd09fe48bb1e292eb58e22d9f8ff681bb6c6790945d54e
generated: "2026-03-11T22:56:56.957400817Z"

View File

@@ -4,14 +4,16 @@ version: 1.0.0
description: Gatus
keywords:
- gatus
- uptime-monitor
home: https://docs.alexlebens.dev/applications/gatus/
- healthcheck
- uptime
- metrics
home: https://wiki.alexlebens.dev/s/2a2b0c83-81c7-49e3-aafc-daff4ff23ce2
sources:
- https://github.com/TwiN/gatus
- https://github.com/cloudnative-pg/cloudnative-pg
- https://github.com/TwiN/gatus/pkgs/container/gatus
- https://github.com/TwiN/helm-charts/tree/master/charts/gatus
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/postgres-cluster
- https://gitea.alexlebens.dev/alexlebens/helm-charts/src/branch/main/charts/volsync-target
maintainers:
- name: alexlebens
dependencies:
@@ -20,7 +22,7 @@ dependencies:
version: 1.5.0
- name: postgres-cluster
alias: postgres-18-cluster
version: 7.10.0
version: 7.9.1
repository: oci://harbor.alexlebens.net/helm-charts
- name: volsync-target
alias: volsync-target-data

Some files were not shown because too many files have changed in this diff Show More