-
Notifications
You must be signed in to change notification settings - Fork 0
Matching Pipeline
apicov scan 한 번이 실제로 하는 일입니다. 진입점은 Sources/StellaCore/Pipeline/Pipeline.swift 의 generate(_:now:) 하나입니다.
OpenAPI 로드 ─┐
├─→ 경로 매칭 ─→ git blame ─→ 담당자 결합 ─→ CoverageSnapshot
Router 스캔 ──┘
URL · 로컬 파일 · 메모리 데이터 세 가지 소스를 받습니다. URL 모드는 Basic 인증 헤더를 붙일 수 있고 2xx 가 아니면 openAPIHTTPStatus 로 끊습니다.
JSON 만 받습니다. info.title · info.version · paths 가 없으면 실패합니다.
paths 를 훑으면서 (경로, 메서드) 쌍마다 오퍼레이션을 만듭니다. GET·POST·PUT·PATCH·DELETE 외의 메서드와 파싱할 수 없는 항목은 조용히 건너뜁니다. tags 는 첫 번째 값만 씁니다.
스키마를 재귀로 걸어 예시 JSON 을 만듭니다. 우선순위는 이렇습니다.
-
example→ 2.default→ 3.enum의 첫 값 → 4.$ref해석 후 재귀 -
allOf는 객체끼리 병합,oneOf/anyOf는 첫 항목만 -
array는 원소 하나짜리 배열,object는 프로퍼티별 재귀 - 그래도 안 정해지면
format→type순으로 기본값 (integer→0,string→"string",date-time→"2026-04-29T00:00:00Z"…)
재귀 깊이는 10 단계에서 끊습니다. 순환 참조가 있는 스키마도 멈춥니다.
프로젝트 루트를 훑으며 glob 에 걸리는 파일만 고릅니다. 지원하는 문법은 **(중첩 디렉터리)과 *(경로 한 조각) 두 개뿐이고 숨김 파일은 건너뜁니다.
| 프로젝트 | glob |
|---|---|
appProduct |
**/Router/*Router.swift |
umcApp |
**/Data/Sources/*Router.swift |
파일이 이 패턴에 안 걸리면 그 Router 는 존재하지 않는 셈이 됩니다. "매치 안됨"에도 안 뜨고 커버리지에서 그냥 빠집니다. 스캔 결과가 텅 비었다면 여기부터 의심하세요.
SwiftSyntax 로 파싱합니다. 정규식이 아니라 구문 트리를 보므로 줄바꿈이나 주석에 흔들리지 않습니다.
- enum 이름이
Router로 끝나거나 -
BaseTargetType/TargetType을 상속하거나 -
XxxRouter: TargetType형태의 extension (이름이Router로 끝나고 상속 절이 있어야 합니다)
-
case 이름과 줄 번호 — 줄 번호는 나중에
git blame좌표가 됩니다 -
var path: String의 switch 각 갈래가return하는 문자열 리터럴 -
var method: …Method의 switch 각 갈래가return하는 멤버 이름 (.post→post) -
///문서 주석 — case 요약으로 붙습니다
프로퍼티는 enum 본체에 있든 extension 에 있든 같은 이름의 타입으로 합쳐집니다. path 는 타입이 정확히 String, method 는 이름이 Method 로 끝나는 타입이어야 인식합니다.
갈래가
return문 하나로 값을 돌려주는 형태만 읽습니다. 중간 변수를 만들거나 삼항 연산자로 조립하면 그 case 는path/method가 비어 "missing path or method arm" 으로 남습니다.
Swift 문자열 보간을 OpenAPI 경로 템플릿으로 바꿉니다.
| 입력 | 출력 |
|---|---|
/notices/\(id) |
/notices/{id} |
/notices/\(notice.id) |
/notices/{id} (점 뒤 마지막 조각) |
/notices/\(id, radix: 10) |
/notices/{id} (콤마 앞까지) |
/notices?page=\(page) |
/notices (? 뒤는 잘라냄) |
case 하나마다 이 순서로 판정합니다.
-
overrides 조회 —
ignore면ignored: <reason>사유로 매치 안됨 처리,openAPI매핑이면 그 키로 즉시 매치 -
path나method갈래가 없으면 →missing path or method arm - 정확 매치 — (메서드, 정규화된 경로)가 스펙 키와 그대로 일치
-
템플릿 매치 — 세그먼트 개수가 같고 파라미터 자리(
{...})끼리는 이름이 달라도 같다고 봅니다. 후보가 여럿이면 경로 사전순으로 첫 번째를 고릅니다 - 다 실패하면 →
no OpenAPI entry for GET /path
4번 덕분에 코드의 \(noticeId) 와 스펙의 {id} 가 붙습니다. 반대로 경로 오타나 버전 차이(/api/v1 vs /v1)는 절대 매치되지 않습니다.
매치된 case 마다 git blame -L <줄>,<줄> --porcelain -- <파일> 을 blame 루트에서 실행해 커밋 이메일·이름·SHA·작성 시각을 읽습니다.
실패해도 스캔은 멈추지 않습니다. 해당 연결의 작성자만 Unknown, SHA 는 빈 문자열, 날짜는 1970-01-01 로 남습니다. 작성자가 전부 Unknown 이면 --blame-root 가 잘못됐거나 얕은 클론(fetch-depth: 1)일 가능성이 높습니다.
-
authors.yml— blame 이메일을 표시명·GitHub username 으로 풉니다 (대소문자 무시) -
owners.yml— 엔드포인트 지정이 태그 지정보다 우선합니다. 나온 이메일을 다시authors.yml로 풀어 이름을 붙입니다
-
endpoints는 스펙의 모든 오퍼레이션입니다. 프로젝트마다 연결이 있으면Connection, 없으면null이 들어갑니다. 한 엔드포인트에 여러 case 가 매치되면 먼저 발견된 하나만 기록됩니다 -
unmatchedRouterCases는 반대 방향입니다. 코드에는 있는데 스펙에 못 붙은 case - 정렬은 엔드포인트가 경로 → 메서드 순, 매치 안됨이
프로젝트.enum.case순입니다. 같은 입력이면 같은 바이트가 나오므로 diff 가 깔끔합니다
프로젝트별 summary 는 이렇게 계산됩니다.
| 필드 | 계산 |
|---|---|
matched |
매치된 Router case 수 |
missing |
스펙 오퍼레이션 총수 − matched
|
unmatched |
매치 안 된 Router case 수 |
matched 가 "연결된 엔드포인트 수"가 아니라 case 수라는 점을 기억하세요. 서로 다른 case 두 개가 같은 엔드포인트에 매치되면 matched 는 2 로 잡히고 missing 은 그만큼 줄어듭니다. 커버리지 퍼센트도 이 값을 그대로 씁니다. 중복 매핑이 많은 프로젝트에서는 수치가 실제보다 후하게 나올 수 있습니다.
UMC-PRODUCT/umc-product-stella · 작성자 제옹(euijjang97) · 관련: iOS 위키 Networking