Александр Кошелев
Александр Кошелев Python-разработчик

svn:externals и django - дружба на век

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

Я всегда использую транк джанги, поскольку релизится она не часто, а для примера можете сравнить насколько отличается текущая её версия от последнего релиза. Ага, как-будто вообще разные фреймворки в некоторых местах:)

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

Обычно я устанавливаю джангу в общедоступный PYTHONPATH, например /usr/lib/python/site-packages/ и все проекты используют именно этот, один инстанс. И тут ключевое слово все. Когда на одном хосте расположены несколько проектов, то сценарий описанный мною ранее, перестает радовать и практически не возможен.

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

А как удобно, сделал ln -s в site-packages и джанга установлена. Но не практично!

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

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

Этими же мыслями я поделился и на конференции, и после уже в кулуарной беседе Вячеслав Буханцов рассказал, как они в своей компании решают схожую проблему - они активно применяют svn:externals и “кладут” джангу рядом с проектом, чтобы он цеплял именно “свою” версию. Спасибо Вячеслав за наводку!

Суть заключается в том, что svn может сам подгружать в рабочую копию код из другого репозитория или из другой иерархии каталогов одного. Надо только прописать откуда и куда что класть. Причем очень важной особенностью является возможность указать ревизию “стороннего” кода, которая нужна.

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

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

Расскажу как получилось у меня.

Что такое свойства svn и как их устанавливать я думаю вызнаете (если нет, то прочтите). Для удобства я создал файл externals в корне джанговского проекта, где описал нужные мне ссылки на библиотеки. Вот:

django -r7210 http://code.djangoproject.com/svn/django/trunk/django
lib1 -r1401 http://path/to/lib1/repo
lib2 -r1390 http://path/to/lib2/repo

Вначале идет имя директории, куда будет выполнен чекаут, далее номер ревизии и путь в репозитории. Потом я этот файл использую для установки свойства svn:externals корня рабочей копии вот так:

$ svn propset svn:externals -F externals .

Далее обычные:

$ svn up && svn commit -m "adding externals property to project"

И мы видим как svn делают чекаут нужных ревизии. В итоге получается что-то вроде такой схемы каталогов:

- project
  - app1
  - app2
  ...
  - django
  - lib1
  - lib2
  ...
  - __init__.py
  - manage.py
  - settings.py
  - urls.py

Поскольку директория, где лежит проект идет первой в sys.path, то именно локальные версии джанги и других библиотек будут использованы. Теперь можно это проект размещать где угодно и всегда он будет находится в правильном “своём” окружении.

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

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

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

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

А вы используете подобную технику в вашем процессе разработки?

comments powered by Disqus