ServerAdmin.ru
28.9K subscribers
305 photos
35 videos
13 files
2.63K links
Авторская информация о системном администрировании.

Информация о рекламе: @srv_admin_reklama_bot
Автор: @zeroxzed

Второй канал: @srv_admin_live
Сайт: serveradmin.ru
Download Telegram
​​Необычный софт посмотрел, аналогов которого раньше не видел. Git репозиторий с просмотром содержимого через консоль - Soft Serve (странное название 🤔). Есть пакеты под все системы. Также можно в докере запустить. Я пробовал и так, и так.

https://github.com/charmbracelet/soft-serve

Ставим на rpm-based:
# rpm -ivh https://github.com/charmbracelet/soft-serve/releases/download/v0.1.2/soft-serve_0.1.2_linux_amd64.rpm
Запускаем:
# soft
У вас должен стоять git, чтобы всё заработало. Вот готовая команда для запуска в докере.

По умолчанию Soft Serve слушает все интерфейсы, работает на порту 23231. Для подключения нужно использовать приватный ключ, который он положит в директорию ~/.ssh, название - soft_serve_server_ed25519.
# ssh localhost -p 23231 -i ~/.ssh/soft_serve_server_ed25519

Окажетесь в TUI. У вас там будет пусто, так как репозитории не настроены. Их через конфиг надо добавлять. Пример есть на github. Причём для настройки надо склонировать дефолтную репу config и запушить изменения настроек.

Посмотреть, как всё это будет работать в настроенном виде, можно в публичном репозитории по адресу git.charm.sh. Подключение по ssh, пароль пустой (без пароля).
# ssh git.charm.sh

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

#git
​​Вы когда-нибудь публиковали в паблик свои пароли по ошибке? Я сам ни разу не замечал за собой такое, но от разработчиков видел подобное много раз. Один знакомый умудрился в публичные репы github выложить Dockerfiles нод криптовалют со своими кредами.

Есть достаточно известный продукт gitguardian (https://gitguardian.com) для автоматического сканирования репозиториев и обнаружения там приватных данных: токенов, паролей, сертификатов. Настраивается он просто, так как доступна готовая интеграция с популярными сервисами. А сам он работает по модели SaaS или self-hosted (только за деньги).

Есть бесплатная версия для небольших команд. У неё одно простое ограничение - не более 25 разработчиков на проект. Разработчиком в данном контексте является активный контрибьютор, который сделал хотя бы один коммит в течении последних 90 дней. Ограничение достаточно лояльное, так что бесплатная версия подойдёт для широкого круга небольших команд.

#git #devops #security #бесплатно
​​Познакомился с отечественным сервисом git, условным аналогом github - https://gitflic.ru. Зарегистрировался там и на всякий случай перенёс несколько своих основных личных репозиториев. Я их всегда хранил на gitlab, но последнее время стал регулярно копировать локально, опасаясь блокировки или ещё каких-нибудь диверсий.

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

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

Надеюсь проект взлетит и наберёт популярность. Мне осталось придумать какой-нибудь наименее костыльный вариант для синхронизации трёх источников с кучей репозиториев - gitlab, gitflic, локальный ноут. У кого какие идеи, как это лучше организовать? Понятно, что можно просто скриптом забабахать, но может как-то по-другому можно?

#git #бесплатно #отечественное
​​Всем хорошо известен продукт Gitlab, который закрывает несколько базовых потребностей современной разработки: хранение кода, сборка и деплой, хранение образов. И всё это реализовано в единой платформе.

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

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

Gitea - легковесная Open Source-система для управления Git-репозиториями, которую можно развернуть на своем сервере. Отличает её простота установки и настройки, низкие требования к производительности. В самом простом случае Gitea может использовать базу данных SQLite.

Drone CI - популярная система непрерывной интеграции, написанная на Go. Также отличается легковесностью (docker образ ~24Мб), простым синтаксисом yaml файлов для сборки. Легко интегрируется с любым git репозиторием и хранилищем docker образов. Отлично подходит для знакомства с ci/cd на основе docker контейнеров.

Docker Registry 2.0 - вариант локального registry от самой компании Docker. Тоже очень простой продукт с минимальными требованиями к железу. Размер docker контейнера, в котором он запускается 8Мб. При желании к нему можно установить веб интерфейс - docker-registry-ui.

Указанный набор программ связывается между собой в единую систему, обеспечивая базовый функционал по разработке и доставке софта на базе docker контейнеров. Drone CI расширяет функционал с помощью плагинов. Например, плагин для отправки уведомлений в telegram, запуска удалённых команд по ssh, выполнения webhook и т.д.

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

Gitea - https://github.com/go-gitea/gitea
Drone - https://github.com/harness/drone
Registry 2.0 - https://hub.docker.com/_/registry

#docker #devops #git
​​Ранее я уже рассказывал про Gitea. Это легковесная Open Source-система для управления Git-репозиториями, которую можно развернуть на своем сервере. Её отличает простота установки и настройки, а так же возможность использования базы sqlite. Для небольших проектов этого достаточно, зато никаких хлопот с обслуживанием и бэкапами.

Попробовать Gitea можно, запустив в Docker. Я решил ещё раз написать про неё, потому что попробовал тему - Gitea Modern. С ней совсем другой вид и впечатление от веб интерфейса. Внешний вид приятнее и функциональнее.

Вот готовый docker-compose.yml для запуска с этой темой по умолчанию:

version: "3"

networks:
 gitea:
  external: false

services:
 server:
  image: gitea/gitea:1.16.8
  container_name: gitea
  environment:
   - USER_UID=1000
   - USER_GID=1000
   - GITEA_CUSTOM=/data/gitea
  restart: always
  networks:
   - gitea
  volumes:
   - ./gitea:/data
   - /etc/timezone:/etc/timezone:ro
   - /etc/localtime:/etc/localtime:ro
  ports:
   - "3000:3000"
   - "222:22"

Я добавил переменную GITEA_CUSTOM, а её содержимое попадает в volume, подключенный к ./gitea. Теперь надо сделать папку /data/gitea/public/css и положить туда файл темы theme-gitea-modern.css. Новую тему также надо добавить в конфиг /data/gitea/conf/app.ini. Добавляем в самый конец:
[ui]
THEMES = gitea,gitea-modern
DEFAULT_THEME = gitea-modern

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

#git #devops
​​Часто возникают ситуации, когда хочется вести контроль версий конфигурационных файлов в системе Linux. Это может быть актуально не только, когда сервером пользуются несколько человек, но и для единственного администратора. Простейший способ контроля версий - создание копии файла перед редактированием.

Я предлагаю автоматизировать эту процедуру и использовать систему контроля версий git для этих целей на примере ОС Debian или Ubuntu. Для любого другого дистрибутива всё настраивается аналогично.

Устанавливаем git:
# apt install git

Создаём файл конфигурации git:
# touch ~/.gitconfig
Добавляем информацию о пользователе:
[user]
name = root
email = root@debian11-websrv

Создадим репозиторий для конфигов и сразу переименуем его, чтобы потом не путать с другими:
# cd ~ && git init && mv .git system.git

Добавим в корень системы / информацию о том, где находится репозиторий:
# echo "gitdir: /root/system.git" > /.git

Устанавливаем права доступа:
# chmod 600 ~/.gitconfig
# chmod 600 /.git
# chmod 700 /root/system.git

Настраиваем с помощью списка exclude директории, за которыми будет следить git. Данном случае это /etc. Для этого добавляем в файл ~/system.git/info/exclude пару строк:
/*
!/etc

У нас всё готово. Делаем первый commit:
# cd ~
# git add -A && git commit -m 'Создание репозитория debian11-websrv'

Добавляем в системный cron выполнение задания с коммитом каждую минуту:
* * * * * root cd / && git add -A && git commit -m "Cron commit" > /dev/null

Теперь можно что-то изменить в конфигурации, дождаться коммита и проверить изменения. Например, добавьте пользователя:
# adduser user01

Посмотрите изменения:
# git log --follow -p /etc/passwd

Я показал пример, как это может быть настроено. При желании, можно добавить и другие директории. Например, /var/spool/cron/crontabs или /usr/local/etc. Последнее было очень актуально в Freebsd, но давно уже не вижу, чтобы там что-то полезное хранилось.

Дополнительно неплохо было бы хранить информацию об установленных пакетах. Все изменения отражаются в лог файле пакетного менеджера, но добавлять в git логи не очень хорошая идея. Удобнее было бы выгружать список пакетов с какой-то периодичностью в файл в /etc/ примерно так:
# rpm -qa > /etc/packages.list
# dpkg -l > /etc/packages.list

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

#linux #git
Простой и быстрый способ забэкапить свои репозитории из github или gitlab. Я не стал тут изобретать велосипед, а просто поискал информацию. Нашёл много всяких программ и утилит на различных языках программирования. Всё это отмёл, пока не нашёл простые bash скрипты. Для меня было очевидно, что для решения этой задачи достаточно bash и api указанных сервисов.

В итоге воспользовался вот этим репозиторием:
https://github.com/alexadam/repo-backup
Очень простое и рабочее решение.

Я всё своё храню в Gitlab. Сходил в настройки, выдал токен с разрешениями на чтение api и репозиториев. И использовал его в скрипте:

#!/bin/sh

dirname=gitlab-backup-$(date "+%Y-%m-%d-%H-%M-%S")
mkdir "$dirname"
cd $dirname

privateToken=YOUR_ACCESS_TOKEN
userName=YOUR_GITLAB_USERNAME

curl --header "Private-Token: $privateToken" \
"https://gitlab.com/api/v4/users/$userName/projects" \
| jq -r '.[] | .id, .name' \
| while IFS= read projectId; read projectName; do
curl --header "Private-Token: $privateToken" \
"https://gitlab.com/api/v4/projects/$projectId/repository/archive.zip" \
--output $projectName.zip
done

echo Done! All files downloaded here: $(pwd)

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

Я до этого git clone делал по списку. Подом думаю, что я ерундой страдаю. В итоге забрал себе этот скрипт, который работает в разы быстрее и проще.

#backup #git #devops
​​В рамках темы по синхронизации файлов расскажу про необычный инструмент на основе git - git-annex. Это система управления файлами без индексации содержимого. С помощью git индексируются только имена, пути файлов и хэш содержимого. Для использования необходимо понимание работы git. Эта система больше всего подойдёт для индивидуального или семейного хранения файлов в разных хранилищах.

Я не буду расписывать, как эта штука работает, так как она довольно заморочена, а сразу покажу на примере. Так будет понятнее. Программа есть под все популярные ОС, но под Windows всё ещё в бете, так что запускать лучше Linux версию в WSL. В Debian и Ubuntu ставим стандартно из базовой репы:
# apt install git-annex

Переходим в директорию /mnt/annex, где хранятся какие-то файлы и инициализируем там репозиторий, пометив его как server:
# git config --global user.email "root@serveradmin.ru"
# git config --global user.name "Serveradmin"
# git init .
# git annex init 'server'

Добавляем файлы в репу:
# git annex add .
# git commit -m "Добавил новые файлы"

Все файлы превратились в символьные ссылки, а их содержимое уехало .git/annex/objects.

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

На ноутбуке копируем репозиторий с сервера и сразу синхронизируем:
# git clone ssh://user@1.2.3.4/mnt/annex
# git annex init 'notebook'
# git annex sync

Мы получили на ноутбуке копию репозитория, но вместо файлов у нас битые ссылки. Так и должно быть. Если вы хотите синхронизировать все реальные файлы, то сделать это можно так:
# git annex get .
А если только один файл, то так:
# git annex get docker-iptables.sh

Когда у вас несколько устройств работают с репозиторием, то узнать, где физически находится файл можно так:
# git annex whereis file.txt
whereis file.txt (2 copies)
    01f4d93-480af476 -- server [origin]
    13f223a-e75b6a69 -- notebook [here]

Вернёмся на сервер и добавим в него информацию о ноуте:
# git remote add notebook ssh://user@4.3.2.1/mnt/annex
# git annex sync

Теперь репозитории знают друг о друге. На ноутбук он был клонирован с сервера, а на сервер мы вручную добавили ноутбук. Добавим новый файл в репозиторий с ноутбука:
# git annex add nout.txt
# git commit -m "add nout.txt"

Проверим его местонахождение:
# git annex whereis nout.txt
whereis nout.txt (1 copy) 
  637597c-3f54f8746 -- notebook [here]

Он только на ноутбуке. Идём на сервер, делаем там синхронизацию и поверяем местонахождение файла:
# git annex sync
# git annex get nout.txt
# git annex whereis nout.txt
whereis nout.txt (2 copies) 
  6307597c-3f54f5ff8746 -- [notebook]
   edeab032-7091559a6ca4 -- server [here]

Теперь он располагается в двух местах.

Надеюсь, у меня получилось хоть немного показать, как эта штука работает. У неё много настроек. Можно очень гибко указать какие файлы по весу, количеству копий, типу папок, имени, типу содержимого нужны в каждом репозитории. Есть преднастроенные группы: backup, archive, client, transfer. Например, можно принудительно скачивать все файлы при синхронизации:
# git annex sync --content

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

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

Сайт / Обучалка

#git #fileserver
Несколько полезных команд из использования GIT. Накопилось на небольшую заметку, поэтому решил опубликовать. Тут ничего особенного, если это можно сказать про GIT. Реально, это довольно сложная система с кучей всяких параметров, ситуаций, вариантов действия и т.д. Эта система контроля версий, про которую можно книги писать. И, наверное, пишут (я не читал).

Простая команда, про которую я узнал случайно. Она объединяет добавление изменённых файлов и коммит. Удобно и пригодится всем:

# git commit -am "changed two files"

Заменяет:

# git add .
# git commit -m "changed two files"

Можно сразу алиас сделать, чтобы ещё упростить команду:

# git config --global alias.cam "commit -am" 
# git cam "changed two files"

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

# git config --global alias.st status 
# git st

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

# git stash

Файлы вернулись в первоначальный вид, а все изменения сохранены отдельно. Если захотите вернуть их, то выполните:

# git stash pop

Все изменения вернулись в файлы. У stash много дополнительных параметров. Если будете пользоваться, то почитайте.

Если посмотреть изменения большого репозитория, то в консоли всё это не очень читаемо, так как много информации. Можно немного упростить:

# git log --oneline

Так читается намного лучше.

📌 Теперь краткая инструкция, как перенести репозиторий из одного удалённого сервера в другой.

# git clone --bare git@server01.local:testrepo.git
# cd testrepo.git && git fetch origin
# git remote add new-origin git@server02.local:testrepo.git
# git push --mirror new-origin
# git remote rm origin && git remote rename new-origin origin

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

#git
Я уже не раз делал заметки про Gitea - легковесную open source систему для управления git репозиториями. Изначально это был просто веб интерфейс для git, который можно было сделать с помощью соответствующей темы очень похожей на github. Написана Gitea на Go и по своей сути это просто бинарник, поэтому с установкой и настройкой никаких проблем. Бонусом гошечки идёт очень быстрая работа и отзывчивость веб интерфейса.

Как я уже сказал, Gitea была создана просто для работы с кодом, без какой-либо автоматизации. Для построения конвейера для сборки, деплоя и хранения образов я предлагал связку: Gitea + Drone CI + Docker Registry.

С недавних пор в Gitea завезли Gitea Actions, которые полностью совместимы с GitHub Actions. То есть она превратилась в полноценную CI/CD систему с собственным Package Registry и Gitea Runner. Получился аналог gitlab на минималках.

Подробно посмотреть обзор всех этих возможностей, а также инструкцию по настройке, можно в серии из двух видеороликов:
▶️ Gitea-01: домашний github (+ actions)
▶️ Gitea-02: домашний github (+ actions)

#git #devops #cicd
​​На канале было много обучающего материала про git. Как курсы, так и тренажёры, игры. Но при этом я ни разу не упомянул про книгу с переводом на русский язык Pro Git (авторы Scott Chacon и Ben Straub). Она полностью переведена и выложена на сайте:

https://git-scm.com/book/ru/v2

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

Сейчас существует не так много обучающего материала в текстовом виде, да ещё в веб версии, с которой легко и удобно работать - искать, читать. Книга даже в электронном виде не так удобна для взаимодействия. А вот если она выложена по главам на сайте, то это уже другое дело. Да ещё и полностью бесплатно.

Там же, кстати, есть и pdf, и epub версия. И так же есть git версия на github:
https://github.com/progit/progit2-ru

#обучение #git
​​🎓 Мне давно скидывали ссылку на небольшой курс по GIT на youtube для начинающих. Там с самых азов даётся инфа для тех, кто с GIT никогда не работал. Только сейчас дошли руки посмотреть. Вроде неплохо сделано, послушал введение. Комментарии хорошие, просмотров много. Весь материал разбит на небольшие ролики по 5-7 минут, где разбирается одна тема. Если вы совсем нулёвый по этой теме, то это для вас:

Базовый курс по Git

Заодно решил подбить всю эту тему с обучением GIT.

▪️ Обзорное видео по GIT от авторского ютуб канала Артема Матяшова - Git. Большой практический выпуск. Автор очень хорошо объясняет. Если прям ничего про GIT не знаете, рекомендую начать с него. Хороший монтаж, подача, оформление. Я почти все его видео с удовольствием смотрел. У него их немного.

🔥Очень прикольная визуальная обучалка по основам GIT - LearnGitBranching. Она поддерживается, регулярно обновляется. Качественно сделана. Есть русский язык.

▪️ Ещё одна игра по обучению GIT - Oh My Git! Она попроще learnGitBranching, но в целом тоже неплохая.

▪️ Бесплатный курс по основам от Слёрм - Git для начинающих.

▪️ Бесплатные курсы по основам GIT на Stepik: Основы работы с Git и Самоучитель по GIT.

▪️ Более продвинутый курс с погружением в нюансы, ориентированный на разработчиков - Git курс от сообщества javascript.ru.

▪️ Бесплатная книга на русском языке - Pro Git book.

В завершении добавлю мотивирующий текст, который я написал ещё 3 года назад здесь на канале, если вы всё ещё сомневаетесь, надо ли вам разбираться с GIT или нет:

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

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

Теперь всегда, прежде чем начать писать какой-то скрипт или более ли менее большой конфиг, создаю репозиторий под это дело и все пушу туда. Я обычно использую облачный gitlab и свой локальный для приватных данных. Gitlab - мое личное предпочтение. Вы можете использовать любой бесплатный сервис. Их сейчас полно развелось. 

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

С тех пор я неизменно верен себе. Все нужные скрипты, конфиги в обязательном порядке хранятся в репозиториях git, как локально на ноуте, так и на удалённых сервисах.

#обучение #git #подборка
Git - довольно замороченная система контроля версий. Не очень понятно, за что она завоевала такую популярность. Разобраться с ней - пуд соли съесть. Какие-то самые простые вещи делать не сложно. А вот если чуть копнуть, то там тёмный лес.

Об этом тёмном лесе даже сайт замутили с говорящим названием - ohshitgit.com. В англоязычном интернете он довольно популярен. Не раз видел на него ссылки. У нас особо не встречал. Есть две русские версии этого сайта.

⇨ Одна матерная - https://ohshitgit.com/ru
⇨ Вторая без мата - https://dangitgit.com/ru

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

📌 Журнал всех действий в репозитории:
# git reflog
Откатится на какое-то событие из этого списка:
# git reset HEAD@{index}

📌 Добавить что-то в уже созданный коммит:
# git add .
# git commit --amend --no-edit

📌 Изменить сообщение последнего коммита:
# git commit --amend

📌 Перенести последний коммит из мастер ветки в новую ветку:
# git branch какое-то-имя-новой-ветки
# git reset HEAD~ --hard
# git checkout какое-то-имя-новой-ветки

📌 Отменить коммит, который был 5 коммитов назад:
# git log
# git revert [сохранённый хеш]

📌 Отменить изменения в файле:
# git log
# git checkout [сохранённый хеш] -- путь/к/файлу
# git commit -m "Ого, теперь не придётся копипастить, чтобы отменить изменения!"

Для тех, кто не видел, оставлю ссылку на публикацию с подбором хороших обучающих материалов по git:

🎓 Подборка бесплатных материалов по GIT

#обучение #git