# ============================================================================ # Brain-5D — Continuous Integration # ============================================================================ # Runs on push/PR to main/develop, and weekly on Monday 06:00 UTC. # # Pipeline stages: # 1. lint-format — code style, linting, pre-commit # 2. type-check — static type analysis (mypy + pyright) # 3. security — SAST / dependency scanning # 4. tests — full test suite across Python versions (matrix) # 5. build — wheel build + install verification # 6. docker — Docker image build test # 7. docs — documentation structure check # ============================================================================ name: Continuous Integration on: push: branches: [main, develop] pull_request: branches: [main] schedule: - cron: "0 6 * * 1" workflow_dispatch: permissions: contents: read checks: write pull-requests: write env: PYTHONUNBUFFERED: "1" PIP_NO_WARN_SCRIPT_LOCATION: "1" PIP_DISABLE_PIP_VERSION_CHECK: "1" FORCE_COLOR: "1" defaults: run: shell: bash jobs: lint-format: runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@v7 - name: Set up Python 3.13 uses: actions/setup-python@v7 with: python-version: "3.13" cache: pip cache-dependency-path: | pyproject.toml requirements.txt - name: Install project and development tools run: | python -m pip install --upgrade pip setuptools wheel python -m pip install -e ".[dev,docs]" python -m pip install pre-commit bandit - name: Black (src + tests + scripts) run: python -m black --check src tests scripts - name: Ruff (fast linter + isort) run: python -m ruff check src/ tests/ scripts/ - name: Pylint quality threshold run: python -m pylint --fail-under=9.0 src - name: Pre-commit hooks (all files) run: python -m pre_commit run --all-files --show-diff-on-failure - name: Whitespace & merge-conflict check run: | git diff --check ! grep -rP '^(<<<<<<< |>>>>>>> )|^=======$' src/ tests/ scripts/ --include='*.py' --include='*.md' --include='*.yaml' --include='*.yml' - name: Summary if: always() run: echo "### Lint & Format passed" >> $GITHUB_STEP_SUMMARY type-check: runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@v7 - name: Set up Python 3.13 uses: actions/setup-python@v7 with: python-version: "3.13" cache: pip cache-dependency-path: | pyproject.toml requirements.txt - name: Install project and development tools run: | python -m pip install --upgrade pip setuptools wheel python -m pip install -e ".[dev,docs]" - name: Mypy (full src/) run: python -m mypy src/ - name: Pyright (pyrightconfig.json scope) run: python -m pyright - name: Pyright (integration scope) run: | python -m pyright \ src/storage/structural_journal.py \ src/storage/structural_recovery.py \ src/storage/core_restore.py \ src/self_organization/approval.py \ src/self_organization/undo.py \ src/self_organization/plasticity.py \ src/self_organization/coordinator.py \ src/dashboard/structural_api.py \ src/dashboard/operator_bridge.py \ src/dashboard/server.py \ src/runtime/control.py \ src/controller/runtime.py \ src/visualization/structural_heatmap.py - name: Summary if: always() run: echo "### Type Check passed" >> $GITHUB_STEP_SUMMARY security: runs-on: ubuntu-latest timeout-minutes: 10 permissions: contents: read security-events: write steps: - uses: actions/checkout@v7 - name: Set up Python 3.13 uses: actions/setup-python@v7 with: python-version: "3.13" cache: pip - name: Install tools run: | python -m pip install --upgrade pip python -m pip install bandit pip-audit - name: Bandit SAST (src/) run: python -m bandit -c pyproject.toml -r src/ -ll -f json -o bandit-results.json - name: Upload Bandit report if: always() uses: actions/upload-artifact@v7 with: name: bandit-results path: bandit-results.json - name: pip-audit run: python -m pip_audit - name: Summary if: always() run: echo "### Security scan completed" >> $GITHUB_STEP_SUMMARY tests: runs-on: ubuntu-latest timeout-minutes: 30 strategy: fail-fast: false matrix: python-version: ["3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v7 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v7 with: python-version: ${{ matrix.python-version }} cache: pip cache-dependency-path: | pyproject.toml requirements.txt - name: Install project and dev dependencies run: | python -m pip install --upgrade pip setuptools wheel python -m pip install -e ".[dev,docs]" - name: Smoke tests run: python -m pytest -m "smoke" -v --tb=short --junit-xml=test-results-smoke.xml - name: Full test suite (excluding slow) run: python -m pytest -m "not slow" -v --tb=short --junit-xml=test-results-fast.xml --cov=src --cov-report=xml:coverage.xml --cov-report=term-missing:skip-covered - name: Slow tests run: python -m pytest -m "slow" -v --tb=short --junit-xml=test-results-slow.xml || echo "Some slow tests failed (non-blocking)" continue-on-error: true - name: Upload test results if: always() uses: actions/upload-artifact@v7 with: name: test-results-${{ matrix.python-version }} path: | test-results-*.xml coverage.xml - name: Publish test report if: always() uses: dorny/test-reporter@v3 with: name: pytest-report-${{ matrix.python-version }} path: test-results-*.xml reporter: java-junit fail-on-error: false only-summary: true - name: Summary if: always() run: | echo "### Tests (${{ matrix.python-version }})" >> $GITHUB_STEP_SUMMARY python -m pytest --collect-only -q 2>&1 | tail -1 >> $GITHUB_STEP_SUMMARY || true browser: runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@v7 - name: Set up Python 3.13 uses: actions/setup-python@v7 with: python-version: "3.13" cache: pip cache-dependency-path: pyproject.toml - name: Install browser check dependencies run: | python -m pip install --upgrade pip setuptools wheel python -m pip install -e ".[browser]" python -m playwright install --with-deps chromium - name: Run Chromium dashboard smoke check run: python scripts/browser_check.py - name: Set up Node.js 22 uses: actions/setup-node@v4 with: node-version: "22" cache: npm - name: Install dashboard E2E dependencies run: npm ci - name: Run dashboard Chromium E2E suite run: npx playwright install --with-deps chromium && npm run test:e2e large-storage: if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest timeout-minutes: 30 steps: - uses: actions/checkout@v7 - name: Set up Python 3.13 uses: actions/setup-python@v7 with: python-version: "3.13" cache: pip cache-dependency-path: | pyproject.toml requirements.txt - name: Install project and development tools run: | python -m pip install --upgrade pip setuptools wheel python -m pip install -e ".[dev]" - name: Run opt-in large storage tests env: BRAIN5D_RUN_LARGE_STORAGE_TESTS: "1" BRAIN5D_RUN_LARGE_STORAGE_TEST: "1" run: | python -m pytest -v --tb=short \ tests/test_b5d_storage.py::test_large_storage_50k_neurons \ tests/test_delta_journal.py::test_large_journal_100k_entries_opt_in \ --junit-xml=large-storage-results.xml - name: Upload large storage results if: always() uses: actions/upload-artifact@v7 with: name: large-storage-results path: large-storage-results.xml - name: Summary if: always() run: echo "### Large storage tests completed" >> $GITHUB_STEP_SUMMARY scientific-integrity: runs-on: ubuntu-latest timeout-minutes: 30 steps: - uses: actions/checkout@v7 - name: Set up Python 3.13 uses: actions/setup-python@v7 with: python-version: "3.13" cache: pip cache-dependency-path: | pyproject.toml requirements.txt - name: Install project and development tools run: | python -m pip install --upgrade pip setuptools wheel python -m pip install -e ".[dev,docs]" - name: Scientific Integrity Gate run: >- python -m pytest -q tests/test_canonical_state.py tests/test_restore_determinism_abc.py tests/test_engine_restore.py tests/test_production_restore.py tests/test_golden_chain.py tests/test_research_registry.py tests/test_ai_boundaries.py tests/test_ai_governance.py tests/test_experiment_validity.py - name: Summary if: always() run: echo "### Scientific Integrity Gate completed" >> $GITHUB_STEP_SUMMARY catalog-audit: runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@v7 - name: Set up Python 3.13 uses: actions/setup-python@v7 with: python-version: "3.13" cache: pip cache-dependency-path: pyproject.toml - name: Install project run: | python -m pip install --upgrade pip setuptools wheel python -m pip install -e ".[dev]" - name: Generate repository catalog audit report run: python scripts/generate_catalog_audit_report.py - name: Upload catalog audit reports if: always() uses: actions/upload-artifact@v7 with: name: catalog-audit-report path: | research/generated/CATALOG_AUDIT_REPORT.md research/generated/CATALOG_AUDIT_REPORT.json - name: Add catalog audit to job summary if: always() run: cat research/generated/CATALOG_AUDIT_REPORT.md >> "$GITHUB_STEP_SUMMARY" || true build: runs-on: ubuntu-latest timeout-minutes: 10 needs: [lint-format, type-check, security, tests] steps: - uses: actions/checkout@v7 - name: Set up Python 3.13 uses: actions/setup-python@v7 with: python-version: "3.13" - name: Build wheel run: | python -m pip install --upgrade pip build python -m build - name: Verify wheel installs cleanly run: | python -m pip install dist/*.whl python -c "from src.version import BRAIN5D_VERSION; print(BRAIN5D_VERSION)" - name: Upload wheel artifact uses: actions/upload-artifact@v7 with: name: wheel path: dist/*.whl - name: Summary run: | echo "### Build successful" >> $GITHUB_STEP_SUMMARY echo "Wheel: $(ls dist/*.whl)" >> $GITHUB_STEP_SUMMARY docker: runs-on: ubuntu-latest timeout-minutes: 15 needs: [build] steps: - uses: actions/checkout@v7 - name: Download wheel artifact uses: actions/download-artifact@v8 with: name: wheel path: dist/ - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - name: Build Docker image uses: docker/build-push-action@v7 with: context: . load: true tags: brain5d:ci cache-from: type=gha cache-to: type=gha,mode=max - name: Verify Docker image runs run: | docker run --rm brain5d:ci python -c " from src.core.neuron import Neuron; from src.core.network import NeuralNetwork; from src.storage.b5d import B5DReader; from src.homeostasis.engine import HomeostasisEngine; print('Docker image OK'); " - name: Summary run: | echo "### Docker build passed" >> $GITHUB_STEP_SUMMARY echo "Image: brain5d:ci" >> $GITHUB_STEP_SUMMARY docs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@v7 - name: Set up Python 3.13 uses: actions/setup-python@v7 with: python-version: "3.13" - name: Install documentation check dependencies run: | python -m pip install --upgrade pip setuptools wheel python -m pip install -e ".[dev]" - name: Check documentation links and consistency run: | python scripts/check_doc_consistency.py --check-tests echo "### Documentation structure" >> $GITHUB_STEP_SUMMARY find docs/ -name "*.md" -type f | sort | head -50 >> $GITHUB_STEP_SUMMARY || true ci-status: if: always() runs-on: ubuntu-latest needs: [lint-format, type-check, security, tests, large-storage, scientific-integrity, catalog-audit, build, docker, docs] steps: - name: CI Pipeline Summary run: | echo "## CI Pipeline Summary" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY declare -A RESULTS RESULTS["lint-format"]="${{ needs.lint-format.result }}" RESULTS["type-check"]="${{ needs.type-check.result }}" RESULTS["security"]="${{ needs.security.result }}" RESULTS["tests"]="${{ needs.tests.result }}" RESULTS["large-storage"]="${{ needs.large-storage.result }}" RESULTS["scientific-integrity"]="${{ needs.scientific-integrity.result }}" RESULTS["catalog-audit"]="${{ needs.catalog-audit.result }}" RESULTS["build"]="${{ needs.build.result }}" RESULTS["docker"]="${{ needs.docker.result }}" RESULTS["docs"]="${{ needs.docs.result }}" ALL_PASSED=true for JOB in "${!RESULTS[@]}"; do STATUS="${RESULTS[$JOB]}" if [ "$STATUS" = "success" ]; then echo "- **$JOB** — passed" >> $GITHUB_STEP_SUMMARY elif [ "$STATUS" = "skipped" ]; then echo "- **$JOB** — skipped" >> $GITHUB_STEP_SUMMARY else echo "- **$JOB** — $STATUS" >> $GITHUB_STEP_SUMMARY ALL_PASSED=false fi done echo "" >> $GITHUB_STEP_SUMMARY if [ "$ALL_PASSED" = true ]; then echo "**All CI checks passed!**" >> $GITHUB_STEP_SUMMARY else echo "**Some checks did not pass — review logs above.**" >> $GITHUB_STEP_SUMMARY exit 1 fi