Cli

github

Ранее я всегда рекомендовал вместо spf13/cobra использовать urfave/cli а конкретно urfave/cli/v2. Пришло время переобуться.

В чем проблема с spf13/cobra: ужасный апи, основанный на мутировании переменных. Пошло это видимо из flag в котором обьявляешь глобальную переменную, а потом биндишь к ней флаг. Разумеется это никак не композируется, поэтому надо бы как то разделять команды и флаги и распихивать их по пакетам, но в кобре это выглядит как куча функций настройки команд и флагов с использованием init только чтобы добавить себя в родительскую команду. В общем нифига не композируемо и не удобно и используются странные практики. Им даже пришлось сделать генератор проекта с либой, а то вообще не понятно, как этим пользоваться.

Почему не urfave/cli: нужно повторять имена флагов которые являются просто строковыми литералами и никто их кроме тебя не проверит. То есть тебе надо обьявить флаг cli.StringFlag с Name: "name" и потом использовать как ctx.String("name"). И НИКТО не поругается если ты сделаешь ctx.Int("name"), ctx.String("mane") или даже ctx.Uint64("aboba") когда этот флаг даже не обьявлен. В частности это ведет к той же проблеме переиспользуемости. Композировать команды можно просто великолепно: родительская команда декларирует список подкомманд, которые она включает и все. С флагами все печальнее, т.к. их надо обьявлять в двух местах: списке флагов и функции Action. И если структуру флага можно переиспользовать, получение флага в Action придется отслеживать руками. Ну либо опять биндить на глобальные переменные и в помойку летит изолированность комманд друг от друга. Другой момент про изолированность: команда декларирует свое имя. Почему это плохо: пусть CmdRoot хочет использовать CmdGitInit и CmdK8sInit, но проблема в том, что обе подкоманды "init" и не получится использовать обе. Это как если бы вы хотели использовать две библиотеки с одинаковым именем, но никто вам не дает возможности их заалиасить и поменять имя. Еще одна проблема с urfave/cli в автокомплишне. Его нет. В доке что-то написано про это, но я хуй знает кем надо быть, чтобы в этом разобраться. Плюс кастомные комплишны как я понимаю придется делать через парсинг команд заново. Например если я хочу сделать флаг --branch и в комплишне к нему показывать все ветки, мне придется в подкоманде искать этот флаг, искать значение после него, и только потом искать и показывать ветки. Но схуяли я это должен делать, если я пользуюсь cli либой именно для того чтобы она парсила аргументы за меня. Замечу, что в spf13/cobra с комплишнами все гораздо лучше и можно спокойно их обьявлять на отдельные аргументы.

Что же тогда использовать? Самый подходящий вариант из которых я видел на данный момент: jessevdk/go-flags но у него есть свои минусы(ну сколько можно блять). Перечислю их сразу: - принтит сообщения ошибок в консоль, не спрашивая разрешения, если вы будете хендлить ошибки через if err := flags.Run(); err != nil { log.Fatal(err) } ошибка будет напечатана два раза. - лишний функционал с неймспейсами, енвами и ini конфигурацией. Хз зачем это, конфиги я могу и сам распарсить, и не только ini, польза мапать в них флаги сомнительна. - help некастомизируемый от слова никак, придется жить с тем текстом который зашит в либе - неочевидно, как начать пользоваться либой, куча паблик символов из которых 90% либо не нужны либо повторяют функционал - entrypoint функция и методы Execute используют остатки позициональных аргументов, хотя их можно аналогично флагам получить через поля структуры - из дочерней команды нельзя получить флаги родительской команды (мб и можно, хз, судя по дизайну нельзя), кмк не минус вообще, т.к. меньше проблем у клиента и разраба либы разбираться куда какой флаг надо запихивать и где он используется/не используется Теперь к плюсам и почему я советую все таки брать ее вместо spf13/cobra или urfave/cli (пока не написал свой форк с фиксами проблем выше): - конфигурация команд определяется декларативно, для этого используются структуры с field тэгами и методы этих структур - имя подкоманды определяет родительская команда тегом command:"name" - структуры флагов спокойно переиспользуются, достаточно их заэмбедить, если нужно точно повторение, либо сделать поле с типом флага и проставить другие теги - структура команды получается строго типизированной, невозможно использовать неопределенные флаги либо флаг одного типа под другим типом - можно определять кастомные флаги через UnmarshalFlag и сразу же там валидировать (в либе есть отдельный метод для этого, хз зачем) и получать любое значение: хендлер на файл, коннект к бд, etc. - можно определять кастомные комплишны на флаги. Комплишн подкоманд и названий флагов работает из коробки, для своих флагов достаточно определить метод Complete(prefix string) []flags.Completion и использовать простейший баш скрипт из репы, чтобы они заработали

Hello World

Постом чуть выше я нарисовал как эта либа парсит аргументы и как это мапается на структуры команд. Хотя это больше к документации моего форка, который хз когда будет готов, приходится переписывать либу с нуля, ибо в коде оригинальной либы я не разобрался.