diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..8f3fc601d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,103 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +EEPM (Etersoft EPM) is a universal package manager wrapper that provides a unified interface across all Linux distributions. It wraps native package managers (apt, yum, dnf, zypper, pacman, etc.) with consistent commands. + +Main commands: +- `epm` - package management (install, remove, search, etc.) +- `serv` - service management wrapper (systemd, sysvinit) +- `distr_info` - distribution detection utility + +## Development Setup + +No build step required - this is a shell script project. Run directly from checkout: +```bash +./bin/epm --help +./bin/epm play --list +``` + +## Testing + +Run individual test scripts from the `tests/` directory: +```bash +./tests/test_distr_info.sh +./tests/test_versions.sh +./tests/test_json.sh +``` + +Code quality checks: +```bash +./check_code.sh # Run shellcheck and checkbashisms on all scripts +./check_code.sh bin/epm-install # Check specific file +``` + +## Architecture + +### Core Scripts (`bin/`) + +- `epm` - main entry point, loads helpers via `load_helper` function +- `epm-sh-functions` - shared shell functions (output, colors, tty detection) +- `epm-*` - command implementations (epm-install, epm-remove, epm-search, etc.) +- `serv` - service management entry point +- `serv-*` - service command implementations +- `distr_info` - standalone distribution detection script +- `tools_*` - internal utility scripts + +The `epm` script uses dynamic helper loading: +```bash +load_helper epm-sh-functions # Load shared functions +load_helper epm-install # Load specific command +``` + +### Play Scripts (`play.d/`) + +Scripts for installing applications from official sources (not repositories). Each script: +- Sources `common.sh` for shared functionality +- Defines: `PKGNAME`, `DESCRIPTION`, `VERSION`, `URL`, `SUPPORTEDARCHES` +- Handles `--description`, `--package-name`, `--installed`, `--remove`, `--info`, `--update`, `--run` +- Downloads and installs packages using `epm` commands + +Example structure: +```bash +BASEPKGNAME=AppName +SUPPORTEDARCHES="x86_64" +DESCRIPTION="App description" +. $(dirname $0)/common.sh +# Download and install logic +``` + +### Repack Scripts (`repack.d/`) + +Scripts for converting packages between formats (rpm↔deb) with distro-specific fixes. + +### Pack Scripts (`pack.d/`) + +Scripts for creating packages from tarballs/binaries. Work in temp directory, return tarball via `return_tar`. + +### Prescription Scripts (`prescription.d/`) + +Meta-package recipes that install groups of related packages. + +## Key Functions in common.sh + +- `epm` - wrapper that shows commands being run +- `eget` - wget-like download utility (`epm tool eget`) +- `get_github_url` / `get_github_tag` - GitHub release helpers +- `install_pkgurl` / `install_pack_pkgurl` - package installation +- `is_glibc_enough` / `is_stdcpp_enough` - version requirement checks +- `fatal` / `info` - output helpers + +## Adding New Play Scripts + +1. Create `play.d/appname.sh` +2. Define required variables (PKGNAME, DESCRIPTION, SUPPORTEDARCHES) +3. Source `common.sh` +4. Implement download/version detection logic +5. Call `install_pkgurl` or `install_pack_pkgurl` + +## Shell Compatibility + +All scripts must be POSIX-compatible (avoid bashisms). Use `#!/bin/sh` for most scripts. The `check_code.sh` script verifies this with `checkbashisms`. diff --git a/CLAUDE.ru.md b/CLAUDE.ru.md new file mode 100644 index 000000000..ca837d660 --- /dev/null +++ b/CLAUDE.ru.md @@ -0,0 +1,103 @@ +# CLAUDE.ru.md + +Этот файл содержит руководство для Claude Code (claude.ai/code) при работе с кодом в этом репозитории. + +## Обзор проекта + +EEPM (Etersoft EPM) — универсальная обёртка над пакетными менеджерами, предоставляющая единый интерфейс для всех дистрибутивов Linux. Оборачивает нативные пакетные менеджеры (apt, yum, dnf, zypper, pacman и др.) едиными командами. + +Основные команды: +- `epm` — управление пакетами (установка, удаление, поиск и т.д.) +- `serv` — обёртка для управления сервисами (systemd, sysvinit) +- `distr_info` — утилита определения дистрибутива + +## Настройка для разработки + +Сборка не требуется — это проект на shell-скриптах. Запуск прямо из checkout: +```bash +./bin/epm --help +./bin/epm play --list +``` + +## Тестирование + +Запуск отдельных тестов из каталога `tests/`: +```bash +./tests/test_distr_info.sh +./tests/test_versions.sh +./tests/test_json.sh +``` + +Проверка качества кода: +```bash +./check_code.sh # Запуск shellcheck и checkbashisms для всех скриптов +./check_code.sh bin/epm-install # Проверка конкретного файла +``` + +## Архитектура + +### Основные скрипты (`bin/`) + +- `epm` — главная точка входа, загружает хелперы через функцию `load_helper` +- `epm-sh-functions` — общие shell-функции (вывод, цвета, определение tty) +- `epm-*` — реализации команд (epm-install, epm-remove, epm-search и др.) +- `serv` — точка входа для управления сервисами +- `serv-*` — реализации команд сервисов +- `distr_info` — автономный скрипт определения дистрибутива +- `tools_*` — внутренние утилиты + +Скрипт `epm` использует динамическую загрузку хелперов: +```bash +load_helper epm-sh-functions # Загрузка общих функций +load_helper epm-install # Загрузка конкретной команды +``` + +### Play-скрипты (`play.d/`) + +Скрипты для установки приложений с официальных сайтов (не из репозиториев). Каждый скрипт: +- Подключает `common.sh` для общей функциональности +- Определяет: `PKGNAME`, `DESCRIPTION`, `VERSION`, `URL`, `SUPPORTEDARCHES` +- Обрабатывает: `--description`, `--package-name`, `--installed`, `--remove`, `--info`, `--update`, `--run` +- Скачивает и устанавливает пакеты через команды `epm` + +Типичная структура: +```bash +BASEPKGNAME=AppName +SUPPORTEDARCHES="x86_64" +DESCRIPTION="Описание приложения" +. $(dirname $0)/common.sh +# Логика скачивания и установки +``` + +### Repack-скрипты (`repack.d/`) + +Скрипты для конвертации пакетов между форматами (rpm↔deb) с дистрибутив-специфичными исправлениями. + +### Pack-скрипты (`pack.d/`) + +Скрипты для создания пакетов из архивов/бинарников. Работают во временном каталоге, возвращают архив через `return_tar`. + +### Prescription-скрипты (`prescription.d/`) + +Рецепты мета-пакетов для установки групп связанных пакетов. + +## Ключевые функции в common.sh + +- `epm` — обёртка, показывающая выполняемые команды +- `eget` — wget-подобная утилита загрузки (`epm tool eget`) +- `get_github_url` / `get_github_tag` — хелперы для релизов GitHub +- `install_pkgurl` / `install_pack_pkgurl` — установка пакетов +- `is_glibc_enough` / `is_stdcpp_enough` — проверка требований к версиям +- `fatal` / `info` — хелперы вывода + +## Добавление новых play-скриптов + +1. Создать `play.d/appname.sh` +2. Определить обязательные переменные (PKGNAME, DESCRIPTION, SUPPORTEDARCHES) +3. Подключить `common.sh` +4. Реализовать логику скачивания/определения версии +5. Вызвать `install_pkgurl` или `install_pack_pkgurl` + +## Совместимость с shell + +Все скрипты должны быть POSIX-совместимыми (избегать башизмов). Использовать `#!/bin/sh` для большинства скриптов. Скрипт `check_code.sh` проверяет это через `checkbashisms`. diff --git a/bin/epm-desktop b/bin/epm-desktop index 5831c06df..b7d1d2e71 100644 --- a/bin/epm-desktop +++ b/bin/epm-desktop @@ -61,9 +61,7 @@ get_value() local de_name="$1" local key="$2" - local json="$(get_json $de_name)" - - get_json_value "$key" < "$json" + get_json $de_name | parse_json_value "$key" } @@ -73,9 +71,7 @@ get_values() local de_name="$1" local key="$2" - local json="$(get_json $de_name)" - - get_json_values "$key" < "$json" | xargs + get_json $de_name | parse_json_values "$key" | xargs } @@ -102,8 +98,7 @@ get_repo_version() local package=$(get_main_package "$de_name") - info "Trying get info of package $package from remote ALT DB ..." - local latest_version="$(eget --quiet --timeout 10 -O- $(__get_api_url $package) | get_json_value '["packages",0,"version"]')" + local latest_version="$(get_json_value "$(__get_api_url $package)" '["packages",0,"version"]')" if [ -n "$latest_version" ] ; then echo "$latest_version" diff --git a/bin/epm-epm_install b/bin/epm-epm_install index ce71c1776..e7ed04c95 100644 --- a/bin/epm-epm_install +++ b/bin/epm-epm_install @@ -87,7 +87,7 @@ __epm_korinf_install_eepm() # TODO: installing via task # as now, can't install one package from task if false && [ "$BASEDISTRNAME" = "alt" ] && [ -z "$direct" ] ; then - local task="$(docmd eget -O- https://eepm.ru/vendor/alt/task)" + local task="$(fetch_url https://eepm.ru/vendor/alt/task)" if [ -n "$task" ] ; then docmd epm install $task:eepm return diff --git a/bin/epm-packages b/bin/epm-packages index 593204acd..89c450c1e 100644 --- a/bin/epm-packages +++ b/bin/epm-packages @@ -21,10 +21,12 @@ load_helper epm-sh-warmup __epm_packages_help() { - message "package management list" - get_help HELPCMD $SHAREDIR/epm-packages + message "Print installed packages" + message "Options:" + get_help HELPOPTIONS $SHAREDIR/epm-packages message ' Examples: + epm qa --short epm packages --sort epm packages --sort=size epm packages --last @@ -94,18 +96,21 @@ epm_packages() local CMD case "$1" in - -h|--help|help) # HELPCMD: help + -h|--help|help) # HELPOPTIONS: help __epm_packages_help return ;; - --sort=size|--sort) # HELPCMD: list package(s) by size, most + --sort=size|--sort) # HELPOPTIONS: list package(s) by size, most __epm_packages_sort return ;; - --last|--sort=time) # HELPCMD: list package(s) by install time, most + --last|--sort=time) # HELPOPTIONS: list package(s) by install time, most __epm_packages_last return ;; + --short) # HELPOPTIONS: print out short package names + # for help entry only + ;; "") ;; *) diff --git a/bin/epm-play b/bin/epm-play index dae62a81c..7d62cb59a 100644 --- a/bin/epm-play +++ b/bin/epm-play @@ -327,7 +327,7 @@ __get_latest_package_version() [ -n "$pkg" ] || return 1 - ver="$(epm tool eget -q -O- "https://eepm.ru/app-versions/$pkg" 2>/dev/null)" + ver="$(fetch_url "https://eepm.ru/app-versions/$pkg")" if [ -n "$ver" ] ; then echo "$ver" diff --git a/bin/epm-provides b/bin/epm-provides index 4ded0b38a..7c81c9915 100644 --- a/bin/epm-provides +++ b/bin/epm-provides @@ -77,7 +77,7 @@ case $PMTYPE in if is_installed $pkg_names ; then CMD="rpm -q --provides" else - docmd apm system info --full --format=json $pkg_names | get_json_values "data packageInfo provides" + docmd apm system info --full --format=json $pkg_names | parse_json_values "data packageInfo provides" return fi ;; diff --git a/bin/epm-requires b/bin/epm-requires index 0312daac6..1550a0782 100644 --- a/bin/epm-requires +++ b/bin/epm-requires @@ -147,9 +147,9 @@ case $PMTYPE in return else if [ -n "$short" ] ; then - docmd apm system info --full --format=json $pkg_names | get_json_values "data packageInfo depends" | __epm_filter_out_base_alt_reqs | sed -e "s| .*||" + docmd apm system info --full --format=json $pkg_names | parse_json_values "data packageInfo depends" | __epm_filter_out_base_alt_reqs | sed -e "s| .*||" else - docmd apm system info --full --format=json $pkg_names | get_json_values "data packageInfo depends" | __epm_filter_out_base_alt_reqs + docmd apm system info --full --format=json $pkg_names | parse_json_values "data packageInfo depends" | __epm_filter_out_base_alt_reqs fi return fi diff --git a/bin/epm-sh-altlinux b/bin/epm-sh-altlinux index c3c21b849..d30055bc6 100644 --- a/bin/epm-sh-altlinux +++ b/bin/epm-sh-altlinux @@ -50,8 +50,7 @@ get_task_packages_list() local tn="$1" local res - showcmd "eget -q -O- $ALTTASKURL/$tn/plan/add-bin" - res="$(eget -q -O- $ALTTASKURL/$tn/plan/add-bin)" || return + res="$(fetch_url $ALTTASKURL/$tn/plan/add-bin)" || return echo "$res" | cut -f1-3 | grep -E "(noarch|$DISTRARCH)$" | cut -f1 } @@ -60,8 +59,7 @@ get_task_arepo_packages_list() local tn="$1" local res - showcmd "eget -q -O- $ALTTASKURL/$tn/plan/arepo-add-x86_64-i586" - res="$(eget -q -O- $ALTTASKURL/$tn/plan/arepo-add-x86_64-i586)" || return + res="$(fetch_url $ALTTASKURL/$tn/plan/arepo-add-x86_64-i586)" || return echo "$res" | cut -f1 } diff --git a/bin/epm-sh-functions b/bin/epm-sh-functions index f321205e2..94cd53e41 100644 --- a/bin/epm-sh-functions +++ b/bin/epm-sh-functions @@ -794,6 +794,12 @@ eget() $EGET "$@" } +fetch_url() +{ + info "Fetching $1 ..." + eget -q -O- "$1" +} + # FIXME: sudocmd_eget() { @@ -836,22 +842,47 @@ print_sha256sum() done } - -# copied from parse_json_value from play.d/common.sh -get_json_value() +parse_json_value() { local field="$1" echo "$field" | grep -q -E "^\[" || field='["'$field'"]' epm --quiet tool json -b | grep -m1 -F "$field" | sed -e 's|.*\][[:space:]]||' | sed -e 's|"\(.*\)"|\1|g' } -get_json_values() +# URL/file ["version"] +get_json_value() +{ + if is_url "$1" ; then + local toutput + toutput="$(fetch_url "$1")" || return + echo "$toutput" | parse_json_value "$2" + else + [ -s "$1" ] || fatal "File $1 is missed, can't get json" + parse_json_value "$2" < "$1" + fi +} + +parse_json_values() { local field="$1" echo "$field" | grep -q -E "^\[" || field="\[$(echo "$field" | sed 's/[^ ]*/"&"/g' | sed 's/ /,/g'),[0-9]*\]" epm --quiet tool json -b | grep "^$field" | sed -e 's|.*\][[:space:]]||' | sed -e 's|"\(.*\)"|\1|g' } +# URL/file ["version"] +get_json_values() +{ + if is_url "$1" ; then + local toutput + toutput="$(fetch_url "$1")" || return + echo "$toutput" | parse_json_values "$2" + else + [ -s "$1" ] || fatal "File $1 is missed, can't get json" + parse_json_values "$2" < "$1" + fi +} + + __epm_assure_7zip() { # install 7zip in any case (can be used) diff --git a/bin/epm-tool b/bin/epm-tool index 3fc967a7e..edf0c4220 100644 --- a/bin/epm-tool +++ b/bin/epm-tool @@ -59,6 +59,32 @@ epm_tool() estrlist "$@" ;; "json") # HELPCMD: json operations + if [ "$1" = "--help" ] || [ "$1" = "-h" ] ; then + echo "epm tool json - JSON parsing tool" + echo + echo "Usage:" + echo " epm tool json [options] - parse JSON from stdin" + echo " epm tool json --get-json-value URL field - get field value from JSON URL" + echo + echo "Options:" + echo " -b Brief output (leaf only + prune empty)" + echo " -l Leaf only" + echo " -p Prune empty" + echo + echo "Examples:" + echo " echo '{\"version\":\"1.0\"}' | epm tool json -b" + echo " epm tool json --get-json-value https://api.example.com/info.json version" + return + fi + # epm tool json --get-json-value URL field + if [ "$1" = "--get-json-value" ] ; then + local url="$2" + local field="$3" + [ -n "$url" ] || fatal "Use: epm tool json --get-json-value URL field" + [ -n "$field" ] || fatal "Use: epm tool json --get-json-value URL field" + get_json_value "$url" "$field" + return + fi showcmd json "$@" $CMDSHELL $SHAREDIR/tools_json "$@" ;; diff --git a/bin/epm-update b/bin/epm-update index 8e5983fe0..5590ca609 100644 --- a/bin/epm-update +++ b/bin/epm-update @@ -28,7 +28,7 @@ get_latest_version() URL="https://eepm.ru/app-versions" #update_url_if_need_mirrored local var - var="$(epm tool eget -q -O- "$URL/$1")" || return + var="$(fetch_url "$URL/$1")" || return echo "$var" | head -n1 | cut -d" " -f1 } diff --git a/bin/tools_eget b/bin/tools_eget index 32799746f..a153d456a 100755 --- a/bin/tools_eget +++ b/bin/tools_eget @@ -1161,6 +1161,8 @@ url_scat() { local URL="$1" download_with_mirroring __wget "$URL" -O- && return + local RES=$? + [ -n "$quiet" ] || return $RES unset_quiet download_with_mirroring __wget "$URL" -O- } @@ -1219,6 +1221,8 @@ url_scat() { local URL="$1" download_with_mirroring __curl "$URL" --output - && return + local RES=$? + [ -n "$quiet" ] || return $RES unset_quiet download_with_mirroring __curl "$URL" --output - } @@ -1279,6 +1283,8 @@ url_scat() { local URL="$1" download_with_mirroring __aria2 -x1 -s1 --allow-piece-length-change=false -o - "$URL" && return + local RES=$? + [ -n "$quiet" ] || return $RES unset_quiet download_with_mirroring __aria2 -x1 -s1 --allow-piece-length-change=false -o - "$URL" } @@ -1337,6 +1343,8 @@ url_scat() fatal "Improve me (via temp. file?)" local URL="$1" download_with_mirroring __axel -o - "$URL" && return + local RES=$? + [ -n "$quiet" ] || return $RES unset_quiet download_with_mirroring __axel -o - "$URL" } diff --git a/pack.d/claude-desktop.sh b/pack.d/claude-desktop.sh new file mode 100644 index 000000000..6acc1adb4 --- /dev/null +++ b/pack.d/claude-desktop.sh @@ -0,0 +1,37 @@ +#!/bin/sh + +TAR="$1" +RETURNTARNAME="$2" +VERSION="$3" + +. $(dirname $0)/common.sh + +PKGNAME=$PRODUCT-$VERSION.tar + +PRODUCTDIR=/opt/eepm-wine/$PRODUCT +INSTALLER="$(basename "$TAR")" + +mkdir -p .$PRODUCTDIR/ + +# c:/users/lav/AppData/Local/AnthropicClaude/claude.exe +cat < .$PRODUCTDIR/run.sh +#!/bin/sh +# AppData\Roaming +# APPDATA=\$(wine cmd /c echo %appdata% | tr -d '\r') +# AppData\Local +LOCALAPPDATA="\$(wine cmd /c echo %localappdata% | tr -d '\r')" +RUNFILE="\$LOCALAPPDATA\\\\AnthropicClaude\\\\claude.exe" + +URUNFILE="\$(winepath -u "\$RUNFILE")" +if [ ! -f "\$URUNFILE" ] ; then + INSTALLER="$PRODUCTDIR/$INSTALLER" + exec wine "\$INSTALLER" +fi +exec wine "\$RUNFILE" "\$@" +EOF +chmod 755 .$PRODUCTDIR/run.sh + +cp $TAR .$PRODUCTDIR +erc pack $PKGNAME opt/eepm-wine + +return_tar $PKGNAME diff --git a/pack.d/claude.sh b/pack.d/claude.sh new file mode 100755 index 000000000..ac872f0f5 --- /dev/null +++ b/pack.d/claude.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +TAR="$1" +RETURNTARNAME="$2" +VERSION="$3" + +. $(dirname $0)/common.sh + +PRODUCTDIR=opt/claude.ai +install -D -m755 $TAR $PRODUCTDIR/claude || fatal + +[ -n "$VERSION" ] || fatal "can't pack with empty VERSION" + +PKGNAME=$PRODUCT-$VERSION + +erc pack $PKGNAME.tar $PRODUCTDIR + +cat <$PKGNAME.tar.eepm.yaml +name: $PRODUCT +version: $VERSION +group: Networking/Instant messaging +license: Proprietary +url: https://claude.ai/ +summary: Claude is a next generation AI assistant +description: Claude is a next generation AI assistant built by Anthropic +EOF + +return_tar $PKGNAME.tar diff --git a/pack.d/common.sh b/pack.d/common.sh index a7ed880b8..09421fe1e 100755 --- a/pack.d/common.sh +++ b/pack.d/common.sh @@ -89,12 +89,7 @@ parse_json_value() # URL/file ["version"] get_json_value() { - if is_url "$1" ; then - eget -q -O- "$1" | parse_json_value "$2" - else - [ -s "$1" ] || fatal "File $1 is missed, can't get json" - parse_json_value "$2" < "$1" - fi + epm tool json --get-json-value "$1" "$2" } diff --git a/pack.d/virtualbox.sh b/pack.d/virtualbox.sh new file mode 100644 index 000000000..2dd7b5840 --- /dev/null +++ b/pack.d/virtualbox.sh @@ -0,0 +1,85 @@ +#!/bin/sh + +TAR="$1" +RETURNTARNAME="$2" + +. $(dirname $0)/common.sh + +# VirtualBox-7.2.4-170995-Linux_amd64.run +BASENAME=$(basename "$TAR" .run) +VERSION=$(echo "$BASENAME" | sed -e 's|VirtualBox-||' -e 's|-Linux_amd64||') +# 7.2.4-170995 -> 7.2.4 +VERSION=$(echo "$VERSION" | sed -e 's|-[0-9]*$||') + +PKGNAME=$PRODUCT-$VERSION + +# Extract .run without executing +chmod +x "$TAR" +"$TAR" --noexec --nox11 --target vbox-installer || fatal "Can't extract VirtualBox installer" + +cd vbox-installer || fatal "Can't enter vbox-installer" + +# Extract the main archive +erc VirtualBox.tar.bz2 || fatal "Can't extract VirtualBox.tar.bz2" + +cd .. || fatal + +PRODUCTDIR=opt/VirtualBox +mkdir -p $PRODUCTDIR +mv vbox-installer/* $PRODUCTDIR/ + +# Remove installer leftovers +rm -f $PRODUCTDIR/VirtualBox.tar.bz2 +rm -f $PRODUCTDIR/*.sh +rm -f $PRODUCTDIR/LICENSE +rm -rf vbox-installer + +# Set SUID on required executables +for exe in VirtualBoxVM VBoxHeadless VBoxNetAdpCtl VBoxNetDHCP VBoxNetNAT VBoxVolInfo ; do + [ -f "$PRODUCTDIR/$exe" ] && chmod 4755 "$PRODUCTDIR/$exe" +done + +# Create /usr/bin symlinks via VBox.sh wrapper +mkdir -p usr/bin +for cmd in VirtualBox VirtualBoxVM VBoxManage VBoxSDL VBoxHeadless VBoxAutostart VBoxBalloonCtrl VBoxBugReport VBoxDTrace VBoxVRDP ; do + [ -f "$PRODUCTDIR/$cmd" ] || [ -L "$PRODUCTDIR/$cmd" ] && ln -s /opt/VirtualBox/VBox.sh "usr/bin/$cmd" +done + +# Install desktop file +mkdir -p usr/share/applications +cp $PRODUCTDIR/virtualbox.desktop usr/share/applications/ 2>/dev/null || true + +# Install icons +for size in 16 32 48 64 128 ; do + mkdir -p "usr/share/icons/hicolor/${size}x${size}/apps" + cp "$PRODUCTDIR/icons/${size}x${size}/virtualbox.png" "usr/share/icons/hicolor/${size}x${size}/apps/" 2>/dev/null || true +done + +# Install MIME types +mkdir -p usr/share/mime/packages +cp $PRODUCTDIR/virtualbox.xml usr/share/mime/packages/ 2>/dev/null || true + +# Install udev rules +mkdir -p usr/lib/udev/rules.d +cp $PRODUCTDIR/60-vboxdrv.rules usr/lib/udev/rules.d/ 2>/dev/null || true + +# Install modprobe config +mkdir -p usr/lib/modprobe.d +echo "install vboxdrv /sbin/modprobe --ignore-install vboxdrv && /sbin/modprobe vboxnetflt && /sbin/modprobe vboxnetadp" > usr/lib/modprobe.d/virtualbox.conf + +# Create sysusers.d for vboxusers group +mkdir -p usr/lib/sysusers.d +echo "g vboxusers - -" > usr/lib/sysusers.d/virtualbox.conf + +erc pack $PKGNAME.tar opt usr || fatal + +cat <$PRODUCT.eepm.yaml +name: $PRODUCT +group: Emulators +license: GPL-2.0-only AND GPL-3.0-only AND CDDL-1.0 +url: https://www.virtualbox.org +summary: VirtualBox - powerful x86 and AMD64/Intel64 virtualization +description: Oracle VM VirtualBox is a powerful cross-platform virtualization product for enterprise and home use. +EOF + +return_tar $PKGNAME.tar diff --git a/play.d/claude-desktop.sh b/play.d/claude-desktop.sh new file mode 100755 index 000000000..f2cadd608 --- /dev/null +++ b/play.d/claude-desktop.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +PKGNAME=claude-desktop +SUPPORTEDARCHES="x86_64 aarch64" +VERSION="$2" +DESCRIPTION="Claude Desktop (Wine based)" +URL="https://claude.com/download" + +. $(dirname $0)/common.sh + +if ! is_command wine ; then + epm play wine || fatal +fi + +warn_version_is_not_supported + +#https://claude.ai/redirect/claudedotcom.v1.1029bab3-186e-4985-87e7-b2a711d887b4/api/desktop/win32/arm64/exe/latest/redirect +#https://claude.ai/redirect/claudedotcom.v1.1029bab3-186e-4985-87e7-b2a711d887b4/api/desktop/win32/x64/exe/latest/redirect + +# https://claude.ai/api/desktop/win32/x64/exe/latest/redirect +# https://claude.ai/api/desktop/win32/arm64/exe/latest/redirect + +VERSION="1.1029" +#PKGURL="https://storage.googleapis.com/osprey-downloads-c02f6a0d-347c-492b-a752-3e0651722e97/nest-win-x64/Claude-Setup-x64.exe" +PKGURL="ipfs://Qme1jkcU95P5dCWTs5sMxsvUruBfaVQMrAhY2585AfsoVJ?filename=Claude-Setup-x64.exe" + +install_pack_pkgurl $VERSION diff --git a/play.d/claude.sh b/play.d/claude.sh new file mode 100755 index 000000000..1e4f373cd --- /dev/null +++ b/play.d/claude.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +PKGNAME=claude +SUPPORTEDARCHES="x86_64 aarch64" +VERSION="$2" +DESCRIPTION="Claude is a next generation AI assistant built by Anthropic" +URL="https://claude.ai/" + +. $(dirname $0)/common.sh + +arch="$(epm print info -a)" +case "$arch" in + x86_64) + arch=x64 + ;; + aarch64) + arch=arm64 + ;; +esac + +# TODO: Darwin support +os="linux" + +platform="${os}-${arch}" + +#GCS_BUCKET="https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases" +GCS_BUCKET="$(fetch_url https://claude.ai/install.sh | grep "^GCS_BUCKET=" | sed -e 's|^GCS_BUCKET=||' -e 's|^"||' -e 's|"$||')" +[ -n "$GCS_BUCKET" ] || fatal "Can't download https://claude.ai/install.sh" + +if [ "$VERSION" = "*" ] ; then + VERSION="$(fetch_url "$GCS_BUCKET/stable")" || fatal "Can't get version from $GCS_BUCKET/stable" + [ -n "$VERSION" ] || fatal "Got empty version from $GCS_BUCKET/stable" +fi + +# ["platforms","linux-x64","checksum"] +checksum="$(get_json_value "$GCS_BUCKET/$VERSION/manifest.json" '["platforms","'$platform'","checksum"]')" || fatal "Can't get checksum" + +PKGURL="$GCS_BUCKET/$VERSION/$platform/claude" + +# TODO: compare checksum + +install_pack_pkgurl $VERSION $checksum diff --git a/play.d/common.sh b/play.d/common.sh index b481badd0..f2f1c4cd7 100755 --- a/play.d/common.sh +++ b/play.d/common.sh @@ -15,7 +15,7 @@ fatal() info() { - echo "$*" + echo "$*" >&2 } [ -n "$BIGTMPDIR" ] || [ -d "/var/tmp" ] && BIGTMPDIR="/var/tmp" || BIGTMPDIR="/tmp" @@ -79,6 +79,7 @@ epm() { #if [ "$1" = "tool" ] ; then # __showcmd_shifted 1 "$@" + [ "$1" = "--quiet" ] && shift && $EPM --quiet "$@" && return if [ "$1" != "print" ] && [ "$1" != "tool" ] && [ "$1" != "status" ] ; then showcmd "$(basename $EPM) $*" fi @@ -91,6 +92,12 @@ eget() epm tool eget "$@" } +fetch_url() +{ + info "Fetching $1 ..." + $EPM --quiet tool eget -q -O- "$1" +} + is_supported_arch() { @@ -159,18 +166,13 @@ parse_json_value() { local field="$1" echo "$field" | grep -q -E "^\[" || field='["'$field'"]' - epm tool json -b | grep -m1 -F "$field" | sed -e 's|.*[[:space:]]||' | sed -e 's|"\(.*\)"|\1|g' + epm --quiet tool json -b | grep -m1 -F "$field" | sed -e 's|.*[[:space:]]||' | sed -e 's|"\(.*\)"|\1|g' } # URL/file ["version"] get_json_value() { - if is_url "$1" ; then - eget -q -O- "$1" | parse_json_value "$2" - else - [ -s "$1" ] || fatal "File $1 is missed, can't get json" - parse_json_value "$2" < "$1" - fi + epm tool json --get-json-value "$1" "$2" } snap_get_pkgurl() diff --git a/play.d/ocenaudio.sh b/play.d/ocenaudio.sh new file mode 100755 index 000000000..e5f7e5fda --- /dev/null +++ b/play.d/ocenaudio.sh @@ -0,0 +1,64 @@ +#!/bin/sh + +PKGNAME=ocenaudio +SUPPORTEDARCHES="x86_64" +VERSION="$2" +DESCRIPTION="Easy, fast and powerful audio editor" +URL="https://www.ocenaudio.com/" + +. $(dirname $0)/common.sh + +warn_version_is_not_supported + +pkgtype="$(epm print info -p)" + +case "$(epm print info -e)" in + openSUSE/15*) + distro="opensuse15" + ;; + Fedora/39) + distro="fedora39" + ;; + Fedora/4*) + distro="fedora40" + ;; + RockyLinux/9*) + distro="rockylinux9.3" + ;; + Debian/11*) + distro="debian11" + ;; + Debian/12*) + distro="debian12" + ;; + Debian/13*) + distro="debian13" + ;; + Ubuntu/20.*) + distro="ubuntu20.04" + ;; + Ubuntu/22.*) + distro="ubuntu22.04" + ;; + Ubuntu/24.*) + distro="ubuntu24.04" + ;; + ALTLinux/Sisyphus) + distro="fedora40" + ;; + ALTLinux/p11) + distro="debian12" + pkgtype="deb" + ;; + ALTLinux/p10) + distro="debian11" + pkgtype="deb" + ;; + *) + distro="debian12" + pkgtype="deb" +esac + +PKGURL="https://www.ocenaudio.com/downloads/index.php/ocenaudio_${distro}.${pkgtype}" + +install_pkgurl diff --git a/play.d/sunshine.sh b/play.d/sunshine.sh index 031599c9a..51567cbd0 100644 --- a/play.d/sunshine.sh +++ b/play.d/sunshine.sh @@ -15,8 +15,8 @@ is_openssl_enough 3 || fatal "There is no needed OpenSSL 3 in the system." arch=$(epm print info -a) # TODO: common way to install from Fedora copr -# FIXME: Anubis doesn't allow download -BUILD_ID=$(eget --list https://copr.fedorainfracloud.org/coprs/lizardbyte/stable/package/Sunshine/ | grep -o 'build/[0-9]\+' | cut -d/ -f2 | sort -n | tail -n1) +# use API instead of HTML page (Anubis bot protection blocks scraping) +BUILD_ID=$(epm tool json --get-json-value "https://copr.fedorainfracloud.org/api_3/build/list?ownername=lizardbyte&projectname=stable&packagename=Sunshine&status=succeeded" '["items",0,"id"]') PKGURL=$(eget --list --latest https://download.copr.fedorainfracloud.org/results/lizardbyte/stable/fedora-41-$arch/0$BUILD_ID-Sunshine/ "Sunshine-*.$arch.rpm") diff --git a/play.d/viber.sh b/play.d/viber.sh index 3a39de5f6..f9d5094c6 100755 --- a/play.d/viber.sh +++ b/play.d/viber.sh @@ -10,6 +10,7 @@ URL="https://viber.com" warn_version_is_not_supported -PKGURL="https://download.cdn.viber.com/desktop/Linux/viber.AppImage" +#PKGURL="https://download.cdn.viber.com/desktop/Linux/viber.AppImage" +PKGURL="https://download.cdn.viber.com/desktop/Linux/viber.rpm" install_pkgurl diff --git a/play.d/virtualbox-bin.sh b/play.d/virtualbox-bin.sh new file mode 100755 index 000000000..3da9281bb --- /dev/null +++ b/play.d/virtualbox-bin.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +PKGNAME=virtualbox +SUPPORTEDARCHES="x86_64" +VERSION="$2" +DESCRIPTION="VirtualBox from the official site" +URL="https://www.virtualbox.org" + +. $(dirname $0)/common.sh + +if [ "$VERSION" = "*" ] ; then + VERSION="$(fetch_url "https://download.virtualbox.org/virtualbox/LATEST.TXT")" || fatal "Can't get version" + VERSION="$(echo "$VERSION" | tr -d '[:space:]')" +fi + +BASEURL="https://download.virtualbox.org/virtualbox/$VERSION" + +# VirtualBox-7.2.4-170995-Linux_amd64.run +PKGURL="$(eget --list --latest "$BASEURL/" "VirtualBox-${VERSION}-*-Linux_amd64.run")" + +[ -n "$PKGURL" ] || fatal "Can't get package URL" + +install_pack_pkgurl + +echo +echo "Note: Add needed users to vboxusers group via # usermod -a -G vboxusers " diff --git a/repack.d/VirtualBox-7.2.sh b/repack.d/VirtualBox-7.2.sh new file mode 100755 index 000000000..938d4086b --- /dev/null +++ b/repack.d/VirtualBox-7.2.sh @@ -0,0 +1,22 @@ +#!/bin/sh -x +# It will run with two args: buildroot spec +BUILDROOT="$1" +SPEC="$2" + +PRODUCT=VirtualBox +PRODUCTDIR=/usr/lib/virtualbox + +. $(dirname $0)/common.sh + +# conflict with the distro package +add_conflicts virtualbox + +# create vboxusers group via systemd-sysusers +cat </dev/null +} + +# args: script, host arch +__get_app_description() +{ + local arch="$2" + #__run_script "$1" --description "$arch" 2>/dev/null + #return + if grep -q '^SUPPORTEDARCHES=.*\<'"$arch"'\>' "$psdir/$1.sh" || grep -q "^SUPPORTEDARCHES=[\"'][\"']$" "$psdir/$1.sh" || ! grep -q "^SUPPORTEDARCHES=" "$psdir/$1.sh" ; then + grep -oP "^DESCRIPTION=[\"']*\K[^\"']+" "$psdir/$1.sh" | sed -e 's| *#*$||' + fi +} + + +#for i in $(__list_all_app) ; do +# n="$(__get_app_description $i x86_64)" +# [ -z "$n" ] || echo $i +#done | wc -l + +__get_fast_short_list_app() +{ + local arch="$1" + [ -n "$arch" ] || fatal + local IGNOREi586 + [ "$arch" = "x86_64" ] && IGNOREi586='NoNo' || IGNOREi586='i586-' + grep -L -E "^DESCRIPTION=(''|\"\")" $psdir/*.sh | xargs grep -l -E "^SUPPORTEDARCHES=(''|\"\"|.*\<$arch\>)" | xargs basename -s .sh | grep -v -E "(^$IGNOREi586|^common)" +} + +#__get_fast_short_list_app x86_64 +#__get_fast_short_list_app x86 + +__get_fast_quiet_list_app() +{ + local arch="$1" + [ -n "$arch" ] || fatal + local IGNOREi586 + [ "$arch" = "x86_64" ] && IGNOREi586='NoNo' || IGNOREi586='i586-' + grep -l -E "^SUPPORTEDARCHES=(''|\"\"|.*\<$arch\>)" $psdir/*.sh | xargs grep -oP "^DESCRIPTION=[\"']*\K[^\"']+" | sed -e "s|.*/\(.*\).sh:|\1 - |" | grep -v -E "(^$IGNOREi586|^common|#.*$)" +} + +__get_fast_list_app() +{ + local arch="$1" + [ -n "$arch" ] || fatal + local IGNOREi586 + [ "$arch" = "x86_64" ] && IGNOREi586='NoNo' || IGNOREi586='i586-' + grep -l -E "^SUPPORTEDARCHES=(''|\"\"|.*\<$arch\>)" $psdir/*.sh | xargs grep -oP "^DESCRIPTION=[\"']*\K[^\"']+" | sed -e "s|.*/\(.*\).sh:| \1 - |" | grep -v -E "(^$IGNOREi586|^common|#.*$)" +} + +#__get_fast_list_app x86_64 + +__get_fast_list_pkg() +{ + local arch="$1" + [ -n "$arch" ] || fatal + local IGNOREi586 + + local tmplist + tmplist="$(mktemp)" || fatal + #remove_on_exit $tmplist + + local tmplist1 + tmplist1="$(mktemp)" || fatal + #remove_on_exit $tmplist1 + + [ "$arch" = "x86_64" ] && IGNOREi586='NoNo' || IGNOREi586='i586-' + + __get_fast_short_list_app $arch | LC_ALL=C sort >$tmplist + grep -l -E "^SUPPORTEDARCHES=(''|\"\"|.*\<$arch\>)" $psdir/*.sh | xargs grep -oP "^PKGNAME=[\"']*\K[^\"']+" | sed -e "s|.*/\(.*\).sh:|\1 |" | grep -v -E "(^$IGNOREi586|^common|#.*$)" | LC_ALL=C sort >$tmplist1 + LC_ALL=C join -j 1 -a 1 $tmplist $tmplist1 + # TODO: if right column is missed, run script +} + +__get_fast_list_pkg x86_64 diff --git a/tests/test_startwith.sh b/tests/test_startwith.sh new file mode 100755 index 000000000..ce2644f07 --- /dev/null +++ b/tests/test_startwith.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +startwith() +{ + # rhas "$1" "^$2" + #echo "${#2}" + #echo "${1:0:${#2}}" + [ "${1:0:${#2}}" = "$2" ] + #[[ "$1" = ${2}* ]] +} + + +check_ok() +{ + startwith "$1" "$2" && echo "OK for '$1' with '$2'" || echo "FATAL with '$1': result FALSE do not match with '$2'" +} + +check_notok() +{ + startwith "$1" "$2" && echo "FATAL with '$1': result TRUE do not match with '$2'" || echo "OK for '$1' with '$2'" +} + +check_ok "/abs" "/" +check_notok "../abs" "/" +check_ok "../abs" ".." +check_ok "common-file.abs" "common" + +startwith_inlist() +{ + local str="$1" + local i + for i in "$@" ; do + startwith "$str" "$i" && return + done + return 1 +} + +startwith_inlist "file-1.2.3.rpm" "base" "name" "filer" "file" && echo OK +startwith_inlist "files-1.2.3.rpm" "base" "name" "filer" "file" || echo OK diff --git a/tests/test_status.sh b/tests/test_status.sh new file mode 100755 index 000000000..5cdc258fe --- /dev/null +++ b/tests/test_status.sh @@ -0,0 +1,22 @@ + +__status_repacked() +{ + local repacked="$(epm print field Description for "$1" | grep -qi "alien" 2>/dev/null)" + local packager="$(epm print field Packager for "$1" 2>/dev/null)" + + local rpmversion="$(epm print field Version for "$1" 2>/dev/null)" + [ -n "$rpmversion" ] || return + + [ "$packager" = "EPM " ] && return 0 + [ "$packager" = "EPM " ] && return 0 + + return 1 +} + +LIST="hiddify mc" + +#__status_repacked $LIST +#echo $? + +FORMAT="%{Name} %{Version} %{Packager}\n" +a= rpmquery --queryformat "$FORMAT" -a | grep "EPM