ci-workflows/.forgejo/workflows/lint.yml
2026-10-18 22:13:10 +01:00

103 lines
4.2 KiB
YAML

# Lint gates, each switched on by the files present in the calling repo.
#
# shell scripts shellcheck (*.sh, *.bash)
# Python ruff check (repo's ruff/pyproject config if any)
# Dockerfiles hadolint (repo's .hadolint.yaml if any)
# YAML yamllint (repo's .yamllint if any, else relaxed)
# CI workflows actionlint (.forgejo/ and .github/ workflows)
# OpenTofu/HCL tofu fmt + tflint (repo's .tflint.hcl if any)
#
# Every linter runs even if an earlier one failed, so one push shows every
# problem; the job fails if any of them did. Turn one off with its input.
on:
workflow_call:
inputs:
shellcheck:
type: boolean
default: true
shellcheck-severity:
description: Lowest shellcheck severity that fails the job (error, warning, info, style).
type: string
default: warning
ruff:
type: boolean
default: true
ruff-format:
description: Also require `ruff format --check` to pass.
type: boolean
default: false
hadolint:
type: boolean
default: true
yamllint:
type: boolean
default: true
actionlint:
type: boolean
default: true
terraform:
type: boolean
default: true
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- name: shellcheck
if: ${{ !cancelled() && inputs.shellcheck }}
env:
SEVERITY: ${{ inputs.shellcheck-severity }}
run: |
if [ -z "$(git ls-files '*.sh' '*.bash')" ]; then echo "No shell scripts; skipping."; exit 0; fi
git ls-files -z '*.sh' '*.bash' | xargs -0 shellcheck --severity="$SEVERITY"
- name: ruff
if: ${{ !cancelled() && inputs.ruff }}
env:
FORMAT: ${{ inputs.ruff-format }}
run: |
if [ -z "$(git ls-files '*.py' '*.pyi')" ]; then echo "No Python; skipping."; exit 0; fi
ruff --version
ruff check --output-format=full .
if [ "$FORMAT" = "true" ]; then ruff format --check .; fi
- name: hadolint
if: ${{ !cancelled() && inputs.hadolint }}
run: |
set -- 'Dockerfile' '*/Dockerfile' 'Dockerfile.*' '*/Dockerfile.*' '*.Dockerfile' 'Containerfile' '*/Containerfile'
if [ -z "$(git ls-files "$@")" ]; then echo "No Dockerfiles; skipping."; exit 0; fi
git ls-files -z "$@" | xargs -0 hadolint --failure-threshold warning
- name: yamllint
if: ${{ !cancelled() && inputs.yamllint }}
run: |
if [ -z "$(git ls-files '*.yml' '*.yaml')" ]; then echo "No YAML; skipping."; exit 0; fi
cfg=""
for f in .yamllint .yamllint.yml .yamllint.yaml; do if [ -f "$f" ]; then cfg="$f"; fi; done
# Without a repo config: relaxed rules, and no line-length check,
# which mostly flags long URLs and Kubernetes annotations.
if [ -n "$cfg" ]; then set -- -c "$cfg"
else set -- -d '{extends: relaxed, rules: {line-length: disable}}'; fi
git ls-files -z '*.yml' '*.yaml' | xargs -0 yamllint -f parsable "$@"
- name: actionlint
if: ${{ !cancelled() && inputs.actionlint }}
run: |
set -- '.forgejo/workflows/*.yml' '.forgejo/workflows/*.yaml' '.github/workflows/*.yml' '.github/workflows/*.yaml'
if [ -z "$(git ls-files "$@")" ]; then echo "No workflow files; skipping."; exit 0; fi
# Forgejo needs runs-on on a job that calls a cross-repo reusable
# workflow (the runner expands it, not the server), which GitHub
# forbids. Ignore that one GitHub-only rule.
git ls-files -z "$@" | xargs -0 actionlint -shellcheck= -ignore '"runs-on" is not available'
- name: OpenTofu fmt and tflint
if: ${{ !cancelled() && inputs.terraform }}
run: |
if [ -z "$(git ls-files '*.tf')" ]; then echo "No .tf files; skipping."; exit 0; fi
rc=0
tofu fmt -check -recursive -diff || rc=1
if [ -f .tflint.hcl ]; then tflint --init; fi
tflint --recursive --format compact || rc=1
exit "$rc"