Advent of Code
4 поста
4 поста
4 поста
1 пост
6 постов
21 пост
В силу определённых обстоятельств – периодически стал возникать вопрос, в каких временных рамках я занимался тем или иным проектом. И готового ответа на него у меня не было – как-то не приходило в голову фиксировать подобную информацию.
Но есть же git – ведь он поможет? Ведь поможет, да?
Возникла тривиальная идея – вытащить эту информацию из гит-лога. Первый и последний коммиты – плюс-минус дадут требуемые рамки. Достаточно найти все коммиты определённого автора (в данном случае – себя) и взять первый+последний.
Чтобы не запоминать (или не искать в истории) довольно длинную команду – её удобно запихать в пользовательский alias (именно гита, не оболочки). Например – в ~/.gitconfig прописать (в секции [alias]):
В частном случае – это уже работает достаточно удобно и выглядит примерно так:
user@host:~/single-repo$ git first-last-by-author dmitry f887e8aa55 Dmitry 2022-06-26
6f7a97ded7 Dmitry 2021-04-08
Когда репозиторий для поиска – один. Или парочка. Или… Нет, когда их парочка сотен – становится грустно. Идея явно требует развития!
Для решения задачи поиска по набору репозиториев – пришлось воспользоваться функциями оболочки. Они у меня сложены в отдельном файле ~/.config/bash/bash_functions.
На базе команды для обновления репозиториев и приведённого выше гит-алиаса для поиска по автору – сделал такую вот bash-функцию:
Теперь поиск работает и в общем случае (в каталоге multi-repo лежит куча git-репозиториев):
user@host:~/multi-repo$ git-first-last-commit-by-author dmitry
8f1d4ae Dmitry 2022-08-16
83a8f89c Dmitry 2023-02-15
Видно, что здесь порядок сортировки по датам – прямой. Мне так удобней, но всегда можно использовать ключ --reverse для sort внутри git-first-last-commit-by-author().
Очередная задачка уровня Easy, но с довольно низким показателем Acceptance (44.4%). Что выражается в достаточно обширном наборе граничных случаев, некоторые из которых делают больно 🙂
Было желание сделать за один проход по исходной строке (без выделения дополнительно памяти). Вроде даже получилось, если ориентироваться на статистику запуска с LeetCode.
Надоело бороться с "Цитатой" в бесплодных попытках использовать её для оформления кода, которое можно был бы читать без самовоспламенения пердячей тяги. Поэтому тут будет пикча, в нормальном виде - в оригинале.
Тоже забавная задачка – со “сломанной клавиатурой”. Решил в ней Arrays.binarySearch использовать для поиска буквы слова в наборе “сломанных клавишей”, да и чтобы не забыть о его (метода) существовании в целом.
Судя по статистике – нормально получилось, в общем-то.
class Solution {
public int canBeTypedWords(String text, String brokenLetters) {
var brL = brokenLetters.toCharArray();
Arrays.sort(brL);
var words = text.split(" ");
int canTypeCnt = 0;
for (var word : words) {
boolean canType = true;
for (int i = 0; i < word.length(); i++) {
if (Arrays.binarySearch(brL, word.charAt(i)) >= 0) {
canType = false;
break;
}
}
if (canType) {
canTypeCnt++;
}
}
return canTypeCnt;
}
}
Success:
Runtime:2 ms, faster than 91.93% of Java online submissions.
Memory Usage:42.7 MB, less than 23.23% of Java online submissions.
Решил слегка “причесать” и обобщить свой опыт по этому вопросу. Чтобы удобней было пользоваться, как этакой краткой инструкцией, а не искать разрозненные ответы на SO и т.п.
Задача проста – сделать удобно.
Взять GitLab (много где используется, как self-hosted) и выкачать с него все репозитории проекта. Чтобы можно было локально работать с ними, не загружая каждый отдельно.
Обновлять эти репозитории одной командой
Тут всё несложно – надо получить сам список и по нему всё скачать. Удобнее это делать с использованием ssh ключа (добавляется в GitLab через веб-интерфейс). Но можно и git credentials включить. Сам список достаточно просто в браузере открыть и сохранить projects.json (название по умолчанию).
Список репозиториев: https://your-gitlab-host/api/v4/projects?per_page=1000
Или список для группы проектов: https://your-gitlab-host/api/v4/groups/{group-id}/projects?per_page=1000
group-id можно тоже через веб-интерфейс посмотреть, примерно так:
Выкачать (в текущий каталог) репозитории по списку из файла: jq -r '.[].ssh_url_to_repo' < /path/to/projects.json | xargs -n1 git clone. Можно аналоги утилиты jq использовать, мне она привычней.
Если авторизация по ssh-ключу не настроена – придется из json вместо поля ssh_url_to_repo извлекать поле http_url_to_repo. Можно хранить логин/пароль для GitLab в ~/.git-credentials (там они лежат в открытом виде!).
Первая часть на этом закончена. На очереди – обновление репозиториев.
Здесь всё ещё проще – достаточно одной команды. Перейти в каталог со склонированными репозиториями и выполнить:
find . -type d -name '.git' \
| xargs -n1 -- sh -c \
'cd "${0}/.." && echo \
&& basename -sa $(git rev-parse --show-toplevel) \
&& git pull --all'
Найдёт все каталоги с директорией .git внутри (а это и есть репозитории) и обновит их через git pull --all. Соответственно, каталоги, не являющиеся репозиториями, будут проигнорированы.
Приятная задачка выпала в поиске – хоть и easy, а сделать интересно. Какая-то “практическая применимость” в ней видится.
Вроде, там где-то и обратная проблема была – перевод записи арабскими цифрами в запись римскими.
В общем-то, список валидных префиксов там прямо в условии задачи описан, особо выдумывать тут ничего не требуется.
Можно ещё вариант со switch-case сделать, но там с валидацией порядка цифр в записи будут вопросы. Реализация со словарём префиксов их снимает автоматом.
Возникла потребность настраивать по-разному конфиги гита для разных проектов (email контактный поменять, хотя бы, или хук на прогон тестов повесить).
Ну и понятно, что как-то эти конфиги подкладывать туда-сюда – не слишком увлекательно. К счастью, в свежих версиях гита – есть инклюды. В том числе – по условию – includeIf. Вот ими и стоит воспользоваться.
Общая концепция такова – в основном конфиге ~/.gitconfig – перечислить (под условиями) конфиги “зависимые”, а все специфичные настройки прописать уже в них. Ничего экстраординарного, в общем.
Примерно вот так у меня это выглядит, по условию включается конфиг из файла gitconfig-project-1:
[user]
name = Dmitry
email = dimio@dimio.org
[includeIf "gitdir:~/project-1/repos/"]
path = ~/.config/git/gitconfig-project-1
Важный момент – директорию после gitdir: нужно указывать без отделения пробелом.
А вот так – выглядит сам файл ~/.config/git/gitconfig-project-1
[user]
email = dmitry@dimio.org
[core]
hooksPath = ~/.config/git/hooks/
autocrlf = input
sshCommand = "ssh -F ${HOME}/.ssh/config-project-1"
Тут уже как раз и переопределены email, хуки (для этого проекта активирован автопрогон тестов в pre-push хуке) и конфиг ssh (там специфические настройки для доступа к репозиторию по ключу).
Раз уж зашла речь о разделении конфигов и о доступе – можно сразу настройки доступа по http(s) разбить немного.
Включаются они так: git config --global credential.helper store. И после этого лежат в ~/.git-credentials в открытом виде!
Разделить их для разных репозиториев внутри одного хоста – можно так (на примере github): git config --global credential.github.com.useHttpPath true
И тогда в .git-credentials будут отдельные записи лежать:
https://<USERNAME>:<PASSWORD>@github.com/path/to/repo1.git
https://<USERNAME>:<PASSWORD>@github.com/path/to/repo2.git
Но, безусловно, удобней (да и безопасней, пожалуй) – пользоваться доступом с ssh ключами.
Долгое время пользовался ssh-туннелем для «разблокировки» некоторых сетевых ресурсов, доступ к которым был ограничен для IP Российского диапазона (ну, драйверы там скачать с сайта производителя, и т.п.).
Но — всему приходит конец. Моему терпению — тоже. Надоело постоянно переключать потребителей на работу через прокси (privoxy), чтобы завернуть трафик в socks-прокси туннеля. TOR стали прикручивать, что привело к очень долгому поиску мостов. И лень моя победила (в очередной раз).
Очевидно — нужен был способ попроще, да ещё — без особых затрат. Ставить VPN сервер (в том или ином виде) на VPS — хотелось ещё меньше, чем кидать туннель. Всякие платные прокси (типа Mullvad) — они удобны, позволяют выбрать «точку выхода», но — надо изыскивать возможность их оплатить. Бесплатные — обычно, работают так себе.
Тут я вспомнил, что есть же CloudFlare и его WARP, причем — в бесплатной версии, да со всей мощью клаудфлэровских серверов, да без ограничений скорости, да ещё и — должен, теоретически, работать через нативный клиент WireGuard. «Надо попробовать» — подумал Штирлиц.
В целом, процесс достаточно понятный, на современных системах — проблем быть не должно (на современных — это где Network Manager поддерживает WireGuard, иначе — можно и через консоль поднимать/гасить интерфейс VPN, но это менее удобно).
Сгенерировать конфиг для WARP — консольной утилитой или онлайн-генератором на её базе. Ещё есть неофициальный CLI, мне он не пригодился.
Полученный конфиг можно сунуть в /etc/wireguard/ и попробовать поднять через systemctl start wg-quick@название-конфига.conf. Но вряд ли это заработает, конфиг придется править.
А чтобы не править его, передергивая интерфейс вручную из консоли, — проще сразу добавить в Network Manager. Подобрать рабочие настройки там, затем — перенести в конфиг. И получить основной (через графический интерфейс) и резервный (из консоли) способы работы с WARP. Чем и займёмся.
Настройка Network Manager
Тут всё тоже несложно: ПКМ на аплете менеджера — Соединения VPN — Настроить VPN — WireGuard — ‘+’ (добавить новое соединение).
Дальше настроить по картинке (скопировать значения из файла конфига):
Но вот адрес для Endpoint придется поподбирать — на разных провайдерах работают разные IP. Я прошелся по адресам с 162.159.193.0 по 162.159.193.10, пока не нашел работающий.
В целом — соединение уже должно работать (надо только Address в Параметры IPv4 вписать, как ниже показано), проверить можно через 2ip.ru. Вот такая картина у меня:
Подключается не всегда с первого раза, бывает, нужно несколько раз вкл/выкл VPN.
Но радость была бы не полной, если бы не ещё одна чудесная возможность WireGuard — он позволяет для соединения легко поменять адрес DNS сервера. Это не защитит от утечки DNS на сто процентов, но позволяет обойти блок, выставляемый на некоторых ресурсах на основе географической принадлежности DNS сервера (а клаудфлэровский — в Москве).
В настройки IPv4 надо вписать гугловый DNS, в настройки IPv6 — можно по аналогии (IPv6-адрес DNS ищется в гугле же), а можно и обойтись только IPv4.
WireGuard Google public DNS
Вот теперь «разблокировка» должна работать нормально.
Из плюсов — VPN включается сразу на уровне системы, приложения ходят через него. Не надо, как в случае с ssh-туннелем, заворачивать приложения для работы с через туннель.
Второй плюс — ровно тем же способом можно включить WARP на Android. Клиент WireGuard под Android есть (ставил из F-Droid), надо только поправить сгенерированный конфиг по аналогии (как в заметке), затем — импортировать его в клиенте на телефоне. Всё!
Следующим шагом — можно настроить подключение WARP на роутере, чтобы всю внутреннюю сеть пускать в интернет через VPN. Я на своём Keenetic планирую так и сделать, но это «уже совсем другая история».
После долгих праздничных выходных — приходится «разогревать» голову задачками с высоким Acceptance. И даже они — не сразу заходят. Со скрипом.
class Solution { public static int[] buildArray(int[] nums) { int[] ans = new int[nums.length]; for (int i = 0; i < nums.length; i++) { ans[i] = nums[nums[i]]; } return ans; } }