refactor: 폴더 구조 변경
This commit is contained in:
Executable
+54
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env bash
|
||||
# Lightweight documentation gate. Manifest examples remain reviewable prose, so
|
||||
# this gate checks deterministic repository contracts instead of applying them.
|
||||
|
||||
set -Eeuo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=../lib/common.sh
|
||||
. "$SCRIPT_DIR/../lib/common.sh"
|
||||
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
require_cmd rg
|
||||
failed=0
|
||||
OPERATIONAL_DOCS=(
|
||||
"$REPO_ROOT/README.md"
|
||||
"$REPO_ROOT/guide.md"
|
||||
"$REPO_ROOT/gitops/PROJECT.md"
|
||||
"$REPO_ROOT/gitops/clusters/lab/main/README.md"
|
||||
"$REPO_ROOT/docs/architecture.md"
|
||||
"$REPO_ROOT/docs/operations.md"
|
||||
"$REPO_ROOT/docs/networking.md"
|
||||
"$REPO_ROOT/docs/ingress-traefik.md"
|
||||
"$REPO_ROOT/docs/security-hardening.md"
|
||||
"$REPO_ROOT/docs/troubleshooting.md"
|
||||
"$REPO_ROOT/docs/vault-vso.md"
|
||||
)
|
||||
|
||||
if rg -n $'\t' "$REPO_ROOT/docs" --glob '*.md'; then
|
||||
err "docs: tab 문자를 사용한 Markdown이 있습니다."
|
||||
failed=$((failed + 1))
|
||||
fi
|
||||
|
||||
if rg -n 'k8s/(base|components|overlays|scripts)|terraform/(modules|environments)|base/(app|managing|plugins)/' \
|
||||
"$REPO_ROOT/README.md" \
|
||||
"$REPO_ROOT/AGENTS.md" \
|
||||
"$REPO_ROOT/guide.md" \
|
||||
"$REPO_ROOT/bootstrap" \
|
||||
"$REPO_ROOT/infrastructure" \
|
||||
"$REPO_ROOT/gitops" \
|
||||
"$REPO_ROOT/scripts/README.md" \
|
||||
"$REPO_ROOT/scripts/AGENTS.md" \
|
||||
"$REPO_ROOT/docs" \
|
||||
--glob '*.md'; then
|
||||
err "docs: 폐기된 Kustomize 경로가 남아 있습니다."
|
||||
failed=$((failed + 1))
|
||||
fi
|
||||
|
||||
if rg -n '(password|token|secret)[[:space:]]*=[[:space:]]*\"[^<${][^\"]+\"' \
|
||||
"${OPERATIONAL_DOCS[@]}"; then
|
||||
err "docs: 실제 값처럼 보이는 credential literal이 있습니다."
|
||||
failed=$((failed + 1))
|
||||
fi
|
||||
|
||||
((failed == 0)) || exit 1
|
||||
log "documentation contract 검사 통과"
|
||||
Executable
+165
@@ -0,0 +1,165 @@
|
||||
#!/usr/bin/env bash
|
||||
# Render every declared entrypoint, then run available schema/policy/shell gates.
|
||||
# CI sets VALIDATION_PROFILE=full so a missing validator is a hard failure.
|
||||
|
||||
set -Eeuo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=../lib/common.sh
|
||||
. "$SCRIPT_DIR/../lib/common.sh"
|
||||
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
ENTRYPOINTS_FILE="$REPO_ROOT/tests/kustomize-entrypoints.txt"
|
||||
KUBE_LINTER_CFG="$REPO_ROOT/.kube-linter.yaml"
|
||||
VALIDATION_PROFILE="${VALIDATION_PROFILE:-local}"
|
||||
CRD_CATALOG='https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json'
|
||||
|
||||
WORK_DIR="$(mktemp -d -t project-infra-validate.XXXXXX)"
|
||||
trap_cleanup_path "$WORK_DIR"
|
||||
|
||||
tool_available() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
require_full_tool() {
|
||||
local tool="$1"
|
||||
if tool_available "$tool"; then
|
||||
return 0
|
||||
fi
|
||||
[[ "$VALIDATION_PROFILE" != "full" ]] \
|
||||
|| die "VALIDATION_PROFILE=full: 필수 도구가 없습니다: $tool"
|
||||
warn "$tool 없음: 해당 검증을 skip합니다. 'mise install' 또는 CI를 사용하세요."
|
||||
return 1
|
||||
}
|
||||
|
||||
render_kustomization() {
|
||||
local entrypoint="$1"
|
||||
local output="$2"
|
||||
if tool_available kustomize; then
|
||||
kustomize build "$entrypoint" >"$output"
|
||||
elif tool_available kubectl; then
|
||||
kubectl kustomize "$entrypoint" >"$output"
|
||||
else
|
||||
die "kustomize 또는 kubectl 중 하나가 필요합니다."
|
||||
fi
|
||||
}
|
||||
|
||||
load_entrypoints() {
|
||||
[[ -f "$ENTRYPOINTS_FILE" ]] || die "entrypoint inventory 없음: $ENTRYPOINTS_FILE"
|
||||
mapfile -t ENTRYPOINTS < <(
|
||||
sed -e 's/#.*$//' -e '/^[[:space:]]*$/d' "$ENTRYPOINTS_FILE"
|
||||
)
|
||||
((${#ENTRYPOINTS[@]} > 0)) || die "검증할 entrypoint가 없습니다."
|
||||
}
|
||||
|
||||
validate_manifests() {
|
||||
local rel entrypoint rendered failed=0
|
||||
local have_kubeconform=false have_kube_linter=false
|
||||
require_full_tool kubeconform && have_kubeconform=true
|
||||
require_full_tool kube-linter && have_kube_linter=true
|
||||
|
||||
for rel in "${ENTRYPOINTS[@]}"; do
|
||||
entrypoint="$REPO_ROOT/$rel"
|
||||
[[ -f "$entrypoint/kustomization.yaml" ]] || {
|
||||
err "$rel: kustomization.yaml 없음"
|
||||
failed=$((failed + 1))
|
||||
continue
|
||||
}
|
||||
|
||||
rendered="$WORK_DIR/$(printf '%s' "$rel" | tr '/' '_').yaml"
|
||||
if ! render_kustomization "$entrypoint" "$rendered" 2>"$rendered.err"; then
|
||||
err "$rel: render 실패"
|
||||
sed 's/^/ /' "$rendered.err" >&2
|
||||
failed=$((failed + 1))
|
||||
continue
|
||||
fi
|
||||
log "$rel: render=ok ($(grep -c '^kind:' "$rendered") resources)"
|
||||
|
||||
if grep -Eq 'replace-in-overlay|__REPLACE_ME_[A-Z0-9_]+' "$rendered"; then
|
||||
err "$rel: unresolved placeholder가 render 결과에 남아 있습니다."
|
||||
grep -En 'replace-in-overlay|__REPLACE_ME_[A-Z0-9_]+' "$rendered" \
|
||||
| sed 's/^/ /' >&2
|
||||
failed=$((failed + 1))
|
||||
fi
|
||||
|
||||
if [[ "$have_kubeconform" == "true" ]]; then
|
||||
if ! kubeconform \
|
||||
-strict \
|
||||
-ignore-missing-schemas \
|
||||
-schema-location default \
|
||||
-schema-location "$CRD_CATALOG" \
|
||||
-summary \
|
||||
"$rendered" >"$rendered.kubeconform" 2>&1; then
|
||||
err "$rel: kubeconform 실패"
|
||||
sed 's/^/ /' "$rendered.kubeconform" >&2
|
||||
failed=$((failed + 1))
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$have_kube_linter" == "true" ]]; then
|
||||
if ! kube-linter lint \
|
||||
--config "$KUBE_LINTER_CFG" \
|
||||
"$rendered" >"$rendered.kube-linter" 2>&1; then
|
||||
err "$rel: kube-linter 실패"
|
||||
sed 's/^/ /' "$rendered.kube-linter" >&2
|
||||
failed=$((failed + 1))
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
((failed == 0)) || return 1
|
||||
}
|
||||
|
||||
validate_shell() {
|
||||
local failed=0
|
||||
mapfile -t shell_files < <(
|
||||
find "$REPO_ROOT/scripts" -type f -name '*.sh' -print | sort
|
||||
)
|
||||
|
||||
if require_full_tool shellcheck; then
|
||||
shellcheck -S style -x -P SCRIPTDIR "${shell_files[@]}" \
|
||||
|| failed=$((failed + 1))
|
||||
fi
|
||||
if require_full_tool shfmt; then
|
||||
shfmt -i 2 -bn -ci -d "${shell_files[@]}" || failed=$((failed + 1))
|
||||
fi
|
||||
|
||||
((failed == 0)) || return 1
|
||||
}
|
||||
|
||||
validate_secrets() {
|
||||
if require_full_tool gitleaks; then
|
||||
local scan_dir="$WORK_DIR/gitleaks-source"
|
||||
mkdir -p "$scan_dir"
|
||||
(
|
||||
cd "$REPO_ROOT"
|
||||
git ls-files -z --cached --others --exclude-standard \
|
||||
| while IFS= read -r -d '' source_file; do
|
||||
if [[ -f "$source_file" ]]; then
|
||||
printf '%s\0' "$source_file"
|
||||
fi
|
||||
done \
|
||||
| tar --null --files-from=- --create --file=-
|
||||
) | tar --extract --file=- --directory="$scan_dir"
|
||||
|
||||
gitleaks dir "$scan_dir" \
|
||||
--no-banner \
|
||||
--redact \
|
||||
--config "$REPO_ROOT/.gitleaks.toml"
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
load_entrypoints
|
||||
|
||||
local failed=0
|
||||
validate_manifests || failed=$((failed + 1))
|
||||
validate_shell || failed=$((failed + 1))
|
||||
validate_secrets || failed=$((failed + 1))
|
||||
|
||||
bash "$SCRIPT_DIR/validate-docs.sh" || failed=$((failed + 1))
|
||||
|
||||
((failed == 0)) || die "$failed validation group(s) 실패"
|
||||
log "모든 validation gate 통과 (profile=$VALIDATION_PROFILE)"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user