초록 빌드가 설치된 브라우저는 아니야
Voyager 집 간판엔 여러 군데서 되풀이되는 줄이 있어. 확인은 swift build가 아니라 /Applications/Voyager.app에서 해. 컴파일이 통과했다는 건 타입이 맞는다는 뜻일 뿐이야. 아빠가 쓰는 건 Applications에 있는 번들이야. 서명돼 있고, 아이콘이랑 버전이 붙어 있고, 아빠 세션이 들어 있는 바로 그 번들. 그래서 scripts/build-app.sh는 빌드, 번들, 서명, 확인, 설치를 트랜잭션 하나로 다뤄. 전부 되든지, 아니면 아빠의 설치본을 손대지 않은 채로 두든지.
스테이징, 서명, 확인
- 일찍 거절해.
cwk-product.json의 버전이 제대로 읽혀야 하고(Voyager는 아직 1.0 전이야), 서명 아이덴티티가 설치돼 있어야 하고, 아이콘이 있어야 해. 전부 컴파일 시작 전에 봐. - 빌드하고 번호 매겨. 릴리스로 빌드하고, 빌드 번호는 저장소 커밋 수로 매겨. 그러면 번들마다 역사의 어느 지점에서 빌드됐는지 알 수 있어.
- 스테이징. 번들은
.build아래 임시 폴더에서 조립해. 바이너리, Info.plist, 아이콘,cwk-product.json사본, 그리고 빌드할 때 키트에서 그려 오는 피파 마크. 저장소엔 마크 사본을 안 두거든. - 서명하고 다시 읽어. 지난 레슨의 아이덴티티로 서명하고, 깊고 엄격하게 확인한 다음 서명 주체, 런타임, designated requirement를 봐. 통과한 번들만
build/로 복사돼.
인터럽트도 exit trap으로 돌려 놔서 Control-C로 멈춘 빌드도 스테이징 폴더를 치우고 가. 스크립트가 지우는 경로는 하나하나 자기 소유 목록이랑 대조해.
돌고 있는 브라우저 위에 설치하기
아빠 Voyager는 대개 켜져 있어. 설치는 확인된 번들을 /Applications에 숨긴 이름으로 복사해. 이름엔 스크립트 프로세스 id가 박혀 있고, 이 사본도 확인해. 그런 다음 돌고 있는 Voyager한테 SIGTERM으로 종료를 부탁해(첫 번째 블록). Voyager는 SIGTERM을 받으면 아빠가 메뉴에서 종료할 때랑 똑같이 세션을 저장해. 그래서 탭이 다시 돌아와. 스크립트는 최대 10초 기다려. 페이지가 대화 상자를 붙잡고 있다든가 해서 그래도 돌고 있으면, 스크립트는 멈추고 그렇다고 말해. 죽이는 건 없어.
Voyager가 꺼지면 예전 번들을 옆으로 치우고, 새 번들을 제자리에 넣고, 거기서 다시 확인해. 그 확인을 통과하기 전까진 exit trap이 예전 번들을 되돌릴 준비를 하고 있어. Launch Services한테 알리는 건 그다음이고, 되면 좋고 아니면 마는 단계야. 등록이 실패해도 무시하고 설치를 끝난 걸로 친 다음 예전 번들을 지워. trap도 못 돌 만큼 세게 죽은 설치는 pid 박힌 찌꺼기를 남기는데, 다음 실행이 프로세스가 죽은 것만 골라서 지워.
닥터는 읽기만 해
scripts/doctor.sh는 뭐가 빌드됐고, 뭐가 설치됐고, Voyager가 뭘 들고 있는지 답해. 그러면서 하나도 안 바꿔. 제품 버전이랑 번들 자체 번호는 따로 보고해. 서로 다른 질문의 답이니까. 서명 아이덴티티를 말하고, 시스템 WebKit 버전도 말해. 엔진은 시스템 거라서 Voyager는 보고만 하고 절대 고정하지 않아. 빌드본과 설치본마다 버전, 빌드, 서명 상태, 서명 주체, 런타임, requirement, 실행 파일 해시를 찍어. Voyager 폴더에 대해선 개수만 찍어. 저장된 세션의 탭, 아이덴티티, 기록된 이탈과 그중 이유 없는 것. 문제가 하나라도 있으면 개수를 달고 VOYAGER_DOCTOR_FAIL로 끝나.
아빠 상태에 손댈 수 없는 확인
진짜 앱을 돌리는 스모크 테스트는 진짜 앱 폴더, 키체인, 피파에 써. Voyager는 그 문을 아이덴티티로 닫아. 그게 키트의 격리 게이트야(두 번째 블록). build-app.sh --verification은 com.cwk.voyager.verify라는 별도 번들을 만들고, URL 타입도 문서 타입도 빼서 macOS가 절대 브라우저로 내놓지 않게 해. --isolated를 받아 주는 건 이 번들뿐이야. 출시용 앱한테 격리로 돌라고 하면 거절 문구를 찍고 종료해. 확인이 슬쩍 진짜 폴더로 넘어가는 게 바로 이 게이트가 막으려는 사고니까. 대역 피파 주소 같은 덮어쓰기 값은 격리 밖에선 비어 있는 걸로 읽히고, 격리 안에서도 피파 주소는 loopback이어야 해.
격리 실행은 버리는 컨테이너에서 살아. 가족 쪽엔 아무 데도 안 닿는 설정(카탈로그 없음, 빈 새 탭, 페이지 확대 1)으로 시작하고, 저장한 로그인은 키체인 대신 메모리 금고에 둬.
smoke.sh는 앱을 두 번 걸어
scripts/smoke.sh는 검증 앱을 빌드하고 loopback 포트에 대역 피파를 띄워. 앞에 무슨 페이지가 있는지 묻고 들은 걸 받아 적는 패널이 있는 작은 서버야. 비밀번호 칸이 있는 테스트 페이지랑 로그인 폼도 같이 내놔. 그리고 앱을 두 번 띄워(세 번째 블록). 한 번은 써 보려고, 한 번은 세션이 돌아오는지 보려고. 실행마다 60초 제한이야. check 줄 58개가 보는 건 이래. 창과 탭, 찾기, 확대, 북마크, 고정과 끌기, Incognito, 홈, 마우스 옆 버튼, 탭을 바꾼 뒤의 키 입력, 설정 창, 비밀번호 값은 빼고 페이지 얘기를 들은 패널, 저장했다가 다시 채운 로그인, 공유했다가 지운 동반 소스, 고정 탭이 된 피파 본인, 앞 탭만 깨어난 복원. 끝나면 컨테이너, 검증 빌드의 WebKit 데이터, 그 설정까지 지워.