По мере распространения Go и Rust появляется всё больше программ, которые состоят из одного бинарника без каких-либо нестандартных зависимостей, и которые мы устанавливаем руками, скачивая релиз с GitHub: либо потому, что данного приложения ещё нет в вашем дистрибутиве, либо потому, что просто хочется всегда иметь актуальную версию, а не ждать, когда её затянут в дистрибутив.
Ставить (а особенно обновлять) такие приложения руками – занятие неблагодарное, особенно когда их количество становится больше одного-двух – и хочется какой-то автоматизации. У меня таких программ около десятка, и довольно долгое время я пользовался различными наколеночными скриптами вроде этого для поддержания их актуальности. Но bash-скрипт – это всё-таки как-то несерьёзно, и поэтому всегда хотелось чего-то более управляемого в виде нормального приложения. Найти что-то готовое, что удовлетворяло бы всем моим потребностям, мне сходу не удалось – поэтому некоторое время назад решил пойти моим излюбленным путём и написать своё приложение под эту конкретную задачу.
binup
Пара недель кодинга по вечерам – и родилась утилита binup. Недавно я зарелизил версию 1.0.0 и полностью перешёл на неё со своих скриптов. Буду рад, если получившаяся тула будет полезна кому-то кроме меня.
Вот как она работает: вы создаёте конфигурационный файл ~/.config/binup/config.yaml
с примерно следующим содержимым, в котором описываете конкретное приложение (как его найти на GitHub):
tools:
binup:
project: KonishchevDmitry/binup
... запускаете binup install
или binup upgrade
– и тула устанавливает, либо обновляет указанные вами приложения.
Работает binup
довольно просто: она нигде не хранит никакую информацию об установленных приложениях, а вместо этого при запуске смотрит на их текущий статус: если нужного бинарника нет, то устанавливает его; если же есть, то пробует запустить приложение с --version
, чтобы определить текущую версию приложения и сравнить её с последним релизом на GitHub. Если же версию определить не удалось (к примеру, программа вовсе может не поддерживать флаг --version
), то binup
ориентируется на время модификации файла, которое при установке приложения задаёт равным времени модификации релиза.
Конфигурация
Вот пример конфигурационного файла со всеми доступными на данный момент опциями:
# Path where to install the binaries (the default is ~/.local/bin)
path: /usr/local/bin
tools:
# Binary name
prometheus:
# GitHub project name
project: prometheus/prometheus
# Changelog URL (will be printed on app upgrade)
changelog: https://github.com/prometheus/prometheus/blob/main/CHANGELOG.md
# Release archive pattern:
# * By default shell-like glob matching is used (https://docs.rs/globset/latest/globset/#syntax)
# * Pattern started with '~' is treated as regular expression (https://docs.rs/regex/latest/regex/#syntax)
#
# If it's not specified, the archive will be chosen automatically according to target platform.
release_matcher: prometheus-*.linux-amd64.tar.gz
# Binary path to look for inside the release archive. If it's not specified, the tool will try to find it automatically.
binary_matcher: "*/prometheus"
# Post-install script
post: systemctl restart prometheus
# If you have a lot of tools, you may hit GitHub API rate limits for anonymous requests at some moment.
# So it's recommended to obtain GitHub token (https://github.com/settings/tokens) and specify it here.
# No permissions are required for the token – it's needed just to make API requests non-anonymous.
github:
token: $token
Последующее развитие
Развивать её в какой-то полноценный пакетный менеджер вроде Homebrew я точно не планирую, но вот в рамках решения вышеописанной задачи – вполне.
Пока что из наиболее явных потенциальных фичей видится разве что поддержка GitLab, если возникнет такая необходимость (лично у меня пока что нет ни одного приложения с него). Прямо сейчас она закрывает все мои потребности, но вполне вероятно, что в процессе использования будут возникать новые – и тогда буду закрывать и их.
Комментарии (13)
OleSv
08.08.2024 06:17+2Посмотрите на eget https://github.com/zyedidia/eget
KonishchevDmitry Автор
08.08.2024 06:17Спасибо, но, судя по README, он, как минимум, не поддерживает post-install-скрипты. Для меня это прямо важно – начиная с того, чтобы рестартануть обновлённый сервис и заканчивая чем-то более сложным вроде:
set -eu prometheus-node-exporter --help > /etc/prometheus/help.dump systemctl restart prometheus-node-exporter curl -s http://localhost:9100/metrics | \ sed -r '/^#/ d; s/^([a-z_]+)(.*)/\1/' | \ sort -u > /etc/prometheus/metrics.dump cd /etc/prometheus && git diff help.dump metrics.dump >&2
– к примеру, вот такой post-install скрипт у меня настроен для Prometheus Node Exporter, чтобы отслеживать, какие новые интересные метрики появились в новом релизе.
Ну и ещё всякие мелочи – к примеру, binup при обновлении выводит ссылку на changelog – мелочь, но очень удобно. :)
Revertis
08.08.2024 06:17В код пока не смотрел, но как вы подменяете бинарник без остановки сервиса?
selivanov_pavel
08.08.2024 06:17В Linux имя файла - это просто название для конкретного inode в каталоге. Можно его менять или удалять, даже если из файла запущен процесс.
Revertis
08.08.2024 06:17У меня как будто не всегда это получается, поэтому и спрашиваю :-/
KonishchevDmitry Автор
08.08.2024 06:17+1Вы видимо пытались открыть именно этот файл и поправить – так да, нельзя. Но так никто никогда и не делает – вдруг вы по какой-то причине не допишете его до конца и тогда только всё запорете. Поэтому всегда в этой же директории создаётся новый файл с другим именем, туда пишутся данные + делается fsync(), чтобы они гарантированно записались на диск, а затем уже делается rename() – и новый файл атомарно заменяет старый.
Если погуглите, то даже сможете найти интересный хак, как можно восстановить удалённый файл через /proc, если есть хотя бы один процесс, у которого он ещё открыт. ;)
13werwolf13
08.08.2024 06:17+3простите но установку софта (а так же различного рантайма/библиотек/зависимостей/etc) в систему в обход штатного пакетного менеджера иначе как замусориванием и вредными советами я назвать не могу.
гораздо правильнее и полезнее потратить 5 секунд времени и помочь мейнтейнерам своего дистрибутива опакетить недостающее ПО, тем более что в случае go и rust это делается проще простого (пример spec для rpm проекта на rust) и даже не потребует тащить себе в систему ничего что требуется для сборки, это можно сделать вообще с телефона в браузере.
zorn-v100500
08.08.2024 06:17+2гораздо правильнее и полезнее потратить 5 секунд времени и помочь мейнтейнерам своего дистрибутива опакетить недостающее ПО
Удачи с дебианом за 5 секунд сделать )
А установку пакетов в обход стандартных репозиториев я считаю еще большим злом (как минимум нужен рут, а в пакет можно запихать скриптов на что фантазии хватит). И тем более добавление каких то левых реп в систему.
zorn-v100500
Бинарник для скачивания бинарников без зависимостей, сам имеет зависимости )
$ binup
binup: error while loading shared libraries: libssl.so.1.1: cannot open shared object file: No such file or directory
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 24.04 LTS
Release: 24.04
Codename: noble
$ apt show openssl
Package: openssl
Version: 3.0.13-0ubuntu3.2
...
KonishchevDmitry Автор
Да – имеет, но вполне себе стандартные:
У меня были мысли делать статическую сборку, но пока решил обойтись без этого:
liblzma
вроде должен быть на любой системе. К примеру, та же Ubuntu мне не даёт его удалить, т. к. на него завязано слишком много всего.libssl
– да, вы правы – та версия, с которой слинкован мой бинарник, довольно старая (релиз специально собирался на старом LTS, чтобы не было проблем с glibc на новых дистрибутивах), и на моём сервере эта версия установлена как зависимость пакетов, которые не установлены из коробки:The following packages will be REMOVED: libdns1104 libisc1100 libpython3.7 libpython3.7-minimal libpython3.7-stdlib libssl1.1 mongo-tools mongodb mongodb-clients mongodb-server mongodb-server-core
. Тут я не прав.Все остальные зависимости довольно стандартные – с ними проблем вроде бы быть не должно (или я ошибаюсь?) – релиз специально собирается на относительно древней 20.04, а у glibc, насколько я знаю, в этом месте с совместимостью всё хорошо.
В общем, решил, что в первом релизе можно пока с этим не мудрить. Но спасибо, замечание валидное – посмотрю, как тут лучше сделать. То, что оно у вас из коробки не запустилось – действительно нехорошо, это надо исправлять.
KonishchevDmitry Автор
Поправил. Теперь вот так:
На выходных напишу тесты на это дело и попробую разобраться с
liblzma
+ подумаю – может всё-таки перейду на musl и буду линковаться с ним – тогда будет вообще 100% статика. Но я к нему отношусь несколько настороженно – поэтому изначально и не стал идти в сторону статической линковки.