Forge
csharp3407750f
1using AgentForge.Abstractions;
2using AgentForge.Data;
3using AgentForge.Data.Entities;
4using AgentForge.Models;
5using AgentForge.Plugin.Sdk;
6using AgentForge.Services;
7
8namespace AgentForge.Plugin.RepositoryImport.GitHub.Core;
9
10public sealed class ForgeGitHubRepositoryImportEngine(
11 ForgeRepository repository,
12 GitRepoService git,
13 ForgeImportVisibilityService visibilityService,
14 ForgeImportCatalogMatcher catalogMatcher)
15{
16 public ForgeRepositoryImportResponse ImportRepositories(
17 ForgeOrgEntity org,
18 string externalSourceLabel,
19 IReadOnlyList<GitHubRepositoryInfo> remoteRepos,
20 ForgeImportCatalogDocument? catalogDoc,
21 ForgeRepositoryImportRequest request,
22 string? gitHubToken,
23 Func<GitHubRepositoryInfo, ForgeImportCatalogMatch>? matchOverride = null)
24 {
25 if (!request.DryRun && catalogDoc is not null)
26 {
27 foreach (var groupPath in catalogDoc.Catalog.EnsureGroups)
28 repository.EnsureRepoGroupPath(org.Id, groupPath);
29 }
30
31 var items = new List<ForgeRepositoryImportItemResult>();
32 var imported = 0;
33 var skipped = 0;
34 var failed = 0;
35
36 foreach (var remote in remoteRepos.OrderBy(r => r.Name, StringComparer.OrdinalIgnoreCase))
37 {
38 var match = matchOverride?.Invoke(remote)
39 ?? (catalogDoc is null
40 ? new ForgeImportCatalogMatch(false, request.CatalogPath ?? "backup/unclassified", null, ForgeImportCatalogMatcher.ToForgeSlug(remote.Name), "single")
41 : catalogMatcher.Match(catalogDoc, remote));
42
43 if (match.Skip)
44 {
45 skipped++;
46 items.Add(Item(remote.Name, "", "", "", "skipped", match.Reason));
47 continue;
48 }
49
50 var forgeSlug = match.ForgeSlug;
51 var fullName = $"{org.Slug}/{ForgeSlug.Normalize(forgeSlug)}";
52 var visibility = visibilityService.ResolveVisibility(
53 request.VisibilityPolicy,
54 match.RuleVisibility,
55 remote.IsPrivate);
56 var visibilitySlug = visibility.ToSlug();
57
58 if (request.DryRun)
59 {
60 items.Add(Item(remote.Name, fullName, match.CatalogPath, visibilitySlug, "planned", match.Reason));
61 continue;
62 }
63
64 try
65 {
66 if (repository.RepoNameExists(fullName))
67 {
68 if (!request.UpdateExisting)
69 {
70 skipped++;
71 items.Add(Item(remote.Name, fullName, match.CatalogPath, visibilitySlug, "skipped", "already exists"));
72 continue;
73 }
74
75 repository.EnsureRepoGroupPath(org.Id, match.CatalogPath);
76 repository.AssignRepoToCatalogGroup(fullName, org.Id, match.CatalogPath);
77 imported++;
78 items.Add(Item(remote.Name, fullName, match.CatalogPath, visibilitySlug, "updated", "catalog group refreshed"));
79 continue;
80 }
81
82 repository.EnsureRepoGroupPath(org.Id, match.CatalogPath);
83 var groupIdForInsert = repository.ResolveRepoGroupIdByPath(org.Id, match.CatalogPath);
84
85 if (!request.SkipMirror)
86 git.MirrorCloneFromRemote(fullName, remote.CloneUrl, gitHubToken);
87 else
88 git.CreateBareRepository(fullName);
89
90 repository.InsertRepo(
91 fullName,
92 $"Imported from GitHub {externalSourceLabel}/{remote.Name}",
93 DriveMode.HumanDriven,
94 groupIdForInsert,
95 org.Id,
96 visibility);
97
98 imported++;
99 items.Add(Item(remote.Name, fullName, match.CatalogPath, visibilitySlug, "imported", match.Reason));
100 }
101 catch (Exception ex)
102 {
103 failed++;
104 items.Add(Item(remote.Name, fullName, match.CatalogPath, visibilitySlug, "failed", ex.Message));
105 }
106 }
107
108 return new ForgeRepositoryImportResponse(
109 org.Slug,
110 externalSourceLabel,
111 items.Count,
112 imported,
113 skipped,
114 failed,
115 items);
116 }
117
118 private static ForgeRepositoryImportItemResult Item(
119 string externalName,
120 string forgeName,
121 string catalogPath,
122 string visibility,
123 string action,
124 string? detail) =>
125 new(externalName, forgeName, catalogPath, visibility, action, detail);
126}
127
View only · write via MCP/CIDE