feat: create autonomous Nivora package repository

Maintain a validated cross-distribution package catalog.
Automate upstream updates, isolated builds, diagnostics, and direct publication.
Build the official GitHub Desktop sources for Linux with working OAuth.
This commit is contained in:
Александр
2026-07-25 00:49:32 +10:00
commit f405ae2bcf
168 changed files with 8483 additions and 0 deletions
+330
View File
@@ -0,0 +1,330 @@
#!/bin/bash
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
repo_root="$(cd "${script_dir}/.." && pwd)"
cd "$repo_root"
mapfile -t all_packages < <(
for staplerfile in */Staplerfile; do
dirname "$staplerfile"
done | sort
)
packages=()
if [[ "${1:-}" == '--all' ]]; then
packages=("${all_packages[@]}")
shift
else
packages=("$@")
fi
[[ "${#packages[@]}" -gt 0 ]] || {
echo 'Использование: tools/verify_artifacts.sh {--all|package...}' >&2
exit 2
}
command -v rpm >/dev/null 2>&1 || {
echo 'Для проверки RPM требуется команда rpm' >&2
exit 2
}
command -v stplr-spec >/dev/null 2>&1 || {
echo 'Для проверки метаданных требуется stplr-spec' >&2
exit 2
}
contains_path() {
local pattern="$1"
local path
for path in "${payload[@]}"; do
# shellcheck disable=SC2053
[[ "$path" == $pattern ]] && return 0
done
return 1
}
for package in "${packages[@]}"; do
[[ -f "${package}/Staplerfile" ]] || {
echo "Неизвестный пакет: ${package}" >&2
exit 2
}
mapfile -t artifacts < <(find "$package" -maxdepth 1 -type f -name '*.rpm' -print)
[[ "${#artifacts[@]}" -eq 1 ]] || {
echo "${package}: ожидался один RPM, найдено ${#artifacts[@]}" >&2
exit 1
}
artifact="${artifacts[0]}"
package_name="$(stplr-spec get-field --path "${package}/Staplerfile" name)"
rpm_name="$(rpm -qp --queryformat '%{NAME}' "$artifact")"
[[ "$rpm_name" == "${package_name}+stplr-"* ]] || {
echo "${package}: неверное имя RPM: ${rpm_name}" >&2
exit 1
}
mapfile -t payload < <(rpm -qlp "$artifact")
contains_path "/usr/share/licenses/${package_name}/*" || {
echo "${package}: отсутствует лицензия в собственном namespace" >&2
exit 1
}
contains_path '/usr/share/licenses/LICENSE' && {
echo "${package}: обнаружен общий конфликтный путь лицензии" >&2
exit 1
}
recipe="$(<"${package}/Staplerfile")"
if [[ "$recipe" == *'files-find-binary'* ]]; then
contains_path '/usr/bin/*' || contains_path '/usr/sbin/*' || {
echo "${package}: files-find-binary не добавил исполняемые файлы" >&2
exit 1
}
fi
if [[ "$recipe" == *'files-find-desktop'* ]]; then
contains_path '/usr/share/applications/*.desktop' || {
echo "${package}: files-find-desktop не добавил desktop-файл" >&2
exit 1
}
fi
if [[ "$recipe" == *'files-find-systemd'* ]]; then
contains_path '/usr/lib/systemd/system/*' || {
echo "${package}: files-find-systemd не добавил unit-файл" >&2
exit 1
}
fi
if [[ "$package" == 'claude-desktop' ]]; then
for required_path in \
/usr/bin/claude-alt \
/usr/bin/claude-desktop-account2 \
/usr/lib/claude-desktop/resources/TrayIconLinux.png \
/usr/lib/claude-desktop/resources/TrayIconLinux-Dark.png \
/usr/lib/claude-alt/claude-alt-bin \
/usr/lib/claude-alt/resources/app.asar \
/usr/lib/claude-alt/resources/icon.png \
/usr/lib/claude-alt/resources/TrayIconLinux.png \
/usr/lib/claude-alt/resources/TrayIconLinux-Dark.png \
/usr/share/applications/com.anthropic.ClaudeAlt.desktop \
/usr/share/icons/hicolor/512x512/apps/claude-alt.png; do
contains_path "$required_path" || {
echo "${package}: отсутствует компонент ClaudeAlt: ${required_path}" >&2
exit 1
}
done
contains_path '/usr/share/applications/claude-desktop-account2.desktop' && {
echo "${package}: обнаружен устаревший desktop-файл второго профиля" >&2
exit 1
}
fi
if [[ "$package" == 'balena-etcher' ]]; then
for required_path in \
/usr/bin/balena-etcher \
/usr/lib/balena-etcher/balena-etcher \
/usr/lib/balena-etcher/balenaEtcher \
/usr/lib/balena-etcher/resources/etcher-util \
/usr/share/applications/balena-etcher.desktop \
/usr/share/pixmaps/balena-etcher.png; do
contains_path "$required_path" || {
echo "${package}: отсутствует компонент balenaEtcher: ${required_path}" >&2
exit 1
}
done
balena_command_target="$(
rpm -qp --dump "$artifact" |
awk '$1 == "/usr/bin/balena-etcher" {print $11}'
)"
balena_alias_target="$(
rpm -qp --dump "$artifact" |
awk '$1 == "/usr/lib/balena-etcher/balenaEtcher" {print $11}'
)"
[[ "$balena_command_target" == '../lib/balena-etcher/balena-etcher' ]] || {
echo "${package}: неверная ссылка /usr/bin/balena-etcher" >&2
exit 1
}
[[ "$balena_alias_target" == 'balena-etcher' ]] || {
echo "${package}: обнаружена битая upstream-ссылка balenaEtcher" >&2
exit 1
}
fi
if [[ "$package" == 'codex' ]]; then
for required_path in \
/usr/bin/codex-app \
/usr/bin/codex-computer-use-linux \
/opt/codex-app/resources/app.asar \
/opt/codex-app/resources/codex.asar \
/opt/codex-app/resources/plugins/openai-bundled/.agents/plugins/marketplace.json \
/opt/codex-app/resources/plugins/openai-bundled/plugins/computer-use/.codex-plugin/plugin.json \
/opt/codex-app/resources/plugins/openai-bundled/plugins/computer-use/.mcp.json \
/opt/codex-app/resources/plugins/openai-bundled/plugins/computer-use/assets/app-icon.png \
/opt/codex-app/resources/plugins/openai-bundled/plugins/computer-use/bin/codex-computer-use-linux \
/opt/codex-app/resources/plugins/openai-bundled/plugins/computer-use/bin/codex-computer-use-cosmic \
/opt/codex-app/resources/plugins/openai-bundled/plugins/computer-use/bin/computer-use-linux-cosmic \
/usr/share/applications/codex-app.desktop \
/usr/share/icons/hicolor/512x512/apps/codex-app.png; do
contains_path "$required_path" || {
echo "${package}: отсутствует upstream-компонент Codex: ${required_path}" >&2
exit 1
}
done
codex_command_target="$(
rpm -qp --dump "$artifact" |
awk '$1 == "/usr/bin/codex-app" {print $11}'
)"
[[ "$codex_command_target" == '/opt/codex-app/codex-app' ]] || {
echo "${package}: команда Codex запускается не напрямую: ${codex_command_target}" >&2
exit 1
}
codex_computer_use_target="$(
rpm -qp --dump "$artifact" |
awk '$1 == "/usr/bin/codex-computer-use-linux" {print $11}'
)"
[[ "$codex_computer_use_target" == '/opt/codex-app/resources/plugins/openai-bundled/plugins/computer-use/bin/codex-computer-use-linux' ]] || {
echo "${package}: неверная ссылка Computer Use: ${codex_computer_use_target}" >&2
exit 1
}
fi
if [[ "$package" == 'github-desktop' ]]; then
for required_path in \
/usr/bin/github-desktop \
/opt/github-desktop/desktop \
/opt/github-desktop/resources/app \
/usr/share/applications/github-desktop.desktop \
/usr/share/icons/hicolor/scalable/apps/github-desktop.svg; do
contains_path "$required_path" || {
echo "${package}: отсутствует компонент GitHub Desktop: ${required_path}" >&2
exit 1
}
done
github_desktop_target="$(
rpm -qp --dump "$artifact" |
awk '$1 == "/usr/bin/github-desktop" {print $11}'
)"
[[ "$github_desktop_target" == '/opt/github-desktop/desktop' ]] || {
echo "${package}: команда GitHub Desktop запускается не напрямую" >&2
exit 1
}
fi
if [[ "$package" == 'opencode' ]]; then
for required_path in \
/usr/bin/opencode-desktop \
/opt/OpenCode/ai.opencode.desktop \
/opt/OpenCode/resources/app.asar \
/usr/share/applications/opencode-desktop.desktop \
/usr/share/icons/hicolor/128x128/apps/ai.opencode.desktop.png; do
contains_path "$required_path" || {
echo "${package}: отсутствует upstream-компонент OpenCode: ${required_path}" >&2
exit 1
}
done
opencode_command_target="$(
rpm -qp --dump "$artifact" |
awk '$1 == "/usr/bin/opencode-desktop" {print $11}'
)"
[[ "$opencode_command_target" == '/opt/OpenCode/ai.opencode.desktop' ]] || {
echo "${package}: команда OpenCode запускается не напрямую: ${opencode_command_target}" >&2
exit 1
}
contains_path '/usr/lib/opencode-desktop/*' && {
echo "${package}: обнаружен удалённый wrapper OpenCode" >&2
exit 1
}
fi
if [[ "$package" == 'ventoy' ]]; then
for required_path in \
/usr/bin/ventoy \
'/opt/ventoy/VentoyGUI.*' \
/opt/ventoy/boot/boot.img \
/opt/ventoy/tool/VentoyWorker.sh \
/opt/ventoy/ventoy/ventoy.disk.img.xz \
/usr/share/applications/ventoy.desktop \
/usr/share/icons/hicolor/128x128/apps/ventoy.png; do
contains_path "$required_path" || {
echo "${package}: отсутствует компонент Ventoy: ${required_path}" >&2
exit 1
}
done
fi
if [[ "$package" == 'yandex-browser-stable' ]]; then
for command in cpio rpm2cpio; do
command -v "$command" >/dev/null 2>&1 || {
echo "${package}: для проверки desktop-файлов требуется ${command}" >&2
exit 2
}
done
for required_path in \
/usr/bin/yandex-browser \
/usr/bin/yandex-browser-stable \
/opt/yandex/browser/yandex-browser \
/opt/yandex/browser/yandex_browser \
/opt/yandex/browser/yandex_browser-sandbox \
/usr/share/appdata/yandex-browser.appdata.xml \
/usr/share/applications/ru.yandex.desktop.browser.desktop \
/usr/share/applications/yandex-browser.desktop \
/usr/share/icons/hicolor/256x256/apps/yandex-browser.png \
/usr/share/mime/packages/yandex-browser-yprotect.xml; do
contains_path "$required_path" || {
echo "${package}: отсутствует компонент Яндекс Браузера: ${required_path}" >&2
exit 1
}
done
contains_path '/etc/cron.daily/yandex-browser' && {
echo "${package}: обнаружена upstream cron-задача обновления" >&2
exit 1
}
contains_path '/etc/xdg/autostart/yandex-browser_user_setup.desktop' && {
echo "${package}: обнаружен нежелательный upstream autostart" >&2
exit 1
}
compatibility_desktop="$(
set +o pipefail
rpm2cpio "$artifact" |
cpio -i --quiet --to-stdout \
/usr/share/applications/yandex-browser.desktop
)"
canonical_desktop="$(
set +o pipefail
rpm2cpio "$artifact" |
cpio -i --quiet --to-stdout \
/usr/share/applications/ru.yandex.desktop.browser.desktop
)"
desktop_entry_hidden() {
awk '
$0 == "[Desktop Entry]" { in_entry = 1; next }
/^\[/ { in_entry = 0 }
in_entry && $0 == "NoDisplay=true" { found = 1 }
END { exit !found }
'
}
desktop_entry_hidden <<<"$compatibility_desktop" || {
echo "${package}: совместимый desktop-id виден в меню приложений" >&2
exit 1
}
if desktop_entry_hidden <<<"$canonical_desktop"; then
echo "${package}: canonical desktop-id ошибочно скрыт" >&2
exit 1
fi
fi
while read -r path _ _ _ mode _; do
[[ "$path" == /usr/bin/* || "$path" == /usr/sbin/* ]] || continue
[[ "$mode" == 0100755 || "$mode" == 0120000 ]] || {
echo "${package}: неверные права ${mode} у ${path}" >&2
exit 1
}
done < <(rpm -qp --dump "$artifact")
echo "OK: ${package} ($(basename "$artifact"), ${#payload[@]} путей)"
done
echo "OK: payload проверен для ${#packages[@]} пакетов"