Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions odev/commands/git/fetch.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ def grouped_changes(self) -> dict[str, list[tuple[str, int, int]]]:
changes: dict[str, list[tuple[str, int, int]]] = {}

for repository in self.repositories:
try:
repository.prune_worktrees()
except Exception as e:
logger.debug(f"Failed to prune worktrees for {repository.name!r}: {e}")
repository.fetch(detached=False)

for name, worktrees in self.grouped_worktrees.items():
Expand Down
12 changes: 11 additions & 1 deletion odev/common/commands/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
from odev.common import args
from odev.common.commands import Command
from odev.common.connectors import GitConnector, GitWorktree
from odev.common.logging import logging
from odev.common.odoobin import odoo_repositories


logger = logging.getLogger(__name__)


class GitCommand(Command, ABC):
"""Base command class for interacting with git repositories and worktrees."""

Expand All @@ -21,7 +25,13 @@ def repositories(self) -> Generator[GitConnector, None, None]:
def worktrees(self) -> Generator[GitWorktree, None, None]:
"""Iterate over worktrees in Odoo repositories."""
for repository in self.repositories:
yield from repository.worktrees()
for worktree in repository.worktrees():
if not worktree.path.exists():
logger.debug(f"Skipping missing worktree {worktree.name!r} at {worktree.path!s}")
continue
if hasattr(self, "args") and self.args.version and worktree.name != self.args.version:
continue
yield worktree

@property
def grouped_worktrees(self) -> dict[str, list[GitWorktree]]:
Expand Down
13 changes: 10 additions & 3 deletions odev/common/connectors/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,10 +213,17 @@ def pending_changes(self) -> tuple[int, int]:
:return: A tuple of commits behind and ahead.
:rtype: Tuple[int, int]
"""
if self.detached:
return 0, 0
repo = Repo(self.path)
rev_list: str = repo.git.rev_list("--left-right", "--count", "@{u}...HEAD")
commits_behind, commits_ahead = (int(commits) for commits in rev_list.split("\t"))
return commits_behind, commits_ahead
try:
rev_list: str = repo.git.rev_list("--left-right", "--count", "@{u}...HEAD")
commits_behind, commits_ahead = (int(commits) for commits in rev_list.split("\t"))
return commits_behind, commits_ahead
except GitCommandError as e:
if "no upstream configured" in str(e) or "does not point to a branch" in str(e):
return 0, 0
raise


class GitConnector(Connector):
Expand Down
17 changes: 14 additions & 3 deletions odev/common/store/tables/secrets.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
from base64 import b64decode, b64encode
from collections.abc import Sequence
from dataclasses import dataclass
Expand Down Expand Up @@ -69,9 +70,13 @@ class SecretStore(PostgresTable):
@classmethod
def _list_ssh_keys(cls) -> list[AgentKey]:
"""List all SSH keys available in the ssh-agent."""
keys = list(SSHAgent().get_keys())
try:
keys = list(SSHAgent().get_keys())
except (SSHException, ConnectionError) as e:
logger.warning(f"Failed to communicate with ssh-agent: {e}")
keys = []

if not keys:
if not keys and not os.environ.get("ODEV_NO_SSH_AGENT"):
raise OdevError("No SSH keys found in ssh-agent, or ssh-agent is not running.")

fingerprint = cls.config.security.encryption_key
Expand Down Expand Up @@ -266,7 +271,13 @@ def _get(
return None

logger.debug(f"Secret '{name}:{scope}:{platform}' retrieved from storage")
return Secret(name, result[0][0], SecretStore.decrypt(result[0][1]), scope, platform)
try:
password = SecretStore.decrypt(result[0][1])
except OdevError:
logger.debug(f"Failed to decrypt secret '{name}:{scope}:{platform}', treating as missing")
return None

return Secret(name, result[0][0], password, scope, platform)

def _set(self, secret: Secret):
"""Save a secret to the vault.
Expand Down
Loading