ОБНОВЛЕНИЕ: Установка мессенджера MAX в Calculate Linux:
Создание ebuild MAX с актуальной версией:
Добавление нестабильных версий пакета:
#
echo "net-im/max ~amd64" >> /etc/portage/package.accept_keywords/custom
Создание директории ebuild MAX:
#
mkdir -p /var/calculate/repos/custom/net-im/max
В прошлой версии скрипта и создаваемые им ebuild не работали звонки, в новой вроде все работает. Так же добавлено подробное описание.
Скрипт создает ebuild MAX с сохранением семи предыдущих версий:
sudo tee /usr/local/bin/generate-max-ebuild.sh <<'SCRIPT'
#!/bin/bash
# Скрипт проверки и создания ebuild для мессенджера MAX
# Устанавливает в /opt/max, с поддержкой звонков
set -e
REPO_DIR="/var/calculate/repos/custom/net-im/max"
DISTFILES_DIR="/var/calculate/distfiles"
EBUILD_NAME="max"
# Цвета для вывода
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Очистка при прерывании
trap 'echo -e "\n${RED}✖ Прервано пользователем${NC}"; exit 1' INT TERM
echo -e "${BLUE}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}║ Обновление ebuild для мессенджера MAX ║${NC}"
echo -e "${BLUE}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
# Показываем установленную версию
INSTALLED_VERSION=$(cat /var/db/pkg/net-im/max-*/PF 2>/dev/null | sed 's/net-im\/max-//' || echo "не установлен")
echo -e "${YELLOW}► Установленная версия:${NC} ${GREEN}${INSTALLED_VERSION}${NC}"
echo ""
# Получаем последнюю версию из репозитория Debian
echo -e "${YELLOW}► Проверка последней версии в репозитории...${NC}"
PKG_INFO=$(curl -sL "https://download.max.ru/linux/deb/dists/stable/main/binary-amd64/Packages" | awk '
/^Package: max$/ { in_max=1 }
in_max && /^Version: / { ver=$2 }
in_max && /^Filename: / { fn=$2; exit }
END { if(ver && fn) print ver "|" fn }
')
if [[ -z "$PKG_INFO" ]]; then
echo -e "${RED}✖ Ошибка: не удалось получить информацию о пакете из репозитория${NC}"
exit 1
fi
VERSION="${PKG_INFO%|*}"
FILENAME="${PKG_INFO#*|}"
DEB_URL="https://download.max.ru/linux/deb/${FILENAME}"
echo -e "${GREEN}✓ Найдена версия:${NC} ${VERSION}"
echo -e "${GREEN}✓ URL для скачивания:${NC} ${DEB_URL}"
echo ""
# Проверяем, не существует ли уже ebuild с этой версией
if [[ -f "${REPO_DIR}/${EBUILD_NAME}-${VERSION}.ebuild" ]]; then
echo -e "${YELLOW}► Ebuild для версии ${VERSION} уже существует. Обновление не требуется.${NC}"
echo ""
echo -e "${GREEN}════════════════════════════════════════════════════════════${NC}"
echo -e "${GREEN} Установленная версия: ${INSTALLED_VERSION}${NC}"
echo -e "${GREEN} Актуальная версия: ${VERSION}${NC}"
echo -e "${GREEN}════════════════════════════════════════════════════════════${NC}"
exit 0
fi
# Создаём директории
mkdir -p "${REPO_DIR}"
mkdir -p "${DISTFILES_DIR}"
DEB_FILE="${DISTFILES_DIR}/${EBUILD_NAME}-${VERSION}.deb"
DEB_VALID=0
# Проверяем существующий файл
if [[ -f "${DEB_FILE}" ]]; then
echo -e "${YELLOW}► Найден существующий .deb файл, проверка целостности...${NC}"
if ar t "${DEB_FILE}" > /dev/null 2>&1; then
FILE_TYPE=$(file -b "${DEB_FILE}")
if [[ "$FILE_TYPE" =~ "Debian binary package" ]]; then
FILE_SIZE=$(du -h "${DEB_FILE}" | cut -f1)
echo -e "${GREEN}✓ Файл целостен (${FILE_SIZE})${NC}"
DEB_VALID=1
else
echo -e "${RED}✖ Файл повреждён (неверный тип: ${FILE_TYPE})${NC}"
echo -e "${YELLOW}► Удаляем битый файл...${NC}"
rm -f "${DEB_FILE}"
fi
else
echo -e "${RED}✖ Файл повреждён (ar не может прочитать структуру)${NC}"
echo -e "${YELLOW}► Удаляем битый файл...${NC}"
rm -f "${DEB_FILE}"
fi
fi
# Скачиваем, если файл не валиден
if [[ "${DEB_VALID}" -eq 0 ]]; then
echo -e "${YELLOW}► Скачивание пакета...${NC}"
echo ""
# Скачиваем во временный файл, чтобы при обрыве не оставлять битый .deb
TMP_FILE="${DEB_FILE}.tmp"
rm -f "${TMP_FILE}"
if curl -L --fail --progress-bar -o "${TMP_FILE}" "${DEB_URL}"; then
# Проверяем скачанный временный файл
echo ""
echo -e "${YELLOW}► Проверка скачанного файла...${NC}"
if ar t "${TMP_FILE}" > /dev/null 2>&1; then
FILE_TYPE=$(file -b "${TMP_FILE}")
if [[ "$FILE_TYPE" =~ "Debian binary package" ]]; then
mv "${TMP_FILE}" "${DEB_FILE}"
FILE_SIZE=$(du -h "${DEB_FILE}" | cut -f1)
echo -e "${GREEN}✓ Пакет проверен и сохранён (${FILE_SIZE})${NC}"
DEB_VALID=1
else
echo -e "${RED}✖ Ошибка: скачанный файл не является валидным .deb пакетом!${NC}"
echo -e "${RED} Тип файла: ${FILE_TYPE}${NC}"
rm -f "${TMP_FILE}"
exit 1
fi
else
echo -e "${RED}✖ Ошибка: скачанный файл повреждён (ar не может прочитать структуру)${NC}"
rm -f "${TMP_FILE}"
exit 1
fi
else
echo ""
echo -e "${RED}✖ Ошибка: не удалось скачать пакет (обрыв соединения или недоступен сервер)${NC}"
rm -f "${TMP_FILE}"
exit 1
fi
fi
# Двойная проверка перед продолжением
if [[ "${DEB_VALID}" -ne 1 ]]; then
echo -e "${RED}✖ Критическая ошибка: .deb файл не прошёл проверку целостности${NC}"
exit 1
fi
# Удаляем старые ebuild'ы, оставляем только 7 последних
echo ""
echo -e "${YELLOW}► Очистка старых ebuild (оставляем 7 последних)...${NC}"
cd "${REPO_DIR}"
ls -1t max-*.ebuild 2>/dev/null | tail -n +8 | while read old_ebuild; do
echo -e " ${RED}Удаляем:${NC} ${old_ebuild}"
rm -f "${old_ebuild}" || true
done
echo -e "${GREEN}✓ Очистка завершена${NC}"
echo ""
# Создаём ebuild
echo -e "${YELLOW}► Создание ebuild...${NC}"
cat > "${REPO_DIR}/${EBUILD_NAME}-${VERSION}.ebuild" <<EOBUILD
# Copyright 2025 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
EAPI=8
inherit unpacker xdg desktop
DESCRIPTION="Russian messenger and digital platform"
HOMEPAGE="https://max.ru"
SRC_URI="${DEB_URL} -> max-\${PV}.deb"
LICENSE="MAX-EULA"
SLOT="0"
KEYWORDS="~amd64"
RESTRICT="strip mirror"
DEPEND="
x11-libs/gtk+:3
x11-libs/libnotify
dev-libs/nss
x11-libs/libXtst
app-accessibility/at-spi2-core
app-crypt/libsecret
x11-libs/libXScrnSaver
x11-misc/xdg-utils
sys-apps/util-linux
x11-libs/libXres
"
RDEPEND="\${DEPEND}
net-libs/grpc
dev-libs/openssl:0/3
sys-libs/libcap
"
BDEPEND=""
QA_PRESTRIPPED="
/opt/max/bin/max
/opt/max/bin/crashpad_handler
/opt/max/bin/max-service/bin/*
/opt/max/bin/max-service/lib64/*
/opt/max/lib64/*
/opt/max/lib/*
"
S="\${WORKDIR}"
src_unpack() {
unpack_deb max-\${PV}.deb
}
src_install() {
# === Установка в /opt/max ===
mkdir -p "\${D}"/opt/max || die
cp -a usr/share/max/. "\${D}"/opt/max/ || die
# Исполняемые файлы
chmod +x "\${D}"/opt/max/bin/max
chmod +x "\${D}"/opt/max/bin/crashpad_handler
chmod +x "\${D}"/opt/max/bin/max-service/bin/max-service
# === ФИКС ЗВОНКОВ: Удаляем конфликтующие системные библиотеки ===
local max_libdir="\${D}/opt/max/bin/max-service/lib64"
local sys_libs="
libmount.so.1
libpcre2-8.so.0
libblkid.so.1
libselinux.so.1
libgio-2.0.so.0
libglib-2.0.so.0
libgobject-2.0.so.0
libgmodule-2.0.so.0
"
for lib in \${sys_libs}; do
if [[ -f "\${max_libdir}/\${lib}" ]]; then
elog "Removing conflicting system library: \${lib}"
rm -f "\${max_libdir}/\${lib}" || true
fi
done
# === ФИКС ЗВОНКОВ: Симлинк libsystemd.so.0 ===
if [[ -f "\${max_libdir}/libsystemd.so.0.23.0" ]]; then
rm -f "\${max_libdir}"/libsystemd.so.0 "\${max_libdir}"/libsystemd.so.0.bak 2>/dev/null || true
ln -s libsystemd.so.0.23.0 "\${max_libdir}"/libsystemd.so.0
fi
# === Обёртка /usr/bin/max ===
rm -f "\${D}"/usr/bin/max 2>/dev/null || true
mkdir -p "\${D}"/usr/bin || die
cat > "\${D}"/usr/bin/max <<'WRAPPER'
#!/bin/bash
# Wrapper для MAX — запускает max-service перед основным приложением
MAX_SERVICE="/opt/max/bin/max-service/bin/max-service"
MAX_BIN="/opt/max/bin/max"
# Проверяем, запущен ли max-service
if ! pgrep -f "\$MAX_SERVICE" > /dev/null 2>&1; then
# Запускаем max-service в фоне
"\$MAX_SERVICE" &
sleep 2
fi
# Запускаем основное приложение
exec "\$MAX_BIN" "\$@"
WRAPPER
chmod +x "\${D}"/usr/bin/max
# === Desktop-файл ===
if [[ -f usr/share/applications/max.desktop ]]; then
sed -e 's|Exec=.*|Exec=/usr/bin/max %U|' \
-e 's|Icon=.*|Icon=/opt/max/icons/max.png|' \
-e 's|DBusActivatable=.*|DBusActivatable=true|' \
-e 's|StartupWMClass=.*|StartupWMClass=max|' \
usr/share/applications/max.desktop > "\${T}"/max.desktop || die
domenu "\${T}"/max.desktop
fi
# === Иконки ===
if [[ -d usr/share/icons ]]; then
insinto /usr/share
doins -r usr/share/icons
fi
if [[ -d usr/share/pixmaps ]]; then
insinto /usr/share
doins -r usr/share/pixmaps
fi
# Симлинк на основную иконку в /opt/max/icons
mkdir -p "\${D}"/opt/max/icons || die
if [[ -f "\${D}"/usr/share/pixmaps/max.png ]]; then
ln -s /usr/share/pixmaps/max.png "\${D}"/opt/max/icons/max.png 2>/dev/null || true
elif [[ -f "\${D}"/usr/share/icons/hicolor/256x256/apps/max.png ]]; then
ln -s /usr/share/icons/hicolor/256x256/apps/max.png "\${D}"/opt/max/icons/max.png 2>/dev/null || true
fi
# === D-Bus сервис ===
insinto /usr/share/dbus-1/services
cat > "\${T}"/ru.max.MAX.service <<'DBUS'
[D-BUS Service]
Name=ru.max.MAX
Exec=/usr/bin/max
DBUS
doins "\${T}"/ru.max.MAX.service
}
pkg_preinst() {
# Полная очистка старой установки
if [[ -d "\${EROOT}/opt/max" ]]; then
elog "Removing old max installation..."
find "\${EROOT}/opt/max" -type f -exec chattr -i {} + 2>/dev/null || true
rm -rf "\${EROOT}/opt/max" || true
fi
rm -f "\${EROOT}/usr/bin/max" 2>/dev/null || true
rm -f "\${EROOT}/usr/share/applications/max.desktop" 2>/dev/null || true
}
pkg_postinst() {
xdg_desktop_database_update
xdg_icon_cache_update
elog "MAX \${PV} установлен в /opt/max."
elog ""
elog "Возможности:"
elog " - Звонки работают (max-service запускается автоматически)"
elog " - Системные библиотеки приоритетнее bundled"
elog ""
elog "Запуск:"
elog " max"
elog " Или через иконку в меню приложений"
elog ""
elog "Если звонки не работают:"
elog " pgrep -f max-service"
elog " ldd /opt/max/bin/max-service/bin/max-service"
}
pkg_postrm() {
xdg_desktop_database_update
xdg_icon_cache_update
}
EOBUILD
# Пересоздаём Manifest ТОЛЬКО после успешной проверки .deb
echo -e "${YELLOW}► Обновление Manifest...${NC}"
cd "${REPO_DIR}"
rm -f Manifest
ebuild "${EBUILD_NAME}-${VERSION}.ebuild" manifest > /dev/null 2>&1
echo -e "${GREEN}✓ Manifest обновлён${NC}"
echo ""
# Итоговый вывод
echo -e "${GREEN}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${GREEN}║ Ebuild успешно создан! ║${NC}"
echo -e "${GREEN}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
echo -e "${YELLOW}► Информация:${NC}"
echo -e " Файл: ${GREEN}${REPO_DIR}/${EBUILD_NAME}-${VERSION}.ebuild${NC}"
echo -e " Версия: ${GREEN}${VERSION}${NC}"
echo ""
echo -e "${YELLOW}► Доступные версии:${NC}"
ls -1t max-*.ebuild 2>/dev/null | sed 's/max-//;s/.ebuild//' | while read v; do
if [[ "$v" == "$VERSION" ]]; then
echo -e " ${GREEN}→ net-im/max-${v} (новая)${NC}"
else
echo -e " net-im/max-${v}"
fi
done
echo ""
echo -e "${YELLOW}► Установка/Обновление:${NC}"
echo -e " ${BLUE}emerge -av =net-im/max-${VERSION}${NC}"
echo ""
SCRIPT
chmod +x /usr/local/bin/generate-max-ebuild.sh
Создать/обновить ebuild:
#
generate-max-ebuild.sh
Установить последнюю версию:
#
emerge -av net-im/max
Вот подробное описание скрипта update-max-ebuild.sh с полным раскрытием всех функций и технических деталей:
Описание скрипта generate-max-ebuild.sh
Назначение
Скрипт автоматически проверяет наличие новых версий мессенджера MAX в официальном Debian-репозитории разработчика, скачивает .deb пакет, проверяет его целостность и генерирует Gentoo ebuild с интегрированными фиксами для корректной работы функции звонков.
Технические характеристики
| Параметр |
Значение |
| Язык |
Bash |
| EAPI |
8 |
| Установочный префикс |
/opt/max (FHS-совместимый путь для сторонних бинарных пакетов) |
| Репозиторий |
/var/calculate/repos/custom/net-im/max |
| Distfiles |
/var/calculate/distfiles |
| Хранение версий |
Последние 7 ebuild (автоочистка старых) |
Алгоритм работы скрипта
┌─────────────────────────────────────────────────────────────┐
│ 1. Определение установленной версии из /var/db/pkg │
│ 2. Запрос Packages из репозитория download.max.ru │
│ 3. Парсинг Version и Filename │
│ 4. Проверка: существует ли ebuild для этой версии? │
│ ├── Да → Выход (обновление не требуется) │
│ └── Нет → Продолжаем │
│ 5. Проверка/скачивание .deb с прогресс-баром │
│ 6. Верификация целостности .deb (ar + file) │
│ ├── Битый → Удаление + повторная попытка │
│ └── Целый → Продолжаем │
│ 7. Очистка старых ebuild (оставляем 7 последних) │
│ 8. Генерация ebuild с интегрированными фиксами │
│ 9. Создание Manifest (только после успешной проверки) │
│ 10. Вывод итоговой информации │
└─────────────────────────────────────────────────────────────┘
Интегрированные фиксы для звонков
1. Удаление конфликтующих системных библиотек
Проблема: Пакет MAX содержит собственные копии системных библиотек в /opt/max/bin/max-service/lib64/. Бинарник max-service имеет жёстко прописанный RPATH=$ORIGIN/../lib64, что заставляет динамический загрузчик искать библиотеки сначала в этом каталоге.
Конфликт: Библиотеки из Debian Stable (на котором собран MAX) старше, чем в Gentoo. Например:
libmount.so.1 из MAX не содержит символа MOUNT_2_40, который требуется системной libgio-2.0.so.0
libpcre2-8.so.0 имеет несовместимую версионную информацию
Решение: При установке автоматически удаляются следующие библиотеки из max-service/lib64/:
libmount.so.1 # → используется системная из /usr/lib64
libpcre2-8.so.0 # → используется системная из /usr/lib64
libblkid.so.1 # → используется системная из /usr/lib64
libselinux.so.1 # → используется системная из /usr/lib64
libgio-2.0.so.0 # → используется системная из /usr/lib64
libglib-2.0.so.0 # → используется системная из /usr/lib64
libgobject-2.0.so.0 # → используется системная из /usr/lib64
libgmodule-2.0.so.0 # → используется системная из /usr/lib64
Механизм: ld-linux.so.2 после удаления библиотек из RPATH-каталога находит их в стандартных системных путях (/usr/lib64, /lib64), где расположены актуальные версии.
2. Симлинк libsystemd.so.0
Проблема: В пакете отсутствует символическая ссылка libsystemd.so.0 → libsystemd.so.0.23.0. Бинарник max-service слинкован с libsystemd.so.0, но динамический загрузчик не находит файл с таким именем.
Ошибка без фикса:
error while loading shared libraries: libsystemd.so.0: cannot open shared object file: No such file or directory
Решение: Автоматическое создание симлинка:
ln -s libsystemd.so.0.23.0 /opt/max/bin/max-service/lib64/libsystemd.so.0
3. Обёртка /usr/bin/max
Проблема: Для работы звонков необходимо, чтобы фоновый процесс max-service был запущен до открытия основного окна мессенджера. При стандартном запуске /opt/max/bin/max сервис не стартует автоматически.
Решение: Создаётся Bash-обёртка /usr/bin/max, которая:
- Проверяет наличие процесса
max-service (pgrep -f)
- Если не запущен — стартует его в фоне (
&) с задержкой 2 секунды для инициализации
- Запускает основной бинарник
/opt/max/bin/max через exec (замещение процесса, без форка)
Код обёртки:
#!/bin/bash
MAX_SERVICE="/opt/max/bin/max-service/bin/max-service"
MAX_BIN="/opt/max/bin/max"
if ! pgrep -f "$MAX_SERVICE" > /dev/null 2>&1; then
"$MAX_SERVICE" &
sleep 2
fi
exec "$MAX_BIN" "$@"
4. Зависимость x11-libs/libXres
Проблема: max-service использует Qt6 XCB platform plugin, который требует библиотеку libXRes.so.1 (X11 Resource extension). В минимальных установках Gentoo эта библиотека может отсутствовать.
Ошибка без зависимости:
error while loading shared libraries: libXRes.so.1: cannot open shared object file: No such file or directory
Решение: Добавлена в DEPEND ebuild, что приводит к автоматической установке при emerge.
Установка в /opt/max
Обоснование: Согласно FHS (Filesystem Hierarchy Standard), каталог /opt предназначен для установки сторонних приложений, не входящих в базовую систему дистрибутива.
Структура установки:
/opt/max/
├── bin/
│ ├── max ← основной бинарник
│ ├── crashpad_handler ← обработчик крашей
│ └── max-service/
│ ├── bin/
│ │ └── max-service ← сервис звонков
│ └── lib64/ ← библиотеки сервиса
├── lib64/ ← основные библиотеки MAX
├── lib/ ← дополнительные библиотеки
└── icons/
└── max.png → /usr/share/pixmaps/max.png ← симлинк
Преимущества /opt:
- Изоляция от системных файлов
- Простое удаление (
rm -rf /opt/max)
- Соответствие стандартам для проприетарного ПО
Desktop-файл в меню приложений
Исходный файл из .deb содержит пути Debian (Exec=/usr/share/max/bin/max, Icon=max), несовместимые с Gentoo.
Модификации ebuild:
| Поле |
Было |
Стало |
Exec |
/usr/share/max/bin/max |
/usr/bin/max %U |
Icon |
max |
/opt/max/icons/max.png |
DBusActivatable |
— |
true |
StartupWMClass |
— |
max |
Результат: Корректное отображение в меню приложений GNOME/KDE/XFCE с рабочей иконкой и запуском через обёртку.
Защита от повреждённых файлов
| Этап |
Проверка |
Действие при ошибке |
Существующий .deb |
ar t + file |
Удаление, повторное скачивание |
| Скачивание |
curl --fail |
Удаление .tmp, выход с ошибкой |
| Временный файл |
ar t + file |
Удаление .tmp, выход с ошибкой |
| Финальный файл |
DEB_VALID=1 |
Блокировка создания ebuild |
Механизм атомарности: Скачивание ведётся в файл .tmp, который переименовывается в .deb только после успешной верификации. При обрыве соединения остаётся только битый .tmp, который удаляется.
Управление версиями
Автоочистка: При создании нового ebuild автоматически удаляются файлы старше 7-го по счёту (сортировка по времени модификации).
Пример хранения:
max-26.15.0.ebuild ← удалится при создании новой
max-26.15.1.ebuild ← удалится при создании новой
...
max-26.15.3.ebuild ← сохранится
max-26.15.4.ebuild ← текущая (новая)
Использование
Запуск скрипта:
#
generate-max-ebuild.sh
Установка конкретной версии:
#
emerge -av =net-im/max-26.15.4
Вывод скрипта (пример)
╔════════════════════════════════════════════════════════════╗
║ Обновление ebuild для мессенджера MAX ║
╚════════════════════════════════════════════════════════════╝
► Установленная версия: 26.15.3
► Проверка последней версии в репозитории...
✓ Найдена версия: 26.15.4
✓ URL для скачивания: https://download.max.ru/linux/deb/...
► Скачивание пакета...
############################################## 100.0%
► Проверка скачанного файла...
✓ Пакет проверен и сохранён (252M)
► Очистка старых ebuild (оставляем 7 последних)...
Удаляем: max-26.15.0.ebuild
✓ Очистка завершена
► Создание ebuild...
► Обновление Manifest...
✓ Manifest обновлён
╔════════════════════════════════════════════════════════════╗
║ Ebuild успешно создан! ║
╚════════════════════════════════════════════════════════════╝
► Доступные версии:
→ net-im/max-26.15.4 (новая)
net-im/max-26.15.3
► Установка/Обновление:
sudo emerge -av =net-im/max-26.15.4