Skip to content

Commit 6a66774

Browse files
alari76claude
andcommitted
fix: improve webhook setup robustness and add parseGitHubSlug tests
- PR Review modal now always shows result screen (fallback message if server returns no setup info), ensuring consistent UX - Extract parseGitHubSlug as a testable pure function with 8 unit tests covering SSH, HTTPS, dots in names, non-GitHub remotes, etc. - Fix regex to allow dots in repo names (e.g. my_repo.name) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent a8dfb2a commit 6a66774

File tree

3 files changed

+53
-6
lines changed

3 files changed

+53
-6
lines changed

server/workflow-routes.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/** Tests for parseGitHubSlug — verifies GitHub slug extraction from various remote URL formats. */
2+
import { describe, it, expect } from 'vitest'
3+
import { parseGitHubSlug } from './workflow-routes.js'
4+
5+
describe('parseGitHubSlug', () => {
6+
it('parses HTTPS URL with .git suffix', () => {
7+
expect(parseGitHubSlug('https://github.com/Multiplier-Labs/codekin.git')).toBe('Multiplier-Labs/codekin')
8+
})
9+
10+
it('parses HTTPS URL without .git suffix', () => {
11+
expect(parseGitHubSlug('https://github.com/owner/repo')).toBe('owner/repo')
12+
})
13+
14+
it('parses SSH URL', () => {
15+
expect(parseGitHubSlug('git@github.com:owner/repo.git')).toBe('owner/repo')
16+
})
17+
18+
it('parses SSH URL without .git suffix', () => {
19+
expect(parseGitHubSlug('git@github.com:owner/repo')).toBe('owner/repo')
20+
})
21+
22+
it('handles trailing whitespace/newline', () => {
23+
expect(parseGitHubSlug('git@github.com:owner/repo.git\n')).toBe('owner/repo')
24+
})
25+
26+
it('returns null for non-GitHub remotes', () => {
27+
expect(parseGitHubSlug('https://gitlab.com/owner/repo.git')).toBeNull()
28+
})
29+
30+
it('returns null for empty string', () => {
31+
expect(parseGitHubSlug('')).toBeNull()
32+
})
33+
34+
it('handles repos with hyphens, underscores, and dots', () => {
35+
expect(parseGitHubSlug('git@github.com:my-org/my_repo.name.git')).toBe('my-org/my_repo.name')
36+
})
37+
})

server/workflow-routes.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,24 @@ import { loadWebhookConfig, generateWebhookSecret, saveWebhookConfig } from './w
3131

3232
const execFileAsync = promisify(execFile)
3333

34+
/**
35+
* Parse a GitHub `owner/repo` slug from a git remote URL.
36+
* Supports SSH (git@github.com:owner/repo.git) and HTTPS (https://github.com/owner/repo.git).
37+
* Returns null for non-GitHub remotes.
38+
*/
39+
export function parseGitHubSlug(remoteUrl: string): string | null {
40+
const match = remoteUrl.trim().match(/github\.com[:/]([^/]+\/[^/]+?)(?:\.git)?$/)
41+
return match?.[1] ?? null
42+
}
43+
3444
/**
3545
* Derive the GitHub `owner/repo` slug from a local repo path
3646
* by parsing the git remote origin URL.
3747
*/
3848
async function getGitHubSlug(repoPath: string): Promise<string | null> {
3949
try {
4050
const { stdout } = await execFileAsync('git', ['-C', repoPath, 'remote', 'get-url', 'origin'], { timeout: 5000 })
41-
const url = stdout.trim()
42-
// Match SSH (git@github.com:owner/repo.git) or HTTPS (https://github.com/owner/repo.git)
43-
const match = url.match(/github\.com[:/]([^/]+\/[^/.]+?)(?:\.git)?$/)
44-
return match?.[1] ?? null
51+
return parseGitHubSlug(stdout)
4552
} catch {
4653
return null
4754
}

src/components/AddWorkflowModal.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -467,8 +467,11 @@ export function AddWorkflowModal({ token, onClose, onAdd }: Props) {
467467
model: form.model || undefined,
468468
provider: form.provider !== 'claude' ? form.provider : undefined,
469469
}, webhookUrl)
470-
if (isPrReview && setupResult) {
471-
setWebhookResult(setupResult)
470+
if (isPrReview) {
471+
setWebhookResult(setupResult ?? {
472+
status: 'failed',
473+
message: 'No webhook setup information returned. Please configure the GitHub webhook manually in Settings.',
474+
})
472475
} else {
473476
onClose()
474477
}

0 commit comments

Comments
 (0)