В октябре Github запустил actions, которые позволяют выполнять CI, не отходя от кассы, в которой этот самый код хранится. Это действительно очень удобно. Как только кто-то отправляет pull request, или просто загружает новые изменения на сервер, или что-то еще специальное (список событий, к которым можно прикрутить actions может быть найден в официальной документации), сборка запускается. Также поддерживаются запланированные повторяющиеся задачи (по принципу cron). Можно создавать конвейеры действий, названные рабочими процессами (workflows). И все это прекрасно, и смахивает на светлое будущее — за исключением документации. Мне потребовалось более часа, чтобы выяснить, как создать контейнер со сторонними сервисами для тестирования приложения. Вот что мне удалось узнать. Пожалуйста, обратите внимание, что официальная документация — откровенно неуклюжая, неполная, а зачастую — и просто ошибочная. Стандартное действие CI (action) использует конфигурационные файлы с синтаксисом, очень похожим на тот, который используется CircleCI. Это просто старый добрый YAML, позволяющий настроить целевую ОС, среду, команды для выполнения и т. д. Сами действия получают уникальные имена, что позволяет ссылаться на другие действия и зависеть от них. Кроме того, конфигурация позволяет указать службы (services). Сервисы должны быть запущены где-то в облаке, и GH будет сопоставлять порты контейнера с портами, которые эти сервисы выставляют наружу, согласно конфигурации. Эта часть слабо освещена в официальной документации, и даже то, что описано, содержит ошибки. Вот рабочий пример конфигурации для проекта Elixir, требующего для тестирования сервисов RabbitMQ и Redis. Как можно видеть, тесты будут запущены на Ubuntu, используя Elixir v1.9.1. Сервисы описаны под ключом services, и здесь начинается чистый детектив. Физический порт, к которому будет привязан сервисный порт, случайным образом выбирается механизмом контейнеров во время выполнения и хранится во внутренней переменной оболочки с именем job.services.rabbitmq.ports[5672]. rabbitmq — это имя сервиса, как указано в этом файле в разделе services и 5672 — исходный порт. Внутренняя переменная имеет синтаксис ${{ foo }} и передается в переменную окружения RABBITMQ_PORT (последний блок настройки, под ключом env). В RABBITMQ_HOST — нужно положить имя службы, в точности как под ключом services. Теперь приложение может считывать переменные среды как обычно и порты будут правильно прокинуты. Вот так мы будем читать эти переменные среды из окружения (это конфиг для Elixir, для других языков будет что-то очень похожее). В файле проекта я создаю специальную среду (environment) :ci, чтобы различать конфигурацию для тестов, выполняемых в локальной среде, и для тех же тестов, выполняемых где-то там в облаке. Кроме того, в CI конвейере я запускаю dialyzer на своих исходниках. Поскольку он выполняется в контейнере, задача занимает некоторое время, потому что приходится перестраивать plts с нуля каждый раз. Именно поэтому я делаю это один раз в день, используя schedule вариант конфигурации. здесь code_quality — псевдоним задачи (task alias), объявленный в mix.exs Вот, в ообщем, и все, что нам нужно для счастливого тестирования проекта с внешними зависимостями в новом рабочем процессе Github. Удачной непрерывной интеграции! Principal Engineer