Python interface to coveralls.io API for uploading code coverage results
—
The repository integration module provides version control system support for extracting git and mercurial repository information. It automatically detects the VCS type and extracts commit details, branch information, and remote URLs required by the coveralls.io API.
Automatically detects repository type and extracts comprehensive repository information.
def repo(root):
"""
Auto-detect repository type and extract information.
Args:
root (str): Path to repository root directory
Returns:
dict | None: Repository information dictionary or None if no VCS detected
Detection Logic:
1. Check for .git directory (Git repository)
2. Check for .hg directory (Mercurial repository)
3. Return None if neither found
Repository Info Structure:
{
"head": {
"id": str, # Commit hash/ID
"author_name": str, # Author name
"author_email": str, # Author email
"committer_name": str, # Committer name
"committer_email": str, # Committer email
"message": str, # Commit message
},
"branch": str, # Current branch name
"remotes": [ # Remote repositories
{
"name": str, # Remote name (e.g., "origin")
"url": str, # Remote URL
}
]
}
"""Extracts comprehensive information from Git repositories including commit details, branch, and remote configuration.
def gitrepo(root):
"""
Extract information from Git repository.
Args:
root (str): Path to Git repository root
Returns:
dict: Git repository information
Extracted Information:
- Latest commit details (hash, author, committer, message)
- Current branch (with CI environment variable fallbacks)
- All configured remotes with fetch URLs
Branch Detection Priority:
1. CIRCLE_BRANCH environment variable (Circle CI)
2. TRAVIS_BRANCH environment variable (Travis CI)
3. git rev-parse --abbrev-ref HEAD (local branch)
Commands Used:
- git log -1 --pretty=format:[format] (commit info)
- git rev-parse --abbrev-ref HEAD (branch name)
- git remote -v (remote URLs)
"""Extracts information from Mercurial repositories with similar structure to Git integration.
def hgrepo(root):
"""
Extract information from Mercurial repository.
Args:
root (str): Path to Mercurial repository root
Returns:
dict: Mercurial repository information
Extracted Information:
- Latest changeset details (hash, author, message)
- Current branch (with CI environment variable fallbacks)
- Configured paths (remotes)
Branch Detection Priority:
1. CIRCLE_BRANCH environment variable (Circle CI)
2. TRAVIS_BRANCH environment variable (Travis CI)
3. hg branch (local branch)
Commands Used:
- hg log -l 1 --template=[template] (changeset info)
- hg branch (branch name)
- hg paths (remote URLs)
"""# Internal format string for git log command
FORMAT = '%n'.join(['%H', '%aN', '%ae', '%cN', '%ce', '%s'])
# %H: Commit hash
# %aN: Author name
# %ae: Author email
# %cN: Committer name
# %ce: Committer email
# %s: Subject (commit message)# Template for mercurial log command
HGLOG = """{node}
{author|person}
{author|email}
{author|person}
{author|email}
{desc}"""
# {node}: Changeset hash
# {author|person}: Author name
# {author|email}: Author email
# {desc}: Description (commit message)from coveralls.repository import repo
# Auto-detect and extract repository info
repo_info = repo('/path/to/project')
if repo_info:
print(f"Repository detected: {repo_info['head']['id'][:8]}")
print(f"Branch: {repo_info['branch']}")
print(f"Author: {repo_info['head']['author_name']}")
for remote in repo_info['remotes']:
print(f"Remote {remote['name']}: {remote['url']}")
else:
print("No version control system detected")from coveralls.repository import gitrepo
import os
# Direct Git repository processing
try:
git_info = gitrepo('/path/to/git/repo')
# Access commit information
commit = git_info['head']
print(f"Commit: {commit['id']}")
print(f"Author: {commit['author_name']} <{commit['author_email']}>")
print(f"Message: {commit['message']}")
# Branch information (with CI detection)
print(f"Branch: {git_info['branch']}")
# Remote repositories
for remote in git_info['remotes']:
print(f"{remote['name']}: {remote['url']}")
except subprocess.CalledProcessError as e:
print(f"Git command failed: {e}")
except Exception as e:
print(f"Repository processing error: {e}")from coveralls.repository import hgrepo
# Direct Mercurial repository processing
try:
hg_info = hgrepo('/path/to/hg/repo')
# Access changeset information
head = hg_info['head']
print(f"Changeset: {head['id']}")
print(f"Author: {head['author_name']} <{head['author_email']}>")
print(f"Description: {head['message']}")
# Branch and remotes
print(f"Branch: {hg_info['branch']}")
for remote in hg_info['remotes']:
print(f"{remote['name']}: {remote['url']}")
except subprocess.CalledProcessError as e:
print(f"Mercurial command failed: {e}")
except Exception as e:
print(f"Repository processing error: {e}")The module automatically detects CI environments and uses their branch information:
# Branch detection priority (in gitrepo and hgrepo)
branch_sources = [
"CIRCLE_BRANCH", # Circle CI
"TRAVIS_BRANCH", # Travis CI
# Local VCS command as fallback
]
# Example CI integration
import os
# Circle CI
if 'CIRCLE_BRANCH' in os.environ:
branch = os.environ['CIRCLE_BRANCH']
# Travis CI
elif 'TRAVIS_BRANCH' in os.environ:
branch = os.environ['TRAVIS_BRANCH']
# Local detection
else:
branch = subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD'])# Travis CI - automatic detection
language: python
script: pytest --cov=mypackage
after_success: coveralls
# Circle CI - manual branch setting
version: 2
jobs:
build:
environment:
CIRCLE_BRANCH: ${CIRCLE_BRANCH}
steps:
- run: pytest --cov=mypackage
- run: coveralls
# GitHub Actions - manual configuration
env:
GITHUB_BRANCH: ${GITHUB_REF#refs/heads/}
run: |
pytest --cov=mypackage
TRAVIS_BRANCH=$GITHUB_BRANCH coverallsfrom coveralls.repository import repo
# Handle missing VCS
repo_info = repo('/path/without/vcs')
if repo_info is None:
# No git or mercurial repository found
# Use empty dict or skip repository info
repo_info = {}from coveralls.repository import gitrepo
import subprocess
try:
git_info = gitrepo('/path/to/repo')
except subprocess.CalledProcessError as e:
# Git command failed (corrupted repo, permissions, etc.)
print(f"Git error: {e}")
git_info = None
except FileNotFoundError:
# Git not installed or not in PATH
print("Git not found")
git_info = None# Robust repository info processing
def safe_repo_info(repo_path):
try:
info = repo(repo_path)
# Validate required fields
if info and 'head' in info and 'id' in info['head']:
return info
else:
return None
except Exception as e:
print(f"Repository info extraction failed: {e}")
return None
# Usage with fallback
repo_info = safe_repo_info('/path/to/project') or {}# From main workflow (wear function)
def wear(args=None):
# ... other setup ...
# Extract repository info only if not disabled
if not args.nogit:
git_info = repo(args.base_dir)
if git_info is None:
git_info = {} # Empty dict for no VCS
else:
git_info = {} # Skip repository info entirely
# ... continue with API submission ...# Repository functions change working directory temporarily
import os
original_dir = os.getcwd()
try:
repo_info = gitrepo('/path/to/repo') # Changes to repo directory internally
finally:
os.chdir(original_dir) # Always restore original directoryThe repository functions change the current working directory temporarily. Ensure proper restoration:
import os
from coveralls.repository import repo
# Save current directory
original_cwd = os.getcwd()
try:
# Extract repository info (may change directory)
repo_info = repo('/path/to/project')
finally:
# Always restore original directory
os.chdir(original_cwd)# Robust repository integration
def get_repo_info(project_path, skip_git=False):
if skip_git:
return {}
try:
return repo(project_path) or {}
except Exception as e:
logging.warning(f"Repository info extraction failed: {e}")
return {}Install with Tessl CLI
npx tessl i tessl/pypi-python-coveralls