(sessionData: SessionResource)
| 372 | * @returns Validation result with status and repo information |
| 373 | */ |
| 374 | export async function validateSessionRepository(sessionData: SessionResource): Promise<RepoValidationResult> { |
| 375 | const currentParsed = await detectCurrentRepositoryWithHost(); |
| 376 | const currentRepo = currentParsed ? `${currentParsed.owner}/${currentParsed.name}` : null; |
| 377 | const gitSource = sessionData.session_context.sources.find((source): source is GitSource => source.type === 'git_repository'); |
| 378 | if (!gitSource?.url) { |
| 379 | // Session has no repo requirement |
| 380 | logForDebugging(currentRepo ? 'Session has no associated repository, proceeding without validation' : 'Session has no repo requirement and not in git directory, proceeding'); |
| 381 | return { |
| 382 | status: 'no_repo_required' |
| 383 | }; |
| 384 | } |
| 385 | const sessionParsed = parseGitRemote(gitSource.url); |
| 386 | const sessionRepo = sessionParsed ? `${sessionParsed.owner}/${sessionParsed.name}` : parseGitHubRepository(gitSource.url); |
| 387 | if (!sessionRepo) { |
| 388 | return { |
| 389 | status: 'no_repo_required' |
| 390 | }; |
| 391 | } |
| 392 | logForDebugging(`Session is for repository: ${sessionRepo}, current repo: ${currentRepo ?? 'none'}`); |
| 393 | if (!currentRepo) { |
| 394 | // Not in a git repo, but session requires one |
| 395 | return { |
| 396 | status: 'not_in_repo', |
| 397 | sessionRepo, |
| 398 | sessionHost: sessionParsed?.host, |
| 399 | currentRepo: null |
| 400 | }; |
| 401 | } |
| 402 | |
| 403 | // Compare both owner/repo and host to avoid cross-instance mismatches. |
| 404 | // Strip ports before comparing hosts — SSH remotes omit the port while |
| 405 | // HTTPS remotes may include a non-standard port (e.g. ghe.corp.com:8443), |
| 406 | // which would cause a false mismatch. |
| 407 | const stripPort = (host: string): string => host.replace(/:\d+$/, ''); |
| 408 | const repoMatch = currentRepo.toLowerCase() === sessionRepo.toLowerCase(); |
| 409 | const hostMatch = !currentParsed || !sessionParsed || stripPort(currentParsed.host.toLowerCase()) === stripPort(sessionParsed.host.toLowerCase()); |
| 410 | if (repoMatch && hostMatch) { |
| 411 | return { |
| 412 | status: 'match', |
| 413 | sessionRepo, |
| 414 | currentRepo |
| 415 | }; |
| 416 | } |
| 417 | |
| 418 | // Repo mismatch — keep sessionRepo/currentRepo as plain "owner/repo" so |
| 419 | // downstream consumers (e.g. getKnownPathsForRepo) can use them as lookup keys. |
| 420 | // Include host information in separate fields for display purposes. |
| 421 | return { |
| 422 | status: 'mismatch', |
| 423 | sessionRepo, |
| 424 | currentRepo, |
| 425 | sessionHost: sessionParsed?.host, |
| 426 | currentHost: currentParsed?.host |
| 427 | }; |
| 428 | } |
| 429 | |
| 430 | /** |
| 431 | * Handles teleporting from a code session ID. |
no test coverage detected