Skip to content

Fix three fabricated-result bugs, close the RCE, and overhaul the documentation #47

Fix three fabricated-result bugs, close the RCE, and overhaul the documentation

Fix three fabricated-result bugs, close the RCE, and overhaul the documentation #47

Workflow file for this run

name: Fast CI
on:
pull_request:
branches: [main, master, develop]
paths:
- 'clustrix/**'
- 'tests/**'
- 'setup.py'
- 'pyproject.toml'
- 'requirements*.txt'
push:
branches: [develop] # Only run on develop pushes to avoid duplication with main Tests workflow
paths:
- 'clustrix/**'
- 'tests/**'
- 'setup.py'
- 'pyproject.toml'
- 'requirements*.txt'
jobs:
quick-checks:
name: Quick Checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Cache dependencies
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-quick-${{ hashFiles('**/requirements.txt') }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
# The dev extra, not a hand-listed set: that list installed an
# unpinned black -- the drift #110 pinned pyproject against -- and
# omitted pytest-timeout while the test step below passes
# --timeout=60, so this job failed with "unrecognized arguments"
# regardless of the code.
pip install -e ".[dev]"
- name: Format check with Black
# Same scope as tests.yml's black step. They covered different
# directories, so a file could pass one gate and fail the other.
run: black --check clustrix/ tests/ scripts/
- name: Lint with Flake8
run: flake8 clustrix/ --count --select=E9,F63,F7,F82 --show-source --statistics
- name: Type check with MyPy
run: mypy clustrix/ --ignore-missing-imports
- name: Run quick unit tests
run: |
pytest tests/unit/ -v \
-m "not real_world and not slow" \
--timeout=60 \
-x \
--tb=short \
--maxfail=3
timeout-minutes: 5
local-integration:
name: Local Integration Test
runs-on: ubuntu-latest
needs: quick-checks
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"
- name: Run local executor test
run: |
python -c "
from clustrix import cluster, configure
# Configure for local execution
configure(cluster_type='local')
@cluster(cores=2, memory='2GB')
def test_function(x, y):
import numpy as np
return np.array([x, y]).sum()
result = test_function(10, 20)
assert result == 30, f'Expected 30, got {result}'
print('✅ Local execution test passed')
"
- name: Run serialization test
run: |
python -c "
from clustrix import cluster
import pickle
import cloudpickle
@cluster(cores=1)
def test_serialization():
return {'status': 'success', 'value': 42}
# Test function can be serialized
serialized = cloudpickle.dumps(test_serialization)
assert len(serialized) > 0
print('✅ Serialization test passed')
# Test execution
result = test_serialization()
assert result['status'] == 'success'
assert result['value'] == 42
print('✅ Execution test passed')
"
docker-test:
name: Docker Container Test
runs-on: ubuntu-latest
needs: quick-checks
steps:
- uses: actions/checkout@v4
- name: Build test container
run: |
cat << 'EOF' > Dockerfile.test
FROM python:3.10-slim
WORKDIR /app
COPY . .
RUN pip install -e .
RUN pip install pytest numpy pandas
CMD ["python", "-c", "from clustrix import cluster; print('✅ Import successful')"]
EOF
docker build -f Dockerfile.test -t clustrix-test .
- name: Run container test
run: |
docker run --rm clustrix-test
# Test with actual computation
docker run --rm clustrix-test python -c "
from clustrix import cluster, configure
import numpy as np
configure(cluster_type='local')
@cluster(cores=1, memory='1GB')
def compute():
data = np.random.randn(1000, 1000)
return {'mean': float(np.mean(data)), 'std': float(np.std(data))}
result = compute()
print(f'Result: {result}')
assert abs(result['mean']) < 0.1
assert abs(result['std'] - 1.0) < 0.1
print('✅ Container test passed')
"
security-scan:
name: Security Scan
runs-on: ubuntu-latest
needs: quick-checks
steps:
- uses: actions/checkout@v4
- name: Run Trivy security scan
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'
- name: Check for secrets
run: python scripts/check_for_secrets.py
status-check:
name: CI Status
runs-on: ubuntu-latest
needs: [quick-checks, local-integration, docker-test, security-scan]
if: always()
steps:
- name: Check status
run: |
# security-scan is in `needs` above but was missing from this
# condition, so a failing security scan still reported "All CI
# checks passed". Every job this gate depends on must be checked,
# or the gate is decorative.
failed=0
for job in quick-checks local-integration docker-test security-scan; do
case "$job" in
quick-checks) result="${{ needs.quick-checks.result }}" ;;
local-integration) result="${{ needs.local-integration.result }}" ;;
docker-test) result="${{ needs.docker-test.result }}" ;;
security-scan) result="${{ needs.security-scan.result }}" ;;
esac
if [ "$result" != "success" ]; then
echo "❌ $job: $result"
failed=1
else
echo "✅ $job: success"
fi
done
if [ "$failed" -ne 0 ]; then
echo "CI checks failed"
exit 1
fi
echo "All CI checks passed"