Три дефекта подряд в механизме обновлений. Каждый по отдельности выглядел мелочью, вместе они приводили к худшему из возможного: система отчитывалась об успехе и ничего не делала.
Как обнаружилось
Правка вышла выпуском, установилась, сайт открылся — а изменения на нём не было. Сверка описи файлов с тем, что реально лежит на диске, показала шесть расхождений.
Первый: опись запоминала то, чего нет
Файл, который владелец правил руками, обновление намеренно не трогает. Но опись после установки записывалась целиком из поставки — вместе с таким файлом. В итоге в описи стояла сумма версии, которой на диске нет, и файл расходился с описью НАВСЕГДА: каждое следующее обновление снова считало его правкой и снова пропускало.
Второй: посторонний репозиторий
Сборка поставки отбирает файлы по списку репозитория — и правильно делает: за годы в рабочем каталоге заводится чужое, и покупателям оно ехать не должно. Но тем же отбором пользовалось принятие состояния УЖЕ РАБОТАЮЩЕГО сайта. На сервере остался репозиторий от первого развёртывания, застрявший на старом коммите, — и опись выходила короче на тридцать девять файлов. Файла нет в описи — он считается правкой владельца.
Третий: одна сумма, два способа счёта
Опись считала контрольную сумму по нормализованному содержимому, а сверка с диском — по сырому файлу. Пока всё приезжало поставкой, разницы не было. Но файл с виндовыми концами строк расходился с описью вечно.
Что сделано
- Пропущенный файл сохраняет в описи ПРЕЖНЮЮ сумму — и остаётся видимой правкой, а не исчезает из обновлений
- Принятие состояния больше не зависит от постороннего репозитория: на работающем сайте всё на диске — наше по определению
- Сумма файла считается одним правилом на весь механизм
Общий вывод простой: отчёт «Готово» ничего не доказывает, пока не сверено состояние. Теперь эта сверка есть, и каждый из трёх случаев закреплён проверкой, которая падает на прежнем коде.