# app/tasks/git.py """ Git operation tasks for Syndarix. These tasks handle Git operations for projects: - Cloning repositories - Creating branches - Committing changes - Pushing to remotes - Creating pull requests Tasks are routed to the 'git' queue for dedicated processing. All operations are scoped by project_id for multi-tenancy. """ import logging from typing import Any from app.celery_app import celery_app logger = logging.getLogger(__name__) @celery_app.task(bind=True, name="app.tasks.git.clone_repository") def clone_repository( self, project_id: str, repo_url: str, branch: str = "main", ) -> dict[str, Any]: """ Clone a repository for a project. This task clones a Git repository to the project workspace: 1. Prepare workspace directory 2. Clone repository with credentials 3. Checkout specified branch 4. Update project metadata Args: project_id: UUID of the project repo_url: Git repository URL (HTTPS or SSH) branch: Branch to checkout (default: main) Returns: dict with status and project_id """ logger.info( f"Cloning repository {repo_url} for project {project_id} on branch {branch}" ) # TODO: Implement repository cloning # This will involve: # 1. Getting project credentials from secrets store # 2. Creating workspace directory # 3. Running git clone with proper auth # 4. Checking out the target branch # 5. Updating project record with clone status return { "status": "pending", "project_id": project_id, } @celery_app.task(bind=True, name="app.tasks.git.commit_changes") def commit_changes( self, project_id: str, message: str, files: list[str] | None = None, ) -> dict[str, Any]: """ Commit changes in a project repository. This task creates a Git commit: 1. Stage specified files (or all if None) 2. Create commit with message 3. Update commit history record Args: project_id: UUID of the project message: Commit message (follows conventional commits) files: List of files to stage, or None for all staged Returns: dict with status and project_id """ logger.info( f"Committing changes for project {project_id}: {message}" ) # TODO: Implement commit operation # This will involve: # 1. Loading project workspace path # 2. Running git add for specified files # 3. Running git commit with message # 4. Recording commit hash in database return { "status": "pending", "project_id": project_id, } @celery_app.task(bind=True, name="app.tasks.git.create_branch") def create_branch( self, project_id: str, branch_name: str, from_ref: str = "HEAD", ) -> dict[str, Any]: """ Create a new branch in a project repository. This task creates a Git branch: 1. Checkout from reference 2. Create new branch 3. Update branch tracking Args: project_id: UUID of the project branch_name: Name of the new branch (e.g., feature/123-description) from_ref: Reference to branch from (default: HEAD) Returns: dict with status and project_id """ logger.info( f"Creating branch {branch_name} from {from_ref} for project {project_id}" ) # TODO: Implement branch creation # This will involve: # 1. Loading project workspace # 2. Running git checkout -b from_ref # 3. Recording branch in database return { "status": "pending", "project_id": project_id, } @celery_app.task(bind=True, name="app.tasks.git.create_pull_request") def create_pull_request( self, project_id: str, title: str, body: str, head_branch: str, base_branch: str = "main", ) -> dict[str, Any]: """ Create a pull request for a project. This task creates a PR on the external Git provider: 1. Push branch if needed 2. Create PR via API (Gitea, GitHub, GitLab) 3. Store PR reference Args: project_id: UUID of the project title: PR title body: PR description (markdown) head_branch: Branch with changes base_branch: Target branch (default: main) Returns: dict with status and project_id """ logger.info( f"Creating PR '{title}' from {head_branch} to {base_branch} for project {project_id}" ) # TODO: Implement PR creation # This will involve: # 1. Loading project and Git provider config # 2. Ensuring head_branch is pushed # 3. Calling provider API to create PR # 4. Storing PR URL and number return { "status": "pending", "project_id": project_id, } @celery_app.task(bind=True, name="app.tasks.git.push_changes") def push_changes( self, project_id: str, branch: str, force: bool = False, ) -> dict[str, Any]: """ Push changes to remote repository. This task pushes commits to the remote: 1. Verify authentication 2. Push branch to remote 3. Handle push failures Args: project_id: UUID of the project branch: Branch to push force: Whether to force push (use with caution) Returns: dict with status and project_id """ logger.info( f"Pushing branch {branch} for project {project_id} (force={force})" ) # TODO: Implement push operation # This will involve: # 1. Loading project credentials # 2. Running git push (with --force if specified) # 3. Handling authentication and conflicts return { "status": "pending", "project_id": project_id, }