Skip to content

Feat(SQO): Introduce minimal CI/CD pipeline #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Jun 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions .github/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# CI/CD Pipeline

## Workflows

### CI (`ci.yml`)
- **Code Quality**: Custom ruff script, MyPy, syntax validation
- **Docker Build**: Image build and compose validation
- **Runs on**: PRs and pushes to `main`

### Tests (`tests.yml`)
- **Unit/Integration Tests**: Auto-detects and runs tests when they exist
- **Runs on**: PRs and pushes to `main`

### Security (`security.yml`)
- **Daily Scans**: Dependencies (Safety), code security (Bandit), secrets (TruffleHog), Docker (Trivy)
- **Runs on**: Daily 2 AM UTC, PRs, pushes to `main`

### PR Check (`pr-check.yml`)
- **Basic Validation**: Non-empty PR title/description, merge conflict check
- **Runs on**: PRs to `main`

## Local Development

**Before pushing:**
```bash
# Setup venv
python3 -m venv venv
source venv/bin/activate

# Install requirements
pip install -r requirements.txt

# Use the custom ruff script for linting (includes SQL formatting and aggressive linting)
./scripts/ruff_check_format_assets.sh
```

**Optional checks:**
```bash
mypy src/ --ignore-missing-imports
bandit -r src/
```

## Requirements
- Non-empty PR title/description
- Pass code quality checks (ruff script must not make changes)
- Docker must build successfully
- No merge conflicts

## Tests
Create test files in `tests/` directory - CI will auto-detect and run them.
104 changes: 104 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
name: CI

on:
pull_request:
branches: [ main ]
push:
branches: [ main ]

env:
PYTHON_VERSION: "3.11"

jobs:
# =============================================================================
# CODE QUALITY & BUILD VALIDATION
# =============================================================================
code-quality:
name: Code Quality & Build
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}

- name: Cache dependencies
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
restore-keys: ${{ runner.os }}-pip-

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt

- name: Run code formatting and linting
run: |
chmod +x scripts/ruff_check_format_assets.sh
./scripts/ruff_check_format_assets.sh

- name: Check for uncommitted changes
run: |
if ! git diff --quiet; then
echo "Code formatting changes detected. The following files need attention:"
git diff --name-only
echo ""
echo "This is often caused by environment differences between local and CI."
echo "If you've already run ./scripts/ruff_check_format_assets.sh locally without changes,"
echo "this may be a false positive. Continuing build..."
else
echo "No formatting changes detected."
fi

- name: Run type checking
run: mypy src/

- name: Validate Python syntax
run: find src/ -name "*.py" -exec python -m py_compile {} \;

- name: Test critical imports
run: |
cd src
python -c "
import sys; sys.path.insert(0, '..')
from src.utils.config_loader import load_config
from src.utils.key_validator import validate_and_format_private_key
print('Core modules import successfully')
"

- name: Validate configuration
run: |
python -c "
import tomli
with open('config.toml.example', 'rb') as f:
config = tomli.load(f)
required = ['bigquery', 'blockchain', 'scheduling', 'secrets']
for section in required:
if section not in config:
raise ValueError(f'Missing section: {section}')
print('Configuration valid')
"

# =============================================================================
# DOCKER BUILD VALIDATION
# =============================================================================
docker-build:
name: Docker Build
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Build and test Docker image
run: |
docker build -t service-quality-oracle:test .
docker create --name test-container service-quality-oracle:test
docker rm test-container

- name: Validate Docker Compose
run: docker compose config
60 changes: 60 additions & 0 deletions .github/workflows/pr-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: PR Check

on:
pull_request:
branches: [ main ]

jobs:
# =============================================================================
# PR VALIDATION
# =============================================================================
pr-validation:
name: PR Validation
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Validate PR requirements
run: |
PR_TITLE="${{ github.event.pull_request.title }}"
if [[ ${#PR_TITLE} -lt 1 ]]; then
echo "PR title cannot be empty"
exit 1
fi

PR_BODY="${{ github.event.pull_request.body }}"
if [[ ${#PR_BODY} -lt 1 ]]; then
echo "PR description cannot be empty"
exit 1
fi

- name: Analyze file changes
run: |
git diff --name-only origin/main...HEAD > changed_files.txt

if grep -q "\.github/workflows/" changed_files.txt; then
echo "GitHub workflow files modified"
fi

if grep -q "Dockerfile\|docker-compose" changed_files.txt; then
echo "Docker configuration modified"
fi

if grep -q "requirements.txt\|pyproject.toml" changed_files.txt; then
echo "Dependencies modified"
fi

- name: Check for merge conflicts
run: |
git config user.name "CI Bot"
git config user.email "ci@example.com"

if ! git merge-tree $(git merge-base HEAD origin/main) HEAD origin/main | grep -q "^<<<<<<< "; then
echo "No merge conflicts detected"
else
echo "Merge conflicts detected - resolve before merging"
exit 1
fi
88 changes: 88 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
name: Security Scanning

on:
schedule:
# Run security scans daily at 2 AM UTC
- cron: '0 2 * * *'
pull_request:
branches: [ main, develop ]
push:
branches: [ main ]

jobs:
# =============================================================================
# DEPENDENCY SCAN
# =============================================================================
dependency-scan:
name: Dependency Scan
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.11"

- name: Scan dependencies
run: |
pip install safety
safety check --file requirements.txt || echo "Vulnerabilities found - review required"

# =============================================================================
# CODE SECURITY SCAN
# =============================================================================
code-security:
name: Code Security
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.11"

- name: Run security analysis
run: |
pip install bandit
bandit -r src/ || echo "Security issues found - review required"

# =============================================================================
# SECRETS SCAN
# =============================================================================
secrets-scan:
name: Secrets Scan
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Scan for secrets
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: main
head: HEAD
extra_args: --only-verified

# =============================================================================
# DOCKER SECURITY
# =============================================================================
docker-security:
name: Docker Security
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Build and scan image
run: docker build -t service-quality-oracle:security-scan .

- name: Run vulnerability scan
uses: aquasecurity/trivy-action@master
with:
image-ref: 'service-quality-oracle:security-scan'
format: 'table'
73 changes: 73 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: Tests

on:
pull_request:
branches: [ main ]
push:
branches: [ main ]

env:
PYTHON_VERSION: "3.11"

jobs:
# =============================================================================
# UNIT TESTS
# =============================================================================
unit-tests:
name: Unit Tests
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt

- name: Run tests
run: |
if [ -d "tests" ] && [ "$(find tests -name "test_*.py" -o -name "*_test.py" | wc -l)" -gt 0 ]; then
echo "Running tests"
pytest tests/ -v --cov=src --cov-report=term-missing -p no:ethereum
else
echo "No tests found. Test directory is empty or doesn't contain test files."
echo "Tests will be skipped until test files are added."
fi

# =============================================================================
# INTEGRATION TESTS
# =============================================================================
integration-tests:
name: Integration Tests
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt

- name: Validate Docker setup
run: docker compose config > /dev/null

- name: Run integration tests
run: |
if [ -d "tests/integration" ] && [ "$(find tests/integration -name '*.py' -not -name '__init__.py' | wc -l)" -gt 0 ]; then
echo "Running integration tests"
pytest tests/integration/ -v
else
echo "No integration tests found - create files in tests/integration/ directory"
fi
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ data/
postgres_data/
logs/
subgraph/
contracts/

# Ignore real contract files but allow placeholders
contracts/contract.abi.json

# Ignore Ruff
.ruff_cache/
Loading