Files
cleveragents-core/.forgejo/workflows/ci.yml
T
freemo 4e3b4180fc
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 14s
CI / helm (pull_request) Successful in 45s
CI / lint (pull_request) Successful in 3m18s
CI / quality (pull_request) Successful in 3m43s
CI / security (pull_request) Successful in 4m9s
CI / typecheck (pull_request) Successful in 4m25s
CI / unit_tests (pull_request) Successful in 9m27s
CI / docker (pull_request) Successful in 1m42s
CI / e2e_tests (pull_request) Failing after 15m11s
CI / coverage (pull_request) Successful in 13m27s
CI / integration_tests (pull_request) Successful in 25m8s
CI / status-check (pull_request) Failing after 1s
CI / benchmark-regression (pull_request) Successful in 54m54s
feat(ci): add Helm chart lint and template validation to CI
Add kubeconform manifest validation to the existing helm CI job.
The helm job already had helm lint and helm template steps (added in
#1085). This commit completes the optional acceptance criterion from
#1089 by adding kubeconform v0.7.0 to validate rendered manifests
against the Kubernetes 1.29.0 schema in strict mode.

Changes:
- Install kubeconform v0.7.0 in the helm CI job
- Add 'Validate rendered manifests with kubeconform' step after
  helm template smoke render (strict mode, ignore-missing-schemas,
  kubernetes-version 1.29.0)
- Add 5 BDD scenarios to ci_workflow_validation.feature covering:
  - helm job existence
  - helm lint step
  - helm template step
  - kubeconform validation step
  - status-check dependency on helm job

ISSUES CLOSED: #1089
2026-04-02 07:47:09 +00:00

592 lines
22 KiB
YAML

name: CI
on:
push:
branches: [master, develop]
pull_request:
branches: [master, develop*]
env:
UV_VERSION: "0.8.0"
PYTHON_VERSION: "3.13"
NOX_DEFAULT_VENV_BACKEND: "uv"
jobs:
lint:
runs-on: docker
container:
image: python:3.13-slim
steps:
- name: Install Node.js (required by actions/checkout)
run: |
apt-get update && apt-get install -y -qq nodejs && rm -rf /var/lib/apt/lists/*
- uses: actions/checkout@v4
- name: Install uv and nox
run: |
pip install -q uv==${{ env.UV_VERSION }} nox
- name: Cache uv packages
uses: actions/cache@v3
with:
path: ~/.cache/uv
key: uv-lint-${{ hashFiles('pyproject.toml') }}
restore-keys: |
uv-lint-
- name: Run lint via nox
run: |
nox -s lint
env:
NOX_DEFAULT_VENV_BACKEND: uv
- name: Run format check via nox
run: |
nox -s format -- --check
env:
NOX_DEFAULT_VENV_BACKEND: uv
typecheck:
runs-on: docker
container:
image: python:3.13-slim
steps:
- name: Install Node.js (required by actions/checkout)
run: |
apt-get update && apt-get install -y -qq nodejs && rm -rf /var/lib/apt/lists/*
- uses: actions/checkout@v4
- name: Install uv and nox
run: |
pip install -q uv==${{ env.UV_VERSION }} nox
- name: Cache uv packages
uses: actions/cache@v3
with:
path: ~/.cache/uv
key: uv-typecheck-${{ hashFiles('pyproject.toml') }}
restore-keys: |
uv-typecheck-
- name: Run typecheck via nox
run: |
nox -s typecheck
env:
NOX_DEFAULT_VENV_BACKEND: uv
security:
runs-on: docker
container:
image: python:3.13-slim
steps:
- name: Install Node.js (required by actions/checkout)
run: |
apt-get update && apt-get install -y -qq nodejs && rm -rf /var/lib/apt/lists/*
- uses: actions/checkout@v4
- name: Install uv and nox
run: |
pip install -q uv==${{ env.UV_VERSION }} nox
- name: Cache uv packages
uses: actions/cache@v3
with:
path: ~/.cache/uv
key: uv-security-${{ hashFiles('pyproject.toml') }}
restore-keys: |
uv-security-
- name: Run security scan via nox
run: |
nox -s security_scan
env:
NOX_DEFAULT_VENV_BACKEND: uv
- name: Run dead code detection via nox
run: |
nox -s dead_code
env:
NOX_DEFAULT_VENV_BACKEND: uv
quality:
runs-on: docker
container:
image: python:3.13-slim
steps:
- name: Install Node.js (required by actions/checkout)
run: |
apt-get update && apt-get install -y -qq nodejs && rm -rf /var/lib/apt/lists/*
- uses: actions/checkout@v4
- name: Install uv and nox
run: |
pip install -q uv==${{ env.UV_VERSION }} nox
- name: Cache uv packages
uses: actions/cache@v3
with:
path: ~/.cache/uv
key: uv-quality-${{ hashFiles('pyproject.toml') }}
restore-keys: |
uv-quality-
- name: Run complexity check via nox
run: |
nox -s complexity
env:
NOX_DEFAULT_VENV_BACKEND: uv
unit_tests:
runs-on: docker
container:
image: python:3.13-slim
steps:
- name: Install system dependencies (nodejs for checkout, git for merge tests, curl/tar for Helm)
run: |
apt-get update && apt-get install -y -qq nodejs git curl tar && rm -rf /var/lib/apt/lists/*
- name: Install Helm CLI
run: |
HELM_VERSION="v3.16.4"
ARCH="amd64"
HELM_TARBALL="helm-${HELM_VERSION}-linux-${ARCH}.tar.gz"
curl -fsSL "https://get.helm.sh/${HELM_TARBALL}" -o "/tmp/${HELM_TARBALL}"
curl -fsSL "https://get.helm.sh/${HELM_TARBALL}.sha256sum" -o /tmp/helm.sha256sum
cd /tmp && sha256sum -c helm.sha256sum
tar -xzf "/tmp/${HELM_TARBALL}" -C /tmp
mv /tmp/linux-${ARCH}/helm /usr/local/bin/helm
chmod +x /usr/local/bin/helm
helm version --short
- uses: actions/checkout@v4
- name: Install uv and nox
run: |
pip install -q uv==${{ env.UV_VERSION }} nox
- name: Cache uv packages
uses: actions/cache@v3
with:
path: ~/.cache/uv
key: uv-tests-${{ hashFiles('pyproject.toml') }}
restore-keys: |
uv-tests-
- name: Run unit tests via nox
run: |
nox -s unit_tests
env:
NOX_DEFAULT_VENV_BACKEND: uv
integration_tests:
runs-on: docker
container:
image: python:3.13-slim
steps:
- name: Install system dependencies (nodejs for checkout, git for integration tests, curl/tar for Helm)
run: |
apt-get update && apt-get install -y -qq nodejs git curl tar && rm -rf /var/lib/apt/lists/*
- name: Install Helm CLI
run: |
HELM_VERSION="v3.16.4"
ARCH="amd64"
HELM_TARBALL="helm-${HELM_VERSION}-linux-${ARCH}.tar.gz"
curl -fsSL "https://get.helm.sh/${HELM_TARBALL}" -o "/tmp/${HELM_TARBALL}"
curl -fsSL "https://get.helm.sh/${HELM_TARBALL}.sha256sum" -o /tmp/helm.sha256sum
cd /tmp && sha256sum -c helm.sha256sum
tar -xzf "/tmp/${HELM_TARBALL}" -C /tmp
mv /tmp/linux-${ARCH}/helm /usr/local/bin/helm
chmod +x /usr/local/bin/helm
helm version --short
- uses: actions/checkout@v4
- name: Install uv and nox
run: |
pip install -q uv==${{ env.UV_VERSION }} nox
- name: Cache uv packages
uses: actions/cache@v3
with:
path: ~/.cache/uv
key: uv-tests-${{ hashFiles('pyproject.toml') }}
restore-keys: |
uv-tests-
- name: Run integration tests via nox
run: |
nox -s integration_tests
env:
NOX_DEFAULT_VENV_BACKEND: uv
CLEVERAGENTS_REQUIRE_HELM_RENDER_ASSERTIONS: "true"
# LLM API keys required for Robot Framework integration tests.
# These secrets must be configured in Forgejo UI:
# Repository Settings > Actions > Secrets
# See docs/development/ci-cd.md for details.
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
e2e_tests:
runs-on: docker
timeout-minutes: 45
container:
image: python:3.13-slim
steps:
- name: Install system dependencies (nodejs for checkout, git for E2E tests)
run: |
apt-get update && apt-get install -y -qq nodejs git && rm -rf /var/lib/apt/lists/*
- uses: actions/checkout@v4
- name: Install uv and nox
run: |
pip install -q uv==${{ env.UV_VERSION }} nox
- name: Cache uv packages
uses: actions/cache@v3
with:
path: ~/.cache/uv
key: uv-tests-${{ hashFiles('pyproject.toml') }}
restore-keys: |
uv-tests-
- name: Run E2E tests via nox
run: |
nox -s e2e_tests
env:
NOX_DEFAULT_VENV_BACKEND: uv
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
coverage:
runs-on: docker
container:
image: python:3.13-slim
needs: [lint, typecheck]
steps:
- name: Install system dependencies (nodejs for checkout, git for merge tests)
run: |
apt-get update && apt-get install -y -qq nodejs git && rm -rf /var/lib/apt/lists/*
- uses: actions/checkout@v4
- name: Install uv and nox
run: |
pip install -q uv==${{ env.UV_VERSION }} nox
- name: Cache uv packages
uses: actions/cache@v3
with:
path: ~/.cache/uv
key: uv-coverage-${{ hashFiles('pyproject.toml') }}
restore-keys: |
uv-coverage-
- name: Run coverage report via nox (fail-under 97%)
id: coverage
run: |
mkdir -p build
nox -s coverage_report 2>&1 | tee build/coverage-output.txt
# Extract the single-line CI summary from nox output
grep -E '^(nox > )?COVERAGE (OK|FAILED):' build/coverage-output.txt || true
env:
NOX_DEFAULT_VENV_BACKEND: uv
- name: Surface coverage summary
if: always()
run: |
if [ -f build/coverage.json ]; then
python3 -c "
import json, sys
with open('build/coverage.json') as f:
data = json.load(f)
summary = data.get('summary') or data.get('totals') or {}
pct = round(summary.get('percent_covered', 0), 1)
threshold = 97
if pct >= threshold:
print(f'COVERAGE OK: {pct}% (threshold: {threshold}%)')
else:
print(f'COVERAGE FAILED: {pct}% < {threshold}% threshold')
sys.exit(1)
"
else
echo "COVERAGE FAILED: no coverage data generated"
exit 1
fi
- name: Upload coverage artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: coverage-reports
path: |
build/coverage.xml
build/coverage.json
build/htmlcov/
retention-days: 30
benchmark-regression:
if: forgejo.event_name == 'pull_request'
runs-on: docker-benchmark
container:
image: python:3.13-slim
needs: [lint, typecheck]
steps:
- name: Install system dependencies (nodejs for checkout, git for merge tests)
run: |
apt-get update && apt-get install -y -qq nodejs git && rm -rf /var/lib/apt/lists/*
- name: Checkout full history
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Compute base commit
id: hash
run: |
git fetch origin "${{ forgejo.base_ref }}" --depth=200
BASE_SHA=$(git merge-base HEAD "origin/${{ forgejo.base_ref }}")
echo "ASV_BASE_SHA=${BASE_SHA}" >> $FORGEJO_OUTPUT
- name: Install dependencies
run: |
python -m pip install -U pip
python -m pip install asv virtualenv uv==${{ env.UV_VERSION }} nox
- name: Restore prior ASV benchmarks
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
ASV_S3_BUCKET: ${{ secrets.ASV_S3_BUCKET }}
run: |
python -m pip install awscli
mkdir -p build/asv/results
aws s3 sync "s3://${ASV_S3_BUCKET}/asv/results" build/asv/results --delete || true
- name: Run asv continuous via nox
env:
ASV_BASE_SHA: ${{ steps.hash.outputs.ASV_BASE_SHA }}
run: |
nox -s benchmark_regression
- name: Archive the results
run: |
tar cf /tmp/asv-results.tar build/asv/results build/asv/html
- name: Upload benchmark artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: asv-results-pr
path: /tmp/asv-results.tar
rentention-days: 30
benchmark-publish:
if: forgejo.event_name == 'push' && ( forgejo.ref == 'refs/heads/master' || forgejo.ref == 'refs/heads/develop' )
runs-on: docker-benchmark
container: python:3.13-slim
steps:
- name: Install system dependencies (nodejs for checkout, git for merge tests)
run: |
apt-get update && apt-get install -y -qq nodejs git && rm -rf /var/lib/apt/lists/*
- name: Checkout full history
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install dependencies
run: |
python -m pip install -U pip
python -m pip install asv virtualenv uv==${{ env.UV_VERSION }} nox
- name: Restore prior ASV benchmarks
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
ASV_S3_BUCKET: ${{ secrets.ASV_S3_BUCKET }}
run: |
python -m pip install awscli
mkdir -p build/asv/results
aws s3 sync "s3://${ASV_S3_BUCKET}/asv/results" build/asv/results --delete || true
- name: Run asv via nox
run: |
nox -s benchmark
- name: Upload updated benchmarks and website to S3
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
ASV_S3_BUCKET: ${{ secrets.ASV_S3_BUCKET }}
run: |
python -m pip install awscli
aws s3 sync build/asv/results "s3://${ASV_S3_BUCKET}/asv/results" --delete
aws s3 sync build/asv/html "s3://${ASV_S3_BUCKET}/asv/html" --delete
- name: Archive the results
run: |
tar cf /tmp/asv-results.tar build/asv/results build/asv/html
- name: Upload benchmark artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: asv-results-pr
path: /tmp/asv-results.tar
rentention-days: 30
build:
runs-on: docker
container:
image: python:3.13-slim
steps:
- name: Install Node.js (required by actions/checkout)
run: |
apt-get update && apt-get install -y -qq nodejs && rm -rf /var/lib/apt/lists/*
- uses: actions/checkout@v4
- name: Install uv and nox
run: |
pip install -q uv==${{ env.UV_VERSION }} nox
- name: Build wheel via nox
run: |
nox -s build
env:
NOX_DEFAULT_VENV_BACKEND: uv
docker:
needs: [lint, typecheck, unit_tests, security]
runs-on: docker
container:
image: docker:dind
options: --privileged
steps:
- name: Start Docker daemon and install dependencies
run: |
dockerd &
apk add --no-cache git nodejs
for i in $(seq 1 30); do docker info >/dev/null 2>&1 && break || sleep 1; done
- uses: actions/checkout@v4
- name: Build Docker image (CLI)
run: |
docker build -t cleverernie:test .
- name: Test Docker image (CLI)
run: |
docker run --rm cleverernie:test --version
- name: Build Docker image (Server)
run: |
docker build -f Dockerfile.server -t cleveragents-server:test .
helm:
runs-on: docker
container:
image: python:3.13-slim
steps:
- name: Install system dependencies (nodejs for checkout, curl for Helm and kubeconform)
run: |
apt-get update && apt-get install -y -qq nodejs curl tar && rm -rf /var/lib/apt/lists/*
- uses: actions/checkout@v4
- name: Install Helm CLI
run: |
HELM_VERSION="v3.16.4"
ARCH="amd64"
HELM_TARBALL="helm-${HELM_VERSION}-linux-${ARCH}.tar.gz"
curl -fsSL "https://get.helm.sh/${HELM_TARBALL}" -o "/tmp/${HELM_TARBALL}"
curl -fsSL "https://get.helm.sh/${HELM_TARBALL}.sha256sum" -o /tmp/helm.sha256sum
cd /tmp && sha256sum -c helm.sha256sum
tar -xzf "/tmp/${HELM_TARBALL}" -C /tmp
mv /tmp/linux-${ARCH}/helm /usr/local/bin/helm
chmod +x /usr/local/bin/helm
helm version --short
- name: Install kubeconform
run: |
KUBECONFORM_VERSION="v0.7.0"
ARCH="amd64"
KUBECONFORM_TARBALL="kubeconform-linux-${ARCH}.tar.gz"
curl -fsSL "https://github.com/yannh/kubeconform/releases/download/${KUBECONFORM_VERSION}/${KUBECONFORM_TARBALL}" \
-o "/tmp/${KUBECONFORM_TARBALL}"
tar -xzf "/tmp/${KUBECONFORM_TARBALL}" -C /tmp
mv /tmp/kubeconform /usr/local/bin/kubeconform
chmod +x /usr/local/bin/kubeconform
kubeconform -v
- name: Build Helm chart dependencies
run: |
helm dependency build ./k8s
- name: Helm lint chart
run: |
helm lint ./k8s \
--set database.url="postgresql+asyncpg://user:pass@db-host:5432/cleveragents"
- name: Helm template smoke render
run: |
helm template cleveragents ./k8s \
--set database.url="postgresql+asyncpg://user:pass@db-host:5432/cleveragents" >/tmp/rendered.yaml
test -s /tmp/rendered.yaml
- name: Validate rendered manifests with kubeconform
run: |
kubeconform \
-strict \
-ignore-missing-schemas \
-kubernetes-version 1.29.0 \
-summary \
/tmp/rendered.yaml
status-check:
if: always()
needs: [lint, typecheck, security, quality, unit_tests, integration_tests, e2e_tests, coverage, build, docker, helm]
runs-on: docker
container:
image: python:3.13-slim
steps:
- name: Check required job results
run: |
echo "lint: ${{ needs.lint.result }}"
echo "typecheck: ${{ needs.typecheck.result }}"
echo "security: ${{ needs.security.result }}"
echo "quality: ${{ needs.quality.result }}"
echo "unit_tests: ${{ needs.unit_tests.result }}"
echo "integration_tests: ${{ needs.integration_tests.result }}"
echo "e2e_tests: ${{ needs.e2e_tests.result }}"
echo "coverage: ${{ needs.coverage.result }}"
echo "build: ${{ needs.build.result }}"
echo "docker: ${{ needs.docker.result }}"
echo "helm: ${{ needs.helm.result }}"
if [ "${{ needs.lint.result }}" != "success" ] || \
[ "${{ needs.typecheck.result }}" != "success" ] || \
[ "${{ needs.security.result }}" != "success" ] || \
[ "${{ needs.quality.result }}" != "success" ] || \
[ "${{ needs.unit_tests.result }}" != "success" ] || \
[ "${{ needs.integration_tests.result }}" != "success" ] || \
[ "${{ needs.e2e_tests.result }}" != "success" ] || \
[ "${{ needs.coverage.result }}" != "success" ] || \
[ "${{ needs.build.result }}" != "success" ] || \
[ "${{ needs.docker.result }}" != "success" ] || \
[ "${{ needs.helm.result }}" != "success" ]; then
echo "FAILED: One or more required jobs did not succeed"
exit 1
fi
echo "All required CI checks passed"