Git Service Feature

This feature provides git repository cloning and management for projects.

Overview

The Git Service:

  • Clones repositories using the project’s encrypted git token
  • Organizes repos by tenant and project ID: {baseReposPath}/{tenantId}/{projectId}/{repoName}
  • Uses a shared folder accessible by both the API and Python parser
  • Automatically clones repositories when project config is updated

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                        Shared Volume                             │
│                     /app/repos (or local path)                   │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │ {tenantId}/                                                  ││
│  │   └── {projectId}/                                           ││
│  │       ├── repo1/                                             ││
│  │       ├── repo2/                                             ││
│  │       └── repo3/                                             ││
│  └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
         ▲                                    ▲
         │                                    │
    ┌────┴────┐                         ┌────┴────┐
    │  MILTON │                         │ Python  │
    │   API   │                         │ Parser  │
    └─────────┘                         └─────────┘

How It Works

  1. Config Update Triggers Clone: When PUT /api/projects/{projectId}/config is called with repositories, a CloneRepositoriesCommand is published.

  2. Async Processing: The command is processed by CloneRepositoriesHandler on a dedicated git-operations queue.

  3. Token Decryption: The IGitCredentialService retrieves and decrypts the git token for authentication.

  4. Clone Operation: IGitService clones each repository to the project’s folder.

Services

IGitService

Main interface for git operations:

public interface IGitService
{
    Task<GitCloneResult> CloneRepositoryAsync(
        Guid tenantId, int projectId, string repositoryUrl, 
        string? gitToken = null, CancellationToken ct = default);
    
    Task<List<GitCloneResult>> CloneAllRepositoriesAsync(
        Guid tenantId, int projectId, CancellationToken ct = default);
    
    string GetProjectRepositoriesPath(Guid tenantId, int projectId);
    
    Task DeleteProjectRepositoriesAsync(
        Guid tenantId, int projectId, CancellationToken ct = default);
}

Usage Example

public class MyService
{
    private readonly IGitService _gitService;
    private readonly ITenantService _tenantService;
 
    public async Task ProcessRepositoriesAsync(int projectId)
    {
        var tenantId = _tenantService.GetCurrentTenant();
        
        // Get path to cloned repos
        var reposPath = _gitService.GetProjectRepositoriesPath(tenantId, projectId);
        
        // Enumerate cloned repositories
        foreach (var repoDir in Directory.GetDirectories(reposPath))
        {
            // Process each repository...
        }
    }
}

Configuration

Repository Path

Set via configuration or environment variable:

{
  "Git": {
    "RepositoriesPath": "C:/path/to/repos"
  }
}

Or via Aspire (set automatically by AppHost):

Git__RepositoriesPath=/app/repos

Queue Configuration

The git-operations queue is configured in Program.cs:

opts.LocalQueue("git-operations")
    .UseDurableInbox()          // Persist messages for reliability
    .Sequential()               // Process one at a time
    .MaximumParallelMessages(1); // Avoid overwhelming git servers

Python Parser Integration

When adding the Python parser as a container, mount the same repos folder:

// In AppHost.cs
var parser = builder.AddContainer("parser", "milton-parser")
    .WithBindMount(reposPath, "/app/repos")
    .WithEnvironment("REPOS_PATH", "/app/repos")
    .WithReference(api);

The Python parser can then access cloned repos at:

/app/repos/{tenantId}/{projectId}/{repoName}/

Wolverine Message Flow

┌─────────────────────┐
│ UpdateProjectConfig │
│     Endpoint        │
└─────────┬───────────┘
          │
          ▼
┌─────────────────────┐
│ UpdateProjectConfig │
│      Handler        │
│  (saves config)     │
└─────────┬───────────┘
          │
          │ SendAsync(CloneRepositoriesCommand)
          ▼
┌─────────────────────┐
│  git-operations     │
│   Local Queue       │
│   (durable)         │
└─────────┬───────────┘
          │
          ▼
┌─────────────────────┐
│ CloneRepositories   │
│      Handler        │
│ (clones all repos)  │
└─────────────────────┘

Error Handling

  • Failed clones are logged but don’t fail the entire operation
  • Results include success/failure status for each repository
  • The durable queue ensures operations survive application restarts

0 items under this folder.