Skip to content

feat: consolidate forbid-artifacts into CI Gate #569

feat: consolidate forbid-artifacts into CI Gate

feat: consolidate forbid-artifacts into CI Gate #569

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
pip install -e ".[dev]"
- name: Lint
run: ruff check .
- name: Test with coverage
run: pytest tests/ -v --cov=faigate --cov-report=term --cov-report=xml:coverage.xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
file: ./coverage.xml
fail_ci_if_error: false
package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Build Python package
run: |
python -m pip install --upgrade pip build twine
python -m build
python -m twine check dist/*
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install ruff pre-commit
- run: ruff check .
- run: ruff format --check .
- run: bash -n scripts/*
- name: Validate pre-commit config
run: pre-commit validate-config .pre-commit-config.yaml
- run: pre-commit run --all-files --show-diff-on-failure
- name: Validate version consistency
run: |
python - <<'PY'
import re, sys
from pathlib import Path
root = Path('.')
pyproject_content = (root / 'pyproject.toml').read_text()
package_content = (root / 'faigate' / '__init__.py').read_text()
pv = re.search(r'^version = "([^"]+)"$', pyproject_content, re.MULTILINE)
iv = re.search(r'^__version__ = "([^"]+)"$', package_content, re.MULTILINE)
if not pv:
sys.exit('ERROR: version not found in pyproject.toml')
if not iv:
sys.exit('ERROR: __version__ not found in faigate/__init__.py')
if pv.group(1) != iv.group(1):
sys.exit(f'ERROR: version mismatch: pyproject={pv.group(1)} package={iv.group(1)}')
print(f'Version OK: {pv.group(1)}')
PY
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install bandit[toml]
- run: bandit -c pyproject.toml -r faigate -f html -o bandit-report.html || true
- run: bandit -c pyproject.toml -r faigate -f json -o bandit-report.json || true
- name: Upload Bandit report
uses: actions/upload-artifact@v4
if: always()
with:
name: bandit-security-report
path: |
bandit-report.html
bandit-report.json
benchmarks:
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install -e .[dev]
- name: Run performance benchmarks
run: pytest tests/benchmarks/ --benchmark-only --benchmark-json=benchmark-results.json
- name: Upload benchmark results
uses: actions/upload-artifact@v4
with:
name: benchmark-results
path: benchmark-results.json
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install -e .[dev]
- name: Generate API documentation
run: python scripts/generate-api-docs.py
- name: Check if API.md changed
run: |
if ! git diff --quiet docs/API.md; then
echo "::warning::API.md is out of date. Run 'python scripts/generate-api-docs.py' and commit."
git diff docs/API.md
else
echo "API.md is up to date."
fi
changelog:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install git-cliff
run: |
VERSION=$(curl -s https://api.github.com/repos/orhun/git-cliff/releases/latest | grep '"tag_name"' | cut -d'"' -f4 | tr -d 'v')
curl -LsSf "https://github.com/orhun/git-cliff/releases/download/v${VERSION}/git-cliff-${VERSION}-x86_64-unknown-linux-gnu.tar.gz" | tar xz -C /tmp
sudo mv /tmp/git-cliff-*/git-cliff /usr/local/bin/
- name: Generate changelog
run: git-cliff --config .cliff.toml --unreleased --strip header -o /tmp/generated-changelog.md
- name: Check if CHANGELOG.md is up to date
run: |
if ! diff -u CHANGELOG.md /tmp/generated-changelog.md; then
echo "::warning::CHANGELOG.md has unreleased entries. Run: git-cliff --unreleased --strip header -o CHANGELOG.md"
else
echo "CHANGELOG.md is up to date."
fi
forbid-artifacts:
name: Forbid artifacts
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Fail if forbidden paths are tracked
run: |
set -euo pipefail
if git ls-files | grep -qE '(\.ssh/|\.db($|-)|.*\.sqlite|.*\.log$)'; then
echo "ERROR: forbidden files are tracked:"
git ls-files | grep -E '(\.ssh/|\.db($|-)|.*\.sqlite|.*\.log$)' || true
exit 1
fi
- name: Fail if history contains forbidden blobs
run: |
set -euo pipefail
if git rev-list --objects --all | grep -qE '(\.ssh/|.*\.db($|-)|.*\.sqlite|.*\.log$)'; then
echo "ERROR: forbidden artifacts exist in history:"
git rev-list --objects --all | grep -E '(\.ssh/|.*\.db($|-)|.*\.sqlite|.*\.log$)' || true
exit 1
fi
gate:
name: CI Gate
runs-on: ubuntu-latest
needs: [test, lint, package, forbid-artifacts]
if: always()
steps:
- name: All required checks passed
run: |
if [[ "${{ needs.test.result }}" != "success" ]] ||
[[ "${{ needs.lint.result }}" != "success" ]] ||
[[ "${{ needs.package.result }}" != "success" ]] ||
[[ "${{ needs.forbid-artifacts.result }}" != "success" ]]; then
echo "::error::One or more required checks failed — merge blocked."
exit 1
fi
echo "All required checks passed."