Forge
csharp3407750f
1using AgentForge.Abstractions;
2using AgentForge.Data;
3using AgentForge.Models;
4using AgentForge.Options;
5using AgentForge.Plugin.RepositoryImport.GitHub.Core;
6using AgentForge.Plugin.Sdk;
7using Microsoft.Extensions.Options;
8
9namespace AgentForge.Plugin.Org.Import.GitHub;
10
11internal sealed class GitHubOrgMembersImporter(
12 GitHubRepositoryClient gitHub,
13 ForgeRepository repository,
14 IOptions<ForgeOptions> options) : IForgeOrgImporter
15{
16 public string SourceId => "github";
17 public string PluginId => "org-import-github";
18
19 public async Task<ForgeOrgImportResponse> ImportMembersAsync(
20 ForgeOrgImportRequest request,
21 CancellationToken cancellationToken)
22 {
23 var forgeOrgSlug = request.ForgeOrg.Trim();
24 var org = repository.GetOrgBySlug(forgeOrgSlug)
25 ?? throw new ForgeApiException(404, $"Organization '{forgeOrgSlug}' not found.");
26
27 var externalOrg = string.IsNullOrWhiteSpace(request.ExternalOrg)
28 ? forgeOrgSlug
29 : request.ExternalOrg.Trim();
30
31 var token = options.Value.GitHubImportToken;
32 if (string.IsNullOrWhiteSpace(token) && !request.DryRun)
33 throw new ForgeApiException(503, "FORGE_GITHUB_IMPORT_TOKEN is not configured.");
34
35 var defaultRole = ForgeOrgRoleExtensions.TryParseSlug(request.DefaultRole, out var parsedRole)
36 ? parsedRole
37 : ForgeOrgRole.Member;
38
39 IReadOnlyList<GitHubOrgMemberInfo> members = request.DryRun && string.IsNullOrWhiteSpace(token)
40 ? []
41 : await gitHub.ListOrgMembersAsync(externalOrg, token, cancellationToken).ConfigureAwait(false);
42
43 var items = new List<ForgeOrgImportMemberItem>();
44 var imported = 0;
45 var skipped = 0;
46 var failed = 0;
47
48 foreach (var member in members.OrderBy(m => m.Login, StringComparer.OrdinalIgnoreCase))
49 {
50 if (request.DryRun)
51 {
52 items.Add(new ForgeOrgImportMemberItem(member.Login, "planned", "dry run"));
53 continue;
54 }
55
56 try
57 {
58 var identity = repository.UpsertIdentity("github", member.Id, member.Login, member.Login);
59 if (repository.IsOrgMember(org.Id, identity.Id))
60 {
61 skipped++;
62 items.Add(new ForgeOrgImportMemberItem(member.Login, "skipped", "already member"));
63 continue;
64 }
65
66 repository.InsertOrgMember(org.Id, identity.Id, defaultRole);
67 imported++;
68 items.Add(new ForgeOrgImportMemberItem(member.Login, "imported", defaultRole.ToSlug()));
69 }
70 catch (Exception ex)
71 {
72 failed++;
73 items.Add(new ForgeOrgImportMemberItem(member.Login, "failed", ex.Message));
74 }
75 }
76
77 return new ForgeOrgImportResponse(
78 forgeOrgSlug,
79 externalOrg,
80 items.Count,
81 imported,
82 skipped,
83 failed,
84 items);
85 }
86}
87
View only · write via MCP/CIDE