.
This commit is contained in:
parent
db5d576afc
commit
16baea21f0
1 changed files with 89 additions and 127 deletions
208
.checks/pre-push
208
.checks/pre-push
|
|
@ -2,139 +2,101 @@
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Colors
|
# Colors
|
||||||
readonly RED='\033[0;31m'
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
||||||
readonly GREEN='\033[0;32m'
|
|
||||||
readonly YELLOW='\033[1;33m'
|
|
||||||
readonly NC='\033[0m'
|
|
||||||
|
|
||||||
readonly REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
echo -e "${GREEN}Running pre-push checks...${NC}"
|
||||||
readonly LOG_DIR="${REPO_ROOT}/.checks/logs"
|
|
||||||
|
|
||||||
log() { echo -e "${GREEN}[INFO] $*${NC}"; }
|
# Paths
|
||||||
warn() { echo -e "${YELLOW}[WARN] $*${NC}"; }
|
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
||||||
err() { echo -e "${RED}[ERROR] $*${NC}" >&2; }
|
CONTENT_DIR="$REPO_ROOT/content"
|
||||||
|
ZENSICAL_CFG_PATH="$REPO_ROOT/zensical.toml"
|
||||||
|
SITE_DIR="$REPO_ROOT/deploy"
|
||||||
|
LOG_DIR="$REPO_ROOT/logs"
|
||||||
|
|
||||||
prepare_logs() {
|
# Clean logs
|
||||||
rm -rf "$LOG_DIR"
|
rm -rf "$LOG_DIR"; mkdir -p "$LOG_DIR"
|
||||||
mkdir -p "$LOG_DIR"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Files that git considers "scannable" (tracked + untracked not ignored)
|
SERVER_PID=""
|
||||||
scannable_files() {
|
cleanup() {
|
||||||
git ls-files --cached --modified --others --exclude-standard
|
if [[ -n "${SERVER_PID:-}" ]] && ps -p "$SERVER_PID" >/dev/null 2>&1; then
|
||||||
}
|
kill "$SERVER_PID" >/dev/null 2>&1 || true
|
||||||
|
for _ in {1..30}; do ps -p "$SERVER_PID" >/dev/null 2>&1 || break; sleep 0.1; done
|
||||||
# Files listed as ignored by git (used to build tool-specific skip args)
|
|
||||||
ignored_files() {
|
|
||||||
git ls-files -o -i --exclude-standard
|
|
||||||
}
|
|
||||||
|
|
||||||
# Build Trivy skip args from git-ignored paths
|
|
||||||
trivy_skip_args() {
|
|
||||||
local path dir
|
|
||||||
while IFS= read -r path; do
|
|
||||||
[[ -z "$path" ]] && continue
|
|
||||||
dir="$(dirname "$path")"
|
|
||||||
printf -- '--skip-files=%q ' "$path"
|
|
||||||
printf -- '--skip-dirs=%q ' "$dir"
|
|
||||||
done < <(ignored_files)
|
|
||||||
}
|
|
||||||
|
|
||||||
run_tool() {
|
|
||||||
local name="$1"; shift
|
|
||||||
if ! command -v "$name" &>/dev/null; then
|
|
||||||
warn "$name not installed; skipping."
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
"$@"
|
|
||||||
}
|
|
||||||
|
|
||||||
run_trivy() {
|
|
||||||
run_tool trivy bash -c '
|
|
||||||
files=("$@")
|
|
||||||
if [[ ${#files[@]} -eq 0 ]]; then
|
|
||||||
echo "no files"; exit 0
|
|
||||||
fi
|
|
||||||
# Expand skip args passed via environment variable SKIP_ARGS
|
|
||||||
eval trivy fs "${files[@]}" $SKIP_ARGS --exit-code 1 --severity CRITICAL,HIGH,MEDIUM,LOW,UNKNOWN --no-progress
|
|
||||||
' _ $(scannable_files)
|
|
||||||
}
|
|
||||||
|
|
||||||
run_trufflehog() {
|
|
||||||
run_tool trufflehog bash -c '
|
|
||||||
files=("$@")
|
|
||||||
tmpf="$(mktemp)"
|
|
||||||
printf "%s\n" "${files[@]}" | xargs trufflehog filesystem --json >"$tmpf" 2>"'"$LOG_DIR"'/trufflehog.log" || true
|
|
||||||
if command -v jq &>/dev/null; then
|
|
||||||
if jq -e '"'"'any(inputs; .verified==true)'"'"' "$tmpf" >/dev/null 2>&1; then
|
|
||||||
cp "$tmpf" "'"$LOG_DIR"'/trufflehog-findings.json"
|
|
||||||
echo "verified"; exit 2
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
rm -f "$tmpf"
|
|
||||||
' _ $(git ls-files)
|
|
||||||
local rc=$?; [[ $rc -eq 2 ]] && { err "TruffleHog found verified secrets. See $LOG_DIR/trufflehog-findings.json"; return 1; }
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
run_git_secrets() {
|
|
||||||
run_tool git-secrets git-secrets --scan >"$LOG_DIR/git-secrets.log" 2>&1 || {
|
|
||||||
err "git-secrets detected potential secrets. See $LOG_DIR/git-secrets.log"; return 1; }
|
|
||||||
}
|
|
||||||
|
|
||||||
run_bandit() {
|
|
||||||
run_tool bandit bash -c '
|
|
||||||
py=("$@")
|
|
||||||
[[ ${#py[@]} -eq 0 ]] && exit 0
|
|
||||||
bandit -r "${py[@]}" -n 5 -lll -f json -o "'"$LOG_DIR"'/bandit.json" 2>"'"$LOG_DIR"'/bandit.log"
|
|
||||||
' _ $(git ls-files '*.py')
|
|
||||||
}
|
|
||||||
|
|
||||||
run_npm_audit() {
|
|
||||||
if [[ -f package.json ]]; then
|
|
||||||
run_tool npm npm audit --audit-level=high >"$LOG_DIR/npm-audit.log" 2>&1 || {
|
|
||||||
err "npm audit found issues. See $LOG_DIR/npm-audit.log"; return 1; }
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
trap cleanup EXIT INT TERM
|
||||||
|
|
||||||
run_pip_audit() {
|
# Trivy check for vulnerabilities in dependencies
|
||||||
if [[ -f requirements.txt ]] || [[ -f pyproject.toml ]]; then
|
if command -v trivy &>/dev/null; then
|
||||||
run_tool pip-audit pip-audit -r requirements.txt >"$LOG_DIR/pip-audit.log" 2>&1 || {
|
echo -e "${GREEN}Running Trivy scan...${NC}"
|
||||||
err "pip-audit found issues. See $LOG_DIR/pip-audit.log"; return 1; }
|
trivy fs . --exit-code 1 --severity CRITICAL,HIGH,MEDIUM,LOW,UNKNOWN --no-progress \
|
||||||
|
>"$LOG_DIR/trivy.log" 2>&1 || { echo -e "${RED}Trivy failed. See $LOG_DIR/trivy.log${NC}"; exit 1; }
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Trivy not installed. Skipping vulnerability scan.${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Trufflehog check for passwords and secrets
|
||||||
|
if command -v trufflehog &>/dev/null && command -v jq &>/dev/null; then
|
||||||
|
echo -e "${GREEN}Running TruffleHog (verified only)...${NC}"
|
||||||
|
TMPF="$(mktemp)"
|
||||||
|
trufflehog filesystem . --json >"$TMPF" 2>"$LOG_DIR/trufflehog.log" || true
|
||||||
|
VERIFIED="$(jq 'select(.verified==true)' "$TMPF" | wc -l | tr -d ' ')"
|
||||||
|
if [[ "$VERIFIED" -gt 0 ]]; then
|
||||||
|
cp "$TMPF" "$LOG_DIR/trufflehog-findings.json"
|
||||||
|
echo -e "${RED}Verified secrets found. See $LOG_DIR/trufflehog-findings.json${NC}"
|
||||||
|
rm -f "$TMPF"; exit 1
|
||||||
fi
|
fi
|
||||||
}
|
rm -f "$TMPF"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}TruffleHog or jq not installed. Skipping secrets scan.${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
run_stylelint() {
|
# Dependabot-like dependency vulnerability check
|
||||||
run_tool stylelint bash -c '
|
if command -v npm &>/dev/null && [[ -f package.json ]]; then
|
||||||
files=("$@")
|
echo -e "${GREEN}Running npm audit...${NC}"
|
||||||
[[ ${#files[@]} -eq 0 ]] && exit 0
|
npm audit --audit-level=high >"$LOG_DIR/npm-audit.log" 2>&1 || {
|
||||||
stylelint "${files[@]}" --formatter json > "'"$LOG_DIR"'/stylelint.json" 2>"'"$LOG_DIR"'/stylelint.log"
|
echo -e "${RED}npm audit found vulnerabilities. See $LOG_DIR/npm-audit.log${NC}"
|
||||||
' _ $(git ls-files '*.{css,scss,sass,less}')
|
|
||||||
}
|
|
||||||
|
|
||||||
main() {
|
|
||||||
prepare_logs
|
|
||||||
|
|
||||||
# Precompute skip args for trivy (safe quoting)
|
|
||||||
export SKIP_ARGS
|
|
||||||
SKIP_ARGS="$(trivy_skip_args || true)"
|
|
||||||
|
|
||||||
local failed=0
|
|
||||||
|
|
||||||
run_trivy || failed=1
|
|
||||||
run_trufflehog || failed=1
|
|
||||||
run_git_secrets || failed=1
|
|
||||||
run_bandit || failed=1
|
|
||||||
run_npm_audit || failed=1
|
|
||||||
run_pip_audit || failed=1
|
|
||||||
run_stylelint || failed=1
|
|
||||||
|
|
||||||
if [[ $failed -ne 0 ]]; then
|
|
||||||
err "Pre-push checks failed. Check logs in $LOG_DIR"
|
|
||||||
exit 1
|
exit 1
|
||||||
|
}
|
||||||
|
elif command -v pip &>/dev/null && [[ -f requirements.txt ]]; then
|
||||||
|
echo -e "${GREEN}Running pip dependency check...${NC}"
|
||||||
|
pip list --outdated >"$LOG_DIR/pip-outdated.log" 2>&1 || true
|
||||||
|
if grep -q "upgradable" "$LOG_DIR/pip-outdated.log"; then
|
||||||
|
echo -e "${YELLOW}Outdated Python dependencies found. See $LOG_DIR/pip-outdated.log${NC}"
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}No dependency management files found. Skipping dependency checks.${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
log "All pre-push checks passed."
|
# Lint all the markdown files using markdownlint-cli2
|
||||||
}
|
if command -v markdownlint-cli2 &>/dev/null; then
|
||||||
|
echo -e "${GREEN}Running markdownlint...${NC}"
|
||||||
|
MD_FILES="$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.md$' || true)"
|
||||||
|
if [[ -n "$MD_FILES" ]]; then
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
echo $MD_FILES | xargs markdownlint-cli2 >"$LOG_DIR/markdownlint.log" 2>&1 || {
|
||||||
|
echo -e "${RED}markdownlint-cli2 failed. See $LOG_DIR/markdownlint.log${NC}"; exit 1; }
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}markdownlint-cli2 not installed. Skipping markdown check.${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
main "$@"
|
# Lint language using Vale
|
||||||
|
if command -v vale &>/dev/null && [[ -f "$REPO_ROOT/.vale.ini" ]]; then
|
||||||
|
echo -e "${GREEN}Running Vale...${NC}"
|
||||||
|
VALE_FILES="$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.md$' || true)"
|
||||||
|
if [[ -n "$VALE_FILES" ]]; then
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
echo $VALE_FILES | xargs vale >"$LOG_DIR/vale.log" 2>&1 || {
|
||||||
|
echo -e "${RED}Vale issues. See $LOG_DIR/vale.log${NC}"; exit 1; }
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Vale not installed or .vale.ini missing. Skipping Vale.${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build the site using Zensical to check for build errors
|
||||||
|
if ! command -v zensical >/dev/null 2>&1; then
|
||||||
|
echo -e "${RED}Zensical not installed; cannot build docs.${NC}"; exit 1
|
||||||
|
fi
|
||||||
|
echo -e "${GREEN}Building documentation (strict)…${NC}"
|
||||||
|
zensical build -f "$ZENSICAL_CFG_PATH" -d "$SITE_DIR" --strict >"$LOG_DIR/zensical-build.log" 2>&1 || {
|
||||||
|
echo -e "${RED}Zensical build failed. See $LOG_DIR/zensical-build.log${NC}"; exit 1; }
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue