forked from cleveragents/cleveragents-core
99aa459bad
Replace per-job cache key prefixes (uv-lint-, uv-typecheck-, uv-security-,
uv-quality-, uv-tests-, uv-coverage-) with a single shared key
uv-${{ hashFiles('pyproject.toml') }} across all jobs in ci.yml.
Also add a missing uv cache step to the build job and add uv caching
to nightly-quality.yml, which previously had no cache at all.
All jobs now share the same cache entry when pyproject.toml is unchanged,
eliminating redundant package downloads and reducing CI wall-clock time.
ISSUES CLOSED: #1535
133 lines
4.2 KiB
YAML
133 lines
4.2 KiB
YAML
name: Nightly Quality
|
|
|
|
on:
|
|
schedule:
|
|
# Run at midnight UTC every day
|
|
- cron: "0 0 * * *"
|
|
workflow_dispatch:
|
|
# Allow manual trigger for testing
|
|
|
|
env:
|
|
UV_VERSION: "0.8.0"
|
|
PYTHON_VERSION: "3.13"
|
|
RUFF_VERSION: ">=0.15,<0.16"
|
|
|
|
jobs:
|
|
full-quality-suite:
|
|
runs-on: docker
|
|
container:
|
|
image: python:3.13-slim
|
|
steps:
|
|
- name: Install system dependencies (git for merge tests)
|
|
run: |
|
|
apt-get update && apt-get install -y -qq git && rm -rf /var/lib/apt/lists/*
|
|
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Install uv
|
|
run: |
|
|
pip install -q uv==${{ env.UV_VERSION }}
|
|
|
|
- name: Cache uv packages
|
|
uses: actions/cache@v3
|
|
with:
|
|
path: ~/.cache/uv
|
|
key: uv-${{ hashFiles('pyproject.toml') }}
|
|
restore-keys: |
|
|
uv-
|
|
|
|
- name: Install all dependencies
|
|
run: |
|
|
uv pip install --system -e ".[dev,tests]"
|
|
|
|
- name: Ensure pinned ruff version
|
|
run: |
|
|
uv pip install --system "ruff${{ env.RUFF_VERSION }}"
|
|
|
|
- name: Install behave-parallel
|
|
run: |
|
|
pip install -q behave-parallel
|
|
|
|
- name: Run full lint suite
|
|
run: |
|
|
ruff format --check .
|
|
ruff check .
|
|
|
|
- name: Run pyright type checking
|
|
run: |
|
|
pyright
|
|
|
|
- name: Run bandit security scan (all severities)
|
|
run: |
|
|
mkdir -p build/reports
|
|
bandit -c pyproject.toml -r src/cleveragents --format json --output build/reports/bandit-full.json || true
|
|
bandit -c pyproject.toml -r src/cleveragents --severity-level high
|
|
|
|
- name: Run vulture dead code detection
|
|
run: |
|
|
vulture src/cleveragents vulture_whitelist.py --min-confidence 80 --exclude src/cleveragents/discovery
|
|
|
|
- name: Run radon complexity analysis
|
|
run: |
|
|
mkdir -p build/reports
|
|
radon cc src/cleveragents --show-complexity --total-average --json > build/reports/complexity.json
|
|
radon mi src/cleveragents --json > build/reports/maintainability.json
|
|
echo "=== Complexity Summary ==="
|
|
radon cc src/cleveragents --min C --show-complexity --total-average
|
|
echo "=== Maintainability Index ==="
|
|
radon mi src/cleveragents --min B
|
|
|
|
- name: Run full Behave test suite with coverage
|
|
run: |
|
|
coverage run --source=src -m behave -q --no-capture || true
|
|
coverage xml -o build/reports/coverage.xml
|
|
coverage json -o build/reports/coverage.json
|
|
coverage report --fail-under=85
|
|
|
|
- name: Run quality gates script
|
|
run: |
|
|
python scripts/check-quality-gates.py --coverage-min 85 --complexity-max F
|
|
|
|
- name: Generate quality trend data
|
|
run: |
|
|
python -c "
|
|
import json, datetime
|
|
from pathlib import Path
|
|
|
|
report = {
|
|
'timestamp': datetime.datetime.now(datetime.timezone.utc).isoformat(),
|
|
'gates': {}
|
|
}
|
|
|
|
# Coverage
|
|
cov_path = Path('build/reports/coverage.json')
|
|
if cov_path.exists():
|
|
cov_data = json.loads(cov_path.read_text())
|
|
summary = cov_data.get('summary') or cov_data.get('totals') or {}
|
|
report['gates']['coverage'] = summary.get('percent_covered', 0)
|
|
|
|
# Complexity
|
|
cx_path = Path('build/reports/complexity.json')
|
|
if cx_path.exists():
|
|
cx_data = json.loads(cx_path.read_text())
|
|
total_blocks = sum(len(b) for b in cx_data.values())
|
|
report['gates']['total_analyzed_blocks'] = total_blocks
|
|
|
|
# Security
|
|
sec_path = Path('build/reports/bandit-full.json')
|
|
if sec_path.exists():
|
|
sec_data = json.loads(sec_path.read_text())
|
|
report['gates']['security_issues'] = len(sec_data.get('results', []))
|
|
|
|
Path('build/reports/quality-trend.json').write_text(json.dumps(report, indent=2))
|
|
print(json.dumps(report, indent=2))
|
|
"
|
|
|
|
- name: Upload quality reports
|
|
if: always()
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: nightly-quality-reports
|
|
path: build/reports/
|
|
retention-days: 90
|