CI/CD Pipeline
This document describes the continuous integration and deployment pipeline for FUSION GUI.
Pipeline Overview
Push/PR
│
├─► Backend Checks
│ ├── Lint (ruff)
│ ├── Type check (mypy) [optional]
│ └── Tests (pytest)
│
├─► Frontend Checks
│ ├── Lint (ESLint)
│ ├── Type check (tsc)
│ ├── Tests (vitest)
│ └── Build (vite)
│
└─► Integration
└── Build wheel with static assets
GitHub Actions Workflow
# .github/workflows/gui.yml
name: GUI CI
on:
push:
branches: [main, develop]
paths:
- 'fusion/api/**'
- 'frontend/**'
- 'docs/gui/**'
- '.github/workflows/gui.yml'
pull_request:
branches: [main, develop]
paths:
- 'fusion/api/**'
- 'frontend/**'
- 'docs/gui/**'
jobs:
backend:
name: Backend Checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- name: Install dependencies
run: |
pip install -e ".[gui,dev]"
- name: Lint with ruff
run: |
ruff check fusion/api/
ruff format --check fusion/api/
- name: Type check with mypy (optional)
run: |
mypy fusion/api/ --ignore-missing-imports || true
continue-on-error: true
- name: Run tests
run: |
pytest fusion/api/tests/ -v --tb=short
frontend:
name: Frontend Checks
runs-on: ubuntu-latest
defaults:
run:
working-directory: frontend
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Type check
run: npm run typecheck
- name: Run tests
run: npm run test:ci
- name: Build
run: npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: frontend-build
path: frontend/dist/
retention-days: 1
e2e:
name: E2E Tests (Playwright)
needs: [backend, frontend]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
- name: Install Python dependencies
run: pip install -e ".[gui,dev]"
- name: Install frontend dependencies
run: cd frontend && npm ci
- name: Install Playwright browsers
run: cd frontend && npx playwright install --with-deps chromium
- name: Download frontend build
uses: actions/download-artifact@v4
with:
name: frontend-build
path: fusion/api/static/
- name: Run E2E tests
run: |
cd frontend && npm run test:e2e
env:
# Use fake simulator for fast, deterministic tests
FUSION_GUI_FAKE_SIMULATOR: "true"
- name: Upload Playwright report
uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-report
path: frontend/playwright-report/
retention-days: 7
integration:
name: Integration Build
needs: [backend, frontend, e2e]
runs-on: ubuntu-latest
if: github.event_name == 'push'
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Download frontend build
uses: actions/download-artifact@v4
with:
name: frontend-build
path: fusion/api/static/
- name: Build wheel
run: |
pip install build
python -m build
- name: Verify wheel contents
run: |
pip install dist/*.whl
# Check that static files are included
python -c "from pathlib import Path; import fusion.api; p = Path(fusion.api.__file__).parent / 'static' / 'index.html'; assert p.exists(), 'Static files missing'"
- name: Upload wheel
uses: actions/upload-artifact@v4
with:
name: fusion-wheel
path: dist/*.whl
retention-days: 7
release:
name: Release
needs: [integration]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- uses: actions/checkout@v4
- name: Download wheel
uses: actions/download-artifact@v4
with:
name: fusion-wheel
path: dist/
# Add PyPI publish step when ready
# - name: Publish to PyPI
# uses: pypa/gh-action-pypi-publish@release/v1
# with:
# password: ${{ secrets.PYPI_API_TOKEN }}
Package Configuration
pyproject.toml Additions
[project.optional-dependencies]
gui = [
"fastapi>=0.109.0",
"uvicorn[standard]>=0.27.0",
"sse-starlette>=1.8.0",
"aiofiles>=23.2.0",
# sqlalchemy already in base deps
]
[project.scripts]
fusion-gui = "fusion.cli.run_gui:main"
[tool.setuptools.packages.find]
where = ["."]
include = ["fusion*"]
[tool.setuptools.package-data]
"fusion.api" = ["static/**/*"]
MANIFEST.in
include fusion/api/static/**/*
recursive-include fusion/api/static *
Build Process
Development Build
No build needed - run dev servers directly:
# Backend
uvicorn fusion.api.main:app --reload --port 8765
# Frontend (separate terminal)
cd frontend && npm run dev
Production Build
# Build frontend
cd frontend
npm ci
npm run build
# Copy to static directory
rm -rf ../fusion/api/static/*
cp -r dist/* ../fusion/api/static/
# Build Python wheel
cd ..
python -m build
Makefile Targets
# Makefile additions
.PHONY: frontend-dev
frontend-dev:
cd frontend && npm run dev
.PHONY: api-dev
api-dev:
uvicorn fusion.api.main:app --reload --port 8765
.PHONY: frontend-build
frontend-build:
cd frontend && npm ci && npm run build
rm -rf fusion/api/static/*
cp -r frontend/dist/* fusion/api/static/
.PHONY: build-gui
build-gui: frontend-build
python -m build
.PHONY: test-api
test-api:
pytest fusion/api/tests/ -v
.PHONY: test-frontend
test-frontend:
cd frontend && npm test
.PHONY: lint-api
lint-api:
ruff check fusion/api/
ruff format --check fusion/api/
.PHONY: lint-frontend
lint-frontend:
cd frontend && npm run lint
.PHONY: validate-gui
validate-gui: lint-api lint-frontend test-api test-frontend
@echo "All GUI checks passed"
Wheel vs Source Distribution
Wheel (Recommended for Users)
Pre-built, fast to install
Includes compiled frontend assets
No Node.js required
pip install fusion-6.1.0-py3-none-any.whl
Source Distribution (sdist)
Requires Node.js to build frontend during install
Needed for:
Users who want to modify frontend
Platforms not supported by wheel
For sdist to work, we need a build hook:
# pyproject.toml
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"
# Note: sdist won't include built frontend
# Users building from source need Node.js
Recommendation: Always distribute wheels with pre-built frontend.
CI Gates
PR Merge Requirements
Check |
Required |
Blocking |
|---|---|---|
Backend lint (ruff) |
Yes |
Yes |
Backend tests |
Yes |
Yes |
Frontend lint (ESLint) |
Yes |
Yes |
Frontend typecheck |
Yes |
Yes |
Frontend tests |
Yes |
Yes |
Frontend build |
Yes |
Yes |
E2E tests (Playwright) |
Yes (M2+) |
Yes |
Accessibility (axe) |
Advisory (M2), Blocking (M4) |
M4+ |
Backend mypy |
No |
No |
E2E Flake Policy
Playwright retries: 1 (configured in
playwright.config.ts)Tests that pass only on retry are considered flaky
Repeated flakes (2+ in a week) require a tracking issue labeled
flaky-testSee 10-testing.md for handling guidelines
Branch Protection Rules
For main and develop:
- Require pull request reviews (1)
- Require status checks to pass
- Backend Checks
- Frontend Checks
- Require branches to be up to date
- Do not allow bypassing settings
Release Process
Version Bumping
Update version in
pyproject.tomlUpdate version in
frontend/package.jsonUpdate CHANGELOG.md
Create git tag
# Example
git checkout main
git pull
# Update versions
git add .
git commit -m "chore: bump version to 6.1.0"
git tag v6.1.0
git push origin main --tags
Release Checklist
## Release Checklist
- [ ] All CI checks pass on `main`
- [ ] CHANGELOG.md updated
- [ ] Version bumped in pyproject.toml
- [ ] Version bumped in frontend/package.json
- [ ] Git tag created
- [ ] Wheel built and tested locally
- [ ] GitHub Release created
- [ ] PyPI package published (when ready)
Local Development Shortcuts
VS Code Tasks
// .vscode/tasks.json
{
"version": "2.0.0",
"tasks": [
{
"label": "API Dev Server",
"type": "shell",
"command": "uvicorn fusion.api.main:app --reload --port 8765",
"group": "build",
"problemMatcher": []
},
{
"label": "Frontend Dev Server",
"type": "shell",
"command": "npm run dev",
"options": { "cwd": "${workspaceFolder}/frontend" },
"group": "build",
"problemMatcher": []
}
]
}
Pre-commit Hooks
Add GUI checks to existing pre-commit config:
# .pre-commit-config.yaml additions
repos:
- repo: local
hooks:
- id: frontend-lint
name: Frontend Lint
entry: bash -c 'cd frontend && npm run lint'
language: system
files: ^frontend/
pass_filenames: false