Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .changeset/remove-project-github-identity-override.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
"agentweaver": patch
---

Remove the inert project-level GitHub identity override endpoints and dead frontend calls left
over after #934 removed the runtime override behavior: the `ProjectGitHubIdentityService` and the
`GET`/`PUT /api/projects/{id}/github-identity` endpoints are deleted, along with the corresponding
`getProjectGitHubIdentity`/`setProjectGitHubIdentityOverride` frontend API calls and types. The
`GitHubSignIn` "switch account" UI is unchanged and now unconditionally uses the per-user default
linked GitHub identity API instead of branching on a project override.

Note: the `project_github_identity_overrides` DB table, its entity, and
`ProjectGitHubIdentityOverrideStore` are intentionally kept (not deleted) — they are expected to be
repurposed as a "workflow Copilot owner" store, since GitHub App installation tokens have no
Copilot entitlements and automation runs still need a human user's Copilot-entitled token.
2 changes: 0 additions & 2 deletions apps/Agentweaver.Api/Auth/LinkedGitHubAccountService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ public sealed class LinkedGitHubAccountService(
GitHubOAuthRedirectService oauthService,
IGitHubTokenStore tokenStore,
IGitHubAccessTokenProvider accessTokenProvider,
ProjectGitHubIdentityOverrideStore overrideStore,
IGitHubCopilotEntitlementProbe entitlementProbe,
IHttpClientFactory httpClientFactory,
ILogger<LinkedGitHubAccountService> logger)
Expand Down Expand Up @@ -126,7 +125,6 @@ public async Task<bool> SetDefaultAsync(string entraUserId, string githubLogin,
if (!removed)
return (false, null);

await overrideStore.RemoveOverridesForLinkedLoginAsync(entraUserId, githubLogin, ct).ConfigureAwait(false);
var newDefault = await multi.GetDefaultLinkedIdentityAsync(entraUserId, ct).ConfigureAwait(false);
return (true, newDefault?.GitHubLogin);
}
Expand Down
64 changes: 0 additions & 64 deletions apps/Agentweaver.Api/Auth/ProjectGitHubIdentityService.cs

This file was deleted.

17 changes: 0 additions & 17 deletions apps/Agentweaver.Api/Contracts/Dtos.cs
Original file line number Diff line number Diff line change
Expand Up @@ -765,23 +765,6 @@ public sealed record AccessibleGitHubRepositoryResponse
[JsonPropertyName("permission")] public required string Permission { get; init; }
}

public sealed record UpdateProjectGitHubIdentityRequest
{
[JsonPropertyName("github_login")] public string? GitHubLogin { get; init; }
}

public sealed record ProjectGitHubIdentityResponse
{
[JsonPropertyName("project_id")] public required string ProjectId { get; init; }
[JsonPropertyName("project_override_login")] public string? ProjectOverrideLogin { get; init; }
[JsonPropertyName("effective_login")] public string? EffectiveLogin { get; init; }
[JsonPropertyName("effective_avatar_url")] public string? EffectiveAvatarUrl { get; init; }
[JsonPropertyName("copilot_entitled")] public bool? CopilotEntitled { get; init; }
[JsonPropertyName("is_default")] public bool? IsDefault { get; init; }
[JsonPropertyName("linked_at")] public DateTimeOffset? LinkedAt { get; init; }
[JsonPropertyName("resolution_source")] public required string ResolutionSource { get; init; }
}

// -----------------------------------------------------------------------
// Casting
// -----------------------------------------------------------------------
Expand Down
87 changes: 0 additions & 87 deletions apps/Agentweaver.Api/Endpoints/ProjectGitHubIdentityEndpoints.cs

This file was deleted.

2 changes: 0 additions & 2 deletions apps/Agentweaver.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,6 @@
builder.Services.AddSingleton<EntraOAuthRedirectService>();
builder.Services.AddScoped<IGitHubCopilotEntitlementProbe, GitHubCopilotEntitlementProbe>();
builder.Services.AddScoped<ProjectGitHubIdentityOverrideStore>();
builder.Services.AddScoped<ProjectGitHubIdentityService>();
builder.Services.AddScoped<LinkedGitHubAccountService>();
builder.Services.AddSingleton<Agentweaver.Api.Webhooks.IGitHubWebhookProvisioningService,
Agentweaver.Api.Webhooks.GitHubWebhookProvisioningService>();
Expand Down Expand Up @@ -1072,7 +1071,6 @@ await memoryDb.Database.ExecuteSqlRawAsync("""

app.MapRunEndpoints();
app.MapProjectEndpoints();
app.MapProjectGitHubIdentityEndpoints();
app.MapProjectWorkspaceEndpoints();
app.MapSkillEndpoints();
app.MapBacklogEndpoints();
Expand Down
78 changes: 41 additions & 37 deletions apps/web/src/__tests__/GitHubSignIn.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ vi.mock('../api/apiClient', () => ({
apiClient: {
getAuthSession: vi.fn(),
listLinkedGitHubAccounts: vi.fn(),
getProjectGitHubIdentity: vi.fn(),
setDefaultLinkedGitHubAccount: vi.fn(),
setProjectGitHubIdentityOverride: vi.fn(),
signOutSession: vi.fn(),
beginLinkGitHubAccount: vi.fn(),
},
Expand Down Expand Up @@ -48,45 +46,51 @@ beforeEach(() => {
copilot_entitled: false,
},
] as never);
vi.mocked(apiClient.getProjectGitHubIdentity).mockResolvedValue({
project_id: 'proj-1',
project_override_login: null,
effective_login: 'octocat',
effective_avatar_url: 'https://example.com/octocat.png',
copilot_entitled: true,
is_default: true,
linked_at: '2026-08-10T00:00:00Z',
resolution_source: 'default',
} as never);
vi.mocked(apiClient.setProjectGitHubIdentityOverride).mockResolvedValue(undefined as never);
vi.mocked(apiClient.setDefaultLinkedGitHubAccount).mockResolvedValue(undefined as never);
vi.mocked(apiClient.beginLinkGitHubAccount).mockResolvedValue({
authorize_url: 'https://github.com/login/oauth/authorize?state=abc',
} as never);
});

describe('GitHubSignIn', () => {
it('shows current account and lets the user switch the current project account', async () => {
vi.mocked(apiClient.getProjectGitHubIdentity)
.mockResolvedValueOnce({
project_id: 'proj-1',
project_override_login: null,
effective_login: 'octocat',
effective_avatar_url: 'https://example.com/octocat.png',
copilot_entitled: true,
is_default: true,
linked_at: '2026-08-10T00:00:00Z',
resolution_source: 'default',
} as never)
.mockResolvedValueOnce({
project_id: 'proj-1',
project_override_login: 'altcat',
effective_login: 'altcat',
effective_avatar_url: 'https://example.com/altcat.png',
copilot_entitled: false,
is_default: false,
linked_at: '2026-08-10T00:00:00Z',
resolution_source: 'project_override',
} as never);
it('shows current account and lets the user switch the current linked account', async () => {
vi.mocked(apiClient.listLinkedGitHubAccounts)
.mockResolvedValueOnce([
{
login: 'octocat',
name: 'Octocat',
avatar_url: 'https://example.com/octocat.png',
type: 'user',
is_default: true,
copilot_entitled: true,
},
{
login: 'altcat',
name: 'Alt Cat',
avatar_url: 'https://example.com/altcat.png',
type: 'user',
is_default: false,
copilot_entitled: false,
},
] as never)
.mockResolvedValueOnce([
{
login: 'octocat',
name: 'Octocat',
avatar_url: 'https://example.com/octocat.png',
type: 'user',
is_default: false,
copilot_entitled: true,
},
{
login: 'altcat',
name: 'Alt Cat',
avatar_url: 'https://example.com/altcat.png',
type: 'user',
is_default: true,
copilot_entitled: false,
},
] as never);

render(
<AzureFluentProvider density="compact">
Expand All @@ -101,9 +105,9 @@ describe('GitHubSignIn', () => {
expect(screen.getByText('@octocat')).toBeDefined();
fireEvent.click(screen.getByRole('button', { name: /Alt Cat/ }));

await waitFor(() => expect(apiClient.setProjectGitHubIdentityOverride).toHaveBeenCalledWith('proj-1', 'altcat'));
await waitFor(() => expect(apiClient.setDefaultLinkedGitHubAccount).toHaveBeenCalledWith('altcat'));
expect(await screen.findByText('@altcat')).toBeDefined();
expect(apiClient.getProjectGitHubIdentity).toHaveBeenCalledTimes(2);
expect(apiClient.listLinkedGitHubAccounts).toHaveBeenCalledTimes(2);
});

it('starts the real link flow via beginLinkGitHubAccount when "Add account" is clicked', async () => {
Expand Down
Loading
Loading