# 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"