mirror of
https://github.com/stablyai/orca.git
synced 2026-10-16 08:03:09 +00:00
* feat: support GitLab MR unlinking and AI generation in ChecksPanel Integrate GitLab merge request actions alongside GitHub pull requests in the sidebar checks panel. This includes unlinking GitLab MRs, enabling AI-driven title and body generation for GitLab, and dynamically adapting menu labels (e.g. "More MR actions" vs "More PR actions") depending on the active provider. * fix: handle null base ref in hosted review creation and add tests Co-authored-by: Orca <help@stably.ai> * Extract sub-components and hooks from renderer components To improve component focus and maintainability, extract large inline sub-components, custom hooks, and logic helpers into dedicated files: - Extract `HeroPaired` from `MobileHero` to `MobileHeroPairedDevices`. - Move `ChromePreview` from `ThemeStep` to `theme-chrome-preview`. - Refactor `HostedReviewActions` to use `useHostedReviewActions` hook. - Move MCP config loading from `McpConfigSection` to helper file. * Refactor usage panes to extract shared formatters and tables Extract duplicated formatting utilities to a shared helper module. Move the large, inline recent sessions tables into dedicated sub-components to reduce duplication and simplify the parent pane components. * Support self-hosted GitLab instances for MR creation eligibility * Extract and check the remote host against `glab auth status` to dynamically recognize and authenticate self-hosted GitLab instances without requiring them to be in a hardcoded list. * Refactor stats usage panes by extracting reusable breakdown sections and sessions tables to eliminate duplication. * Suggest Linear prompts only if the launcher can resolve the CLI. * Extract GitLab project ref tests and update usage stats translations - Move GitLab project ref parsing tests into a dedicated test file to keep modules focused and add tests for candidate parsing. - Add missing translations for the usage sessions table and breakdown section across multiple locales. * Optimize GitLab ref parsing, deduplicate formatters, and add locales - Clean up GitLab ref parsing by extracting normalized known hosts. - Fix an escaped newline sequence in the self-hosted GitLab mock test. - Deduplicate stats helper functions into a single shared file. - Translate path status message strings across ES, JA, KO, and ZH. --------- Co-authored-by: Orca <help@stably.ai>
85 lines
2.6 KiB
TypeScript
85 lines
2.6 KiB
TypeScript
import type { GitLabProjectRef } from '../../shared/types'
|
|
|
|
export type ProjectRef = GitLabProjectRef
|
|
|
|
/**
|
|
* Hosts always treated as GitLab. Self-hosted instances are added at
|
|
* runtime via `getGlabKnownHosts()`, which inspects `glab auth status`.
|
|
*/
|
|
export const DEFAULT_GITLAB_HOSTS = ['gitlab.com'] as const
|
|
|
|
export function normalizeGitLabHost(value: string): string {
|
|
return value.trim().toLowerCase()
|
|
}
|
|
|
|
function stripGitSuffix(path: string): string {
|
|
return path.replace(/\/+$/, '').replace(/\.git$/i, '')
|
|
}
|
|
|
|
function makeProjectRefForTrustedHost(host: string, path: string): ProjectRef | null {
|
|
const normalizedHost = normalizeGitLabHost(host)
|
|
const normalizedPath = stripGitSuffix(path.replace(/^\/+/, '')).trim()
|
|
// Reject paths without at least one group segment — `gitlab.com:foo`
|
|
// alone is not a project reference.
|
|
if (!normalizedPath.includes('/')) {
|
|
return null
|
|
}
|
|
return { host: normalizedHost, path: normalizedPath }
|
|
}
|
|
|
|
function makeProjectRef(
|
|
host: string,
|
|
path: string,
|
|
knownHosts: readonly string[]
|
|
): ProjectRef | null {
|
|
const normalizedHost = normalizeGitLabHost(host)
|
|
const normalizedKnownHosts = knownHosts.map(normalizeGitLabHost)
|
|
if (!normalizedKnownHosts.includes(normalizedHost)) {
|
|
return null
|
|
}
|
|
return makeProjectRefForTrustedHost(normalizedHost, path)
|
|
}
|
|
|
|
export function parseRemoteProjectRefCandidate(remoteUrl: string): ProjectRef | null {
|
|
const trimmed = remoteUrl.trim()
|
|
if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed)) {
|
|
const scpLike = trimmed.match(/^(?:[^@/:]+@)?([^:\s/]+):([^\s]+?)(?:\.git)?$/)
|
|
if (scpLike) {
|
|
return makeProjectRefForTrustedHost(scpLike[1], scpLike[2])
|
|
}
|
|
}
|
|
|
|
try {
|
|
const url = new URL(trimmed)
|
|
if (!['http:', 'https:', 'ssh:', 'git:', 'git+ssh:'].includes(url.protocol.toLowerCase())) {
|
|
return null
|
|
}
|
|
return makeProjectRefForTrustedHost(url.hostname, url.pathname)
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
export function parseGitLabProjectRef(
|
|
remoteUrl: string,
|
|
knownHosts: readonly string[] = DEFAULT_GITLAB_HOSTS
|
|
): ProjectRef | null {
|
|
const trimmed = remoteUrl.trim()
|
|
if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed)) {
|
|
const scpLike = trimmed.match(/^(?:[^@/:]+@)?([^:\s/]+):([^\s]+?)(?:\.git)?$/)
|
|
if (scpLike) {
|
|
return makeProjectRef(scpLike[1], scpLike[2], knownHosts)
|
|
}
|
|
}
|
|
|
|
try {
|
|
const url = new URL(trimmed)
|
|
if (!['http:', 'https:', 'ssh:', 'git:', 'git+ssh:'].includes(url.protocol.toLowerCase())) {
|
|
return null
|
|
}
|
|
return makeProjectRef(url.hostname, url.pathname, knownHosts)
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|