#!/usr/bin/env bash # ci/lint.sh — Stage 1: yaml/lint. Runs in CI without a cluster (live now). # Lints all YAML in the repo against ci/yamllint.yaml. set -euo pipefail CI_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # shellcheck disable=SC1091 source "${CI_DIR}/lib.sh" log "Stage: yaml/lint" require_tool yamllint "Install: pip install 'yamllint==${YAMLLINT_VERSION}'" # Lint tracked + untracked (not yet committed) YAML; skip .git and artifact dirs. cd "$REPO_ROOT" mapfile -t files < <( { git ls-files '*.yaml' '*.yml' 2>/dev/null git ls-files --others --exclude-standard '*.yaml' '*.yml' 2>/dev/null } | sort -u ) # Fallback for a non-git checkout. if [ "${#files[@]}" -eq 0 ]; then mapfile -t files < <(find . -path ./.git -prune -o \( -name '*.yaml' -o -name '*.yml' \) -print) fi if [ "${#files[@]}" -eq 0 ]; then ok "no YAML files to lint" exit 0 fi yamllint -c "${CI_DIR}/yamllint.yaml" -f "${YAMLLINT_FORMAT:-standard}" "${files[@]}" ok "yaml/lint passed (${#files[@]} files)" # A workflow's `configurationFile:` must name a file that exists. Renovate's did not after #79 # renamed renovate.json -> renovate.json5, and every scheduled run failed with ENOENT for 11 days # while the gate stayed green. Valid YAML, so yamllint cannot see it. missing=0 while IFS=: read -r wf _ value; do value="${value#*configurationFile:}" value="$(echo "$value" | sed -E 's/#.*//; s/^[[:space:]"'\'']+//; s/[[:space:]"'\'']+$//')" if [ -n "$value" ] && [ ! -e "$value" ]; then err "${wf}: configurationFile '${value}' does not exist in the repo" missing=1 fi done < <(grep -nE '^[[:space:]]*configurationFile:' .gitea/workflows/*.y*ml 2>/dev/null || true) [ "$missing" -eq 0 ] || exit 1 ok "workflow configurationFile references resolve" # A workflow must not use an action that needs a Docker daemon: jobs on this runner get NONE, by # decision (decisions/2026-10-29-image-pipeline.md; act_runner maps docker_host to `-`). Such an # action is valid YAML and passes every other stage, then dies at runtime with # "failed to connect to the docker API at unix:///var/run/docker.sock". renovatebot/github-action # did exactly that on every Renovate run until 2026-10-30 (run 343). Run the tool's image as the # job `container:` instead (see .gitea/workflows/renovate.yaml). Container (`docker://`) actions # and Docker's own daemon-bound actions are rejected for the same reason. Comments are ignored. daemon_uses='uses:[[:space:]]*["'\'']?(docker://|renovatebot/github-action|docker/(build-push|setup-buildx|setup-qemu|login)-action)' bad=0 while IFS= read -r hit; do err "${hit%%:*}: needs a Docker daemon, which jobs here do not get: $(echo "${hit#*:*:}" | sed -E 's/^[[:space:]]+//')" bad=1 done < <(grep -HnE "^[^#]*${daemon_uses}" .gitea/workflows/*.y*ml 2>/dev/null || true) [ "$bad" -eq 0 ] || exit 1 ok "no workflow uses a Docker-daemon-bound action"