Python bindings for libgit2 providing comprehensive Git repository operations and version control functionality.
—
Credential management for Git operations supporting various authentication methods including SSH keys, username/password, and SSH agent. Provides secure authentication for remote operations with flexible credential providers.
Different credential types for various authentication scenarios.
class Username:
def __init__(self, username: str):
"""
Username-only credentials.
Parameters:
- username: Username for authentication
"""
class UserPass:
def __init__(self, username: str, password: str):
"""
Username and password credentials.
Parameters:
- username: Username for authentication
- password: Password or personal access token
"""
class Keypair:
def __init__(
self,
username: str,
pubkey_path: str,
privkey_path: str,
passphrase: str = ''
):
"""
SSH key pair credentials from files.
Parameters:
- username: Username for SSH authentication (usually 'git')
- pubkey_path: Path to public key file
- privkey_path: Path to private key file
- passphrase: Private key passphrase (empty if none)
"""
class KeypairFromAgent:
def __init__(self, username: str):
"""
SSH key pair credentials from SSH agent.
Parameters:
- username: Username for SSH authentication (usually 'git')
"""
class KeypairFromMemory:
def __init__(
self,
username: str,
pubkey_data: str,
privkey_data: str,
passphrase: str = ''
):
"""
SSH key pair credentials from memory.
Parameters:
- username: Username for SSH authentication
- pubkey_data: Public key data as string
- privkey_data: Private key data as string
- passphrase: Private key passphrase (empty if none)
"""Constants for identifying credential types in authentication callbacks.
# Credential Type Flags
GIT_CREDENTIAL_USERPASS_PLAINTEXT: int # Username/password
GIT_CREDENTIAL_SSH_KEY: int # SSH key pair
GIT_CREDENTIAL_SSH_CUSTOM: int # Custom SSH authentication
GIT_CREDENTIAL_DEFAULT: int # Default credentials
GIT_CREDENTIAL_SSH_INTERACTIVE: int # Interactive SSH
GIT_CREDENTIAL_USERNAME: int # Username only
GIT_CREDENTIAL_SSH_MEMORY: int # SSH key from memoryUtility functions for credential management.
def get_credentials(
url: str,
username_from_url: str,
allowed_types: int
):
"""
Default credential provider function.
Parameters:
- url: Remote URL requesting credentials
- username_from_url: Username extracted from URL
- allowed_types: Bitfield of allowed credential types
Returns:
Appropriate credential object or None
"""Authentication is typically handled through RemoteCallbacks credential method.
class RemoteCallbacks:
def credentials(
self,
url: str,
username_from_url: str,
allowed_types: int
):
"""
Provide credentials for remote operation.
Parameters:
- url: Remote URL needing authentication
- username_from_url: Username from URL (if any)
- allowed_types: Credential types allowed by remote
Returns:
Credential object matching allowed types
"""
return None
def certificate_check(
self,
certificate,
valid: bool,
host: str
) -> bool:
"""
Verify server certificate for HTTPS.
Parameters:
- certificate: Server certificate
- valid: True if certificate passed basic validation
- host: Hostname being connected to
Returns:
True to accept certificate, False to reject
"""
return validimport pygit2
# SSH key from files
class SSHKeyCallbacks(pygit2.RemoteCallbacks):
def credentials(self, url, username_from_url, allowed_types):
if allowed_types & pygit2.GIT_CREDENTIAL_SSH_KEY:
return pygit2.Keypair(
'git',
'/home/user/.ssh/id_rsa.pub',
'/home/user/.ssh/id_rsa',
'my_passphrase' # Empty string if no passphrase
)
return None
# Use with remote operations
repo = pygit2.Repository('/path/to/repo')
callbacks = SSHKeyCallbacks()
origin = repo.remotes['origin']
origin.fetch(callbacks=callbacks)# Use SSH agent for key management
class SSHAgentCallbacks(pygit2.RemoteCallbacks):
def credentials(self, url, username_from_url, allowed_types):
if allowed_types & pygit2.GIT_CREDENTIAL_SSH_KEY:
return pygit2.KeypairFromAgent('git')
return None
callbacks = SSHAgentCallbacks()
origin.fetch(callbacks=callbacks)# HTTPS with username/password
class HTTPSCallbacks(pygit2.RemoteCallbacks):
def credentials(self, url, username_from_url, allowed_types):
if allowed_types & pygit2.GIT_CREDENTIAL_USERPASS_PLAINTEXT:
# For GitHub, use personal access token as password
return pygit2.UserPass('username', 'personal_access_token')
return None
callbacks = HTTPSCallbacks()
origin.push(['refs/heads/main'], callbacks=callbacks)# Support multiple authentication methods
class FlexibleCallbacks(pygit2.RemoteCallbacks):
def __init__(self, ssh_key_path=None, username=None, password=None):
self.ssh_key_path = ssh_key_path
self.username = username
self.password = password
def credentials(self, url, username_from_url, allowed_types):
# Try SSH key first
if (allowed_types & pygit2.GIT_CREDENTIAL_SSH_KEY and
self.ssh_key_path):
return pygit2.Keypair(
'git',
f'{self.ssh_key_path}.pub',
self.ssh_key_path,
''
)
# Try SSH agent
if allowed_types & pygit2.GIT_CREDENTIAL_SSH_KEY:
return pygit2.KeypairFromAgent('git')
# Try username/password
if (allowed_types & pygit2.GIT_CREDENTIAL_USERPASS_PLAINTEXT and
self.username and self.password):
return pygit2.UserPass(self.username, self.password)
# Try username only
if (allowed_types & pygit2.GIT_CREDENTIAL_USERNAME and
self.username):
return pygit2.Username(self.username)
return None
# Use flexible authentication
callbacks = FlexibleCallbacks(
ssh_key_path='/home/user/.ssh/id_rsa',
username='myuser',
password='mytoken'
)# Load SSH keys from strings (useful for CI/CD)
class MemoryKeyCallbacks(pygit2.RemoteCallbacks):
def __init__(self, private_key_data, public_key_data, passphrase=''):
self.private_key_data = private_key_data
self.public_key_data = public_key_data
self.passphrase = passphrase
def credentials(self, url, username_from_url, allowed_types):
if allowed_types & pygit2.GIT_CREDENTIAL_SSH_KEY:
return pygit2.KeypairFromMemory(
'git',
self.public_key_data,
self.private_key_data,
self.passphrase
)
return None
# Read keys from environment or config
import os
private_key = os.environ.get('SSH_PRIVATE_KEY')
public_key = os.environ.get('SSH_PUBLIC_KEY')
if private_key and public_key:
callbacks = MemoryKeyCallbacks(private_key, public_key)
origin.fetch(callbacks=callbacks)# Custom certificate verification
class SecureCallbacks(pygit2.RemoteCallbacks):
def certificate_check(self, certificate, valid, host):
# Always verify certificates in production
if not valid:
print(f"Invalid certificate for {host}")
return False
# Additional custom verification
if 'github.com' in host:
# Accept GitHub certificates
return True
elif 'company.com' in host:
# Custom verification for company servers
return self.verify_company_cert(certificate)
# Default to system validation
return valid
def verify_company_cert(self, certificate):
# Custom certificate verification logic
return True
def credentials(self, url, username_from_url, allowed_types):
if allowed_types & pygit2.GIT_CREDENTIAL_USERPASS_PLAINTEXT:
return pygit2.UserPass('user', 'token')
return Noneimport os
class EnvironmentCallbacks(pygit2.RemoteCallbacks):
def credentials(self, url, username_from_url, allowed_types):
# SSH key from environment
if allowed_types & pygit2.GIT_CREDENTIAL_SSH_KEY:
ssh_key = os.environ.get('GIT_SSH_KEY')
if ssh_key:
public_key = ssh_key + '.pub'
if os.path.exists(ssh_key) and os.path.exists(public_key):
passphrase = os.environ.get('GIT_SSH_PASSPHRASE', '')
return pygit2.Keypair('git', public_key, ssh_key, passphrase)
# Username/password from environment
if allowed_types & pygit2.GIT_CREDENTIAL_USERPASS_PLAINTEXT:
username = os.environ.get('GIT_USERNAME')
password = os.environ.get('GIT_PASSWORD') or os.environ.get('GIT_TOKEN')
if username and password:
return pygit2.UserPass(username, password)
return None
# Use environment-based authentication
callbacks = EnvironmentCallbacks()import getpass
class InteractiveCallbacks(pygit2.RemoteCallbacks):
def credentials(self, url, username_from_url, allowed_types):
print(f"Authentication required for {url}")
# SSH key with prompt for passphrase
if allowed_types & pygit2.GIT_CREDENTIAL_SSH_KEY:
ssh_key = input("SSH private key path [~/.ssh/id_rsa]: ")
if not ssh_key:
ssh_key = os.path.expanduser('~/.ssh/id_rsa')
public_key = ssh_key + '.pub'
if os.path.exists(ssh_key) and os.path.exists(public_key):
passphrase = getpass.getpass("SSH key passphrase (empty if none): ")
return pygit2.Keypair('git', public_key, ssh_key, passphrase)
# Username/password with prompts
if allowed_types & pygit2.GIT_CREDENTIAL_USERPASS_PLAINTEXT:
username = input(f"Username [{username_from_url}]: ") or username_from_url
password = getpass.getpass("Password/Token: ")
if username and password:
return pygit2.UserPass(username, password)
return None
# Note: Only use interactive auth in appropriate contexts
callbacks = InteractiveCallbacks()# Clone repository with authentication
def clone_with_auth(url, path, auth_method='ssh_agent'):
if auth_method == 'ssh_agent':
callbacks = SSHAgentCallbacks()
elif auth_method == 'ssh_key':
callbacks = SSHKeyCallbacks()
elif auth_method == 'https':
callbacks = HTTPSCallbacks()
else:
callbacks = FlexibleCallbacks()
try:
repo = pygit2.clone_repository(url, path, callbacks=callbacks)
print(f"Successfully cloned {url} to {path}")
return repo
except pygit2.GitError as e:
print(f"Clone failed: {e}")
return None
# Clone with different auth methods
repo = clone_with_auth('git@github.com:user/repo.git', '/local/path', 'ssh_agent')
repo = clone_with_auth('https://github.com/user/repo.git', '/local/path', 'https')class RobustCallbacks(pygit2.RemoteCallbacks):
def __init__(self):
self.auth_attempts = 0
self.max_attempts = 3
def credentials(self, url, username_from_url, allowed_types):
self.auth_attempts += 1
if self.auth_attempts > self.max_attempts:
print("Maximum authentication attempts exceeded")
return None
print(f"Authentication attempt {self.auth_attempts}/{self.max_attempts}")
# Try different methods based on attempt number
if self.auth_attempts == 1 and allowed_types & pygit2.GIT_CREDENTIAL_SSH_KEY:
try:
return pygit2.KeypairFromAgent('git')
except Exception as e:
print(f"SSH agent failed: {e}")
if self.auth_attempts == 2 and allowed_types & pygit2.GIT_CREDENTIAL_SSH_KEY:
key_path = os.path.expanduser('~/.ssh/id_rsa')
if os.path.exists(key_path):
return pygit2.Keypair('git', key_path + '.pub', key_path, '')
if allowed_types & pygit2.GIT_CREDENTIAL_USERPASS_PLAINTEXT:
# Final attempt with username/password
username = input("Username: ")
password = getpass.getpass("Password: ")
return pygit2.UserPass(username, password)
return None
callbacks = RobustCallbacks()
try:
origin.fetch(callbacks=callbacks)
except pygit2.GitError as e:
if "authentication" in str(e).lower():
print("Authentication failed after all attempts")
else:
print(f"Other error: {e}")Install with Tessl CLI
npx tessl i tessl/pypi-pygit2