Skip to content

Commit 0b42141

Browse files
jeremymanningclaude
andcommitted
Add GitHub Actions coverage badge generation
Created coverage badge workflow that: - Generates coverage.svg badge using coverage-badge package - Uploads coverage artifacts in main test workflow - Creates separate coverage-badge workflow that commits badge to repo - Replaces Codecov dependency with self-contained solution This enables coverage badge display without external service accounts. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent f7d7363 commit 0b42141

File tree

3 files changed

+151
-7
lines changed

3 files changed

+151
-7
lines changed

.github/workflows/coverage-badge.yml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: Coverage Badge
2+
3+
on:
4+
push:
5+
branches: [ master ]
6+
workflow_run:
7+
workflows: ["Tests"]
8+
types:
9+
- completed
10+
11+
jobs:
12+
coverage-badge:
13+
runs-on: ubuntu-latest
14+
if: github.ref == 'refs/heads/master'
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
with:
19+
persist-credentials: false
20+
fetch-depth: 0
21+
22+
- name: Set up Python 3.11
23+
uses: actions/setup-python@v4
24+
with:
25+
python-version: '3.11'
26+
27+
- name: Install dependencies
28+
run: |
29+
python -m pip install --upgrade pip
30+
pip install -e ".[dev,test]"
31+
pip install coverage-badge
32+
33+
- name: Run tests with coverage
34+
run: |
35+
pytest tests/ -v --cov=clustrix --cov-report=xml --cov-report=term
36+
37+
- name: Generate coverage badge
38+
run: |
39+
coverage-badge -o coverage.svg -f
40+
41+
- name: Verify badge created
42+
run: |
43+
ls -la coverage.svg
44+
cat coverage.svg | head -5
45+
46+
- name: Commit badge
47+
run: |
48+
git config --local user.email "action@github.com"
49+
git config --local user.name "GitHub Action"
50+
git add coverage.svg
51+
git diff --staged --quiet || git commit -m "Update coverage badge" || echo "No changes to commit"
52+
53+
- name: Push badge
54+
uses: ad-m/github-push-action@master
55+
with:
56+
github_token: ${{ secrets.GITHUB_TOKEN }}
57+
branch: master
58+
force: false

.github/workflows/tests.yml

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,21 @@ jobs:
4949
run: |
5050
pytest tests/ -v --cov=clustrix --cov-report=xml --cov-report=html --cov-report=term-missing
5151
52-
- name: Upload coverage to Codecov
52+
- name: Generate coverage badge
5353
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11'
54-
uses: codecov/codecov-action@v3
54+
run: |
55+
pip install coverage-badge
56+
coverage-badge -o coverage.svg -f
57+
58+
- name: Upload coverage report
59+
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11'
60+
uses: actions/upload-artifact@v4
5561
with:
56-
file: ./coverage.xml
57-
flags: unittests
58-
name: codecov-umbrella
59-
fail_ci_if_error: false
62+
name: coverage-report
63+
path: |
64+
coverage.xml
65+
htmlcov/
66+
coverage.svg
6067
6168
- name: Test installation
6269
run: |

clustrix/executor.py

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,86 @@ def _submit_sge_job(
242242
def _submit_k8s_job(
243243
self, func_data: Dict[str, Any], job_config: Dict[str, Any]
244244
) -> str:
245-
"""Submit job via Kubernetes."""
245+
"""
246+
Submit a job to Kubernetes cluster using containerized Python execution.
247+
248+
This method implements a sophisticated Kubernetes job submission strategy that
249+
packages Python functions and data into self-contained container jobs without
250+
requiring custom Docker images or persistent storage.
251+
252+
**Architecture:**
253+
254+
1. **Function Serialization**: Uses cloudpickle to serialize the function and all data
255+
2. **Base64 Encoding**: Encodes serialized data for safe embedding in container args
256+
3. **Container Execution**: Creates a Job with inline Python code that:
257+
- Decodes the base64 data
258+
- Deserializes the function and arguments
259+
- Executes the function
260+
- Captures results or errors
261+
4. **Resource Management**: Applies CPU and memory limits from job_config
262+
263+
**Key Features:**
264+
- **No Custom Images**: Uses standard `python:3.11-slim` image
265+
- **Self-Contained**: All code and data embedded in Job manifest
266+
- **Resource Aware**: Respects CPU/memory requirements
267+
- **Error Handling**: Captures exceptions with full tracebacks
268+
- **Cloud Native**: Leverages Kubernetes Job semantics for reliability
269+
270+
**Job Manifest Structure:**
271+
```yaml
272+
apiVersion: batch/v1
273+
kind: Job
274+
metadata:
275+
name: clustrix-job-{timestamp}
276+
spec:
277+
template:
278+
spec:
279+
containers:
280+
- name: clustrix-worker
281+
image: python:3.11-slim
282+
command: ["python", "-c"]
283+
args: ["<embedded Python code>"]
284+
resources:
285+
requests/limits: {cpu, memory from job_config}
286+
restartPolicy: Never
287+
```
288+
289+
Args:
290+
func_data: Serialized function data containing:
291+
- 'func': The function to execute
292+
- 'args': Positional arguments
293+
- 'kwargs': Keyword arguments
294+
- 'requirements': Package dependencies (not used for K8s)
295+
job_config: Job configuration including:
296+
- 'cores': CPU request/limit (default: 1)
297+
- 'memory': Memory request/limit (default: "1Gi")
298+
- Additional K8s-specific settings
299+
300+
Returns:
301+
str: Kubernetes Job name that can be used for status tracking
302+
303+
Raises:
304+
ImportError: If kubernetes package is not installed
305+
Exception: If Kubernetes API calls fail
306+
307+
Examples:
308+
>>> func_data = {
309+
... 'func': lambda x: x**2,
310+
... 'args': (5,),
311+
... 'kwargs': {},
312+
... 'requirements': {}
313+
... }
314+
>>> job_config = {'cores': 2, 'memory': '4Gi'}
315+
>>> job_id = executor._submit_k8s_job(func_data, job_config)
316+
>>> print(job_id) # "clustrix-job-1234567890"
317+
318+
Note:
319+
- Requires kubernetes package: `pip install kubernetes`
320+
- Assumes kubectl is configured with cluster access
321+
- Jobs are created in the "default" namespace
322+
- Cloudpickle is used for function serialization
323+
- Results are captured via stdout parsing (CLUSTRIX_RESULT: prefix)
324+
"""
246325
try:
247326
from kubernetes import client, config as k8s_config
248327
except ImportError:

0 commit comments

Comments
 (0)