Ресурсы приложения легко повредить незаметно. Слияние разрешается неверно, и иконка возвращается к старой. Рефакторинг сценария сборки теряет манифест. Круг локализации теряет три подписи меню. Компоновщик ничего не сообщает, приложение работает, и проблема попадает в релиз.
Проверка регрессии ресурсов позволяет это поймать. Идея проста: хранить в системе контроля версий эталонный снимок ресурсов, сравнивать с ним каждую сборку и останавливать сборку, если что-то из прежде имевшегося пропало. Консольный инструмент Resource Builder sircc64.exe выполняет сравнение и возвращает настоящий код завершения, поэтому подключить его к CI можно несколькими строками.
Что нужно для проверки регрессии ресурсов
sircc64.exe— консольный компилятор и инструмент для работы с ресурсами, устанавливаемый вместе с Resource Builder. Запускайте его на сборочной машине или агенте, где установлен Resource Builder.- Эталон: файл
.rc/.res(или.exe/.dll), представляющий ресурсы, которые должна содержать каждая сборка; храните его вместе с исходным кодом.
Шаг 1: выберите эталон
Если ресурсы уже собираются из скрипта .rc
Если ресурсы вашего проекта описаны в файле .rc, хранящемся в системе контроля версий, всё готово: этот скрипт и есть эталон. -diff принимает .rc напрямую и компилирует его в памяти перед сравнением, поэтому ничего не нужно отдельно извлекать или фиксировать, а любое намеренное изменение ресурсов видно как обычная проверяемая разница скрипта. Переходите к шагу 2 и укажите для -diff путь src\App.rc.
Иначе извлеките эталон из заведомо рабочей сборки
Извлеките ресурсы из вашего последнего заведомо рабочего двоичного файла в файл .res:
sircc64.exe -e ALL LastGood.exe ci\baseline.res Файл .res содержит только ресурсы, а не весь двоичный файл, поэтому он остаётся небольшим. Однако это двоичный формат, поэтому сравнение baseline.res в системе контроля версий покажет лишь то, что он изменился, а не что именно изменилось. Для самой проверки это не важно: читаемый отчёт создаёт шаг -diff ниже, и сборка завершается с ошибкой независимо от того, что показывает ваша система контроля версий для этого файла.
Если вы хотите, чтобы извлечённый эталон можно было просматривать в запросе на слияние, извлеките его как скрипт RC. sircc64.exe этого не умеет (он записывает только двоичные .res); форматированное извлечение скрипта RC — возможность основного приложения в консольном режиме:
Resbldr5.exe -e ALL LastGood.exe ci\baseline.rc -style P -nowait Структурные изменения (добавленный или удалённый ресурс, изменённая строка, перемещённый элемент диалога) тогда видны в сравнении как читаемый текст. Встроенные иконки и изображения по-прежнему представлены длинными шестнадцатеричными блоками, поэтому здесь возможен шум. -diff принимает эталон .rc так же легко, как и .res.
В любом случае зафиксируйте эталон и относитесь к нему как к договорённости: он меняется только тогда, когда кто-то намерен изменить ресурсы, в проверенном коммите.
Шаг 2: сравнивайте каждую сборку с эталоном
Когда сборка создаст двоичный файл, сравните его с эталоном. Подставьте ниже путь к вашему эталону: src\App.rc, если вы используете исходный скрипт, или ci\baseline.res / ci\baseline.rc, если вы извлекли эталон.
sircc64.exe -diff src\App.rc build\App.exe -failmissing -parsestringtable -failonmissingstring > ci\resource-diff.txt -failmissingустанавливает код завершения 1, если какой-либо ресурс эталона отсутствует в новой сборке.-parsestringtableразбирает наборыSTRINGTABLEна отдельные строки.-failonmissingstringустанавливает код завершения 1, если отдельная строка была удалена из набора.
Добавленные ресурсы и изменения содержимого на месте сами по себе проверку не проваливают. Это правильное поведение по умолчанию: CI должен блокировать сборку при «что-то пропало», а не при каждой правке. Полный отчёт в любом случае записывается в ci\resource-diff.txt, чтобы вы могли прочитать, что изменилось.
Отчёт выглядит так:
~ VERSION 1 (lang 1033, 1096 -> 1088 bytes)
- BITMAP BITMAP_SPLASH (lang 0, 65576 bytes)
- STRING 4067 (lang 0, 96 bytes)
string 65071: removed (was "Some string value")
~ STRING 4068 (lang 0, 556 -> 548 bytes)
string 65072: changed ("Invalid stream operation 1234" -> "Invalid stream operation")
~ STRING 4069 (lang 0, 986 -> 988 bytes)
~ STRING 4071 (lang 0, 2902 -> 2904 bytes)
~ STRING 4073 (lang 0, 702 -> 704 bytes)
~ STRING 4078 (lang 0, 986 -> 988 bytes)
+ GROUP_ICON MAINICON (lang 1033, 188 bytes)
+ ICON 1 (lang 1033, 49135 bytes)
+ ICON 10 (lang 1033, 1128 bytes)
+ ICON 11 (lang 1033, 872 bytes)
+ ICON 12 (lang 1033, 1384 bytes)
+ ICON 13 (lang 1033, 296 bytes)
+ ICON 2 (lang 1033, 9640 bytes)
+ ICON 3 (lang 1033, 7336 bytes)
+ ICON 4 (lang 1033, 3752 bytes)
+ ICON 5 (lang 1033, 1640 bytes)
+ ICON 6 (lang 1033, 4264 bytes)
+ ICON 7 (lang 1033, 3240 bytes)
+ ICON 8 (lang 1033, 2216 bytes)
+ ICON 9 (lang 1033, 744 bytes)
Added: 14, Removed: 2, Changed: 6
Removed strings: 1
- — удалено, + — добавлено, ~ — изменено. Здесь сборка потеряла растровое изображение и строку, поэтому проверка завершается ошибкой.
Коды завершения
0 означает, что эталон присутствует полностью. 1 означает либо настоящую ошибку (например, файл, не являющийся допустимым двоичным файлом), либо срабатывание -failmissing / -failonmissingstring. В обоих случаях конвейер должен остановиться. Строже: ошибка при любом различии
Если вы хотите остановить сборку при любом изменении, включая добавленные и изменённые ресурсы, проверьте итоговую строку:
findstr /C:"Added: 0, Removed: 0, Changed: 0" ci\resource-diff.txt >nul || exit /b 1
Шаг 3: подключите к CI
Обычный пакетный файл или PowerShell
sircc64.exe -diff src\App.rc build\App.exe -failmissing -parsestringtable -failonmissingstring > ci\resource-diff.txt
if errorlevel 1 (
echo Resource regression detected. See ci\resource-diff.txt
type ci\resource-diff.txt
exit /b 1
) (src\App.rc здесь означает тот эталон, который вы выбрали на шаге 1.)
GitHub Actions
Ненулевой код завершения автоматически завершает шаг ошибкой. Загрузите отчёт, чтобы он был доступен независимо от того, прошёл шаг или нет:
- name: Resource regression check
run: sircc64.exe -diff src\App.rc build\App.exe -failmissing -parsestringtable -failonmissingstring | Tee-Object ci\resource-diff.txt
shell: pwsh
- name: Upload resource diff
if: always()
uses: actions/upload-artifact@v4
with:
name: resource-diff
path: ci/resource-diff.txt На раннере должен быть установлен Resource Builder, поэтому используйте собственный Windows-раннер или более ранний шаг настройки, который его устанавливает.
Настройка проверки
- Игнорируйте ресурсы, которые меняются в каждой сборке. Если вы записываете номер сборки в блок RCDATA или пользовательский тип, исключите его:
-ignoretype BUILDINFO. Можно перечислить несколько:-ignoretype BUILDINFO,SIGNATURE. - Важно только наличие, а не содержимое. Добавьте
-nochanges, чтобы получать сведения о добавленных и удалённых ресурсах и полностью пропускать сравнение содержимого. - Обновление эталона — осознанное действие. Когда изменение ресурса задумано, создайте эталон заново тем же способом, каким создавали его впервые, и зафиксируйте в том же изменении, которое внесло правку, чтобы рецензент видел и то и другое.
Дополнительно: архивируйте ресурсы каждой сборки
Помимо проверки, храните для каждой сборки снимок самих файлов ресурсов как артефакт CI:
sircc64.exe -unpack -types ALL -o artifacts\resources build\App.exe Каждая иконка, растровое изображение, курсор, манифест и блок данных записывается в отдельный файл. Если что-то всё же проскочит, вы сможете пройти назад по артефактам сборок и увидеть, когда именно это изменилось.
Если ваш конвейер ещё и внедряет ресурсы
Если на более позднем этапе ресурсы внедряются в двоичный файл с помощью -la, -lr или -ls, а целевой файл уже подписан Authenticode, sircc64.exe по умолчанию отказывается это делать. Он продолжит только с явным -forcesign, который аннулирует подпись. Не используйте -forcesign в автоматическом конвейере, если сразу после него не выполняется повторная подпись: так сборка никогда не выпустит незаметно двоичный файл с нарушенной подписью.
Попробуйте
Resource Builder — коммерческий продукт с бесплатным 30-дневным пробным периодом. Скачайте его на странице загрузки, извлеките эталон командой sircc64.exe -e ALL и добавьте проверку -diff в следующий запуск конвейера.