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