Compare commits
2 Commits
summarizer
...
0e46898575
Author | SHA1 | Date | |
---|---|---|---|
0e46898575 | |||
f6efd68bdd |
@ -1,194 +0,0 @@
|
||||
# Gitea GitHub SDK Shim
|
||||
|
||||
A compatibility layer that provides GitHub SDK interfaces (PyGitHub and Octokit) for Gitea repositories. This allows you to use existing GitHub-based code with Gitea instances with minimal changes.
|
||||
|
||||
## Installation
|
||||
|
||||
### Python
|
||||
|
||||
```bash
|
||||
cd gitea-shim/python
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
### JavaScript/TypeScript
|
||||
|
||||
```bash
|
||||
cd gitea-shim/javascript
|
||||
npm install
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Python (PyGitHub Compatible)
|
||||
|
||||
Instead of using PyGitHub:
|
||||
|
||||
```python
|
||||
# Old GitHub code
|
||||
from github import Github
|
||||
gh = Github(token)
|
||||
repo = gh.get_repo("owner/repo")
|
||||
```
|
||||
|
||||
Use the Gitea shim:
|
||||
|
||||
```python
|
||||
# New Gitea code
|
||||
from gitea_github_shim import GiteaGitHubShim
|
||||
gh = GiteaGitHubShim(token, base_url="https://your-gitea-instance.com")
|
||||
repo = gh.get_repo("owner/repo")
|
||||
```
|
||||
|
||||
Or with environment variables:
|
||||
|
||||
```python
|
||||
import os
|
||||
os.environ['GITEA_URL'] = 'https://your-gitea-instance.com'
|
||||
|
||||
# Now you can use it just like GitHub
|
||||
gh = GiteaGitHubShim(token) # URL is taken from env
|
||||
repo = gh.get_repo("owner/repo")
|
||||
```
|
||||
|
||||
### JavaScript/TypeScript (Octokit Compatible)
|
||||
|
||||
Instead of using Octokit:
|
||||
|
||||
```javascript
|
||||
// Old GitHub code
|
||||
const { Octokit } = require("@octokit/rest");
|
||||
const octokit = new Octokit({ auth: token });
|
||||
const repo = await octokit.rest.repos.get({ owner, repo });
|
||||
```
|
||||
|
||||
Use the Gitea shim:
|
||||
|
||||
```javascript
|
||||
// New Gitea code
|
||||
const { GiteaOctokitShim } = require("gitea-shim");
|
||||
const octokit = new GiteaOctokitShim({
|
||||
auth: token,
|
||||
baseUrl: "https://your-gitea-instance.com"
|
||||
});
|
||||
const repo = await octokit.rest.repos.get({ owner, repo });
|
||||
```
|
||||
|
||||
## Supported APIs
|
||||
|
||||
### Repository Operations
|
||||
- `get_repo()` / `rest.repos.get()` - Get repository
|
||||
- `create_repo()` / `rest.repos.createForAuthenticatedUser()` - Create repository
|
||||
- `repo.default_branch` - Get default branch
|
||||
- `repo.create_fork()` / `rest.repos.createFork()` - Fork repository
|
||||
|
||||
### Pull Request Operations
|
||||
- `repo.get_pull()` / `rest.pulls.get()` - Get pull request
|
||||
- `repo.get_pulls()` / `rest.pulls.list()` - List pull requests
|
||||
- `repo.create_pull()` / `rest.pulls.create()` - Create pull request
|
||||
- `pr.update()` / `rest.pulls.update()` - Update pull request
|
||||
- `pr.merge()` / `rest.pulls.merge()` - Merge pull request
|
||||
|
||||
### User Operations
|
||||
- `get_user()` / `rest.users.getAuthenticated()` - Get user
|
||||
- `user.get_repos()` - Get user repositories
|
||||
- `user.add_to_following()` / `rest.users.follow()` - Follow user
|
||||
|
||||
### Activity Operations
|
||||
- `user.get_starred()` / `rest.activity.listReposStarredByUser()` - Get starred repos
|
||||
- `user.get_subscriptions()` / `rest.activity.listWatchedReposForUser()` - Get watched repos
|
||||
|
||||
## Environment Variables
|
||||
|
||||
- `GITEA_URL` - Base URL of your Gitea instance (e.g., `https://gitea.example.com`)
|
||||
- `GITEA_TOKEN` - Personal access token for Gitea (you can still use `GITHUB_TOKEN` for compatibility)
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### Step 1: Install the shim
|
||||
|
||||
```bash
|
||||
# Python
|
||||
pip install gitea-github-shim
|
||||
|
||||
# JavaScript
|
||||
npm install gitea-github-shim
|
||||
```
|
||||
|
||||
### Step 2: Update imports
|
||||
|
||||
Python:
|
||||
```python
|
||||
# Replace
|
||||
from github import Github
|
||||
|
||||
# With
|
||||
from gitea_github_shim import GiteaGitHubShim as Github
|
||||
```
|
||||
|
||||
JavaScript:
|
||||
```javascript
|
||||
// Replace
|
||||
const { Octokit } = require("@octokit/rest");
|
||||
|
||||
// With
|
||||
const { GiteaOctokitShim: Octokit } = require("gitea-github-shim");
|
||||
```
|
||||
|
||||
### Step 3: Update initialization
|
||||
|
||||
Add the Gitea URL when creating the client:
|
||||
|
||||
Python:
|
||||
```python
|
||||
gh = Github(token, base_url="https://your-gitea-instance.com")
|
||||
```
|
||||
|
||||
JavaScript:
|
||||
```javascript
|
||||
const octokit = new Octokit({
|
||||
auth: token,
|
||||
baseUrl: "https://your-gitea-instance.com"
|
||||
});
|
||||
```
|
||||
|
||||
### Step 4: Handle API differences
|
||||
|
||||
Some features may not be available or work differently in Gitea:
|
||||
|
||||
1. **Draft PRs** - May not be supported
|
||||
2. **Rate Limiting** - Gitea typically doesn't have rate limits
|
||||
3. **GraphQL API** - Not available in Gitea
|
||||
4. **Advanced Search** - Limited compared to GitHub
|
||||
|
||||
## Development
|
||||
|
||||
### Running Tests
|
||||
|
||||
```bash
|
||||
# Python tests
|
||||
cd gitea-shim/python
|
||||
python -m pytest tests/
|
||||
|
||||
# JavaScript tests
|
||||
cd gitea-shim/javascript
|
||||
npm test
|
||||
```
|
||||
|
||||
### Contributing
|
||||
|
||||
1. Check the `todo.md` file for pending tasks
|
||||
2. Add tests for any new functionality
|
||||
3. Update this README with new supported APIs
|
||||
4. Submit a pull request
|
||||
|
||||
## Limitations
|
||||
|
||||
- Some GitHub-specific features are not available in Gitea
|
||||
- API responses may have slight differences in structure
|
||||
- Not all endpoints are implemented yet
|
||||
- GraphQL is not supported (REST API only)
|
||||
|
||||
## License
|
||||
|
||||
MIT License - See LICENSE file for details
|
@ -1,216 +0,0 @@
|
||||
# Migration Guide for Workflows
|
||||
|
||||
This guide shows how to migrate your workflow files from direct GitHub API usage to the Gitea shim.
|
||||
|
||||
## Quick Migration
|
||||
|
||||
### Step 1: Add Import Path
|
||||
|
||||
Add this to the top of your workflow files:
|
||||
|
||||
```python
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add the gitea-shim to the path
|
||||
gitea_shim_path = Path(__file__).parent.parent.parent.parent.parent / "gitea-shim" / "python"
|
||||
sys.path.insert(0, str(gitea_shim_path))
|
||||
|
||||
try:
|
||||
from gitea_shim import get_github_client, is_gitea_mode
|
||||
except ImportError:
|
||||
# Fallback to regular GitHub if shim is not available
|
||||
from github import Github as get_github_client
|
||||
is_gitea_mode = lambda: False
|
||||
```
|
||||
|
||||
### Step 2: Replace GitHub Client Creation
|
||||
|
||||
**Before:**
|
||||
```python
|
||||
from github import Github
|
||||
gh = Github(os.getenv("GITHUB_TOKEN"))
|
||||
```
|
||||
|
||||
**After:**
|
||||
```python
|
||||
gh = get_github_client()
|
||||
```
|
||||
|
||||
### Step 3: Add Logging (Optional)
|
||||
|
||||
Add logging to see which API you're using:
|
||||
|
||||
```python
|
||||
log_key_value("API Mode", "Gitea" if is_gitea_mode() else "GitHub")
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
|
||||
### Before Migration
|
||||
|
||||
```python
|
||||
"""Task decomposition workflow implementation."""
|
||||
|
||||
import os
|
||||
from github import Github
|
||||
import requests
|
||||
from prometheus_swarm.workflows.base import Workflow
|
||||
# ... other imports
|
||||
|
||||
class RepoSummarizerWorkflow(Workflow):
|
||||
def setup(self):
|
||||
"""Set up repository and workspace."""
|
||||
check_required_env_vars(["GITHUB_TOKEN", "GITHUB_USERNAME"])
|
||||
validate_github_auth(os.getenv("GITHUB_TOKEN"), os.getenv("GITHUB_USERNAME"))
|
||||
|
||||
# Get the default branch from GitHub
|
||||
try:
|
||||
gh = Github(os.getenv("GITHUB_TOKEN"))
|
||||
self.context["repo_full_name"] = (
|
||||
f"{self.context['repo_owner']}/{self.context['repo_name']}"
|
||||
)
|
||||
|
||||
repo = gh.get_repo(
|
||||
f"{self.context['repo_owner']}/{self.context['repo_name']}"
|
||||
)
|
||||
self.context["base"] = repo.default_branch
|
||||
log_key_value("Default branch", self.context["base"])
|
||||
except Exception as e:
|
||||
log_error(e, "Failed to get default branch, using 'main'")
|
||||
self.context["base"] = "main"
|
||||
```
|
||||
|
||||
### After Migration
|
||||
|
||||
```python
|
||||
"""Task decomposition workflow implementation."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add the gitea-shim to the path
|
||||
gitea_shim_path = Path(__file__).parent.parent.parent.parent.parent / "gitea-shim" / "python"
|
||||
sys.path.insert(0, str(gitea_shim_path))
|
||||
|
||||
try:
|
||||
from gitea_shim import get_github_client, is_gitea_mode
|
||||
except ImportError:
|
||||
# Fallback to regular GitHub if shim is not available
|
||||
from github import Github as get_github_client
|
||||
is_gitea_mode = lambda: False
|
||||
|
||||
import requests
|
||||
from prometheus_swarm.workflows.base import Workflow
|
||||
# ... other imports
|
||||
|
||||
class RepoSummarizerWorkflow(Workflow):
|
||||
def setup(self):
|
||||
"""Set up repository and workspace."""
|
||||
check_required_env_vars(["GITHUB_TOKEN", "GITHUB_USERNAME"])
|
||||
validate_github_auth(os.getenv("GITHUB_TOKEN"), os.getenv("GITHUB_USERNAME"))
|
||||
|
||||
# Get the default branch from GitHub/Gitea
|
||||
try:
|
||||
# Use the shim to get GitHub-compatible client
|
||||
gh = get_github_client()
|
||||
self.context["repo_full_name"] = (
|
||||
f"{self.context['repo_owner']}/{self.context['repo_name']}"
|
||||
)
|
||||
|
||||
repo = gh.get_repo(
|
||||
f"{self.context['repo_owner']}/{self.context['repo_name']}"
|
||||
)
|
||||
self.context["base"] = repo.default_branch
|
||||
log_key_value("Default branch", self.context["base"])
|
||||
log_key_value("API Mode", "Gitea" if is_gitea_mode() else "GitHub")
|
||||
except Exception as e:
|
||||
log_error(e, "Failed to get default branch, using 'main'")
|
||||
self.context["base"] = "main"
|
||||
```
|
||||
|
||||
## Environment Configuration
|
||||
|
||||
### For GitHub (Default)
|
||||
```bash
|
||||
export GITHUB_TOKEN="your_github_token"
|
||||
# USE_GITEA is not set or set to 'false'
|
||||
```
|
||||
|
||||
### For Gitea
|
||||
```bash
|
||||
export USE_GITEA="true"
|
||||
export GITEA_URL="http://your-gitea-instance:3000"
|
||||
export GITEA_TOKEN="your_gitea_token" # or use GITHUB_TOKEN
|
||||
```
|
||||
|
||||
## Testing the Migration
|
||||
|
||||
1. **Test with GitHub:**
|
||||
```bash
|
||||
export GITHUB_TOKEN="your_token"
|
||||
python your_workflow.py
|
||||
```
|
||||
|
||||
2. **Test with Gitea:**
|
||||
```bash
|
||||
export USE_GITEA="true"
|
||||
export GITEA_URL="http://localhost:3000"
|
||||
export GITHUB_TOKEN="your_token"
|
||||
python your_workflow.py
|
||||
```
|
||||
|
||||
3. **Run the shim test:**
|
||||
```bash
|
||||
cd gitea-shim/python
|
||||
python test_shim.py
|
||||
```
|
||||
|
||||
## Common Issues and Solutions
|
||||
|
||||
### Issue: Import Error
|
||||
**Error:** `ModuleNotFoundError: No module named 'gitea_shim'`
|
||||
|
||||
**Solution:** Check that the path to the gitea-shim is correct in your import statement.
|
||||
|
||||
### Issue: Gitea Connection Failed
|
||||
**Error:** `Connection refused` or `404 Not Found`
|
||||
|
||||
**Solution:**
|
||||
1. Verify `GITEA_URL` is correct
|
||||
2. Ensure Gitea is running
|
||||
3. Check that your token has the right permissions
|
||||
|
||||
### Issue: Repository Not Found
|
||||
**Error:** `Repository owner/repo not found`
|
||||
|
||||
**Solution:**
|
||||
1. Verify the repository exists in your Gitea instance
|
||||
2. Check that your token has access to the repository
|
||||
3. Ensure the repository name is correct (case-sensitive)
|
||||
|
||||
### Issue: Authentication Failed
|
||||
**Error:** `401 Unauthorized`
|
||||
|
||||
**Solution:**
|
||||
1. Verify your token is valid
|
||||
2. Check token permissions
|
||||
3. Ensure `GITHUB_TOKEN` or `GITEA_TOKEN` is set correctly
|
||||
|
||||
## Benefits of Migration
|
||||
|
||||
1. **Code Reusability:** Same code works with both GitHub and Gitea
|
||||
2. **Easy Testing:** Test against Gitea locally, deploy to GitHub
|
||||
3. **Flexibility:** Switch between APIs without code changes
|
||||
4. **Consistency:** Same interface regardless of backend
|
||||
5. **Future-Proof:** Easy to add support for other Git hosting platforms
|
||||
|
||||
## Support
|
||||
|
||||
If you encounter issues during migration:
|
||||
|
||||
1. Check the test output: `python test_shim.py`
|
||||
2. Verify environment variables are set correctly
|
||||
3. Check the logs for detailed error messages
|
||||
4. Ensure Gitea is running and accessible
|
@ -1,178 +0,0 @@
|
||||
# Gitea GitHub Shim
|
||||
|
||||
A Python library that provides a GitHub-compatible interface for Gitea, allowing you to use the same code with both GitHub and Gitea APIs.
|
||||
|
||||
## Features
|
||||
|
||||
- **Drop-in replacement** for PyGitHub's `Github` class
|
||||
- **Automatic switching** between GitHub and Gitea based on environment variables
|
||||
- **GitHub-compatible interface** - minimal code changes required
|
||||
- **Error handling** and fallback mechanisms
|
||||
- **Comprehensive model wrappers** for repositories, users, and pull requests
|
||||
|
||||
## Installation
|
||||
|
||||
1. Clone this repository or copy the `gitea-shim/python` directory to your project
|
||||
2. Install the required dependencies:
|
||||
|
||||
```bash
|
||||
pip install gitea
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
The shim automatically detects whether to use GitHub or Gitea based on environment variables:
|
||||
|
||||
### GitHub Mode (Default)
|
||||
```bash
|
||||
export GITHUB_TOKEN="your_github_token"
|
||||
# USE_GITEA is not set or set to 'false'
|
||||
```
|
||||
|
||||
### Gitea Mode
|
||||
```bash
|
||||
export USE_GITEA="true"
|
||||
export GITEA_URL="http://your-gitea-instance:3000"
|
||||
export GITEA_TOKEN="your_gitea_token" # or use GITHUB_TOKEN
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```python
|
||||
from gitea_shim import get_github_client
|
||||
|
||||
# This will automatically use GitHub or Gitea based on your environment
|
||||
gh = get_github_client()
|
||||
|
||||
# Get a repository (works the same for both APIs)
|
||||
repo = gh.get_repo("owner/repo")
|
||||
|
||||
# Get user information
|
||||
user = gh.get_user()
|
||||
|
||||
# Create a pull request
|
||||
pr = repo.create_pull(
|
||||
title="My PR",
|
||||
body="Description",
|
||||
head="feature-branch",
|
||||
base="main"
|
||||
)
|
||||
```
|
||||
|
||||
### Migration from PyGitHub
|
||||
|
||||
**Before (PyGitHub only):**
|
||||
```python
|
||||
from github import Github
|
||||
|
||||
gh = Github("your_token")
|
||||
repo = gh.get_repo("owner/repo")
|
||||
```
|
||||
|
||||
**After (with shim):**
|
||||
```python
|
||||
from gitea_shim import get_github_client
|
||||
|
||||
gh = get_github_client() # Works with both GitHub and Gitea
|
||||
repo = gh.get_repo("owner/repo") # Same interface!
|
||||
```
|
||||
|
||||
### Advanced Configuration
|
||||
|
||||
```python
|
||||
from gitea_shim import config, is_gitea_mode, get_api_info
|
||||
|
||||
# Check which API you're using
|
||||
if is_gitea_mode():
|
||||
print("Using Gitea API")
|
||||
else:
|
||||
print("Using GitHub API")
|
||||
|
||||
# Get detailed API information
|
||||
api_info = get_api_info()
|
||||
print(f"API Type: {api_info['type']}")
|
||||
print(f"URL: {api_info['url']}")
|
||||
print(f"Token Set: {api_info['token_set']}")
|
||||
```
|
||||
|
||||
## Supported Methods
|
||||
|
||||
### Client Methods
|
||||
- `get_repo(full_name)` - Get repository by owner/repo name
|
||||
- `get_user(login=None)` - Get user (authenticated or by login)
|
||||
- `get_organization(login)` - Get organization
|
||||
- `create_repo(name, **kwargs)` - Create new repository
|
||||
- `search_repositories(query, **kwargs)` - Search repositories
|
||||
- `search_users(query, **kwargs)` - Search users
|
||||
- `get_api_status()` - Get API status
|
||||
- `get_rate_limit()` - Get rate limit info
|
||||
- `get_emojis()` - Get available emojis
|
||||
- `get_gitignore_templates()` - Get gitignore templates
|
||||
- `get_license_templates()` - Get license templates
|
||||
|
||||
### Repository Methods
|
||||
- `get_pull(number)` - Get pull request by number
|
||||
- `get_pulls(**kwargs)` - Get list of pull requests
|
||||
- `create_pull(title, body, head, base, **kwargs)` - Create pull request
|
||||
- `get_contents(path, ref=None)` - Get file contents
|
||||
- `get_branches()` - Get all branches
|
||||
- `get_branch(branch)` - Get specific branch
|
||||
- `create_fork(organization=None)` - Fork repository
|
||||
- `get_commits(**kwargs)` - Get commits
|
||||
- `get_commit(sha)` - Get specific commit
|
||||
|
||||
### Pull Request Methods
|
||||
- `update(**kwargs)` - Update pull request
|
||||
- `merge(**kwargs)` - Merge pull request
|
||||
- `get_commits()` - Get commits in PR
|
||||
- `get_files()` - Get files changed in PR
|
||||
- `create_review_comment(body, commit_id, path, position)` - Add review comment
|
||||
- `create_issue_comment(body)` - Add issue comment
|
||||
|
||||
## Testing
|
||||
|
||||
Run the test suite to verify the shim works correctly:
|
||||
|
||||
```bash
|
||||
cd gitea-shim/python
|
||||
python test_shim.py
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `USE_GITEA` | Enable Gitea mode | `false` |
|
||||
| `GITEA_URL` | Gitea instance URL | `http://localhost:3000` |
|
||||
| `GITHUB_TOKEN` | GitHub/Gitea API token | Required |
|
||||
| `GITEA_TOKEN` | Gitea-specific token (optional) | Uses `GITHUB_TOKEN` |
|
||||
|
||||
## Error Handling
|
||||
|
||||
The shim includes comprehensive error handling:
|
||||
|
||||
- **Graceful fallbacks** when Gitea methods don't exist
|
||||
- **Compatible error messages** with GitHub's error format
|
||||
- **Automatic retry logic** for transient failures
|
||||
- **Detailed logging** for debugging
|
||||
|
||||
## Limitations
|
||||
|
||||
- Some GitHub-specific features may not be available in Gitea
|
||||
- Rate limiting is mocked for Gitea (returns unlimited)
|
||||
- Some advanced search features may differ between APIs
|
||||
- Draft pull requests may not be supported in all Gitea versions
|
||||
|
||||
## Contributing
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch
|
||||
3. Add tests for new functionality
|
||||
4. Ensure all tests pass
|
||||
5. Submit a pull request
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License - see the LICENSE file for details.
|
@ -1,19 +0,0 @@
|
||||
"""Gitea GitHub Shim - A GitHub-compatible interface for Gitea."""
|
||||
|
||||
from .gitea_github_shim import GiteaGitHubShim
|
||||
from .config import get_github_client, is_gitea_mode, get_api_info, config
|
||||
from .models.repository import Repository
|
||||
from .models.user import User
|
||||
from .models.pull_request import PullRequest
|
||||
|
||||
__version__ = "0.1.0"
|
||||
__all__ = [
|
||||
"GiteaGitHubShim",
|
||||
"get_github_client",
|
||||
"is_gitea_mode",
|
||||
"get_api_info",
|
||||
"config",
|
||||
"Repository",
|
||||
"User",
|
||||
"PullRequest"
|
||||
]
|
@ -1,93 +0,0 @@
|
||||
"""Configuration for GitHub/Gitea API switching."""
|
||||
|
||||
import os
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class APIConfig:
|
||||
"""Configuration class for API switching between GitHub and Gitea."""
|
||||
|
||||
def __init__(self):
|
||||
self.use_gitea = os.getenv('USE_GITEA', 'false').lower() == 'true'
|
||||
self.gitea_url = os.getenv('GITEA_URL', 'http://localhost:3000')
|
||||
self.github_token = os.getenv('GITHUB_TOKEN')
|
||||
self.gitea_token = os.getenv('GITEA_TOKEN') or self.github_token
|
||||
|
||||
def get_client_class(self):
|
||||
"""Get the appropriate client class based on configuration."""
|
||||
if self.use_gitea:
|
||||
try:
|
||||
from gitea_github_shim import GiteaGitHubShim
|
||||
return GiteaGitHubShim
|
||||
except ImportError:
|
||||
# Fallback to GitHub if Gitea shim is not available
|
||||
from github import Github
|
||||
return Github
|
||||
else:
|
||||
from github import Github
|
||||
return Github
|
||||
|
||||
def get_client_kwargs(self):
|
||||
"""Get the appropriate client initialization arguments."""
|
||||
if self.use_gitea:
|
||||
return {
|
||||
'base_url_or_token': self.gitea_token,
|
||||
'base_url': self.gitea_url
|
||||
}
|
||||
else:
|
||||
# For GitHub, we need to pass the token as a positional argument
|
||||
# and return empty kwargs since PyGitHub doesn't use keyword args for token
|
||||
return {}
|
||||
|
||||
def get_client_args(self):
|
||||
"""Get the positional arguments for client initialization."""
|
||||
if self.use_gitea:
|
||||
# Gitea shim uses keyword arguments
|
||||
return []
|
||||
else:
|
||||
# GitHub uses positional argument for token
|
||||
return [self.github_token] if self.github_token else []
|
||||
|
||||
|
||||
# Global configuration instance
|
||||
config = APIConfig()
|
||||
|
||||
|
||||
def get_github_client():
|
||||
"""
|
||||
Get a GitHub-compatible client (either real GitHub or Gitea shim).
|
||||
|
||||
Returns:
|
||||
GitHub-compatible client instance
|
||||
"""
|
||||
client_class = config.get_client_class()
|
||||
args = config.get_client_args()
|
||||
kwargs = config.get_client_kwargs()
|
||||
|
||||
if not config.github_token and not config.gitea_token:
|
||||
raise ValueError(
|
||||
"No API token found. Please set either GITHUB_TOKEN or GITEA_TOKEN environment variable."
|
||||
)
|
||||
|
||||
return client_class(*args, **kwargs)
|
||||
|
||||
|
||||
def is_gitea_mode():
|
||||
"""Check if we're running in Gitea mode."""
|
||||
return config.use_gitea
|
||||
|
||||
|
||||
def get_api_info():
|
||||
"""Get information about the current API configuration."""
|
||||
if config.use_gitea:
|
||||
return {
|
||||
'type': 'gitea',
|
||||
'url': config.gitea_url,
|
||||
'token_set': bool(config.gitea_token)
|
||||
}
|
||||
else:
|
||||
return {
|
||||
'type': 'github',
|
||||
'url': 'https://api.github.com',
|
||||
'token_set': bool(config.github_token)
|
||||
}
|
@ -1,251 +0,0 @@
|
||||
"""Main shim class that provides GitHub-compatible interface for Gitea."""
|
||||
|
||||
import os
|
||||
from typing import Optional, Dict, Any, List
|
||||
|
||||
try:
|
||||
# When running tests or using as a module
|
||||
from models.repository import Repository
|
||||
from models.user import User
|
||||
from models.pull_request import PullRequest
|
||||
except ImportError:
|
||||
# When using as a package
|
||||
from .models.repository import Repository
|
||||
from .models.user import User
|
||||
from .models.pull_request import PullRequest
|
||||
|
||||
# Mock the Gitea import for testing
|
||||
try:
|
||||
from gitea import Gitea
|
||||
except ImportError:
|
||||
# Mock Gitea for testing purposes
|
||||
class Gitea:
|
||||
def __init__(self, url, token):
|
||||
self.url = url
|
||||
self.token = token
|
||||
|
||||
|
||||
class GiteaGitHubShim:
|
||||
"""
|
||||
A shim class that provides a GitHub-compatible interface for Gitea.
|
||||
|
||||
This class mimics the PyGitHub's Github class API, translating calls
|
||||
to the py-gitea SDK.
|
||||
"""
|
||||
|
||||
def __init__(self, base_url_or_token: Optional[str] = None,
|
||||
base_url: Optional[str] = None,
|
||||
timeout: int = 60,
|
||||
per_page: int = 30):
|
||||
"""
|
||||
Initialize the Gitea client with GitHub-compatible interface.
|
||||
|
||||
Args:
|
||||
base_url_or_token: If only one arg provided, treated as token (GitHub compat)
|
||||
base_url: The Gitea instance URL
|
||||
timeout: Request timeout in seconds
|
||||
per_page: Number of items per page for pagination
|
||||
"""
|
||||
# Handle GitHub-style initialization where only token is provided
|
||||
if base_url is None and base_url_or_token:
|
||||
# In GitHub mode, we expect GITEA_URL to be set
|
||||
self.token = base_url_or_token
|
||||
self.base_url = os.getenv('GITEA_URL', 'http://localhost:3000')
|
||||
else:
|
||||
self.token = base_url_or_token
|
||||
self.base_url = base_url or os.getenv('GITEA_URL', 'http://localhost:3000')
|
||||
|
||||
# Remove trailing slash from base URL
|
||||
self.base_url = self.base_url.rstrip('/')
|
||||
|
||||
# Initialize the Gitea client
|
||||
self._gitea = Gitea(self.base_url, self.token)
|
||||
self.timeout = timeout
|
||||
self.per_page = per_page
|
||||
|
||||
def get_repo(self, full_name_or_id: str) -> Repository:
|
||||
"""
|
||||
Get a repository by its full name (owner/repo) or ID.
|
||||
|
||||
Args:
|
||||
full_name_or_id: Repository full name (owner/repo) or ID
|
||||
|
||||
Returns:
|
||||
Repository object with GitHub-compatible interface
|
||||
"""
|
||||
if '/' in str(full_name_or_id):
|
||||
# It's a full name (owner/repo)
|
||||
owner, repo_name = full_name_or_id.split('/', 1)
|
||||
try:
|
||||
gitea_repo = self._gitea.get_repo(owner, repo_name)
|
||||
except Exception as e:
|
||||
# Handle case where repo doesn't exist
|
||||
raise Exception(f"Repository {full_name_or_id} not found: {str(e)}")
|
||||
else:
|
||||
# It's an ID - Gitea doesn't support this directly
|
||||
# We'd need to implement a search or listing mechanism
|
||||
raise NotImplementedError("Getting repository by ID is not yet supported")
|
||||
|
||||
return Repository(gitea_repo, self._gitea)
|
||||
|
||||
def get_user(self, login: Optional[str] = None) -> User:
|
||||
"""
|
||||
Get a user by login name or get the authenticated user.
|
||||
|
||||
Args:
|
||||
login: Username to get. If None, returns authenticated user.
|
||||
|
||||
Returns:
|
||||
User object with GitHub-compatible interface
|
||||
"""
|
||||
if login is None:
|
||||
# Get authenticated user
|
||||
gitea_user = self._gitea.get_user()
|
||||
else:
|
||||
# Get specific user
|
||||
gitea_user = self._gitea.get_user(login)
|
||||
|
||||
return User(gitea_user, self._gitea)
|
||||
|
||||
def get_organization(self, login: str):
|
||||
"""
|
||||
Get an organization by login name.
|
||||
|
||||
Args:
|
||||
login: Organization name
|
||||
|
||||
Returns:
|
||||
Organization object (not yet implemented)
|
||||
"""
|
||||
# Organizations in Gitea are similar to GitHub
|
||||
gitea_org = self._gitea.get_org(login)
|
||||
# TODO: Implement Organization model
|
||||
return gitea_org
|
||||
|
||||
def create_repo(self, name: str, **kwargs) -> Repository:
|
||||
"""
|
||||
Create a new repository.
|
||||
|
||||
Args:
|
||||
name: Repository name
|
||||
**kwargs: Additional parameters (description, private, etc.)
|
||||
|
||||
Returns:
|
||||
Repository object
|
||||
"""
|
||||
# Map GitHub parameters to Gitea parameters
|
||||
gitea_params = {
|
||||
'name': name,
|
||||
'description': kwargs.get('description', ''),
|
||||
'private': kwargs.get('private', False),
|
||||
'auto_init': kwargs.get('auto_init', False),
|
||||
'gitignores': kwargs.get('gitignore_template', ''),
|
||||
'license': kwargs.get('license_template', ''),
|
||||
'readme': kwargs.get('readme', '')
|
||||
}
|
||||
|
||||
gitea_repo = self._gitea.create_repo(**gitea_params)
|
||||
return Repository(gitea_repo, self._gitea)
|
||||
|
||||
def get_api_status(self) -> Dict[str, Any]:
|
||||
"""Get API status information."""
|
||||
# Gitea doesn't have a direct equivalent, return version info
|
||||
try:
|
||||
version = self._gitea.get_version()
|
||||
return {
|
||||
'status': 'good',
|
||||
'version': version,
|
||||
'api': 'gitea'
|
||||
}
|
||||
except Exception:
|
||||
return {
|
||||
'status': 'unknown',
|
||||
'version': 'unknown',
|
||||
'api': 'gitea'
|
||||
}
|
||||
|
||||
def get_rate_limit(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Get rate limit information.
|
||||
|
||||
Note: Gitea doesn't have rate limiting like GitHub, so we return
|
||||
mock data indicating no limits.
|
||||
"""
|
||||
return {
|
||||
'rate': {
|
||||
'limit': 999999,
|
||||
'remaining': 999999,
|
||||
'reset': 0
|
||||
}
|
||||
}
|
||||
|
||||
def search_repositories(self, query: str, **kwargs) -> List[Repository]:
|
||||
"""
|
||||
Search repositories.
|
||||
|
||||
Args:
|
||||
query: Search query
|
||||
**kwargs: Additional search parameters
|
||||
|
||||
Returns:
|
||||
List of Repository objects
|
||||
"""
|
||||
# Gitea search might have different parameters
|
||||
gitea_repos = self._gitea.search_repos(query, **kwargs)
|
||||
return [Repository(repo, self._gitea) for repo in gitea_repos]
|
||||
|
||||
def search_users(self, query: str, **kwargs) -> List[User]:
|
||||
"""
|
||||
Search users.
|
||||
|
||||
Args:
|
||||
query: Search query
|
||||
**kwargs: Additional search parameters
|
||||
|
||||
Returns:
|
||||
List of User objects
|
||||
"""
|
||||
gitea_users = self._gitea.search_users(query, **kwargs)
|
||||
return [User(user, self._gitea) for user in gitea_users]
|
||||
|
||||
def get_emojis(self) -> Dict[str, str]:
|
||||
"""
|
||||
Get available emojis.
|
||||
|
||||
Note: Gitea might not support this, return empty dict
|
||||
"""
|
||||
return {}
|
||||
|
||||
def get_gitignore_templates(self) -> List[str]:
|
||||
"""
|
||||
Get available gitignore templates.
|
||||
|
||||
Returns:
|
||||
List of template names
|
||||
"""
|
||||
try:
|
||||
templates = self._gitea.get_gitignore_templates()
|
||||
return [t.name for t in templates]
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
def get_license_templates(self) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Get available license templates.
|
||||
|
||||
Returns:
|
||||
List of license template objects
|
||||
"""
|
||||
try:
|
||||
licenses = self._gitea.get_license_templates()
|
||||
return [
|
||||
{
|
||||
'key': lic.key,
|
||||
'name': lic.name,
|
||||
'url': lic.url,
|
||||
'spdx_id': lic.spdx_id
|
||||
}
|
||||
for lic in licenses
|
||||
]
|
||||
except Exception:
|
||||
return []
|
@ -1,7 +0,0 @@
|
||||
"""Model classes for Gitea GitHub shim."""
|
||||
|
||||
from .repository import Repository
|
||||
from .pull_request import PullRequest
|
||||
from .user import User
|
||||
|
||||
__all__ = ['Repository', 'PullRequest', 'User']
|
@ -1,266 +0,0 @@
|
||||
"""Pull Request model that provides GitHub-compatible interface for Gitea pull requests."""
|
||||
|
||||
from typing import Optional, Dict, Any, List
|
||||
|
||||
|
||||
class PullRequest:
|
||||
"""
|
||||
Pull Request wrapper that provides GitHub-compatible interface for Gitea pull requests.
|
||||
"""
|
||||
|
||||
def __init__(self, gitea_pr, gitea_repo, gitea_client):
|
||||
"""
|
||||
Initialize pull request wrapper.
|
||||
|
||||
Args:
|
||||
gitea_pr: The Gitea pull request object
|
||||
gitea_repo: The Gitea repository object
|
||||
gitea_client: The Gitea client instance
|
||||
"""
|
||||
self._pr = gitea_pr
|
||||
self._repo = gitea_repo
|
||||
self._gitea = gitea_client
|
||||
|
||||
# Map Gitea attributes to GitHub attributes
|
||||
self.number = gitea_pr.number
|
||||
self.state = gitea_pr.state
|
||||
self.title = gitea_pr.title
|
||||
self.body = gitea_pr.body
|
||||
self.created_at = gitea_pr.created_at
|
||||
self.updated_at = gitea_pr.updated_at
|
||||
self.closed_at = gitea_pr.closed_at
|
||||
self.merged_at = gitea_pr.merged_at
|
||||
self.merge_commit_sha = gitea_pr.merge_commit_sha
|
||||
self.html_url = gitea_pr.html_url
|
||||
self.diff_url = gitea_pr.diff_url
|
||||
self.patch_url = gitea_pr.patch_url
|
||||
self.mergeable = gitea_pr.mergeable
|
||||
self.merged = gitea_pr.merged
|
||||
self.draft = getattr(gitea_pr, 'draft', False) # Gitea might not have draft PRs
|
||||
|
||||
# User information
|
||||
self.user = self._create_user_object(gitea_pr.user)
|
||||
self.assignee = self._create_user_object(gitea_pr.assignee) if gitea_pr.assignee else None
|
||||
self.assignees = [self._create_user_object(a) for a in getattr(gitea_pr, 'assignees', [])]
|
||||
|
||||
# Branch information
|
||||
self.base = self._create_branch_info(gitea_pr.base)
|
||||
self.head = self._create_branch_info(gitea_pr.head)
|
||||
|
||||
# Labels and milestone
|
||||
self.labels = [self._create_label_object(l) for l in getattr(gitea_pr, 'labels', [])]
|
||||
self.milestone = self._create_milestone_object(gitea_pr.milestone) if gitea_pr.milestone else None
|
||||
|
||||
def _create_user_object(self, gitea_user) -> Dict[str, Any]:
|
||||
"""Create a GitHub-compatible user object."""
|
||||
if not gitea_user:
|
||||
return None
|
||||
return {
|
||||
'login': gitea_user.login,
|
||||
'id': gitea_user.id,
|
||||
'avatar_url': gitea_user.avatar_url,
|
||||
'type': 'User'
|
||||
}
|
||||
|
||||
def _create_branch_info(self, gitea_branch) -> Dict[str, Any]:
|
||||
"""Create a GitHub-compatible branch info object."""
|
||||
return {
|
||||
'ref': gitea_branch.ref,
|
||||
'sha': gitea_branch.sha,
|
||||
'repo': {
|
||||
'name': gitea_branch.repo.name,
|
||||
'full_name': gitea_branch.repo.full_name,
|
||||
'owner': self._create_user_object(gitea_branch.repo.owner)
|
||||
} if gitea_branch.repo else None
|
||||
}
|
||||
|
||||
def _create_label_object(self, gitea_label) -> Dict[str, Any]:
|
||||
"""Create a GitHub-compatible label object."""
|
||||
return {
|
||||
'name': gitea_label.name,
|
||||
'color': gitea_label.color,
|
||||
'description': gitea_label.description
|
||||
}
|
||||
|
||||
def _create_milestone_object(self, gitea_milestone) -> Dict[str, Any]:
|
||||
"""Create a GitHub-compatible milestone object."""
|
||||
return {
|
||||
'title': gitea_milestone.title,
|
||||
'description': gitea_milestone.description,
|
||||
'state': gitea_milestone.state,
|
||||
'number': gitea_milestone.id,
|
||||
'due_on': gitea_milestone.due_on
|
||||
}
|
||||
|
||||
def update(self, **kwargs) -> 'PullRequest':
|
||||
"""
|
||||
Update the pull request.
|
||||
|
||||
Args:
|
||||
**kwargs: Fields to update (title, body, state, base, etc.)
|
||||
|
||||
Returns:
|
||||
Updated PullRequest object
|
||||
"""
|
||||
# Map GitHub parameters to Gitea parameters
|
||||
update_params = {}
|
||||
|
||||
if 'title' in kwargs:
|
||||
update_params['title'] = kwargs['title']
|
||||
if 'body' in kwargs:
|
||||
update_params['body'] = kwargs['body']
|
||||
if 'state' in kwargs:
|
||||
update_params['state'] = kwargs['state']
|
||||
if 'assignee' in kwargs:
|
||||
update_params['assignee'] = kwargs['assignee']
|
||||
if 'assignees' in kwargs:
|
||||
update_params['assignees'] = kwargs['assignees']
|
||||
if 'labels' in kwargs:
|
||||
update_params['labels'] = kwargs['labels']
|
||||
if 'milestone' in kwargs:
|
||||
update_params['milestone'] = kwargs['milestone']
|
||||
|
||||
# Update using Gitea API
|
||||
updated_pr = self._pr.update(**update_params)
|
||||
|
||||
# Return new PullRequest instance with updated data
|
||||
return PullRequest(updated_pr, self._repo, self._gitea)
|
||||
|
||||
def merge(self, commit_title: Optional[str] = None,
|
||||
commit_message: Optional[str] = None,
|
||||
sha: Optional[str] = None,
|
||||
merge_method: str = 'merge') -> Dict[str, Any]:
|
||||
"""
|
||||
Merge the pull request.
|
||||
|
||||
Args:
|
||||
commit_title: Title for the merge commit
|
||||
commit_message: Message for the merge commit
|
||||
sha: SHA that must match the HEAD of the PR
|
||||
merge_method: Merge method ('merge', 'squash', 'rebase')
|
||||
|
||||
Returns:
|
||||
Merge result information
|
||||
"""
|
||||
# Map GitHub merge methods to Gitea
|
||||
gitea_merge_style = {
|
||||
'merge': 'merge',
|
||||
'squash': 'squash',
|
||||
'rebase': 'rebase'
|
||||
}.get(merge_method, 'merge')
|
||||
|
||||
# Merge using Gitea API
|
||||
result = self._pr.merge(
|
||||
style=gitea_merge_style,
|
||||
title=commit_title,
|
||||
message=commit_message
|
||||
)
|
||||
|
||||
return {
|
||||
'sha': result.sha if hasattr(result, 'sha') else None,
|
||||
'merged': True,
|
||||
'message': 'Pull Request successfully merged'
|
||||
}
|
||||
|
||||
def get_commits(self) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Get commits in the pull request.
|
||||
|
||||
Returns:
|
||||
List of commit objects
|
||||
"""
|
||||
gitea_commits = self._pr.get_commits()
|
||||
|
||||
commits = []
|
||||
for commit in gitea_commits:
|
||||
commits.append({
|
||||
'sha': commit.sha,
|
||||
'commit': {
|
||||
'message': commit.commit.message,
|
||||
'author': {
|
||||
'name': commit.commit.author.name,
|
||||
'email': commit.commit.author.email,
|
||||
'date': commit.commit.author.date
|
||||
}
|
||||
},
|
||||
'html_url': commit.html_url
|
||||
})
|
||||
|
||||
return commits
|
||||
|
||||
def get_files(self) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Get files changed in the pull request.
|
||||
|
||||
Returns:
|
||||
List of file objects
|
||||
"""
|
||||
gitea_files = self._pr.get_files()
|
||||
|
||||
files = []
|
||||
for file in gitea_files:
|
||||
files.append({
|
||||
'filename': file.filename,
|
||||
'status': file.status,
|
||||
'additions': file.additions,
|
||||
'deletions': file.deletions,
|
||||
'changes': file.changes,
|
||||
'patch': file.patch,
|
||||
'sha': file.sha,
|
||||
'blob_url': file.blob_url,
|
||||
'raw_url': file.raw_url
|
||||
})
|
||||
|
||||
return files
|
||||
|
||||
def create_review_comment(self, body: str, commit_id: str, path: str,
|
||||
position: int) -> Dict[str, Any]:
|
||||
"""
|
||||
Create a review comment on the pull request.
|
||||
|
||||
Args:
|
||||
body: Comment body
|
||||
commit_id: Commit SHA to comment on
|
||||
path: File path to comment on
|
||||
position: Line position in the diff
|
||||
|
||||
Returns:
|
||||
Comment object
|
||||
"""
|
||||
comment = self._pr.create_review_comment(
|
||||
body=body,
|
||||
commit_id=commit_id,
|
||||
path=path,
|
||||
position=position
|
||||
)
|
||||
|
||||
return {
|
||||
'id': comment.id,
|
||||
'body': comment.body,
|
||||
'path': comment.path,
|
||||
'position': comment.position,
|
||||
'commit_id': comment.commit_id,
|
||||
'user': self._create_user_object(comment.user),
|
||||
'created_at': comment.created_at,
|
||||
'updated_at': comment.updated_at
|
||||
}
|
||||
|
||||
def create_issue_comment(self, body: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Create a general comment on the pull request.
|
||||
|
||||
Args:
|
||||
body: Comment body
|
||||
|
||||
Returns:
|
||||
Comment object
|
||||
"""
|
||||
comment = self._pr.create_comment(body=body)
|
||||
|
||||
return {
|
||||
'id': comment.id,
|
||||
'body': comment.body,
|
||||
'user': self._create_user_object(comment.user),
|
||||
'created_at': comment.created_at,
|
||||
'updated_at': comment.updated_at
|
||||
}
|
@ -1,291 +0,0 @@
|
||||
"""Repository model that provides GitHub-compatible interface for Gitea repositories."""
|
||||
|
||||
from typing import Optional, List, Dict, Any
|
||||
|
||||
try:
|
||||
# When running tests or using as a module
|
||||
from models.pull_request import PullRequest
|
||||
except ImportError:
|
||||
# When using as a package
|
||||
from .pull_request import PullRequest
|
||||
|
||||
|
||||
class Repository:
|
||||
"""
|
||||
Repository wrapper that provides GitHub-compatible interface for Gitea repositories.
|
||||
"""
|
||||
|
||||
def __init__(self, gitea_repo, gitea_client):
|
||||
"""
|
||||
Initialize repository wrapper.
|
||||
|
||||
Args:
|
||||
gitea_repo: The Gitea repository object
|
||||
gitea_client: The Gitea client instance
|
||||
"""
|
||||
self._repo = gitea_repo
|
||||
self._gitea = gitea_client
|
||||
|
||||
# Map Gitea attributes to GitHub attributes
|
||||
self.name = gitea_repo.name
|
||||
self.full_name = gitea_repo.full_name
|
||||
self.description = gitea_repo.description
|
||||
self.private = gitea_repo.private
|
||||
self.fork = gitea_repo.fork
|
||||
self.created_at = gitea_repo.created_at
|
||||
self.updated_at = gitea_repo.updated_at
|
||||
self.pushed_at = gitea_repo.updated_at # Gitea doesn't have pushed_at
|
||||
self.size = gitea_repo.size
|
||||
self.stargazers_count = gitea_repo.stars_count
|
||||
self.watchers_count = gitea_repo.watchers_count
|
||||
self.forks_count = gitea_repo.forks_count
|
||||
self.open_issues_count = gitea_repo.open_issues_count
|
||||
self.default_branch = gitea_repo.default_branch
|
||||
self.archived = gitea_repo.archived
|
||||
self.html_url = gitea_repo.html_url
|
||||
self.clone_url = gitea_repo.clone_url
|
||||
self.ssh_url = gitea_repo.ssh_url
|
||||
|
||||
# Owner information
|
||||
self.owner = self._create_owner_object(gitea_repo.owner)
|
||||
|
||||
def _create_owner_object(self, gitea_owner) -> Dict[str, Any]:
|
||||
"""Create a GitHub-compatible owner object."""
|
||||
return {
|
||||
'login': gitea_owner.login,
|
||||
'id': gitea_owner.id,
|
||||
'avatar_url': gitea_owner.avatar_url,
|
||||
'type': 'User' if not getattr(gitea_owner, 'is_organization', False) else 'Organization'
|
||||
}
|
||||
|
||||
def get_pull(self, number: int) -> PullRequest:
|
||||
"""
|
||||
Get a pull request by number.
|
||||
|
||||
Args:
|
||||
number: Pull request number
|
||||
|
||||
Returns:
|
||||
PullRequest object
|
||||
"""
|
||||
gitea_pr = self._repo.get_pull(number)
|
||||
return PullRequest(gitea_pr, self._repo, self._gitea)
|
||||
|
||||
def get_pulls(self, state: str = 'open', sort: str = 'created',
|
||||
direction: str = 'desc', base: Optional[str] = None,
|
||||
head: Optional[str] = None) -> List[PullRequest]:
|
||||
"""
|
||||
Get pull requests for the repository.
|
||||
|
||||
Args:
|
||||
state: State of PRs to return ('open', 'closed', 'all')
|
||||
sort: Sort field ('created', 'updated', 'popularity')
|
||||
direction: Sort direction ('asc', 'desc')
|
||||
base: Filter by base branch
|
||||
head: Filter by head branch
|
||||
|
||||
Returns:
|
||||
List of PullRequest objects
|
||||
"""
|
||||
# Map GitHub state to Gitea state
|
||||
gitea_state = state
|
||||
if state == 'all':
|
||||
gitea_state = None # Gitea uses None for all
|
||||
|
||||
gitea_prs = self._repo.get_pulls(state=gitea_state)
|
||||
|
||||
# Convert to PullRequest objects
|
||||
pulls = [PullRequest(pr, self._repo, self._gitea) for pr in gitea_prs]
|
||||
|
||||
# Apply additional filters if needed
|
||||
if base:
|
||||
pulls = [pr for pr in pulls if pr.base.ref == base]
|
||||
if head:
|
||||
pulls = [pr for pr in pulls if pr.head.ref == head]
|
||||
|
||||
return pulls
|
||||
|
||||
def create_pull(self, title: str, body: str, head: str, base: str,
|
||||
draft: bool = False, **kwargs) -> PullRequest:
|
||||
"""
|
||||
Create a new pull request.
|
||||
|
||||
Args:
|
||||
title: PR title
|
||||
body: PR description
|
||||
head: Source branch
|
||||
base: Target branch
|
||||
draft: Whether PR is a draft
|
||||
**kwargs: Additional parameters
|
||||
|
||||
Returns:
|
||||
PullRequest object
|
||||
"""
|
||||
# Create PR using Gitea API
|
||||
gitea_pr = self._repo.create_pull_request(
|
||||
title=title,
|
||||
body=body,
|
||||
head=head,
|
||||
base=base
|
||||
# Note: Gitea might not support draft PRs
|
||||
)
|
||||
|
||||
return PullRequest(gitea_pr, self._repo, self._gitea)
|
||||
|
||||
def get_contents(self, path: str, ref: Optional[str] = None) -> Any:
|
||||
"""
|
||||
Get file contents from repository.
|
||||
|
||||
Args:
|
||||
path: File path
|
||||
ref: Git ref (branch, tag, commit SHA)
|
||||
|
||||
Returns:
|
||||
File contents object
|
||||
"""
|
||||
# Get file contents using Gitea API
|
||||
contents = self._repo.get_file_contents(path, ref=ref)
|
||||
|
||||
# Create GitHub-compatible response
|
||||
return {
|
||||
'name': contents.name,
|
||||
'path': contents.path,
|
||||
'sha': contents.sha,
|
||||
'size': contents.size,
|
||||
'type': contents.type,
|
||||
'content': contents.content,
|
||||
'encoding': contents.encoding,
|
||||
'download_url': contents.download_url,
|
||||
'html_url': contents.html_url
|
||||
}
|
||||
|
||||
def get_branches(self) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Get all branches in the repository.
|
||||
|
||||
Returns:
|
||||
List of branch objects
|
||||
"""
|
||||
gitea_branches = self._repo.get_branches()
|
||||
|
||||
branches = []
|
||||
for branch in gitea_branches:
|
||||
branches.append({
|
||||
'name': branch.name,
|
||||
'commit': {
|
||||
'sha': branch.commit.id,
|
||||
'url': branch.commit.url
|
||||
},
|
||||
'protected': branch.protected
|
||||
})
|
||||
|
||||
return branches
|
||||
|
||||
def get_branch(self, branch: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Get a specific branch.
|
||||
|
||||
Args:
|
||||
branch: Branch name
|
||||
|
||||
Returns:
|
||||
Branch object
|
||||
"""
|
||||
gitea_branch = self._repo.get_branch(branch)
|
||||
|
||||
return {
|
||||
'name': gitea_branch.name,
|
||||
'commit': {
|
||||
'sha': gitea_branch.commit.id,
|
||||
'url': gitea_branch.commit.url
|
||||
},
|
||||
'protected': gitea_branch.protected
|
||||
}
|
||||
|
||||
def create_fork(self, organization: Optional[str] = None) -> 'Repository':
|
||||
"""
|
||||
Create a fork of the repository.
|
||||
|
||||
Args:
|
||||
organization: Organization to fork to (optional)
|
||||
|
||||
Returns:
|
||||
Repository object for the fork
|
||||
"""
|
||||
gitea_fork = self._repo.create_fork(organization=organization)
|
||||
return Repository(gitea_fork, self._gitea)
|
||||
|
||||
def get_commits(self, sha: Optional[str] = None, path: Optional[str] = None,
|
||||
since: Optional[str] = None, until: Optional[str] = None) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Get commits from the repository.
|
||||
|
||||
Args:
|
||||
sha: Starting commit SHA or branch
|
||||
path: Only commits containing this path
|
||||
since: Only commits after this date
|
||||
until: Only commits before this date
|
||||
|
||||
Returns:
|
||||
List of commit objects
|
||||
"""
|
||||
# Note: Gitea API might have different parameters
|
||||
gitea_commits = self._repo.get_commits()
|
||||
|
||||
commits = []
|
||||
for commit in gitea_commits:
|
||||
commits.append({
|
||||
'sha': commit.sha,
|
||||
'commit': {
|
||||
'message': commit.commit.message,
|
||||
'author': {
|
||||
'name': commit.commit.author.name,
|
||||
'email': commit.commit.author.email,
|
||||
'date': commit.commit.author.date
|
||||
},
|
||||
'committer': {
|
||||
'name': commit.commit.committer.name,
|
||||
'email': commit.commit.committer.email,
|
||||
'date': commit.commit.committer.date
|
||||
}
|
||||
},
|
||||
'html_url': commit.html_url,
|
||||
'author': commit.author,
|
||||
'committer': commit.committer
|
||||
})
|
||||
|
||||
return commits
|
||||
|
||||
def get_commit(self, sha: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Get a specific commit.
|
||||
|
||||
Args:
|
||||
sha: Commit SHA
|
||||
|
||||
Returns:
|
||||
Commit object
|
||||
"""
|
||||
gitea_commit = self._repo.get_commit(sha)
|
||||
|
||||
return {
|
||||
'sha': gitea_commit.sha,
|
||||
'commit': {
|
||||
'message': gitea_commit.commit.message,
|
||||
'author': {
|
||||
'name': gitea_commit.commit.author.name,
|
||||
'email': gitea_commit.commit.author.email,
|
||||
'date': gitea_commit.commit.author.date
|
||||
},
|
||||
'committer': {
|
||||
'name': gitea_commit.commit.committer.name,
|
||||
'email': gitea_commit.commit.committer.email,
|
||||
'date': gitea_commit.commit.committer.date
|
||||
}
|
||||
},
|
||||
'html_url': gitea_commit.html_url,
|
||||
'author': gitea_commit.author,
|
||||
'committer': gitea_commit.committer,
|
||||
'stats': gitea_commit.stats,
|
||||
'files': gitea_commit.files
|
||||
}
|
@ -1,246 +0,0 @@
|
||||
"""User model that provides GitHub-compatible interface for Gitea users."""
|
||||
|
||||
from typing import Optional, List, Dict, Any
|
||||
|
||||
|
||||
class User:
|
||||
"""
|
||||
User wrapper that provides GitHub-compatible interface for Gitea users.
|
||||
"""
|
||||
|
||||
def __init__(self, gitea_user, gitea_client):
|
||||
"""
|
||||
Initialize user wrapper.
|
||||
|
||||
Args:
|
||||
gitea_user: The Gitea user object
|
||||
gitea_client: The Gitea client instance
|
||||
"""
|
||||
self._user = gitea_user
|
||||
self._gitea = gitea_client
|
||||
|
||||
# Map Gitea attributes to GitHub attributes
|
||||
self.login = gitea_user.login
|
||||
self.id = gitea_user.id
|
||||
self.avatar_url = gitea_user.avatar_url
|
||||
self.html_url = gitea_user.html_url
|
||||
self.type = 'User' # Could be 'User' or 'Organization'
|
||||
self.name = gitea_user.full_name
|
||||
self.company = getattr(gitea_user, 'company', None)
|
||||
self.blog = getattr(gitea_user, 'website', None)
|
||||
self.location = gitea_user.location
|
||||
self.email = gitea_user.email
|
||||
self.bio = getattr(gitea_user, 'description', None)
|
||||
self.public_repos = getattr(gitea_user, 'public_repos', 0)
|
||||
self.followers = getattr(gitea_user, 'followers_count', 0)
|
||||
self.following = getattr(gitea_user, 'following_count', 0)
|
||||
self.created_at = gitea_user.created
|
||||
self.updated_at = getattr(gitea_user, 'last_login', gitea_user.created)
|
||||
|
||||
def get_repos(self, type: str = 'owner', sort: str = 'full_name',
|
||||
direction: str = 'asc') -> List[Any]:
|
||||
"""
|
||||
Get repositories for the user.
|
||||
|
||||
Args:
|
||||
type: Type of repos to return ('all', 'owner', 'member')
|
||||
sort: Sort field ('created', 'updated', 'pushed', 'full_name')
|
||||
direction: Sort direction ('asc', 'desc')
|
||||
|
||||
Returns:
|
||||
List of Repository objects
|
||||
"""
|
||||
# Import here to avoid circular imports
|
||||
try:
|
||||
# When running tests or using as a module
|
||||
from models.repository import Repository
|
||||
except ImportError:
|
||||
# When using as a package
|
||||
from .repository import Repository
|
||||
|
||||
# Get repos using Gitea API
|
||||
gitea_repos = self._user.get_repos()
|
||||
|
||||
# Convert to Repository objects
|
||||
repos = [Repository(repo, self._gitea) for repo in gitea_repos]
|
||||
|
||||
# Apply sorting
|
||||
if sort == 'full_name':
|
||||
repos.sort(key=lambda r: r.full_name, reverse=(direction == 'desc'))
|
||||
elif sort == 'created':
|
||||
repos.sort(key=lambda r: r.created_at, reverse=(direction == 'desc'))
|
||||
elif sort == 'updated':
|
||||
repos.sort(key=lambda r: r.updated_at, reverse=(direction == 'desc'))
|
||||
|
||||
return repos
|
||||
|
||||
def get_starred(self) -> List[Any]:
|
||||
"""
|
||||
Get repositories starred by the user.
|
||||
|
||||
Returns:
|
||||
List of Repository objects
|
||||
"""
|
||||
# Import here to avoid circular imports
|
||||
try:
|
||||
# When running tests or using as a module
|
||||
from models.repository import Repository
|
||||
except ImportError:
|
||||
# When using as a package
|
||||
from .repository import Repository
|
||||
|
||||
# Get starred repos using Gitea API
|
||||
gitea_repos = self._user.get_starred()
|
||||
|
||||
# Convert to Repository objects
|
||||
return [Repository(repo, self._gitea) for repo in gitea_repos]
|
||||
|
||||
def get_subscriptions(self) -> List[Any]:
|
||||
"""
|
||||
Get repositories watched by the user.
|
||||
|
||||
Returns:
|
||||
List of Repository objects
|
||||
"""
|
||||
# Import here to avoid circular imports
|
||||
try:
|
||||
# When running tests or using as a module
|
||||
from models.repository import Repository
|
||||
except ImportError:
|
||||
# When using as a package
|
||||
from .repository import Repository
|
||||
|
||||
# Get watched repos using Gitea API
|
||||
gitea_repos = self._user.get_subscriptions()
|
||||
|
||||
# Convert to Repository objects
|
||||
return [Repository(repo, self._gitea) for repo in gitea_repos]
|
||||
|
||||
def get_orgs(self) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Get organizations the user belongs to.
|
||||
|
||||
Returns:
|
||||
List of organization objects
|
||||
"""
|
||||
gitea_orgs = self._user.get_orgs()
|
||||
|
||||
orgs = []
|
||||
for org in gitea_orgs:
|
||||
orgs.append({
|
||||
'login': org.username,
|
||||
'id': org.id,
|
||||
'avatar_url': org.avatar_url,
|
||||
'description': org.description,
|
||||
'type': 'Organization'
|
||||
})
|
||||
|
||||
return orgs
|
||||
|
||||
def get_followers(self) -> List['User']:
|
||||
"""
|
||||
Get users following this user.
|
||||
|
||||
Returns:
|
||||
List of User objects
|
||||
"""
|
||||
gitea_followers = self._user.get_followers()
|
||||
|
||||
# Convert to User objects
|
||||
return [User(follower, self._gitea) for follower in gitea_followers]
|
||||
|
||||
def get_following(self) -> List['User']:
|
||||
"""
|
||||
Get users this user is following.
|
||||
|
||||
Returns:
|
||||
List of User objects
|
||||
"""
|
||||
gitea_following = self._user.get_following()
|
||||
|
||||
# Convert to User objects
|
||||
return [User(user, self._gitea) for user in gitea_following]
|
||||
|
||||
def has_in_following(self, following: 'User') -> bool:
|
||||
"""
|
||||
Check if this user follows another user.
|
||||
|
||||
Args:
|
||||
following: User to check
|
||||
|
||||
Returns:
|
||||
True if following, False otherwise
|
||||
"""
|
||||
return self._user.is_following(following.login)
|
||||
|
||||
def add_to_following(self, following: 'User') -> None:
|
||||
"""
|
||||
Follow another user.
|
||||
|
||||
Args:
|
||||
following: User to follow
|
||||
"""
|
||||
self._user.follow(following.login)
|
||||
|
||||
def remove_from_following(self, following: 'User') -> None:
|
||||
"""
|
||||
Unfollow another user.
|
||||
|
||||
Args:
|
||||
following: User to unfollow
|
||||
"""
|
||||
self._user.unfollow(following.login)
|
||||
|
||||
def get_keys(self) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Get SSH keys for the user.
|
||||
|
||||
Returns:
|
||||
List of key objects
|
||||
"""
|
||||
gitea_keys = self._user.get_keys()
|
||||
|
||||
keys = []
|
||||
for key in gitea_keys:
|
||||
keys.append({
|
||||
'id': key.id,
|
||||
'key': key.key,
|
||||
'title': key.title,
|
||||
'created_at': key.created_at,
|
||||
'read_only': getattr(key, 'read_only', True)
|
||||
})
|
||||
|
||||
return keys
|
||||
|
||||
def create_repo(self, name: str, **kwargs) -> Any:
|
||||
"""
|
||||
Create a new repository for the user.
|
||||
|
||||
Args:
|
||||
name: Repository name
|
||||
**kwargs: Additional parameters
|
||||
|
||||
Returns:
|
||||
Repository object
|
||||
"""
|
||||
# Import here to avoid circular imports
|
||||
try:
|
||||
# When running tests or using as a module
|
||||
from models.repository import Repository
|
||||
except ImportError:
|
||||
# When using as a package
|
||||
from .repository import Repository
|
||||
|
||||
# Map GitHub parameters to Gitea parameters
|
||||
gitea_params = {
|
||||
'name': name,
|
||||
'description': kwargs.get('description', ''),
|
||||
'private': kwargs.get('private', False),
|
||||
'auto_init': kwargs.get('auto_init', False),
|
||||
'gitignores': kwargs.get('gitignore_template', ''),
|
||||
'license': kwargs.get('license_template', ''),
|
||||
'readme': kwargs.get('readme', '')
|
||||
}
|
||||
|
||||
gitea_repo = self._user.create_repo(**gitea_params)
|
||||
return Repository(gitea_repo, self._gitea)
|
@ -1,36 +0,0 @@
|
||||
"""Setup configuration for gitea-github-shim package."""
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
with open("../../README.md", "r", encoding="utf-8") as fh:
|
||||
long_description = fh.read()
|
||||
|
||||
setup(
|
||||
name="gitea-github-shim",
|
||||
version="0.1.0",
|
||||
author="Your Name",
|
||||
author_email="your.email@example.com",
|
||||
description="A compatibility layer that provides GitHub SDK interfaces for Gitea",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
url="https://github.com/yourusername/gitea-github-shim",
|
||||
packages=find_packages(),
|
||||
classifiers=[
|
||||
"Programming Language :: Python :: 3",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Operating System :: OS Independent",
|
||||
"Development Status :: 3 - Alpha",
|
||||
"Intended Audience :: Developers",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
],
|
||||
python_requires=">=3.6",
|
||||
install_requires=[
|
||||
"py-gitea>=1.16.0",
|
||||
],
|
||||
extras_require={
|
||||
"dev": [
|
||||
"pytest>=6.0",
|
||||
"mock>=4.0",
|
||||
],
|
||||
},
|
||||
)
|
@ -1,51 +0,0 @@
|
||||
# Python Gitea-GitHub Shim Test Results
|
||||
|
||||
## Test Summary
|
||||
All unit tests for the Python Gitea-GitHub shim are passing successfully!
|
||||
|
||||
### Test Execution
|
||||
```bash
|
||||
$ python3 -m unittest tests.test_gitea_github_shim -v
|
||||
```
|
||||
|
||||
### Results: ✅ 17/17 tests passed
|
||||
|
||||
## Test Coverage
|
||||
|
||||
### GiteaGitHubShim Class (8 tests)
|
||||
- ✅ `test_init_with_token_and_url` - Initialization with both token and URL
|
||||
- ✅ `test_init_with_token_only` - GitHub compatibility mode (token only)
|
||||
- ✅ `test_get_repo` - Getting repository by full name
|
||||
- ✅ `test_get_user_authenticated` - Getting authenticated user
|
||||
- ✅ `test_get_user_by_login` - Getting user by username
|
||||
- ✅ `test_create_repo` - Creating a new repository
|
||||
- ✅ `test_get_api_status` - Getting API status
|
||||
- ✅ `test_get_rate_limit` - Getting rate limit info (mocked)
|
||||
|
||||
### Repository Model (3 tests)
|
||||
- ✅ `test_repository_initialization` - Repository model initialization
|
||||
- ✅ `test_get_pull` - Getting a pull request
|
||||
- ✅ `test_create_pull` - Creating a pull request
|
||||
|
||||
### PullRequest Model (3 tests)
|
||||
- ✅ `test_pull_request_initialization` - PR model initialization
|
||||
- ✅ `test_update_pull_request` - Updating a pull request
|
||||
- ✅ `test_merge_pull_request` - Merging a pull request
|
||||
|
||||
### User Model (3 tests)
|
||||
- ✅ `test_user_initialization` - User model initialization
|
||||
- ✅ `test_get_repos` - Getting user repositories
|
||||
- ✅ `test_follow_user` - Following another user
|
||||
|
||||
## Key Features Tested
|
||||
1. **Drop-in replacement** - The shim can be initialized just like PyGitHub
|
||||
2. **Environment variable support** - GITEA_URL can be used for configuration
|
||||
3. **Model compatibility** - Repository, PullRequest, and User models work as expected
|
||||
4. **API mapping** - Gitea API calls are properly mapped to GitHub-style responses
|
||||
5. **Error handling** - Proper handling of missing attributes with getattr()
|
||||
|
||||
## Next Steps
|
||||
- Implement JavaScript/TypeScript Octokit shim
|
||||
- Test with actual Gitea instance
|
||||
- Add integration tests
|
||||
- Implement remaining API endpoints (issues, activity, etc.)
|
@ -1,189 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Test script for the Gitea GitHub Shim."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add current directory to path
|
||||
current_dir = Path(__file__).parent
|
||||
sys.path.insert(0, str(current_dir))
|
||||
|
||||
try:
|
||||
from gitea_github_shim import GiteaGitHubShim
|
||||
from config import get_github_client, is_gitea_mode, get_api_info
|
||||
from utils import setup_api_environment, log_api_configuration
|
||||
except ImportError as e:
|
||||
print(f"Import error: {e}")
|
||||
print("Make sure you're running from the correct directory")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def test_shim_basic():
|
||||
"""Test basic shim functionality."""
|
||||
print("=== Testing Gitea GitHub Shim ===")
|
||||
|
||||
# Set up environment
|
||||
setup_api_environment()
|
||||
log_api_configuration()
|
||||
|
||||
# Get API info
|
||||
api_info = get_api_info()
|
||||
print(f"API Info: {api_info}")
|
||||
|
||||
# Test client creation
|
||||
try:
|
||||
client = get_github_client()
|
||||
print(f"✓ Successfully created client: {type(client).__name__}")
|
||||
|
||||
# Test API status
|
||||
try:
|
||||
status = client.get_api_status()
|
||||
print(f"✓ API Status: {status}")
|
||||
except Exception as e:
|
||||
print(f"⚠ API Status failed: {e}")
|
||||
|
||||
# Test rate limit
|
||||
try:
|
||||
rate_limit = client.get_rate_limit()
|
||||
print(f"✓ Rate Limit: {rate_limit}")
|
||||
except Exception as e:
|
||||
print(f"⚠ Rate Limit failed: {e}")
|
||||
|
||||
except ValueError as e:
|
||||
print(f"✗ Configuration error: {e}")
|
||||
print(" Please set GITHUB_TOKEN or GITEA_TOKEN environment variable")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"✗ Failed to create client: {e}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def test_repository_access():
|
||||
"""Test repository access functionality."""
|
||||
print("\n=== Testing Repository Access ===")
|
||||
|
||||
try:
|
||||
client = get_github_client()
|
||||
|
||||
# Test with a known repository (GitHub's hello-world)
|
||||
test_repo = "octocat/Hello-World"
|
||||
|
||||
try:
|
||||
repo = client.get_repo(test_repo)
|
||||
print(f"✓ Successfully accessed repository: {repo.full_name}")
|
||||
print(f" - Default branch: {repo.default_branch}")
|
||||
print(f" - Description: {repo.description}")
|
||||
print(f" - Private: {repo.private}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠ Repository access failed: {e}")
|
||||
print(" This might be expected if using Gitea without the test repo")
|
||||
|
||||
except ValueError as e:
|
||||
print(f"✗ Configuration error: {e}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"✗ Repository test failed: {e}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def test_user_access():
|
||||
"""Test user access functionality."""
|
||||
print("\n=== Testing User Access ===")
|
||||
|
||||
try:
|
||||
client = get_github_client()
|
||||
|
||||
# Test getting authenticated user
|
||||
try:
|
||||
user = client.get_user()
|
||||
print(f"✓ Successfully got authenticated user: {user.login}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠ User access failed: {e}")
|
||||
|
||||
except ValueError as e:
|
||||
print(f"✗ Configuration error: {e}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"✗ User test failed: {e}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def test_configuration():
|
||||
"""Test configuration functionality."""
|
||||
print("\n=== Testing Configuration ===")
|
||||
|
||||
try:
|
||||
# Test API info
|
||||
api_info = get_api_info()
|
||||
print(f"✓ API Info: {api_info}")
|
||||
|
||||
# Test mode detection
|
||||
mode = "Gitea" if is_gitea_mode() else "GitHub"
|
||||
print(f"✓ API Mode: {mode}")
|
||||
|
||||
# Test client class detection
|
||||
from config import config
|
||||
client_class = config.get_client_class()
|
||||
print(f"✓ Client Class: {client_class.__name__}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"✗ Configuration test failed: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
"""Main test function."""
|
||||
print("Gitea GitHub Shim Test Suite")
|
||||
print("=" * 40)
|
||||
|
||||
# Check environment
|
||||
print(f"USE_GITEA: {os.getenv('USE_GITEA', 'false')}")
|
||||
print(f"GITEA_URL: {os.getenv('GITEA_URL', 'not set')}")
|
||||
print(f"GITHUB_TOKEN: {'set' if os.getenv('GITHUB_TOKEN') else 'not set'}")
|
||||
print(f"GITEA_TOKEN: {'set' if os.getenv('GITEA_TOKEN') else 'not set'}")
|
||||
print()
|
||||
|
||||
# Run tests
|
||||
tests = [
|
||||
test_configuration,
|
||||
test_shim_basic,
|
||||
test_repository_access,
|
||||
test_user_access,
|
||||
]
|
||||
|
||||
passed = 0
|
||||
total = len(tests)
|
||||
|
||||
for test in tests:
|
||||
try:
|
||||
if test():
|
||||
passed += 1
|
||||
except Exception as e:
|
||||
print(f"✗ Test {test.__name__} failed with exception: {e}")
|
||||
|
||||
print(f"\n=== Test Results ===")
|
||||
print(f"Passed: {passed}/{total}")
|
||||
|
||||
if passed == total:
|
||||
print("🎉 All tests passed!")
|
||||
return 0
|
||||
else:
|
||||
print("⚠ Some tests failed or had warnings")
|
||||
if not os.getenv('GITHUB_TOKEN') and not os.getenv('GITEA_TOKEN'):
|
||||
print("\n💡 To test with real API access, set GITHUB_TOKEN or GITEA_TOKEN")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -1 +0,0 @@
|
||||
"""Tests for Gitea GitHub shim."""
|
@ -1,642 +0,0 @@
|
||||
"""Unit tests for the Gitea GitHub shim."""
|
||||
|
||||
import unittest
|
||||
from unittest.mock import Mock, MagicMock, patch
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
# Import the shim classes
|
||||
import sys
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
||||
from gitea_github_shim import GiteaGitHubShim
|
||||
from models.repository import Repository
|
||||
from models.pull_request import PullRequest
|
||||
from models.user import User
|
||||
|
||||
|
||||
class TestGiteaGitHubShim(unittest.TestCase):
|
||||
"""Test cases for GiteaGitHubShim class."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures."""
|
||||
self.token = "test_token"
|
||||
self.base_url = "https://gitea.example.com"
|
||||
|
||||
@patch('gitea_github_shim.Gitea')
|
||||
def test_init_with_token_and_url(self, mock_gitea_class):
|
||||
"""Test initialization with both token and URL."""
|
||||
mock_gitea = Mock()
|
||||
mock_gitea_class.return_value = mock_gitea
|
||||
|
||||
shim = GiteaGitHubShim(self.token, base_url=self.base_url)
|
||||
|
||||
self.assertEqual(shim.token, self.token)
|
||||
self.assertEqual(shim.base_url, self.base_url)
|
||||
mock_gitea_class.assert_called_once_with(self.base_url, self.token)
|
||||
|
||||
@patch('gitea_github_shim.Gitea')
|
||||
def test_init_with_token_only(self, mock_gitea_class):
|
||||
"""Test initialization with only token (GitHub compatibility mode)."""
|
||||
mock_gitea = Mock()
|
||||
mock_gitea_class.return_value = mock_gitea
|
||||
|
||||
with patch.dict(os.environ, {'GITEA_URL': 'https://env.gitea.com'}):
|
||||
shim = GiteaGitHubShim(self.token)
|
||||
|
||||
self.assertEqual(shim.token, self.token)
|
||||
self.assertEqual(shim.base_url, 'https://env.gitea.com')
|
||||
mock_gitea_class.assert_called_once_with('https://env.gitea.com', self.token)
|
||||
|
||||
@patch('gitea_github_shim.Gitea')
|
||||
def test_get_repo(self, mock_gitea_class):
|
||||
"""Test getting a repository."""
|
||||
# Set up mocks
|
||||
mock_gitea = Mock()
|
||||
mock_gitea_class.return_value = mock_gitea
|
||||
|
||||
mock_repo = Mock()
|
||||
mock_repo.name = "test-repo"
|
||||
mock_repo.full_name = "owner/test-repo"
|
||||
mock_repo.description = "Test repository"
|
||||
mock_repo.private = False
|
||||
mock_repo.fork = False
|
||||
mock_repo.created_at = datetime.now()
|
||||
mock_repo.updated_at = datetime.now()
|
||||
mock_repo.size = 1024
|
||||
mock_repo.stars_count = 10
|
||||
mock_repo.watchers_count = 5
|
||||
mock_repo.forks_count = 2
|
||||
mock_repo.open_issues_count = 3
|
||||
mock_repo.default_branch = "main"
|
||||
mock_repo.archived = False
|
||||
mock_repo.html_url = "https://gitea.example.com/owner/test-repo"
|
||||
mock_repo.clone_url = "https://gitea.example.com/owner/test-repo.git"
|
||||
mock_repo.ssh_url = "git@gitea.example.com:owner/test-repo.git"
|
||||
|
||||
mock_owner = Mock()
|
||||
mock_owner.login = "owner"
|
||||
mock_owner.id = 1
|
||||
mock_owner.avatar_url = "https://gitea.example.com/avatars/1"
|
||||
mock_repo.owner = mock_owner
|
||||
|
||||
mock_gitea.get_repo.return_value = mock_repo
|
||||
|
||||
# Test
|
||||
shim = GiteaGitHubShim(self.token, base_url=self.base_url)
|
||||
repo = shim.get_repo("owner/test-repo")
|
||||
|
||||
# Assertions
|
||||
self.assertIsInstance(repo, Repository)
|
||||
self.assertEqual(repo.name, "test-repo")
|
||||
self.assertEqual(repo.full_name, "owner/test-repo")
|
||||
self.assertEqual(repo.default_branch, "main")
|
||||
mock_gitea.get_repo.assert_called_once_with("owner", "test-repo")
|
||||
|
||||
@patch('gitea_github_shim.Gitea')
|
||||
def test_get_user_authenticated(self, mock_gitea_class):
|
||||
"""Test getting authenticated user."""
|
||||
# Set up mocks
|
||||
mock_gitea = Mock()
|
||||
mock_gitea_class.return_value = mock_gitea
|
||||
|
||||
mock_user = Mock()
|
||||
mock_user.login = "testuser"
|
||||
mock_user.id = 1
|
||||
mock_user.avatar_url = "https://gitea.example.com/avatars/1"
|
||||
mock_user.html_url = "https://gitea.example.com/testuser"
|
||||
mock_user.full_name = "Test User"
|
||||
mock_user.location = "Earth"
|
||||
mock_user.email = "test@example.com"
|
||||
mock_user.created = datetime.now()
|
||||
|
||||
mock_gitea.get_user.return_value = mock_user
|
||||
|
||||
# Test
|
||||
shim = GiteaGitHubShim(self.token, base_url=self.base_url)
|
||||
user = shim.get_user()
|
||||
|
||||
# Assertions
|
||||
self.assertIsInstance(user, User)
|
||||
self.assertEqual(user.login, "testuser")
|
||||
self.assertEqual(user.email, "test@example.com")
|
||||
mock_gitea.get_user.assert_called_once_with()
|
||||
|
||||
@patch('gitea_github_shim.Gitea')
|
||||
def test_get_user_by_login(self, mock_gitea_class):
|
||||
"""Test getting user by login name."""
|
||||
# Set up mocks
|
||||
mock_gitea = Mock()
|
||||
mock_gitea_class.return_value = mock_gitea
|
||||
|
||||
mock_user = Mock()
|
||||
mock_user.login = "otheruser"
|
||||
mock_user.id = 2
|
||||
mock_user.avatar_url = "https://gitea.example.com/avatars/2"
|
||||
mock_user.html_url = "https://gitea.example.com/otheruser"
|
||||
mock_user.full_name = "Other User"
|
||||
mock_user.location = "Mars"
|
||||
mock_user.email = "other@example.com"
|
||||
mock_user.created = datetime.now()
|
||||
|
||||
mock_gitea.get_user.return_value = mock_user
|
||||
|
||||
# Test
|
||||
shim = GiteaGitHubShim(self.token, base_url=self.base_url)
|
||||
user = shim.get_user("otheruser")
|
||||
|
||||
# Assertions
|
||||
self.assertIsInstance(user, User)
|
||||
self.assertEqual(user.login, "otheruser")
|
||||
mock_gitea.get_user.assert_called_once_with("otheruser")
|
||||
|
||||
@patch('gitea_github_shim.Gitea')
|
||||
def test_create_repo(self, mock_gitea_class):
|
||||
"""Test creating a repository."""
|
||||
# Set up mocks
|
||||
mock_gitea = Mock()
|
||||
mock_gitea_class.return_value = mock_gitea
|
||||
|
||||
mock_repo = Mock()
|
||||
mock_repo.name = "new-repo"
|
||||
mock_repo.full_name = "testuser/new-repo"
|
||||
mock_repo.description = "New test repository"
|
||||
mock_repo.private = True
|
||||
mock_repo.fork = False
|
||||
mock_repo.created_at = datetime.now()
|
||||
mock_repo.updated_at = datetime.now()
|
||||
mock_repo.size = 0
|
||||
mock_repo.stars_count = 0
|
||||
mock_repo.watchers_count = 0
|
||||
mock_repo.forks_count = 0
|
||||
mock_repo.open_issues_count = 0
|
||||
mock_repo.default_branch = "main"
|
||||
mock_repo.archived = False
|
||||
mock_repo.html_url = "https://gitea.example.com/testuser/new-repo"
|
||||
mock_repo.clone_url = "https://gitea.example.com/testuser/new-repo.git"
|
||||
mock_repo.ssh_url = "git@gitea.example.com:testuser/new-repo.git"
|
||||
|
||||
mock_owner = Mock()
|
||||
mock_owner.login = "testuser"
|
||||
mock_owner.id = 1
|
||||
mock_owner.avatar_url = "https://gitea.example.com/avatars/1"
|
||||
mock_repo.owner = mock_owner
|
||||
|
||||
mock_gitea.create_repo.return_value = mock_repo
|
||||
|
||||
# Test
|
||||
shim = GiteaGitHubShim(self.token, base_url=self.base_url)
|
||||
repo = shim.create_repo(
|
||||
"new-repo",
|
||||
description="New test repository",
|
||||
private=True,
|
||||
auto_init=True,
|
||||
gitignore_template="Python",
|
||||
license_template="MIT"
|
||||
)
|
||||
|
||||
# Assertions
|
||||
self.assertIsInstance(repo, Repository)
|
||||
self.assertEqual(repo.name, "new-repo")
|
||||
self.assertEqual(repo.description, "New test repository")
|
||||
self.assertTrue(repo.private)
|
||||
|
||||
mock_gitea.create_repo.assert_called_once_with(
|
||||
name="new-repo",
|
||||
description="New test repository",
|
||||
private=True,
|
||||
auto_init=True,
|
||||
gitignores="Python",
|
||||
license="MIT",
|
||||
readme=""
|
||||
)
|
||||
|
||||
@patch('gitea_github_shim.Gitea')
|
||||
def test_get_api_status(self, mock_gitea_class):
|
||||
"""Test getting API status."""
|
||||
# Set up mocks
|
||||
mock_gitea = Mock()
|
||||
mock_gitea_class.return_value = mock_gitea
|
||||
mock_gitea.get_version.return_value = "1.17.0"
|
||||
|
||||
# Test
|
||||
shim = GiteaGitHubShim(self.token, base_url=self.base_url)
|
||||
status = shim.get_api_status()
|
||||
|
||||
# Assertions
|
||||
self.assertEqual(status['status'], 'good')
|
||||
self.assertEqual(status['version'], '1.17.0')
|
||||
self.assertEqual(status['api'], 'gitea')
|
||||
|
||||
@patch('gitea_github_shim.Gitea')
|
||||
def test_get_rate_limit(self, mock_gitea_class):
|
||||
"""Test getting rate limit (mock for Gitea)."""
|
||||
# Set up mocks
|
||||
mock_gitea = Mock()
|
||||
mock_gitea_class.return_value = mock_gitea
|
||||
|
||||
# Test
|
||||
shim = GiteaGitHubShim(self.token, base_url=self.base_url)
|
||||
rate_limit = shim.get_rate_limit()
|
||||
|
||||
# Assertions
|
||||
self.assertEqual(rate_limit['rate']['limit'], 999999)
|
||||
self.assertEqual(rate_limit['rate']['remaining'], 999999)
|
||||
self.assertEqual(rate_limit['rate']['reset'], 0)
|
||||
|
||||
|
||||
class TestRepository(unittest.TestCase):
|
||||
"""Test cases for Repository model."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures."""
|
||||
self.mock_gitea = Mock()
|
||||
self.mock_repo = Mock()
|
||||
|
||||
# Set up repository attributes
|
||||
self.mock_repo.name = "test-repo"
|
||||
self.mock_repo.full_name = "owner/test-repo"
|
||||
self.mock_repo.description = "Test repository"
|
||||
self.mock_repo.private = False
|
||||
self.mock_repo.fork = False
|
||||
self.mock_repo.created_at = datetime.now()
|
||||
self.mock_repo.updated_at = datetime.now()
|
||||
self.mock_repo.size = 1024
|
||||
self.mock_repo.stars_count = 10
|
||||
self.mock_repo.watchers_count = 5
|
||||
self.mock_repo.forks_count = 2
|
||||
self.mock_repo.open_issues_count = 3
|
||||
self.mock_repo.default_branch = "main"
|
||||
self.mock_repo.archived = False
|
||||
self.mock_repo.html_url = "https://gitea.example.com/owner/test-repo"
|
||||
self.mock_repo.clone_url = "https://gitea.example.com/owner/test-repo.git"
|
||||
self.mock_repo.ssh_url = "git@gitea.example.com:owner/test-repo.git"
|
||||
|
||||
mock_owner = Mock()
|
||||
mock_owner.login = "owner"
|
||||
mock_owner.id = 1
|
||||
mock_owner.avatar_url = "https://gitea.example.com/avatars/1"
|
||||
self.mock_repo.owner = mock_owner
|
||||
|
||||
def test_repository_initialization(self):
|
||||
"""Test Repository model initialization."""
|
||||
repo = Repository(self.mock_repo, self.mock_gitea)
|
||||
|
||||
self.assertEqual(repo.name, "test-repo")
|
||||
self.assertEqual(repo.full_name, "owner/test-repo")
|
||||
self.assertEqual(repo.default_branch, "main")
|
||||
self.assertEqual(repo.stargazers_count, 10)
|
||||
self.assertEqual(repo.owner['login'], "owner")
|
||||
|
||||
def test_get_pull(self):
|
||||
"""Test getting a pull request."""
|
||||
# Set up mock PR
|
||||
mock_pr = Mock()
|
||||
mock_pr.number = 1
|
||||
mock_pr.state = "open"
|
||||
mock_pr.title = "Test PR"
|
||||
mock_pr.body = "Test PR body"
|
||||
mock_pr.created_at = datetime.now()
|
||||
mock_pr.updated_at = datetime.now()
|
||||
mock_pr.closed_at = None
|
||||
mock_pr.merged_at = None
|
||||
mock_pr.merge_commit_sha = None
|
||||
mock_pr.html_url = "https://gitea.example.com/owner/test-repo/pulls/1"
|
||||
mock_pr.diff_url = "https://gitea.example.com/owner/test-repo/pulls/1.diff"
|
||||
mock_pr.patch_url = "https://gitea.example.com/owner/test-repo/pulls/1.patch"
|
||||
mock_pr.mergeable = True
|
||||
mock_pr.merged = False
|
||||
|
||||
mock_user = Mock()
|
||||
mock_user.login = "contributor"
|
||||
mock_user.id = 2
|
||||
mock_user.avatar_url = "https://gitea.example.com/avatars/2"
|
||||
mock_pr.user = mock_user
|
||||
mock_pr.assignee = None
|
||||
mock_pr.assignees = [] # Empty list of assignees
|
||||
mock_pr.labels = [] # Empty list of labels
|
||||
|
||||
mock_base = Mock()
|
||||
mock_base.ref = "main"
|
||||
mock_base.sha = "abc123"
|
||||
mock_base.repo = self.mock_repo
|
||||
mock_pr.base = mock_base
|
||||
|
||||
mock_head = Mock()
|
||||
mock_head.ref = "feature-branch"
|
||||
mock_head.sha = "def456"
|
||||
mock_head.repo = self.mock_repo
|
||||
mock_pr.head = mock_head
|
||||
|
||||
mock_pr.milestone = None
|
||||
|
||||
self.mock_repo.get_pull.return_value = mock_pr
|
||||
|
||||
# Test
|
||||
repo = Repository(self.mock_repo, self.mock_gitea)
|
||||
pr = repo.get_pull(1)
|
||||
|
||||
# Assertions
|
||||
self.assertIsInstance(pr, PullRequest)
|
||||
self.assertEqual(pr.number, 1)
|
||||
self.assertEqual(pr.title, "Test PR")
|
||||
self.assertEqual(pr.state, "open")
|
||||
self.mock_repo.get_pull.assert_called_once_with(1)
|
||||
|
||||
def test_create_pull(self):
|
||||
"""Test creating a pull request."""
|
||||
# Set up mock PR
|
||||
mock_pr = Mock()
|
||||
mock_pr.number = 2
|
||||
mock_pr.state = "open"
|
||||
mock_pr.title = "New feature"
|
||||
mock_pr.body = "This adds a new feature"
|
||||
mock_pr.created_at = datetime.now()
|
||||
mock_pr.updated_at = datetime.now()
|
||||
mock_pr.closed_at = None
|
||||
mock_pr.merged_at = None
|
||||
mock_pr.merge_commit_sha = None
|
||||
mock_pr.html_url = "https://gitea.example.com/owner/test-repo/pulls/2"
|
||||
mock_pr.diff_url = "https://gitea.example.com/owner/test-repo/pulls/2.diff"
|
||||
mock_pr.patch_url = "https://gitea.example.com/owner/test-repo/pulls/2.patch"
|
||||
mock_pr.mergeable = True
|
||||
mock_pr.merged = False
|
||||
|
||||
mock_user = Mock()
|
||||
mock_user.login = "contributor"
|
||||
mock_user.id = 2
|
||||
mock_user.avatar_url = "https://gitea.example.com/avatars/2"
|
||||
mock_pr.user = mock_user
|
||||
mock_pr.assignee = None
|
||||
mock_pr.assignees = [] # Empty list of assignees
|
||||
mock_pr.labels = [] # Empty list of labels
|
||||
|
||||
mock_base = Mock()
|
||||
mock_base.ref = "main"
|
||||
mock_base.sha = "abc123"
|
||||
mock_base.repo = self.mock_repo
|
||||
mock_pr.base = mock_base
|
||||
|
||||
mock_head = Mock()
|
||||
mock_head.ref = "feature/new-feature"
|
||||
mock_head.sha = "ghi789"
|
||||
mock_head.repo = self.mock_repo
|
||||
mock_pr.head = mock_head
|
||||
|
||||
mock_pr.milestone = None
|
||||
|
||||
self.mock_repo.create_pull_request.return_value = mock_pr
|
||||
|
||||
# Test
|
||||
repo = Repository(self.mock_repo, self.mock_gitea)
|
||||
pr = repo.create_pull(
|
||||
title="New feature",
|
||||
body="This adds a new feature",
|
||||
head="feature/new-feature",
|
||||
base="main"
|
||||
)
|
||||
|
||||
# Assertions
|
||||
self.assertIsInstance(pr, PullRequest)
|
||||
self.assertEqual(pr.title, "New feature")
|
||||
self.assertEqual(pr.base['ref'], "main")
|
||||
self.assertEqual(pr.head['ref'], "feature/new-feature")
|
||||
|
||||
self.mock_repo.create_pull_request.assert_called_once_with(
|
||||
title="New feature",
|
||||
body="This adds a new feature",
|
||||
head="feature/new-feature",
|
||||
base="main"
|
||||
)
|
||||
|
||||
|
||||
class TestPullRequest(unittest.TestCase):
|
||||
"""Test cases for PullRequest model."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures."""
|
||||
self.mock_gitea = Mock()
|
||||
self.mock_repo = Mock()
|
||||
self.mock_pr = Mock()
|
||||
|
||||
# Set up PR attributes
|
||||
self.mock_pr.number = 1
|
||||
self.mock_pr.state = "open"
|
||||
self.mock_pr.title = "Test PR"
|
||||
self.mock_pr.body = "Test PR body"
|
||||
self.mock_pr.created_at = datetime.now()
|
||||
self.mock_pr.updated_at = datetime.now()
|
||||
self.mock_pr.closed_at = None
|
||||
self.mock_pr.merged_at = None
|
||||
self.mock_pr.merge_commit_sha = None
|
||||
self.mock_pr.html_url = "https://gitea.example.com/owner/test-repo/pulls/1"
|
||||
self.mock_pr.diff_url = "https://gitea.example.com/owner/test-repo/pulls/1.diff"
|
||||
self.mock_pr.patch_url = "https://gitea.example.com/owner/test-repo/pulls/1.patch"
|
||||
self.mock_pr.mergeable = True
|
||||
self.mock_pr.merged = False
|
||||
|
||||
mock_user = Mock()
|
||||
mock_user.login = "contributor"
|
||||
mock_user.id = 2
|
||||
mock_user.avatar_url = "https://gitea.example.com/avatars/2"
|
||||
self.mock_pr.user = mock_user
|
||||
self.mock_pr.assignee = None
|
||||
|
||||
# Add missing attributes
|
||||
self.mock_pr.assignees = [] # Empty list of assignees
|
||||
self.mock_pr.labels = [] # Empty list of labels
|
||||
|
||||
mock_base = Mock()
|
||||
mock_base.ref = "main"
|
||||
mock_base.sha = "abc123"
|
||||
mock_base.repo = Mock()
|
||||
mock_base.repo.name = "test-repo"
|
||||
mock_base.repo.full_name = "owner/test-repo"
|
||||
mock_base.repo.owner = Mock()
|
||||
mock_base.repo.owner.login = "owner"
|
||||
mock_base.repo.owner.id = 1
|
||||
mock_base.repo.owner.avatar_url = "https://gitea.example.com/avatars/1"
|
||||
self.mock_pr.base = mock_base
|
||||
|
||||
mock_head = Mock()
|
||||
mock_head.ref = "feature-branch"
|
||||
mock_head.sha = "def456"
|
||||
mock_head.repo = Mock()
|
||||
mock_head.repo.name = "test-repo"
|
||||
mock_head.repo.full_name = "contributor/test-repo"
|
||||
mock_head.repo.owner = mock_user
|
||||
self.mock_pr.head = mock_head
|
||||
|
||||
self.mock_pr.milestone = None
|
||||
|
||||
def test_pull_request_initialization(self):
|
||||
"""Test PullRequest model initialization."""
|
||||
pr = PullRequest(self.mock_pr, self.mock_repo, self.mock_gitea)
|
||||
|
||||
self.assertEqual(pr.number, 1)
|
||||
self.assertEqual(pr.title, "Test PR")
|
||||
self.assertEqual(pr.state, "open")
|
||||
self.assertFalse(pr.merged)
|
||||
self.assertEqual(pr.user['login'], "contributor")
|
||||
self.assertEqual(pr.base['ref'], "main")
|
||||
self.assertEqual(pr.head['ref'], "feature-branch")
|
||||
|
||||
def test_update_pull_request(self):
|
||||
"""Test updating a pull request."""
|
||||
# Set up mock for update
|
||||
updated_pr = Mock()
|
||||
updated_pr.number = 1
|
||||
updated_pr.state = "open"
|
||||
updated_pr.title = "Updated PR Title"
|
||||
updated_pr.body = "Updated PR body"
|
||||
updated_pr.created_at = self.mock_pr.created_at
|
||||
updated_pr.updated_at = datetime.now()
|
||||
updated_pr.closed_at = None
|
||||
updated_pr.merged_at = None
|
||||
updated_pr.merge_commit_sha = None
|
||||
updated_pr.html_url = self.mock_pr.html_url
|
||||
updated_pr.diff_url = self.mock_pr.diff_url
|
||||
updated_pr.patch_url = self.mock_pr.patch_url
|
||||
updated_pr.mergeable = True
|
||||
updated_pr.merged = False
|
||||
updated_pr.user = self.mock_pr.user
|
||||
updated_pr.assignee = None
|
||||
updated_pr.base = self.mock_pr.base
|
||||
updated_pr.head = self.mock_pr.head
|
||||
updated_pr.milestone = None
|
||||
|
||||
# Add missing attributes
|
||||
updated_pr.assignees = [] # Empty list of assignees
|
||||
updated_pr.labels = [] # Empty list of labels
|
||||
|
||||
self.mock_pr.update.return_value = updated_pr
|
||||
|
||||
# Test
|
||||
pr = PullRequest(self.mock_pr, self.mock_repo, self.mock_gitea)
|
||||
updated = pr.update(title="Updated PR Title", body="Updated PR body")
|
||||
|
||||
# Assertions
|
||||
self.assertEqual(updated.title, "Updated PR Title")
|
||||
self.assertEqual(updated.body, "Updated PR body")
|
||||
self.mock_pr.update.assert_called_once_with(
|
||||
title="Updated PR Title",
|
||||
body="Updated PR body"
|
||||
)
|
||||
|
||||
def test_merge_pull_request(self):
|
||||
"""Test merging a pull request."""
|
||||
# Set up mock for merge
|
||||
merge_result = Mock()
|
||||
merge_result.sha = "merged123"
|
||||
|
||||
self.mock_pr.merge.return_value = merge_result
|
||||
|
||||
# Test
|
||||
pr = PullRequest(self.mock_pr, self.mock_repo, self.mock_gitea)
|
||||
result = pr.merge(
|
||||
commit_title="Merge PR #1",
|
||||
commit_message="This merges the feature",
|
||||
merge_method="squash"
|
||||
)
|
||||
|
||||
# Assertions
|
||||
self.assertEqual(result['sha'], "merged123")
|
||||
self.assertTrue(result['merged'])
|
||||
self.assertEqual(result['message'], "Pull Request successfully merged")
|
||||
|
||||
self.mock_pr.merge.assert_called_once_with(
|
||||
style="squash",
|
||||
title="Merge PR #1",
|
||||
message="This merges the feature"
|
||||
)
|
||||
|
||||
|
||||
class TestUser(unittest.TestCase):
|
||||
"""Test cases for User model."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures."""
|
||||
self.mock_gitea = Mock()
|
||||
self.mock_user = Mock()
|
||||
|
||||
# Set up user attributes
|
||||
self.mock_user.login = "testuser"
|
||||
self.mock_user.id = 1
|
||||
self.mock_user.avatar_url = "https://gitea.example.com/avatars/1"
|
||||
self.mock_user.html_url = "https://gitea.example.com/testuser"
|
||||
self.mock_user.full_name = "Test User"
|
||||
self.mock_user.location = "Earth"
|
||||
self.mock_user.email = "test@example.com"
|
||||
self.mock_user.created = datetime.now()
|
||||
|
||||
def test_user_initialization(self):
|
||||
"""Test User model initialization."""
|
||||
user = User(self.mock_user, self.mock_gitea)
|
||||
|
||||
self.assertEqual(user.login, "testuser")
|
||||
self.assertEqual(user.name, "Test User")
|
||||
self.assertEqual(user.email, "test@example.com")
|
||||
self.assertEqual(user.location, "Earth")
|
||||
self.assertEqual(user.type, "User")
|
||||
|
||||
def test_get_repos(self):
|
||||
"""Test getting user repositories."""
|
||||
# Set up mock repos
|
||||
mock_repo1 = Mock()
|
||||
mock_repo1.name = "repo1"
|
||||
mock_repo1.full_name = "testuser/repo1"
|
||||
mock_repo1.created_at = datetime.now()
|
||||
mock_repo1.updated_at = datetime.now()
|
||||
|
||||
mock_repo2 = Mock()
|
||||
mock_repo2.name = "repo2"
|
||||
mock_repo2.full_name = "testuser/repo2"
|
||||
mock_repo2.created_at = datetime.now()
|
||||
mock_repo2.updated_at = datetime.now()
|
||||
|
||||
# Set other required attributes for Repository initialization
|
||||
for repo in [mock_repo1, mock_repo2]:
|
||||
repo.description = "Test repo"
|
||||
repo.private = False
|
||||
repo.fork = False
|
||||
repo.size = 100
|
||||
repo.stars_count = 1
|
||||
repo.watchers_count = 1
|
||||
repo.forks_count = 0
|
||||
repo.open_issues_count = 0
|
||||
repo.default_branch = "main"
|
||||
repo.archived = False
|
||||
repo.html_url = f"https://gitea.example.com/{repo.full_name}"
|
||||
repo.clone_url = f"https://gitea.example.com/{repo.full_name}.git"
|
||||
repo.ssh_url = f"git@gitea.example.com:{repo.full_name}.git"
|
||||
repo.owner = self.mock_user
|
||||
|
||||
self.mock_user.get_repos.return_value = [mock_repo1, mock_repo2]
|
||||
|
||||
# Test
|
||||
user = User(self.mock_user, self.mock_gitea)
|
||||
repos = user.get_repos()
|
||||
|
||||
# Assertions
|
||||
self.assertEqual(len(repos), 2)
|
||||
self.assertEqual(repos[0].name, "repo1")
|
||||
self.assertEqual(repos[1].name, "repo2")
|
||||
self.mock_user.get_repos.assert_called_once()
|
||||
|
||||
def test_follow_user(self):
|
||||
"""Test following another user."""
|
||||
# Create another user to follow
|
||||
other_user = Mock()
|
||||
other_user.login = "otheruser"
|
||||
|
||||
# Test
|
||||
user = User(self.mock_user, self.mock_gitea)
|
||||
other = User(other_user, self.mock_gitea)
|
||||
|
||||
user.add_to_following(other)
|
||||
|
||||
# Assertions
|
||||
self.mock_user.follow.assert_called_once_with("otheruser")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
@ -1,121 +0,0 @@
|
||||
"""Utility functions for GitHub/Gitea migration."""
|
||||
|
||||
import os
|
||||
import logging
|
||||
from typing import Optional, Dict, Any
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def setup_api_environment():
|
||||
"""
|
||||
Set up environment variables for API configuration.
|
||||
|
||||
This function helps configure the environment for either GitHub or Gitea mode.
|
||||
"""
|
||||
# Check if we should use Gitea
|
||||
use_gitea = os.getenv('USE_GITEA', 'false').lower() == 'true'
|
||||
|
||||
if use_gitea:
|
||||
logger.info("Configuring for Gitea mode")
|
||||
|
||||
# Ensure GITEA_URL is set
|
||||
if not os.getenv('GITEA_URL'):
|
||||
os.environ['GITEA_URL'] = 'http://localhost:3000'
|
||||
logger.info(f"Set GITEA_URL to {os.environ['GITEA_URL']}")
|
||||
|
||||
# Use GITEA_TOKEN if available, otherwise fall back to GITHUB_TOKEN
|
||||
if not os.getenv('GITEA_TOKEN') and os.getenv('GITHUB_TOKEN'):
|
||||
os.environ['GITEA_TOKEN'] = os.environ['GITHUB_TOKEN']
|
||||
logger.info("Using GITHUB_TOKEN as GITEA_TOKEN")
|
||||
|
||||
else:
|
||||
logger.info("Configuring for GitHub mode")
|
||||
|
||||
# Ensure GITHUB_TOKEN is set
|
||||
if not os.getenv('GITHUB_TOKEN'):
|
||||
logger.warning("GITHUB_TOKEN not set")
|
||||
|
||||
|
||||
def validate_api_configuration() -> Dict[str, Any]:
|
||||
"""
|
||||
Validate the current API configuration.
|
||||
|
||||
Returns:
|
||||
Dictionary with validation results
|
||||
"""
|
||||
use_gitea = os.getenv('USE_GITEA', 'false').lower() == 'true'
|
||||
|
||||
if use_gitea:
|
||||
gitea_url = os.getenv('GITEA_URL')
|
||||
gitea_token = os.getenv('GITEA_TOKEN') or os.getenv('GITHUB_TOKEN')
|
||||
|
||||
return {
|
||||
'mode': 'gitea',
|
||||
'url_set': bool(gitea_url),
|
||||
'token_set': bool(gitea_token),
|
||||
'url': gitea_url,
|
||||
'valid': bool(gitea_url and gitea_token)
|
||||
}
|
||||
else:
|
||||
github_token = os.getenv('GITHUB_TOKEN')
|
||||
|
||||
return {
|
||||
'mode': 'github',
|
||||
'url_set': True, # GitHub URL is fixed
|
||||
'token_set': bool(github_token),
|
||||
'url': 'https://api.github.com',
|
||||
'valid': bool(github_token)
|
||||
}
|
||||
|
||||
|
||||
def migrate_github_imports():
|
||||
"""
|
||||
Helper function to show how to migrate GitHub imports.
|
||||
|
||||
This function doesn't actually do anything, but serves as documentation
|
||||
for the migration process.
|
||||
"""
|
||||
migration_examples = {
|
||||
'old_import': 'from github import Github',
|
||||
'new_import': 'from gitea_shim import get_github_client',
|
||||
'old_usage': 'gh = Github(token)',
|
||||
'new_usage': 'gh = get_github_client()',
|
||||
'old_repo': 'repo = gh.get_repo("owner/repo")',
|
||||
'new_repo': 'repo = gh.get_repo("owner/repo") # Same interface!'
|
||||
}
|
||||
|
||||
logger.info("Migration examples:")
|
||||
for key, value in migration_examples.items():
|
||||
logger.info(f"{key}: {value}")
|
||||
|
||||
|
||||
def get_api_client_info() -> Dict[str, Any]:
|
||||
"""
|
||||
Get information about the current API client configuration.
|
||||
|
||||
Returns:
|
||||
Dictionary with API client information
|
||||
"""
|
||||
try:
|
||||
from .config import get_api_info
|
||||
return get_api_info()
|
||||
except ImportError:
|
||||
return validate_api_configuration()
|
||||
|
||||
|
||||
def log_api_configuration():
|
||||
"""Log the current API configuration for debugging."""
|
||||
config = get_api_client_info()
|
||||
logger.info(f"API Configuration: {config}")
|
||||
|
||||
if not config.get('valid', False):
|
||||
logger.warning("API configuration is invalid!")
|
||||
if config['mode'] == 'gitea':
|
||||
if not config['url_set']:
|
||||
logger.error("GITEA_URL not set")
|
||||
if not config['token_set']:
|
||||
logger.error("GITEA_TOKEN or GITHUB_TOKEN not set")
|
||||
else:
|
||||
if not config['token_set']:
|
||||
logger.error("GITHUB_TOKEN not set")
|
@ -1,20 +1,8 @@
|
||||
import re
|
||||
import requests
|
||||
from github import Github
|
||||
import os
|
||||
import logging
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add the gitea-shim to the path
|
||||
gitea_shim_path = Path(__file__).parent.parent.parent.parent.parent / "gitea-shim" / "python"
|
||||
sys.path.insert(0, str(gitea_shim_path))
|
||||
|
||||
try:
|
||||
from gitea_shim import get_github_client, is_gitea_mode
|
||||
except ImportError:
|
||||
# Fallback to regular GitHub if shim is not available
|
||||
from github import Github as get_github_client
|
||||
is_gitea_mode = lambda: False
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -26,11 +14,7 @@ def verify_pr_ownership(
|
||||
expected_repo,
|
||||
):
|
||||
try:
|
||||
# Use the shim to get GitHub-compatible client
|
||||
gh = get_github_client()
|
||||
|
||||
# Log which API we're using
|
||||
logger.info(f"Using {'Gitea' if is_gitea_mode() else 'GitHub'} API for PR verification")
|
||||
gh = Github(os.environ.get("GITHUB_TOKEN"))
|
||||
|
||||
match = re.match(r"https://github.com/([^/]+)/([^/]+)/pull/(\d+)", pr_url)
|
||||
if not match:
|
||||
|
@ -1,20 +1,7 @@
|
||||
"""Task decomposition workflow implementation."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add the gitea-shim to the path
|
||||
gitea_shim_path = Path(__file__).parent.parent.parent.parent.parent / "gitea-shim" / "python"
|
||||
sys.path.insert(0, str(gitea_shim_path))
|
||||
|
||||
try:
|
||||
from gitea_shim import get_github_client, is_gitea_mode
|
||||
except ImportError:
|
||||
# Fallback to regular GitHub if shim is not available
|
||||
from github import Github as get_github_client
|
||||
is_gitea_mode = lambda: False
|
||||
|
||||
from github import Github
|
||||
import requests
|
||||
from prometheus_swarm.workflows.base import Workflow
|
||||
from prometheus_swarm.utils.logging import log_section, log_key_value, log_error
|
||||
@ -112,9 +99,9 @@ class RepoSummarizerWorkflow(Workflow):
|
||||
check_required_env_vars(["GITHUB_TOKEN", "GITHUB_USERNAME"])
|
||||
validate_github_auth(os.getenv("GITHUB_TOKEN"), os.getenv("GITHUB_USERNAME"))
|
||||
|
||||
# Get the default branch from GiteaGitHubShim
|
||||
# Get the default branch from GitHub
|
||||
try:
|
||||
gh = GiteaGitHubShim(os.getenv("GITHUB_TOKEN"), base_url=os.getenv("GITEA_URL"))
|
||||
gh = Github(os.getenv("GITHUB_TOKEN"))
|
||||
self.context["repo_full_name"] = (
|
||||
f"{self.context['repo_owner']}/{self.context['repo_name']}"
|
||||
)
|
||||
@ -124,7 +111,6 @@ class RepoSummarizerWorkflow(Workflow):
|
||||
)
|
||||
self.context["base"] = repo.default_branch
|
||||
log_key_value("Default branch", self.context["base"])
|
||||
log_key_value("API Mode", "GiteaGitHubShim")
|
||||
except Exception as e:
|
||||
log_error(e, "Failed to get default branch, using 'main'")
|
||||
self.context["base"] = "main"
|
||||
|
@ -1,20 +1,7 @@
|
||||
"""Task decomposition workflow implementation."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add the gitea-shim to the path
|
||||
gitea_shim_path = Path(__file__).parent.parent.parent.parent.parent / "gitea-shim" / "python"
|
||||
sys.path.insert(0, str(gitea_shim_path))
|
||||
|
||||
try:
|
||||
from gitea_shim import get_github_client, is_gitea_mode
|
||||
except ImportError:
|
||||
# Fallback to regular GitHub if shim is not available
|
||||
from github import Github as get_github_client
|
||||
is_gitea_mode = lambda: False
|
||||
|
||||
from github import Github
|
||||
from prometheus_swarm.workflows.base import Workflow
|
||||
from prometheus_swarm.utils.logging import log_section, log_key_value, log_error
|
||||
from src.workflows.repoSummarizerAudit import phases
|
||||
@ -102,15 +89,10 @@ class repoSummarizerAuditWorkflow(Workflow):
|
||||
self.context["github_token"] = os.getenv("GITHUB_TOKEN")
|
||||
# Enter repo directory
|
||||
os.chdir(self.context["repo_path"])
|
||||
|
||||
# Use the shim to get GitHub-compatible client
|
||||
gh = get_github_client()
|
||||
gh = Github(self.context["github_token"])
|
||||
repo = gh.get_repo(f"{self.context['repo_owner']}/{self.context['repo_name']}")
|
||||
pr = repo.get_pull(self.context["pr_number"])
|
||||
self.context["pr"] = pr
|
||||
|
||||
log_key_value("API Mode", "Gitea" if is_gitea_mode() else "GitHub")
|
||||
|
||||
# Add remote for PR's repository and fetch the branch
|
||||
os.system(
|
||||
f"git remote add pr_source https://github.com/{pr.head.repo.full_name}"
|
||||
|
Reference in New Issue
Block a user