Автоматическая проверка хеш-суммы файлов в Gitlab CI на примере Hugo

Логотип компании Gitlab

При деплое часто приходится сравнивать хеш-суммы файлов. Например, при развёртывании Hugo сайта через Gitlab CI. Заметка не позиционируется как прямое руководство к действию, а скорее как указание на то, с чем мне пришлось столкнутся. Быть может, кому-то она послужит источником полезной информации.

Официальный пример Hugo от Gitlab Pages предлагает такой способ:

image: alpine

variables:
  HUGO_VERSION: '0.23'
  HUGO_SHA: 'c9cf515067f396807161466c9968f10e61f762f49d766215db37b01402ca7ca7'

before_script:
  - apk update && apk add openssl ca-certificates
  - wget -O ${HUGO_VERSION}.tar.gz https://github.com/spf13/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_Linux-64bit.tar.gz
  - echo "${HUGO_SHA}  ${HUGO_VERSION}.tar.gz" | sha256sum -c
  - tar xf ${HUGO_VERSION}.tar.gz && mv hugo* /usr/bin/hugo
  - hugo version

test:
  script:
  - hugo
  except:
  - master

pages:
  script:
  - hugo
  artifacts:
    paths:
    - public
  only:
  - master

Посмотреть на этот файл можно и в Gitlab

Ничего сложного, простой типичный Gitlab CI скрипт. Но если для кого-то это представляет трудности, то кратко здесь происходит следующее: мы берём последний доступный docker-образ alpine linux, скачиваем дополнительные пакеты для корректной работы с SSL. Потом скачиваем архив Hugo с номером версии из переменной HUGO_VERSION, затем сравниваем SHA256-сумму этого файла c SHA256-суммой из переменной HUGO_SHA. После этого распаковываем и перемещаем файл hugo по пути /usr/bin/hugo и вызываем команду hugo version. Так мы проверяем, что Hugo работает, а заодно и номер версии узнаем. После происходит процесс тестирования и непосредственно сам деплой.

В чём минус такого решения? Каждый раз, когда нам необходимо обновить Hugo, приходится:

  1. изменять номер версии в переменной HUGO_VERSION
  2. вычислять хеш-сумму скачиваемого архива с HUGO в переменную HUGO_SHA

Учитывая, что в последнее время новые мажорные билды Hugo выходят слишком уж часто (приблизительно раз в месяц), этот процесс хотелось и стоило бы упростить до одного действия. А когда за неделю вышло сразу несколько минорных исправлений с исправлением кучи багфиксов, я понял, что надо срочно автоматизировать сравнение хеш-сумм, тем более, что начиная с версии v0.20.3, к каждому билду Hugo стали генерировать дополнительный текстовый файл с хешсуммами в формате SHA-256, серьёзно упростив задачу.

Для начала набросаем краткий план действий:

  1. Скачиваем файл hugo_checksums.txt
  2. Grep’аем название нашего билда
  3. Отрезаем вторую часть (с названием билда), разделённую от первой знаком табуляции, оставляем только хешсумму и кладём её в переменную.
  4. Сравниваем хешсумму из переменной с хешсуммой самого архива Hugo.
  5. Удаляем файл hugo_checksums.txt

Думаю, со скачиванием и с grep всё понятно без лишних объяснений, а вот чтобы отсечь лишнее, мы воспользуемся командой cut с ключом -f со значением 1:

cut -f 1

Конкретно эта команда означает то, что мы сохраняем только кусочек строки, до первого разделителя. По-умолчанию cut использует в качестве разделителя знак табуляции и это подходит для нашего файла hugo_checksums.txt. В случае если бы в файле использовался другой разделитель, его можно было явно задать ключом -d. Подробнее всегда можно почитать через man cut или в других источниках.

А вот чтобы заполучить последовательный результат выполнения нескольких команд в переменную, нужно использовать следующий синтаксис:

variable=$(command1 | command2)

Конкретно в нашем случае должно получится такое:

checksum=$(grep -i "hugo_${HUGO_VERSION}_Linux-64bit.tar.gz" hugo_checksums.txt | cut -f 1)

В итоге, в переменной checksum будет хранится контрольная хеш-сумма нашего архива. А дальше остаётся только добавить нашу переменную в строку сравнения sha256 и удалить файл hugo_checksums.txt.

Финальная версия файла gitlab-ci.yml после всех наших изменений будет выглядеть примерно так:

image: alpine

variables:
  HUGO_VERSION: '0.23'

before_script:
  - apk update && apk add openssl ca-certificates
  - wget https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_checksums.txt
  - wget -O ${HUGO_VERSION}.tar.gz https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_Linux-64bit.tar.gz
  - checksum=$(grep -i "hugo_${HUGO_VERSION}_Linux-64bit.tar.gz" hugo_checksums.txt | cut -f 1)
  - echo "$checksum  ${HUGO_VERSION}.tar.gz" | sha256sum -c
  - rm hugo_checksums.txt
  - tar xf ${HUGO_VERSION}.tar.gz && mv hugo* /usr/bin/hugo
  - hugo version

test:
  script:
  - hugo
  except:
  - master

pages:
  script:
  - hugo
  artifacts:
    paths:
    - public
  only:
  - master

Теперь достаточно изменить номер версии в файле gitlab-ci.yml и сравнение хеш-суммы произойдёт при билде автоматически. Главное, чтобы разработчики Hugo ничего не «поломали» в последующих версиях. В последнее время они частенько проворачивают подобные трюки с изменением форматов своих билдов.