Непрерывная интеграция
Возможность сразу получать обратную связь о том, над чем мы работаем, должна быть одной из важнейших характеристик разработки программного обеспечения. Представьте, что мы внесли одно изменение в исходный код и должны ждать 2 недели, чтобы узнать, не сломало ли оно что-нибудь. Ох! Это был бы кошмар! Здесь команде поможет непрерывная интеграция: она позволяет получать немедленную и частую обратную связь о состоянии разрабатываемого продукта.
Мартин Фаулер определяет непрерывную интеграцию как практику разработки программного обеспечения, при которой участники команды часто интегрируют свою работу: обычно каждый участник делает это как минимум раз в день, что приводит к нескольким интеграциям в день. Каждая интеграция проверяется автоматической сборкой (включая тестирование), чтобы как можно быстрее обнаружить ошибки интеграции. Многие команды считают, что такой подход значительно сокращает количество проблем с интеграцией и позволяет быстрее разрабатывать согласованное программное обеспечение.
В следующих подразделах мы рассмотрим два инструмента непрерывной интеграции: GitHub Actions и Circle CI, и используем их с примером приложения на Crystal.
Эти инструменты не только позволят нам собирать и тестировать код при каждом изменении исходных файлов, но и развертывать результат (если сборка прошла успешно), использовать автоматические сборки и, например, тестировать код на разных платформах.
Пример приложения
В качестве примера приложения мы воспользуемся игрой «Жизнь» Конвея. Точнее, мы возьмем только первые итерации решения ката «Игра „Жизнь“ Конвея» с использованием разработки через тестирование (TDD).
Обратите внимание: в самом примере мы не будем использовать TDD, а лишь представим, что приведенный код — результат первых итераций.
Также важно упомянуть, что для создания приложения мы используем crystal init.
Вот реализация:
class Location
getter x : Int32
getter y : Int32
def self.random
Location.new(Random.rand(10), Random.rand(10))
end
def initialize(@x, @y)
end
end
class World
@living_cells : Array(Location)
def self.empty
new
end
def initialize(living_cells = [] of Location)
@living_cells = living_cells
end
def set_living_at(a_location)
@living_cells << a_location
end
def is_empty?
@living_cells.size == 0
end
end
А вот спецификации:
require "./spec_helper"
describe "a new world" do
it "should be empty" do
world = World.new
world.is_empty?.should be_true
end
end
describe "an empty world" do
it "should not be empty after adding a cell" do
world = World.empty
world.set_living_at(Location.random)
world.is_empty?.should be_false
end
end
Это всё, что нужно для примеров непрерывной интеграции! Начнем!
Непрерывная интеграция: пошаговое руководство
Вот список того, чего мы хотим добиться:
- Собрать и запустить спецификации с помощью трех разных версий Crystal:
- последней
- ночной
- 0.31.1 (с использованием образа Docker)
- Установить пакеты shards
- Установить бинарные зависимости
- Использовать базу данных (например, MySQL)
- Кэшировать зависимости, чтобы ускорить сборку
Выберите, с чего продолжить:
- Я хочу использовать GitHub Actions
- Я хочу использовать CircleCI
To the extent possible under law, the persons who contributed to this workhave waived
all copyright and related or neighboring rights to this workby associating CC0 with it.
https://crystal-lang.org/reference/1.2/guides/ci/index.html