diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..51cc282 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,138 @@ +name: CI + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + +permissions: + contents: read + +jobs: + yamllint: + name: YAML lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: pip install yamllint --quiet + - run: yamllint -c .yamllint.yml . + + secrets: + name: Secrets detection + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Install gitleaks + run: | + GITLEAKS_VERSION=$(curl -sf https://api.github.com/repos/gitleaks/gitleaks/releases/latest \ + | grep '"tag_name"' | cut -d'"' -f4) + curl -sfL "https://github.com/gitleaks/gitleaks/releases/download/${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION#v}_linux_x64.tar.gz" \ + | tar -xz -C /usr/local/bin gitleaks + - name: Detect secrets + run: gitleaks detect --source . --redact + + kubeconform: + name: Kubernetes manifest validation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install kubeconform + run: | + curl -sL https://github.com/yannh/kubeconform/releases/latest/download/kubeconform-linux-amd64.tar.gz \ + | tar -xz -C /usr/local/bin kubeconform + + - name: Validate Kubernetes manifests + run: | + find bootstrap infrastructure clusters \( -name '*.yaml' -o -name '*.yml' \) \ + ! -name 'values.yaml' ! -name 'values.yml' 2>/dev/null \ + | xargs kubeconform \ + -strict \ + -ignore-missing-schemas \ + -summary + + helm-lint: + name: Helm lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Helm + uses: azure/setup-helm@v4 + with: + version: "3.17.0" + + - name: Add chart repositories + run: | + helm repo add cilium https://helm.cilium.io + helm repo add jetstack https://charts.jetstack.io + helm repo add argo https://argoproj.github.io/argo-helm + helm repo update + + - name: Lint Cilium chart + run: | + CHART_VERSION=$(python3 -c " + import yaml + doc = yaml.safe_load(open('infrastructure/cilium/application.yaml')) + srcs = doc['spec']['sources'] + print(next(s['targetRevision'] for s in srcs if 'chart' in s)) + ") + helm pull cilium/cilium --version "$CHART_VERSION" --untar --untardir /tmp/charts + helm lint /tmp/charts/cilium --values infrastructure/cilium/values.yaml + + - name: Lint cert-manager chart + run: | + CHART_VERSION=$(python3 -c " + import yaml + doc = yaml.safe_load(open('infrastructure/cert-manager/application.yaml')) + srcs = doc['spec']['sources'] + print(next(s['targetRevision'] for s in srcs if 'chart' in s)) + ") + helm pull jetstack/cert-manager --version "$CHART_VERSION" --untar --untardir /tmp/charts + helm lint /tmp/charts/cert-manager --values infrastructure/cert-manager/values.yaml + + pluto: + name: API deprecation check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Read target Kubernetes version + id: k8s + run: | + K8S_VERSION=$(grep '^kubernetes_version:' omni/talos-image.yaml | awk '{print $2}') + echo "version=${K8S_VERSION}" >> "$GITHUB_OUTPUT" + echo "Checking against Kubernetes ${K8S_VERSION}" + + - name: Install pluto + run: | + PLUTO_VERSION=$(curl -sf https://api.github.com/repos/FairwindsOps/pluto/releases/latest \ + | grep '"tag_name"' | cut -d'"' -f4) + if [[ -z "$PLUTO_VERSION" ]]; then + echo "ERROR: failed to resolve pluto version" >&2 + exit 1 + fi + curl -sfL "https://github.com/FairwindsOps/pluto/releases/download/${PLUTO_VERSION}/pluto_${PLUTO_VERSION#v}_linux_amd64.tar.gz" \ + | tar -xz -C /usr/local/bin pluto + + - name: Check for deprecated APIs + run: | + pluto detect-files \ + --directory . \ + --target-versions "k8s=${{ steps.k8s.outputs.version }}" \ + --ignore-deprecations \ + --output wide + + markdownlint: + name: Markdown lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: "20" + - run: npm install -g markdownlint-cli2 + - run: markdownlint-cli2 "**/*.md" "#node_modules" diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml new file mode 100644 index 0000000..e8270d9 --- /dev/null +++ b/.markdownlint-cli2.yaml @@ -0,0 +1,2 @@ +config: + MD013: false # line-length: URLs and long paths make this impractical diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..9b4985b --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,26 @@ +repos: + - repo: https://github.com/adrienverge/yamllint + rev: v1.35.1 + hooks: + - id: yamllint + args: [-c, .yamllint.yml] + + - repo: https://github.com/gitleaks/gitleaks + rev: v8.21.2 + hooks: + - id: gitleaks + + - repo: https://github.com/yannh/kubeconform + rev: v0.6.7 + hooks: + - id: kubeconform + args: + - -strict + - -ignore-missing-schemas + - -summary + + - repo: https://github.com/igorshubovych/markdownlint-cli + rev: v0.44.0 + hooks: + - id: markdownlint + args: ["--ignore", "node_modules"] diff --git a/.yamllint.yml b/.yamllint.yml new file mode 100644 index 0000000..80f1fca --- /dev/null +++ b/.yamllint.yml @@ -0,0 +1,11 @@ +extends: default + +rules: + line-length: + max: 120 + level: warning + truthy: + allowed-values: ["true", "false"] + check-keys: false + comments: + min-spaces-from-content: 1 diff --git a/README.md b/README.md index 52cc4dd..294dc8c 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ On first boot, the App-of-Apps in `bootstrap/argocd-app-of-apps.yaml` is applied ## Structure -``` +```text bootstrap/ App-of-Apps + manually bootstrapped apps (Argo CD, Tailscale operator) infrastructure/ Helm charts + values, one directory per app clusters/oci/ Cluster-specific kustomize patches diff --git a/omni/machine-classes/control-plane.yaml b/omni/machine-classes/control-plane.yaml index 1bf8a62..31071a0 100644 --- a/omni/machine-classes/control-plane.yaml +++ b/omni/machine-classes/control-plane.yaml @@ -4,6 +4,7 @@ metadata: id: oci-cp spec: + installImage: ghcr.io/syscode-labs/talos-images/installer:v1.9.0 matchLabels: # Applied manually in Omni UI after OCI nodes connect via SideroLink. # omnictl machine set-labels role=oci-cp diff --git a/omni/machine-classes/worker.yaml b/omni/machine-classes/worker.yaml index 126b700..ebdfc54 100644 --- a/omni/machine-classes/worker.yaml +++ b/omni/machine-classes/worker.yaml @@ -4,6 +4,7 @@ metadata: id: oci-worker spec: + installImage: ghcr.io/syscode-labs/talos-images/installer:v1.9.0 matchLabels: # Applied manually in Omni UI after OCI nodes connect via SideroLink. # omnictl machine set-labels role=oci-worker diff --git a/omni/talos-image.yaml b/omni/talos-image.yaml new file mode 100644 index 0000000..9d8cc88 --- /dev/null +++ b/omni/talos-image.yaml @@ -0,0 +1,6 @@ +# Auto-updated by talos-images workflow — do not edit manually +talos_version: v1.9.0 +kubernetes_version: v1.32.3 +schematic_id: "" +oci_image_ocid: "" +updated_at: ""