Вы пользуетесь GitHub, пишете код и делаете прочие веселые штуки. Для повышения качества своей работы и оптимизации своего времени используете статический анализатор. И вот вам приходит идея - а почему бы не смотреть на ошибки, которые выдал анализатор, прямо в GitHub? Да и еще, чтобы это красиво выглядело. Что же делать в этом случае? Ответ очень простой. Ваш выбор – SARIF. О том что это такое, как это настроить, и будет рассказано в данной статье. Приятного чтения.
Что такое SARIF?
SARIF (Static Analysis Results Interchange Format) – это формат обмена результатами статического анализа на основе JSON для вывода инструментов статического анализа. То есть нам достаточно получить отчет анализатора в этом формате, и далее мы можем его использовать в тех продуктах, которые поддерживают данный формат - например, на GitHub или в Visual Studio Code.
Этот формат появился из-за того, что в мире инструментов статического анализа каждый создавал свой собственный выходной формат. Однако, даже если отчёты разных анализаторов представлены в одном и том же формате (например, JSON), структура самих отчетов может кардинально отличаться друг от друга. Поэтому один общий стандарт был лишь вопросом времени.
Данный формат (SARIF) быстро развивается, и его начинают использовать все чаще и чаще. Однако на данный момент у него есть небольшой недостаток. Иногда он меняет свою структуру, и приходится немного корректировать код, чтобы SARIF файл проходил валидацию. Однако это мелочи по сравнению с тем, какую пользу он несёт. В теории, в идеальном мире, достаточно получить отчет в этом формате и далее его можно открыть в любой программе\системе, которая работает с результатами статического анализа. Ну красота же!
Настройка репозитория GitHub
Чтобы GitHub начал анализировать SARIF файлы, сначала необходимо настроить репозиторий. При настройке мы пользовались вот этой инструкцией.
Итак, открываем свой репозиторий и нажимаем на "Security".
Находим по центру "Code scanning alerts" и нажимаем справа на кнопку "Set up code scanning".
Далее нажимаем на "Set up this workflow".
Теперь даем имя для yml файла (например upload-sarif.yml) и пишем следующее содержимое:
name: "Upload SARIF"
# Run workflow each time code is pushed to your repository and on a schedule.
# The scheduled workflow runs every at 00:00 on Sunday UTC time.
on:
push:
schedule:
- cron: '0 0 * * 0'
jobs:
build:
runs-on: ubuntu-latest
steps:
# This step checks out a copy of your repository.
- name: Checkout repository
uses: actions/checkout@v2
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v1
with:
# Path to SARIF file relative to the root of the repository
sarif_file: results.sarif
Выглядеть это должно следующим образом:
Теперь нажимаем "Start commit", пишем какое-то сообщение (например "Create upload-sarif.yml") и коммитим.
Отлично, мы настроили репозиторий! Можно приступать к получению SARIF файла.
Получение SARIF файла
Как вы поняли, SARIF – унифицированный стандарт, и получить его можно с помощью разных статических анализаторов и инструментов. В этой статье мы будем использовать PVS-Studio и PlogConverter. Обо всем об этом – далее.
Проверяем проект
Чтобы получить SARIF файл, сначала нам необходимо проверить проект с помощью статического анализатора. Поэтому в настроенный выше репозиторий мы добавили небольшой тестовый С++ проект с одним файлом для демонстрации. Ибо что мы будет проверять-то в самом деле? :) Вот содержимое файла:
#include <iostream>
void f(unsigned int ch)
{
unsigned int chx = -1;
if (ch >= 0x0fff0)
{
if ( !((ch >= 0x0FF10) && (ch <= 0x0FF19))
|| ((ch >= 0x0FF21) && (ch <= 0x0FF3A))
|| ((ch >= 0x0FF41) && (ch <= 0x0FF5A)))
{
ch = chx;
}
}
}
int main()
{
std::cout << "error" << std::endl;
}
Кстати, синтетический пример с ошибкой имеет реальный прототип, описанный в статье "Как PVS-Studio оказался внимательнее, чем три с половиной программиста".
Как уже было упомянуто выше, проверка будет осуществляться с помощью статического анализатора PVS-Studio. А именно - с помощью консольной утилиты "PVS-Studio_Cmd.exe". Данная утилита позволяет производить анализ C++, C# MSBuild-проектов на Windows. По умолчанию найти ее можно по пути "C:\Program Files (x86)\PVS-Studio". Подробнее о данной утилите Вы можете почитать здесь.
Если у вас нет лицензии для проверки, то не отчаивайтесь. Кликнув сюда, вы перейдёте на сайт анализатора, где сможете скачать его, а также получить триальную лицензию.
Собственно, приступаем к анализу. Чтобы его произвести достаточно выполнить вот эту команду:
PVS-Studio_Cmd.exe -t "D:\Use_SARIF_Example\BestProjectCpp.sln" \
-o "D:\Use_SARIF_Example\results.plog" -e "D:\Use_SARIF_Example\"
Рассмотрим строку запуска немного подробнее. Флаг "-t" является обязательным. Он позволяет указать объект для проверки (sln или csproj/vcxproj файл). Флаг "-o" отвечает за путь до файла, в который будут записаны результаты анализа. Флаг "-e" - корневая часть пути, которую PVS-Studio будет использовать при генерации относительных путей в предупреждениях. Нужно это потому, что отчет будет обрабатываться в облаке.
Отлично, теперь нужно преобразовать plog файл в SARIF файл. Для этого воспользуемся утилитой PlogConverter.
Преобразование из Plog в SARIF
Преобразование мы будем выполнять с помощью утилиты PlogConverter, поэтому пару слов о ней. PlogConverter – это утилита с открытым исходным кодом, предназначенная для преобразования отчетов анализатора PVS-Studio из одного формата в другой. Более подробно утилита описана в документации.
Итак, нам необходимо найти PlogConverter.exe на компьютере. Эта утилита устанавливается вместе с PVS-Studio и располагается рядом с "PVS-Studio_Cmd.exe". Переходим туда, открываем консоль и пишем следующую команду:
PlogConverter.exe "D:\Use_SARIF_Example\results.plog" \
-o "D:\Use_SARIF_Example" -t sarif -n results
Вот и все. Теперь можно заливать этот файл и смотреть результаты анализа.
Проверяем, что все работает
Чтобы проверить, что мы все сделали правильно, быстренько загрузим наш SARIF файл руками и посмотрим результаты анализа. Для этого переходим в репозиторий и нажимаем "Add file -> Upload files".
Далее добавляем SARIF файл и ждем, пока он обработается. Если хочется посмотреть, как там проходит обработка, то необходимо нажать на "Actions" и далее выбрать работающую задачу.
Как только все будет готово, заходим во вкладку "Security". В ней выбираем слева "Code scanning alerts -> PVS-Studio".
Справа у нас и будут сообщения анализатора. Откроем какое-нибудь предупреждение:
Мы видим:
Быстрый фильтр по ошибкам;
Сообщение об ошибке. Оно же и указывает, где именно в исходном коде находится ошибка;
Ссылка на документацию для предупреждения анализатора.
Реальный сценарий использования SARIF в GitHub
Пришло время посмотреть, как это все может работать на практике. Предлагаю рассмотреть сценарий, когда человек скачает репозиторий, выполнит какую-то работу и с помощью описанных выше команд, создаст SARIF файл и зальет изменения в отдельную ветку. В итоге такой сценарий позволит нам смотреть не только, какие файлы изменил пользователь, но и какие ошибки он допустил. Поэтому скачиваем репозиторий и делаем изменения в файле с C++ кодом:
#include <iostream>
void f(unsigned int ch)
{
unsigned int chx = -1;
if (ch >= 0x0fff0)
{
if (!((ch >= 0x0FF10) && (ch <= 0x0FF19))
|| ((ch >= 0x0FF21) && (ch <= 0x0FF3A))
|| ((ch >= 0x0FF41) && (ch <= 0x0FF5A)))
{
ch = chx;
}
}
}
int ComputeProjectionMatrixFOV(float fov)
{
float yScale = 1.0 / tan((3.141592538 / 180.0) * fov / 2);
return yScale;
}
int main()
{
std::cout << "error" << std::endl;
}
Далее проверяем файл, сохраняем отчет, получаем SARIF файл (заменяя им уже лежащий в скаченном репозитории) и делаем коммит в отдельную ветку. Все, пользователь сделал свое дело. Теперь черед смотреть ошибки.
Заходим в репозиторий. Далее "Security" -> "Code scanning alerts" -> "PVS-Studio" и справа в "Branch" выбираем нужную ветку. Смотрим результаты:
Как вы видите, сообщения об ошибках в ветке хранятся отдельно. Согласитесь, это достаточно удобно. При желании можно, например, создать bat файл, который будет запускать анализатор, конвертировать отчет в SARIF и заменять существующий SARIF файл.
Что можно делать результатами?
Итак, вы остались один на один с отчетом. Что доступно в вашем распоряжении? Первое, на что нужно обратить внимание, — это то, что все ошибки разделены на две группы. Это "Open" и "Closed". "Open" — это активные ошибки, которые мы не обработали. "Closed" – это ошибки, которые мы исправили или пометили как ложные.
Второе — это фильтры по статусам ошибок (закрытые, отрытые и все такое).
Еще есть фильтры по характеристикам ошибок. Например, можно отсортировать по номеру ошибки.
Также GitHub позволяет нам помечать сообщения как "false positive", "used in tests", и мое любимое "won't fix" :). Чтобы пометить сообщение, необходимо его выбрать (слева от сообщения есть checkbox) и справа вверху нажать на "Dismiss".
Сообщения, которые вы так разметите, не будут попадать в графу открытых ошибок при следующей загрузке SARIF файла.
Если у нас появилась необходимость вернуть сообщения в "Open", то это очень легко сделать. Для этого выбираем "Closed", далее выбираем то, что хотим вернуть, и нажимаем справа "Reopen".
Также обратите внимание, что если загрузить новый лог, то он перезаписывает текущие открытые ошибки. Если ошибки, которые были в "Open", не встретились в новом логе, то они попадают в "Closed". Поэтому рекомендуем использовать SARIF только для анализа всего проекта. Если вам требуется проанализировать только pull request, то для этого у нас есть ряд статей на эту тему. Например, вот эта. Использовать же SARIF для анализа pull request будет не очень удобно.
А работает только для C++?
Конечно же нет, вы вообще не зависите от языка. Все, что вам нужно – это инструмент статического анализа, который сможет проанализировать ваш код и создать SARIF файл. Например, используемый в этой статье PVS-Studio умеет анализировать C++, C#, Java. Поэтому давайте еще попробуем проверить код на C#, потому что это лучший язык в мире один из авторов статьи его любит. Для примера быстренько проделываем все то же самое, о чем говорилось в статье, но уже для C# проекта. Вот содержимое файла, который был проанализирован:
using System;
using System.Collections.Generic;
using System.Linq;
namespace TestSarif
{
class Program
{
static void Main()
{
var result = Formula42(3, 5);
}
static int Formula42(int? coefficientA, int? coefficientB)
{
var data = new List<int>();
if (coefficientA != null)
data.Add(Formula42(coefficientA.Value));
else if (coefficientB != null)
data.Add(Formula42(coefficientA.Value));
return data.SingleOrDefault();
}
static private int Formula42(int coefficient)
{
return coefficient;
}
}
}
Вот результат:
Ну и посмотрим на саму ошибку.
Вывод
Подводя итог, хочется сказать, что SARIF — это удобный формат, который позволяет просматривать результаты анализа. Да и настройка использования SARIF быстрая и простая. Например, в VS Code это вообще делается в пару кликов. Кстати, если вам будет интересно, как это сделать, то напишите об этом в комментариях. Да и вообще, если у вас есть какие-то пожелания по темам статьи, то напишите об этом.
Так что пробуйте и пользуйтесь. Спасибо за внимание.
Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Nikolay Mironov, Evgeniy Ovsannikov. How to Get Nice Error Reports Using SARIF.