Skip to content

feat(web): add /api/blame endpoint#1158

Merged
brendan-kellam merged 3 commits intomainfrom
brendan/file-blame-api
Apr 29, 2026
Merged

feat(web): add /api/blame endpoint#1158
brendan-kellam merged 3 commits intomainfrom
brendan/file-blame-api

Conversation

@brendan-kellam
Copy link
Copy Markdown
Contributor

@brendan-kellam brendan-kellam commented Apr 29, 2026

Summary

  • Adds a getFileBlame git helper (packages/web/src/features/git/getFileBlameApi.ts) that runs git blame --porcelain and parses the output into contiguous line ranges + deduplicated commit metadata.
  • Exposes it as a public REST endpoint GET /api/blame, mirroring the shape and conventions of /api/source.
  • Registers OpenAPI schemas (PublicFileBlameRequest, PublicFileBlameResponse) and adds the endpoint to the API Reference nav.
  • Adds a new user.fetched_file_blame audit event (mirroring user.fetched_file_source).

Response shape

{
  ranges: Array<{ hash: string; startLine: number; lineCount: number }>;
  commits: Record<hash, {
    hash: string;
    date: string;        // ISO 8601
    message: string;
    authorName: string;
    authorEmail: string;
    previous?: { hash: string; path: string };  // points to next step backwards in blame walk
  }>;
}

Range-based (not per-line) so payload size scales with the number of contiguous attribution groups, not file length. Field naming aligns with the existing commitSchema (hash, date, message).

The previous pointer is captured from porcelain so we can support a "blame at previous commit" reblame UX later.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added public GET /api/blame endpoint to retrieve per-line blame with commit-attributed ranges and deduplicated commit metadata; supports optional ref for history traversal.
  • Documentation

    • API reference and navigation updated to include the blame endpoint; changelog and docs updated.
  • Audit

    • Audit event emitted when file blame is fetched (user.fetched_file_blame).

Adds a new git helper (`getFileBlame`) that runs `git blame --porcelain`
and parses the output into contiguous line ranges plus deduplicated commit
metadata (hash, date, message, author, optional `previous` pointer for
walking back through history).

Exposes it as a new public REST endpoint `GET /api/blame`, mirroring the
shape of `/api/source`. Registers OpenAPI schemas, updates the API
Reference nav, and adds a `user.fetched_file_blame` audit event.

API-only; the CodeMirror gutter UI is a follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mintlify
Copy link
Copy Markdown

mintlify Bot commented Apr 29, 2026

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
sourcebot 🟢 Ready View Preview Apr 29, 2026, 8:31 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 29, 2026

Walkthrough

Adds a new public GET /api/blame endpoint with request/response schemas, server route handler, git-porcelain blame parser, audit logging integration, OpenAPI registration, and documentation updates.

Changes

Cohort / File(s) Summary
Documentation
CHANGELOG.md, docs/docs.json, docs/docs/configuration/audit-logs.mdx
Documented the new /api/blame endpoint and added user.fetched_file_blame to audit action types and API nav.
API Route
packages/web/src/app/api/(server)/blame/route.ts
New Next.js route handler GET /api/blame that validates query params and delegates to getFileBlame, returning standardized ServiceError or JSON response.
Core Blame Logic
packages/web/src/features/git/getFileBlameApi.ts
Implements getFileBlame, repository/ref/path validation, executes git blame --porcelain, maps common failures to service errors, parses porcelain into ranges and commits, and emits audit events when applicable.
Schemas
packages/web/src/features/git/schemas.ts, packages/web/src/openapi/publicApiSchemas.ts
Adds Zod schemas for file blame request/response, blame ranges, commit metadata (with optional previous), and exposes OpenAPI-wrapped public schemas.
OpenAPI Registration
docs/api-reference/sourcebot-public.openapi.json, packages/web/src/openapi/publicApiDocument.ts
Registers GET /api/blame in the public OpenAPI document using the new request/response schemas and standard error responses.
Feature Export
packages/web/src/features/git/index.ts
Exports the new blame API module from the git feature barrel.

Sequence Diagram

sequenceDiagram
    participant Client
    participant RouteHandler as Route Handler<br/>(/api/blame)
    participant Validator as Parameter<br/>Validator
    participant GitService as Git Service<br/>(getFileBlame)
    participant GitCmd as Git Command<br/>(git blame)
    participant Parser as Porcelain<br/>Parser

    Client->>RouteHandler: GET /api/blame?repo=X&path=Y&ref=Z
    RouteHandler->>Validator: Parse & validate query params
    Validator-->>RouteHandler: Valid params or error
    alt Invalid Parameters
        RouteHandler-->>Client: 400 Error Response
    else Valid Parameters
        RouteHandler->>GitService: getFileBlame(repo, path, ref)
        GitService->>GitService: Validate repo, path & ref
        GitService->>GitService: Emit audit event (if user)
        GitService->>GitCmd: Execute git blame --porcelain
        GitCmd-->>GitService: Porcelain output or error
        alt Command Failed
            GitService-->>RouteHandler: ServiceError (fileNotFound / unresolvedGitRef / unexpectedError)
            RouteHandler-->>Client: 404/500 Error Response
        else Command Succeeded
            GitService->>Parser: parsePorcelainBlame(output)
            Parser-->>GitService: ranges + commits
            GitService-->>RouteHandler: FileBlameResponse (200)
            RouteHandler-->>Client: 200 JSON Response
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

  • [FR] Git blame support #650: Implements the server- and API-level Git blame feature (porcelain parsing, response schemas, GET /api/blame) described by that request.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change—adding a new /api/blame endpoint—which aligns with the core feature being introduced across multiple files and the PR objectives.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch brendan/file-blame-api

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 6/8 reviews remaining, refill in 14 minutes and 57 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/web/src/features/git/getFileBlameApi.ts`:
- Around line 170-180: The current catch block directly returns internal
git/parser text via unexpectedError(errorMessage), exposing sensitive internals;
instead capture the thrown error from git.raw in a local variable, log the full
error details server-side (e.g., logger.error with error and stack) and return a
generic API-facing error message (e.g., unexpectedError('An internal error
occurred while processing the blame request')); update both the git.raw catch
and the later parse-error paths (the branches that now call
unexpectedError(errorMessage)) to follow this pattern and keep
fileNotFound(filePath, repoName) and unresolvedGitRef(gitRef) behavior
unchanged.
- Around line 140-146: The call to getAuditService().createAudit({...}) in the
async handler should be awaited to avoid unhandled promise rejections and ensure
the audit completes; update the code in the function using withOptionalAuth to
change the fire-and-forget createAudit invocation to await
getAuditService().createAudit({...}) so the Promise<Audit|null> is properly
handled.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 88abb785-1145-462f-ae97-1f92a1109d56

📥 Commits

Reviewing files that changed from the base of the PR and between b537325 and c028f58.

📒 Files selected for processing (10)
  • CHANGELOG.md
  • docs/api-reference/sourcebot-public.openapi.json
  • docs/docs.json
  • docs/docs/configuration/audit-logs.mdx
  • packages/web/src/app/api/(server)/blame/route.ts
  • packages/web/src/features/git/getFileBlameApi.ts
  • packages/web/src/features/git/index.ts
  • packages/web/src/features/git/schemas.ts
  • packages/web/src/openapi/publicApiDocument.ts
  • packages/web/src/openapi/publicApiSchemas.ts

Comment thread packages/web/src/features/git/getFileBlameApi.ts Outdated
Comment thread packages/web/src/features/git/getFileBlameApi.ts
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
packages/web/src/features/git/getFileBlameApi.ts (1)

170-180: ⚠️ Potential issue | 🟠 Major

Avoid returning raw git/parser errors to public clients.

On Line 180 and Line 187, internal error text is surfaced directly through unexpectedError(...). That leaks backend details; log server-side and return generic client-facing messages.

Proposed hardening diff
 import { getRepoPath } from '@sourcebot/shared';
+import { createLogger } from '@sourcebot/shared';
@@
 type CommitMeta = FileBlameResponse['commits'][string];
+const logger = createLogger('getFileBlameApi');
@@
             } catch (error: unknown) {
                 const errorMessage = error instanceof Error ? error.message : String(error);
                 if (errorMessage.includes('no such path') || errorMessage.includes('does not exist') || errorMessage.includes('fatal: path') || errorMessage.includes('no such file')) {
                     return fileNotFound(filePath, repoName);
                 }
                 if (errorMessage.includes('unknown revision') || errorMessage.includes('bad revision') || errorMessage.includes('invalid object name')) {
                     return unresolvedGitRef(gitRef);
                 }
-                return unexpectedError(errorMessage);
+                logger.error({ error: errorMessage, repoName, filePath, gitRef }, 'git blame failed');
+                return unexpectedError('Failed to compute file blame.');
             }
@@
             } catch (error: unknown) {
                 const errorMessage = error instanceof Error ? error.message : String(error);
-                return unexpectedError(`Failed to parse git blame output: ${errorMessage}`);
+                logger.error({ error: errorMessage, repoName, filePath, gitRef }, 'Failed to parse git blame output');
+                return unexpectedError('Failed to parse file blame output.');
             }

Also applies to: 183-188

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web/src/features/git/getFileBlameApi.ts` around lines 170 - 180, The
handler around the git.raw(['blame'...]) call currently passes raw git/parser
messages into unexpectedError(errorMessage) which leaks internals; instead,
capture the error (error / errorMessage), write the full details to server logs
(e.g., logger.error or processLogger.error) and call unexpectedError with a
generic, client-safe message like "Unable to process file blame" or "Internal
error while retrieving blame" so clients don't receive backend internals; keep
existing branches that return fileNotFound(filePath, repoName) and
unresolvedGitRef(gitRef) for known conditions, only replace the final return
unexpectedError(errorMessage) with a logged internal error plus a generic
unexpectedError call.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@packages/web/src/features/git/getFileBlameApi.ts`:
- Around line 170-180: The handler around the git.raw(['blame'...]) call
currently passes raw git/parser messages into unexpectedError(errorMessage)
which leaks internals; instead, capture the error (error / errorMessage), write
the full details to server logs (e.g., logger.error or processLogger.error) and
call unexpectedError with a generic, client-safe message like "Unable to process
file blame" or "Internal error while retrieving blame" so clients don't receive
backend internals; keep existing branches that return fileNotFound(filePath,
repoName) and unresolvedGitRef(gitRef) for known conditions, only replace the
final return unexpectedError(errorMessage) with a logged internal error plus a
generic unexpectedError call.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e6e0609e-9bdf-49d3-8788-678cfcc8dc1e

📥 Commits

Reviewing files that changed from the base of the PR and between c028f58 and 641779b.

📒 Files selected for processing (1)
  • packages/web/src/features/git/getFileBlameApi.ts

@brendan-kellam brendan-kellam merged commit cbf50e7 into main Apr 29, 2026
10 checks passed
@brendan-kellam brendan-kellam deleted the brendan/file-blame-api branch April 29, 2026 20:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant