Skip to content
Merged
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
142 changes: 142 additions & 0 deletions specs/future/contributors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# GitHub Contributors Feature

## Overview

Add contributor avatars with profile links to documentation pages using git + one GitHub API call.

## Approach (Hybrid)

1. One-time call to GitHub's `/repos/{owner}/{repo}/contributors` endpoint → cache email→user mapping
2. Use `git log` locally to get contributor emails per file
3. Match emails to cached GitHub usernames
4. Construct URLs: `https://github.com/{username}.png` (avatar), `https://github.com/{username}` (profile)

## Core Model

**New file: `src/CloudNimble.DotNetDocs.Core/Contributor.cs`**

```csharp
public class Contributor
{
public string Name { get; set; }
public string Email { get; set; }
public string? Username { get; set; }
public string? AvatarUrl { get; set; }
public string? ProfileUrl { get; set; }
}
```

**Modify: `src/CloudNimble.DotNetDocs.Core/DocEntity.cs`**

```csharp
public List<Contributor>? Contributors { get; set; }
```

**Modify: `src/CloudNimble.DotNetDocs.Core/ProjectContext.cs`**

```csharp
public bool ContributorsEnabled { get; set; } = false;
```

## Implementation

### 1. GitHelper (in Core for reuse by future GitLab/AzureDevOps plugins)

**New file: `src/CloudNimble.DotNetDocs.Core/GitHelper.cs`**

```csharp
public static class GitHelper
{
// git log --format="%an|%ae" -- {filePath} | sort -u
public static List<(string Name, string Email)> GetFileContributors(string filePath);

// git remote get-url origin
public static string? GetRemoteUrl();

// Parse "https://github.com/Owner/Repo.git" → (Provider, Owner, Repo)
public static (string? Provider, string? Owner, string? Repo) ParseRemoteUrl(string url);
}
```

### 2. GitHubContributorEnricher (in GitHub plugin)

**New file: `src/CloudNimble.DotNetDocs.Plugins.GitHub/GitHubContributorEnricher.cs`**

Implements `IDocEnricher`:

```csharp
public class GitHubContributorEnricher : IDocEnricher
{
private Dictionary<string, Contributor>? _contributorCache; // email → Contributor

public async Task EnrichAsync(DocEntity entity)
{
if (entity is not DocAssembly assembly) return;

// 1. Build cache once: GET /repos/{owner}/{repo}/contributors
await BuildContributorCacheAsync();

// 2. Walk entity graph recursively
foreach (var ns in assembly.Namespaces)
foreach (var type in ns.Types)
EnrichType(type);
}

private void EnrichType(DocType type)
{
var contributors = new HashSet<Contributor>();

// Source code file
var sourcePath = type.Symbol?.Locations.FirstOrDefault()?.SourceTree?.FilePath;
if (sourcePath is not null)
AddContributorsFromFile(sourcePath, contributors);

// Conceptual files (Usage.md, Examples.md, etc.)
// ... get paths from ProjectContext.GetFullConceptualPath() + type path

type.Contributors = contributors.ToList();
}
}
```

### 3. Non-API Docs (Guides, etc.)

**TODO: Define rendering approach**

For non-generated pages (guides, tutorials, etc.), contributors need to be collected on-the-fly and rendered. Options:

- **Mintlify Component**: Create a `<Contributors />` React component
- **Markdown Injection**: Renderer injects contributor HTML directly into .mdx files

Example output:

```html
<div class="contributors">
<a href="https://github.com/username"><img src="https://github.com/username.png" alt="username" /></a>
...
</div>
```

## Files to Create/Modify

| File | Action |
|------|--------|
| `src/CloudNimble.DotNetDocs.Core/Contributor.cs` | Create |
| `src/CloudNimble.DotNetDocs.Core/GitHelper.cs` | Create |
| `src/CloudNimble.DotNetDocs.Core/DocEntity.cs` | Add `Contributors` property |
| `src/CloudNimble.DotNetDocs.Core/ProjectContext.cs` | Add `ContributorsEnabled` flag |
| `src/CloudNimble.DotNetDocs.Plugins.GitHub/GitHubContributorEnricher.cs` | Create |

## Open Items

- [ ] Define Mintlify vs Markdown rendering approach
- [ ] Determine where contributor section appears in rendered output (top, bottom, sidebar?)
- [ ] Handle case where GitHub API is unavailable (graceful degradation to name/email only?)
- [ ] Consider caching the contributor cache to disk to avoid API calls on every build

## Verification

1. `dotnet build src -c Debug` - ensure it compiles
2. Run against a project with git history
3. Debug/inspect that `DocType.Contributors` is populated
4. Check rendered .mdx output includes contributor avatars
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="DotNetDocs.Sdk/1.2.0">
<Project Sdk="DotNetDocs.Sdk/1.3.0">

<PropertyGroup>
<DocumentationType>Mintlify</DocumentationType>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="DotNetDocs.Sdk/1.2.0">
<Project Sdk="DotNetDocs.Sdk/1.3.0">
<PropertyGroup>
<DocumentationType>Mintlify</DocumentationType>
<GenerateMintlifyDocs>true</GenerateMintlifyDocs>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

<ItemGroup>
<!-- MSBuild task dependencies -->
<PackageReference Include="EasyAF.MSBuild" Version="4.*-*" />
<PackageReference Include="EasyAF.MSBuild" Version="4.*" />
<PackageReference Include="System.Collections.Immutable" Version="9.*" PrivateAssets="all" />

<!-- Microsoft.CodeAnalysis.CSharp.Workspaces needs to be available for the task -->
Expand Down
Loading