Русский
Русский
English
Статистика
Реклама

Shell

Перевод В чем именно был смысл xvar xval ?

16.04.2021 12:14:51 | Автор: admin


Краткая история жизни и смерти багов консольных скриптов, для борьбы с которыми привлекался загадочный и не имеющий собственного значения x. Что это за символ, от каких проблем он спасал и актуально ли его применение сегодня?

При написании консольных скриптов мы иногда сталкиваемся со сравнениями, в которых каждое значение имеет префикс x. Вот примеры с GitHub:

if [ "x${JAVA}" = "x" ]; thenif [ "x${server_ip}" = "xlocalhost" ]; thenif test x$1 = 'x--help' ; then

Назову этот прием x-hack.

Для любой POSIX-совместимой оболочки значение x-hack будет равно нулю, то есть сравнение в 100% случаев сработает и без x. В чем же тогда его суть?

Ресурсы вроде StackOverflow Q&A размыто поясняют, что это альтернатива цитированию вне контекста, указывающему на проблемы с некоторыми версиями конкретных оболочек или в целом предостерегающему о загадочном поведении, в особенности древних UNIX-систем. Примерами же эти пояснения не подкрепляются.

Чтобы определить, должна ли ShellCheck об этом предупреждать, и если да, то на каком логическом обосновании, я решил обратиться к истории Unix, а именно к архивам Unix Heritage Society. К сожалению, мне не удалось заглянуть в тщательно охраняемый мир подобий HP-UX и AIX, так что пастухам динозавров рекомендую сохранять бдительность.

Вот найденные мной кейсы, которые могут провалиться.

Левая сторона представлена унарным оператором


Оболочка AT&T Unix v6 от 1973 года, по крайней мере согласно данным из PWB/UNIX от 1977 года, проваливала выполнение тестовых команд, где левая сторона была представлена унарным оператором. Это мог заметить любой, кто пытался выполнить проверку параметров командной строки:

% arg="-f"% test "$arg" = "-f"syntax error: -f% test "x$arg" = "x-f"(true)

Ошибка была исправлена в оболочке Борна ОС Unix v7, выпущенной в 1979 году. Тем не менее test и [ были также доступны как отдельные исполняемые файлы, и сохранили вариант ошибочного поведения:

$ arg="-f"$ [ "$arg" = "-f" ](false)$ [ "x$arg" = "x-f" ](true)

Это произошло, потому что в утилите использовался простой метод рекурсивного спуска, который давал унарным операторам преимущество над бинарными и игнорировал завершающие аргументы.

Современное поведение оболочки Борна в 1988 было скопировано общедоступной KornShell и стало частью POSIX.2 в 1992 году. В GNU Bash 1.14 то же самое было сделано для встроенной инструкции [, при этом пакет GNU shellutils, предоставлявший внешние исполняемые файлы test/[, последовал уже за POSIX. В результате ранние дистрибутивы GNU/Linux вроде SLS этим багом затронуты не были также, как и FreeBSD 1.0.

X-hack в данном случае эффективен по той причине, что ни один унарный оператор не может начинаться с x.

Одна из сторон представлена оператором длины строки -1


Похожая проблема, просуществовавшая дольше предыдущей, была связана с оператором длины строки -1. В отличие от стандартных унарных предикатов этот считывался только как часть операнда для бинарных предикатов:

var="helloworld"[ -l "$var" -gt 8 ] && echo "String is longer than 8 chars"

Согласно приведенному выше обоснованию, он не перешел в POSIX, так как: не был задокументирован в большинстве реализаций, был удален из некоторых реализаций (включая System V), и эта функциональность предоставляется оболочкой. В пример приводится [ ${#var} -gt 8 ].

Это не было проблемой в UNIX v7, где приоритет отдавался =, но в Bash 1.14 от 1996 года данный оператор считывался наперед:

$ var="-l"$ [ "$var" = "-l" ]test: -l: binary operator expected$ [ "x$var" = "x-l" ](true)

Та же проблема касалась и правой стороны, но только во вложенных выражениях. Проверка на -1 гарантировала наличие второго аргумента, следовательно, требовалось дополнительное выражение или скобки для его активации:

$ [ "$1" = "-l" -o 1 -eq 1 ][: too many arguments$ [ "x$1" = "x-l" -o 1 -eq 1 ](true)

Позже в том же году этот оператор был удален из Bash 2.0, и проблема ушла вместе с ним.

Левая сторона представлена "!"


Еще одно затруднение в ранних оболочках возникало, когда левая сторона сравнения была представлена оператором отрицания !:

$ var="!"$ [ "$var" = "!" ]test: argument expected            (UNIX v7, 1979)test: =: unary operator expected   (bash 1.14, 1996)(false)                            (pd-ksh88, 1988)$ [ "x$var" = "x!" ](true)

Опять же, x-hack решал проблему, не позволяя распознать ! как оператор отрицания.

Ksh рассматривала его как [ ! "=" ]и игнорировала остальные аргументы. В итоге просто возвращался false, так как = не является нулевой строкой. При этом в ksh завершающие аргументы игнорируются и по сей день:

$ [ -e / random words/ops here ](true)                              (ksh93, 2021)bash: [: too many arguments         (bash5, 2021)

В Bash 2.0 и ksh93 эта проблема в соответствии с POSIX была решена за счет предоставления оператору = приоритета в случае с тремя аргументами.

Левая сторона представлена "("


Это, безусловно, моя любимая.

Встроенная в UNIX v7 оболочка давала сбой, когда левая сторона была представлена левой скобкой:

$ left="(" right="("$ [ "$left" = "$right" ]test: argument expected$ [ "x$left" = "x$right" ](true)

Это происходило из-за того, что ( получал приоритет над = и становился недопустимой группой скобок.

Но почему моя любимая? Вот Dash 0.5.4 вплоть до 2009:

$ left="(" right="("$ [ "$left" = "$right" ][: 1: closing paren expected$ [ "x$left" = "x$right" ](true)

На момент публикации темы в StakOverflow Q&A данный баг продолжал существовать.

Но это еще не все!

Вот Zsh конца 2015 года перед самым выходом версии 5.3:

% left="(" right=")"% [ "$left" = "$right" ](true)% [ "x$left" = "x$right" ](false)

Удивительно, что x-hack продолжали использовать для обхода ряда багов аж до 2015 года, семь лет после того, как на StackOverflow этот прием списали как архаичный пережиток прошлого.

Конечно же, встретить эти баги становится все труднее. Ошибка в Zsh, к примеру, срабатывает только при сравнении левой и правой скобок, так как в остальных случаях парсер понимает в чем дело.

В запоздавших можно также записать Solaris, чья /bin/sh оставалась устаревшей оболочкой Борна даже в Solaris 10 2009 года. Однако причиной такой задержки определенно стала совместимость, а не оценка разработчиками этой оболочки как оптимальной. Совместимая со стандартами оболочка оставалась опцией достаточно долго, пока Solaris 11 не перетащил ее в 21 век или как минимум в 90-е переключившись на ksh93 по умолчанию в 2011 году.

X-hack выручает во всех подобных случаях, не давая распознать операнды как скобки.

Заключение


Прием с добавлением x действительно был полезен и успешно исправлял ряд реальных практических проблем в нескольких оболочках.

Тем не менее его ценность уже к концу 90-х практически полностью сошла на нет, и лишь несколько остававшихся проблем были подчищены только к 2010 году поразительно поздно, но все же больше десяти лет назад.

Последний баг умудрился дожить до 2015 года, но только в одной оболочке и только в очень специфичном случае сравнения открывающей скобки с закрывающей.

Думаю, что настало время отказаться от этой идиомы, и даже ShellCheck теперь по умолчанию предлагает соответствующие рекомендации по стилю.

Эпилог


Проблема с [ "(" = ")" ] в Dash была впервые отмечена в 2008 году и проявлялась как в Bash 3.2.48, так и в Dash 0.5.4. В bash на macOS ее можно встретить до сих пор:

$ str="-e"$ [ \( ! "$str" \) ][: 1: closing paren expected     # dashbash: [: `)' expected, found ]   # bash

POSIX исправляет все эти неоднозначности в командах, содержащих вплоть до 4-х параметров, гарантируя, что условные конструкции оболочки будут работать одинаково везде и всегда.

Мейнтейнер Dash, Герберт Сюй, по этому поводу оставил в исправлении такой комментарий:

/* * Регламент POSIX: написавший это заслуживает Нобелевской премии мира* */
Подробнее..

Консольный менеджер сертификатов для JKSPCKS12

05.02.2021 14:16:53 | Автор: admin

Привет Хабр!

С момента популяризации https, работа с сертификатами теперь есть практически в каждом проекте.

habr-certificatehabr-certificate

Сертификаты, которые любой может сгенерировать сам;
Сертификаты которые выдает местный СА в компании, доверие к которому есть только внутри компании;
Бесплатный публичный let's encrypt
Платный сертификат от крупных центров авторизации с дополнительными услугами, типа проверки компании.

Сертификаты для tls/ssl, сертификаты для авторизации, сертификаты для two-say-tls/ssl - даже в пределах одного небольшого проекта с десятком микросервисов и несколькими тестовыми окружениями - сертификатов набирается уйма.

При работе с множеством сертификатов, их удобно хранить не отдельными файлами, а складывать в хранилища (keystore). Самые популярные keystore форматы на сегодня - JKS (java keystore, который считается legacy с выходом 9-й джавы) и PKCS12. Последний вышел уже около 20 лет назад, и несмотря на некоторую критику, является одним из самых надежных, открытых и популярных форматов, который в скором будущем должен уже полностью вытеснить JKS.

Я много работал в проектах, где основным языком разработки был java, поэтому при работе с keystore основной инструмент для меня это консольная утилита keytool, которая поставляется с JDK. И для большинства популярных действий у меня конечно есть микро-шпаргалка:

Посмотреть список сертификатов:

keytool -list -v -keystore "файл.jks" -storepass "пароль"|grep -P '(Alias name:|Entry type:|Serial number:|Owner:|Valid from:)'

Скопировать сертификат из одного keystore в другой keystore:

keytool -importkeystore -srckeystore "откуда.jks" -srcstorepass "пароль" -destkeystore "куда.jks" -deststorepass "пароль" -srcalias "имя сертификата" [ -destalias "новое имя" ]

Импортировать сертификат в keystore из PEM файла:

keytool -import -file "откуда.pem" -keystore "куда.jks" -storepass "пароль" -noprompt -alias "имя_сертификата"

Экспортировать сертификат из keystore в файл:

keytool -exportcert -v -alias "alias" -keystore "keystore.jks" -storepass "storepassword" -rfc -file "exportedcert.cer"

Но однажды мне пришлось поработать в проекте, где сертификатов оказалось мягко говоря много. Плюс несколько сотен виртуалок и контейнеров, где все сертификаты обновлялись вручную. Перед тем? как подойти к вопросу автоматизации, было необходимо все это разобрать и причесать. Но долгое время руки просто не доходили - с одной стороны были вещи более приоритетные а сертификаты в общем напрягали нечасто. С другой стороны, опыта разгребания такого объема у меня раньше не было, и навскидку казалось, что в лоб задача решается слишком уныло и кропотливо, браться за нее по своей инициативе не хотелось - задача чисто техническая, никакие менеджеры и представители бизнеса такое перед нашей командой не ставили. Но где-то в подсознании желание все это перебрать и привести в порядок крутилось. Изредка я почитывал статьи, просматривал сертификат эксплореры, но все они были под графическую оболочку Windows, а политика безопасности не позволяла ставить в компании неодобренный софт на десктопы.

Возможно, я бы собрался силой воли, помучился бы недельку, сделал бы все стандартными однострочниками+notepad+excel, но тут внезапно у меня случился больничный отпуск, а когда температура меня попустила, а на работу еще можно было не выходить, я случайно вспомнил, что считаю себя спецом по bash.

И пусть я не хватаю лавры автора PIUPIU), но тем не менее встречайте:

Консольный двух-панельный keystore менеджер на Linux shell.

Сперва я написал рабочий вариант на bash, но в какой-то момент подумал и переписал все башизмы в пользу POSIX совместимости. Код стал выглядеть более громоздко, зато скрипт проверенно работает в bash/ksh/dash без проблем.
Если я что-то пропустил - напишите в комментариях, ну или форкайте на здоровье ;)

Также используется sed и grep, по поводу последнего - я не очень уверен, насколько POSIX-совместимо grep -P. Но если что, там несложно переписать на sed.

Некоторое сожаление: привычка работы с java вынудила меня всю работу с хранилищами выполнять именно через keystore, который должен быть доступен в PATH (а может быть стоило разобраться и сделать все через openssl?).

Но давайте к делу. Что умеет менеджер вкратце можно наглядно увидеть на скриншотах.

В одно-панельном режиме:

В двух-панельном режиме:

Для поклонников панельных менеджеров (NC, VC, MC, FAR и др), функциональные клавиши и навигация должны быть интуитивно понятны.

Весьма важным плюсом я считаю, что jks_mgr.sh - это просто шелл скрипт, одним файлом размером 20 килобайт (update: уже 30+ кб) (update2: уже 35+ кб).

Никаких обсфукаций, код максимально простой - проверить на отсутствие закладок может в принципе любой джуниор - то есть такой скрипт можно использовать в любом проекте, не нарушая никаких требований по безопасности, требований по лицензионности, лоялен к производительности, установка не нужна - лишь бы на целевой машине был доступен шелл, keytool, sed и grep.

Что было самое интересное во время разработки:

Автоматическая подстройка панелей под высоту/ширину экрана.

Так как экран у меня перерисовывается почти с каждым нажатием, то менять ширину экрана (например если сидеть через графический ssh клиент) можно в произвольное время - после первого же нажатия любой клавиши экран перерисуется и все адаптируется. Пару вечеров ушло на опции отображения/скрытия столбцов - нужно было высчитать и отладить подстройку для одно-панельного и двух-панельного режима, плюс заголовки и текст считается разными формулами. Вот кусочек для определения насколько надо обрезать Certificate Alias, если экран слишком узкий и затем пример вывода заголовка:

WindowWidth="$(tput cols)"if [ -n "$RFILE" ]; then # two-panel    used=24    [ -n "$SHOW_TYPE" ] && used=$(( $used+34 ))    localWidth=$(( ( $WindowWidth - $used ) / 2 - 1 ))    if [ $localWidth -ne $aliasWidth ]; then        aliasWidth=$localWidth        [ $aliasWidth -lt 1 ] && aliasWidth=1        clear    fi..................................headerWidth=$(( $aliasWidth + 5 ))[ -n "$SHOW_TYPE" ] && headerWidth=$(( $headerWidth + 17 ))printf " store: ${blue}%-$(( $headerWidth ))s${rst}" "$LFILE"printf "| store: ${blue}%-$(( $headerWidth -1 ))s${rst}\n" "$RFILE"printf " %-10s" "Valid to"[ -n "$SHOW_TYPE" ] && printf " %-16s" "Storetype"printf " %-${aliasWidth}s |" "Alias"printf " %-10s" "Valid to"[ -n "$SHOW_TYPE" ] && printf " %-16s" "Storetype"printf " %-${aliasWidth}s\n" "Alias"

Определение нажатия функциональных клавиш.

Некоторые специальные keypress могут иметь длину до 4 символов, поэтому просто через read было сделать непросто - он не позволяет вариативно менять длину читаемой строки, но после поиска в гугле и экспериментов c read в bash/dash, это выглядит так:

# Special keypress could take up to 4 charactersread -rsN1 keypressif [ "$keypress" == "$escape_char" ]; then    read -sn1 -t 0.01 k1    read -sn1 -t 0.01 k2    read -sn1 -t 0.01 k3    read -sn1 -t 0.01 k4    keypress=${k1}${k2}${k3}${k4}    unset k1 k2 k3 k4fi

Теперь можно сравнивать $keypress с комбинациями типа '[A' (стрелка вверх), '[B' (стрелка вниз), '[13~' (F3) и так далее. Я не совсем уверен, что мой вариант будет работать везде идеально - поэтому на всякий случай почти все хоткеи продублированы обычными буквами.

В упрощенном виде я подобную навигацию уже копирую в другие свои велосипеды - в некоторых случаях это гораздо удобнее, чем стандартный select благодаря наглядности и возможности делать хоткеи.

На текущий момент все самые часто используемые мной функции уже воплощены, и я взял передышку, да и собственно сам скрипт я начал писать чтобы получить еще немного практики с tput и навигацией, но немного увлекся.

Я догадываюсь, что можно заметно ускорить навигацию - производительность в git-bash ужасна - мне быстрее скопировать keystore на ближайшую виртуалку, поковырять там менеджером и скопировать исправленный keystore назад. Но нужно будет переписывать много вычислений, делать независимую пагинацию для каждой панели, а на Linux все работает отлично, поэтому в текущем варианте скрипт меня устраивает и дойдут ли руки до переделки - не уверен.

В конце нужно написать какой-то умный итог и заключение ну он простой:

Хочешь получить от практики удовольствие - придумай задачу, результат которой будет полезен для тебя.

Хочешь получить от практики двойное удовольствие - придумай задачу, результат которой будет полезен и для тебя и для кого-нибудь еще.

Сейчас моим скриптом пользуется небольшой круг людей в компании, и то, что я, не разработчик, смог написать нечто полезное и востребованное, меня очень радует.

P.S. На фоне некоторых проблем с nginx, Wargaming и др., сразу хочу уточнить, что jks_mgr.sh был написан ИСКЛЮЧИТЕЛЬНО в нерабочее время на личном компьютере.

Подробнее..

Перевод Оптимизация рабочего процесса при помощи fzf

05.04.2021 12:20:40 | Автор: admin

Работа в оболочке включает в себя выполнение одних и тех же команд снова и снова; что меняется, так это порядок выполнения команд и их параметры. Один из способов упростить рабочий процесс найти паттерны выполнения команд с аргументами и обернуть их в небольшие скрипты. Такой подход часто оказывается полезным, сильно упрощая работу; другой способ оптимизации рабочего процесса понять, как добавляются параметры, и попробовать упростить сам ввод. В этом посте я расскажу о втором подходе.


Обычные параметры моего рабочего процесса имена файлов и ветвей git: если посмотреть на мою историю команд, окажется, что git я ввожу чаще всего; ручной ввод команд git сопряжен с трудностями и часто приводит к ошибкам, поэтому я не ввожу команды руками везде, где это возможно. В зависимости от команды может подойти автозамена по табуляции, и она может оказаться очень полезной, но удобна она не всегда. В этом посте я покажу, как в качестве альтернативы использовать fzf.

Базовая функциональность fzf очень проста: он читает строки из стандартного потока ввода и даёт пользователю интерфейс, чтобы можно было выбрать одну или несколько строк и вписать их в стандартный поток вывода. Вики-страницы этого инструмента содержат массу примеров эффективного применения fzf. Это прекрасный ресурс, я взял несколько функций оттуда в свой репертуар и пользуюсь этими функциями почти каждый день, но в своей работе вы можете обнаружить очень специфические процессы, которых нет в вики, тогда как автоматизировать их было бы полезно.

Чтобы показать, как я подхожу к процессам подобного рода, я расскажу о 4 задачах, с которыми обычно сталкиваюсь. Затем напишу функцию оболочки с fzf, которая сделает работу удобнее. Кроме того, я расположу эти функции в порядке возрастания сложности:

В большинстве случаев я покажу простую функцию, которой достаточно почти для всех юзкейсов, а затем допишу её так, чтобы она стала удобнее или надёжнее.

Последние версии функций, включая варианты для fish, вы найдёте на Github.

Активация виртуальных сред python

Переменные моих виртуальных сред python содержится в файле ~/.venv. Вот, что я обычно делаю, чтобы активировать одну из сред:

  • начинаю ввод source ~/.venv/;

  • чтобы запустить автозавершение, нажимаю <tab>;

  • выбираю среду по желанию;

  • добавляю bin/activate и нажимаю <enter>.

Процесс можно улучшить чем-то вроде virtualenvwrapper, но есть и хороший пример с fzf: это простейшее решение, которое может занять всего одну строку.

function activate-venv() {  source "$HOME/.venv/$(ls ~/.venv/ | fzf)/bin/activate"}

activate-venv-simple.bash(download)

Активировать эту функцию можно с помощью команды:

source activate-venv-simple.bash

(добавьте этот код в свой .bashrc, чтобы он выполнялся постоянно), а затем используйте его, как показано ниже.

В окне выбора fzf показывает несколько виртуальных сред; среда активируется, когда строка выбрана.

Меньшая проблема то, что, если выйти из fzf нажатием ctrl-d, скрипт упадет с такой ошибкой:

bash: /home/crepels/.venv//bin/activate: No such file or directory

Её можно проигнорировать, так как вы получите желаемый эффект никаких средств активирован не будет; но решить эту проблему можно ещё проще, а именно сохранить вывод в переменную и попробовать активировать виртуальную среду, только если переменная не пуста.

function activate-venv() {  local selected_env  selected_env=$(ls ~/.venv/ | fzf)  if [ -n "$selected_env" ]; then    source "$HOME/.venv/$selected_env/bin/activate"  fi}

Удаление веток git

Другим обнаруженным паттернам самоповторения оказалось удаление веток git. Как только фича смержена в основную ветку или эксперимент больше не нужен, ветка может быть удалена, но обычно я не удаляю её сразу: вместо этого ветки накапливаются, пока их количество не достигает критической точки, в которой навигация по веткам затрудняется. Когда дело в самом деле доходит до удаления веток, картина выглядит так:

  • я начинаю ввод git branch -D;

  • нажимаю табуляцию, чтобы вызвать автозавершение;

  • выбираю ветку, которую, как мне кажется, можно удалить.

Иногда я не уверен в том, какую ветку действительно можно удалить. Сначала мне нужно запустить команду git log в этой ветке, чтобы увидеть, что она содержит, и затем удалить её, как показано выше. Затем процесс повторяется до тех пор, пока я не удалю все залежавшиеся ветки.

Этот рабочий процесс хороший пример того, как сильно fzf упрощает работу. В этот раз мы воспользуемся опцией fzf --multi, которая позволяет по нажатию табуляции выбрать несколько записей.

function delete-branches() {  local branches_to_delete  branches_to_delete=$(git branch | fzf --multi)  if [ -n "$branches_to_delete" ]; then     git branch --delete --force $branches_to_delete  fi}

После выполнения source delete-branches-simple.bash мы можем использовать этот код следующим образом.

 Удаление веток при помощи fzf Удаление веток при помощи fzf

Код в основном работает, но реализовать эту функциональность можно по-разному. Первый вариант git branch показывает все ветки, включая ту, в которой мы находимся, она отмечена звёздочкой (*). Поскольку нельзя удалить ветку, в которой мы находимся, то и показывать её смысла нет, так что мы можем опустить эту ветку, предоставив вывод git branch команде grep --invert-match

Ещё один способ: мы можем пропустить переменным $branches_to_delete без кавычек в git branch -D. Сделать это нужно потому, что git каждая ветка нужна как отдельный аргумент. Если вы пользуетесь линтером вроде shallcheck, эта строка ему не понравится, поскольку переменные без кавычек могут вызвать глоббинг и разделение слов. В нашем случае срабатывание будет ложным: ветка не может содержать символов глоббинга; тем не менее я думаю, что избегать переменных без кавычек, где это возможно, хорошая практика, и один из способов сделать это пропустить вывод fzf через xargs прямо в git branch -D, а не хранить этот вывод в переменной. Если в xargs добавить опцию --no-run-if-empty, git будет вызываться только в том случае, если была выбрана хотя бы одна ветка.

Наконец, я упоминал, что, чтобы увидеть выбранную ветку, полезно посмотреть на вывод git log. Сделать это можно при помощи опции --preview: значением этой опции может быть какая-нибудь команда, которая будет выполняться всякий раз, когда в fzf будет выбрана новая строка, и вывод будет показан в окне предварительного просмотра. Фигурные скобки в этой команде работают как плейсхолдер, то есть заменяются на текущую выбранную строку.

function delete-branches() {  git branch |    grep --invert-match '\*' |    cut -c 3- |    fzf --multi --preview="git log {} --" |    xargs --no-run-if-empty git branch --delete --force}

Также обратите внимание на то, что вывод git branch пропускается через cut -с -3, которая из каждой строки удаляет 2 пробела. Если посмотреть на вывод git branch, видно, что каждая ветка, за исключением текущей, имеет префикс в 2 пробела. Если их не удалить, команда в --preview будет такой: git log ' branch-name', что приведёт к жалобам git на лишние начальные пробелы. В качестве альтернативы используйте команду git log {..}, которая тоже удалит пробелы из выбранной строки.

Вот пример: мы удаляем те же три ветки, что и выше, но при этом получаем больше информации.

Поток fzf для удаления ветвей в окне предварительного просмотра. Показаны ветки и вывод git log. Ударение ветвей с помощью fzf улучшенная версия.

Локально заходим в пул-реквест

Когда делается код-ревью, полезно бывает переключиться в ветку кода, который вы просматриваете . Интерфейс командной строки от гитхаба упрощает эту задачу: можно просто выполнить в репозитории команду пр pr-checkout. Так вы окажетесь в ветке соответствующего пул-реквеста и уже локально. Но как узнать номер пул-реквеста? Вот что я обычно делал:

  • открывал пул-реквест в браузере;

  • читал номер в URL;

  • переключался на окно терминала и вводил gh pr checkout, а затем номер.

Этот подход работает, когда мы имеем дело с пул-реквестом в 1 или 2 цифры, но, даже когда цифры всего 3, иногда я переключаюсь на браузер, чтобы убедиться, что запомнил номер правильно.

В моём прошлом посте я уже рассказывал, как при помощи gh автоматически опрашивал api Github, чтобы узнать номер пул-реквеста. Вы можете воспользоваться запросом к api, который я показываю ниже:

gh api 'repos/:owner/:repo/pulls'

Этот запрос возвращает массив JSON-объектов по одному объекту на каждый пул-реквест. Нам нужно конвертировать этот массив в подходящий fzf формат по строке на пул-реквест. Если говорить о данных, которые нам нужны, первое это номер пул-реквеста, который мы хотим пропустить через gh checkout. Также нам нужен способ идентифицировать интересный нам пул-реквест, в этом смысле лучший кандидат его заголовок. Чтобы извлечь эту информацию из JSON, мы можем воспользоваться интерполяцией строки в jq.

gh api 'repos/:owner/:repo/pulls' |    jq --raw-output '.[] | "#\(.number) - \(.title)"'

Вот опция сырого вывода --raw-output, которая определяет строку JSON; без неё каждая строка данных будет окружена кавычками. К примеру, если я выполню команду pr checkout https://github.com/junegunn/fzf, она выведет эти строки:

#2368 - ansi: speed up parsing by roughly 7.5x#2349 - Vim plugin fix for Cygwin 3.1.7 and above#2348 - [completion] Default behaviour to use fd if present else use find.#2302 - Leading double-quote for exact match + case sensitive search#2197 - Action accept-1 to accept a single match#2183 - Fix quality issues#2172 - Draft: Introduce --print-selected-count#2131 - #2130 allow sudo -E env fzf completion#2112 - Add arglist support to fzf.vim#2107 - Add instructions on command for installing fzf with Guix and/or Guix System#2077 - Use fzf-redraw-prompt in history widget#2004 - Milis Linux support#1964 - Use tmux shell-command#1900 - Prompt generally signals that the shell is ready#1867 - add {r}aw flag to disable quoting in templates#1802 - [zsh completion] Expand aliases recursively#1705 - Option to select line index of input feed and to output cursor line index#1667 - $(...) calls should be quoted: \"$(...)\"#1664 - Add information about installing using Vundle#1616 - Use the vim-specific shell instead of the environment variable#1581 - add pre / post completion 'hooks'#1439 - Suppress the zsh autocomplete line number output#1299 - zsh completion: Add support for per-command completion triggers.#1245 - Respect switchbuf option#1177 - [zsh] let key bindings be customized through zstyle#1154 - Improve kill completion.#1115 - _fzf_complete_ssh: support Include in ssh configs#559 - [vim] use a window-local variable to find the previous window#489 - Bash: Key bindings fixes

Пропустив их через fzf, мы сможем выбрать какие-то из этих строк и записать их стандартный поток вывода. Нам интересен только номер запятой, так что извлечём его при помощи команды set регулярного выражения с захватом группы. 1-я работающая версия выглядит так:

function pr-checkout() {  local pr_number  pr_number=$(    gh api 'repos/:owner/:repo/pulls' |    jq --raw-output '.[] | "#\(.number) \(.title)"' |    fzf |    sed 's/^#\([0-9]\+\).*/\1/'  )  if [ -n "$pr_number" ]; then    gh pr checkout "$pr_number"  fi}

Попробуем его на репозитории fzf.

Поток fzf в окне выбора показывает заголовки пул-реквестов. Выбирая строку, мы попадаем на соответствующий пул-реквест.

В большинстве случаев этого, вероятно, достаточно. В блоге на гитхабе скрипт даже проще: он работает с выводом gh напрямую, но, чтобы определить нужный нам пул-реквест, заголовка может быть недостаточно. Нам поможет дополнительная информация. Например, в окне предпросмотра мы можем показывать описание пул-реквеста и иную информацию, которую мы можем получить с помощью запроса к api.

В этой функции мы удаляем ветки выше и заполняем окно предварительного просмотра с помощью вызова git log в выбранной ветви. Первой идеей может быть попытка попробовать то, что я уже показывал, то есть сделать запрос к api, чтобы получить информацию о выбранном реквесте. Но если мы выбираем разные ветви, то задержка запроса к api может начать нас раздражать, затрудняя работу. К счастью, запросы api нам больше не понадобятся: все нужные данные у нас уже есть: мы получили их, когда сделали первый запрос. Что нам нужно это дописать шаблон строки jq, чтобы извлечь всю нужную информацию и затем воспользоваться функцией fzf, которая позволяет спрятать информацию входящих строк в окне выбора и показать её в окне предпросмотра.

fzf рассматривает каждую строку как массив полей. По умолчанию поля разделяются последовательностями пробелов (табуляциями и пробелами), но мы можем управлять разделителем с помощью опции --delimiter. Например, если мы зададим --delimiter=',' и передадим строку first,second,third в fzf, то поля будут first,, second и third. Само по себе это бесполезно. Но с помощью опции --with-nth мы можем управлять полями в окне выбора. Например, fzf --with-nth=1,2 будет отображать только первое и второе поля каждой строки. Кроме того, мы видели выше, что можно написать {} в качестве плейсхолдера в команде предварительного просмотра и fzf заменит его текущей выбранной строкой. Но {} это простейшая форма плейсхолдера. Можно указать индексы полей в фигурных скобках, и fzf заменит плейсхолдер этими полями.

Вот пример, где мы используем как --with-nth, так и --preview, а <tab> играет роль разделителя.

echo -e 'first line\tfirst preview\nsecond line\tsecond preview' |    fzf --delimiter='\t' --with-nth=1 --preview='echo {2}'

fzf разбивает каждую строку по символу табуляции; опция --with-nth=1 указывает fzf показать первую часть в окне выбора; {2} в команде предварительного просмотра будет заменена второй частью, и так как она передаётся в echo, то просто отобразится.

Пример работы с полями в fzfПример работы с полями в fzf

Этот скрипт мы используем, чтобы показать полезную информацию в окне предпросмотра. Давайте сначала просто посмотрим на него, а затем я расскажу, как он выполняется, шаг за шагом.

function pr-checkout() {  local jq_template pr_number  jq_template='"'\'#\(.number) - \(.title)'\'\t'\'Author: \(.user.login)\n'\'Created: \(.created_at)\n'\'Updated: \(.updated_at)\n\n'\'\(.body)'\'"'  pr_number=$(    gh api 'repos/:owner/:repo/pulls' |    jq ".[] | $jq_template" |    sed -e 's/"\(.*\)"/\1/' -e 's/\\t/\t/' |    fzf \      --with-nth=1 \      --delimiter='\t' \      --preview='echo -e {2}' \      --preview-window=top:wrap |    sed 's/^#\([0-9]\+\).*/\1/'  )  if [ -n "$pr_number" ]; then    gh pr checkout "$pr_number"  fi}

Мы немного изменили эту простую функцию. Извлекли шаблон строки jq в переменную, а затем дополнили её информацией об авторе, времени создания пул-реквеста, времени его последнего обновления, а также его описанием . Всю эту информацию мы получили в объекте JSON. Ответьте на этот запрос к api гитхаба: gh api 'repos/:owner/:repo/pulls'.

Обратите внимание, что мы отделили новую информацию номер и заголовок символом табуляции \t. Символ табуляции используется также в качестве разделителя в fzf, затем мы показываем номер пул-реквеста и заголовок в окне выбора (при помощи --with-nth=1), а оставшуюся информацию показываем в окне предварительного просмотра (при помощи --preview='echo -e {2}').

Обратите внимание также, что на этот раз в jq мы не используем опцию --raw-output. Причина немного неочевидна. Строки, которые мы создаём с помощью jq, содержат экранированные символы новой строки. Если мы передадим опцию --raw-output в jq, она будет интерпретировать все экранированные символы, и, в частности, вместо \n отобразится именно новая строка. Вот пример, сравните выходные данные этой команды:

echo '{}' | jq --raw-output '"first\nsecond"'

и команды

echo '{}' | jq '"first\nsecond"'

первая выведет

firstsecond

А вторая вот такую строку:

"first\nsecond"

Первая версия проблематична. Помните, что fzf работает со строками, делает список строк, позволяя пользователю выбрать одну или несколько строк и вывести их. Это означает, что без опции сырого вывода каждый пул-реквест в fzf будет показан как множество строк. И это определённо не то, чего мы хотим. Поэтому позволим jq вывести escape-версию, чтобы гарантировать, что каждый пул-реквест это одна строка.

Однако такой подход вводит новые проблемы, первая мы по-прежнему хотим настоящие символы новой строки, а не символы \n. Эта проблема решается командой echo -e, которая включает интерпретацию escape-символов. Вторая проблема в том, что без опции сырого вывода jq в начале и в конце строки показывает символы кавычек и распечатывает наш разделитель, то есть табуляцию, как символ в escape. Эту проблему мы решим удалением кавычек в ручном режиме и заменой первого escape-символа \t на настоящую табуляцию. Именно это делается в sed после jq.

Наконец, обратите внимание, что мы определили опцию --preview-window=top:wrap, чтобы fzf оборачивал строки в окне предпросмотра и отображал их верхней части экрана, а не справа.

И вот как это выглядит в действии:

Создание веток для фич из проблем (issues) в JIRA

Мы видели выше, как использовать fzf для удаления ветвей git. Теперь давайте посмотрим на противоположную задачу создание новых ветвей. На работе для отслеживания проблем мы используем JIRA. Каждая ветвь функции обычно соответствует какой-то проблеме JIRA. Чтобы поддерживать эту взаимосвязь, я использую схему именования ветвей git, о которой расскажу ниже. Предположим, что проект JIRA называется BLOG, и сейчас я работаю над проблемой BLOG-1232 с названием Добавить в сценарий запуска флаг вывода подробностей. Я называю свою ветку BLOG-1232/add-a-verbose-flag-to-the-startup-script; описание обычно даёт достаточно информации, чтобы определить функцию, которой соответствует ветвь, а часть BLOG-1232 позволяет мне перейти к тикету JIRA, когда я ищу подробности о проблеме.

Вполне понятно, как выглядит рабочий процесс создания этих веток:

  • вы открываете issue из JIRA в браузере;

  • копируете номер проблемы или запоминаете его;

  • переключаетесь на терминал, начинаете вводить git checkout -b BLOG-1232/;

  • переключаетесь на браузер и смотрите на название;

  • переключаетесь на терминал и добавляете похожее на название в JIRA описание в kebab-cased.

Обычно мне приходится несколько раз переключаться между браузером и терминалом, и я всё равно делаю опечатки в названии ветки.

И это ещё один рабочий процесс, который можно полностью автоматизировать. С проблемами в Jira можно работать так же, как мы работали с пул-реквестами, через API JIRA. Функция, которую мы напишем, подобна pr-checkout, но будет иметь несколько заметных отличий от неё.

Во-первых, от жира нет удобного инструмента, подобного gh, чтобы общаться с её api. Во-вторых, сервер (по крайней мере сервер, с которым работаю я) не разрешает создавать токены доступа, что заставляет меня при доступе к api использовать простые имя пользователя и пароль. Мне не хочется сохранить мой пароль в скрипте оболочки, а точнее, не хочется делать это в незашифрованном файле, поэтому, чтобы пароль хранился безопаснее, воспользуемся secret-tool. Наконец, создание имени ветки требует большего, чем простое извлечение текста; воспользуемся комбинаций cut, sed, и awk.

Давайте сначала посмотрим на скрипт, а потом попробуем понять, как он работает.

function create-branch() {  # The function expectes that username and password are stored using secret-tool.  # To store these, use  # secret-tool store --label="JIRA username" jira username  # secret-tool store --label="JIRA password" jira password  local jq_template query username password branch_name  jq_template='"'\'\(.key). \(.fields.summary)'\'\t'\'Reporter: \(.fields.reporter.displayName)\n'\'Created: \(.fields.created)\n'\'Updated: \(.fields.updated)\n\n'\'\(.fields.description)'\'"'  query='project=BLOG AND status="In Progress" AND assignee=currentUser()'  username=$(secret-tool lookup jira username)  password=$(secret-tool lookup jira password)  branch_name=$(    curl \      --data-urlencode "jql=$query" \      --get \      --user "$username:$password" \      --silent \      --compressed \      'https://jira.example.com/rest/api/2/search' |    jq ".issues[] | $jq_template" |    sed -e 's/"\(.*\)"/\1/' -e 's/\\t/\t/' |    fzf \      --with-nth=1 \      --delimiter='\t' \      --preview='echo -e {2}' \      --preview-window=top:wrap |    cut -f1 |    sed -e 's/\. /\t/' -e 's/[^a-zA-Z0-9\t]/-/g' |    awk '{printf "%s/%s", $1, tolower($2)}'  )  if [ -n "$branch_name" ]; then    git checkout -b "$branch_name"  fi}

В скрипте мы видим три части. Первая часть это команда curl, её переменные. Через них скрипт общается с API JIRA. Затем вывод api конвертируется строки формата, удобного для fzf; это часть скрипта такая же, как у pr-checkout. Наконец, вывод fzf конвертируется формат имени ветки.

Самые существенные изменения в сравнении с pr-checkout эта команда curl. Мы воспользовались конечной точкой поиска JIRA, которая в качестве параметра URL ожидает запрос на языке JQL. В моём случае меня интересуют все проблемы проекта BLOG, которые закреплены за мной, и те, что отмечены строкой In Progress. Строка запроса JQL содержит пробелы, знаки и скобки. Все они недопустимы в url, поэтому их нужно закодировать. Опция curl --data-urlencode автоматически закодирует эти символы. Поскольку в этой опции по умолчанию применяется запрос POST, чтобы переключиться на get, мы должны добавить опцию --get. Также воспользуемся опцией --user, чтобы сообщить curl, что нужно добавить заголовок базовой аутентификации. И последнее: добавим опцию --silent, чтобы опустить информацию о прогрессе выполнения и --compressed, чтобы сэкономить на пропускную способность.

Затем, чтобы конвертировать записи массива в JSON ответе в одну строку, воспользуемся той же техничкой, что и выше, разделив строку поиска в окне предпросмотра по символу табуляции и пропустив вывод через fzf, чтобы позволить пользователю выбрать запись. Вывод fzf будет строкой вроде BLOG-1232. Add a verbose flag to the startup script{...preview part}, чтобы удалить часть предварительного просмотра строки, воспользуемся командой cut. По умолчанию cut в качестве разделителя использует символ табуляции, а опция -f1 сообщает cut, что нужно вывести первое поле. Результат выполнения команды будет таким: BLOG-1232. Add-a-verbose-flag-to-the-startup-scrip. Затем команда sed заменит первую точку на символ табуляции, а все нечисловые и неалфавитные символы на -, сохранив при этом наши табуляции. И вот результат: BLOG-1232<tab>Add-a-verbose-flag-to-the-startup-script. Наконец, awk возьмёт строку, разделит её по табуляции, преобразует её вторую часть в нижний регистр и вернёт обе части символом косой черты в качестве разделителя.

 Создание новой ветки из проблем в JIRA Создание новой ветки из проблем в JIRA

Заключение

Я представил четыре типичных рабочих процесса оболочки и показал, как с помощью fzf их можно упростить. Полученные функции варьируются от простого однострочника до более сложных функций с вызовами API и нетривиальной логикой, но все они сокращают несколько шагов рабочего процесса до одной команды без параметров.

Представленные мной рабочие процессы могут никак не касаться вас. Но, надеюсь, вам поможет техника в целом: попробовать понаблюдать, как вы добавляете параметр к командам и как этот процесс можно автоматизировать. Параметры могут быть файлами или каким-то местом в системе, расположение которого не меняется, например, виртуальными средами. Или это могут быть параметры, которые пропускаются через другую команду (пример: ветви гита) или через API (номер пул-реквеста или заголовок из JIRA).

Узнайте, как прокачаться в других специальностях или освоить их с нуля:

Другие профессии и курсы
Подробнее..

Как приручить консоль, или 5 шагов к жизни с командной строкой

25.01.2021 16:17:27 | Автор: admin

Всем привет! Меня зовут Осип, я Android-разработчик в Redmadrobot и я люблю автоматизировать всё, что автоматизируется. В этом мне помогает консоль, поэтому решил поделиться опытом, как настроить командную оболочку так, чтобы в ней было приятно работать и она ежедневно помогала вам решать задачи.

Статья для тех, кто использует Linux или macOS. Если у вас Windows, вы можете использовать WSL (приравнивается к Ubuntu).

Есть задачи, которые проще выполнить в командном интерфейсе, а не в графическом, к примеру:

  • посчитать количество строк кода в проекте,

  • скопировать все файлы с расширением .png из одной папки в другую,

  • постучаться API и посмотреть какой ответ он выдаёт.

По опыту знаю: когда возникает одна из таких задач, разбираться, как это сделать в командной строке, если не используешь её это последнее, чего хочется. Кажется, что нет смысла залезать в неё ради одной задачи. Проще найти плагин или утилиту.

Поверьте, стоит один раз настроить командную оболочку и разобраться, как в ней работать, и рабочее окружение начнёт помогать вам как хорошая IDE.

Обо всём по порядку, начнём с терминологии и пойдем дальше. Если вы уже знакомы с основами, можно переходить к разделу Изучаем арсенал.


Статья только началась, а по тексту уже встречались и командная строка, и командная оболочка. Чем отличаются консоль, терминал, командная оболочка и командная строка?

Если объяснять из центра наружу: командная строка строка, где пользователь пишет команды; командная оболочка программа, которая интерпретирует команды, введённые в командную строку и выводит результат.

Термины консоль и терминал в статье используются как синонимы и обозначают программу для ввода и вывода информации. В терминале запускается командная оболочка.

Подробнее о различиях можно почитать на Ask Ubuntu: What is the difference between Terminal, Console, Shell, and Command Line?

В статье будут встречаться примеры команд. Если по ходу прочтения вы не понимаете, что делает консольная команда, скопируйте её и вставьте в ExplainShell. Благо Роскомнадзор перестал его блокировать после разблокировки Telegram.

Зачем вообще использовать командную строку

Дисклеймер: Я не призываю использовать только командную строку и отказаться от графического интерфейса. Нужно просто учитывать командную строку при выборе инструмента для решения задачи.

Когда хотят рассказать чем же хорош CLI, выделяют разные преимущества перед GUI:

  • Доступность. Командная строка доступна везде. Внутри Android Studio есть вкладка с командной строкой. Можно и вовсе настроить drop-down терминал (ещё его называют quake style), который будет появляться поверх всех приложений по нажатию сочетания клавиш.

  • Многофункциональность. Одна точка доступа к любым утилитам.

  • Простота. Простой командный интерфейс может заменить сложный графический интерфейс. Сделать графический интерфейс который позволит ввести, например, пять параметров может быть довольно нетривиальной задачей, а сделать то же самое с командным интерфейсом просто.

  • Легковесность. Как правило, CLI утилиты используют меньше ресурсов.

Меня как разработчика больше всего впечатляет, как можно комбинировать CLI утилиты. Текст интерфейс общения, который понятен для всех утилит с командным интерфейсом. Утилиты принимают на вход текст и возвращают тоже текст. Это один из принципов Unix, которые сформулировал Дуглас Макилрой в 1978 году:

Пишите программы, которые делают одну вещь и делают её хорошо.


Пишите программы, которые бы работали вместе.


Пишите программы, которые поддерживают текстовые потоки, потому что это универсальный интерфейс.

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

Примеры из жизни

Я задал вопрос коллегам-роботам: Для чего вы чаще всего открываете терминал? Получился такой ТОП-5:

  1. Работа с Git там, где не хватает графического интерфейса.

  2. Установка пакетов и управление зависимостями (подробнее про менеджер пакетов поговорим в разделе Устанавливаем менеджер пакетов).

  3. Работа с SSH.

  4. Проверка API с помощью curl.

  5. Когда нужно грохнуть процесс.

Есть и менее очевидные применения:

  1. Скачать видео из YouTube поможет youtube-dl. Качаете подкаст и нужна только аудио-дорожка? Добавьте к команде флаг --audio. Хотите скачать весь плейлист или даже весь канал? Подставляйте ссылку на канал и готовьте побольше свободного места.

  2. Хотите посмотреть отличия между файлами? Выполните команду diff и укажите пути до файлов, которые надо сравнить.

Шаг 1: Открываем терминал

Не терминал, а эмулятор терминала. (c) Департамент зануд

Обычно в ОС уже есть какой-то терминал, и для начала можно использовать его. Если хочется больше функций и возможностей кастомизации, то есть из чего выбирать. Некоторые терминалы позволяют открывать вкладки, разделять экран и даже рендерить картинки. Можно найти на любой вкус, например, стилизованный под старый ламповый монитор.

cool-retro-terminalcool-retro-terminal

Выбор терминала это тема для отдельной статьи. Кратко: если у вас Linux, начните с этого списка. На macOS популярен iTerm2, но я его не использовал, поэтому не могу ни поругать, ни похвалить.

Для меня важно чтобы и на компьютере с Linux, и на рабочем ноутбуке с macOS был один и тот же терминал с одинаковыми настройками. Я выбирал среди кроссплатформенных и остановился на kitty.

Шаг 2: Устанавливаем менеджер пакетов

Мы оказались в терминале. Первое, с чем нужно разобраться, менеджер пакетов.

Я встречал людей, которые уверены, что, для того чтобы установить программу из консоли, нужно непременно собирать её из исходников и вообще это сложно. Это не так. Программы в собранном виде хранятся в репозиториях. Пакетные менеджеры устанавливают программы из репозиториев и отвечают за их обновление и удаление.

Это даже проще, чем искать надёжный источник, из которого можно скачать программу, и удобнее, чем магазины приложений в macOS или Windows, где зачастую нет нужных программ.

Менеджеры пакетов в Linux

В дистрибутивах Linux менеджер пакетов есть по умолчанию. В Ubuntu, Debian и Mint это apt-get, а в Manjaro и ArchLinux pacman.

Чтобы установить пакет достаточно в терминале написать apt-get install [package]. Если у вас pacman, то pacman -S [package]. Может понадобиться sudo в начале, чтобы выполнить команду с правами root.

Чтобы обновить все пакеты с помощью apt-get введите команду apt-get update && apt-get upgrade. В pacman pacman -Syu.

В pacman много флагов и сложно сразу запомнить нужные. Ещё одно неудобство он не поддерживает установку пакетов из репозитория AUR. Это репозиторий, в который могут загружать пакеты любые пользователи. Исправить минусы помогут утилиты, которые упрощают работу с pacman. Рекомендую попробовать yay.

Менеджеры пакетов в macOS

В macOS придется установить пакетный менеджер. Самые популярные Homebrew и MacPorts. Homebrew активнее поддерживается сообществом, а пакеты в нём обновляются чаще, поэтому лучше использовать его. Для установки скопируйте актуальную команду установки c официального сайта. Эта команда скачает скрипт установки и запустит его.

Может понадобиться установка XCode Command Line Tools. Это базовый набор консольных инструментов clang, git, make и других. Он не зависит от XCode, а называется так, потому что необходим XCode для компиляции.

Теперь, чтобы установить пакет, достаточно в терминале написать brew install [package].

Обновляются все пакеты одной командой brew upgrade. Если brew отказывается работать, напишите brew doctor , и brew подскажет, что с ним не так, а также как это исправить.

Шаг 3: Устанавливаем командную оболочку

Чтобы было проще работать, командная оболочка должна вам помогать. В ней должен быть поиск по введённым ранее командам, а ещё должно работать автодополнение. Здорово, если будут подсказки, которые учитывают, какие команды вы используете чаще других. Не помешает подсветка синтаксиса и ошибок при вводе команд.

Все примеры в статье будут сделаны с использованием командной оболочки fish. Эта командная оболочка проста для освоения и в ней все нужные функции работают из коробки.

Чтобы узнать, какая оболочка используется по умолчанию у вас, выполните команду echo $SHELL. Скорее всего, команда выведет /env/bash или /env/zsh это самые популярные оболочки. Если хотите сравнить возможности bash, zsh и fish, посмотрите эту таблицу.

Установим fish c помощью менеджера пакетов:

# Если pacmansudo pacman -S fish# Если apt-getsudo apt-get install fish# Если brewbrew install fish

В статье будут упоминаться и другие программы, все они устанавливаются таким же образом. Команды установки написаны на страницах программ, чтобы не повторяться, не буду писать их для каждой утилиты.

Fish установлен. Запускаем его командой fish:

osip@homepc ~ % fishWelcome to fish, the friendly interactive shellType `help` for instructions on how to use fishosip@homepc ~>

Теперь можно настроить внешний вид. Команда fish_config откроет в браузере страницу настроек. Здесь можно настроить цветовую схему, приглашение командной строки (prompt), посмотреть список функций, переменные окружения, историю команд и список горячих клавиш. Здесь же можно добавлять, редактировать и удалять аббревиатуры команд. Про аббревиатуры поговорим чуть позже.

Fish по умолчанию

Закройте терминал и откройте его заново. Вы окажетесь не в fish, а в командной оболочке по умолчанию. Есть два варианта, как сделать так, чтобы открывался fish:

  1. Назначить fish командной оболочкой по умолчанию.

    Нужно учитывать, что скрипты инициализации текущей командной оболочки не будут выполняться. Команды и переменные окружения из .bashrc, .bash_profile, .zshrc и т.д, нужно переместить в .config/fish/fish.config , а затем адаптировать под синтаксис fish.

  2. Использовать fish только как интерактивную оболочку.

    Это более безболезненный способ, потому что не нужно мигрировать скрипты и переменные окружения. В конце скрипта инициализации текущей командной оболочки нужно запустить fish. Добавьте строку exec fish в файл .bash_profile, если у вас bash или в .zshrc, если zsh. Эти файлы находятся в корневой директории пользователя.

    На ArchWIki есть более подробное описание этого и еще нескольких способов.

Поиск по истории

Давайте-ка посмотрим, что умеет fish. Если еще не установили, можно попробовать в браузере. Я изменил только цвета и prompt, больше ничего не настраивал.

Когда вы начинаете набирать команду, fish подсказывает команды и аргументы, которые вы использовали раньше. Чтобы применить подсказку нажмите . Подставить одно слово из подсказки Ctrl+.

sample_command это демонстрационная функция. Она принимает любые параметры и ничего не возвращает.

Чтобы ввести предыдущую команду, нажмите клавишу . С помощью клавиш и можно перемещаться по истории команд. Если командная строка пуста, команды будут листаться по очереди. Если в командной строке есть текст, то история будет фильтроваться по нему.

Автодополнение

Начните писать любую команду и нажмите Tab, не дописывая её до конца. Попробуйте с командой git config:

Дополнение работает с командами, подкомандами и аргументами. Вместе с вариантами дополнения выводится описание подкоманд из документации.

Если утилита не поддерживает автодополнение, fish умеет создавать дополнения из документации man. Для этого нужно выполнить команду fish_update_completions.

А что с путями? Например, хотим перейти в папку dev/tools/jarjar/:

Дополнение путей тоже работает на Tab. Для перехода по пути не обязательно писать команду cd в начале. А еще работает дополнение, если написать первую букву имени каждой папки в пути. Если указан несуществующий путь, он подсвечивается красным.

Сложно запомнить все нужные флаги у команд. Хочу вывести дерево файлов, но не помню, как ограничить его глубину и сделать так, чтобы вывод был цветным. Для такого случая есть Shift+Tab дополнение с поиском:

Автодополнение может сработать в самых неожиданных местах, например, так работает автодополнение для команды kill:

Убийство Android Studio на глазах у studentdУбийство Android Studio на глазах у studentd

Wildcards

В fish, как и в bash, есть поддержка wildcards. Wildcards позволяют выполнить команду для нескольких файлов.

Выводим все файлы с расширением .md в текущей папкеВыводим все файлы с расширением .md в текущей папке

* соответствует любой строке
** соответствует любой иерархии папок, то есть рекурсивно заходит во вложенные папки

Применим wildcard, чтобы скопировать все файлы apk после сборки в папку output:

  • cp build/*.apk output/ скопирует все apk из папки build.

  • cp build/**.apk output/ скопирует все apk из папки build и из всех вложенных папок. То, что надо.

Функции, алиасы и аббревиатуры

Большиство команд fish это функции. Можно писать и свои функции. Синтаксис такой:

funcion [название]    [тело функции]end

Если функцию нужно сохранить для будущих сессий, не забудьте выполнить команду funcsave [название] после того как напишете функцию и проверите, что она корректно работает.

Для часто используемых команд можно создать более короткие синонимы алиасы. В fish команда alias создаёт однострочную функцию.

Как выглядит alias?

Флаг --save указывает, что нужно автоматически вызвать команду funcsave после создания алиаса. Таким образом алиасы сохранятся для будущих сессий.

Другой вариант сокращения команд аббревиатуры. Они настраиваются командой abbr или в fish_config во вкладке Abbreviations.

Удобно создавать аббревиатуру одновременно в английской и русской раскладках (см. скриншот). Такая аббревиатура выполнится, даже если вы забыли переключить раскладку клавиатуры.

Аббревиатуры подставляются на лету, когда вы нажимаете Space или Enter. В отличие от алиасов, аббревиатуры не являются функциями.

И па и gf превращается в git fetchИ па и gf превращается в git fetch

Шаг 4: Изучаем арсенал

Командная оболочка есть, теперь нужны команды.

Консольные утилиты могут быть с CLI и TUI. Command Line Interface (CLI) программа принимает команды через командную строку. Так работает большинство утилит. Text User Interface (TUI) интерфейс рисуется псевдографикой и по нему можно кликать мышкой как по GUI.

TUI для SpotifyTUI для Spotify

CLI не нужно бояться. На замену старым утилитам появляются новые с улучшенным UX, ориентированные на человека, а не на скрипты.

Например, многие старые утилиты, которые выводят размер файла, по умолчанию выводят его в байтах. А утилита df вообще выводит свободное место на диске в количестве блоков по 512 байт.

Чтобы выводились понятные человеку размерности, нужно добавить флаг -h (human readable). Цветной вывод удобнее читать, но он тоже по умолчанию обычно отключен и включается добавлением флага, чаще всего -C. В современных же утилитах по умолчанию включен цветной человекопонятный вывод.

Стандартные команды

Чтобы пользоваться командной строкой, нужно знать несколько стандартных команд:

  • cd [сhange directory] команда для навигации по файловой системе. Если запустить её без аргументов, вы окажетесь в домашней папке;

  • cp [copy], mv [move], rm [remove] команды для копирования, перемещения и удаления файлов, соответственно;

  • mkdir [make directory] команда для создания папки;

  • echo выводит строку, которую ей передали.

Если команда долго работает и вы не хотите дожидаться её завершения, прервите её выполнение сочетанием клавиш Ctrl + C.

Помощь: man, help, tldr

Есть несколько способов получить справку по команде.

man выводит полную справку:

  • описание команды,

  • список аргументов и описание каждого из них,

  • какие переменные окружения использует утилита и для чего,

  • известные баги,

  • советы и примеры использования,

  • другая информация, которую посчитал полезной разработчик.

Если ввести man man, вы получите справку по команде man, где всё это подробно описано.

man это утилита с TUI, в ней есть горячие клавиши. Для поиска нажмите /, а для выхода q. / и q стандартные клавиши для поиска и выхода, они работают во многих TUI утилитах. Ещё один стандартная клавиша ?, она открывает справку.

Можно выполнить команду из man для этого нажмите ! и введите команду. Хотите открыть man для другой команды внутри man или сразу попробовать выполнить команду, следуя документации? Легко.

Страницы в man пишут разработчики утилит. Если разработчик не написал справку, man выдаст No manual entry for [command]. Но даже если нет страницы в man можно вывести краткую справку с помощью флага --help. Попробуйте написать man --help.

Для команд fish можно открыть справку в браузере командой help <command>.

Если читать мануалы некогда, то поможет утилита tldr. Она отображает типичные случаи использования команд:

tldr tldrtldr tldr

Объединяем команды

Помните, в самом начале я говорил, что консольные команды как кирпичики и их можно объединять, чтобы решить более сложную задачу? Сейчас мы этим и займёмся.

Чтобы направить вывод одной команды на вход другой, используется оператор |. Он называется pipe, а на русский его переводят как конвейер. Если мы хотим подать вывод команды find_bone на вход команде eat, нужно между этими командами поставить трубу (pipe):

$ find_bone | eat

Синтаксис понятен, но таких команд не существует и запустить их у себя в консоли не получится. Давайте посмотрим на более реальный пример:

$ echo -e "spot\\nhandle\\npick\\natlas" > robots.txt$ cat robots.txt | sortatlashandlepickspot

Оператор | нам уже знаком, но что делает >? Этот оператор направляет вывод команды в файл. После этого командой cat мы достаём содержимое файла и с помощью оператора | отдаём на сортировку.

Современные утилиты

Просмотр списка файлов: ls, tree exa

Для просмотра списка файлов в папке обычно используют стандартную команду ls, а для просмотра иерархии папках tree. Обе эти утилиты можно заменить более дружелюбной утилитой exa.

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

Скриншоты. Сравнение ls, tree и exa.
Сравнение вывода ls и exaСравнение вывода ls и exaСравнение вывода tree и exaСравнение вывода tree и exa

Бонус: В exa можно совместить два режима вывода.

Просмотр запущенных процессов: top htop

top и htop. Обе утилиты выводят список запущенных процессов, но htop делает это гораздо приятнее.

В htop можно сортировать процессы по разным столбцам, нажимая на них. Кнопки в нижнем меню тоже нажимаются. Столбцы и информационные блоки в шапке настраиваются можно убрать лишнее и добавить нужное.

А как выглядит top?

Работа с JSON: jq

jq незаменимая утилита, если вы работаете с JSON. Проще показать на примерах что умеет делать jq.

Валидируем json:

$ echo '{"model": spot}' | jq typeparse error: Invalid numeric literal at line 1, column 15$ echo '{"model": "spot"}' | jq type"object"

Форматируем json:

$ echo '{"model":"spot"," type":"robodog"}' | jq{  "model": "spot",  "type": "robodog"}

Выкусываем из json'а только то, что нужно:

$ set json '[{"model": "spot", "type": "robodog"}, {"model": "atlas", "type": "humanoid"}]'$ echo $json | jq 'map(.model)' --compact-output["spot","atlas"]$ echo $json | jq .[0].model"spot"# А теперь пример посложнее$ echo $json | jq 'map({(.model): .type}) | add'{  "spot": "robodog",  "atlas": "humanoid"}

Это только малая часть возможностей. Все возможности смотрите в доке.

Другие утилиты

Чтобы этот раздел не раздулся, остальные утилиты я спрятал под спойлеры. При первом прочтении их можно пропустить и позже использовать этот раздел как справочник.

Консольный HTTP клиент: curl, wget httpie

httpie делает то же что curl отправляет запросы в сеть. Но посмотрите как отличается синтаксис и оформление вывода в curl и httpie.

На фотографии слева направо: curl и httpieНа фотографии слева направо: curl и httpie
Отображение содержимого файла: cat bat

cat и bat выводят содержимое файла, но bat подсвечивает синтаксис и отображает изменения из гита.

Поиск по содержимому файлов: grep ripgrep

ripgrep более быстрая версия grep. Сравнение скорости работы показывает, что ripgrep быстрее всех :)

Поиск файлов: find fd, fzf

Для поиска файлов можно использовать стандартную утилиту find. Для неё есть альтернатива fd. Она работает быстрее, поддерживает цветной вывод, по умолчанию игнорирует скрытые файлы и файлы из .gitignore. Посмотрите на гифку, которая демонстрирует работу fd.

Ещё одна утилита для поиска fzf [fuzzy finder]. Это утилита с TUI для интерактивного поиска файлов с использованием нечёткого поиска по названиям.

Ещё из приятного есть предпросмотр содержимого.

Подсчёт количества строк кода: wc tokei

Стандартная утилита wc [word count] считает количество слов, символов и строк в файлах, но чтобы с помощью неё посчитать количество строк кода в проекте, придётся написать что-то такое:

$ fd -g '*.kt' | xargs wc -l

У такой команды есть сразу несколько недостатков:

  • считаются все строки, включая комментарии и пустые строки,

  • ищутся только файлы с расширением .kt, для поиска других придётся менять команду,

  • сгенерированные файлы и остальные файлы, которые заигнорены в гите, тоже попадут в статистику,

  • такую команду долго писать.

Утилита tokei лишена перечисленных недостатков. Вот пример вывода tokei на одном из наших проектов:

Упс, файлы proguard засчитались в пользу PrologУпс, файлы proguard засчитались в пользу Prolog
Свободное место на диске: du ncdu

Ещё один пример разницы CLI и TUI. В отличие от du, ncdu это TUI. Тут же можно перейти внутрь папки или удалить ненужное нажав d.

Хм, накопилось много врапперов и кэшей Gradle. Можно почистить.Хм, накопилось много врапперов и кэшей Gradle. Можно почистить.
Сравнение файлов: diff delta

Отличная замена старому-доброму diff - delta. Можно использовать режим отображения side-by-side, если больше нравится, включить отображение номеров строк. Даже без дополнительных настроек диффы выглядят приятно:

Измерение времени работы программы: time hyperfine

Не верьте на слово, если я говорю, что одна утилита работает быстрее другой. Лучше проверьте.

Можно измерить время выполнения команды с помощью time (в macOS gtime). Эта утилита не предназначена для бенчмарков нет возможности прогрева, команда выполняется один раз. hyperfine подойдёт лучше, потому что изначально разработан для бенчмарков.

Попробуем замерить время выполнения команды tree:

Вывод команды tree перенаправлен в пустоту (/dev/null), потому что здесь не важен вывод команды, важно только время её выполнения. С hyperfine этого делать не нужно, он сам отбрасывает вывод команды.

Hyperfine умеет отслеживать признаки неправильного бенчмарка. Вот некоторые подсказки:

  • если во время замера есть выбросы, hyperfine посоветует закрыть другие программы, потому что они могут влиять на бенчмарк,

  • если первый запуск команды дольше остальных, hyperfine посоветует сделать прогрев, задав параметр --warmup N. Перед бенчмарком программа выполнится N раз.

Можно сравнивать скорость выполнения нескольких команд. Для этого нужно написать команды друг за другом, заключив их в кавычки:

$ hyperfine 'command_one' 'command_two' 'command_three'

Шаг 5: Сохраняем настройки

Чтобы не настраивать каждый раз все программы заново, нужно где-то сохранять настройки.

Конфиги это файлы. Обычно они хранятся в корневой директории пользователя вместе со скриптами инициализации командной оболочки, например, в папке .config/. Если вы установили fish, то найдёте папку .config/fish/ и в ней файлы с настройками. Самый простой способ сохранить конфиги сохранить их в Git-репозиторий.

Имена файлов и папок с настройками обычно начинаются с точки, поэтому одним словом их называют dotfiles. На момент написания статьи на GitHub опубликовано 138 425 репозиториев с именем dotfiles есть куда подсмотреть.

На странице awesome-dotfiles вы найдёте много информации про dotfiles. Там же есть ссылки на инструменты, которые помогают управлять dotfiles.

Я использую yadm. Мне важна кроссплатформенность, поэтому пригождается его возможность создавать альтернативные версии файлов для разных ОС.

Заключение

В консоли может быть сложно работать, если ее не приручить. Но если выбрать удобную для себя командную оболочку и один раз ее настроить, то она будет постоянно помогать вам.

Чтобы не настраивать рабочее окружение на каждом новом компьютере заново, сохраняйте конфиги в Git-репозиторий. Но не забывайте о безопасности следите, чтобы в репозиторий не попал ключи и пароли.

На замену старым утилитам приходят новые, нацеленные на пользователя, а не на скрипты. Используйте их и ищите новые! Если вам кажется, что утилита слишком сложная, возможно, вы такой не один и кто-то уже написал более простой в использовании аналог.

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

Если будут вопросы или вам понадобится помощь с освоением консоли, пишите мне в Telegram@osipxd. Ещё я иногда пишу в канал @rareilly заметки про Android и вообще про всё интересное, что нахожу. Спасибо за внимание!

Что ещё почитать

Подробнее..

Что такое bash shell

26.03.2021 14:15:31 | Автор: admin

И то, и другое интерпретаторы командной строки в линуксе. То есть если вы откроете командную строку и введете любую команду, да хоть:

cd /home

То именно интерпретатор ее расшифрует и скажет компьютеру он хочет перейти в директорию /home. Компьютер ведь не понимает команды на русском / английском языке. Ему нужны байтики. Этим и занимается интерпретатор переводом с нашего на компьютерный язык.

Так что cd /home это shell-команда! Или bash. Смотря какой интерпретатор установлен в вашей системе. В каждой операционной системе установлен интерпретатор по умолчанию. У них есть какие-то различия, но есть и набор базовых команд, которые понимают все: cd, mv, cp, ls (в винде эти команды немного другие)

А что такое shell-скрипт тогда? Это просто текстовый документ, внутри которого написан набор команд! Это не обязательно должны быть сложные команды, которые делают что-то супер-навороченное. Это любые команды, которые вы выполняете в консоли.

См также:

Основные linux-команды для новичка что можно выполнять в консоли

Например, создадим скриптик, который создаст директорию и в ней файлик:

mkdir /home/testcd /home/testtouch test.txt

Так, команды записали, осталось сохранить их в файлик. Скрипты хранят в файлах с расширением .sh, поэтому назовем файл first_script.sh. Но есть нюанс линуксу плевать на ваше расширение файла. Его может вообще не быть, и все равно скрипт останется скриптом. Почему? Потому что у любого скрипта в первой строке должен содержаться путь к интерпретатору. Например:

#!/bin/bashили#!/bin/sh

Весь файл целиком:

#!/bin/bashmkdir /home/testcd /home/testtouch test.txt

И даже если у такого файла не будет расширения вовсе, его можно будет запустить как скрипт:

sh first_script# Проверяем директорию /home там появилась папка test с файлом test.txt внутри.

Расширение .sh ставится для понимания челоком. Зашел в директорию:

Ага, что тут у нас? Файлы sh, скрипты какие-то лежат...

Скрипты могут быть простые, а могут быть сложные. Вот, например, в одном проекте мы вначале вручную обновляли тестовые платформы. Для обновления надо:

  1. Остановить сервис.

  2. Переподложить war-файл с приложением (лежат они в директории /opt)

  3. Запустить сервис

Сервиса два, допустим это test и cloud. Так что шагов уже 6.

Когда обновлять вручную надоело, мы положили на все линукс машины простой скриптик:

#!/bin/bashservice test stopcp test.war /opt/jboss-test/binservice test startservice cloud stopcp cloud.war /opt/jboss-cloud/binservice cloud start

Собираешь приложение, подкладываешь к скриптику и запускаешь 1 команду вместо 6. Удобно! Это называется автоматизация рутины =)

Другой пример с того же проекта мы делали серверное приложение. И во время установки приложения на сервере linux нужно выполнить пункты по настройке самой системы. Например, увеличить параметр max_map_count сколько максимум памяти может использовать процесс.

Приложение в пике работы требует много памяти. Если не настроить параметр, то тяжеловесная задача просто упадет с ошибкой Не хватает памяти. И если мы видим такую ошибку, то в первую очередь идем проверять настройки системы.

Вообще, если вы отдаете установку приложения на откуп чужим админам, лучше потом проверять а всё ли настроено верно? Конечно, обычно на production (машина, с которой работают реальные пользователи) настраивают всё внимательно, это на тестовых стендах могут что-то пропустить... Но лучше перебдеть!

Мы написали скрипт по проверке настройки окружения (символ # в начале строки означает, что это комментарий):

#!/bin/sh## check sysctl#if [ -f /proc/sys/vm/max_map_count ] && [ $(cat /proc/sys/vm/max_map_count) -ge 16777216  ]; thenecho "vm.max_map_count: ok"elseecho "vm.max_map_count: failed"fiif [ -f /proc/sys/vm/overcommit_memory ] && [ $(cat /proc/sys/vm/overcommit_memory) -eq 2  ]; thenecho "vm.overcommit_memory: ok"elseecho "vm.overcommit_memory: failed"fiif [ -f /proc/sys/vm/overcommit_ratio ] && [ $(cat /proc/sys/vm/overcommit_ratio) -eq 100  ]; thenecho "vm.overcommit_ratio: ok"elseecho "vm.overcommit_ratio: failed"fiif [ -f /proc/sys/vm/swappiness ] && [ $(cat /proc/sys/vm/swappiness) -le 10  ]; thenecho "vm.swappiness: ok"elseecho "vm.swappiness: failed"fi

В итоге админы настраивают окружение, а потом мы даем им скрипт, просим запустить его и прислать результаты. Я запустила скрипт на голой системе, где, разумеется, параметры настроены не были, и вот ответ:

Видим, что все проверки провалились, статус failed. Если и от админов приходит похожая картина, направляем их в документацию по настройке системы. Если к нам приходят с проблемой падения из-за нехватки памяти, снова просим выполнить скрипт. Так проще локализовать ошибку: это в приложении косяк, или окружение настроено плохо?

Просить других людей выполнить 10 команд не очень хорошо. Потому что часть команд может потеряться при выполнении плохо скопировал, забыл выполнить проверку, которую дали сообщением позже... Гораздо проще сделать 1 скрипт и попросить выполнить именно его.

Когда надо писать скрипт?

  • Когда надо выполнить больше 3 команд за раз проще выполнить одну, запустить скрипт.

  • Когда одну и ту же команду надо выполнять чаще 3 раз лучше автоматизировать эту работу.

По сути своей, bash-скрипты это та же автоматизация. А когда нужна автоматизация? Когда мы хотим избавиться от рутины, от постоянного выполнения одного и того же действия вручную. Повторяете одно и то же каждый день / неделю? Напишите скрипт. Даже если он на 2-3 строчки будет, это правда удобнее. Поверьте, сама делала небольшие скрипты ?

См также по bash:

Википедия

Что такое shell и зачем он нужен

Основы BASH. Часть 1 (Хабр) цикл статей о том, как писать скрипты

См также другие статьи из цикла Что такое...:

Что такое API

Что такое клиент-серверная архитектура

Что такое CI (Continuous Integration)

Что такое транзакция

Что такое XML

Что такое регулярные выражения (regexp)

PS больше полезных статей ищитев моем блоге по метке полезное. А полезные видео намоем youtube-канале

Подробнее..

Из песочницы Масштабирование CICD монорепозитория

19.07.2020 18:07:47 | Автор: admin

Lerna


Дано


  1. Монорепозиторий на базе Lerna и Yarn workspaces.
  2. Десяток приложений, и десятки общих пакетов на TypeScript, Angular, NodeJS.
  3. Высокое покрытие тестами самых разных мастей (модульные, интеграционные, e2e).
  4. и Atlassian Bamboo CI/CD.

Задача


Ускорить имеющиеся пайплайны в 2 раза (до, хотя бы, получаса). Попутно повысив стабильность до 90%.


Забегая вперед, скажу что требуемые показатели были достигнуты.


Было



Для инкрементальной сборки lerna filter options:


lerna run build:packages --since --include-merged-tags --include-dependencies

Чтобы попасть в инкремент пакеты должны проходить фазу lerna publish в артифакторий (JFrog):


# Masterlerna publish patch --yes# Featurelerna publish prepatch --yes --no-push --preid "${PREID}"

При такой организации pipeline, возможно только вертикальное масштабирование путём увеличения мощностей elastic агентов.


Этот подход крайне ограничен. И с ростом числа пакетов средняя длительность постепенно росла (~1ч).


Надо заметить, что в силу короткого релизного цикла (сутки), стабильность JFrog и, как следствие, всего pipeline была низка (~70%).


Идея


Собирать каждое приложение независимо от остальных.
На входе монорепозиторий
На выходе production image приложения.


Тестировать тоже независимо от остальных.
На входе production image (зависимости устанвлены, все пакеты собраны)
На выходе отчеты о тестировании и покрытии.


Это позволит собирать и тестировать в параллельном режиме, масштабируясь горизонтально по мере развития линейки продуктов.


Но в таком случае размер node_modules составил бы ~1.5Gb (суммарные зависимости всех пакетов монорепозитория). Что негативно отразилось бы на размере image, времени его загрузки в AWS ECR, и времени развертывания.


Фокусировка


Чтобы "урезать" ("сфокусировать") монорепозиторий для сборки, тестирования и развертывания одного конкретного приложения, достаточно найти подмножество пакетов в общем графе пакетов и переписать декларацию workspaces в корневом package.json непосредственно перед сборкой на CI.


#!/usr/bin/env nodeconst { spawnSync } = require('child_process');const { existsSync, promises: { readFile, writeFile } } = require('fs');const { join, dirname, relative, normalize } = require('path');const PACK_JSON_PATH = './package.json';(async (apps) => {    const packJSON = JSON.parse((await readFile(PACK_JSON_PATH)).toString());    await spawnSync('yarn', ['global', 'add', 'lerna'], { shell: true });    const locations = await listPackages(apps);    const [someLocation] = locations;    const basePath = findBasePath(someLocation);    // All paths should be relative to monorepo root    const workspaces = locations.map((loc) => normalize(relative(basePath, loc)));    packJSON.workspaces.packages = workspaces;    const packJSONStr = JSON.stringify(packJSON, undefined, 2);    await writeFile(PACK_JSON_PATH, `${packJSONStr}\n`);})(    process.argv.slice(2),);async function listPackages(apps = []) {    const filterOptions = apps.flatMap((app) => ['--scope', app]);    const { stdout } = await spawnSync(        'lerna',        ['ls', '-pa', '--include-dependencies', ...filterOptions],        { shell: true },    );    return String(stdout).split(/[\r\n]+/).filter(Boolean);}function findBasePath(packageLocation) {    return existsSync(join(packageLocation, 'lerna.json'))        ? packageLocation        : findBasePath(dirname(packageLocation));}

После "фокусировки" все команды (в том числе и changed) будут относится лишь к подмножетсву пакетов конкретного приложения.


Размер node_modules удалось снизить в среднем в 3 раза.


Fixed mode


Lerna fixed mode, отказ от lerna publish и артифактория позволили повысить стабильность и упростить логику pipeline.


Но как же быть с инкрементальностью сборок?


Инкремент


Для инкремента достаточно отслеживать изменения через команду lerna changed


lerna changed -a --include-merged-tags

Если изменений не обнаружено, то можно переиспользовать latest image приложения для развертывания и тестирования:


#!/usr/bin/env bashAPP=$1lerna-focus.js "${APP}"function nothing_changed() {    [[ -z "$(lerna changed -a --include-merged-tags || true)" ]]}function pull_latest_image() {...}function push_latest_image() {...}function tag_latest_with_version() {...}pull_latest_imageif nothing_changed; then    tag_latest_with_version    exitfibuild-app.sh "${APP}"if is-master.sh; then    push_latest_imagefi

Стало



Что дальше?


Сейчас активно набирают обороты такие решения как Nx: Extensible Dev Tools for Monorepos. Это предмет следующих разборов.


Если эта статья окажется полезной, то в следующей расскажу о горизонтальном масштабировании "на коленке" модульных тестов (Angular, Jest, ElasticSearch, Bamboo CI).

Подробнее..

AWS CLI через MFA

23.08.2020 16:12:18 | Автор: admin

Далее последует инструкция по настройке AWS MFA, и последющей установке и настройке AWS CLI.


К сожалению, у меня на эту обязательную процедуру ушла половина рабочего дня. Чтобы другим неуверенным пользователям AWS ;) как и я сам, не тратить драгоценное время на эту банальное, решил составить инструкцию.


Даже для sandbox аккаунта настройка MFA это, как правило, обязательное требование. У нас это так.


Настройка MFA


  1. Установите совместимое мобильное приложение
  2. Зайдите в AWS консоль
  3. My Security Credentials -> Assign MFA Device
  4. Virtual MFA Device
  5. Следуйте инструкциям на экране

  6. Виртуальное устройство готово

Установка AWS CLI


https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html


Настройка именованного профиля


https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html


  1. My Security Credentials -> Create access key
  2. Скопируйте ключ в буфер обмена. Он понадобится на следующем шаге
  3. $ aws configure --profile <your profile name>

AWS CLI через MFA


  1. Скопируйте ARN виртуального устройства
  2. aws sts get-session-token --profile <имя профиля> --serial-number <ARN виртуального устройства> --token-code <одноразовый пароль>
    Одноразовый пароль необходимо взять из мобильного приложения настроенного ранее.
  3. Команда выведет JSON, отдельные поля которго необходимо подставить в соответствующие переменные окржуения AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN

Я решил автоматизировать через ~/.bash_profile
Для разбора JSON данный скрипт требует jq.


#!/usr/bin/env bashaws_login() {    session=$(aws sts get-session-token "$@")    echo "${session}"    AWS_ACCESS_KEY_ID=$(echo "${session}" | jq -r '.Credentials.AccessKeyId')    export AWS_ACCESS_KEY_ID    AWS_SECRET_ACCESS_KEY=$(echo "${session}" | jq -r '.Credentials.SecretAccessKey')    export AWS_SECRET_ACCESS_KEY    AWS_SESSION_TOKEN=$(echo "${session}" | jq -r '.Credentials.SessionToken')    export AWS_SESSION_TOKEN}alias aws-login-dev='aws_login --profile <имя dev профиля> --serial-number <ARN виртуального устройства> --token-code 'alias aws-login-prod='aws_login --profile <имя prod профиля> --serial-number <ARN виртуального устройства> --token-code '

Использование:


$ aws-login-dev <одноразовый пароль>

Надеюсь данная инструкция поможет вам избежать пространных блужданий по официальной документации ;)

Подробнее..
Категории: Devops , Cli , Amazon web services , Aws , Shell , Mfa , Recipes

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru