feat: add mobile git auth strategy parsing
This commit is contained in:
46
apps/mobile/src/mobileGitRemote.test.ts
Normal file
46
apps/mobile/src/mobileGitRemote.test.ts
Normal 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()
|
||||
})
|
||||
})
|
||||
71
apps/mobile/src/mobileGitRemote.ts
Normal file
71
apps/mobile/src/mobileGitRemote.ts
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user