
sonar v0.4.1
Инструмент командной строки для проверки и управления службами, прослушивающими порты localhost
██████ ███ ██████ ██ ██████
██ ██ ██ ██ ██ ██
██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ ██ ██ ██ ████
██ ██ ██ ██ ██ ██ ██ ██
██████ ███ ██ ██ ██ ██ ██ ██
Знайте, что работает на вашей машине.
Sonar показывает всё, что слушает на localhost, и приводит это в порядок: каждый порт
принадлежит группе — обычно это репозиторий, из которого он был запущен, — а
внутри этой группы именованному сервису. Запускайте свои dev-серверы с помощью
sonar start, и весь проект становится одной сущностью, которую можно вывести в виде дерева,
дождаться, отслеживать и остановить одной командой. Docker-контейнеры, проекты Compose
и процессы, запущенные вручную, тоже подхватываются, без какой-либо
конфигурации.
$ sonar list --tree
my-app (3 ports, running) ~/code/my-app
├─ 5432 db postgres:17 http://localhost:5432
├─ 5173 frontend vite (v5.4) http://localhost:5173
└─ 8000 api uvicorn app:app http://localhost:8000
ungrouped (1 port)
└─ 3000 next-server (v16.1.6) http://localhost:3000
Установка
Homebrew (macOS / Linux)
brew install raskrebs/sonar/sonar
Homebrew 6 отказывается загружать формулы из сторонних tap-репозиториев, пока вы один раз не подтвердите доверие к tap
(Error: Refusing to load formula raskrebs/sonar/sonar from untrusted tap):
brew trust raskrebs/sonar
Скрипт установки
curl -sfL https://raw.githubusercontent.com/raskrebs/sonar/main/scripts/install.sh | bash
Загружает последний бинарный файл в ~/.local/bin и добавляет его в ваш PATH при необходимости. Перезапустите терминал или выполните source ~/.zshrc.
В Windows (PowerShell):
irm https://raw.githubusercontent.com/raskrebs/sonar/main/scripts/install.ps1 | iex
Пользовательское расположение установки:
curl -sfL https://raw.githubusercontent.com/raskrebs/sonar/main/scripts/install.sh | SONAR_INSTALL_DIR=/usr/local/bin bash
Установите конкретную версию:
curl -sfL https://raw.githubusercontent.com/raskrebs/sonar/main/scripts/install.sh | SONAR_VERSION=vX.Y.Z bash
$env:SONAR_VERSION="vX.Y.Z"; irm https://raw.githubusercontent.com/raskrebs/sonar/main/scripts/install.ps1 | iex
Использование Go
go install github.com/raskrebs/sonar@latest
Завершения оболочки (автодополнение номеров портов по Tab):
sonar completion zsh > "${fpath[1]}/_sonar" # zsh
sonar completion bash > /etc/bash_completion.d/sonar # bash
sonar completion fish | source # fish
Шестьдесят секунд
Опишите, как запускается ваш проект, в файле sonar.yaml в корне репозитория,
вместо dev.sh:
name: my-app
services:
- name: db
cmd: docker compose up db
port: 5432
- name: api
cmd: uv run uvicorn app:app --port ${port}
port: auto
depends_on: [db]
- name: frontend
cmd: npm run dev -- --port ${port} --strictPort
port: auto
depends_on: [api]
env:
VITE_API_URL: ${api.url}
Затем запустите его:
sonar start
✓ db http://localhost:5432 pid 41022 ~/.config/sonar/logs/my-app/db.log
✓ api http://localhost:21408 pid 41040 ~/.config/sonar/logs/my-app/api.log
✓ frontend http://localhost:21409 pid 41077 ~/.config/sonar/logs/my-app/frontend.log
3 started
following the logs; Ctrl+C stops the 3 services started here
api | INFO: Uvicorn running on http://127.0.0.1:21408
frontend | VITE v5.4 ready in 312 ms
Sonar выбирает свободный порт для каждого сервиса с port: auto и сообщает каждому сервису, где находятся остальные, поэтому второе рабочее дерево запускается рядом с первым без единого конфликта портов. sonar start -d делает то же самое в фоновом режиме, а sonar down останавливает его и освобождает порты. В другом терминале:
sonar list --tree
my-app (3 ports, running) ~/code/my-app
├─ 5432 db postgres:17 http://localhost:5432
├─ 21408 api uvicorn app:app http://localhost:21408
└─ 21409 frontend vite (v5.4) http://localhost:21409
Нет sonar.yaml? sonar start -- npm run dev запускает одну команду как именованный
сервис, а sonar list и sonar kill работают со всем, что слушает.
Примеры ниже, помеченные # check, выполняются на свежей сборке
скриптом scripts/readme-check.sh при каждом запуске CI.
Команды
sonar list
sonar list
sonar list --tree
sonar list --group my-app
sonar list --json
# check
sonar list --stats # CPU, memory, threads, uptime, state
sonar list --health # HTTP health checks
sonar list --filter docker # only Docker ports
sonar list --sort name # port | pid | name | type
sonar list -a # include desktop apps
sonar list -c port,process,group,cpu,mem
sonar list --host user@server # scan a remote machine over SSH
Столбцы по умолчанию: port, process, group, container, image,
containerport, url, где process показывает имя, которое вы дали порту
(sonar rename), затем имя сервиса, затем то, что было обнаружено.
Доступные столбцы: port, process, pid, type, url, group, cpu,
mem, threads, uptime, state, connections, health, latency,
container, image, containerport, compose, project, user, bind,
ip.
Десктопные приложения и системные службы, которые случайно прослушивают порты — Figma, Discord,
Spotify, ControlCenter, macOS .app bundles, демоны /System/Library/ —
скрыты, если не передать -a.
sonar start
Запустить проект из его sonar.yaml:
sonar start # every service in the nearest sonar.yaml
sonar start api frontend # only these (they still wait for their dependencies)
sonar start ../other-project # the sonar.yaml in or above another directory
sonar start -d # in the background, like `sonar up`
На переднем плане sonar запускает сервисы через демон, следит за их логами, добавляя имя сервиса перед каждой строкой, и при нажатии Ctrl+C останавливает запущенные им сервисы. Сервис, который уже был запущен, не затрагивается, и sonar завершается сам, как только все запущенные им сервисы завершатся.
Аргументы перед -- задают каталог или сервисы; всё после -- — это
команда. Либо запустите одну команду как именованный сервис в группе:
sonar start -- npm run dev
sonar start --group my-app --name frontend -- npm run dev
sonar start --port 5173 -- npm run dev # expected port, before it binds
sonar start --detach --name api -- uv run uvicorn app:app
sonar start --list
Ничего передавать не нужно:
- Group —
--group, иначеnameиз ближайшегоsonar.yaml, иначе имя каталога git-корня (worktree становитсяrepo@worktree), иначе имя текущего каталога. - Name —
--name, иначе сервис изsonar.yaml, чейcmdсовпадает, иначе выводится из команды (npm run dev→dev,uv run api→api,python -m uvicorn→uvicorn,./dev.sh→dev.sh). - Port —
--port— это подсказка, а не привязка: запуск отображается какstarting, пока порт фактически не начнёт прослушиваться, и демон использует его для сопоставления процесса с портом.
Дочерний процесс наследует stdin, stdout, stderr, cwd и окружение, а также
SONAR_GROUP, SONAR_NAME и SONAR_RUN_ID. Он получает собственную группу процессов,
поэтому sonar kill уничтожает всё дерево — dev-сервер с его наблюдателями и
воркерами. Ctrl+C перенаправляется, и sonar завершается с кодом выхода дочернего процесса.