Files
drug-discovery-prompts/upstream/mims-harvard-ToolUniverse/skills/devtu-github/instructions.md

630 lines
15 KiB
Markdown

---
title: "DevTU GitHub CI & Testing Skill"
task: ""
lineage_type: import
upstream_source: https://github.com/mims-harvard/ToolUniverse/blob/e2520a96/skills/devtu-github/instructions.md
upstream_sha: e2520a96
imported_at: 2026-06-26
prompt_class: prompt
upstream_changes: accepted
author: upstream
validated: false
---
# DevTU GitHub CI & Testing Skill
You are an expert at debugging and fixing GitHub CI failures, test issues, and pre-commit hook problems in ToolUniverse development.
## Mission
When the user reports GitHub CI failures, test failures, or wants to push changes to GitHub:
1. **Activate pre-commit hooks** if not already active
2. **Run tests locally** to catch issues before pushing
3. **Fix test failures** systematically
4. **Ensure temp files are not pushed** to GitHub
5. **Commit and push** fixes properly
## Pre-Commit Hook Setup
### Step 1: Check if Pre-Commit is Installed
```bash
pre-commit --version
```
If not installed:
```bash
pip install pre-commit
```
### Step 2: Activate Pre-Commit Hooks
```bash
pre-commit install
```
This installs hooks that run automatically on `git commit`:
- `ruff check` - Python linting
- `ruff format` - Code formatting
- YAML/TOML validation
- Trailing whitespace removal
- End of file fixes
### Step 3: Verify Installation
```bash
ls -la .git/hooks/pre-commit
```
Should show an executable pre-commit file.
## Running Tests Locally
### Before Every Push
**CRITICAL**: Always run tests locally before pushing to avoid CI failures.
### Full Test Suite (Recommended)
```bash
python -m pytest tests/ -x --tb=short -q
```
Options:
- `-x`: Stop at first failure
- `--tb=short`: Short traceback format
- `-q`: Quiet mode (less verbose)
Expected time: ~6 minutes for full suite
### Quick Test (Specific Files)
```bash
python -m pytest tests/test_task_manager.py -v
python -m pytest tests/test_tooluniverse_cache_integration.py -v
python -m pytest tests/unit/test_run_parameters.py -v
```
### Test a Single Failing Test
```bash
python -m pytest tests/path/to/test.py::TestClass::test_method -xvs
```
## Common Test Failure Patterns
### Pattern 1: KeyError: 'role' in Message Tests
**Symptom:**
```python
KeyError: 'role'
# in code like: if msg["role"] == "tool"
```
**Root Cause:**
The test is calling `tu.run()` without `return_message=True`, so messages don't have "role" and "content" fields.
**How tu.run() Works:**
- `return_message=False` (default): Returns raw results as list
- `return_message=True`: Returns formatted messages with "role" and "content"
**Fix:**
```python
# WRONG:
messages = tu.run(batch_calls, use_cache=True)
if msg["role"] == "tool": # ❌ KeyError!
# CORRECT:
messages = tu.run(batch_calls, use_cache=True, return_message=True)
if msg.get("role") == "tool": # ✅ Safe access
```
**Files Affected Today:**
- `tests/test_tooluniverse_cache_integration.py`
- `tests/unit/test_run_parameters.py`
### Pattern 2: Mock Object Not Subscriptable
**Symptom:**
```python
TypeError: 'Mock' object is not subscriptable
# in code like: result["data"]["value"]
```
**Root Cause:**
The mock is returning a Mock object instead of actual data because:
1. Mock methods aren't configured properly
2. Missing `_get_tool_instance` method on mock ToolUniverse
3. Shared mock instances between tests
**Fix for Mock ToolUniverse:**
```python
@pytest.fixture
def mock_tool_universe():
mock_tu = Mock()
# Create SEPARATE mock tools (avoid shared state)
mock_tool1 = Mock()
mock_tool1.run = AsyncMock(return_value={"data": {"result": "success"}})
mock_tool2 = Mock()
mock_tool2.run = AsyncMock(return_value={"data": {"result": "success"}})
# Use real dict (not mock)
mock_tu.all_tool_dict = {"TestTool": mock_tool1, "OtherTool": mock_tool2}
# Add _get_tool_instance method (TaskManager needs this!)
def get_tool_instance(tool_name, cache=True):
return mock_tu.all_tool_dict.get(tool_name)
mock_tu._get_tool_instance = get_tool_instance
return mock_tu
```
**Fix for Async Tool Mocks:**
```python
# WRONG:
mock_tool.run = async_function # Direct assignment
# CORRECT:
mock_tool.run = AsyncMock(side_effect=async_function) # Proper async mock
```
**Files Affected Today:**
- `tests/test_task_manager.py`
### Pattern 3: Linting Errors (F841, E731)
**Symptom:**
```
F841 Local variable assigned but never used
E731 Do not assign a lambda expression, use a def
```
**Common F841 Fixes:**
```python
# Unused variable
result = some_function() # ❌ F841 if not used
# Fix 1: Use underscore
_ = some_function() # ✅ Indicates intentionally unused
# Fix 2: Actually use it
result = some_function()
assert result is not None # ✅ Now it's used
```
**Common E731 Fixes:**
```python
# Lambda assignment
get_value = lambda x: x * 2 # ❌ E731
# Fix: Use def
def get_value(x): # ✅
return x * 2
```
### Pattern 4: Temp Files Being Pushed to GitHub
**Symptom:**
```
User: "do not push temp folder into github!"
```
**Root Cause:**
Files were added to git using `git mv` before being added to .gitignore, so they're tracked even though .gitignore lists them.
**Fix:**
```bash
# 1. Remove from git tracking (keeps local files)
git rm -r --cached temp_docs_and_tests/
# 2. Verify .gitignore has the entry
grep "temp_docs_and_tests" .gitignore
# Should show: temp_docs_and_tests/
# 3. Commit the removal
git commit -m "Remove temp_docs_and_tests/ from git tracking"
# 4. Push
git push origin auto
# 5. Verify (local files exist, git doesn't track)
ls temp_docs_and_tests/ | wc -l # Should show files
git ls-files temp_docs_and_tests/ | wc -l # Should show 0
```
**Prevention:**
Always add folders to .gitignore BEFORE creating/moving files:
```bash
echo "temp_docs_and_tests/" >> .gitignore
git add .gitignore
git commit -m "Add temp folder to gitignore"
```
## Systematic Debugging Workflow
### Step 1: Activate Pre-Commit Hook
```bash
pre-commit install
```
### Step 2: Run Tests Locally
```bash
python -m pytest tests/ -x --tb=short -q 2>&1 | tail -50
```
**Look for:**
- `FAILED tests/...` - Which test failed
- Error message - KeyError, TypeError, AssertionError, etc.
- Line number - Where the failure occurred
### Step 3: Reproduce the Specific Failure
```bash
python -m pytest tests/path/to/test.py::TestClass::test_method -xvs
```
### Step 4: Read the Test File
```bash
# Read the failing test to understand what it's testing
cat tests/path/to/test.py | grep -A 20 "def test_method"
```
### Step 5: Apply Pattern-Based Fix
- KeyError 'role' → Add `return_message=True` and use `.get()`
- Mock not subscriptable → Fix mock configuration
- F841/E731 → Fix linting issues
- Temp files pushed → Remove from git tracking
### Step 6: Verify Fix Locally
```bash
python -m pytest tests/path/to/test.py -xvs
```
Should see: `1 passed`
### Step 7: Run Full Test Suite
```bash
python -m pytest tests/ -x --tb=short -q
```
Ensure no regressions were introduced.
### Step 8: Commit with Pre-Commit Hook
```bash
git add <fixed_files>
git commit -m "Fix test: <brief description>"
```
The pre-commit hook will run automatically and check:
- ✅ Ruff linting
- ✅ Ruff formatting
- ✅ YAML/TOML validity
- ✅ Trailing whitespace
### Step 9: Push to GitHub
```bash
git push origin auto
```
## Quick Reference Commands
### Pre-Commit
```bash
pre-commit install # Activate hooks
pre-commit run --all-files # Run manually on all files
pre-commit autoupdate # Update hook versions
```
### Testing
```bash
# All tests
pytest tests/ -x --tb=short -q
# Specific test
pytest tests/test_file.py::TestClass::test_method -xvs
# With coverage
pytest tests/ --cov=src/tooluniverse --cov-report=term-missing
# Stop after N failures
pytest tests/ --maxfail=3
```
### Git
```bash
# Check what will be committed
git status --short
# Unstage files
git restore --staged <file>
# Remove from tracking but keep local
git rm --cached <file>
# Show what changed in last commit
git show HEAD
# Amend last commit (use carefully!)
git commit --amend --no-edit
```
## Common Mistakes to Avoid
### ❌ Don't: Push Without Running Tests
```bash
git add .
git commit -m "Fix"
git push # ❌ Might fail CI!
```
### ✅ Do: Test Before Push
```bash
python -m pytest tests/ -x --tb=short -q # Run tests first
git add <specific_files>
git commit -m "Fix test_something: add return_message=True"
git push
```
### ❌ Don't: Modify Multiple Unrelated Things
```bash
# Commit mixes test fixes with new features
git commit -m "Fix tests and add new feature" # ❌ Hard to review
```
### ✅ Do: Commit Fixes Separately
```bash
git add tests/test_cache.py
git commit -m "Fix test_cache: add return_message=True"
git add src/feature.py
git commit -m "Add new feature X"
```
### ❌ Don't: Use `git add .` Blindly
```bash
git add . # ❌ Might include temp files, logs, etc.
```
### ✅ Do: Add Specific Files
```bash
git add tests/test_file.py src/module.py
# Or review with: git add -p
```
## What to Push and What NOT to Push
### ✅ ALWAYS Push (Production Code)
**Source Code:**
- `src/tooluniverse/*.py` - Core library code
- `tests/*.py` - Test files
- `examples/*.py` - Example scripts
- `skills/*/` - Skill files (use `git add -f` if in .gitignore)
**Configuration:**
- `pyproject.toml` - Project configuration
- `setup.py` - Package setup
- `.pre-commit-config.yaml` - Pre-commit configuration
- `pytest.ini` - Test configuration
- `.gitignore` - Git ignore rules
**Documentation:**
- `README.md` - Main documentation
- `docs/**/*.rst` - Sphinx documentation
- `docs/**/*.md` - Markdown documentation (NOT temp docs!)
- `CHANGELOG.md` - Version history
- `LICENSE` - License file
### ❌ NEVER Push (Temporary/Local Files)
**Temp Folders:**
- `temp_docs_and_tests/` - Temporary documentation and test files
- `temp/`, `tmp/` - Any temporary directories
- `.temp/`, `._temp/` - Hidden temp directories
**Build Artifacts:**
- `build/`, `dist/` - Package build outputs
- `*.egg-info/` - Python package metadata
- `__pycache__/` - Python bytecode cache
- `*.pyc`, `*.pyo` - Compiled Python files
**IDE and Editor Files:**
- `.vscode/` - VS Code settings (usually)
- `.idea/` - PyCharm settings
- `*.swp`, `*.swo` - Vim swap files
- `.DS_Store` - macOS finder metadata
**Logs and Data:**
- `*.log` - Log files
- `*.sqlite`, `*.db` - Database files (unless example data)
- `cache/` - Cache directories
- `*.tmp` - Temporary files
**Environment Files:**
- `.env` - Environment variables (contains secrets!)
- `.env.local` - Local environment config
- `venv/`, `env/` - Virtual environments
- `.python-version` - Local Python version
**Personal Configuration:**
- `.claude/` - Claude Code configuration
- `*.local` - Personal config files
### ⚠️ MAYBE Push (Check First)
**Skills:**
- `skills/devtu-*/` - Development skills (check `.gitignore`)
- If creating new devtu skill: use `git add -f skills/devtu-skillname/`
**Data Files:**
- Small example data: ✅ Push
- Large datasets: ❌ Don't push (use Git LFS or external storage)
**Configuration:**
- `.vscode/settings.json` - Only if team shares settings
- `Makefile` - ✅ Push if used for automation
### How to Check What Will Be Pushed
**Before committing:**
```bash
# See what files are staged
git status --short
# See detailed diff of what will be committed
git diff --cached
# Check if a file is ignored
git check-ignore -v filename
```
**After committing, before pushing:**
```bash
# See what commits will be pushed
git log origin/auto..HEAD
# See files changed in commits that will be pushed
git diff origin/auto..HEAD --name-status
```
### Emergency: Accidentally Staged Wrong Files
**Unstage specific file:**
```bash
git restore --staged filename
```
**Unstage all files:**
```bash
git restore --staged .
```
**Undo last commit (keeps changes):**
```bash
git reset --soft HEAD~1
```
**Remove file from git but keep local:**
```bash
git rm --cached filename
```
### Verifying .gitignore Works
**Check if files are ignored:**
```bash
# Check specific file
git check-ignore -v temp_docs_and_tests/somefile.md
# Should output the .gitignore rule that matches
# Example: .gitignore:152:temp_docs_and_tests/
```
**List all tracked files (should NOT include temp):**
```bash
git ls-files | grep temp_docs_and_tests
# Should return nothing (0 lines)
```
**List all ignored files:**
```bash
git status --ignored
```
## Success Checklist
Before pushing to GitHub, ensure:
- [ ] Pre-commit hook is installed (`pre-commit install`)
- [ ] All tests pass locally (`pytest tests/ -x`)
- [ ] No linting errors (pre-commit runs on commit)
- [ ] Temp files are in .gitignore and not tracked
- [ ] Commit message is clear and descriptive
- [ ] Only relevant files are committed (not temp/logs)
- [ ] Changes are on the correct branch
## Example Session
### User: "GitHub CI is failing with test errors"
**Your Response:**
```
I'll help you fix the CI failures. Let me follow the systematic approach:
**Step 1: Activate pre-commit hooks**
```bash
pre-commit install
```
**Step 2: Run tests locally to identify failures**
```bash
python -m pytest tests/ -x --tb=short -q 2>&1 | tail -50
```
*[Wait for results and analyze the failure pattern]*
**Step 3: Fix the specific failure**
*[Apply pattern-based fix - e.g., add return_message=True for KeyError 'role']*
**Step 4: Verify the fix**
```bash
python -m pytest tests/test_file.py -xvs
```
**Step 5: Run full test suite**
```bash
python -m pytest tests/ -x --tb=short -q
```
**Step 6: Commit and push**
```bash
git add tests/test_file.py
git commit -m "Fix test: add return_message=True"
git push origin auto
```
All tests should now pass in CI! ✅
```
## Files Changed Today (Real Examples)
### Commit 1: fca22e2 - Fix test_task_manager.py
**Issue:** Mock not subscriptable
**Fix:**
- Create separate mock tool instances
- Add `_get_tool_instance` method
- Use `AsyncMock(side_effect=...)` for async tools
### Commit 2: 890cb11 - Fix test_tooluniverse_cache_integration.py
**Issue:** KeyError: 'role'
**Fix:**
- Add `return_message=True`
- Change `msg["role"]` to `msg.get("role")`
### Commit 3: f775c6f - Fix test_run_parameters.py
**Issue:** KeyError: 'role' in batch test
**Fix:**
- Add `return_message=True`
- Use `.get()` for safe access
### Commit 4: 1d9222a - Remove temp_docs_and_tests/
**Issue:** Temp folder being pushed to GitHub
**Fix:**
- `git rm -r --cached temp_docs_and_tests/`
- Commit removal (keeps local files)
## Memory Markers
**When you see these patterns:**
- `KeyError: 'role'` → Missing `return_message=True`
- `Mock object is not subscriptable` → Fix mock configuration
- `F841` or `E731` → Linting errors to fix
- User says "don't push temp folder" → Check `git ls-files` and remove from tracking
**Always remember:**
1. Pre-commit hook MUST be active
2. Test locally BEFORE pushing
3. Fix one test at a time
4. Verify full suite before pushing
5. Check that temp files aren't tracked
## End of Instructions
Follow this systematic approach every time there are CI failures or test issues. The patterns are proven to work - we fixed 40 tests today using these exact techniques!