상황
PR 하나의 CI에서 macOS 작업만 계속 빨간불이었다. 리눅스 작업은 초록이었다. 실패한 테스트는 설치된 관리 파일 묶음을 복구하는 기능을 확인하는 것이었고, 오류 메시지는 "검증된 관리 스냅숏이 아니다"였다. macOS의 임시 디렉터리가 심볼릭 링크 아래에 있다는 건 잘 알려진 사실이라, 다들 자연스럽게 링크 처리 차이를 의심했다.
그럴듯했던 수정
제안된 수정은 macOS에서만 대상 경로를 실제 경로로 풀어서 비교하는 분기였다. 테스트는 아마 통과했을 것이다. 하지만 그 코드에는 링크를 따라가지 않도록 일부러 막아 둔 검사가 있었다. 링크를 통해 엉뚱한 위치를 덮어쓰지 않게 하려는 안전장치다. OS 분기 하나가 테스트를 초록으로 만드는 대신, 한 플랫폼에서만 그 안전장치를 꺼 버리는 셈이었다. 이 변경은 반려했다.
실제 원인
오류 메시지가 말한 건 OS가 아니라 "검증되지 않았다"는 사실이었다. 실제 설치 과정은 관리 디렉터리 안에 버전 표식 파일을 남기고, 복구 기능은 그 표식이 있어야 스냅숏을 믿는다. 테스트 픽스처는 이 파일을 만들지 않았다. 리눅스에서 그 테스트 하나만 따로 돌리자 똑같은 메시지로 실패했다. 리눅스 작업이 초록이었던 건 다른 조건 때문에 그 경로를 지나가지 않았을 뿐이었다. 픽스처가 버전 표식을 쓰도록 고치자 두 OS 모두 통과했다.
팁: 오류 메시지가 가리키는 검사부터 읽는다
한 OS에서만 실패하면 OS 차이부터 떠올리기 쉽다. 먼저 오류 메시지가 어떤 검사에서 나왔는지 코드에서 찾고, 그 검사가 무엇을 요구하는지 읽는다. 요구 조건이 OS와 무관하다면 원인도 OS와 무관할 가능성이 크다.
팁: 가진 OS에서 같은 조건으로 재현한다
실패한 테스트를 이름으로 골라 단독으로, 가능하면 깨끗한 임시 디렉터리에서 돌린다. 다른 OS에서도 실패하면 플랫폼 문제가 아니다. 통과한다면 그때 두 환경에서 그 검사에 들어가는 값, 예를 들어 실제 경로와 파일 목록을 나란히 출력해 어디서 갈리는지 본다.
팁: 안전 검사를 느슨하게 하는 OS 분기는 의심한다
if macOS로 시작하는 수정이 링크 따라가기, 권한 확인, 경로 검증 같은 검사를 그 플랫폼에서만 완화한다면 거의 항상 잘못된 방향이다. 테스트 환경을 실제 설치와 같게 만들거나, 검사가 정말 틀렸다면 모든 플랫폼에서 같은 방식으로 고친다.
체크리스트
1) 오류 메시지가 나온 검사를 코드에서 찾는다. 2) 그 검사의 요구 조건이 OS와 관련 있는지 본다. 3) 가진 OS에서 그 테스트를 단독으로 돌린다. 4) 픽스처가 실제 설치가 남기는 파일을 모두 만드는지 확인한다. 5) 안전 검사를 한 플랫폼에서만 끄는 수정은 받지 않는다.
What happened
On one pull request, only the macOS CI job kept failing; the Linux job was green. The failing test covered a feature that repairs an installed set of managed files, and the error said the target was "not a verified managed snapshot". It is well known that macOS temporary directories sit under a symlink, so everyone naturally suspected a difference in link handling.
The plausible fix
The proposed fix added a macOS-only branch that resolved the target to its real path before comparing. The test would probably have passed. But that code contained a check that deliberately refuses to follow links, a safeguard against overwriting the wrong location through a link. One OS branch would have turned the test green by switching that safeguard off on a single platform. The change was rejected.
The real cause
The error message did not talk about the OS. It said the snapshot was not verified. A real install leaves a version marker file inside the managed directory, and the repair feature only trusts a snapshot when that marker exists. The test fixture never wrote that file. Running that one test alone on Linux failed with exactly the same message; the Linux job had been green only because other conditions kept it off that path. Once the fixture wrote the version marker, both operating systems passed.
Tip: start from the check the error names
When something fails on only one OS, it is easy to jump to OS differences. First find in the code which check produced the error message, and read what that check requires. If the requirement has nothing to do with the OS, the cause most likely does not either.
Tip: reproduce the same condition on the OS you have
Select the failing test by name and run it alone, ideally in a clean temporary directory. If it also fails on the other OS, it is not a platform problem. If it passes, print the values that feed the check, such as the resolved path and the file list, side by side on both systems and see where they diverge.
Tip: distrust OS branches that loosen a safety check
A fix that starts with if macOS and relaxes link following, permission checks or path validation on that platform alone is almost always the wrong direction. Make the test environment match a real install, or, if the check really is wrong, fix it the same way on every platform.
Checklist
1) Find the check in the code that produced the error message. 2) Decide whether its requirement has anything to do with the OS. 3) Run that test alone on the OS you have. 4) Confirm the fixture creates every file a real install leaves behind. 5) Do not accept a fix that disables a safety check on one platform only.
发生了什么
某个 PR 的 CI 里,只有 macOS 任务一直失败,Linux 任务是绿的。失败的测试验证的是"修复已安装的一组托管文件"这个功能,错误信息是"不是经过验证的托管快照"。macOS 的临时目录位于一个符号链接之下,这是众所周知的事,所以大家很自然地怀疑是链接处理的差异。
看似合理的修复
提出的修复是加一个只在 macOS 上生效的分支:先把目标路径解析成真实路径再比较。测试大概会通过。但那段代码里有一个故意不跟随链接的检查,是为了防止通过链接覆盖到错误位置的安全措施。一个系统分支让测试变绿的代价,是在某一个平台上把这个安全措施关掉。这个改动被拒绝了。
真正的原因
错误信息说的不是操作系统,而是"未经验证"。真实安装会在托管目录里留下一个版本标记文件,修复功能只有看到这个标记才信任快照。测试夹具从来没有写这个文件。在 Linux 上单独运行这一个测试,失败信息一字不差;Linux 任务之前是绿的,只是因为其他条件让它没走到这条路径。夹具补上版本标记之后,两个系统都通过了。
提示:从错误信息指向的检查开始读
只在一个系统上失败时,人很容易直接想到系统差异。先在代码里找到是哪个检查产生了这条错误信息,再读这个检查要求什么。如果要求和操作系统无关,原因多半也和操作系统无关。
提示:在手头的系统上用同样条件复现
按名字挑出失败的测试单独运行,最好在干净的临时目录里。如果在另一个系统上也失败,那就不是平台问题。如果通过了,再把进入这个检查的值,比如解析后的路径和文件列表,在两个系统上并排打印出来,看看从哪里开始不同。
提示:对放松安全检查的系统分支保持怀疑
以 if macOS 开头、只在该平台上放松链接跟随、权限检查或路径校验的修复,几乎总是方向错了。应该让测试环境和真实安装一致;如果检查本身确实有错,就在所有平台上用同样的方式修。
检查清单
1) 在代码里找到产生这条错误信息的检查。2) 判断它的要求和操作系统有没有关系。3) 在手头的系统上单独运行这个测试。4) 确认夹具创建了真实安装会留下的所有文件。5) 不接受只在一个平台上关闭安全检查的修复。
何が起きたか
ある PR の CI で、macOS のジョブだけが落ち続けた。Linux のジョブは緑だった。落ちたテストは、インストール済みの管理ファイル一式を修復する機能を確かめるもので、エラーは「検証済みの管理スナップショットではない」だった。macOS の一時ディレクトリがシンボリックリンクの下にあるのはよく知られた話なので、みんな自然とリンクの扱いの違いを疑った。
もっともらしい修正
出てきた修正案は、macOS でだけ対象パスを実パスに解決してから比べる分岐だった。テストはたぶん通っただろう。しかしそのコードには、リンクをたどらないようにわざと作られたチェックがあった。リンク経由で間違った場所を上書きしないための安全装置だ。OS の分岐ひとつでテストを緑にする代わりに、一つのプラットフォームでだけその安全装置を切ることになる。この変更は却下した。
本当の原因
エラーメッセージが言っていたのは OS のことではなく、「検証されていない」という事実だった。実際のインストールは管理ディレクトリの中にバージョンの目印ファイルを残し、修復機能はその目印があるときだけスナップショットを信用する。テストのフィクスチャーはこのファイルを作っていなかった。Linux でそのテストだけを単独で走らせると、まったく同じメッセージで落ちた。Linux のジョブが緑だったのは、別の条件でその経路を通っていなかっただけだった。フィクスチャーがバージョンの目印を書くように直すと、両方の OS で通った。
Tip: エラーが指しているチェックから読む
一つの OS でだけ落ちると、すぐ OS の違いを思い浮かべがちだ。まずそのエラーメッセージがどのチェックから出たのかをコードで探し、そのチェックが何を求めているかを読む。求めている条件が OS と関係ないなら、原因もたぶん OS とは関係ない。
Tip: 手元の OS で同じ条件を再現する
落ちたテストを名前で選んで単独で、できればきれいな一時ディレクトリで走らせる。別の OS でも落ちるなら、プラットフォームの問題ではない。通るなら、そのチェックに入る値、たとえば解決後のパスやファイル一覧を、両方の環境で並べて出力し、どこで分かれるかを見る。
Tip: 安全チェックを緩める OS 分岐は疑う
if macOS で始まり、リンクの追跡や権限確認、パス検証をそのプラットフォームでだけ緩める修正は、ほぼ必ず間違った方向だ。テスト環境を実際のインストールと同じにするか、チェックが本当に間違っているなら、すべてのプラットフォームで同じように直す。
チェックリスト
1) エラーメッセージを出したチェックをコードで探す。2) その条件が OS と関係あるかを見る。3) 手元の OS でそのテストを単独で走らせる。4) フィクスチャーが実際のインストールの残すファイルをすべて作っているか確かめる。5) 一つのプラットフォームでだけ安全チェックを切る修正は受け入れない。