feat: add pre-commit configuration #9

Merged
unkinben merged 1 commits from benvin/pre-commit into main 2026-03-02 00:09:21 +11:00
5 changed files with 150 additions and 0 deletions

53
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,53 @@
repos:
# General file checks
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: check-executables-have-shebangs
- id: check-json
- id: check-added-large-files
args: ['--maxkb=500']
- id: check-merge-conflict
- id: check-shebang-scripts-are-executable
- id: check-symlinks
- id: check-toml
- id: check-yaml
args: [--allow-multiple-documents]
- id: detect-aws-credentials
args: [--allow-missing-credentials]
- id: detect-private-key
- id: end-of-file-fixer
- id: forbid-new-submodules
- id: no-commit-to-branch
- id: pretty-format-json
- id: trailing-whitespace
# YAML linting
- repo: https://github.com/adrienverge/yamllint.git
rev: v1.37.1
hooks:
- id: yamllint
args:
[
"-d {extends: relaxed, rules: {line-length: disable}, ignore: chart}",
"-s",
]
# Kubernetes manifest validation
- repo: local
hooks:
- id: kubeconform_validate_apps
name: kubeconform validate apps
entry: ci/validate-apps.sh
language: system
pass_filenames: false
- id: kubeconform_validate_clusters
name: kubeconform validate clusters
entry: ci/validate-clusters.sh
language: system
pass_filenames: false
- id: no_plain_secrets
name: prevent plain kubernetes secrets
entry: ci/validate-no-secrets.sh
language: system
pass_filenames: false

29
.yamllint.yaml Normal file
View File

@ -0,0 +1,29 @@
# .yamllint.yaml
extends: default
rules:
# Allow long lines for base64 encoded values and URLs
line-length:
max: 200
allow-non-breakable-words: true
allow-non-breakable-inline-mappings: true
# Kubernetes manifests use 2-space indentation
indentation:
spaces: 2
indent-sequences: consistent
# Allow multiple documents (---) in a single file
document-start: enable
# Be lenient with comments
comments:
require-starting-space: true
min-spaces-from-content: 1
# Allow empty values (common in Kustomize patches)
empty-values: enable
truthy:
# Allow 'on' and 'yes' values (common in Kubernetes)
allowed-values: ['true', 'false', 'yes', 'no', 'on', 'off']

23
ci/validate-apps.sh Executable file
View File

@ -0,0 +1,23 @@
#!/usr/bin/env bash
set -euo pipefail
KUBE_VERSION="1.33.7"
schema_args=(
-schema-location "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/{{.NormalizedKubernetesVersion}}-standalone{{.StrictSuffix}}/{{.ResourceKind}}{{.KindSuffix}}.json"
-schema-location "https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json"
)
while IFS= read -r -d "" k; do
dir="$(dirname "$k")"
echo "==> kubeconform: $dir" >&2
kustomize build --enable-helm "$dir" \
| kubeconform \
-kubernetes-version "$KUBE_VERSION" \
-summary \
-output pretty \
-verbose \
-skip CustomResourceDefinition \
"${schema_args[@]}"
done < <(find apps/overlays -name kustomization.yaml -print0)

23
ci/validate-clusters.sh Executable file
View File

@ -0,0 +1,23 @@
#!/usr/bin/env bash
set -euo pipefail
KUBE_VERSION="1.33.7"
schema_args=(
-schema-location "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/{{.NormalizedKubernetesVersion}}-standalone{{.StrictSuffix}}/{{.ResourceKind}}{{.KindSuffix}}.json"
-schema-location "https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json"
)
while IFS= read -r -d "" k; do
dir="$(dirname "$k")"
echo "==> kubeconform: $dir" >&2
kustomize build --enable-helm "$dir" \
| kubeconform \
-kubernetes-version "$KUBE_VERSION" \
-summary \
-output pretty \
-verbose \
-skip CustomResourceDefinition \
"${schema_args[@]}"
done < <(find clusters -name kustomization.yaml -print0)

22
ci/validate-no-secrets.sh Executable file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env bash
set -euo pipefail
# Check staged files for plain Kubernetes Secrets
ERRORS=0
while IFS= read -r -d '' file; do
# Skip if file doesn't exist (e.g., deleted files)
[[ -f "$file" ]] || continue
# Check if the file contains a plain Kubernetes Secret
if grep -q "^kind: Secret" "$file"; then
# Allow secure secret types
if ! grep -q -E "^kind: (SealedSecret|ExternalSecret|VaultStaticSecret|VaultDynamicSecret)" "$file"; then
echo "BLOCKED: $file contains a plain Kubernetes Secret" >&2
echo " Use VaultStaticSecret or VaultDynamicSecret instead" >&2
((ERRORS++))
fi
fi
done < <(git diff --cached --name-only --diff-filter=ACM -z | grep -zE '\.(yaml|yml)$')
exit $ERRORS