Читать в телеге. Когда-то там были посты не только от меня.
Поиск неактуальных веток git
Продолжаю прибираться в проекте. На этот раз цель — избавиться от никому не нужных веток, которые были уже вмержены или забыты.
Главный помощник в этом деле — команда git ls-remote --heads [repoUrl], которая позволяет получить список веток репозитория без его клонирования. Удобно, когда тебе нужно 5+ реп обработать.
Со списком веток на руках можно сделать запрос к баг-трекеру, чтобы получить статус соответствующего тикета (если ветка содержит его идентификатор, разумеется). Мне не повезло и у меня Jira, хорошо хоть к ней есть подобие клиента. А дальше — закрываем ручками то, что в финальном статусе (можно автоматизировать и удаление, но как-то стремно). Полностью скрипт можно посмотреть в gist.
Если надо почистить локальные ветки, которые уже были удалены в удаленном репозитории — есть плагин, который это делает в пару кликов (VCS → Git → Delete old branches…). Правда он скорее всего будет одноразовый: вы же будете держать репу в чистоте после генеральной уборки?
Про Slack
Долгое время я скептически относился к слаку, считая, что телега лучше. И для личного общения/ботов/перекидывания стикерами она все равно лучше. Но для работы слак оказался удобнее. В мае моя команда на него перешла и возвращаться в телегу вряд ли будет.
- Киллер-фича — это треды. Особенно, когда в чате дохрена людей и тебе не нужно читать ВСЕ. Вдобавок, когда обсуждается несколько тем одновременно, тележный чат быстро превращается в помойку. 100 непрочитанных сообщений в секунду будут читать только психи. Чаты на 100+ человек в телеге даже более-менее на 1 тему — это рак. Я вообще не понимаю, какая от них польза, особенно когда туда заходят новые люди с типовыми вопросами. Конечно, к тредам надо приучать, но как показала практика, через пару недель игры в злого полицейского останутся только совсем отъявленные “не такие как все”.
- Настоящее отключение уведомлений. Не показывать сереньким, не показывать сереньким в архиве, а просто не показывать никак. Полезно для всяких чатов поддержки: понадобилось тебе что-то, зашел, спросил, в треде тебе ответили (и на это приходят уведомления разумеется), потом об этом чате забыл.
- Настоящие имена. Мелочь, но до переезда были случаи, когда пишешь кому-то в личку по рабочему вопросу и даже его пола не знаешь, не то что имени.
- Выключил рабочий комп = выключил работу. Никаких переписок в полночь о том, что надо бы завтра какую-нибудь фигню сделать.
Но по стикерам иногда скучаешь… Радужные попугаи — это круто, но огромную рожу Гарольда уже не вбросишь.
Дебри рефлексии
Картинка с двумя стульями у меня родилась не просто так. Довольно типичная рабочая ситуация, в которую я вляпался на прошлой неделе. Ближе к концу я уже сел на третий стул пулл-реквестов и сделал их аж три штуки, но расскажу о дебрях, в которые зашел, пытаясь решить проблему через рефлексию.
Пусть у нас есть публичный метод, в котором надо немного подшаманить:
@Override
String exampleMethod(){
String firstPart = privateMethodToModify();
String secondPart = super.exampleMethod();
String thirdPart = anotherPrivateMethod();
return firstPart + secondPart + thirdPart;
}
Первая реакция будет: “Чё тут делать-то? Отнаследуйся да переопредели!”. Но есть нюанс: super будет уже не тот. А беспокоить дедушек в java нельзя.
Ок, может тогда получим указатели на exampleMethod и anotherPrivateMethod, и в месте вызова ручками все подергаем? Однако полиморфизм будет суров, и exampleMethod вызовется из дочернего класса, даже если указатель вы получали у родителя.
Что же делать? Сдаться и скопипастить дочерний класс, изменив в нем что нужно? Скорее всего да, но если вы хотите сидеть на пиках до конца, то есть еще один способ — MethodHandles. Это новый API, который не является заменой рефлексии, а дополнением к ней, в основном для всяких низкоуровневых манипуляций для разработчиков языков под JVM. С ним можно вызвать родительский exampleMethod для ребенка:
MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(Child.class, MethodHandles.lookup());
MethodHandle baseHandle = lookup.findSpecial(Parent.class, "exampleMethod", MethodType.methodType(String.class), Child.class);
return (String) baseHandle.invoke(child);
Но, как всегда, есть нюансы. Во-первых, если java меньше 9 версии, то придется еще и рефлексию использовать, чтобы в приватные методы залезть (выглядит стремно). Во-вторых, в kotlin это работать из коробки пока не будет без специальных флагов, но в 1.4 эти флаги включат по умолчанию. 100% интеропа такие 100%.
Посмотреть примеры подробнее можно github. Познакомиться с API подробнее — тут, а узнать, зачем оно вообще нужно — тут.
Случайная выборка из csv
Безо всяких питонов сделать случайную выборку данных из текстового файла можно командой shuf:
cat dataset.csv | shuf | head -n100 | tee random_sample.csv
Закрытие старых ревью в Upsource
К сожалению, теорию разбитых окон можно наблюдать и в разработке. Поэтому периодически стоит наводить чистоту в коде, трекерах задач (можно называть это модной фразой “груминг бэклога”) и прочих штуках.
Недавно дошли руки до того, чтобы почистить незакрытые ревью в Upsource. Поскольку в проекте их было более 400 штук, а из коробки такой фичи нет, то в мою коллекцию добавился еще один скрипт для автоматизации всякой дичи.
Внезапно, у Upsource есть API, но есть нюанс: названия методов придется угадывать самостоятельно по названию DTO. В остальном все относительно просто: берем список ревью по подобранному методом тыка запросу, и закрываем по одному. Все вместе можно посмотреть в gist.
Форматирование строки в python
Я тот еще консерватор и не очень люблю новое. Поэтому статьи про
"hello, {}!".format("world")
"hello, {0} and {0} again!".format("world")
"hello, {name}".format(name="world")
вызывали у меня легкое раздражение: я не видел никаких существенных преимуществ перед старым добрым "hello %s" % "world". Но пора бы очнуться от летаргического сна, и посмотреть на Python 3.6+ (выпущенный в 2016).
Там есть f"hello, {name}!", у которого в скобках будет полноценное выражение, т.е. можно сделать f"hello, {''.join(map(chr, [87, 111, 114, 108, 100]))}!". Этот вариант хорош для большинства случаев, особенно с учетом того, что почти во всех распространенных языках строковая интерполяция плюс-минус так и работает. Кроме Java, но она всегда была немного “особенной”.
Пример случая, когда такая интерполяция не очень уместна: фиксированный формат, который используется несколько раз и вынесен в “константу”. Например, у меня в скриптах довольно часто встречается такое:
XX_PATH = "http://%s/some/api/path/%s"
...
response = requests.get(XX_PATH % (host, param), ...)
Тут придется садиться на один из стульев: % или format.
Поиск удаленного в истории git
Искать в текущей версии любой дурак может, а как найти что-то, что было удалено в истории git?
git --no-pager grep -i "search term" $(git rev-list --all)
--no-pager уже может быть знаком по journalctl, -i, как и в обычном grep, говорит об неважности регистра, а вызов $(git rev-list --all) дает список всех ревизий.
Будет вывод в формате хэш коммита:/путь/к/файлу: совпавшая строка. А дальше переключаемся на нужную ревизию и ищем как раньше.
Что означает поле update в результатах update by query в Elasticsearch?
Смотрим в документацию:
updated
The number of documents that were successfully updated.
Внимание, вопрос: учитываются все затронутные документы или все измененные? Вроде как все затронутые, да и обновления могут быть без изменений документа (для того, чтобы обновить документ в индексе, например). Но хочется побольше уверенности.
Роемся в исходниках, недолгий поиск по ключевому слову updated приводит сначала к WorkerBulkByScrollTaskState, а потом к AbstractAsyncBulkByScrollAction:
switch (item.getOpType()) {
case CREATE:
case INDEX:
if (item.getResponse().getResult() == DocWriteResponse.Result.CREATED) {
worker.countCreated();
} else {
worker.countUpdated();
}
break;
case UPDATE:
worker.countUpdated();
break;
case DELETE:
worker.countDeleted();
break;
}
Тут видно, что в подсчете вообще не учитывается, был ли изменен документ — интересен только тип операции. Соответственно в поле updated ответа будет число всех затронутых документов, даже если они не менялись. Отличаться от total оно будет только тогда, когда создаются или удаляются документы. Т.е. total = updated + deleted + created.
Люблю Open Source продукты за то, что можно при желании разобраться почти в любой проблеме.
Из чего только не строят графы
Прикольная статья про то, как чувак строил графы по автодоплнению поисковой выдачи гугла ключевое слово vs. Перевод для нихтферштейнов.
Кто-то сделал даже страничку, чтобы поиграться самому. Запрос к гуглу идет напрямую из браузера, так что персонализация поиска скорее всего будет работать (BB is watching you).
Кто может залогинится на сервере по ssh?
Иногда при установке разных сервисов добавляются пользователи в систему. В некоторых случаях — с возможностью логина в систему со стандартным паролем (sic!). Чтобы найти таких редисок, можно выполнить
cat /etc/shadow | grep -v \*\: | grep -v \!\:
И еще потенциально пользователи отсюда могут залогиниться
ls -l /home
Для того, чтобы ограничить доступ, надо в конфиге sshd добавить строчку
AllowUsers user1 user2