Forge
csharp3407750f
1using AgentForge.Abstractions;
2using AgentForge.Data;
3using AgentForge.Options;
4using AgentForge.Plugin.RepositoryImport.GitHub.Core;
5using AgentForge.Plugin.Sdk;
6using Microsoft.Extensions.Options;
7
8namespace AgentForge.Plugin.RepositoryImport.GitHub;
9
10internal sealed class GitHubSingleRepositoryImporter(
11 ForgeGitHubRepositoryImportEngine engine,
12 ForgeRepository repository,
13 IOptions<ForgeOptions> options) : IForgeRepositoryImporter
14{
15 public string SourceId => "github";
16
17 public ForgeRepositoryImportScope Scope => ForgeRepositoryImportScope.Single;
18
19 public string PluginId => "repository-import-single-github";
20
21 public Task<ForgeRepositoryImportResponse> ImportAsync(
22 ForgeRepositoryImportRequest request,
23 CancellationToken cancellationToken)
24 {
25 if (request.Scope != ForgeRepositoryImportScope.Single)
26 throw new ForgeApiException(400, $"Importer '{PluginId}' only supports single scope.");
27
28 var cloneUrl = request.SingleCloneUrl?.Trim()
29 ?? throw new ForgeApiException(400, "cloneUrl is required.");
30 var forgeOrgSlug = ForgeRepositoryImportRequestMapping.FirstNonEmpty(
31 request.ForgeOrg,
32 options.Value.DefaultOrgSlug)
33 ?? throw new ForgeApiException(400, "forgeOrg is required.");
34 var repoName = request.SingleRepoName?.Trim()
35 ?? InferRepoName(cloneUrl)
36 ?? throw new ForgeApiException(400, "repoName is required when it cannot be inferred from cloneUrl.");
37
38 var org = repository.GetOrgBySlug(forgeOrgSlug)
39 ?? throw new ForgeApiException(404, $"Organization '{forgeOrgSlug}' not found.");
40
41 var token = options.Value.GitHubImportToken;
42 if (string.IsNullOrWhiteSpace(token) && !request.DryRun)
43 throw new ForgeApiException(503, "FORGE_GITHUB_IMPORT_TOKEN is not configured.");
44
45 var remote = new GitHubRepositoryInfo(
46 repoName,
47 cloneUrl,
48 IsPrivate: true,
49 IsArchived: false,
50 Topics: []);
51
52 var response = engine.ImportRepositories(
53 org,
54 "single",
55 [remote],
56 catalogDoc: null,
57 request,
58 token);
59
60 return Task.FromResult(response);
61 }
62
63 private static string? InferRepoName(string cloneUrl)
64 {
65 if (!Uri.TryCreate(cloneUrl, UriKind.Absolute, out var uri))
66 return null;
67 var segment = uri.AbsolutePath.Trim('/').Split('/').LastOrDefault();
68 if (string.IsNullOrWhiteSpace(segment))
69 return null;
70 return segment.EndsWith(".git", StringComparison.OrdinalIgnoreCase)
71 ? segment[..^4]
72 : segment;
73 }
74}
75
View only · write via MCP/CIDE