117 lines
2.9 KiB
Bash
Executable File
117 lines
2.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
PHASE="${1:-}"
|
|
|
|
usage() {
|
|
echo "Usage: scripts/ci <format|lint|build|test|security|all>" >&2
|
|
exit 2
|
|
}
|
|
|
|
if [[ -z "${PHASE}" ]]; then
|
|
usage
|
|
fi
|
|
|
|
repo_root() {
|
|
git rev-parse --show-toplevel 2>/dev/null || pwd
|
|
}
|
|
|
|
run_outside_container() {
|
|
local phase="$1"
|
|
local root
|
|
root="$(repo_root)"
|
|
if ! command -v docker >/dev/null 2>&1; then
|
|
echo "Docker is required to run CI tasks locally." >&2
|
|
exit 1
|
|
fi
|
|
if ! command -v docker-compose >/dev/null 2>&1 && ! docker compose version >/dev/null 2>&1; then
|
|
echo "Docker Compose v2+ is required (docker compose)." >&2
|
|
exit 1
|
|
fi
|
|
# Build ci image if needed and run the requested phase inside the container
|
|
# Important: detach stdin so git hook-provided stdin (ref updates)
|
|
# doesn't get executed inside the container shell.
|
|
(cd "$root" && docker compose -f docker/ci.compose.yml run --rm \
|
|
-e IN_CI_CONTAINER=1 \
|
|
ci bash -lc "cd /workspace && scripts/ci --inside ${phase}" </dev/null)
|
|
}
|
|
|
|
run_format() {
|
|
echo ">> Formatting"
|
|
# shell: format in-place
|
|
shfmt -bn -ci -i 2 -w .
|
|
# prettier for markdown/yaml/json/etc
|
|
prettier --log-level warn --write \
|
|
"**/*.md" "**/*.yaml" "**/*.yml" "**/*.json" \
|
|
"**/*.css" "**/*.html" 2>/dev/null || true
|
|
}
|
|
|
|
run_lint() {
|
|
echo ">> Linting"
|
|
# shellcheck
|
|
mapfile -t sh_files < <(git ls-files -z | xargs -0 file --mime-type | awk -F: '/(x-shellscript|text\/x-shellscript)/{print $1}'; git ls-files "*.sh")
|
|
if [[ ${#sh_files[@]} -gt 0 ]]; then
|
|
shellcheck -x "${sh_files[@]}" || (echo "Shellcheck failed" && exit 1)
|
|
shfmt -d .
|
|
fi
|
|
# hadolint on Dockerfiles
|
|
if ls Dockerfile* docker/*Dockerfile* 1>/dev/null 2>&1; then
|
|
hadolint Dockerfile* docker/*Dockerfile* 2>/dev/null || true
|
|
fi
|
|
# yamllint
|
|
if git ls-files "*.yml" "*.yaml" | grep -q .; then
|
|
yamllint -s $(git ls-files "*.yml" "*.yaml")
|
|
fi
|
|
# markdownlint
|
|
if git ls-files "*.md" | grep -q .; then
|
|
markdownlint $(git ls-files "*.md")
|
|
fi
|
|
# actionlint for workflow files if present
|
|
if [ -d .gitea/workflows ]; then
|
|
actionlint -color
|
|
fi
|
|
}
|
|
|
|
run_build() {
|
|
echo ">> Build checks"
|
|
# Validate docker compose configs if present
|
|
if [ -f docker-compose.yml ] || [ -f docker/compose.yml ]; then
|
|
docker compose config -q
|
|
fi
|
|
}
|
|
|
|
run_test() {
|
|
echo ">> Tests (none defined)"
|
|
}
|
|
|
|
run_security() {
|
|
echo ">> Security checks (skipped for this repo)"
|
|
}
|
|
|
|
run_inside_container() {
|
|
local phase="$1"
|
|
case "$phase" in
|
|
format) run_format ;;
|
|
lint) run_lint ;;
|
|
build) run_build ;;
|
|
test) run_test ;;
|
|
security) run_security ;;
|
|
all) run_format; run_lint; run_build; run_test; run_security ;;
|
|
*) usage ;;
|
|
esac
|
|
}
|
|
|
|
if [[ "${1:-}" == "--inside" ]]; then
|
|
shift
|
|
PHASE="${1:-}"
|
|
[[ -z "$PHASE" ]] && usage
|
|
run_inside_container "$PHASE"
|
|
exit 0
|
|
fi
|
|
|
|
if [[ "${IN_CI_CONTAINER:-}" != "1" ]]; then
|
|
run_outside_container "$PHASE"
|
|
else
|
|
run_inside_container "$PHASE"
|
|
fi
|