В данной заметке описано, как и для чего мне пригодился файл .gitignore при копировании файлов через rsync.
Для удаленного заказчика необходимо поставлять программный код в виде релизов. Поставка осуществляется архивом tar.gz из ветки мастер в закрытый контур, где нет ни исходящего интернета, ни системы контроля версий. Из возможностей – только утилиты Linux.
Задача же состоит в следующем: в корневой директории сайта должно быть ровно то, что есть в архиве tar.gz с релизом, а всё остальное должно быть удалено, за исключением файлов ядра битрикса и того, что описано в гитигнор.
Можно было бы поступить просто: распаковывать архив на целевом сервере, выполняя rsync -av source_code/ target_dir
. Но в таком случае файлы, которые в процессе разработки были удалены, не будут удаляться из целевой директории и эта директория превратится в помойку с неактуальными данными. Такой вариант не подходит.
Ключ –delete у rsync идеально же решит возникшую проблему, который будет удалять по пути назначения всё то, чего нет в исходной директории. Но есть одна проблема…
В исходном репозитории исполнителя хранится только разрабатываемый программный код проекта на битрикс. Соответственно, в .gitignore прописаны исключения для множества директорий (/bitrix, /upload, /crm и прочие, которые не хранятся в гите). При выполнении команды rsync -av --delete source_code/ target_dir
на сервере заказчика в целевом каталоге окажется только релизный код, а ядро битрикса, пользовательский аплоад и прочие директории вне гита будут удалены.
Именно на этом момент и возникла идея, что для rsync нужно указать исключения для файлов, чтобы удалялись только изменения относительно релизного архива, а все директории, которые вне гита, оставались нетронутыми. Все эти директории уже указаны в исходном .gitignore, осталось только разобраться, как это применить вместе с rsync.
В данном случае используется расширенная версия ключей exclude\include под названием filter:--filter=':- /path/to/.gitignore'
Filter принимает на вход файл, в котором указаны директории для копирования или исключения. Но т.к. файл в формате .gitignore, для этого используется конструкция “:- “. Обращаю внимание, что после знака “-” обязательно следует пробел, который указывает выполнять слияние файлов из gitignore понятный для –filter формат.
Все файлы в .gitignore должны начинаться со слеша, чтобы rsync исключал все файлы с такими названиями не рекурсивно по всей директории, а относительно корня. Например, если указать файл test без слеша в гитигнор, то rsync будет искать по всему каталогу такой файл и исключать из копирования, в результате чего могут быть различные неприятные сюрпризы в виде “недоехавших” файлов.
Также возникает нюанс: в .gitignore попадает вся директория с ядром bitrix, но из неё средствами gitignore может исключаться какой-либо файл, например, /bitrix/.access.php, а его необходимо также копировать по адресу назначения в случае изменения. Для этого используется ключ include, который явно указывает скопировать необходимый файл, а под синтаксис .gitignore (!/bitrix/.access.php) данный файл не попадает, а потому исключение не сработает и файл окажется по адресу назначения.
Итого для примера как выглядит .gitignore:
/.idea/
/.git/*
/.pki/*
/_test/
/bitrix/*
!/bitrix/access.php
/builds/*
/desktop_app/*
/upload/*
Таким образом, отправка релиза в архиве к заказчику выполняется следующим образом:
- Архив передается заказчику любым удобным способом, они размещают его на целевых серверах
- Создается временная директория на сервере:
mkdir -p /tmp/release_`date +%d.%m.%Y`
- Во временную директорию распаковывается архив с релизом: tar -xzvf
/tmp/master.tar.gz -C /tmp/release_`date +%d.%m.%Y`
- И выполняется копирование в целевую директорию:
rsync -av --delete --include='bitrix/.access.php' --filter=':- /tmp/release_date +%d.%m.%Y
/master/.gitignore' /tmp/release_date +%d.%m.%Y
/master/ /mnt/u01/ext_www/target-site.ru
В результате на сайт доезжают только те изменения, которые были в исходном архиве, а всё остальное удаляется, кроме файлов, указанных в .gitignore.
Заключение
Данный способ мне кажется жутко кустарным и костыльным – так не стоит не делать где-то в продакшене, если есть доступ в гит. Тем не менее, в моём случае при текущих условиях иных более корректных или простых вариантов я придумать не смог, а потому способ рабочий и выполняет поставленную задачу.
P.S. Прочие возможности при работе с filter у rsync можно найти в man rsync, там много разных интересных примеров на различные ситуации.