feat: add mobile git auth strategy parsing

This commit is contained in:
lucaronin
2026-05-04 09:34:48 +02:00
parent 16de4e529b
commit 601306afb5
3 changed files with 124 additions and 2 deletions

View File

@@ -0,0 +1,46 @@
import { describe, expect, it } from 'vitest'
import { parseMobileGitRemote } from './mobileGitRemote'
describe('mobile git remote parsing', () => {
it('uses GitHub OAuth for GitHub HTTPS remotes', () => {
expect(parseMobileGitRemote('https://github.com/refactoringhq/tolaria.git')).toEqual({
url: 'https://github.com/refactoringhq/tolaria.git',
host: 'github.com',
owner: 'refactoringhq',
repository: 'tolaria',
authStrategy: 'githubOAuth',
})
})
it('uses GitHub OAuth for GitHub SSH shorthand remotes', () => {
expect(parseMobileGitRemote('git@github.com:refactoringhq/tolaria.git')).toMatchObject({
host: 'github.com',
owner: 'refactoringhq',
repository: 'tolaria',
authStrategy: 'githubOAuth',
})
})
it('uses SSH keys for non-GitHub SSH remotes', () => {
expect(parseMobileGitRemote('ssh://git@git.example.com/acme/notes.git')).toMatchObject({
host: 'git.example.com',
owner: 'acme',
repository: 'notes',
authStrategy: 'sshKey',
})
})
it('uses SSH keys for non-GitHub HTTPS remotes', () => {
expect(parseMobileGitRemote('https://gitlab.com/acme/notes.git')).toMatchObject({
host: 'gitlab.com',
owner: 'acme',
repository: 'notes',
authStrategy: 'sshKey',
})
})
it('rejects non-remote input', () => {
expect(parseMobileGitRemote('/Users/luca/Laputa')).toBeNull()
expect(parseMobileGitRemote('')).toBeNull()
})
})

View File

@@ -0,0 +1,71 @@
export type MobileGitAuthStrategy = 'githubOAuth' | 'sshKey'
export type MobileGitRemote = {
url: string
host: string
owner: string
repository: string
authStrategy: MobileGitAuthStrategy
}
export function parseMobileGitRemote(url: string): MobileGitRemote | null {
const normalizedUrl = url.trim()
return parseHttpRemote(normalizedUrl) ?? parseScpRemote(normalizedUrl) ?? parseSshRemote(normalizedUrl)
}
function parseHttpRemote(url: string): MobileGitRemote | null {
const parsedUrl = parseUrl(url)
if (!parsedUrl || !['http:', 'https:'].includes(parsedUrl.protocol)) {
return null
}
return createRemote(url, parsedUrl.hostname, pathParts(parsedUrl.pathname))
}
function parseSshRemote(url: string): MobileGitRemote | null {
const parsedUrl = parseUrl(url)
if (!parsedUrl || parsedUrl.protocol !== 'ssh:') {
return null
}
return createRemote(url, parsedUrl.hostname, pathParts(parsedUrl.pathname))
}
function parseScpRemote(url: string): MobileGitRemote | null {
const match = /^git@([^:]+):([^/]+)\/(.+)$/.exec(url)
if (!match) {
return null
}
return createRemote(url, match[1], [match[2], stripGitSuffix(match[3])])
}
function createRemote(url: string, host: string, parts: string[]): MobileGitRemote | null {
if (parts.length < 2) {
return null
}
return {
url,
host,
owner: parts[0],
repository: stripGitSuffix(parts[1]),
authStrategy: host === 'github.com' ? 'githubOAuth' : 'sshKey',
}
}
function parseUrl(url: string) {
try {
return new URL(url)
} catch {
return null
}
}
function pathParts(pathname: string) {
return pathname.split('/').filter(Boolean)
}
function stripGitSuffix(value: string) {
return value.endsWith('.git') ? value.slice(0, -4) : value
}

View File

@@ -8,7 +8,7 @@ This file is the resumable working log for Tolaria mobile. The strategy and road
- Branch: `codex/mobile`
- Active phase: Phase 2 - Mobile Shell
- Active slice: Add compact swipe gesture support
- Active slice: Add mobile Git remote auth strategy parsing
- Push policy: commit locally; do not push unless explicitly requested
- Validation target: iPad/iOS simulator first
@@ -43,13 +43,14 @@ This file is the resumable working log for Tolaria mobile. The strategy and road
- Wrapped the mobile root in `GestureHandlerRootView`.
- Added a tested compact gesture mapper and `SwipeSurface` wrapper so phone panels can transition through swipe gestures while keeping panel behavior in the reducer.
- Re-tested the iPad simulator path; the app now launches in Expo Go on `iPad Pro 13-inch (M4)`.
- Added a pure mobile Git remote parser that codifies the auth choice: GitHub remotes use the GitHub OAuth App path, arbitrary Git remotes use the SSH-key path.
## Next Action
Continue Phase 2 with the next mobile shell slice:
1. Dismiss or suppress Expo Go's first-run tools modal during simulator QA so screenshots capture the app without the overlay.
2. Add the storage/auth abstraction skeleton behind the mobile vault repository.
2. Connect parsed Git remotes to a mobile vault configuration model.
3. Begin the TenTap editor spike behind a `MobileEditorAdapter` once the shell/storage boundary is stable.
## Verification Log
@@ -114,6 +115,10 @@ Continue Phase 2 with the next mobile shell slice:
- CodeScene after gesture support: `apps/mobile/src/compactGestures.ts`, `apps/mobile/src/compactGestures.test.ts`, `apps/mobile/src/SwipeSurface.tsx`, touched app/root files, and touched style module scored `10`; `apps/mobile/index.ts` returned no scorable code.
- `pnpm --filter @tolaria/mobile exec expo export --platform ios --output-dir /tmp/tolaria-mobile-export` passed after adding gesture support.
- Simulator screenshot after gesture support captured at `/tmp/tolaria-mobile-gestures-ipad.png`; no red runtime error overlay appeared.
- `pnpm --filter @tolaria/mobile test` passed after Git remote auth parsing: 6 files / 20 tests.
- `pnpm --filter @tolaria/mobile typecheck` passed after Git remote auth parsing.
- CodeScene after Git remote auth parsing: `apps/mobile/src/mobileGitRemote.ts` and `apps/mobile/src/mobileGitRemote.test.ts` scored `10`.
- `pnpm --filter @tolaria/mobile exec expo export --platform ios --output-dir /tmp/tolaria-mobile-export` passed after Git remote auth parsing.
## Risks / Watch Items