Мы хотим максимально упростить интеграцию с Braintree и считаем, что одним из лучших способов сделать это является предоставление клиентских библиотек. Они помогают нашим клиентам быстро интегрироваться, предоставляют идиоматический код на языке по выбору клиента и позволяют нам лучше устранять проблемы интеграции.
Хотя предоставление клиентских библиотек имеет много преимуществ, одной из проблем является уверенность в том, что новые функции, которые мы разрабатываем для нашего шлюза, не сломают ни одну из выпущенных библиотек. На момент написания этой статьи у нас было 51 поддерживаемая версия библиотеки на 5 языках, что делает поддержание обратной совместимости несложной задачей.
обзор
Наша команда разработчиков имеет два основных направления работы: наш шлюз и наши клиентские библиотеки . По мере того, как мы разрабатываем новые функции для шлюза, мы поддерживаем как ветки master, так и ветки release в git. Ветка релиза используется для исправления ошибок и отслеживания кода, который в данный момент находится в производстве. В основной ветке происходит разработка новых функций.
Рабочий процесс разработки для клиентских библиотек более сложен, но для целей этого поста мы примем ветку master и release для каждого языка. Мы также помечаем наш код перед каждым выпуском. Более подробное обсуждение процесса выпуска нашей клиентской библиотеки смотрите в нашем предыдущем посте .
Общее решение
Одним словом, наше решение для поддержки этих библиотек — это сборка. Мы верим в постоянную интеграцию и хотели бы автоматизировать все, что можем. Мы используем hudson в качестве инструмента непрерывной интеграции и имеем автоматизированные тестовые наборы для нашего шлюза и всех наших клиентских библиотек.
Сборки, которые мы создали для наших клиентских библиотек, делятся на две группы: небольшие сборки с быстрым циклом обратной связи и большие сборки с более медленным циклом обратной связи.
Небольшие сборки
С каждой клиентской библиотекой связана одна небольшая сборка. Эта сборка запускает тесты на главной ветви клиентской библиотеки по отношению к главной ветви нашего кода шлюза. Эти сборки запускаются двумя способами:
- Каждый раз, когда код отправляется в главную ветвь клиентской библиотеки , для этой библиотеки запускается небольшая сборка.
- При каждом отправке временного кода в главную ветвь шлюза выполняется сборка шлюза , за которой следует небольшая сборка для каждой библиотеки.
Эти сборки имеют быстрые циклы обратной связи и дают нам уверенность в том, что код, который мы отправляем на шлюз или в наши клиентские библиотеки, не нарушил никакой текущей функциональности.
Big Build
Небольшие сборки хороши тем, что они запускаются быстро и часто, но у нас все еще есть проблема. Что, если это небольшое изменение, которое мы вводим в шлюз, разрушает библиотеку python, которую мы выпустили 3 месяца назад?
Одной из стратегий будет создание небольшой сборки для каждого тега каждой клиентской библиотеки, которую мы когда-либо выпускали, но это имеет несколько проблем. Во-первых, это раздражающее руководство и займет значительное время. Во-вторых, это приводит к огромному количеству сборок. С 50+ сборками, сбои могут затеряться в шуме.
Подход, который мы выбрали, состоял в том, чтобы создать одну большую сборку, которая работает ночью. Короче говоря, эта сборка запускает каждый тег каждой клиентской библиотеки в соответствии с ветвями релиза и мастера шлюза. Ниже мы шаг за шагом пройдемся по сборке и покажем несколько примеров кода в ruby. Мы постарались сделать код достаточно простым, чтобы вы могли следовать за ним практически без знания рубина.
Сначала мы клонируем репозитории git для шлюза и каждой клиентской библиотеки.
sh "git clone git@gitserver:gateway.git" LANGUAGES.each do |language| sh "git clone git@gitserver:client-library-#{language}.git" end
Затем мы переключаемся на ветку выпуска шлюза, перейдя в каталог шлюза и выбрав соответствующую ветку.
Dir.chdir(GATEWAY_ROOT) do sh "git checkout origin/release" end
Для каждой библиотеки мы теперь перебираем все выпущенные теги, используем git, чтобы проверить этот тег и запустить задачу rake по умолчанию. Эта задача rake запускает набор тестов этой библиотеки для ветки выпуска шлюза.
def release_tags `git tag -l` end def clean_working_copy sh "git reset --hard" sh "git clean -d -x -f" end LANGUAGES.each do |language| Dir.chdir("client-library-#{language}") do release_tags.each do |tag| clean_working_copy sh "git checkout #{tag}" sh "rake" end end
Наконец, мы переключаемся на главную ветвь шлюза и повторяем предыдущий шаг.
Dir.chdir(GATEWAY_ROOT) do sh "git checkout origin/master" end
Управление временем сборки
Мы называем это большой сборкой по причине — для ее запуска требуется много времени. Когда мы выпускаем обновления для наших клиентских библиотек, время сборки будет расти. Один из способов контроля времени сборки — это управление списком завершений для каждой клиентской библиотеки. В этот список входят теги, которые не используются в нашей изолированной программной среде или рабочей среде. Поскольку эти библиотеки не поддерживаются, и мы предварительно блокируем их от доступа к шлюзу, нет необходимости проверять эти теги в нашей сборке.
def end_of_life_tag?(language, tag) { "dotnet" => %w[tag_1 tag_2], "java" => %w[tag_1 tag_2], "php" => %w[tag_1 tag_2], "python" => %w[tag_1 tag_2], "ruby" => %w[tag_1 tag_2] }.fetch(language, []).include?(tag) end LANGUAGES.each do |language| Dir.chdir("client-library-#{language}") do release_tags.each do |tag| next if end_of_life_tag?(language, tag) clean_working_copy sh "git checkout #{tag}" sh "rake" end end end
Заворачивать
Такой подход дает нам уверенность в том, что выпускаемый нами код поддерживает обратную совместимость со всеми нашими поддерживаемыми клиентскими библиотеками. Сочетание маленьких и больших сборок дает нам как быструю обратную связь, так и исчерпывающее тестирование всех версий библиотеки. Мы считаем, что это гибкое решение, которое может использовать любой, кто предоставляет клиентские библиотеки для облегчения взаимодействия с их API.