Skip to content

🎨 Palette: [UX improvement] Surface background process errors to user #26

🎨 Palette: [UX improvement] Surface background process errors to user

🎨 Palette: [UX improvement] Surface background process errors to user #26

Workflow file for this run

name: Minimal CI
on:
push:
branches: [main]
pull_request:
branches: [main]
# Cancel in-progress runs for same PR
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
name: Tests
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12']
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest-cov
pip install -e ".[dev]"
- name: Run tests
run: |
pytest tests/ -xvs --tb=short --cov=src --cov-report=xml
env:
PYTHONPATH: src
- name: Upload coverage
uses: codecov/codecov-action@v4
if: matrix.python-version == '3.11'
with:
files: ./coverage.xml
flags: unittests
name: codecov-umbrella
fail_ci_if_error: false
security:
name: Security Scan
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- name: Install security tools
run: |
python -m pip install bandit safety pip-audit
- name: Run Bandit (SAST)
id: bandit
run: |
bandit -r src/ -f json -o bandit-report.json || true
continue-on-error: true
- name: Run Safety (Dependency Check)
id: safety
run: |
# The safety command might output non-json warnings to stdout before the JSON output
output=$(safety check --json || true)
# Use Node.js to safely extract the JSON object from stdin to avoid template literal escaping issues
echo "$output" | node -e "
const text = require('fs').readFileSync(0, 'utf-8');
const start = text.indexOf('{');
const end = text.lastIndexOf('}');
if (start !== -1 && end !== -1 && end > start) {
console.log(text.substring(start, end + 1));
} else {
console.log('{}');
}
" > safety-report.json
continue-on-error: true
- name: Run Pip-audit
id: pip-audit
run: |
pip-audit --desc --format json > pip-audit-report.json || true
if [ ! -s pip-audit-report.json ]; then echo "{}" > pip-audit-report.json; fi
continue-on-error: true
- name: Parse security reports
run: |
python << 'EOF'
import json
import sys
# Bandit
try:
with open('bandit-report.json') as f:
bandit = json.load(f)
print(f"Bandit: {bandit.get('metrics', {}).get('SEVERITY.HIGH', 0)} HIGH issues")
except:
pass
# Safety
try:
with open('safety-report.json') as f:
safety = json.load(f)
vulns = safety.get('vulnerabilities', [])
print(f"Safety: {len(vulns)} known vulnerabilities")
except:
pass
# Pip-audit
try:
with open('pip-audit-report.json') as f:
pip_audit = json.load(f)
deps = pip_audit.get('dependencies', [])
vuln_deps = [d for d in deps if d.get('vulnerabilities')]
print(f"Pip-audit: {len(vuln_deps)} vulnerable packages")
except:
pass
EOF
- name: Upload security reports
uses: actions/upload-artifact@v4
if: always()
with:
name: security-reports
path: |
bandit-report.json
safety-report.json
pip-audit-report.json
- name: Comment PR with security results
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const parseReport = (file) => {
try {
if (!fs.existsSync(file)) return {};
const content = fs.readFileSync(file, 'utf8').trim();
return content ? JSON.parse(content) : {};
} catch (e) {
return {};
}
};
const bandit = parseReport('bandit-report.json');
const safety = parseReport('safety-report.json');
const pipAudit = parseReport('pip-audit-report.json');
let body = '## 🔒 Security Scan Results\n\n';
body += '### Bandit (SAST)\n';
body += `- ${(bandit.metrics && bandit.metrics['SEVERITY.HIGH']) || 0} HIGH severity issues\n\n`;
body += '### Safety (Dependencies)\n';
body += `- ${(safety.vulnerabilities && safety.vulnerabilities.length) || 0} known vulnerabilities\n\n`;
body += '### Pip-audit (Dependencies)\n';
body += `- ${(pipAudit.dependencies && pipAudit.dependencies.filter(d => d.vulnerabilities && d.vulnerabilities.length).length) || 0} vulnerable packages\n`;
// Use core.summary to avoid GITHUB_TOKEN write permission issues on forks
await core.summary
.addRaw(body)
.write();
github-token: ${{ secrets.GITHUB_TOKEN }}
lint:
name: Code Quality
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- name: Install linting tools
run: |
python -m pip install ruff black mypy
- name: Run Ruff (Fast Python Linter)
run: |
ruff check src/ tests/ --output-format=github
continue-on-error: true
- name: Check formatting (Black)
run: |
black --check src/ tests/ --diff
continue-on-error: true
- name: Type checking (MyPy)
run: |
mypy src/ --ignore-missing-imports || true
continue-on-error: true
build:
name: Build Test
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[gguf]"
- name: Verify import
run: |
python -c "import ledgermind; print('✓ Imports work')"
- name: Create distribution
run: |
pip install build
python -m build