diff --git a/.github/workflows/secrets.yml b/.github/workflows/secrets.yml new file mode 100644 index 0000000..1c6b516 --- /dev/null +++ b/.github/workflows/secrets.yml @@ -0,0 +1,50 @@ +name: secrets + +# Server-side complement to the gitleaks pre-commit hook in .pre-commit-config.yaml. +# Uses the MIT-licensed gitleaks CLI directly rather than the gitleaks-action +# wrapper (which requires a paid license for org-owned repos as of v2). +# +# Why both pre-commit AND this: +# - Pre-commit runs locally and is bypassable (`git commit --no-verify`), +# skippable (if `pre-commit install` was never run), and doesn't fire for +# commits authored via the GitHub web UI, mobile app, or REST API. +# - This workflow runs in CI on every PR + push, can be enforced via branch +# protection ("required check"), and is the last line before merge. +# - Same engine, same rules — different enforcement surface. +# +# To bump gitleaks: edit GITLEAKS_VERSION below. + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +env: + GITLEAKS_VERSION: "8.30.1" + +jobs: + gitleaks: + name: gitleaks + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + # Fetch the full history so gitleaks scans every commit in the PR, + # not just the tip. Catches secrets that were committed and later + # "deleted" without being rotated. + fetch-depth: 0 + + - name: Install gitleaks + run: | + curl -sfL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \ + | sudo tar -xz -C /usr/local/bin gitleaks + gitleaks version + + - name: Scan repo history + # --redact : redact secret values in the output (don't leak them into CI logs) + # --verbose : per-commit progress (helpful when something fails) + # --no-banner : skip the ASCII art banner + run: gitleaks detect --redact --verbose --no-banner diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 0000000..a0e7277 --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,58 @@ +name: validate + +# Pre-merge validation: yamllint, kustomize rendering, kubeconform, shellcheck. +# Calls the same script developers can run locally — see ci/validate.sh and +# the README "Optional, for running CI checks locally" line in Prerequisites. +# +# Tool versions are pinned via env vars below — bump them as needed. + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +env: + KUSTOMIZE_VERSION: "5.8.1" + KUBECONFORM_VERSION: "0.7.0" + HELM_VERSION: "v3.16.0" + +jobs: + manifests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + - run: pip install --quiet yamllint + + - name: Install kustomize + # Direct download from a pinned release. (The kubernetes-sigs + # `install_kustomize.sh` script does its own discovery and is flaky + # under GitHub API rate limits — we pin instead.) The release tag is + # `kustomize/v`, so the slash is URL-encoded as `%2F`. + run: | + curl -sfL "https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv${KUSTOMIZE_VERSION}/kustomize_v${KUSTOMIZE_VERSION}_linux_amd64.tar.gz" \ + | sudo tar -xz -C /usr/local/bin kustomize + kustomize version + + # helm is required for kustomize's `--enable-helm` flag (renders the + # helmCharts: block in manifests/quine-enterprise/). + - uses: azure/setup-helm@v4 + with: + version: ${{ env.HELM_VERSION }} + + - name: Install kubeconform + run: | + curl -sfL "https://github.com/yannh/kubeconform/releases/download/v${KUBECONFORM_VERSION}/kubeconform-linux-amd64.tar.gz" \ + | sudo tar -xz -C /usr/local/bin kubeconform + kubeconform -v + + # shellcheck is pre-installed on ubuntu-latest runners. + + - name: Run validation + run: ./ci/validate.sh diff --git a/.yamllint.yaml b/.yamllint.yaml new file mode 100644 index 0000000..0ddf384 --- /dev/null +++ b/.yamllint.yaml @@ -0,0 +1,27 @@ +# yamllint config — relaxed preset, with rules tuned for this repo. +# +# We disable line-length because Kustomize patches, Helm values, and operator +# CRs commonly carry long URLs or composite identifiers. We disable +# `truthy.check-keys` because Kubernetes manifests use `on:` (workflow trigger) +# and similar field names that yamllint would otherwise flag. +# +# What still fires (and should): +# - syntax errors +# - duplicate keys +# - inconsistent indentation +# - trailing whitespace +# - missing document start where required by surrounding context + +extends: relaxed + +rules: + line-length: disable + truthy: + check-keys: false + +ignore: | + charts/ + **/charts/ + tmp/ + temp/ + .pre-commit-cache/ diff --git a/README.md b/README.md index 407d6f3..a0f62a4 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,12 @@ export THATDOT_REGISTRY_USERNAME="..." export THATDOT_REGISTRY_PASSWORD="..." ``` +Optional — for reproducing the CI validation checks locally before pushing (`./ci/validate.sh` runs the same checks `.github/workflows/validate.yml` runs): + +```bash +brew install yamllint shellcheck kustomize helm kubeconform +``` + ## First-time setup ```bash diff --git a/ci/validate.sh b/ci/validate.sh new file mode 100755 index 0000000..d239c53 --- /dev/null +++ b/ci/validate.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +set -uo pipefail + +# Validates the manifest tree, Kustomize rendering, and helper scripts. +# Same checks the .github/workflows/validate.yml workflow runs — install the +# tools locally and you can reproduce CI before pushing. +# +# Tools required: +# yamllint, shellcheck, kustomize, helm, kubeconform +# +# macOS install: +# brew install yamllint shellcheck kustomize helm kubeconform +# +# Usage: ./ci/validate.sh + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +cd "$PROJECT_DIR" || exit 1 + +# ---- Tool check ---- +missing=() +for tool in yamllint shellcheck kustomize helm kubeconform; do + command -v "$tool" >/dev/null 2>&1 || missing+=("$tool") +done +if [[ ${#missing[@]} -gt 0 ]]; then + echo "ERROR: missing required tools: ${missing[*]}" + echo " macOS install: brew install ${missing[*]}" + exit 1 +fi + +# Run all checks, collecting failures rather than aborting on the first one — +# so the developer sees the full picture in a single run. +failed=() + +# Note on `--ignore-missing-schemas`: +# kubeconform validates against built-in Kubernetes schemas only. Custom +# resources (ArgoCD Application, OLM Subscription/OperatorGroup, OpenShift +# Route, CassandraDatacenter, Keycloak, KeycloakRealmImport) are skipped +# rather than failed. To close the gap, pre-download the schemas we want +# from https://github.com/datreeio/CRDs-catalog into ci/schemas/ and add +# --schema-location flags pointing at the local paths. kubeconform v0.7.0 +# doesn't expose a `lower` template fn, so the catalog can't be referenced +# by URL directly (datreeio's filenames are lowercase; {{.ResourceKind}} +# renders PascalCase). Future work. + +echo "==> yamllint" +yamllint . || failed+=("yamllint") + +echo "" +echo "==> shellcheck scripts/*.sh ci/*.sh" +shellcheck scripts/*.sh ci/*.sh || failed+=("shellcheck") + +echo "" +echo "==> kubeconform bootstrap/*.yaml" +# argocd-customizations.yaml is a patch payload (no apiVersion/kind/metadata), +# not a complete manifest — kubeconform would correctly reject it. Skip. +for f in bootstrap/*.yaml; do + case "$f" in + bootstrap/argocd-customizations.yaml) continue ;; + esac + echo " --- $f ---" + if ! kubeconform --strict --ignore-missing-schemas --summary "$f"; then + failed+=("$f") + fi +done + +echo "" +echo "==> kustomize + kubeconform per leaf" +for leaf in manifests/root manifests/platform manifests/product manifests/cassandra manifests/keycloak manifests/quine-enterprise; do + echo " --- $leaf ---" + if ! kustomize build --enable-helm "$leaf" \ + | kubeconform --strict --ignore-missing-schemas --summary; then + failed+=("$leaf") + fi +done + +echo "" +if [[ ${#failed[@]} -gt 0 ]]; then + echo "FAILED: ${failed[*]}" + exit 1 +fi +echo "All checks passed."