Последний релиз GitHub с curl и jq: выбираем один файл для загрузки
Используйте curl, чтобы получить последний релиз GitHub, и jq, чтобы выбрать ровно один файл для нужных ОС, CPU и формата. Скрипт ниже останавливается при ошибках HTTP, неверном JSON, отсутствии или дублировании совпадений; он возвращает метаданные без загрузки файла.
Конечная точка releases/latest не вычисляет численно наибольший тег Git. Документация API релизов различает обычные теги, черновики, предварительные и опубликованные релизы.
Пример запрашивает в публичном репозитории adoptium/temurin17-binaries метаданные HotSpot JDK для Linux x64 в формате tar.gz. Не нужны ни токен, ни фактическая загрузка файла. Если аргумент репозитория не указан, используется это значение по умолчанию; явно пустой аргумент отклоняется.
Порядок обработки
Ошибка HTTP останавливает скрипт до разбора JSON. Допустимый объект релиза должен содержать ровно один подходящий ресурс; выбор его URL не проверяет загруженный файл.
Отделяем ошибку HTTP от ошибки выбора из JSON
Нужны curl, jq и POSIX sh. Сохраните скрипт в latest-release.sh.
latest-release.sh
#!/bin/sh
set -eu
if [ "$#" -gt 1 ]; then
printf '%s\n' 'usage: latest-release.sh OWNER/REPO' >&2
exit 2
fi
repo=${1-adoptium/temurin17-binaries}
case "$repo" in
*/*/*|/*|*/|*[!A-Za-z0-9_./-]*) printf '%s\n' 'usage: latest-release.sh OWNER/REPO' >&2; exit 2 ;;
esac
case "$repo" in
*/*) ;;
*) printf '%s\n' 'usage: latest-release.sh OWNER/REPO' >&2; exit 2 ;;
esac
api=${GITHUB_API_URL:-https://api.github.com}
response=$(curl --fail --silent --show-error --location --max-time 20 \
--header 'Accept: application/vnd.github+json' \
--header 'X-GitHub-Api-Version: 2026-03-10' \
"$api/repos/$repo/releases/latest") || exit 1
printf '%s' "$response" | jq --slurp --exit-status '
if length != 1 or (.[0] | type) != "object" then
error("expected one release object")
else .[0] end
| . as $release
| if (.tag_name | type) != "string" or (.tag_name | length) == 0
or (.assets | type) != "array" then
error("missing release tag or assets")
else . end
| [.assets[] | select(.name | test("^OpenJDK17U-jdk_x64_linux_hotspot_.*\\.tar\\.gz$"))] as $assets
| if ($assets | length) != 1 then
error("expected exactly one Linux x64 JDK tar.gz asset")
elif ($assets[0].browser_download_url | type) != "string"
or ($assets[0].browser_download_url | length) == 0 then
error("missing asset download URL")
else {tag_name: $release.tag_name, download_url: $assets[0].browser_download_url} end
'
sh latest-release.sh adoptium/temurin17-binaries
При успехе результат — объект JSON с полями tag_name и download_url. Значения зависят от релиза на момент запроса. Ниже приведены тестовые данные для демонстрации структуры, а не утверждение о текущем последнем релизе:
{"tag_name":"jdk-17.0.8.1+1","download_url":"https://github.com/adoptium/temurin17-binaries/releases/download/example/OpenJDK17U-jdk_x64_linux_hotspot_example.tar.gz"}
curl --fail сначала обрабатывает ошибки HTTP. Сохранение ответа до разбора не даёт последней команде конвейера скрыть сетевую ошибку. См. параметры curl.
jq --slurp читает весь ввод в массив. Требование ровно одного значения JSON также отклоняет пустой ответ. Затем фильтр проверяет объект релиза, тег и массив ресурсов и выдаёт результат, только если совпало ровно одно имя файла. См. руководство jq.
Результаты, которые нужно учитывать в автоматизации
| Условие | Результат |
|---|---|
| Допустимый ответ и ровно один подходящий ресурс | Вывод JSON, код 0. |
| HTTP403/404 или ошибка соединения | Ненулевой код. |
| Пустое тело или некорректный JSON | Ненулевой код. |
| Нет подходящих ресурсов или их несколько | Ошибка вместо произвольного выбора. |
| Неверный синтаксис аргумента репозитория | Справка об использовании, код 2. |
Если HTTP-запрос выполнен успешно, но подходящего файла нет, проверьте имена файлов релиза, прежде чем менять фильтр jq: этот пример выбирает Java 17 для Linux x64, а не любой пакет JDK. Для Java 21 используйте пример выбора метаданных платформы Temurin. Если значения репозитория или платформы поступают из YAML, разберите и проверьте конфигурацию как данные, прежде чем передавать значения команде.
Локальные ответы HTTP использовались для проверки успеха, ошибок HTTP, неверного JSON, отсутствующих ресурсов и дублей. GITHUB_API_URL по умолчанию указывает на публичный API; его можно направить на тестовый сервер с тем же контрактом ответа.
Для чтения публичных ресурсов аутентификация не требуется. Если добавляете аутентифицированные запросы, не помещайте токены в исходный код и журналы трассировки Shell. Проверяйте заголовки ответа с ограничениями частоты и документацию GitHub по этим ограничениям.
Скрипт выбирает URL, но не проверяет целостность установочного файла. Автоматизация установки по-прежнему требует учёта контрольных сумм или подписей поставщика и целевой платформы. Прежде чем передавать тег в компаратор числовых версий, проверьте соответствие его входному контракту.
После скачивания выбранного файла сравните его контрольную сумму SHA-256 с ожидаемым значением из доверенного источника. Программа читает локальные байты; она не скачивает релиз и не удостоверяет личность издателя.
Обсуждение