From fdb4a492ef70df9c772b32c3ed3be32a370e81f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?= <153805936+Cheviiot@users.noreply.github.com> Date: Sat, 25 Jul 2026 01:43:11 +1000 Subject: [PATCH] feat: add Nivora CLI and msvc-go-wine --- CHANGELOG.md | 12 + README.md | 5 +- docs/maintenance.md | 2 +- docs/packages/nivora-cli.md | 112 ++++ docs/packages/nivora-stplr.md | 43 -- .../.stapler/update-check | 0 msvc-go-wine/LICENSE | 25 + msvc-go-wine/Staplerfile | 68 +++ nivora-cli/.stapler/update-check | 6 + nivora-cli/LICENSE | 16 + nivora-cli/Staplerfile | 55 ++ nivora-cli/nivora | 546 ++++++++++++++++++ .../stapler-repo.toml | 0 nivora-stplr/LICENSE | 16 - nivora-stplr/Staplerfile | 55 -- nivora-stplr/nivora-stplr | 239 -------- tests/test_helper.sh | 51 -- tests/test_nivora_cli.sh | 96 +++ tools/package_updates.sh | 6 +- tools/run_checks.sh | 2 +- tools/test_package_lifecycle.sh | 2 +- tools/validate_repo.py | 5 +- 22 files changed, 949 insertions(+), 413 deletions(-) create mode 100644 docs/packages/nivora-cli.md delete mode 100644 docs/packages/nivora-stplr.md rename {nivora-stplr => msvc-go-wine}/.stapler/update-check (100%) create mode 100644 msvc-go-wine/LICENSE create mode 100644 msvc-go-wine/Staplerfile create mode 100755 nivora-cli/.stapler/update-check create mode 100644 nivora-cli/LICENSE create mode 100644 nivora-cli/Staplerfile create mode 100755 nivora-cli/nivora rename {nivora-stplr => nivora-cli}/stapler-repo.toml (100%) delete mode 100644 nivora-stplr/LICENSE delete mode 100644 nivora-stplr/Staplerfile delete mode 100755 nivora-stplr/nivora-stplr delete mode 100755 tests/test_helper.sh create mode 100755 tests/test_nivora_cli.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c1cc0c..11a5377 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ В файле фиксируются значимые изменения Nivora. +## 2026-07-25 — Nivora CLI + +### Изменено + +- Пакет `nivora-stplr` полностью переработан и переименован в `nivora-cli`. +- Новая команда `nv` получила компактное интерактивное меню, русскую и + английскую локализацию, диагностику, dry-run и генерацию автодополнения. +- Повседневные операции доступны отдельными командами в стиле eepm: + `nvi`, `nve`, `nvu`, `nvs`, `nvqi`, `nvqa`, `nvr`, `nvrl`, `nvf`, `nvd` + и `nvc`. +- Прежние команды семейства `sl*` удалены без слоя совместимости. + ## 2026-07-24 — GitHub Desktop ### Добавлено diff --git a/README.md b/README.md index f6e8875..f971e36 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Nivora предлагает открытые рецепты упаковки д официальный репозиторий Stapler и не официальные пакеты upstream-проектов. -**20 пакетов** · **6 категорий** · `amd64`, `arm64` и `all` +**21 пакет** · **6 категорий** · `amd64`, `arm64` и `all` [![Проверка Nivora](https://github.com/Cheviiot/Nivora/actions/workflows/quality.yml/badge.svg)](https://github.com/Cheviiot/Nivora/actions/workflows/quality.yml) @@ -66,6 +66,7 @@ stplr info nivora/codex | [ClaudeAlt](docs/packages/claude-alt.md) | `1.24012.0` | `amd64`, `arm64` | `stplr install nivora/claude-alt` | | [Codex](docs/packages/codex.md) | `26.721.31836` | `amd64` | `stplr install nivora/codex` | | [GitHub Desktop](docs/packages/github-desktop.md) | `3.6.3` | `amd64`, `arm64` | `stplr install nivora/github-desktop` | +| [msvc-go-wine](https://github.com/Cheviiot/msvc-go-wine) | `0.1.0` | `amd64`, `arm64` | `stplr install nivora/msvc-go-wine` | | [OpenCode](https://opencode.ai/) | `1.18.4` | `amd64`, `arm64` | `stplr install nivora/opencode` | ### Рабочий стол @@ -88,7 +89,7 @@ stplr info nivora/codex |:--|:--:|:--:|:--| | [balenaEtcher](https://etcher.balena.io/) | `2.1.6` | `amd64` | `stplr install nivora/balena-etcher` | | [Fisher](https://github.com/jorgebucaran/fisher) | `4.4.8` | `all` | `stplr install nivora/fisher` | -| [Nivora Stapler Helper](docs/packages/nivora-stplr.md) | `0.3.0` | `all` | `stplr install nivora/nivora-stplr` | +| [Nivora CLI](docs/packages/nivora-cli.md) | `1.0.0` | `all` | `stplr install nivora/nivora-cli` | | [Ventoy](docs/packages/ventoy.md) | `1.1.17` | `amd64`, `arm64` | `stplr install nivora/ventoy` | ## Обновление diff --git a/docs/maintenance.md b/docs/maintenance.md index fd064ec..6eef9dd 100644 --- a/docs/maintenance.md +++ b/docs/maintenance.md @@ -20,7 +20,7 @@ Stapler сам добавляет текущее `name` в generated `Provides` | `claude` | Upstream DEB: `Package: claude-desktop`; desktop-id `com.anthropic.Claude` сохранён; старое имя пакета `claude-desktop` заменяется | | `codex` | Отображаемое имя `Codex`; команда и desktop-id `codex-app` сохранены | | `github-desktop` | Официальный upstream `desktop/desktop`; Linux-сборка без стороннего форка | -| `nivora-stplr` | Собственный helper Nivora | +| `nivora-cli` | Многоязычная оболочка Nivora для Stapler | ## Локальные проверки diff --git a/docs/packages/nivora-cli.md b/docs/packages/nivora-cli.md new file mode 100644 index 0000000..c1cfb03 --- /dev/null +++ b/docs/packages/nivora-cli.md @@ -0,0 +1,112 @@ +# Nivora CLI + +Nivora CLI — компактная многоязычная оболочка для Stapler. Она упрощает +установку, удаление, обновление и поиск пакетов, управление репозиториями и +диагностику системы. + +```bash +sudo stplr install nivora/nivora-cli +``` + +## Интерактивный режим + +Команда `nv` без аргументов открывает компактное меню: + +```text +╭─ NIVORA ─────────────────────────────╮ +│ Простое управление пакетами Stapler │ +╰──────────────────────────────────────╯ + + Пакеты + [i] Установить nvi + [e] Удалить nve + [u] Обновить nvu + [s] Найти nvs + [q] Информация nvqi + [a] Все пакеты nvqa + + Система + [r] Обновить репозитории nvr + [l] Репозитории nvrl + [f] Исправить nvf + [d] Диагностика nvd +``` + +В конвейере или другом неинтерактивном окружении `nv` выводит обычную справку. + +## Короткие команды + +| Команда | Операция Stapler | +|:--|:--| +| `nvi package` | установить `nivora/package` | +| `nve package` | удалить `nivora/package` | +| `nvu` | обновить установленные пакеты | +| `nvs query` | найти пакет | +| `nvqi package` | показать информацию о пакете | +| `nvqa` | показать каталог пакетов | +| `nvr` | обновить индексы репозиториев | +| `nvrl` | показать подключённые репозитории | +| `nvf` | исправить состояние Stapler | +| `nvd` | проверить окружение | +| `nvc` | показать или изменить конфигурацию Stapler | + +Основная команда принимает и полные названия операций: + +```bash +nv install codex +nv search editor +nv info codex +nv repo list +``` + +## Репозитории + +Короткое имя автоматически получает префикс `nivora`: + +```bash +nvi codex # stplr install nivora/codex +nvi other/package # stplr install other/package +``` + +Репозиторий можно изменить или отключить: + +```bash +nvi --repo other package +nvi --repo none package +NIVORA_REPO=other nvi package +``` + +Локальные пути и файлы пакетов никогда не получают префикс. + +## Язык и оформление + +Русский или английский язык выбирается по системной локали. Явное +переопределение: + +```bash +nv --lang en help +NIVORA_LANG=ru nvd +``` + +Цветной вывод включается только для терминала. Его можно отключить стандартной +переменной `NO_COLOR`, параметром `--no-color` или значением +`NIVORA_COLOR=never`. + +## Безопасность + +Изменяющие систему операции запускаются через `sudo`. Другую команду повышения +привилегий можно задать в `NIVORA_SUDO`. Аргументы передаются массивом без +вычисления shell-строк. Параметр `--dry-run` показывает итоговую команду без её +выполнения. + +| Переменная | Назначение | +|:--|:--| +| `NIVORA_LANG` | язык интерфейса: `ru` или `en` | +| `NIVORA_REPO` | репозиторий коротких имён | +| `NIVORA_SUDO` | команда повышения привилегий | +| `NIVORA_COLOR` | режим цвета; `never` отключает цвет | +| `NIVORA_QUIET=1` | не печатать запускаемую команду | +| `NO_COLOR` | стандартное отключение цвета | + +Автодополнение для Bash, Fish или Zsh генерируется командой +`nv completion `. diff --git a/docs/packages/nivora-stplr.md b/docs/packages/nivora-stplr.md deleted file mode 100644 index 30208c8..0000000 --- a/docs/packages/nivora-stplr.md +++ /dev/null @@ -1,43 +0,0 @@ -# Nivora Stapler Helper - -`nivora-stplr` добавляет короткие команды для Stapler. - -```bash -sudo stplr install nivora/nivora-stplr -``` - -## Команды - -| Alias | Вызов Stapler | -|:--|:--| -| `sli` | `sudo stplr install` | -| `slr` | `sudo stplr remove` | -| `slu` | `sudo stplr up` | -| `slf` | `sudo stplr fix` | -| `slref` | `sudo stplr refresh` | -| `sls` | `stplr search` | -| `slii` | `stplr info` | -| `sll` | `stplr list` | -| `sl` | Диспетчер тех же команд | - -Примеры: - -```bash -sli --repo nivora parsec -slii nivora/codex -slu --clean -``` - -Короткое имя пакета не привязывается к Nivora по умолчанию. Префикс задаётся явно: - -```bash -export NIVORA_STPLR_REPO=nivora -``` - -| Переменная | Назначение | -|:--|:--| -| `NIVORA_STPLR_REPO` | Префикс для коротких package names | -| `NIVORA_STPLR_SUDO` | Команда повышения привилегий, по умолчанию `sudo` | -| `NIVORA_STPLR_QUIET=1` | Не печатать команду перед запуском | - -Helper не передаёт произвольные shell-строки через `sudo`. diff --git a/nivora-stplr/.stapler/update-check b/msvc-go-wine/.stapler/update-check similarity index 100% rename from nivora-stplr/.stapler/update-check rename to msvc-go-wine/.stapler/update-check diff --git a/msvc-go-wine/LICENSE b/msvc-go-wine/LICENSE new file mode 100644 index 0000000..d8ad75f --- /dev/null +++ b/msvc-go-wine/LICENSE @@ -0,0 +1,25 @@ +MIT License + +Copyright (c) 2026 Cheviiot + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +This license covers msvc-go-wine's own source code. It does not cover the +Microsoft Visual C++ Build Tools / Windows SDK that `msvc-go-wine download` +fetches and unpacks, which remain governed by Microsoft's own license terms. diff --git a/msvc-go-wine/Staplerfile b/msvc-go-wine/Staplerfile new file mode 100644 index 0000000..2f56f26 --- /dev/null +++ b/msvc-go-wine/Staplerfile @@ -0,0 +1,68 @@ +name='msvc-go-wine' +version=0.1.1 +release=1 +summary='Cross compile with MSVC on Linux via Wine' +summary_ru='Кросс-компиляция с MSVC на Linux через Wine' +group='Development/Tools' +desc='msvc-go-wine is a single-binary tool for cross compiling with the real MSVC toolchain on Linux via Wine. Once you run its download/install subcommands, cl, link, lib, rc, midl, mt, dumpbin, msbuild, nmake, ml, ml64, armasm, armasm64, cmd and findstr all work as drop-in commands, exactly like on Windows.' +desc_ru='msvc-go-wine — однобинарный инструмент для кросс-компиляции с реальным MSVC на Linux через Wine. После выполнения его подкоманд download/install команды cl, link, lib, rc, midl, mt, dumpbin, msbuild, nmake, ml, ml64, armasm, armasm64, cmd и findstr работают как обычные команды, точно как в Windows.' +homepage='https://github.com/Cheviiot/msvc-go-wine' +maintainer='Cheviiot ' + +architectures=('amd64' 'arm64') + +license=('MIT') + +provides=() +replaces=('msvc-go-wine') +conflicts=() + +auto_reqprov_method="dirty" +auto_req=0 +auto_prov=0 + +# wine/msitools/git are needed at runtime by `msvc-go-wine download`/`install` +# and by the tool wrappers themselves, not just at build time. +deps=() +deps_debian=('wine' 'msitools' 'git') +deps_ubuntu=("${deps_debian[@]}") +deps_fedora=('wine' 'msitools' 'git') +deps_arch=('wine' 'msitools' 'git') +deps_opensuse=('wine' 'msitools' 'git') +deps_altlinux=('wine' 'msitools' 'git') + +build_deps=('binutils') + +# Prebuilt by msvc-go-wine's own CI (.github/workflows/release.yml) and +# published to its GitHub Releases; renamed to a common local name so +# package() doesn't need to branch on architecture. +sources=( + "https://github.com/Cheviiot/msvc-go-wine/releases/download/v${version}/msvc-go-wine-linux-amd64?~archive=false&~name=msvc-go-wine" + 'local:///LICENSE' +) + +sources_arm64=( + "https://github.com/Cheviiot/msvc-go-wine/releases/download/v${version}/msvc-go-wine-linux-arm64?~archive=false&~name=msvc-go-wine" + 'local:///LICENSE' +) + +checksums=( + 'sha256:c4193012bab8499cf2000faba61bcd10f77c6faaeea088fe5748e3531f7134e3' + 'sha256:693a85757d0e3c527a78af6f12dc5068900c3cd363b92f0998ec41b607b15885' +) + +checksums_arm64=( + 'sha256:0bc74b95414085acba3e08a2b994063adc3c011cdff61f92e17810c34703ffbc' + 'sha256:693a85757d0e3c527a78af6f12dc5068900c3cd363b92f0998ec41b607b15885' +) + +package() { + cd "${srcdir}" + install-binary "${srcdir}/msvc-go-wine" + install-license "${srcdir}/LICENSE" "msvc-go-wine/LICENSE" +} + +files() { + files-find-binary + files-find-license +} diff --git a/nivora-cli/.stapler/update-check b/nivora-cli/.stapler/update-check new file mode 100755 index 0000000..efb050a --- /dev/null +++ b/nivora-cli/.stapler/update-check @@ -0,0 +1,6 @@ +#!/bin/bash +set -euo pipefail + +package_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +repo_root="$(cd "${package_dir}/.." && pwd)" +exec "${repo_root}/tools/package_updates.sh" check "$(basename "$package_dir")" diff --git a/nivora-cli/LICENSE b/nivora-cli/LICENSE new file mode 100644 index 0000000..7079138 --- /dev/null +++ b/nivora-cli/LICENSE @@ -0,0 +1,16 @@ +Сведения о лицензии пакета Nivora CLI + +Лицензия: MIT. +Исходный проект: https://github.com/Cheviiot/Nivora + +Нюансы пакета: +- Пакет устанавливает оболочку Stapler с командами `nv`, `nvi`, `nve`, `nvu`, `nvs`, `nvqi`, `nvqa`, `nvr`, `nvrl`, `nvf`, `nvd` и `nvc`. +- Nivora CLI не заменяет Stapler: все операции выполняются через системную команду `stplr`. +- Через команду повышения привилегий запускаются только `install`, `remove`, `up`, `fix` и `refresh`. +- Команды чтения `search`, `info` и `list` выполняются без повышения привилегий. +- Префикс репозитория задается через `--repo ` или `NIVORA_REPO`; по умолчанию используется репозиторий `nivora`. +- Пути вида `repo/package` не изменяются. +- Nivora CLI не исполняет произвольные shell-строки через `sudo`. + +Полный официальный текст MIT: +https://opensource.org/licenses/MIT diff --git a/nivora-cli/Staplerfile b/nivora-cli/Staplerfile new file mode 100644 index 0000000..6d8bac3 --- /dev/null +++ b/nivora-cli/Staplerfile @@ -0,0 +1,55 @@ +name='nivora-cli' +version='1.0.0' +release=1 +summary='Friendly multilingual command shell for Stapler' +summary_ru='Удобная многоязычная оболочка для Stapler' +desc='Nivora CLI is a compact bilingual command shell for everyday Stapler package management, repository administration, diagnostics, interactive navigation and shell completion.' +desc_ru='Nivora CLI — компактная двуязычная оболочка для управления пакетами и репозиториями Stapler, диагностики, интерактивной навигации и автодополнения.' +group='System/Configuration/Packaging' +homepage='https://github.com/Cheviiot/Nivora' +maintainer='Cheviiot ' + +architectures=('all') +license=('MIT') + +provides=() +replaces=('nivora-cli') +conflicts=() + +auto_reqprov_method='dirty' +auto_req=0 +auto_prov=0 + +deps=() +deps_debian=('bash' 'sudo') +deps_ubuntu=("${deps_debian[@]}") +deps_fedora=('bash' 'sudo') +deps_arch=('bash' 'sudo') +deps_altlinux=('bash' 'sudo') +deps_opensuse=('bash' 'sudo') + +sources=( + 'local:///nivora' + 'local:///LICENSE' +) + +checksums=( + 'sha256:438a5b3b73cd8f69108174b25e647b0f8013b3c16f4fcd5ca9e9769fd19f98b7' + 'sha256:16f1438f500706ac85b363c2549b392daef3a920b24139fca6b30f5648a7801a' +) + +package() { + install -Dm755 "${srcdir}/nivora" "${pkgdir}/usr/bin/nv" + + shortcut='' + for shortcut in nvi nve nvu nvs nvqi nvqa nvr nvrl nvf nvd nvc; do + ln -sfn nv "${pkgdir}/usr/bin/${shortcut}" + done + + install-license "${srcdir}/LICENSE" 'nivora-cli/LICENSE.nivora' +} + +files() { + files-find-binary + files-find-license +} diff --git a/nivora-cli/nivora b/nivora-cli/nivora new file mode 100755 index 0000000..fb8d42f --- /dev/null +++ b/nivora-cli/nivora @@ -0,0 +1,546 @@ +#!/bin/bash +set -euo pipefail + +readonly NIVORA_VERSION='1.0.0' +readonly DEFAULT_REPOSITORY='nivora' + +language="${NIVORA_LANG:-}" +repository="${NIVORA_REPO:-${DEFAULT_REPOSITORY}}" +dry_run=0 +color_mode="${NIVORA_COLOR:-auto}" + +detect_language() { + [[ -n "$language" ]] && return + case "${LC_ALL:-${LC_MESSAGES:-${LANG:-en}}}" in + ru* | RU*) language='ru' ;; + *) language='en' ;; + esac +} + +message() { + local key="$1" + case "${language}:${key}" in + ru:title) printf 'Простое управление пакетами Stapler' ;; + ru:usage) printf 'Использование' ;; + ru:packages) printf 'Пакеты' ;; + ru:system) printf 'Система' ;; + ru:install) printf 'Установить' ;; + ru:remove) printf 'Удалить' ;; + ru:upgrade) printf 'Обновить' ;; + ru:search) printf 'Найти' ;; + ru:info) printf 'Информация' ;; + ru:all_packages) printf 'Все пакеты' ;; + ru:refresh) printf 'Обновить репозитории' ;; + ru:repositories) printf 'Репозитории' ;; + ru:fix) printf 'Исправить' ;; + ru:doctor) printf 'Диагностика' ;; + ru:config) printf 'Конфигурация' ;; + ru:help) printf 'Справка' ;; + ru:exit) printf 'Выход' ;; + ru:choose) printf 'Выберите действие' ;; + ru:package_prompt) printf 'Имя пакета' ;; + ru:query_prompt) printf 'Поисковый запрос' ;; + ru:unknown_command) printf 'неизвестная команда' ;; + ru:unknown_choice) printf 'нет такого пункта' ;; + ru:missing_argument) printf 'необходим аргумент' ;; + ru:invalid_language) printf 'поддерживаются языки ru и en' ;; + ru:invalid_repository) printf 'некорректное имя репозитория' ;; + ru:stapler_missing) printf 'команда stplr не найдена' ;; + ru:privilege_missing) printf 'команда повышения привилегий не найдена' ;; + ru:running) printf 'Выполняется' ;; + ru:preview) printf 'Предпросмотр' ;; + ru:diagnostics) printf 'Диагностика окружения' ;; + ru:ready) printf 'Система готова к работе' ;; + ru:problems) printf 'Обнаружены проблемы' ;; + ru:available) printf 'доступен' ;; + ru:missing) printf 'не найден' ;; + ru:language) printf 'Язык' ;; + ru:repository) printf 'Репозиторий' ;; + ru:privileges) printf 'Привилегии' ;; + ru:commands) printf 'Короткие команды' ;; + ru:options) printf 'Параметры' ;; + ru:examples) printf 'Примеры' ;; + en:title) printf 'Simple Stapler package management' ;; + en:usage) printf 'Usage' ;; + en:packages) printf 'Packages' ;; + en:system) printf 'System' ;; + en:install) printf 'Install' ;; + en:remove) printf 'Remove' ;; + en:upgrade) printf 'Upgrade' ;; + en:search) printf 'Search' ;; + en:info) printf 'Information' ;; + en:all_packages) printf 'All packages' ;; + en:refresh) printf 'Refresh repositories' ;; + en:repositories) printf 'Repositories' ;; + en:fix) printf 'Repair' ;; + en:doctor) printf 'Diagnostics' ;; + en:config) printf 'Configuration' ;; + en:help) printf 'Help' ;; + en:exit) printf 'Exit' ;; + en:choose) printf 'Choose an action' ;; + en:package_prompt) printf 'Package name' ;; + en:query_prompt) printf 'Search query' ;; + en:unknown_command) printf 'unknown command' ;; + en:unknown_choice) printf 'unknown choice' ;; + en:missing_argument) printf 'an argument is required' ;; + en:invalid_language) printf 'supported languages are ru and en' ;; + en:invalid_repository) printf 'invalid repository name' ;; + en:stapler_missing) printf 'stplr command was not found' ;; + en:privilege_missing) printf 'privilege command was not found' ;; + en:running) printf 'Running' ;; + en:preview) printf 'Preview' ;; + en:diagnostics) printf 'Environment diagnostics' ;; + en:ready) printf 'The system is ready' ;; + en:problems) printf 'Problems found' ;; + en:available) printf 'available' ;; + en:missing) printf 'missing' ;; + en:language) printf 'Language' ;; + en:repository) printf 'Repository' ;; + en:privileges) printf 'Privileges' ;; + en:commands) printf 'Short commands' ;; + en:options) printf 'Options' ;; + en:examples) printf 'Examples' ;; + *) printf '%s' "$key" ;; + esac +} + +setup_style() { + if [[ "$color_mode" == 'never' || -n "${NO_COLOR:-}" || ! -t 1 ]]; then + bold=''; dim=''; cyan=''; blue=''; green=''; red=''; reset='' + return + fi + bold=$'\033[1m' + dim=$'\033[2m' + cyan=$'\033[38;5;44m' + blue=$'\033[38;5;75m' + green=$'\033[38;5;78m' + red=$'\033[38;5;203m' + reset=$'\033[0m' +} + +fail() { + printf '%s%sNivora:%s %s\n' "$bold" "$red" "$reset" "$*" >&2 + exit 2 +} + +validate_settings() { + case "$language" in + ru | en) ;; + *) + local invalid_language="$language" + language='en' + fail "$(message invalid_language): ${invalid_language}" + ;; + esac + if [[ "$repository" != 'none' && + ! "$repository" =~ ^[[:alnum:]_.-]+$ ]]; then + fail "$(message invalid_repository): ${repository}" + fi +} + +parse_options() { + arguments=() + while [[ "$#" -gt 0 ]]; do + case "$1" in + --lang) + [[ "$#" -ge 2 ]] || fail "$(message missing_argument): --lang" + language="$2" + shift 2 + ;; + --lang=*) + language="${1#--lang=}" + shift + ;; + --repo) + [[ "$#" -ge 2 ]] || fail "$(message missing_argument): --repo" + repository="$2" + shift 2 + ;; + --repo=*) + repository="${1#--repo=}" + shift + ;; + --dry-run) + dry_run=1 + shift + ;; + --no-color) + color_mode='never' + shift + ;; + --) + shift + arguments+=("$@") + break + ;; + *) + arguments+=("$1") + shift + ;; + esac + done + setup_style + validate_settings +} + +print_header() { + printf '%s%s╭─ NIVORA ─────────────────────────────╮%s\n' "$bold" "$cyan" "$reset" + if [[ "$language" == 'ru' ]]; then + printf '%s%s│%s Простое управление пакетами Stapler %s%s│%s\n' \ + "$cyan" "$bold" "$reset" "$bold" "$cyan" "$reset" + else + printf '%s%s│%s Simple Stapler package management %s%s│%s\n' \ + "$cyan" "$bold" "$reset" "$bold" "$cyan" "$reset" + fi + printf '%s%s╰──────────────────────────────────────╯%s\n' "$bold" "$cyan" "$reset" +} + +menu_row() { + local key="$1" + local label="$2" + local shortcut="$3" + local padding=$((23 - ${#label})) + [[ "$padding" -lt 1 ]] && padding=1 + printf ' %s[%s]%s %s' "$cyan" "$key" "$reset" "$label" + printf '%*s' "$padding" '' + printf '%s%s%s\n' "$dim" "$shortcut" "$reset" +} + +interactive_menu() { + if [[ ! -t 0 || ! -t 1 ]]; then + show_help + return + fi + + print_header + printf '\n %s%s%s\n' "$bold" "$(message packages)" "$reset" + menu_row i "$(message install)" nvi + menu_row e "$(message remove)" nve + menu_row u "$(message upgrade)" nvu + menu_row s "$(message search)" nvs + menu_row q "$(message info)" nvqi + menu_row a "$(message all_packages)" nvqa + printf '\n %s%s%s\n' "$bold" "$(message system)" "$reset" + menu_row r "$(message refresh)" nvr + menu_row l "$(message repositories)" nvrl + menu_row f "$(message fix)" nvf + menu_row d "$(message doctor)" nvd + local help_label + help_label="$(message help)" + printf '\n %s[h]%s %s' "$cyan" "$reset" "$help_label" + printf '%*s' "$((23 - ${#help_label}))" '' + printf '%s[x]%s %s\n\n' "$cyan" "$reset" "$(message exit)" + printf '%s › ' "$(message choose)" + read -r choice + case "$choice" in + i) prompt_and_dispatch install package ;; + e) prompt_and_dispatch remove package ;; + u) dispatch upgrade ;; + s) prompt_and_dispatch search query ;; + q) prompt_and_dispatch info package ;; + a) dispatch list ;; + r) dispatch refresh ;; + l) dispatch repo-list ;; + f) dispatch fix ;; + d) doctor ;; + h) show_help ;; + x | q!) return ;; + *) fail "$(message unknown_choice): ${choice}" ;; + esac +} + +prompt_and_dispatch() { + local command_name="$1" + local prompt_type="$2" + local value + if [[ "$prompt_type" == 'package' ]]; then + printf '%s › ' "$(message package_prompt)" + else + printf '%s › ' "$(message query_prompt)" + fi + read -r value + [[ -n "$value" ]] || fail "$(message missing_argument): ${command_name}" + dispatch "$command_name" "$value" +} + +show_help() { + print_header + if [[ "$language" == 'ru' ]]; then + cat < [аргументы] выполнить команду + +${bold}$(message commands)${reset} + ${cyan}nvi${reset} <пакет...> установить ${cyan}nvr${reset} обновить репозитории + ${cyan}nve${reset} <пакет...> удалить ${cyan}nvrl${reset} показать репозитории + ${cyan}nvu${reset} обновить пакеты ${cyan}nvf${reset} исправить Stapler + ${cyan}nvs${reset} <запрос> найти ${cyan}nvd${reset} запустить диагностику + ${cyan}nvqi${reset} <пакет> информация ${cyan}nvc${reset} конфигурация Stapler + ${cyan}nvqa${reset} каталог пакетов + +${bold}$(message options)${reset} + --repo <имя|none> репозиторий коротких имён (по умолчанию: nivora) + --lang язык интерфейса + --dry-run показать команду без выполнения + --no-color отключить цвета + +${bold}$(message examples)${reset} + nvi codex + nvs editor + nvqi codex + nv --repo none install other/package +EOF + else + cat < [arguments] run a command + +${bold}$(message commands)${reset} + ${cyan}nvi${reset} install ${cyan}nvr${reset} refresh repositories + ${cyan}nve${reset} remove ${cyan}nvrl${reset} list repositories + ${cyan}nvu${reset} upgrade packages ${cyan}nvf${reset} repair Stapler + ${cyan}nvs${reset} search ${cyan}nvd${reset} run diagnostics + ${cyan}nvqi${reset} information ${cyan}nvc${reset} configure Stapler + ${cyan}nvqa${reset} package catalog + +${bold}$(message options)${reset} + --repo repository for short names (default: nivora) + --lang interface language + --dry-run print the command without running it + --no-color disable colors + +${bold}$(message examples)${reset} + nvi codex + nvs editor + nvqi codex + nv --repo none install other/package +EOF + fi +} + +need_stapler() { + command -v stplr >/dev/null 2>&1 || fail "$(message stapler_missing)" +} + +privilege_command() { + [[ "$EUID" -eq 0 ]] && return + local command_name="${NIVORA_SUDO:-sudo}" + command -v "$command_name" >/dev/null 2>&1 || + fail "$(message privilege_missing): ${command_name}" + printf '%s\n' "$command_name" +} + +is_package_name() { + local value="$1" + [[ -n "$value" && + "$value" != -* && + "$value" != */* && + "$value" != ./* && + "$value" != ../* && + "$value" != /* && + "$value" != *.rpm && + "$value" != *.deb && + "$value" != *.pkg.tar.* ]] +} + +normalize_packages() { + local argument + for argument in "$@"; do + if [[ "$repository" != 'none' ]] && is_package_name "$argument"; then + printf '%s/%s\n' "$repository" "$argument" + else + printf '%s\n' "$argument" + fi + done +} + +print_command() { + local label + if [[ "$dry_run" -eq 1 ]]; then + label="$(message preview)" + else + label="$(message running)" + fi + printf '%s%s%s%s %s›%s' "$bold" "$blue" "$label" "$reset" "$cyan" "$reset" >&2 + printf ' %q' "$@" >&2 + printf '\n' >&2 +} + +run_stapler() { + local access="$1" + shift + need_stapler + + local -a command=() + local privilege + if [[ "$access" == 'privileged' ]]; then + privilege="$(privilege_command)" + [[ -n "$privilege" ]] && command+=("$privilege") + fi + command+=(stplr "$@") + + [[ "${NIVORA_QUIET:-0}" == '1' ]] || print_command "${command[@]}" + [[ "$dry_run" -eq 1 ]] && return + exec "${command[@]}" +} + +require_arguments() { + local command_name="$1" + local count="$2" + [[ "$count" -gt 0 ]] || fail "$(message missing_argument): ${command_name}" +} + +doctor() { + local failures=0 + local privilege="${NIVORA_SUDO:-sudo}" + printf '%s%s%s\n\n' "$bold" "$(message diagnostics)" "$reset" + + if command -v stplr >/dev/null 2>&1; then + printf ' %s●%s %-14s %s\n' "$green" "$reset" 'Stapler' "$(message available)" + else + printf ' %s●%s %-14s %s\n' "$red" "$reset" 'Stapler' "$(message missing)" + failures=1 + fi + + if [[ "$EUID" -eq 0 ]] || command -v "$privilege" >/dev/null 2>&1; then + printf ' %s●%s %-14s %s\n' \ + "$green" "$reset" "$(message privileges)" \ + "$([[ "$EUID" -eq 0 ]] && printf 'root' || printf '%s' "$privilege")" + else + printf ' %s●%s %-14s %s\n' \ + "$red" "$reset" "$(message privileges)" "$(message missing)" + failures=1 + fi + + printf ' %s●%s %-14s %s\n' "$green" "$reset" "$(message language)" "$language" + printf ' %s●%s %-14s %s\n\n' \ + "$green" "$reset" "$(message repository)" "$repository" + + if [[ "$failures" -eq 0 ]]; then + printf '%s✓ %s%s\n' "$green" "$(message ready)" "$reset" + else + printf '%s✗ %s%s\n' "$red" "$(message problems)" "$reset" + fi + return "$failures" +} + +completion() { + local shell="${1:-}" + case "$shell" in + bash) + cat <<'EOF' +_nivora() { + local commands='install remove upgrade search info list refresh repo-list fix doctor config completion version help' + if (( COMP_CWORD == 1 )); then + COMPREPLY=($(compgen -W "$commands" -- "${COMP_WORDS[COMP_CWORD]}")) + fi +} +complete -F _nivora nv +EOF + ;; + fish) + cat <<'EOF' +complete -c nv -f -n '__fish_use_subcommand' -a 'install remove upgrade search info list refresh repo-list fix doctor config completion version help' +EOF + ;; + zsh) + cat <<'EOF' +#compdef nv +_arguments '1:command:(install remove upgrade search info list refresh repo-list fix doctor config completion version help)' '*::argument:->arguments' +EOF + ;; + *) fail "$(message missing_argument): completion " ;; + esac +} + +dispatch() { + local command_name="${1:-help}" + [[ "$#" -gt 0 ]] && shift + + case "$command_name" in + install | i) + require_arguments "$command_name" "$#" + local -a packages=() + mapfile -t packages < <(normalize_packages "$@") + run_stapler privileged install "${packages[@]}" + ;; + remove | erase | e) + require_arguments "$command_name" "$#" + local -a packages=() + mapfile -t packages < <(normalize_packages "$@") + run_stapler privileged remove "${packages[@]}" + ;; + upgrade | update | u) run_stapler privileged up "$@" ;; + search | s) + require_arguments "$command_name" "$#" + run_stapler user search "$@" + ;; + info | query-info | qi) + require_arguments "$command_name" "$#" + local -a packages=() + mapfile -t packages < <(normalize_packages "$@") + run_stapler user info "${packages[@]}" + ;; + list | packages | query-all | qa) run_stapler user list "$@" ;; + refresh | r) run_stapler privileged refresh "$@" ;; + repo-list | rl) run_stapler user repo list "$@" ;; + repo) + if [[ "$#" -eq 0 ]]; then + run_stapler user repo list + elif [[ "${1:-}" == 'list' || "${1:-}" == 'ls' ]]; then + run_stapler user repo "$@" + else + run_stapler privileged repo "$@" + fi + ;; + fix | f) run_stapler privileged fix "$@" ;; + doctor | d) doctor ;; + config | c) + if [[ "$#" -eq 0 ]]; then + run_stapler user config show + else + run_stapler user config "$@" + fi + ;; + completion) completion "$@" ;; + support) run_stapler user support "$@" ;; + version | v | --version) + printf 'Nivora CLI %s\n' "$NIVORA_VERSION" + command -v stplr >/dev/null 2>&1 && stplr version + ;; + help | h | -h | --help) show_help ;; + *) fail "$(message unknown_command): ${command_name}" ;; + esac +} + +main() { + detect_language + setup_style + parse_options "$@" + set -- "${arguments[@]}" + + case "$(basename "$0")" in + nv) + if [[ "$#" -eq 0 ]]; then interactive_menu; else dispatch "$@"; fi + ;; + nvi) dispatch install "$@" ;; + nve) dispatch remove "$@" ;; + nvu) dispatch upgrade "$@" ;; + nvs) dispatch search "$@" ;; + nvqi) dispatch info "$@" ;; + nvqa) dispatch list "$@" ;; + nvr) dispatch refresh "$@" ;; + nvrl) dispatch repo-list "$@" ;; + nvf) dispatch fix "$@" ;; + nvd) doctor ;; + nvc) dispatch config "$@" ;; + nivora) dispatch "$@" ;; + *) fail "$(message unknown_command): $(basename "$0")" ;; + esac +} + +main "$@" diff --git a/nivora-stplr/stapler-repo.toml b/nivora-cli/stapler-repo.toml similarity index 100% rename from nivora-stplr/stapler-repo.toml rename to nivora-cli/stapler-repo.toml diff --git a/nivora-stplr/LICENSE b/nivora-stplr/LICENSE deleted file mode 100644 index dc1024b..0000000 --- a/nivora-stplr/LICENSE +++ /dev/null @@ -1,16 +0,0 @@ -Сведения о лицензии пакета Nivora Stapler Helper - -Лицензия: MIT. -Исходный проект: https://github.com/Cheviiot/Nivora - -Нюансы пакета: -- Пакет устанавливает CLI-helper для Stapler с командами `sl`, `sli`, `slr`, `slu`, `slf`, `slref`, `sls`, `slii` и `sll`. -- Helper не заменяет Stapler: все операции выполняются через системную команду `stplr`. -- Через команду повышения привилегий запускаются только `install`, `remove`, `up`, `fix` и `refresh`. -- Команды чтения `search`, `info` и `list` выполняются без повышения привилегий. -- Префикс репозитория задается через `--repo ` или `NIVORA_STPLR_REPO`; по умолчанию короткие имена не привязаны к одному репозиторию. -- Пути вида `repo/package` не изменяются. -- Helper не исполняет произвольные shell-строки через `sudo`. - -Полный официальный текст MIT: -https://opensource.org/licenses/MIT diff --git a/nivora-stplr/Staplerfile b/nivora-stplr/Staplerfile deleted file mode 100644 index e943c29..0000000 --- a/nivora-stplr/Staplerfile +++ /dev/null @@ -1,55 +0,0 @@ -name='nivora-stplr' -version='0.3.0' -release=1 -summary='Short command helper for Stapler' -summary_ru='Короткие команды для Stapler' -desc='Nivora Stapler Helper provides short commands for common Stapler operations across configured repositories and elevates only operations that modify packages or caches.' -desc_ru='Nivora Stapler Helper добавляет короткие команды для типовых операций Stapler и повышает привилегии только для изменения пакетов и кэша.' -group='System/Configuration/Packaging' -homepage='https://github.com/Cheviiot/Nivora' -maintainer='Cheviiot ' - -architectures=('all') -license=('MIT') - -provides=() -replaces=('nivora-stplr') -conflicts=() - -auto_reqprov_method='dirty' -auto_req=0 -auto_prov=0 - -deps=() -deps_debian=('bash' 'sudo') -deps_ubuntu=("${deps_debian[@]}") -deps_fedora=('bash' 'sudo') -deps_arch=('bash' 'sudo') -deps_altlinux=('bash' 'sudo') -deps_opensuse=('bash' 'sudo') - -sources=( - 'local:///nivora-stplr' - 'local:///LICENSE' -) - -checksums=( - 'sha256:7b0c2a69aed30bc39ac5546aabd0cb82d45b857a52408b4326ad1fd667684ecb' - 'sha256:c5674e6873211a8ea0f08ee3a950f809c9145c60750746dafff9aee7e1c21cf1' -) - -package() { - install -Dm755 "${srcdir}/nivora-stplr" "${pkgdir}/usr/bin/sl" - - shortcut='' - for shortcut in sli slr slu slf slref sls slii sll; do - ln -sfn sl "${pkgdir}/usr/bin/${shortcut}" - done - - install-license "${srcdir}/LICENSE" 'nivora-stplr/LICENSE.nivora' -} - -files() { - files-find-binary - files-find-license -} diff --git a/nivora-stplr/nivora-stplr b/nivora-stplr/nivora-stplr deleted file mode 100755 index 917ed54..0000000 --- a/nivora-stplr/nivora-stplr +++ /dev/null @@ -1,239 +0,0 @@ -#!/bin/bash -set -euo pipefail - -repo_prefix="${NIVORA_STPLR_REPO:-}" - -usage() { - cat <<'EOF' -Nivora Stapler Helper - -Usage: - sl [args...] - sli [stplr args...] sudo stplr install - slr [stplr args...] sudo stplr remove - slu [stplr args...] sudo stplr up - slf [stplr args...] sudo stplr fix - slref [stplr args...] sudo stplr refresh - sls [stplr args...] stplr search - slii [stplr args...] stplr info - sll [stplr args...] stplr list - -Commands: - install, in, i install package with sudo - remove, rm, r remove package with sudo - up, upgrade, u upgrade installed packages with sudo - fix, f run stplr fix with sudo - refresh, ref refresh repositories with sudo - search, s search packages - info, show show package info - list, ls list repository packages - help show this help - -Options: - --repo prefix short package names with repository, e.g. sli --repo nivora parsec - -Environment: - NIVORA_STPLR_REPO optional repository prefix for short names, default: disabled - NIVORA_STPLR_SUDO privilege command, default: sudo - NIVORA_STPLR_QUIET=1 do not print command before execution -EOF -} - -die() { - echo "nivora-stplr: $*" >&2 - exit 2 -} - -need_stplr() { - command -v stplr >/dev/null 2>&1 || die "команда stplr не найдена" -} - -sudo_cmd() { - if [[ "${EUID}" -eq 0 ]]; then - return 0 - fi - - local command_name="${NIVORA_STPLR_SUDO:-sudo}" - command -v "${command_name}" >/dev/null 2>&1 || die "команда ${command_name} не найдена" - printf '%s\n' "${command_name}" -} - -is_package_token() { - local value="$1" - - [[ -n "${value}" ]] || return 1 - [[ "${value}" == -* ]] && return 1 - [[ "${value}" == */* ]] && return 1 - [[ "${value}" == ./* || "${value}" == ../* || "${value}" == /* ]] && return 1 - [[ "${value}" == *.rpm || "${value}" == *.deb || "${value}" == *.pkg.tar.* ]] && return 1 - return 0 -} - -normalize_package() { - local value="$1" - - if [[ -n "${repo_prefix}" ]] && is_package_token "${value}"; then - printf '%s/%s\n' "${repo_prefix}" "${value}" - else - printf '%s\n' "${value}" - fi -} - -normalize_package_args() { - local arg - - for arg in "$@"; do - normalize_package "${arg}" - done -} - -extract_repo_option() { - local -n output_ref="$1" - shift - - output_ref=() - while [[ "$#" -gt 0 ]]; do - case "$1" in - --repo) - [[ "$#" -ge 2 ]] || die "для --repo нужно имя репозитория" - repo_prefix="$2" - shift 2 - ;; - --repo=*) - repo_prefix="${1#--repo=}" - shift - ;; - *) - output_ref+=("$1") - shift - ;; - esac - done -} - -print_and_exec() { - local -a command=("$@") - - if [[ "${NIVORA_STPLR_QUIET:-0}" != "1" ]]; then - printf '=>' - printf ' %q' "${command[@]}" - printf '\n' - fi - - exec "${command[@]}" -} - -run_stplr() { - local mode="$1" - shift - - need_stplr - - local -a command=() - if [[ "${mode}" == "sudo" ]]; then - local privilege - privilege="$(sudo_cmd)" - if [[ -n "${privilege}" ]]; then - command+=("${privilege}") - fi - fi - - command+=(stplr "$@") - print_and_exec "${command[@]}" -} - -require_args() { - local command_name="$1" - local count="$2" - - if [[ "${count}" -eq 0 ]]; then - die "для команды ${command_name} нужен аргумент" - fi -} - -dispatch() { - local command_name="$1" - shift - - case "${command_name}" in - install | in | i) - require_args "${command_name}" "$#" - local -a args=() - extract_repo_option args "$@" - require_args "${command_name}" "${#args[@]}" - mapfile -t normalized < <(normalize_package_args "${args[@]}") - run_stplr sudo install "${normalized[@]}" - ;; - remove | rm | r | erase | delete | del) - require_args "${command_name}" "$#" - local -a args=() - extract_repo_option args "$@" - require_args "${command_name}" "${#args[@]}" - mapfile -t normalized < <(normalize_package_args "${args[@]}") - run_stplr sudo remove "${normalized[@]}" - ;; - up | upgrade | u) - run_stplr sudo up "$@" - ;; - fix | f) - run_stplr sudo fix "$@" - ;; - refresh | ref) - run_stplr sudo refresh "$@" - ;; - search | s) - require_args "${command_name}" "$#" - run_stplr user search "$@" - ;; - info | show) - require_args "${command_name}" "$#" - local -a args=() - extract_repo_option args "$@" - require_args "${command_name}" "${#args[@]}" - mapfile -t normalized < <(normalize_package_args "${args[@]}") - run_stplr user info "${normalized[@]}" - ;; - list | ls | l) - run_stplr user list "$@" - ;; - version | support) - run_stplr user "${command_name}" "$@" - ;; - help | -h | --help | "") - usage - ;; - *) - die "неизвестная команда: ${command_name}. Запустите: sl help" - ;; - esac -} - -main() { - local invoked - invoked="$(basename "$0")" - - case "${invoked}" in - sli) dispatch install "$@" ;; - slr) dispatch remove "$@" ;; - slu) dispatch up "$@" ;; - slf) dispatch fix "$@" ;; - slref) dispatch refresh "$@" ;; - sls) dispatch search "$@" ;; - slii) dispatch info "$@" ;; - sll) dispatch list "$@" ;; - sl) - if [[ "$#" -eq 0 ]]; then - usage - return 0 - fi - local command_name="$1" - shift - dispatch "${command_name}" "$@" - ;; - *) - die "неизвестное имя запуска: ${invoked}" - ;; - esac -} - -main "$@" diff --git a/tests/test_helper.sh b/tests/test_helper.sh deleted file mode 100755 index 34d939a..0000000 --- a/tests/test_helper.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash -set -euo pipefail - -repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -temp_dir="$(mktemp -d)" - -cleanup() { - find "$temp_dir" -mindepth 1 -delete - rmdir "$temp_dir" -} -trap cleanup EXIT - -mkdir -p "${temp_dir}/bin" -log="${temp_dir}/commands.log" - -cat >"${temp_dir}/bin/stplr" <<'EOF' -#!/bin/bash -printf 'stplr' >>"$NIVORA_TEST_LOG" -printf ' %s' "$@" >>"$NIVORA_TEST_LOG" -printf '\n' >>"$NIVORA_TEST_LOG" -EOF - -cat >"${temp_dir}/bin/test-sudo" <<'EOF' -#!/bin/bash -printf 'sudo' >>"$NIVORA_TEST_LOG" -printf ' %s' "$@" >>"$NIVORA_TEST_LOG" -printf '\n' >>"$NIVORA_TEST_LOG" -exec "$@" -EOF - -chmod 0755 "${temp_dir}/bin/"* -for alias in sli slii sl; do - ln -s "$repo_root/nivora-stplr/nivora-stplr" "${temp_dir}/bin/${alias}" -done - -export NIVORA_TEST_LOG="$log" -export NIVORA_STPLR_SUDO=test-sudo -export NIVORA_STPLR_QUIET=1 -export PATH="${temp_dir}/bin:${PATH}" - -sli --repo nivora parsec --clean -slii nivora/codex - -cat >"${temp_dir}/expected" <<'EOF' -sudo stplr install nivora/parsec --clean -stplr install nivora/parsec --clean -stplr info nivora/codex -EOF - -diff -u "${temp_dir}/expected" "$log" -echo 'OK: Nivora helper' diff --git a/tests/test_nivora_cli.sh b/tests/test_nivora_cli.sh new file mode 100755 index 0000000..ceaa538 --- /dev/null +++ b/tests/test_nivora_cli.sh @@ -0,0 +1,96 @@ +#!/bin/bash +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +temp_dir="$(mktemp -d)" + +cleanup() { + find "$temp_dir" -mindepth 1 -delete + rmdir "$temp_dir" +} +trap cleanup EXIT + +mkdir -p "${temp_dir}/bin" +log="${temp_dir}/commands.log" + +cat >"${temp_dir}/bin/stplr" <<'EOF' +#!/bin/bash +printf 'stplr' >>"$NIVORA_TEST_LOG" +printf ' %s' "$@" >>"$NIVORA_TEST_LOG" +printf '\n' >>"$NIVORA_TEST_LOG" +EOF + +cat >"${temp_dir}/bin/test-sudo" <<'EOF' +#!/bin/bash +printf 'sudo' >>"$NIVORA_TEST_LOG" +printf ' %s' "$@" >>"$NIVORA_TEST_LOG" +printf '\n' >>"$NIVORA_TEST_LOG" +exec "$@" +EOF + +chmod 0755 "${temp_dir}/bin/"* +for alias in nv nvi nve nvu nvs nvqi nvqa nvr nvrl nvf nvd nvc; do + ln -s "${repo_root}/nivora-cli/nivora" "${temp_dir}/bin/${alias}" +done + +export NIVORA_TEST_LOG="$log" +export NIVORA_SUDO='test-sudo' +export NIVORA_QUIET=1 +export PATH="${temp_dir}/bin:${PATH}" + +nvi codex --clean +nve other/package +nvu +nvs editor +nvqi --json codex +nvqa --installed +nvr +nvrl +nvf +nvc +nv --repo none install local-name + +cat >"${temp_dir}/expected" <<'EOF' +sudo stplr install nivora/codex --clean +stplr install nivora/codex --clean +sudo stplr remove other/package +stplr remove other/package +sudo stplr up +stplr up +stplr search editor +stplr info --json nivora/codex +stplr list --installed +sudo stplr refresh +stplr refresh +stplr repo list +sudo stplr fix +stplr fix +stplr config show +sudo stplr install local-name +stplr install local-name +EOF + +diff -u "${temp_dir}/expected" "$log" + +nv --lang en --help >"${temp_dir}/help-en" +nv --lang ru --help >"${temp_dir}/help-ru" +nv --lang ru completion fish >"${temp_dir}/completion" +nvd --lang en >"${temp_dir}/doctor" +NIVORA_QUIET=0 nv --lang en --dry-run install codex \ + >"${temp_dir}/preview-stdout" 2>"${temp_dir}/preview" + +grep -q 'Simple Stapler package management' "${temp_dir}/help-en" +grep -q 'Простое управление пакетами Stapler' "${temp_dir}/help-ru" +grep -q 'nvi.*установить' "${temp_dir}/help-ru" +grep -q '__fish_use_subcommand' "${temp_dir}/completion" +grep -q 'The system is ready' "${temp_dir}/doctor" +grep -q 'Preview.*test-sudo stplr install nivora/codex' "${temp_dir}/preview" +[[ ! -s "${temp_dir}/preview-stdout" ]] + +if nv --lang de help >"${temp_dir}/invalid-out" 2>"${temp_dir}/invalid"; then + echo 'Ожидалась ошибка для неподдерживаемого языка' >&2 + exit 1 +fi +grep -q 'supported languages are ru and en' "${temp_dir}/invalid" + +echo 'OK: Nivora CLI' diff --git a/tools/package_updates.sh b/tools/package_updates.sh index 2f37212..2f1b9f8 100755 --- a/tools/package_updates.sh +++ b/tools/package_updates.sh @@ -13,8 +13,9 @@ readonly -a PACKAGES=( fisher github-desktop happ + msvc-go-wine netbird - nivora-stplr + nivora-cli opencode parsec pineconemc @@ -226,8 +227,9 @@ latest_version() { github_latest_release desktop/desktop | sed 's/^release-//' ;; happ) github_latest_release Happ-proxy/happ-desktop ;; + msvc-go-wine) github_latest_release Cheviiot/msvc-go-wine ;; netbird) github_latest_release netbirdio/netbird ;; - nivora-stplr) current_version nivora-stplr ;; + nivora-cli) current_version nivora-cli ;; opencode) github_latest_release anomalyco/opencode ;; parsec) latest_parsec ;; pineconemc) github_latest_release ElyPrismLauncher/Launcher ;; diff --git a/tools/run_checks.sh b/tools/run_checks.sh index 197a54e..71cbafc 100755 --- a/tools/run_checks.sh +++ b/tools/run_checks.sh @@ -32,7 +32,7 @@ if [[ "${#python_files[@]}" -gt 0 ]]; then fi python3 -m unittest discover -s tests -p 'test_*.py' -v -bash tests/test_helper.sh +bash tests/test_nivora_cli.sh bash tests/test_claude_alt.sh bash tests/test_codex_computer_use.sh bash tests/test_happ_theme.sh diff --git a/tools/test_package_lifecycle.sh b/tools/test_package_lifecycle.sh index db5d1dc..8c24220 100755 --- a/tools/test_package_lifecycle.sh +++ b/tools/test_package_lifecycle.sh @@ -28,7 +28,7 @@ readonly -a lifecycle_packages=( 'clash-verge-rev|/usr/bin/clash-verge|/home/nivora-test/.local/share/io.github.clash-verge-rev.clash-verge-rev/nivora-lifecycle-state|/usr/share/applications/Clash Verge.desktop|/usr/share/icons/hicolor/128x128/apps/clash-verge.png' 'claude|/usr/bin/claude-desktop|/home/nivora-test/.config/Claude/nivora-lifecycle-state|/usr/share/applications/com.anthropic.Claude.desktop|/usr/share/icons/hicolor/128x128/apps/claude-desktop.png' 'codex|/usr/bin/codex-app|/home/nivora-test/.codex/nivora-lifecycle-state|/usr/share/applications/codex-app.desktop|/usr/share/icons/hicolor/512x512/apps/codex-app.png' - 'nivora-stplr|/usr/bin/sl|/home/nivora-test/.config/nivora-stplr/nivora-lifecycle-state|-|-' + 'nivora-cli|/usr/bin/nv|/home/nivora-test/.config/nivora-cli/nivora-lifecycle-state|-|-' 'opencode|/usr/bin/opencode-desktop|/home/nivora-test/.config/opencode/nivora-lifecycle-state|/usr/share/applications/opencode-desktop.desktop|/usr/share/icons/hicolor/128x128/apps/ai.opencode.desktop.png' 'tailscale|/usr/bin/tailscale|/var/lib/tailscale/nivora-lifecycle-state|/usr/lib/systemd/system/tailscaled.service|-' 'netbird|/usr/bin/netbird|/var/lib/netbird/nivora-lifecycle-state|/usr/lib/systemd/system/netbird.service|-' diff --git a/tools/validate_repo.py b/tools/validate_repo.py index e66c280..51acfcc 100755 --- a/tools/validate_repo.py +++ b/tools/validate_repo.py @@ -23,8 +23,9 @@ EXPECTED_PACKAGES = ( "fisher", "github-desktop", "happ", + "msvc-go-wine", "netbird", - "nivora-stplr", + "nivora-cli", "opencode", "parsec", "pineconemc", @@ -48,7 +49,7 @@ REQUIRED_ROOT_FILES = { Path("docs/packages/codex.md"), Path("docs/packages/github-desktop.md"), Path("docs/packages/opencode.md"), - Path("docs/packages/nivora-stplr.md"), + Path("docs/packages/nivora-cli.md"), Path("docs/packages/ventoy.md"), }