
Flutter & GitLab CI/CD. Подготовка и настройка физической машины
В мире мобильной разработки, где скорость и стабильность доставки приложений определяют успех, выбор инструментов для автоматизации становится особенно важным. Работая мобильным разработчиком в TAGES более пяти лет, я убедился, что использование self‑hosted решений дает значительные преимущества в гибкости, надежности и контроле над процессами. Одним из таких мощных инструментов является GitLab — платформа, которая позволяет не только управлять кодом, но и настраивать полноценные CI/CD‑процессы.
Эта статья станет первым шагом на пути к полной автоматизации сборки и доставки Flutter-приложений. Мы разберем, как превратить вашу физическую машину в надежный инструмент для автоматизации. В конце статьи у вас будет готовая машина, которая сможет запускать и обрабатывать задачи из GitLab.
Для удобства материал разделен на несколько ключевых частей. Вы можете ознакомиться со статьей последовательно или сразу перейти к интересующему вас разделу:
Что подтолкнуло нас на self-hosted решение? Делюсь своими выводами о том, какие преимущества дает self-hosted подход по сравнению с облачными решениями.
Почему Gitlab CI/CD? Разбираю, почему выбор пал именно на GitLab и чем эта платформа выделяется среди конкурентов.
Gitlab Runner. Docker vs Shell Executor. Рассказываю, какие типы исполнителей GitLab Runner подходят для различных задач.
Выбор физической машины. Даю рекомендации по выбору физической машины для выполнения CI/CD задач.
Настройка режима сна и пробуждения системы. Описал шаги по настройке системы, которые помогают создать стабильную среду.
Создание и настройка Gitlab Runner. Делюсь информацией о том, как настроить GitLab Runner на вашей физической машине.
Подготовка окружения. Описал шаги по установке необходимых зависимостей, которые пригодятся для сборки приложений.
Заключение. Подвел итоги, какие результаты можно достичь с настроенным self-hosted решением.
Что подтолкнуло нас на self-hosted решение?
На первый взгляд, использование готовых CI/CD-сервисов, таких как Codemagic, TeamCity, Appcircle и других, кажется более удобным вариантом. Они предлагают готовую инфраструктуру, приятный интерфейс и минимальные усилия для настройки.
Однако, как это часто бывает с облачными сервисами, стабильность и удобство оказались не вечными. Изначально в нашей команде для автоматизации процессов CI/CD использовался Visual Studio App Center. Он полностью нас устраивал, пока не стало известно, что 31 марта 2025 года сервис прекращает свою работу.
Эта новость заставила нас пересмотреть подход к организации CI/CD-процессов. Мы начали задумываться о долгосрочном решении, которое не будет зависеть от политики сторонних сервисов, поэтому были определены главные критерии, которые повлияли на наше решение:
Контроль над инфраструктурой. Вы сами решаете, на каком оборудовании будет выполняться пайплайн (англ. pipeline), какие ресурсы использовать и как настраивать окружение.
Скорость и производительность. Облачные сервисы часто работают в условиях общего пула ресурсов, где вы делите мощности с другими пользователями. Это может привести к задержкам в выполнении пайплайнов, особенно в периоды высокой нагрузки на сервис.
Конфиденциальность и безопасность. В некоторых случаях использование облачных CI/CD‑сервисов может быть проблематичным с точки зрения конфиденциальности данных. Например, если ваш проект содержит чувствительную информацию или закрытый код, передача этих данных на сторонние сервисы может быть нежелательной.
Внутренняя дистрибуция. Одной из ключевых функций, которая нам очень нравилась в Microsoft App Center — внутренняя дистрибуция тестовых сборок вне магазинов, таких как TestFlight и Google Play. К сожалению, мы не смогли найти другой сервис, который предоставлял бы аналогичную функциональность.
Сотрудничество. Многие популярные CI/CD‑сервисы — это иностранные компании, и оплата их услуг может стать серьезным препятствием из‑за сложностей с международными транзакциями.
Эти критерии стали для нас очевидным аргументом в пользу self‑hosted подхода. Несмотря на то, что он требует больше усилий для настройки и поддержки, мы уверены, что долгосрочные преимущества перевешивают все возможные трудности.
Почему Gitlab CI/CD?
GitLab CI/CD представляет собой мощный инструмент для автоматизации процессов разработки, а его особенности позволяют значительно повысить эффективность работы команды. Рассмотрим ключевые преимущества:
GitLab поддерживает методологии CI/CD, что позволяет автоматически тестировать и развертывать код. Это сокращает time‑to‑market и снижает вероятность ошибок.
GitLab можно установить на собственных серверах, что дает полный контроль над кодом и данными. Это повышает безопасность и конфиденциальность, позволяя настраивать окружение под специфические требования вашей команды.
GitLab легко интегрируется с различными инструментами и внешними сервисами, что позволяет расширить его функциональность и адаптировать под текущие рабочие процессы.
Эти ключевые аспекты делают GitLab CI/CD привлекательным выбором для команд, стремящихся к повышению безопасности, качества и скорости разработки.
Gitlab Runner. Docker vs. Shell executor
Gitlab Runner — это незаменимый помощник, который выполняет задачи из пайплайнов CI/CD в GitLab. Когда вы настраиваете автоматизацию (например, тестирование, сборку или деплой проекта), именно он подключается к серверу, забирает задания и запускает их в разных средах.
Когда вы настраиваете процесс CI/CD для Flutter‑приложений в GitLab, важно правильно выбрать среду для выполнения скриптов в Gitlab Runner, поскольку требования для Android и iOS значительно отличаются. Давайте разберем, какой тип исполнителя подойдет для каждой платформы.
Android. Docker executor – лучший выбор
Для сборки Android-приложений на GitLab оптимальным выбором является использование Docker executor. Вот почему:
Изолированная среда. Docker предоставляет контейнеризированное окружение, где каждый контейнер работает независимо от системы. Это упрощает настройку и делает процессы сборки воспроизводимыми вне зависимости от вашего окружения.
Готовые образы. Существуют Docker-образы, которые уже содержат все необходимое для Android, включая Flutter SDK, Android SDK и другие инструменты. Это позволяет сократить время на подготовку среды и ускорить выполнение задач.
Кроссплатформенность. Docker может работать на любых системах, будь то Windows, Linux или macOS, что делает его универсальным решением для сборки Android-приложений.
iOS. Shell Executor на macOS – единственный путь
С iOS ситуация сложнее. Для сборки приложений под эту платформу придется использовать Shell executor на macOS. Причины этого заключаются в следующих аспектах:
Экосистема Apple. iOS-приложения требуют Xcode – официального инструмента разработки от Apple, который доступен исключительно на macOS. Ни одна другая система не предоставляет полноценной поддержки Xcode.
Ограничения Docker. Хотя Docker отлично подходит для Android, он не может полноценно эмулировать среду разработки для iOS. В macOS нет возможности запускать Xcode внутри контейнера Docker из-за архитектурных и лицензионных ограничений.
Про виртуализацию macOS
В ходе изучения вопроса я также неоднократно сталкивался с Docker‑OSX — решением, которое позволяет запускать macOS в контейнере Docker. На первый взгляд оно может показаться удобным и применимым, но на практике это «билет в один конец». Вот почему:
Лицензирование macOS. Использование macOS в виртуальных средах, таких как Docker‑OSX, возможно только на физических устройствах Apple в соответствии с лицензионным соглашением Apple. Это делает использование Docker‑OSX для iOS сборки на стороннем оборудовании юридически спорным.
Ограниченная поддержка. Docker‑OSX не гарантирует корректную работу всех процессов, что делает его непригодным для реальных проектов.
Сложность настройки. Чтобы добиться приемлемой производительности и стабильности в Docker‑OSX, потребуется значительное время и глубокие технические знания. Даже после этого результат может оказаться непригодным для реальных задач.
Из‑за этих ограничений Docker‑OSX нельзя считать надежным инструментом для сборки iOS‑приложений. Чтобы предостеречь от возможных проблем, я настоятельно рекомендую обходить данный инструмент стороной.
Стоит отметить, что существует более сложный альтернативный инструмент — Lume. Он также позволяет запускать виртуальные машины macOS, но использует иной принцип работы. Если ваша задача связана с виртуализацией, я рекомендую в первую очередь обратить внимание на этот вариант.
Выбор физической машины
Первый шаг на пути к автоматизации — это выбор физической машины. Основные моменты при выборе:
Выбор железа. Если у вас уже есть Mac, отлично! Для CI/CD лучше всего подойдут Mac mini или Mac Studio. Они достаточно компактны и производительны. Однако, обязательно убедитесь, что на машине достаточно оперативной памяти и свободного места на диске.
Обновление macOS. Удостоверьтесь, что на машине установлена последняя стабильная версия macOS. Это важно, так как устаревшая версия может не поддерживать свежие версии Xcode или других инструментов.
Стабильное интернет‑соединение. GitLab Runner будет постоянно взаимодействовать с вашим GitLab‑репозиторием, а также загружать зависимости и пакеты. Убедитесь, что соединение надёжное и с высокой пропускной способностью.
Рекомендации по выбору
Для универсальности. Если вам необходимо собирать как iOS, так и Android-приложения, используйте машину на macOS. Это позволит вам решать любые задачи в рамках CI/CD.
Для одной платформы. Если вы работаете только с Android или задачами, не связанными с iOS, выбирайте машину на Linux. Это позволит сэкономить и упростить настройку окружений.
История о Mac Pro 6.1, который стал героем
Когда-то давным-давно, в 2013 году, с конвейера Apple сошёл Mac Pro 6.1, известный в народе как «чёрный цилиндр». В своё время он был героем: мощным, стильным и передовым. Когда-то он рендерил фильмы и создавал сложные 3D-модели, а теперь... он стал GitLab Runner-ом.
Если вы когда-нибудь видели его вживую, то знаете: он действительно похож на элитный увлажнитель воздуха для офиса:

Несмотря на все шутки и его возраст, этот Mac Pro 6.1 справляется отлично. Он тихо работает, раздает тепло (иногда даже слишком много) и, не без магических заклинаний, исправно выполняет свои задачи на последней версии macOS.
Настройка режима сна и пробуждения системы
Для корректной работы GitLab Runner на физической машине важно убедиться, что система не уходит в спящий режим и способна беспрепятственно запускать новые задачи. Если этого не настроить, задания могут быть приостановлены или вовсе не выполнены, что приведет к сбоям в работе CI/CD.
Шаг 1. Отключение запроса пароля при появлении заставки
Первое, что необходимо сделать — отключить запрос пароля при появлении заставки. Это важно, чтобы система не блокировала сессию после выключения дисплея или срабатывания заставки.
Для этого:
Откройте «Настройки» → «Экран блокировки».
Убедитесь, что у опции «Запрашивать пароль при появлении заставки или включении дисплея» стоит значение «Никогда».

Шаг 2. Настройка автоматического входа в систему
Чтобы GitLab Runner автоматически запускался после перезагрузки системы, нужно настроить автоматический вход в систему под учетной записью, где он установлен.
Перейдите в Настройки → Пароль для входа.
Включите тумблер «Автоматически входить после перезагрузки».

Далее нужно выбрать пользователя, за которым будет происходить автоматический вход:
Перейдите в «Настройки» → «Пользователи и группы».
Выберите в опции «Автоматически входить как» пользователя системы, под которым работает GitLab Runner.
Если система требует подтверждения, введите пароль текущего пользователя.

Эти шаги гарантируют, что после перезапуска машины пользователь автоматически войдет в систему, а GitLab Runner начнет свою работу.
Шаг 3. Настройки энергосбережения
Следующим шагом нужно убедиться, что спящий режим отключен и не блокирует пользовательскую сессию, так как это может привести к приостановке задач Gitlab Runner.
Для этого:
Перейдите в «Настройки» → «Энергия».
Включите тумблер «Не переходить в режим сна автоматически при выключенном дисплее».
Включите тумблер «Перезапускать автоматически при сбое питания».

С этими настройками физическая машина будет стабильно работать в качестве Runner для GitLab CI/CD, автоматически обрабатывая новые задачи без необходимости постоянного контроля.
Шаг 4. Настройка связки ключей (Keychain)
При взаимодействии с Git на macOS может возникнуть проблема, связанная с использованием связки ключей (англ. Keychain). По умолчанию Git на macOS использует менеджер учетных данных credential.helper=osxkeychain, который сохраняет ваши учетные данные (например, логины, пароли и персональные токены) в связку ключей.
Если связка ключей будет заблокирована (например, из-за настроек безопасности), то процесс клонирования репозитория зависнет, так как система покажет диалоговое окно с запросом пароля для разблокировки связки ключей.
Чтобы избежать этой проблемы, нужно отключить использование credential.helper. Сделать это можно следующей командой:
git config --global --add credential.helper ''
После этого Git перестанет обращаться к связке ключей, а процесс клонирования репозитория больше не будет зависеть от ее состояния.
Для проверки текущего статуса credential.helper выполните команду:
git config credential.helper
Если в ответе будет выведена пустая строка, значит, использование связки ключей успешно отключено.
Создание и настройка Gitlab Runner
В этом разделе будут рассматриваться шаги создания и настройки GitLab Runner для macOS, так как в этой системе больше особенностей, чем в других. Вы всегда можете обратиться к официальной документации GitLab, чтобы создать и настроить его под вашу систему.
Вы можете либо пройти весь путь самостоятельно, следуя шагам, описанным ниже, либо воспользоваться предоставленным скриптом, который в интерактивном режиме выполнит все необходимые действия за вас:
setup.sh
#!/bin/bash
set -e
set -o pipefail
setup_keychain() {
echo "Managing Git credential.helper..."
echo "Checking the current status of credential.helper..."
current_helper=$(git config credential.helper)
if [[ -z $current_helper ]]; then
echo "credential.helper is currently disabled."
else
git config --global --add credential.helper ''
echo "credential.helper has been disabled globally."
fi
echo "Press Enter to return to the main menu."
read -r
}
setup_gitlab_runner() {
echo "Setting up and configuring GitLab Runner..."
echo "Step 1: Checking the current shell..."
echo "Your current shell is: $SHELL"
if [[ $SHELL != "/bin/bash" ]]; then
echo "Your current shell is not Bash. Changing to Bash..."
chsh -s /bin/bash
echo "Shell has been changed to Bash. Please restart your terminal for changes to take effect."
else
echo "Your shell is already Bash. Proceeding..."
fi
echo "Step 2: Installing GitLab Runner..."
if [[ -f /usr/local/bin/gitlab-runner ]]; then
echo "GitLab Runner is already installed."
else
echo "GitLab Runner is not installed. Proceeding with installation..."
sudo mkdir -p /usr/local/bin
arch=$(uname -m)
if [[ $arch == "x86_64" ]]; then
echo "Detected Intel-based system. Downloading GitLab Runner for Intel architecture..."
sudo curl --output /usr/local/bin/gitlab-runner "https://s3.amazonaws.com/gitlab-runner-downloads/latest/binaries/gitlab-runner-darwin-amd64"
elif [[ $arch == "arm64" ]]; then
echo "Detected ARM-based system. Downloading GitLab Runner for ARM architecture..."
sudo curl --output /usr/local/bin/gitlab-runner "https://s3.amazonaws.com/gitlab-runner-downloads/latest/binaries/gitlab-runner-darwin-arm64"
else
echo "Unsupported architecture: $arch"
exit 1
fi
sudo chmod +x /usr/local/bin/gitlab-runner
echo "Installing GitLab Runner as a service..."
gitlab-runner install
echo "GitLab Runner has been installed successfully."
fi
echo "Step 3: Configuring GitLab Runner..."
config_file="$HOME/.gitlab-runner/config.toml"
if [[ ! -f $config_file ]]; then
echo "Configuration file not found. Creating a new one..."
mkdir -p "$(dirname "$config_file")"
touch "$config_file"
fi
default_concurrent=10
echo "Enter the number of concurrent jobs for GitLab Runner (default is $default_concurrent):"
read -r user_concurrent
user_concurrent=${user_concurrent:-$default_concurrent}
if grep -q "^concurrent =" "$config_file"; then
echo "Updating concurrent value in the configuration file..."
sed -i '' "s/^concurrent = .*/concurrent = $user_concurrent/" "$config_file"
else
echo "Adding concurrent value to the configuration file..."
sed -i '' "1i\\
concurrent = $user_concurrent\\
" "$config_file"
fi
echo "Concurrent setting has been configured successfully."
echo "Step 4: Registering GitLab Runner..."
echo "Do you want to register GitLab Runner now? (y/n)"
read -r register_choice
if [[ "$register_choice" =~ ^[Yy]$ ]]; then
gitlab-runner register --executor "shell"
gitlab-runner start
echo "GitLab Runner has been started successfully."
else
echo "Runner registration skipped. You can register it manually later using 'gitlab-runner register'."
fi
echo "GitLab Runner setup and configuration completed."
echo "Press Enter to return to the main menu."
read -r
}
install_dependencies() {
check_command() {
command -v "$1" >/dev/null 2>&1
}
check_xcode() {
echo "Checking if Xcode is installed..."
if [ -d "/Applications/Xcode.app" ]; then
echo "✅ Xcode is already installed."
else
echo "❌ Xcode is not installed. Please install Xcode manually via the Mac App Store:"
echo "https://apps.apple.com/us/app/xcode/id497799835"
echo "Or download it from the Apple Developer portal:"
echo "https://developer.apple.com/download/all/?q=xcode"
exit 1
fi
echo "Setting Xcode path..."
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
echo "Xcode path set successfully."
echo "Running initial Xcode setup..."
sudo xcodebuild -runFirstLaunch
xcodebuild -downloadPlatform iOS
echo "Xcode initial setup completed successfully."
echo "✅ Xcode installation and setup completed. You're ready to start developing iOS applications!"
}
install_homebrew() {
echo "Checking if Homebrew is installed..."
if check_command brew; then
echo "✅ Homebrew is already installed."
else
echo "❌ Homebrew is not installed. Installing Homebrew..."
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
echo "export PATH=/opt/homebrew/bin:$PATH" >>~/.bash_profile && source "$HOME/.bash_profile"
echo "✅ Homebrew has been installed."
fi
}
install_rbenv() {
echo "Checking if rbenv is installed..."
if check_command rbenv; then
echo "✅ rbenv is already installed."
else
echo "❌ rbenv is not installed. Installing rbenv..."
brew install rbenv
echo "if which rbenv > /dev/null; then eval $(rbenv init -); fi" >>~/.bash_profile && source "$HOME/.bash_profile"
echo "✅ rbenv has been installed."
fi
}
install_ruby_version() {
default_version="3.3.5"
echo "Enter the Ruby version you want to install (default: $default_version):"
read -r ruby_version
ruby_version=${ruby_version:-$default_version}
echo "Checking if Ruby $ruby_version is already installed..."
if rbenv versions | grep -q "$ruby_version"; then
echo "✅ Ruby $ruby_version is already installed."
else
echo "Installing Ruby $ruby_version..."
rbenv install "$ruby_version"
rbenv global "$ruby_version"
ruby --version
echo "✅ Ruby $ruby_version has been installed and set as global version."
fi
}
install_cocoapods() {
echo "Checking if CocoaPods is installed..."
if check_command pod; then
echo "✅ CocoaPods is already installed."
else
echo "❌ CocoaPods is not installed. Installing CocoaPods..."
sudo gem install cocoapods -V
echo "Verifying CocoaPods installation..."
pod --version
echo "✅ CocoaPods has been installed and verified."
fi
}
install_fvm() {
echo "Checking if FVM is installed..."
if check_command fvm; then
echo "✅ FVM is already installed."
else
echo "❌ FVM is not installed. Installing FVM..."
brew tap leoafarias/fvm
brew install fvm
echo "Configuring environment variables..."
{
echo "# FVM configuration"
echo "export FVM_PATH=\$HOME/fvm"
echo "export PATH=\"\$PATH:\$FVM_PATH/default/bin:\$FVM_PATH/default/bin/cache/dart-sdk/bin:\$HOME/.pub-cache/bin\""
} >>~/.bash_profile
echo "Applying changes to the current session..."
source "$HOME/.bash_profile"
echo "Installing stable version of Flutter using FVM..."
fvm global stable --force
echo "Verifying Flutter installation..."
if [[ $(uname -m) == "arm64" ]]; then
echo "The system is on ARM architecture. Installing Rosetta..."
sudo softwareupdate --install-rosetta --agree-to-license
fi
flutter --version
echo "✅ FVM setup completed successfully!"
fi
}
install_jdk() {
default_jdk_version=17
echo "Enter the version of OpenJDK you want to install (default is $default_jdk_version):"
read -r jdk_version
jdk_version=${jdk_version:-$default_jdk_version}
echo "Checking if OpenJDK version $jdk_version is already installed..."
jdk_path="/Library/Java/JavaVirtualMachines/openjdk-${jdk_version}.jdk"
if [[ -d "$jdk_path" ]]; then
echo "✅ OpenJDK version $jdk_version is already installed at: $jdk_path"
else
echo "Installing OpenJDK version $jdk_version..."
brew install openjdk@"$jdk_version"
echo "Creating a symbolic link for OpenJDK $jdk_version..."
sudo ln -sfn "$(brew --prefix)"/opt/openjdk@"$jdk_version"/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk-"$jdk_version".jdk
/usr/libexec/java_home -V
echo "✅ OpenJDK $jdk_version installation and setup is complete!"
fi
}
install_android_sdk() {
echo "Checking if Android Command Line Tools is installed..."
if check_command sdkmanager; then
echo "✅ Android Command Line Tools is already installed."
else
echo "Installing Android Command Line Tools..."
brew install --cask android-commandlinetools
echo "Creating a symbolic link for Android Command Line Tools..."
mkdir -p "$HOME/Library/Android"
ln -sfn "$(brew --prefix)"/share/android-commandlinetools "$HOME/Library/Android/sdk"
if ! grep -q "ANDROID_HOME" ~/.bash_profile; then
echo "Setting up environment variables for Android SDK..."
{
echo "# Android SDK"
echo "export ANDROID_HOME=\$HOME/Library/Android/sdk"
echo "export PATH=\$PATH:\$ANDROID_HOME/emulator:\$ANDROID_HOME/tools:\$ANDROID_HOME/tools/bin:\$ANDROID_HOME/platform-tools"
} >>~/.bash_profile
source "$HOME/.bash_profile"
echo "✅ Environment variables configured successfully."
fi
fi
default_platform="34"
default_build_tools="34.0.0"
echo "Enter the Android platform version to install (default: $default_platform): "
read -r platform_version
platform_version=${platform_version:-$default_platform}
echo "Enter the Android build-tools version to install (default: $default_build_tools): "
read -r build_tools_version
build_tools_version=${build_tools_version:-$default_build_tools}
echo "Installing Android SDK components..."
yes | sdkmanager 'platform-tools' "platforms;android-$platform_version" "build-tools;$build_tools_version"
echo "✅ Android SDK installation completed successfully."
}
echo "Installing required dependencies..."
check_xcode
install_homebrew
install_rbenv
install_ruby_version
install_cocoapods
install_fvm
install_jdk
install_android_sdk
echo "Verifying installation..."
echo "Homebrew version: $(brew --version | head -n 1)"
echo "rbenv version: $(rbenv --version)"
echo "Ruby version: $(ruby --version)"
echo "CocoaPods version: $(pod --version)"
echo "FVM version: $(fvm --version)"
echo "Required dependencies have been successfully installed."
echo "Press Enter to return to the main menu."
read -r
}
show_menu() {
clear
echo "============================================"
echo " GitLab Runner Setup Script "
echo "============================================"
echo "Please select an option:"
echo "1. Manage git credential.helper"
echo "2. Install and configure GitLab Runner"
echo "3. Install required dependencies for build Flutter apps"
echo "4. Exit"
echo "============================================"
}
while true; do
show_menu
read -rp "Enter your choice [1-4]: " choice
case $choice in
1)
setup_keychain
;;
2)
setup_gitlab_runner
;;
3)
install_dependencies
;;
4)
echo "Exiting the script. Goodbye!"
exit 0
;;
*)
echo "Invalid option. Please choose a valid option (1-4)."
echo "Press Enter to try again."
read -r
;;
esac
done
main_menu
Сохраните скрипт с именем setup.sh, откройте терминал и выполните команду, чтобы запустить его:
chmod +x setup.sh && ./setup.sh
После этого следуйте шагам в статье и выполняется поочередно действия, которые будет запрашивать скрипт.
Шаг 1. Изменение системной оболочки на Bash
Перед началом установки необходимо убедиться, что системной оболочкой по умолчанию является Bash. Это важно, поскольку GitLab Runner не поддерживает работу с оболочкой Zsh.
Для того, чтобы проверить текущую оболочку можно открыть терминал и выполнить команду:
echo $SHELL
Если текущая оболочка отличается от Bash (обычно /bin/bash), то необходимо ее изменить с помощью команды:
chsh -s /bin/bash
После этого следует перезапустить терминал.
Шаг 2. Установка GitLab Runner
Сначала нужно загрузить двоичный файл.
Для систем на базе Intel:
sudo curl --output /usr/local/bin/gitlab-runner "https://s3.dualstack.us-east-1.amazonaws.com/gitlab-runner-downloads/latest/binaries/gitlab-runner-darwin-amd64"
Для систем на базе Apple Silicon:
sudo curl --output /usr/local/bin/gitlab-runner "https://s3.dualstack.us-east-1.amazonaws.com/gitlab-runner-downloads/latest/binaries/gitlab-runner-darwin-arm64"
Далее необходимо сделать этот файл исполняемым:
sudo chmod +x /usr/local/bin/gitlab-runner
Теперь необходимо установить Gitlab Runner как системную службу, чтобы он мог автоматически запускаться после перезагрузки системы:
gitlab-runner install
Шаг 3. Настройка параметров в config.toml
По умолчанию GitLab Runner обрабатывает задачи последовательно. То есть выполняет по одной задаче за раз, ожидая завершения текущей задачи перед началом следующей, поэтому для повышения производительности требуется отредактировать файл конфигурации.
Для этого откройте файл ~/.gitlab-runner/config.toml в текстовом редакторе и добавьте/измените в начале файла параметр concurrent:
concurrent = 10
Это значение определяет максимальное количество задач, которые Gitlab Runner может выполнять одновременно. При необходимости настройку можно корректировать в зависимости от аппаратных ресурсов машины.
Шаг 4. Регистрация GitLab Runner
Регистрация должна производиться в пользовательском режиме, так как это обеспечивает более гибкое управление и ограничивает доступ к системным ресурсам, минимизируя потенциальные риски. Выполните регистрацию Gitlab Runner, используя команду:
gitlab-runner register
В процессе регистрации вам потребуется выбрать тип исполнителя (англ. executor), который будет использоваться для выполнения задач. GitLab поддерживает несколько типов исполнителей, но для Flutter приложений важно учитывать различия между платформами Android и iOS:
Android. Рекомендуется использовать Docker executor. Docker предоставляет изолированную, воспроизводимую среду, которая идеально подходит для сборки Android приложений.
iOS. Сборка приложений для iOS требует использования Shell executor, который работает напрямую с системой. Это связано с тем, что инструменты разработки для iOS (например, Xcode) доступны только на macOS, и их работа в контейнеризированной среде невозможна.
Если ваш проект требует сборки для обеих платформ, вы можете зарегистрировать два разных исполнителя на одной машине. Во второй части статьи мы обеспечим совместимость для каждого типа исполнителя.
После регистрации запустите Gitlab Runner с помощью команды:
gitlab-runner start
Если всё выполнено правильно, проверьте, что Gitlab Runner активен и назначен для использования в проекте.
Для этого в интерфейсе GitLab перейдите в Settings → CI/CD → Runners, в разделе Other available runners найдите ранее созданный Gitlab Runner и нажмите на кнопку Enable for this project/group, чтобы сделать его доступным.
Подготовка окружения
В этом разделе мы рассмотрим ключевые шаги, которые помогут обеспечить стабильную работу GitLab Runner на физической машине.
Установка Xcode и его компонентов
Для разработки и сборки iOS-приложений Xcode – это обязательный инструмент. Вы можете установить его двумя способами: через Mac App Store или скачав с портала разработчиков Apple.
После установки убедитесь, что Xcode находится в папке /Applications. Чтобы система и инструменты командной строки знали, где находится Xcode, необходимо вручную указать его путь, выполнив команду:
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
Далее необходимо выполнить первичную настройку компонентов Xcode. Выполните следующие команды:
sudo xcodebuild -runFirstLaunch
xcodebuild -downloadPlatform iOS
Эта команды проинициализируют Xcode, примут лицензионные соглашения и выполнят настройку инструментов разработки. Следующим шагом будет установка менеджера зависимостей CocoaPods:
sudo gem install cocoapods -V
После успешной установки убедитесь, что команда pod доступна, выполнив:
pod --version
Если команда выводит версию CocoaPods, значит, установка прошла успешно.
Установка Ruby и настройка rbenv
Для настройки CI/CD в процессе работы с Flutter нам потребуется Ruby. На большинстве систем (особенно macOS) предустановленная версия Ruby может быть устаревшей и не поддерживать актуальные версии зависимостей, которые требуются Fastlane или другим инструментам.
Использование rbenv решает эти проблемы, позволяя вам настроить современную и стабильную версию Ruby, которая будет изолирована от системной.
Первым делом нужно установить Homebrew — популярный менеджер пакетов для macOS. Он понадобится нам для установки rbenv. В терминале выполните следующие команды:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
echo "export PATH=/opt/homebrew/bin:$PATH" >>~/.bash_profile
source ~/.bash_profile
Эта команда скачивает и запускает официальный установочный скрипт Homebrew. Следуйте инструкциям в терминале, чтобы завершить установку.
После того как Homebrew установлен, мы можем приступить к установке rbenv. Выполните следующую команду:
brew install rbenv
Когда установка завершится, добавьте rbenv в файл конфигурации вашего терминала, чтобы он правильно инициализировался при каждом запуске. Для этого выполните команду:
echo 'if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi' >> ~/.bash_profile
source ~/.bash_profile
Теперь, когда rbenv настроен, мы можем установить стабильную версию Ruby (на момент написания статьи, это версия 3.3.5).
rbenv install 3.3.5
rbenv global 3.3.5
Чтобы убедиться, что Ruby установлен правильно, проверьте текущую версию с помощью команды:
ruby --version
Вы должны увидеть версию Ruby, которую только что установили.
Установка FVM
Для управления версиями Flutter в проекте мы будем использовать Flutter Version Manager (FVM). Это удобный инструмент, позволяющий устанавливать и переключаться между разными версиями Flutter для различных проектов. Такой подход особенно полезен в командах, где нужно поддерживать определённую версию Flutter для каждого проекта, а также при работе с CI/CD, чтобы гарантировать стабильность сборки.
Однако, если по каким‑либо причинам вы не хотите использовать FVM, можно рассмотреть альтернативу — Puro. Это ещё один инструмент для управления версиями Flutter, который предлагает аналогичный функционал, но обеспечивает более высокую производительность за счет оптимизированного скачивания и управления версиями Flutter.
Мы остановимся на использовании FVM, так как он является популярным и хорошо документированным инструментом. Выполните следующие команды в терминале:
brew tap leoafarias/fvm
brew install fvm
Эти команды добавят репозиторий FVM в Homebrew и установят сам инструмент. После установки нужно настроить переменные окружения, чтобы система могла корректно использовать Flutter, установленный через FVM. Для этого выполните следующие команды:
echo 'export FVM_PATH=$HOME/fvm' >> ~/.bash_profile
echo 'export PATH="$PATH:$FVM_PATH/default/bin:$FVM_PATH/default/bin/cache/dart-sdk/bin:$HOME/.pub-cache/bin"' >> ~/.bash_profile
source ~/.bash_profile
После выполнения этих команд изменения будут сохранены, и система сможет находить Flutter и связанные с ним инструменты. Теперь, когда FVM установлен, нам нужно загрузить и установить стабильную версию Flutter (stable). Это можно сделать одной командой:
fvm global stable --force
После завершения установки убедитесь, что Flutter установлен и работает корректно. Для этого выполните:
flutter --version
Если всё настроено правильно, вы увидите информацию о текущей версии Flutter, установленной через FVM. Также для корректной работы Flutter на процессорах серии Apple M (ARM-архитектура) необходимо включить Rosetta. Это можно сделать с помощью команды:
sudo softwareupdate --install-rosetta --agree-to-license
В следующей части статьи мы будем использовать FVM для автоматической установки версии Flutter в CI/CD.
Установка OpenJDK и Android Command Line Tools
Для работы с Flutter и сборки приложений на Android необходима установка Java Development Kit (сокр. JDK), поскольку Gradle требует его наличия. Рекомендуемой версией является OpenJDK 17, так как она стабильно работает и поддерживается большинством современных инструментов разработки.
Однако выбор версии JDK следует осуществлять, исходя из потребностей вашего проекта и его совместимости с Gradle. К слову, можно также установить несколько версий JDK в одной системе. Если вы хотите установить OpenJDK 17, то выполните следующую команду в терминале:
brew install openjdk@17
После установки OpenJDK нам необходимо создать символическую ссылку в системной папке, чтобы Java могла быть корректно обнаружена другими инструментами. Выполните следующую команду:
sudo ln -sfn "$(brew --prefix)"/opt/openjdk@17/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk-17.jdk
Теперь нужно убедиться, что Java установлена корректно и используется верная версия. Выполните следующую команду:
/usr/libexec/java_home -V
Эта команда отобразит список всех установленных версий Java на вашей машине. Убедитесь, что OpenJDK 17 находится в этом списке.
Далее нужно установить Android Command Line Tools, настроить системные переменные и загрузить необходимые компоненты SDK:
brew install --cask android-commandlinetools
mkdir -p "$HOME/Library/Android"
ln -sfn "$(brew --prefix)"/share/android-commandlinetools "$HOME/Library/Android/sdk"
echo "export ANDROID_HOME=\$HOME/Library/Android/sdk" >> ~/.bash_profile
echo "export PATH=\$PATH:\$ANDROID_HOME/emulator:\$ANDROID_HOME/tools:\$ANDROID_HOME/tools/bin:\$ANDROID_HOME/platform-tools" >> ~/.bash_profile
source ~/.bash_profile
yes | sdkmanager 'platform-tools' "platforms;android-34" "build-tools;34.0.0"
Заключение
Теперь ваша физическая машина полностью готова к выполнению задач CI/CD. Вы настроили GitLab Runner, подготовили Flutter SDK и все необходимые зависимости для Android и iOS.
Благодаря выполненным настройкам, вы сможете эффективно запускать пайплайны, адаптированные под ваши проекты, и гарантировать, что сборки будут выполняться без сбоев, даже при долгосрочной работе системы. Такой подход позволяет минимизировать ручные действия и устранить потенциальные проблемы, связанные с зависимостями или конфигурацией окружения.
В следующей статье мы подробно разберем, как автоматизировать процесс, чтобы каждая новая версия вашего приложения собиралась и подписывалась без вашего участия.