feat: implement Syntax Guard as Gitea pre-receive hook
Add pre-receive hook to prevent merging code with Python syntax errors. Features: - Checks all Python files (.py) in each push using python -m py_compile - Special protection for critical files: - run_agent.py - model_tools.py - hermes-agent/tools/nexus_architect.py - cli.py, batch_runner.py, hermes_state.py - Clear error messages showing file and line number - Rejects pushes containing syntax errors Files added: - .githooks/pre-receive (Bash implementation) - .githooks/pre-receive.py (Python implementation) - docs/GITEA_SYNTAX_GUARD.md (installation guide) - .githooks/pre-commit (existing secret detection hook) Closes #82
This commit is contained in:
209
docs/GITEA_SYNTAX_GUARD.md
Normal file
209
docs/GITEA_SYNTAX_GUARD.md
Normal file
@@ -0,0 +1,209 @@
|
||||
# Gitea Syntax Guard - Pre-receive Hook
|
||||
|
||||
This document describes how to install and configure the Python Syntax Guard pre-receive hook in Gitea to prevent merging code with syntax errors.
|
||||
|
||||
## Overview
|
||||
|
||||
The Syntax Guard is a pre-receive hook that validates Python files for syntax errors before allowing pushes to the repository. It uses Python's built-in `py_compile` module to check files.
|
||||
|
||||
### Features
|
||||
|
||||
- **Automatic Syntax Checking**: Checks all Python files (.py) in each push
|
||||
- **Critical File Protection**: Special attention to essential files:
|
||||
- `run_agent.py` - Main agent runner
|
||||
- `model_tools.py` - Tool orchestration layer
|
||||
- `hermes-agent/tools/nexus_architect.py` - Nexus architect tool
|
||||
- `cli.py` - Command-line interface
|
||||
- `batch_runner.py` - Batch processing
|
||||
- `hermes_state.py` - State management
|
||||
- **Clear Error Messages**: Shows exact file and line number of syntax errors
|
||||
- **Push Rejection**: Blocks pushes containing syntax errors
|
||||
|
||||
## Installation Methods
|
||||
|
||||
### Method 1: Gitea Web Interface (Recommended)
|
||||
|
||||
1. Navigate to your repository in Gitea
|
||||
2. Go to **Settings** → **Git Hooks**
|
||||
3. Find the **pre-receive** hook and click **Edit**
|
||||
4. Copy the contents of `.githooks/pre-receive` (Bash version) or `.githooks/pre-receive.py` (Python version)
|
||||
5. Paste into the Gitea hook editor
|
||||
6. Click **Update Hook**
|
||||
|
||||
### Method 2: Server-Side Installation
|
||||
|
||||
If you have server access to the Gitea installation:
|
||||
|
||||
```bash
|
||||
# Locate the repository on the Gitea server
|
||||
# Usually in: /var/lib/gitea/repositories/<owner>/<repo>.git/hooks/
|
||||
|
||||
# Copy the hook
|
||||
cp /path/to/hermes-agent/.githooks/pre-receive \
|
||||
/var/lib/gitea/repositories/Timmy_Foundation/hermes-agent.git/hooks/pre-receive
|
||||
|
||||
# Make it executable
|
||||
chmod +x /var/lib/gitea/repositories/Timmy_Foundation/hermes-agent.git/hooks/pre-receive
|
||||
```
|
||||
|
||||
### Method 3: Repository-Level Git Hook (for local testing)
|
||||
|
||||
```bash
|
||||
# From the repository root
|
||||
cp .githooks/pre-receive .git/hooks/pre-receive
|
||||
chmod +x .git/hooks/pre-receive
|
||||
|
||||
# Or use the Python version
|
||||
cp .githooks/pre-receive.py .git/hooks/pre-receive
|
||||
chmod +x .git/hooks/pre-receive
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Customizing Critical Files
|
||||
|
||||
Edit the `CRITICAL_FILES` array in the hook to add or remove files:
|
||||
|
||||
**Bash version:**
|
||||
```bash
|
||||
CRITICAL_FILES=(
|
||||
"run_agent.py"
|
||||
"model_tools.py"
|
||||
"hermes-agent/tools/nexus_architect.py"
|
||||
# Add your files here
|
||||
)
|
||||
```
|
||||
|
||||
**Python version:**
|
||||
```python
|
||||
CRITICAL_FILES = [
|
||||
"run_agent.py",
|
||||
"model_tools.py",
|
||||
"hermes-agent/tools/nexus_architect.py",
|
||||
# Add your files here
|
||||
]
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
The hook respects the following environment variables:
|
||||
|
||||
- `PYTHON_CMD`: Path to Python executable (default: `python3`)
|
||||
- `SYNTAX_GUARD_STRICT`: Set to `1` to fail on warnings (default: `0`)
|
||||
|
||||
## Testing the Hook
|
||||
|
||||
### Local Testing
|
||||
|
||||
1. Create a test branch:
|
||||
```bash
|
||||
git checkout -b test/syntax-guard
|
||||
```
|
||||
|
||||
2. Create a file with intentional syntax error:
|
||||
```bash
|
||||
echo 'def broken_function(' > broken_test.py
|
||||
git add broken_test.py
|
||||
git commit -m "Test syntax error"
|
||||
```
|
||||
|
||||
3. Try to push (should be rejected):
|
||||
```bash
|
||||
git push origin test/syntax-guard
|
||||
```
|
||||
|
||||
4. You should see output like:
|
||||
```
|
||||
[ERROR] Syntax error in: broken_test.py
|
||||
File "broken_test.py", line 1
|
||||
def broken_function(
|
||||
^
|
||||
SyntaxError: unexpected EOF while parsing
|
||||
```
|
||||
|
||||
### Clean Up Test
|
||||
|
||||
```bash
|
||||
git checkout main
|
||||
git branch -D test/syntax-guard
|
||||
git push origin --delete test/syntax-guard # if it somehow got through
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Hook Not Running
|
||||
|
||||
1. Check hook permissions:
|
||||
```bash
|
||||
ls -la .git/hooks/pre-receive
|
||||
# Should show executable permission (-rwxr-xr-x)
|
||||
```
|
||||
|
||||
2. Verify Git hook path:
|
||||
```bash
|
||||
git config core.hooksPath
|
||||
# Should be .git/hooks or empty
|
||||
```
|
||||
|
||||
### Python Not Found
|
||||
|
||||
If Gitea reports "python3: command not found":
|
||||
|
||||
1. Check Python path on Gitea server:
|
||||
```bash
|
||||
which python3
|
||||
which python
|
||||
```
|
||||
|
||||
2. Update the hook to use the correct path:
|
||||
```bash
|
||||
# In the hook, change:
|
||||
python3 -m py_compile ...
|
||||
# To:
|
||||
/usr/bin/python3 -m py_compile ...
|
||||
```
|
||||
|
||||
### Bypassing the Hook (Emergency Only)
|
||||
|
||||
**⚠️ WARNING: Only use in emergencies with team approval!**
|
||||
|
||||
Administrators can bypass hooks by pushing with `--no-verify`, but this won't work for pre-receive hooks on the server. To temporarily disable:
|
||||
|
||||
1. Go to Gitea repository settings
|
||||
2. Disable the pre-receive hook
|
||||
3. Push your changes
|
||||
4. Re-enable the hook immediately
|
||||
|
||||
## How It Works
|
||||
|
||||
1. **Hook Invocation**: Git calls the pre-receive hook before accepting a push
|
||||
2. **File Discovery**: Hook reads changed files from stdin (Git provides refs)
|
||||
3. **Python Detection**: Filters for .py files only
|
||||
4. **Syntax Check**: Extracts each file and runs `python -m py_compile`
|
||||
5. **Error Reporting**: Collects all errors and displays them
|
||||
6. **Decision**: Exits with code 1 to reject or 0 to accept
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
- The hook only checks changed files, not the entire repository
|
||||
- Syntax checking is fast (typically <100ms per file)
|
||||
- Large pushes (100+ files) may take a few seconds
|
||||
|
||||
## Security Notes
|
||||
|
||||
- The hook runs on the Gitea server with the server's Python
|
||||
- No code is executed, only syntax-checked
|
||||
- Temporary files are created in a secure temp directory and cleaned up
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions:
|
||||
1. Check Gitea logs: `/var/log/gitea/gitea.log`
|
||||
2. Test the hook locally first
|
||||
3. Review the hook script for your specific environment
|
||||
|
||||
## Related Files
|
||||
|
||||
- `.githooks/pre-receive` - Bash implementation
|
||||
- `.githooks/pre-receive.py` - Python implementation
|
||||
- `.githooks/pre-commit` - Client-side secret detection hook
|
||||
Reference in New Issue
Block a user