Files
iptv-docker/iptv

835 lines
30 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
##########################################################################################
# Скрипт управления проектом iptv.axenov.dev
#
# Copyright (c) 2025 Антон Аксенов <anthonyaxenov@gmail.com>
# MIT License, see LICENSE file for more info.
##########################################################################################
# shellcheck disable=SC2015,SC2103,SC2164,SC2155
set -o pipefail
########################################################
# Служебные исходные переменные
########################################################
if [[ "${BASH_SOURCE[*]}" ]]; then
[[ "${BASH_SOURCE[0]}" != "$0" ]] && echo "*** Команда source iptv запрещена ***" && exit 40
ROOT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
else
# script running from stdin
ROOT_PATH="$(pwd)"
fi
IPTV_PROJECTS=("iptvc" "web" "playlists")
IPTV_GITEA_URL_SSH="git@git.axenov.dev:IPTV"
IPTV_GITEA_URL_HTTPS="https://git.axenov.dev/IPTV"
IPTV_DOCKER_URL_SSH="$IPTV_GITEA_URL_SSH/iptv-docker.git"
for sig in SIGHUP SIGINT SIGQUIT SIGABRT SIGKILL SIGTERM SIGTSTP; do
# https://faculty.cs.niu.edu/~hutchins/csci480/signals.htm
# shellcheck disable=SC2064
trap "set +x && echo && echo && echo '*** Прервано сигналом $sig, остановка ***' && exit" $sig
done
[[ $IPTV_DEBUG == 1 ]] && DEBUG_MODE=1
[[ $IPTV_DEBUG -gt 1 ]] && set -x
RAW_ARGS=("$@")
COMMAND="$1"
shift
########################################################
# Ввод/вывод
########################################################
which tput > /dev/null 2>&1 && [ "$(tput -T"$TERM" colors)" -gt 8 ] && CAN_USE_COLORS=1 || CAN_USE_COLORS=0
IPTV_COLORS=${IPTV_COLORS:-$CAN_USE_COLORS}
[[ $IPTV_COLORS == 1 ]] && FBOLD="$(tput bold)" || FBOLD=''
[[ $IPTV_COLORS == 1 ]] && FDIM="$(tput dim)" || FDIM=''
[[ $IPTV_COLORS == 1 ]] && FRESET="$(tput sgr0)" || FRESET=''
ask() {
IFS= read -rp "$(print "${FBOLD}$1" ): " "$2"
}
print() {
echo -e "$*${FRESET}"
}
debug() {
[[ "$DEBUG_MODE" != 1 ]] && return
if [ "$2" ]; then
print "${FDIM}> ${FUNCNAME[1]:-?}():${BASH_LINENO:-?}\t$* ${FRESET}" >&2
else
print "${FDIM}> $* ${FRESET}" >&2
fi
}
var_dump() {
debug "$1 = ${!1}"
}
inform() {
print "$*${FRESET}"
}
subtitle() {
echo
inform "${FBOLD}$*${FRESET}"
}
title() {
subtitle "$@"
echo
}
success() {
print "${FBOLD}$*${FRESET}"
}
warn() {
print "${FBOLD}Внимание!${FRESET} $*${FRESET}"
}
error() {
print "${FBOLD}Ошибка:${FRESET} $*${FRESET}" >&2
print_stacktrace
}
die() {
error "${1:-halted}"
exit "${2:-255}"
}
print_stacktrace() {
[[ "$DEBUG_MODE" != 1 ]] && return
local i
local stack_size=${#FUNCNAME[@]}
debug "Callstack:"
# for (( i=$stack_size-1; i>=1; i-- )); do
for (( i=1; i<stack_size; i++ )); do
local func="${FUNCNAME[$i]}"
[ "$func" = "" ] && func=MAIN
local linen="${BASH_LINENO[$(( i - 1 ))]}"
local src="${BASH_SOURCE[$i]}"
[ "$src" = "" ] && src=non_file_source
debug " at $func $src:$linen"
done
}
########################################################
# Базовые хелперы
########################################################
# Возвращает абсолютный путь до $1
abspath() {
realpath -q "$1"
}
# Проверяет, указывает ли путь $1 на директорию
is_dir() {
[ -d "$(abspath "$1")" ]
}
# Проверяет, указывает ли путь $1 на файл
is_file() {
[ -f "$(abspath "$1")" ]
}
# Проверяет существование функции с именем $1
is_function() {
declare -F "$1" > /dev/null
}
# Проверяет соответствие строки $1 регулярному выражению $2
regex_match() {
[[ "$1" =~ ^$2$ ]]
}
# Проверяет соответствие строки $1 регулярному выражению $2 с помощью grep
grep_match() {
printf "%s" "$1" | grep -qE "$2" >/dev/null 2>&1
}
# Возвращает название текущей ОС
get_os() {
case "$(uname -s)" in
Linux*) echo Linux ;;
Darwin*) echo Macos ;;
CYGWIN*) echo Cygwin ;;
MINGW*) echo MinGw ;;
MSYS_NT*) echo Git ;;
*) return 1 ;;
esac
}
# Обрезает пробельные символы с начала и с конца строки
trim() {
echo "$1" | xargs
}
########################################################
# Функции парсинга аргументов
# https://gist.axenov.dev/anthony/sh-args
########################################################
# Парсит короткий аргумент
arg() {
[ "$1" ] || { echo "Argument name is not specified!" >&2 && exit 1; }
local arg_name="${1:0:1}" # first character of argument name to find
local is_flag="$2" || 0 # 1 if we need just find a flag, 0 to get a value
local var_name="$3" || 0 # variable name to return value into or 0 to echo it in stdout
local value= # initialize empty value to check if we found one later
local arg_found=0 # marker of found argument
for idx in "${!RAW_ARGS[@]}"; do # going through all args
local arg_search=${RAW_ARGS[idx]} # get current argument
# skip $arg_search if it starts with '--' or letter
grep_match "$arg_search" "^(\w|--)" && continue
# clear $arg_search from special and duplicate characters, e.g. 'fas-)dfs' will become 'fasd'
local arg_chars="$(printf "%s" "$arg_search" \
| tr -s "[$arg_search]" 2>/dev/null \
| tr -d "[:punct:][:blank:]" 2>/dev/null)"
# if $arg_name is not one of $arg_chars the skip it
grep_match "-$arg_name" "^-[$arg_chars]$" || continue
arg_found=1
# then return '1'|'0' back into $value if we need flag or next arg value otherwise
[[ "$is_flag" == 1 ]] && value=1 || value="${RAW_ARGS[idx+1]}"
break
done
[[ "$is_flag" == 1 ]] && [[ -z "$value" ]] && value=0;
# if value we found is empty or looks like another argument then exit with error message
if [ "$arg_found" = 1 ] && ! grep_match "$value" "^[[:graph:]]+$" || grep_match "$value" "^--?\w+$"; then
echo "ERROR: Argument '-$arg_name' must have correct value!" >&2 && exit 1
fi
# return '$value' back into $var_name (if exists) or echo in stdout
[ "$var_name" ] && eval "$var_name='$value'" || echo "$value"
}
# Парсит длинный аргумент
argl() {
[ "$1" ] || { echo "Argument name is not specified!" >&2 && exit 1; }
local arg_name="$1" # argument name to find
local is_flag="$2" || 0 # 1 if we need just find a flag, 0 to get a value
local var_name="$3" || 0 # variable name to return value into or 0 to echo it in stdout
local value= # initialize empty value to check if we found one later
local arg_found=0 # marker of found argument
for idx in "${!RAW_ARGS[@]}"; do # going through all args
local arg_search="${RAW_ARGS[idx]}" # get current argument
if [ "$arg_search" = "--$arg_name" ]; then # if current arg begins with two dashes
# then return '1' back into $value if we need flag or next arg value otherwise
[[ "$is_flag" == 1 ]] && value=1 || value="${RAW_ARGS[idx+1]}"
break # stop the loop
elif grep_match "$arg_search" "^--$arg_name=.+$"; then # check if $arg like '--foo=bar'
# then return '1' back into $value if we need flag or part from '=' to arg's end as value otherwise
[[ "$is_flag" == 1 ]] && value=1 || value="${arg_search#*=}"
break # stop the loop
fi
done
[[ "$is_flag" == 1 ]] && [[ -z "$value" ]] && value=0;
# if value we found is empty or looks like another argument then exit with error message
if [ "$arg_found" = 1 ] && ! grep_match "$value" "^[[:graph:]]+$" || grep_match "$value" "^--?\w+$"; then
echo "ERROR: Argument '--$arg_name' must have correct value!" >&2 && exit 1;
fi
# return '$value' back into $var_name (if exists) or echo in stdout
[ "$var_name" ] && eval "$var_name='$value'" || echo "$value"
}
########################################################
# Функции контроля системных пакетов
########################################################
# Проверяет наличие команды в системе
installed() {
command -v "$1" >/dev/null 2>&1
}
# Проверяет наличие пакета в системе
installed_pkg() {
dpkg --list | grep -qw "ii $1"
}
# Требует наличие пакета в системе
require() {
sw=()
for package in "$@"; do
# if ! installed "$package" && ! installed_pkg "$package"; then
if ! installed "$package"; then
sw+=("$package")
fi
done
if [ ${#sw[@]} -gt 0 ]; then
die "Это ПО должно быть установлено в системе:\n${sw[*]}" 200
fi
}
########################################################
# Функции для работы с git
########################################################
# Проверяет, является ли директория git-репозиторием
git.is_repo() {
require git
is_dir "$1/" && is_dir "$1/.git/"
}
# Клонирует репозиторий
git.clone() {
require git
cmd="git clone $*"
debug "Команда: $cmd"
$cmd
}
########################################################
# Функции для работы с docker
########################################################
# Вызывает корректную команду docker compose
docker.compose() {
require docker
args=${*/--profiles=[a-zA-Z_,0-9]*/}
if docker compose &>/dev/null; then
local cmd="docker compose $args"
elif installed_pkg "docker-compose"; then
local cmd="docker-compose $args"
warn
warn "docker-compose v1 устарел и не поддерживается, его поведение непредсказуемо."
warn "Обнови docker согласно документации: https://docs.docker.com/engine/install/"
warn
else
error "Должен быть установлен docker-compose-plugin!"
die "Установи docker согласно документации: https://docs.docker.com/engine/install/" 2
fi
debug "Команда: $cmd"
$cmd
}
# Выводит информацию о контейнере
docker.inspect() {
cmd="docker inspect $*"
debug "Команда: $cmd"
$cmd 2>/dev/null
}
# Строит образы согласно файлов iptv-docker/docker-compose*.yml
docker.build_base_images() {
# for file in compose*.yml; do
# subtitle "Построение базовых образов: $file"
# docker.compose -f "$file" build
# done
subtitle "Построение образов"
docker.compose build
success "Базовые образы построены"
}
# Выполняет команду в контейнере от имени root
docker.exec() {
cmd="docker exec -u root -it $*"
debug "Команда: $cmd"
$cmd
}
# Выполняет команду в контейнере от имени www-data
docker.exec_www() {
cmd="docker exec -u www-data -it $*"
debug "Команда: $cmd"
$cmd
}
########################################################
# Хелперы для обработки команд
########################################################
# Возвращает ssh-адрес к репозиторию проекта
project_url_ssh() {
echo "$IPTV_GITEA_URL_SSH/$1.git"
}
# Возвращает https-адрес к репозиторию проекта
project_url_https() {
echo "$IPTV_GITEA_URL_HTTPS/$1"
}
# Копирует .env.example в .env, если возможно
prepare_dot_env() {
if is_file .env.example && ! is_file .env; then
debug "Копирование .env.example => .env"
cp .env.example .env
fi
}
# Клонирует проект
project_clone() {
local repo_url="$1"; shift
local repo_path="$1"; shift
if git.is_repo "$repo_path"; then
debug "Репозиторий уже существует: $repo_path"
else
debug "Репозиторий будет склонирован: $repo_path"
git.clone "$repo_url" "$repo_path" "$@"
fi
}
# Проверяет передачу флагов -h|--help в команду и выводит справку
process_help_arg() {
command="${FUNCNAME[1]}"
need_help=$(arg help 1)
[[ "$need_help" -eq 0 ]] && need_help=$(argl help 1)
[[ "$need_help" -eq 1 ]] && help "$command"
}
# Выводит список сервисов
list_services_compose() {
services=$(docker.compose --profile full config --services | sort)
IFS=$'\n' sorted=("${services[@]}")
unset IFS
for name in "${sorted[@]}"; do
print " $name"
done
}
# Возвращает корректное название сервиса из композа
find_service_compose() {
svc="$1"
[ -z "$svc" ] && die "неизвестный сервис" 2
for known in $(docker.compose config --services); do
if [ "$known" = "$svc" ]; then
debug "Сервис '$known' найден в композе"
echo "$known"
exit
fi
done
debug "Сервис '$svc' не найден в композе"
return 1
}
# Возвращает корректные названия сервисов из композа
find_services_compose() {
local services=''
[ "$*" ] && for svc in "$@"; do
grep_match "$svc" "^--?.*" && continue
svc="$(find_service_compose "$svc")"
services="$services $svc"
done
trim "$services"
}
########################################################
# Главные функции обработки команд
########################################################
# Инициализирует репозиторий iptv-docker и все вложенные в него
init() {
process_help_arg
local docker_repo_path=$ROOT_PATH
local basename=$(basename "$docker_repo_path")
if [ ! "$basename" = "iptv-docker" ]; then
# это наиболее надёжный способ понять откуда запускается скрипт
docker_repo_path="$docker_repo_path/iptv-docker"
fi
#TODO установка ПО
subtitle "Подготовка локальной среды..."
project_clone "$IPTV_DOCKER_URL_SSH" "$docker_repo_path"
cd "$docker_repo_path"
prepare_dot_env
local counter=1
local repo_count=${#IPTV_PROJECTS[@]}
for repo_name in "${IPTV_PROJECTS[@]}"; do
local project_repo_path="$docker_repo_path/$repo_name"
subtitle "[$counter/$repo_count] Подготовка репозитория ${FBOLD}$repo_name${FRESET}..."
local project_repo_url=$(project_url_ssh "$repo_name")
debug "Известная ссылка на репозиторий: $project_repo_url"
project_clone "$project_repo_url" "$project_repo_path"
cd "$project_repo_path"
prepare_dot_env
cd - >/dev/null
success "Репозиторий $repo_name готов"
counter=$((counter+1))
done
success "Локальная среда развёрнута: $docker_repo_path"
cd "$docker_repo_path"
docker.build_base_images && up
success "Проверь корректность значений, указанных в файлах .env, и выполни ${FBOLD}./iptv rebuild${FRESET} при необходимости."
}
# Создаёт и запускает контейнеры
up() {
process_help_arg
subtitle "Создание и запуск контейнеров"
argl profiles 0 profiles
local services=''
[ "$*" ] && services="$(find_services_compose "$@")"
COMPOSE_PROFILES="$profiles" docker.compose up "$services" --build --detach --remove-orphans && \
success 'Среда запущена успешно'
}
# Запускает созданные контейнеры
start() {
process_help_arg
subtitle "Запуск созданных ранее контейнеров"
profiles="$(argl profiles 0)"
local services=''
[ "$*" ] && services="$(find_services_compose "$@")"
COMPOSE_PROFILES="$profiles" docker.compose start "$services" && \
success 'Среда запущена успешно'
}
# Останавливает и удаляет контейнеры
down() {
process_help_arg
subtitle "Остановка и удаление контейнеров"
argl profiles 0 profiles
[[ -z "$profiles" ]] && profiles="full"
local services=''
[ "$*" ] && services="$(find_services_compose "$@")"
COMPOSE_PROFILES="$profiles" docker.compose down "$services" --remove-orphans && \
success 'Среда остановлена успешно'
}
# Останавливает контейнеры
stop() {
process_help_arg
subtitle "Остановка контейнеров"
profiles=$(argl profiles 0)
[[ -z "$profiles" ]] && profiles="full"
local services=''
[ "$*" ] && services="$(find_services_compose "$@")"
COMPOSE_PROFILES="$profiles" docker.compose stop "$services" && \
success 'Среда остановлена успешно'
}
# Останавливает, удаляет, создаёт и запускает контейнеры
rebuild() {
process_help_arg
is_full=$(arg full 1)
[ "$is_full" = 0 ] && is_full=$(argl full 1)
[ -n "$*" ] && down "$@"
[ "$is_full" = 1 ] && docker.build_base_images
up "$@"
}
# Останавливает и запускает созданные контейнеры
restart() {
process_help_arg
stop "$@" && start "$@"
}
# Останавливает среду, удаляет все контейнеры и образы
purge() {
process_help_arg
subtitle "Удаление docker-образов и контейнеров"
down
docker rmi "$(docker image list | grep iptv- | awk '{print $3}')"
docker system prune --all --force
success 'Образы удалены успешно'
}
# Выводит логи сервиса
logs() {
process_help_arg
[ -z "$1" ] && die "не указан сервис" 8
svc=$(find_service_compose "$1")
[ -z "$svc" ] && die "неизвестный сервис" 3
subtitle "Логи сервиса $svc"
shift
cmd="docker logs $svc $*"
debug "Команда: $cmd"
$cmd
}
# Выводит статистику потребления ресурсов контейнерами
stats() {
subtitle "Состояние контейнеров"
docker.compose stats "$@"
}
########################################################
# Команды справки
########################################################
help() {
print "${FBOLD}Среда разработки iptv.axenov.dev"
is_function "help.$1" && help."$1" && exit
print "Использование:"
print " ./iptv КОМАНДА [АРГУМЕНТЫ]"
print
print "Доступные КОМАНДЫ:"
print " h|help - вывести это сообщение и выйти"
print " init - инициализировать окружение"
print " u|up - построить образы, контейнеры и запустить их"
print " start - запустить построенные ранее контейнеры"
print " d|down - остановить и удалить контейнеры"
print " stop - остановить контейнеры"
print " r|rebuild - выполнить 'down' и 'up'"
print " restart - выполнить 'stop' и 'start'"
print " purge - удалить контейнеры и образы"
print " update - обновить репозитории"
print " info - вывести версии ПО контейнера сервиса"
print " f|flame - сгенерировать флеймграф из трейса xdebug"
print " fix - исправить некоторые проблемы окружения"
print " exec - выполнить произвольную команду в контейнере"
print " logs - вывести логи контейнера"
print " profiles - вывести список профилей"
print " services - вывести список сервисов профиля"
print " info - вывести информацию о ПО в контейнере"
print " status - вывести статус сервиса и его репозитория"
print " stats - вывести информацию о потреблении ресурсов окружением"
print
print "Доступные АРГУМЕНТЫ:"
print " -h|--help - вывести справку по любой КОМАНДЕ"
print
print "Любая КОМАНДА может также иметь свои АРГУМЕНТЫ."
print "Некоторые КОМАНДЫ могут также поддерживать АРГУМЕНТЫ 'docker' и его субсомманд."
print
print "См. './iptv help КОМАНДА' или './iptv КОМАНДА -h|--help' для справки."
}
help.help() {
print "КОМАНДА: help"
print "Отображает справку по скрипту и его командам."
print
print "Использование:"
print " ./iptv help [КОМАНДА]"
print " ./iptv КОМАНДА [-h|--help]"
print
print "Если КОМАНДА не указана, будет отображена справка по скрипту со списком допустимых КОМАНД."
}
help.init() {
print "КОМАНДА: init"
print "Инициализирует окружение разработки. Клонирует репозиторий iptv-docker, клонирует"
print "внутрь исходники всех сервисов и создаёт заготовки файлов .env для них. Можно"
print "использовать для подгрузки свежих сервисов, ещё не развёрнутых локально."
print
print "Использование:"
print " ./iptv init [АРГУМЕНТЫ]"
print
print "Доступные АРГУМЕНТЫ:"
print " -n|--name ИМЯ - установить ИМЯ в качестве имени пользователя git"
print " -e|--email ПОЧТА - установить ПОЧТУ в качестве email пользователя git"
print " -h|--help - вывести это сообщение и выйти"
print
print "Если --name и/или --email не переданы, то данные будут взяты из глобального конфига git."
print
print "ИМЯ и ПОЧТА проверяются на валидность:"
print " - ИМЯ должно содержать имя и фамилию на кириллице, разделённые пробелом;"
print " - ПОЧТА должна быть корпоративной (@bars.group или @bars-open.ru)."
print
print "Если ИМЯ или ПОЧТА некорректны, КОМАНДА завершится с ошибкой и предложением передать"
print "явно ИМЯ и ПОЧТУ с помощью аргументов."
print
print "После выполнения КОМАНДЫ необходимо указать корректные данные в файлах .env сервисов."
}
help.up() {
print "КОМАНДА: up"
print "Выполняет перестроение образов (включая базовые, при необходимости)"
print "создание и запуск контейнеров."
print
print "Использование:"
print " ./iptv up [АРГУМЕНТЫ] [СЕРВИСЫ...]"
print
print "Доступные АРГУМЕНТЫ:"
print " -h|--help - вывести это сообщение и выйти"
print
print "Если указан один и более СЕРВИСОВ, то КОМАНДА будет применена только к ним."
print
print "СЕРВИСЫ можно передавать без префикса 'iptv-'"
}
help.start() {
print "КОМАНДА: start"
print "Выполняет запуск построенных ранее контейнеров."
print
print "Использование:"
print " ./iptv start [АРГУМЕНТЫ] [СЕРВИСЫ...]"
print
print "Доступные АРГУМЕНТЫ:"
print " -h|--help - вывести это сообщение и выйти"
print
print "Если указан один и более СЕРВИСОВ, то КОМАНДА будет применена только к ним."
print
print "СЕРВИСЫ можно передавать без префикса 'iptv-'"
}
help.down() {
print "КОМАНДА: down"
print "Выполняет остановку и удаление запущенных контейнеров."
print
print "Использование:"
print " ./iptv down [АРГУМЕНТЫ] [СЕРВИСЫ...]"
print
print "Доступные АРГУМЕНТЫ:"
print " -h|--help - вывести это сообщение и выйти"
print
print "Если указан один и более СЕРВИСОВ, то КОМАНДА будет применена только к ним."
print
print "СЕРВИСЫ можно передавать без префикса 'iptv-'"
}
help.stop() {
print "КОМАНДА: stop"
print "Выполняет остановку запущенных контейнеров."
print
print "Использование:"
print " ./iptv stop [АРГУМЕНТЫ] [СЕРВИСЫ...]"
print
print "Доступные АРГУМЕНТЫ:"
print " -h|--help - вывести это сообщение и выйти"
print
print "Если указан один и более СЕРВИСОВ, то КОМАНДА будет применена только к ним."
print
print "СЕРВИСЫ можно передавать без префикса 'iptv-'"
}
help.rebuild() {
print "КОМАНДА: rebuild"
print "Выполняет 'down' и 'up'."
print
print "Использование:"
print " ./iptv rebuild [АРГУМЕНТЫ] [СЕРВИСЫ...]"
print
print "Доступные АРГУМЕНТЫ:"
print " -h|--help - вывести это сообщение и выйти"
print
print "Если указан один и более СЕРВИСОВ, то КОМАНДА будет применена только к ним."
print
print "СЕРВИСЫ можно передавать без префикса 'iptv-'"
}
help.restart() {
print "КОМАНДА: restart"
print "Выполняет 'stop' и 'start'."
print
print "Использование:"
print " ./iptv restart [АРГУМЕНТЫ] [СЕРВИСЫ...]"
print
print "Доступные АРГУМЕНТЫ:"
print " -h|--help - вывести это сообщение и выйти"
print
print "Если указан один и более СЕРВИСОВ, то КОМАНДА будет применена только к ним."
print
print "СЕРВИСЫ можно передавать без префикса 'iptv-'"
}
help.purge() {
print "КОМАНДА: purge"
print "Удаляет все образы и контейнеры 'iptv-*'."
print
print "Использование:"
print " ./iptv purge [АРГУМЕНТЫ]"
print
print "Доступные АРГУМЕНТЫ:"
print " -h|--help - вывести это сообщение и выйти"
}
help.logs() {
print "КОМАНДА: logs"
print "Выводит логи указанного СЕРВИСА."
print
print "Использование:"
print " ./iptv logs СЕРВИС [АРГУМЕНТЫ]"
print
print "Доступные АРГУМЕНТЫ:"
print " -h|--help - вывести это сообщение и выйти"
print
print "Поддерживаются АРГУМЕНТЫ 'docker logs'."
print
print "СЕРВИС можно передавать без префикса 'iptv-'"
}
help.stats() {
print "КОМАНДА: stats"
print "Выводит потребление ресурсов окружением разработки."
print
print "Использование:"
print " ./iptv stats [АРГУМЕНТЫ]"
print
print "Доступные АРГУМЕНТЫ:"
print " -h|--help - вывести это сообщение и выйти"
print
print "Поддерживаются АРГУМЕНТЫ 'docker compose stats'."
print "Рекомендуется использовать вместе с '--no-stream'."
}
########################################################
# Точка входа
########################################################
debug "Директория выполнения: $ROOT_PATH/$IPTV_DIR"
case "$COMMAND" in
h|help ) help "$1" ;;
init ) init "$@" ;;
u|up ) up "$@" ;;
start ) start "$@" ;;
d|down ) down "$@" ;;
stop ) stop "$@" ;;
r|rebuild ) rebuild "$@" ;;
restart ) restart "$@" ;;
purge ) purge ;;
logs ) logs "$@" ;;
stats ) stats "$@" ;;
* ) die "неизвестная команда. Смотри './iptv help' для справки." ;;
esac