Skip to content

security: make the secret-detection gates actually reject (#45) #424

security: make the secret-detection gates actually reject (#45)

security: make the secret-detection gates actually reject (#45) #424

Workflow file for this run

name: CI
on:
push:
branches: ["main", "develop", "feature/*"]
pull_request:
types: [opened, synchronize, reopened]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# Stage 1: Quick quality checks (fail fast on basic issues)
quality-checks:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: "3.12"
- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Install dependencies
# Lockfile-pinned: lint/format/type results must be reproducible.
# `uv pip install -e ".[dev]"` ignores uv.lock and resolves floors
# (ruff>=0.12.0) to whatever is newest, so CI silently drifted to
# ruff 0.16 / mypy 2.3 while uv.lock pins 0.12.5 / 1.17.0.
# --locked (not --frozen) so a lockfile that has drifted from
# pyproject.toml fails the build instead of installing a stale set.
run: uv sync --locked --all-extras
- name: Secret Detection
run: ./scripts/ci/secret-detection.sh
- name: Lint (ruff)
run: uv run --locked ruff check .
- name: Format (ruff)
run: uv run --locked ruff format --check .
- name: Type Check (mypy)
run: uv run --locked mypy .
# Stage 2: Light smoke test (quick test run before full matrix)
light-smoke-test:
runs-on: ubuntu-latest
timeout-minutes: 5
needs: quality-checks
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: "3.12"
- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Install dependencies
run: |
uv venv --python python3.12
uv pip install -e ".[dev]"
- name: Run basic smoke tests
run: |
# Run only critical tests to quickly catch obvious failures
source .venv/bin/activate
python -m pytest tests/unit/test_imports.py tests/unit/test_louie_factory.py -v --tb=short
# Stage 3: Unit tests (only run if smoke test passes)
unit-tests:
runs-on: ubuntu-latest
timeout-minutes: 10
needs: [quality-checks, light-smoke-test]
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Install dependencies
run: |
uv venv --python python${{ matrix.python-version }}
uv pip install -e ".[dev]"
- name: Run unit tests
run: |
# Run only unit tests with coverage
source .venv/bin/activate
python -m pytest tests/unit/ -v --cov=louieai --cov-report=xml --cov-report=term --cov-fail-under=85
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml
flags: unit
name: Unit Tests
fail_ci_if_error: false # Don't fail if Codecov is down
# Stage 3b: Targeted pandas version checks
unit-tests-pandas-compat:
name: unit-tests-pandas (${{ matrix.name }})
runs-on: ubuntu-latest
timeout-minutes: 10
needs: [quality-checks, light-smoke-test]
strategy:
matrix:
include:
- name: pandas2-py310
python-version: "3.10"
pandas-version: "pandas>=2,<3"
- name: pandas3-py313
python-version: "3.13"
pandas-version: "pandas>=3,<4"
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Install dependencies
run: |
uv venv --python python${{ matrix.python-version }}
uv pip install -e ".[dev]"
uv pip install "${{ matrix.pandas-version }}"
- name: Run unit tests
run: |
source .venv/bin/activate
python -m pytest tests/unit/ -v
# Stage 4: Integration tests (conditional on credentials)
integration-tests:
runs-on: ubuntu-latest
timeout-minutes: 10
needs: quality-checks
# Only run if we have credentials (for PRs from the main repo)
if: |
github.event_name == 'push' ||
(github.event.pull_request.head.repo.full_name == github.repository)
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: "3.12"
- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Install dependencies
run: |
uv venv --python python3.12
uv pip install -e ".[dev]"
- name: Run integration tests
env:
GRAPHISTRY_SERVER: ${{ secrets.GRAPHISTRY_SERVER }}
GRAPHISTRY_USERNAME: ${{ secrets.GRAPHISTRY_USERNAME }}
GRAPHISTRY_PASSWORD: ${{ secrets.GRAPHISTRY_PASSWORD }}
LOUIE_SERVER: ${{ secrets.LOUIE_SERVER }}
run: |
# Only run if credentials are configured
if [ -n "$GRAPHISTRY_USERNAME" ]; then
uv run --locked pytest tests/integration/ -v --cov=louieai --cov-report=xml
else
echo "⚠️ Skipping integration tests - no credentials configured"
fi
- name: Upload coverage to Codecov
if: env.GRAPHISTRY_USERNAME != ''
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml
flags: integration
name: Integration Tests
fail_ci_if_error: false
# Stage 5: Documentation tests (can run in parallel with other tests)
docs-test:
runs-on: ubuntu-latest
timeout-minutes: 10
needs: quality-checks
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: "3.12"
- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Install dependencies
# Lockfile-pinned: `mkdocs build --strict` is a determinism gate, so a
# floating mkdocs/plugin version must not change pass/fail. See the
# matching note in quality-checks.
run: uv sync --locked --all-extras
- name: Test documentation examples
run: |
uv run --locked pytest tests/unit/test_documentation.py -v
- name: Validate ReadTheDocs config
run: |
curl -sSL https://raw.githubusercontent.com/readthedocs/readthedocs.org/main/readthedocs/rtd_tests/fixtures/spec/v2/schema.json -o rtd-schema.json
# jsonschema and pyyaml already come from `uv sync --all-extras`; a
# bare `uv pip install` here would perturb the pinned environment.
uv run --locked python -c "
import yaml, json, jsonschema
with open('.readthedocs.yml') as f:
config = yaml.safe_load(f)
with open('rtd-schema.json') as f:
schema = json.load(f)
jsonschema.validate(config, schema)
print('✅ ReadTheDocs config is valid')
"
- name: Build documentation
run: uv run --locked mkdocs build --strict
- name: Verify logo included
run: |
test -f site/assets/louie-logo.png || (echo "❌ Logo not found in built docs" && exit 1)
echo "✅ Logo included in documentation"
# Stage 6: Build and installation tests (only if unit tests pass)
install-test:
runs-on: ubuntu-latest
timeout-minutes: 10
needs: unit-tests
strategy:
matrix:
installer: ["pip", "uv"]
python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install build tools
run: |
pip install --upgrade pip
pip install build
- name: Build package
run: python -m build
- name: Install uv (if needed)
if: matrix.installer == 'uv'
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Test pip installation
if: matrix.installer == 'pip'
run: |
python -m venv test_env
source test_env/bin/activate
pip install dist/*.whl
python -c "import louieai; print(f'✅ louieai {louieai.__version__} installed')"
python -c "from louieai import louie, Cursor; print('✅ louie and Cursor imported')"
python -c "from louieai import louie; lui = louie(); print('✅ louie() factory works')"
- name: Test uv installation
if: matrix.installer == 'uv'
run: |
uv venv test_env
source test_env/bin/activate
uv pip install dist/*.whl
python -c "import louieai; print(f'✅ louieai {louieai.__version__} installed')"
python -c "from louieai import louie, Cursor; print('✅ louie and Cursor imported')"
python -c "from louieai import louie; lui = louie(); print('✅ louie() factory works')"
# Final summary job (for branch protection rules)
ci-success:
runs-on: ubuntu-latest
needs: [quality-checks, light-smoke-test, unit-tests, unit-tests-pandas-compat, docs-test, install-test]
# Note: integration-tests is not required since it's conditional
if: always()
steps:
- name: Check all required jobs
run: |
if [[ "${{ needs.quality-checks.result }}" != "success" || \
"${{ needs.unit-tests.result }}" != "success" || \
"${{ needs.unit-tests-pandas-compat.result }}" != "success" || \
"${{ needs.docs-test.result }}" != "success" || \
"${{ needs.install-test.result }}" != "success" ]]; then
echo "❌ One or more required jobs failed"
exit 1
fi
echo "✅ All required jobs passed"