Repository: presubmit/ai-reviewer
Pull request #40: Add: support for GitHub Enterprise
Author: login: ncryptedV1
Created: 2025-05-19T22:33:47Z | Merged: 2025-05-20T09:15:39Z



Diff:
diff --git a/.env.example b/.env.example
index 3520cb5..cf3e569 100644
--- a/.env.example
+++ b/.env.example
@@ -4,6 +4,10 @@
 # REQUIRED: Set this as your Github personal access token
 GITHUB_TOKEN=
 
+# OPTIONAL: GitHub Enterprise Server configuration
+# GITHUB_API_URL=https://github.example.com/api/v3 # API URL for GitHub Enterprise Server
+# GITHUB_SERVER_URL=https://github.example.com # Server URL for GitHub Enterprise Server
+
 # REQUIRED: LLM API
 LLM_API_KEY= # API key from your LLM provider (ie Anthropic, Google, OpenAI)
 LLM_MODEL= # Model name from your LLM provider (ie claude-3-5-sonnet-20241022, gemini-1.5-flash-latest, gpt-4o-mini)
@@ -22,4 +26,4 @@ GITHUB_COMMENT_ID=1111111 # update with your comment id
 # GITHUB_EVENT_ACTION=created
 # GITHUB_REPOSITORY=presubmit/ai-reviewer # update with your fork
 # GITHUB_PULL_REQUEST=1 # update with your pull request number
-# GITHUB_COMMENT_ID=1111111 # update with your comment id
\ No newline at end of file
+# GITHUB_COMMENT_ID=1111111 # update with your comment id
diff --git a/README.md b/README.md
index 0388681..fd40800 100644
--- a/README.md
+++ b/README.md
@@ -106,6 +106,28 @@ The action requires:
 - `LLM_API_KEY`: Your API key (added in step 1)
 - `LLM_MODEL`: Which LLM model to use. Make sure the model is [supported](https://github.com/presubmit/ai-reviewer/blob/main/src/ai.ts) and matches the `LLM_API_KEY`.
 
+### GitHub Enterprise Server Support
+
+If you're using GitHub Enterprise Server, you can configure the action to work with your instance by adding these environment variables:
+
+```yaml
+      - uses: presubmit/ai-reviewer@latest
+        env:
+          GITHUB_API_URL: "https://github.example.com/api/v3"
+          GITHUB_SERVER_URL: "https://github.example.com"
+```
+
+You can also configure these settings using input parameters:
+
+```yaml
+      - uses: presubmit/ai-reviewer@latest
+        with:
+          github_api_url: "https://github.example.com/api/v3"
+          github_server_url: "https://github.example.com"
+```
+
+Make sure to replace `https://github.example.com` with your actual GitHub Enterprise Server URL.
+
 <br/>
 
 ## Features
diff --git a/action.yml b/action.yml
index 9dd5e30..2c18326 100644
--- a/action.yml
+++ b/action.yml
@@ -14,4 +14,12 @@ inputs:
   style_guide_rules:
     required: false
     description: 'Custom style guide rules that will be enforced during review by generating critical comments'
-    default: ''
\ No newline at end of file
+    default: ''
+  github_api_url:
+    required: false
+    description: 'GitHub API URL for GitHub Enterprise Server (e.g., https://github.example.com/api/v3)'
+    default: 'https://api.github.com'
+  github_server_url:
+    required: false
+    description: 'GitHub Server URL for GitHub Enterprise Server (e.g., https://github.example.com)'
+    default: 'https://github.com'
diff --git a/src/__tests__/config.test.ts b/src/__tests__/config.test.ts
index c4dc7c0..4fb59cd 100644
--- a/src/__tests__/config.test.ts
+++ b/src/__tests__/config.test.ts
@@ -15,74 +15,115 @@ jest.mock('@actions/core', () => ({
 
 describe('Config', () => {
   const originalEnv = process.env;
-  
+
   beforeEach(() => {
     // Reset all mocks
     jest.clearAllMocks();
     mockGetInput.mockImplementation(() => '');
     mockGetMultilineInput.mockImplementation(() => []);
-    
+
     // Reset environment
     process.env = { ...originalEnv };
   });
-  
+
   afterAll(() => {
     process.env = originalEnv;
   });
-  
+
   test('throws error when GITHUB_TOKEN is not set', () => {
     process.env.GITHUB_TOKEN = '';
     process.env.LLM_API_KEY = 'test-api-key';
     process.env.LLM_MODEL = 'test-model';
-    
+
     expect(() => new Config()).toThrow('GITHUB_TOKEN is not set');
   });
-  
+
   test('throws error when LLM_API_KEY is not set', () => {
     process.env.GITHUB_TOKEN = 'test-token';
     process.env.LLM_API_KEY = '';
     process.env.LLM_MODEL = 'test-model';
-    
+
     expect(() => new Config()).toThrow('LLM_API_KEY is not set');
   });
-  
+
   test('throws error when LLM_MODEL is not set', () => {
     process.env.GITHUB_TOKEN = 'test-token';
     process.env.LLM_API_KEY = 'test-api-key';
     process.env.LLM_MODEL = '';
-    
+
     expect(() => new Config()).toThrow('LLM_MODEL is not set');
   });
-  
+
   test('loads style guide rules from action inputs', () => {
     process.env.GITHUB_TOKEN = 'test-token';
     process.env.LLM_API_KEY = 'test-api-key';
     process.env.LLM_MODEL = 'test-model';
     process.env.DEBUG = '';
-    
+
     const styleGuideRules = ['Rule 1', 'Rule 2', 'Rule 3'];
     mockGetMultilineInput.mockImplementation((name) => {
       if (name === 'style_guide_rules') return styleGuideRules;
       return [];
     });
-    
+
     const config = new Config();
     config.loadInputs();
-    
+
     expect(config.styleGuideRules).toBe(styleGuideRules.join('\n'));
   });
-  
-//   test('skips loading inputs when DEBUG is set', () => {
-//     process.env.GITHUB_TOKEN = 'test-token';
-//     process.env.LLM_API_KEY = 'test-api-key';
-//     process.env.LLM_MODEL = 'test-model';
-//     process.env.DEBUG = 'true';
-//     process.env.STYLE_GUIDE_RULES = 'Debug rule';
-    
-//     const config = new Config();
-//     config.loadInputs();
-    
-//     expect(config.styleGuideRules).toBe('Debug rule');
-//     expect(core.getMultilineInput).not.toHaveBeenCalled();
-//   });
-}); 
\ No newline at end of file
+
+  test('uses default GitHub URLs when not provided', () => {
+    process.env.GITHUB_TOKEN = 'test-token';
+    process.env.LLM_API_KEY = 'test-api-key';
+    process.env.LLM_MODEL = 'test-model';
+
+    const config = new Config();
+
+    expect(config.githubApiUrl).toBe('https://api.github.com');
+    expect(config.githubServerUrl).toBe('https://github.com');
+  });
+
+  test('loads GitHub Enterprise Server URLs from environment variables', () => {
+    process.env.GITHUB_TOKEN = 'test-token';
+    process.env.LLM_API_KEY = 'test-api-key';
+    process.env.LLM_MODEL = 'test-model';
+    process.env.GITHUB_API_URL = 'https://github.example.com/api/v3';
+    process.env.GITHUB_SERVER_URL = 'https://github.example.com';
+
+    const config = new Config();
+
+    expect(config.githubApiUrl).toBe('https://github.example.com/api/v3');
+    expect(config.githubServerUrl).toBe('https://github.example.com');
+  });
+
+  test('loads GitHub Enterprise Server URLs from action inputs', () => {
+    process.env.GITHUB_TOKEN = 'test-token';
+    process.env.LLM_API_KEY = 'test-api-key';
+    process.env.LLM_MODEL = 'test-model';
+
+    mockGetInput.mockImplementation((name) => {
+      if (name === 'github_api_url') return 'https://github.example.com/api/v3';
+      if (name === 'github_server_url') return 'https://github.example.com';
+      return '';
+    });
+
+    const config = new Config();
+
+    expect(config.githubApiUrl).toBe('https://github.example.com/api/v3');
+    expect(config.githubServerUrl).toBe('https://github.example.com');
+  });
+
+  //   test('skips loading inputs when DEBUG is set', () => {
+  //     process.env.GITHUB_TOKEN = 'test-token';
+  //     process.env.LLM_API_KEY = 'test-api-key';
+  //     process.env.LLM_MODEL = 'test-model';
+  //     process.env.DEBUG = 'true';
+  //     process.env.STYLE_GUIDE_RULES = 'Debug rule';
+
+  //     const config = new Config();
+  //     config.loadInputs();
+
+  //     expect(config.styleGuideRules).toBe('Debug rule');
+  //     expect(core.getMultilineInput).not.toHaveBeenCalled();
+  //   });
+});
diff --git a/src/__tests__/messages.test.ts b/src/__tests__/messages.test.ts
index d571903..829424e 100644
--- a/src/__tests__/messages.test.ts
+++ b/src/__tests__/messages.test.ts
@@ -1,6 +1,6 @@
-import { 
-  buildLoadingMessage, 
-  buildOverviewMessage, 
+import {
+  buildLoadingMessage,
+  buildOverviewMessage,
   buildReviewSummary,
   OVERVIEW_MESSAGE_SIGNATURE,
   PAYLOAD_TAG_OPEN,
@@ -28,6 +28,8 @@ jest.mock('../config', () => ({
     llmApiKey: 'mock-api-key',
     llmModel: 'mock-model',
     styleGuideRules: '',
+    githubApiUrl: 'https://api.github.com',
+    githubServerUrl: 'https://github.com',
     loadInputs: jest.fn()
   }
 }));
@@ -49,15 +51,15 @@ describe('Messages', () => {
       hunks: [{ startLine: 1, endLine: 3, diff: '@@ -0,0 +1,3 @@\n+new file\n+content\n+here' }]
     }
   ];
-  
+
   const mockCommits = [
     { sha: 'abc123', commit: { message: 'First commit' } },
     { sha: 'def456', commit: { message: 'Second commit' } }
   ];
-  
+
   test('buildLoadingMessage formats correctly', () => {
     const message = buildLoadingMessage('base-sha', mockCommits, mockFileDiffs);
-    
+
     expect(message).toContain('Analyzing changes in this PR');
     expect(message).toContain('base-sh');
     expect(message).toContain('abc123');
@@ -67,8 +69,9 @@ describe('Messages', () => {
     expect(message).toContain('src/test1.ts');
     expect(message).toContain('src/test2.ts');
     expect(message).toContain(OVERVIEW_MESSAGE_SIGNATURE);
+    expect(message).toContain('https://github.com/test-owner/test-repo/commit/');
   });
-  
+
   test('buildOverviewMessage formats correctly', () => {
     const mockSummary: PullRequestSummary = {
       title: 'Test PR',
@@ -79,9 +82,9 @@ describe('Messages', () => {
       ],
       type: ['ENHANCEMENT']
     };
-    
+
     const message = buildOverviewMessage(mockSummary, ['commit1', 'commit2']);
-    
+
     expect(message).toContain('PR Summary');
     expect(message).toContain('This is a test PR');
     expect(message).toContain('src/test1.ts');
@@ -93,7 +96,7 @@ describe('Messages', () => {
     expect(message).toContain(PAYLOAD_TAG_CLOSE);
     expect(message).toContain('"commits":["commit1","commit2"]');
   });
-  
+
   test('buildReviewSummary formats correctly with comments', () => {
     const mockActionableComments: AIComment[] = [
       {
@@ -107,7 +110,7 @@ describe('Messages', () => {
         critical: true
       }
     ];
-    
+
     const mockSkippedComments: AIComment[] = [
       {
         file: 'src/test2.ts',
@@ -120,7 +123,7 @@ describe('Messages', () => {
         critical: false
       }
     ];
-    
+
     const summary = buildReviewSummary(
       mockContext,
       mockFileDiffs,
@@ -128,7 +131,7 @@ describe('Messages', () => {
       mockActionableComments,
       mockSkippedComments
     );
-    
+
     expect(summary).toContain('Pull request needs attention');
     expect(summary).toContain('Review Summary');
     expect(summary).toContain('Commits Considered (2)');
@@ -139,8 +142,9 @@ describe('Messages', () => {
     expect(summary).toContain('possible bug: "Potential issue"');
     expect(summary).toContain('src/test2.ts [1-1]');
     expect(summary).toContain('style: "Style suggestion"');
+    expect(summary).toContain('https://github.com/test-owner/test-repo/commit/');
   });
-  
+
   test('buildReviewSummary formats correctly with no comments', () => {
     const summary = buildReviewSummary(
       mockContext,
@@ -153,5 +157,25 @@ describe('Messages', () => {
     expect(summary).toContain('LGTM!');
     expect(summary).toContain('Actionable Comments (0)');
     expect(summary).toContain('Skipped Comments (0)');
+    expect(summary).toContain('https://github.com/test-owner/test-repo/commit/');
+  });
+
+  test('buildLoadingMessage uses custom GitHub server URL', () => {
+    // Temporarily override the githubServerUrl
+    const originalServerUrl = config.githubServerUrl;
+    Object.defineProperty(config, 'githubServerUrl', {
+      value: 'https://github.example.com',
+      writable: true
+    });
+
+    const message = buildLoadingMessage('base-sha', mockCommits, mockFileDiffs);
+
+    expect(message).toContain('https://github.example.com/test-owner/test-repo/commit/');
+
+    // Restore the original value
+    Object.defineProperty(config, 'githubServerUrl', {
+      value: originalServerUrl,
+      writable: true
+    });
   });
-}); 
\ No newline at end of file
+});
diff --git a/src/__tests__/octokit.test.ts b/src/__tests__/octokit.test.ts
index 6057b52..f588083 100644
--- a/src/__tests__/octokit.test.ts
+++ b/src/__tests__/octokit.test.ts
@@ -12,7 +12,7 @@ jest.mock('@octokit/action', () => {
   Object.defineProperty(MockOctokit, 'plugin', {
     value: mockPlugin
   });
-  
+
   return {
     Octokit: MockOctokit
   };
@@ -37,4 +37,9 @@ describe('Octokit', () => {
   test('throws when no token is provided', () => {
     expect(() => initOctokit()).toThrow('GitHub token is required but was not provided');
   });
-}); 
\ No newline at end of file
+
+  test('initializes with a token and baseUrl', () => {
+    const octokit = initOctokit('test-token', 'https://github.example.com/api/v3');
+    expect(octokit).toBeDefined();
+  });
+});
diff --git a/src/__tests__/pull_request.test.ts b/src/__tests__/pull_request.test.ts
index 551f61a..773e9a5 100644
--- a/src/__tests__/pull_request.test.ts
+++ b/src/__tests__/pull_request.test.ts
@@ -15,6 +15,8 @@ jest.mock('../config', () => ({
     llmApiKey: 'mock-api-key',
     llmModel: 'mock-model',
     styleGuideRules: '',
+    githubApiUrl: 'https://api.github.com',
+    githubServerUrl: 'https://github.com',
     loadInputs: jest.fn()
   }
 }));
@@ -147,4 +149,4 @@ describe('Pull Request Handler', () => {
     expect(mockOctokit.rest.pulls.listCommits).not.toHaveBeenCalled();
     expect(runSummaryPrompt).not.toHaveBeenCalled();
   });
-}); 
\ No newline at end of file
+}); 
diff --git a/src/__tests__/pull_request_comment.test.ts b/src/__tests__/pull_request_comment.test.ts
index ea07a28..f20544a 100644
--- a/src/__tests__/pull_request_comment.test.ts
+++ b/src/__tests__/pull_request_comment.test.ts
@@ -22,6 +22,8 @@ jest.mock('../config', () => ({
     llmApiKey: 'mock-api-key',
     llmModel: 'mock-model',
     styleGuideRules: '',
+    githubApiUrl: 'https://api.github.com',
+    githubServerUrl: 'https://github.com',
     loadInputs: jest.fn()
   }
 }));
@@ -174,4 +176,4 @@ describe('Pull Request Comment Handler', () => {
     const mockOctokit = (initOctokit as jest.Mock).mock.results[0].value;
     expect(mockOctokit.rest.pulls.createReviewComment).not.toHaveBeenCalled();
   });
-}); 
\ No newline at end of file
+}); 
diff --git a/src/config.ts b/src/config.ts
index 48002a9..19e5455 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -5,6 +5,8 @@ export class Config {
   public llmModel: string | undefined;
   public githubToken: string | undefined;
   public styleGuideRules: string | undefined;
+  public githubApiUrl: string;
+  public githubServerUrl: string;
 
   constructor() {
     this.githubToken = process.env.GITHUB_TOKEN;
@@ -22,6 +24,12 @@ export class Config {
       throw new Error("LLM_MODEL is not set");
     }
 
+    // GitHub Enterprise Server support
+    this.githubApiUrl =
+      process.env.GITHUB_API_URL || getInput('github_api_url') || 'https://api.github.com';
+    this.githubServerUrl =
+      process.env.GITHUB_SERVER_URL || getInput('github_server_url') || 'https://github.com';
+
     if (!process.env.DEBUG) {
       return;
     }
@@ -59,13 +67,15 @@ if (process.env.NODE_ENV !== 'test') {
 }
 
 // Export the instance or a function to create one for tests
-export default process.env.NODE_ENV === 'test' 
-  ? { 
+export default process.env.NODE_ENV === 'test'
+  ? {
       // Default values for tests
       githubToken: 'mock-token',
       llmApiKey: 'mock-api-key',
       llmModel: 'mock-model',
       styleGuideRules: '',
+      githubApiUrl: 'https://api.github.com',
+      githubServerUrl: 'https://github.com',
       loadInputs: jest.fn()
-    } 
+    }
   : configInstance!;
diff --git a/src/context.ts b/src/context.ts
index 2904d9d..070e725 100644
--- a/src/context.ts
+++ b/src/context.ts
@@ -12,7 +12,8 @@ async function loadDebugContext(): Promise<Context> {
   if (!process.env.GITHUB_TOKEN) {
     throw new Error("GITHUB_TOKEN is not set");
   }
-  const octokit = getOctokit(process.env.GITHUB_TOKEN);
+  const baseUrl = process.env.GITHUB_API_URL || undefined;
+  const octokit = getOctokit(process.env.GITHUB_TOKEN, { baseUrl });
 
   const [owner, repo] = process.env.GITHUB_REPOSITORY?.split("/") || [];
 
diff --git a/src/messages.ts b/src/messages.ts
index c2b9ba9..f2fe913 100644
--- a/src/messages.ts
+++ b/src/messages.ts
@@ -2,6 +2,7 @@ import { context } from "@actions/github";
 import { FileDiff } from "./diff";
 import { AIComment, PullRequestSummary } from "./prompts";
 import { Context } from "@actions/github/lib/context";
+import config from "./config";
 
 export const OVERVIEW_MESSAGE_SIGNATURE =
   "\n<!-- presubmit.ai: overview message -->";
@@ -13,6 +14,17 @@ export const PAYLOAD_TAG_CLOSE = "\n-- presubmit.ai: payload -->";
 
 const PRESUBMIT_SIGNATURE = "--- \n_autogenerated by [presubmit.ai](https://github.com/presubmit/ai-reviewer)_";
 
+function getCommitUrl(
+  serverUrl: string,
+  owner: string,
+  repo: string,
+  sha: string
+): string {
+  // Remove trailing slash if present
+  const baseUrl = serverUrl.endsWith("/") ? serverUrl.slice(0, -1) : serverUrl;
+  return `${baseUrl}/${owner}/${repo}/commit/${sha}`;
+}
+
 export function buildLoadingMessage(
   baseCommit: string,
   commits: {
@@ -39,12 +51,12 @@ export function buildLoadingMessage(
   )}\`):\n`;
 
   for (const commit of commits.reverse()) {
-    message += `- [${commit.sha.slice(
-      0,
-      7
-    )}](https://github.com/${owner}/${repo}/commit/${commit.sha}): ${
-      commit.commit.message
-    }\n`;
+    message += `- [${commit.sha.slice(0, 7)}](${getCommitUrl(
+      config.githubServerUrl,
+      owner,
+      repo,
+      commit.sha
+    )}): ${commit.commit.message}\n`;
   }
 
   message += "\n\n</details>\n\n";
@@ -135,12 +147,12 @@ export function buildReviewSummary(
   // Commits section
   body += `<details>\n<summary>Commits Considered (${commits.length})</summary>\n\n`;
   for (const commit of commits) {
-    body += `- [${commit.sha.slice(
-      0,
-      7
-    )}](https://github.com/${owner}/${repo}/commit/${commit.sha}): ${
-      commit.commit.message
-    }\n`;
+    body += `- [${commit.sha.slice(0, 7)}](${getCommitUrl(
+      config.githubServerUrl,
+      owner,
+      repo,
+      commit.sha
+    )}): ${commit.commit.message}\n`;
   }
   body += "\n</details>\n\n";
 
diff --git a/src/octokit.ts b/src/octokit.ts
index c40db62..7a65d35 100644
--- a/src/octokit.ts
+++ b/src/octokit.ts
@@ -10,13 +10,14 @@ interface ThrottleOptions {
 
 const SmartOctokit = Octokit.plugin(throttling, retry);
 
-export function initOctokit(token?: string): Octokit {
+export function initOctokit(token?: string, apiBaseUrl?: string): Octokit {
   if (!token) {
     throw new Error("GitHub token is required but was not provided");
   }
 
   return new SmartOctokit({
     auth: token,
+    baseUrl: apiBaseUrl,
     throttle: {
       onRateLimit: (
         retryAfter: number,
diff --git a/src/pull_request.ts b/src/pull_request.ts
index 0dac4aa..16b1365 100644
--- a/src/pull_request.ts
+++ b/src/pull_request.ts
@@ -32,7 +32,7 @@ export async function handlePullRequest() {
     return;
   }
 
-  const octokit = initOctokit(config.githubToken);
+  const octokit = initOctokit(config.githubToken, config.githubApiUrl);
 
   if (shouldIgnorePullRequest(pull_request)) {
     return;
diff --git a/src/pull_request_comment.ts b/src/pull_request_comment.ts
index dbf5e27..1ce4033 100644
--- a/src/pull_request_comment.ts
+++ b/src/pull_request_comment.ts
@@ -36,7 +36,7 @@ export async function handlePullRequestComment() {
     return;
   }
 
-  const octokit = initOctokit(config.githubToken);
+  const octokit = initOctokit(config.githubToken, config.githubApiUrl);
 
   // Fetch comment thread
   const commentThread = await getCommentThread(octokit, {


Reviews:
user: login: github-actions[bot]
body: ✅ **LGTM!**

### Review Summary

<details>
<summary>Commits Considered (2)</summary>

- [4f7924b](https://github.com/presubmit/ai-reviewer/commit/4f7924b4313a9ccd3518d52d63e161ed9e22ef0a): fix: tests
- [7184e7d](https://github.com/presubmit/ai-reviewer/commit/7184e7dfe53983589f78c9e4e0cdc3f8f8f3477b): add: cusotm github server & api URL for GHES support

</details>

<details>
<summary>Files Processed (14)</summary>

- .env.example _(2 hunks)_
- README.md _(1 hunk)_
- action.yml _(1 hunk)_
- src/__tests__/config.test.ts _(1 hunk)_
- src/__tests__/messages.test.ts _(11 hunks)_
- src/__tests__/octokit.test.ts _(2 hunks)_
- src/__tests__/pull_request.test.ts _(2 hunks)_
- src/__tests__/pull_request_comment.test.ts _(2 hunks)_
- src/config.ts _(3 hunks)_
- src/context.ts _(1 hunk)_
- src/messages.ts _(4 hunks)_
- src/octokit.ts _(1 hunk)_
- src/pull_request.ts _(1 hunk)_
- src/pull_request_comment.ts _(1 hunk)_

</details>

<details>
<summary>Actionable Comments (0)</summary>


</details>

<details>
<summary>Skipped Comments (2)</summary>

- <details>
  <summary>src/messages.ts [17-26]</summary>

  > enhancement: "Add URL validation for server URL parameter"
  </details>
- <details>
  <summary>src/config.ts [28-31]</summary>

  > maintainability: "Use constants for default GitHub URLs"
  </details>
</details>


state: COMMENTED
author_association: NONE
submitted_at: 2025-05-19T22:34:31Z
user: login: bstanga
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T08:55:31Z
user: login: ncryptedV1
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T09:13:26Z
user: login: github-actions[bot]
body: 🚨 **Pull request needs attention.**

### Review Summary

<details>
<summary>Commits Considered (1)</summary>

- [a541935](https://github.com/presubmit/ai-reviewer/commit/a541935731baee838dc2862ffb8341e041b3cb1c): refactor: baseUrl -> apiBaseUrl

</details>

<details>
<summary>Files Processed (3)</summary>

- src/__tests__/pull_request.test.ts _(2 hunks)_
- src/__tests__/pull_request_comment.test.ts _(2 hunks)_
- src/octokit.ts _(1 hunk)_

</details>

<details>
<summary>Actionable Comments (1)</summary>

- <details>
  <summary>src/octokit.ts [13-13]</summary>

  > possible issue: "Add URL validation for API base URL"
  </details>

</details>

<details>
<summary>Skipped Comments (1)</summary>

- <details>
  <summary>src/__tests__/pull_request.test.ts [152-153]</summary>

  > best practice: "Missing newline at end of file"
  </details>
</details>


state: COMMENTED
author_association: NONE
submitted_at: 2025-05-20T09:13:57Z

Review comments:
diff_hunk: @@ -10,13 +10,14 @@ interface ThrottleOptions {
 
 const SmartOctokit = Octokit.plugin(throttling, retry);
 
-export function initOctokit(token?: string): Octokit {
+export function initOctokit(token?: string, baseUrl?: string): Octokit {
path: src/octokit.ts
user: login: bstanga
body: nit: can you name this apiBaseUrl to be more specific this is about the API url?
created_at: 2025-05-20T08:52:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -10,13 +10,14 @@ interface ThrottleOptions {
 
 const SmartOctokit = Octokit.plugin(throttling, retry);
 
-export function initOctokit(token?: string): Octokit {
+export function initOctokit(token?: string, baseUrl?: string): Octokit {
path: src/octokit.ts
user: login: ncryptedV1
body: good catch! done
created_at: 2025-05-20T09:13:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -10,13 +10,14 @@ interface ThrottleOptions {
 
 const SmartOctokit = Octokit.plugin(throttling, retry);
 
-export function initOctokit(token?: string): Octokit {
+export function initOctokit(token?: string, apiBaseUrl?: string): Octokit {
path: src/octokit.ts
user: login: github-actions[bot]
body: Consider adding input validation for `apiBaseUrl` parameter to ensure it's a valid URL. Invalid URLs could cause runtime errors when making API calls. You can use `new URL()` constructor or a URL validation library to verify the input.


<!-- presubmit.ai: comment -->
created_at: 2025-05-20T09:13:56Z
author_association: NONE

PR comments:
user: login: github-actions[bot]
created_at: 2025-05-19T22:33:57Z
body: ## PR Summary

Added support for GitHub Enterprise Server (GHES) by implementing custom GitHub API and server URL configurations. This allows users to specify their enterprise instance URLs through environment variables or action inputs. Updated tests, documentation, and internal URL handling to support GHES deployments.

### Changes

| File | Summary |
|:----------|:---------------|
| `.env.example` | Added optional environment variables `GITHUB_API_URL` and `GITHUB_SERVER_URL` for GitHub Enterprise Server configuration |
| `README.md` | Added new section documenting GitHub Enterprise Server support with examples for both environment variables and action input configuration methods |
| `action.yml` | Added two new optional input parameters: `github_api_url` and `github_server_url` for configuring GitHub Enterprise Server URLs |
| `src/config.ts` | Added `githubApiUrl` and `githubServerUrl` properties to Config class with support for environment variables and action inputs |
| `src/context.ts` | Modified debug context to support custom GitHub API base URL from environment variables |
| `src/messages.ts` | Added `getCommitUrl` function to generate correct commit URLs based on configured GitHub server URL |
| `src/octokit.ts` | Modified `initOctokit` to accept custom API base URL parameter for GHES support |
| `src/pull_request.ts` | Modified pull request handler to pass custom API URL when initializing Octokit |
| `src/pull_request_comment.ts` | Modified pull request comment handler to pass custom API URL when initializing Octokit |
| `src/__tests__/config.test.ts` | Added new test cases for GitHub Enterprise Server URL configurations from both environment variables and action inputs |
| `src/__tests__/messages.test.ts` | Added tests for custom GitHub server URL handling in message generation and updated mock configurations |
| `src/__tests__/octokit.test.ts` | Added test case for Octokit initialization with custom API base URL |
| `src/__tests__/pull_request.test.ts` | Added GitHub URL configurations to mock config object |
| `src/__tests__/pull_request_comment.test.ts` | Added GitHub URL configurations to mock config object |
--- 
_autogenerated by [presubmit.ai](https://github.com/presubmit/ai-reviewer)_
<!-- presubmit.ai: overview message -->
<!-- presubmit.ai: payload --{"commits":["7184e7dfe53983589f78c9e4e0cdc3f8f8f3477b","4f7924b4313a9ccd3518d52d63e161ed9e22ef0a","a541935731baee838dc2862ffb8341e041b3cb1c"]}
-- presubmit.ai: payload -->
author_association: NONE
user: login: bstanga
created_at: 2025-05-20T08:55:09Z
body: This looks great! Thanks for adding this.

I added a small nit comment about readability, but otherwise LGTM. I'll approve and draft a new release after that comment is resolved.
author_association: CONTRIBUTOR
user: login: bstanga
created_at: 2025-05-29T06:47:42Z
body: sorry for the delay. drafting a new release now
author_association: CONTRIBUTOR