Как настроить удаленный конвейер сборки и отладки Flutter-приложений для ОС «Аврора»
По пути на презентацию я в очередной раз просматривал функции приложения и заметил небольшой баг. Ноутбука под рукой не оказалось, а исправить его нужно было срочно. Я вспомнил про тестовый стенд с онлайн-IDE, развернул там нужные инструменты и прямо с планшета оперативно поправил код.
Я мог бы закончить историю на этом, но идея мобильной разработки не давала мне покоя: получается, имея под рукой планшет с большим экраном, вы можете организовать полноценный рабочий процесс. Правда, инструменты для сборки, тестирования и работы с SDK обычно выпускают только для ПК. А можно ли скомпилировать код прямо на смартфоне? Можно — например, в ALT Mobile или «Авроре ОС», если установить дополнительные пакеты. Но удобно ли это? Любителям Vim — вполне.
Среди веб-решений (GigaIDE, GitLab Web IDE и другие) мне больше всего нравится code-server (coder) — полноценная веб-версия Visual Studio Code, которая поддерживает большинство привычных плагинов. В статье я подробно расскажу, как развернуть облачную среду разработки, привязать ее к планшету и написать собственный плагин для автоматизации сборки под «Аврору ОС».

Настройка coder
Среду coder я разворачивал в рамках эксперимента по созданию удаленного рабочего места. Это не стало целевым решением, но виртуальная машина продолжала крутиться для других экспериментов. Серверу coder минимально требуется 1 ГБ ОЗУ и 2 ядра, однако для ускорения сборки приложения стоит увеличить эти параметры, хотя бы 4 ядра и 4 ГБ ОЗУ.
Формально code-server и сборочную среду можно упаковать в Docker, монтируя папки с проектами. Однако Aurora Platform SDK для работы с flutter-aurora штатно рассчитан на запуск вне контейнерной среды. Сценарий с Docker подходит для быстрой проверки гипотез, но для стабильной повседневной разработки лучше разворачивать SDK и IDE нативно на хост-системе.
Перед развертыванием IDE необходимо установить сам Aurora Platform SDK. Сделать это можно двумя способами:
- вручную по официальной инструкции (я предпочитаю этот вариант);
- автоматически прямо через плагин для VS Code.
После подготовки SDK переходим к развертыванию code-server. Разработчики подготовили удобный скрипт автоматической установки:
curl -fsSL https://code-server.dev/install.sh | sh
В процессе работы скрипт затребует права sudo. По завершении инсталляции в консоли появится инструкция по добавлению сервера в systemd — это нужно, чтобы coder автоматически запускался после ребута.
Запустим службу от имени нашего пользователя и проверим сгенерированный пароль для входа в веб-интерфейс. При необходимости пароль в файле ~/.config/code-server/config.yaml можно сразу изменить на свой:
sudo systemctl enable --now code-server@$USER
cat ~/.config/code-server/config.yaml
По умолчанию code-server запущен и доступен только внутри виртуальной машины (127.0.0.1:8080). Чтобы организовать доступ к Web IDE с других устройств через интернет, необходимо либо пробросить порт по SSH, либо настроить одно из прокси-решений.
Мне понравился вариант с Caddy: он настраивается буквально в пару строк и берет на себя управление SSL-сертификатами.
Установка Caddy
Выполним установку из официального репозитория:
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo chmod o+r /usr/share/keyrings/caddy-stable-archive-keyring.gpg
sudo chmod o+r /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
Конфигурация прокси
Откроем файл конфигурации /etc/caddy/Caddyfile и добавим в него строки следующего вида:
coder.doman.ru {
reverse_proxy 127.0.0.1:8080
tls /etc/caddy/code_ssl/fullchain.cer /etc/caddy/code_ssl/coder.key
}
Здесь coder.doman.ru — ваше доменное имя для сервера coder, 127.0.0.1:8080 – адрес и порт из конфигурации, а ~/.config/code-server/config.yaml, /etc/caddy/code_ssl – путь к файлам цепочки сертификатов и приватного ключа.
Если у вас есть публичный домен, сертификат проще всего выпустить с помощью скрипта acme.sh. Достаточно запустить скрипт, получить TXT запись, добавить ее на стороне регистратора доменов и минут через 15 перезапросить ключи.
Если домена нет или доступ идет по IP-адресу (например, без управляемого DNS в VPN-сети), можно сгенерировать самоподписанный сертификат. Учитывайте, что браузер будет выдавать предупреждение о недействительном сертификате, но соединение все равно останется зашифрованным. Сгенерировать пару «ключ + сертификат» можно следующей командой:
sudo mkdir -p /etc/caddy/code_ssl
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/caddy/code_ssl/coder.key -out /etc/caddy/code_ssl/fullchain.cer -subj "/CN=10.0.40.203"
sudo chown -R caddy:caddy /etc/caddy/code_ssl
sudo chmod 600 /etc/caddy/code_ssl/coder.key
sudo chmod 644 /etc/caddy/code_ssl/fullchain.cer
sudo systemctl restart caddy
На этом настройка сервера завершена. Перейдите в браузере вашего планшета по указанному домену или IP-адресу, введите пароль из config.yaml — и перед вами откроется полноценная облачная среда разработки, готовая к установке плагинов, языковых пакетов и сборке проектов.

Новый плагин
Плагины для VS Code разрабатываются на TypeScript. Это относительно простой язык программирования, с помощью которого можно автоматизировать процессы, вызывать системные команды, работать с файлами, верстать графический интерфейс и многое другое. Остается только определиться с задачами и реализовать их в коде.

Для начала соберем плагин Hello World. В первую очередь необходимо установить yo (генератор шаблонов) и generator-code (расширение для yo, предназначенное для генерации плагинов VS Code):
npm install -g yo generator-code
А также инструментарий для упаковки и сборки пакетов:
npm install -g vsce
Не хотите устанавливать Node.js и другие инструменты для сборки и разработки плагинов на TypeScript? Не беда, если у вас установлен Docker (Docker Desktop на macOS и Windows):
DOCKER_BUILDKIT=1 docker build --tag codegen "https://github.com/Microsoft/vscode-generator-code.git#main"
DOCKER_BUILDKIT=1 docker build --tag vsce "https://github.com/microsoft/vscode-vsce.git#main"
Далее вызываем следующие команды для генерации и сборки:
docker run -it -v $(pwd):/usr/src/app codegen
docker run --rm -it -v "$(pwd)":/workspace vsce publish
Теперь запускаем генератор. Этот интерактивный мастер задокументирует ответы на несколько вопросов, чтобы создать правильную структуру проекта и обеспечить удобство дальнейшей разработки плагина. Он автоматически сгенерирует директорию проекта в соответствии с указанным вами названием.
✔ What type of extension do you want to create? New Extension (TypeScript)
✔ What's the name of your extension? hello-world-plugin
✔ What's the identifier of your extension? hello-world-plugin
✔ What's the description of your extension? This is an example plugin.
✔ Initialize a git repository? Yes
`list` prompt is deprecated. Use `select` prompt instead.
✔ Which bundler to use? unbundled
`list` prompt is deprecated. Use `select` prompt instead.
✔ Which package manager to use? npm
Если вы не любите интерактивные формы ввода, можно сгенерировать конфигурацию одной строкой (не работает при использовании Docker):
yo code ./hello-world-plugin -t=ts -q
После генерации откройте созданную папку в VS Code и нажмите F5 — запустится окно отладки с вашим плагином.

Как это работает
Весь основной код описан в файле src/extension.ts:
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
console.log('Congratulations, your extension "hello-world-plugin" is now active!');
const disposable = vscode.commands.registerCommand('hello-world-plugin.helloWorld', () => {
vscode.window.showInformationMessage('Hello World from hello-world-plugin!');
});
context.subscriptions.push(disposable);
}
export function deactivate() {}
В первую очередь импортируется API VS Code — именно с ним взаимодействует плагин для регистрации команд, отображения диалоговых окон, кнопок и элементов интерфейса.
При инициализации плагина VS Code вызывает функцию activate(). В рамках процедуры активации срабатывает метод registerCommand(), в котором указываются уникальный идентификатор команды и исполняемая им функция.
Чтобы команду можно было вызвать из палитры вводом команды (Ctrl+Shift+P / Cmd+Shift+P), ее необходимо добавить в файл package.json (здесь же находятся название плагина, его описание, версия и зависимости). В разделе contributes указываются сама команда и ее название для пользователя:
"contributes": {
"commands": [
{
"command": "hello-world-plugin.helloWorld",
"title": "Hello World"
}
]
},
Архитектура плагина
Теперь перейдем к основной реализации. Функциональность плагина я разделяю на три логических блока:
- Управление устройствами: добавление девайса с описанием, вызов консоли конкретного устройства, отправка файлов и установка приложения на него.
- Управление проектом: базовые функции Flutter (создание проекта, запуск анализатора и тестов) и сборка приложения под каждую архитектуру. В этом блоке важно учитывать особенности
flutter-aurora, например передачу названия организации. - Инструменты: настройки плагина и списка устройств (изменение IP-адресов, комментариев), а также функциональность подписания пакета сертификатами разработчика и организации.
Добавляем интерфейс (боковую панель)
Чтобы не искать команды в палитре и не вводить их вручную, добавим боковую панель (View Container) с несколькими представлениями.
Для этого внесем в раздел contributes файла package.json следующую запись:
"viewsContainers": {
"activitybar": [
{
"id": "aurora-sidebar",
"title": "Flutter for Aurora OS",
"icon": "media/sidebar-icon.svg"
}
]
},
"views": {
"aurora-sidebar": [
{
"id": "aurora-devices-view",
"name": "Устройства"
},
{
"id": "aurora-project-view",
"name": "Проект"
},
{
"id": "aurora-tools-view",
"name": "Инструменты"
}
]
},

Простые действия с Flutter
Теперь нужно заполнить созданные представления функциями. Самое простое и быстрое — продублировать базовые команды flutter из консоли: создание проекта, добавление платформы, сборку под нужную архитектуру и запуск тестов.
Сначала зарегистрируем команды и настроим их отображение в интерфейсе:
export function registerProjectCommands(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('ffa.create', createProject),
vscode.commands.registerCommand('ffa.addPlatform', addAuroraPlatform),
vscode.commands.registerCommand('ffa.pubGet', pubGet),
vscode.commands.registerCommand('ffa.analyze', analyze),
vscode.commands.registerCommand('ffa.test', runTests),
vscode.commands.registerCommand('ffa.build', (arch: string) => { if (arch) build(arch); })
);
}
…
export class ProjectProvider implements vscode.TreeDataProvider {
getTreeItem(element: vscode.TreeItem) { return element; }
getChildren(): vscode.ProviderResult {
const items = [
{ label: 'Создать проект', cmd: 'ffa.create', icon: 'new-folder', args:[] },
{ label: 'Добавить платформу Аврора', cmd: 'ffa.addPlatform', icon: 'call-incoming', args:[] },
{ label: 'Pub Get', cmd: 'ffa.pubGet', icon: 'cloud-download', args:[] },
{ label: 'Анализатор', cmd: 'ffa.analyze', icon: 'beaker', args:[] },
{ label: 'Запуск тестов', cmd: 'ffa.test', icon: 'pass', args:[] },
...ARCHITECTURES.map(arch => ({
label: arch.displayName,
cmd: 'ffa.build',
icon: 'play',
args: [arch.label]
}))
];
return items.map(i => {
const item = new vscode.TreeItem(i.label);
item.command = { command: i.cmd, title: i.label, arguments: i.args };
item.iconPath = new vscode.ThemeIcon(i.icon);
return item;
});
}
}
Чтобы не перечислять варианты сборки в виде нескольких однотипных команд, вынесем список архитектур в отдельный массив:
const ARCHITECTURES = [
{ label: 'aarch64', displayName: 'Сборка (aarch64)', psdkArch: "aurora-arm64" },
{ label: 'armv7', displayName: 'Сборка (armv7)', psdkArch: "aurora-arm" },
{ label: 'x86_64', displayName: 'Сборка (x86_64)', psdkArch: "aurora-x64" }
];
Далее опишем сами функции и их аргументы. Так как вызов команд консоли будет выполняться регулярно с разным набором параметров, для него создана небольшая вспомогательная обертка runCommand().
Самая объемная операция в этом блоке — сборка проекта. Из переданного аргумента выбирается целевая архитектура, а ее псевдоним подставляется в параметр --target-platform:
function pubGet() { runCommand(flutterAurora, ['pub', 'get'], "AuroraFlutter"); }
function analyze() { runCommand(flutterAurora, ['analyze', '--no-fatal-infos'], "AuroraFlutter"); }
function runTests() { runCommand(flutterAurora, ['test'], "AuroraFlutter"); }
export function build(arch: string) {
const archInfo = ARCHITECTURES.find(a => a.label === arch);
if (!archInfo) return;
runCommand(flutterAurora, ['build', 'aurora', '--release', `--target-platform=${archInfo.psdkArch}`], "AuroraFlutter");
}
Мастер создания проекта
Чтобы сделать процесс создания нового проекта более удобным, добавим немного «волшебства» — интерактивный мастер (Wizard). Поля, которые необходимо заполнить: название проекта, целевая папка и (опционально) идентификатор организации (по умолчанию для ОС «Аврора» используется ru.aurora).
API VS Code позволяет легко реализовать такие диалоговые окна с помощью vscode.window.showInputBox и vscode.window.showOpenDialog:
async function createProject() {
const appName = await vscode.window.showInputBox({
prompt: 'Название проекта',
value: 'app_demo',
validateInput: (text) => !text?.trim() ? 'Название не может быть пустым' : null
});
if (!appName) return; // escape
const org = await vscode.window.showInputBox({
prompt: 'Организация',
value: readAuroraConfig().developer?.app_prefix || 'ru.aurora',
validateInput: (text) => !text?.trim() ? 'Организация не может быть пустой!' : null
});
if (!org) return;
const parentDirUri = await vscode.window.showOpenDialog({
canSelectFiles: false,
canSelectFolders: true,
canSelectMany: false,
openLabel: 'Выберите папку для создания проекта'
});
if (!parentDirUri || parentDirUri.length === 0) return;
…
Аналогичным образом реализован класс ToolsProvider — представление для дополнительных инструментов. Он позволяет открывать настройки плагина, редактировать IP-адреса и параметры подключенных устройств, а также подписывать собранный RPM-пакет сертификатом разработчика или организации.
Реверс IDE
Примерно по той же логике добавлен и класс DeviceProvider. Он содержит функции, которые выполняются непосредственно в связке с самим устройством:
- Обмен SSH-ключами: упрощает подключение и позволяет в дальнейшем не вводить пароль (при использовании ключа без фразы-пароля);
- SSH-терминал: для отладки и подключения к устройству;
- Отправка файлов и пакетов приложения: загрузка собранных файлов на устройство;
- Управление приложением: установка, запуск, просмотр логов и остановка процесса.
Каждая операция должна выполняться на конкретном устройстве по его IP-адресу. Перечень кнопок и устройств формируется из JSON-файла, а для его заполнения предусмотрено простое интерактивное меню:
export async function addDevice() {
const host = await vscode.window.showInputBox({ prompt: 'Имя устройства (напр. R570E)' });
if (!host) return;
const hostname = await vscode.window.showInputBox({ prompt: 'IP адрес устройства' });
if (!hostname) return;
const archPick = await vscode.window.showQuickPick([
{ label: 'aarch64', detail: '64-bit ARM' }, { label: 'armv7', detail: '32-bit ARM' }, { label: 'x86_64', detail: 'x86_64' }
], { placeHolder: 'Архитектура' });
if (!archPick) return;
const devices = readDevices();
devices.push({ host: host.trim(), hostname: hostname.trim(), user: 'defaultuser', arch: archPick.label });
writeDevices(devices);
vscode.window.showInformationMessage(`Устройство "${host}" добавлено.`);
}
Компания ОМП уделяет много внимания поддержке сообщества. В частности, они адаптировали популярную open-source IDE Qt Creator под нужды ОС «Аврора», заложив в нее готовые инструменты для сборки, доставки, установки и запуска приложений.
Доставить собранное приложение на устройство проще всего: достаточно передать полученный RPM-пакет по SSH командой scp:
runCommand('scp', ['-o', 'StrictHostKeyChecking=no', '-o', 'UserKnownHostsFile=/dev/null', rpmPath, `${device.user}@${device.hostname}:${remotePath}`], "Deploy");
Далее начинаются нюансы. Установить пакет привычной командой rpm -i без повышения привилегий не так просто. Конечно, можно каждый раз запрашивать у пользователя пароль root, но это неудобно. Другой вариант — использовать утилиту sfdk.
Однако есть более изящное, неочевидное, но простое решение: установка пакета через встроенный системный сервис. Для этого достаточно отправить запрос службе ru.omp.APM через системную шину DBus:
setTimeout(() => {
runCommand('ssh', ['-o', 'StrictHostKeyChecking=no', '-o', 'UserKnownHostsFile=/dev/null', `${device.user}@${device.hostname}`,
`gdbus call -y -d ru.omp.APM -o /ru/omp/APM -m ru.omp.APM.Install ${remotePath} '{}'`], "Deploy");
}, 2000);
Занимательный факт: один из разработчиков ОМП опубликовал аналогичный подход на платформе CSDN.
Заключительный шаг — запуск приложения в пользовательском или отладочном режиме. Для этого используется системная утилита на устройстве или в эмуляторе (доступная также через DBus):
let sshCommand: string;
if (isDebug) {
sshCommand = `/usr/bin/runtime-manager-tool Control startDebug ${appId} --output-to-console -e 'AURORA_APP_ID=${appId}' -e 'AURORA_PACKAGE_NAME=${appId}'`;
} else {
sshCommand = `/usr/bin/runtime-manager-tool Control start ${appId}`;
}
Сборка, установка
После отладки и фиксации мелких проблем, небольшого рефакторинга и обратной связи от друзей плагин можно собрать и установить в среду разработки.
Сборка выполняется стандартной командой:
npx @vscode/vsce package
Программа vsce выполнит валидацию файлов и соберет пакет плагина в папку out. Чтобы пройти проверку пакета при наличии локальных ключей и сертификатов, добавлен флаг --allow-package-secrets privatekey.
Вы также можете опубликовать плагин в официальном маркетплейсе VS Code (инструкция доступна в документации). После публикации расширение проходит автоматическую проверку и становится доступно для установки в течение нескольких минут.

Финальный этап — установка плагина. Для онлайн- и офлайн-сред поддерживается установка как из локального файла, так и напрямую из маркетплейса:
code --install-extension ffa-0.0.3.vsix # установить из файла
code --install-extension techvlad.ffa # установить из маркетплейса (если расширение туда загружено)
После установки вы получаете полностью рабочий инструмент в интерфейсе вашей IDE:

Исходный код плагина опубликован в открытом Git-репозитории. Вы можете скачать его, изучить архитектуру или адаптировать под свои задачи.
Заключение
Описанный вариант мобильного рабочего места не является абсолютной заменой классической десктопной среды: необходимо поддерживать сервер, организовывать безопасный доступ и иметь стабильное подключение к интернету. Тем не менее это отличное резервное решение для работы «в поле» или экстренной отладки.
Под подобные задачи всегда можно разработать собственный плагин: достаточно немного времени, и вот рутинные консольные команды легко превращаются в понятные кнопки интерфейса. Это напоминает создание специализированной оснастки для обработки деталей. Представленный плагин — лишь наглядная демонстрация того, что автоматизация VS Code доступна каждому. В целом, типовой список команд и сценариев можно доверить нейросети: она сгенерирует базовый шаблон, после чего останется лишь отладить логику и собрать готовый VSIX-пакет.