Приложение на Electron#
Electron-приложение приходит от поставщика собранным: движок Chromium, Node и
код приложения лежат в одном каталоге. При упаковке нужно разложить этот каталог
в /app и запустить движок так, чтобы внутри песочницы Flatpak заработала его
собственная изоляция.
Почему нужна обёртка#
Chromium изолирует свои процессы через вспомогательный бинарник
chrome-sandbox с битом setuid. В песочнице Flatpak setuid запрещён, поэтому
движок либо не стартует, либо работает без внутренней изоляции.
Обходится это обёрткой zypak: она подменяет собой вспомогательный бинарник и
переводит запуск дочерних процессов на механизмы, разрешённые в песочнице.
Обёртка приезжает с базовым приложением org.astra.Electron.BaseApp. Движок
базовое приложение не содержит: Electron приносит приложение, а базовое
приложение даёт только обёртку и вспомогательные обвязки.
Чем собирать#
Базовое приложение подключается ключом base, а его понимает
flatpak-builder. У aft-app такого поля нет, поэтому Electron-приложения
собираются flatpak-builder. Про сам инструмент - в блоке
Сборка с помощью flatpak-builder.
Подготовка:
sudo flatpak install astra-main org.astra.Gtk//1.8.6
sudo flatpak install astra-extended org.astra.gtkSdk//1.8.6
sudo flatpak install astra-extended org.astra.Electron.BaseApp//1.8.6
Что приходит от поставщика#
Внутри поставки, независимо от того, deb это или архив, лежит каталог примерно такого вида:
Messenger/
|-- messenger исполняемый файл движка
|-- chrome-sandbox вспомогательный бинарник, работать не будет
|-- chrome_crashpad_handler
|-- libEGL.so, libGLESv2.so, libffmpeg.so, libvk_swiftshader.so
|-- icudtl.dat, resources.pak, snapshot_blob.bin, v8_context_snapshot.bin
|-- locales/
`-- resources/
`-- app.asar код приложения
Посмотреть содержимое, не распаковывая:
dpkg -c messenger_5.2.0_amd64.deb
tar -tf messenger-5.2.0-linux-x64.tar.xz
В deb-пакетах такой каталог обычно лежит в /opt/<Название>, а рядом
приезжают usr/share/applications/<имя>.desktop и иконки в
usr/share/icons/hicolor/<размер>/apps/<имя>.png. Именно на это дерево
рассчитан манифест ниже. Если у поставки другая раскладка, меняются только пути
в командах.
Дерево проекта сборки#
messenger/
|-- ru.example.Messenger.yml
|-- messenger.sh
|-- ru.example.Messenger.metainfo.xml
`-- messenger_5.2.0_amd64.deb
Скрипт запуска messenger.sh:
#!/bin/bash
export TMPDIR="${XDG_RUNTIME_DIR}/app/${FLATPAK_ID}"
exec zypak-wrapper /app/Messenger/messenger "$@"
Обёртка получает путь к движку первым аргументом, всё остальное дописывается после и доходит до приложения без изменений.
Описание ru.example.Messenger.metainfo.xml:
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>ru.example.Messenger</id>
<metadata_license>CC0-1.0</metadata_license>
<project_license>LicenseRef-proprietary</project_license>
<name>Messenger</name>
<summary>Пример упаковки Electron-приложения</summary>
<description>
<p>Готовая поставка Electron, упакованная во Flatpak.</p>
</description>
<launchable type="desktop-id">ru.example.Messenger.desktop</launchable>
</component>
Манифест#
ru.example.Messenger.yml:
id: ru.example.Messenger
base: org.astra.Electron.BaseApp
base-version: '1.8.6'
runtime: org.astra.Gtk
runtime-version: '1.8.6'
sdk: org.astra.gtkSdk
command: messenger
separate-locales: false
finish-args:
- --share=ipc
- --share=network
- --socket=wayland
- --socket=fallback-x11
- --socket=pulseaudio
- --device=dri
- --filesystem=xdg-download
- --talk-name=org.freedesktop.Notifications
- --talk-name=org.freedesktop.secrets
- --talk-name=org.kde.StatusNotifierWatcher
- --system-talk-name=org.freedesktop.login1
modules:
- name: messenger
buildsystem: simple
build-commands:
- dpkg-deb -x messenger.deb unpack
- mkdir -p ${FLATPAK_DEST}
- mv unpack/opt/Messenger ${FLATPAK_DEST}/Messenger
- rm -f ${FLATPAK_DEST}/Messenger/chrome-sandbox
- install -Dm0755 messenger.sh ${FLATPAK_DEST}/bin/messenger
- install -Dm0644 unpack/usr/share/applications/messenger.desktop
${FLATPAK_DEST}/share/applications/${FLATPAK_ID}.desktop
- sed -i "s|^Exec=.*|Exec=messenger %U|"
${FLATPAK_DEST}/share/applications/${FLATPAK_ID}.desktop
- sed -i "s|^Icon=.*|Icon=${FLATPAK_ID}|"
${FLATPAK_DEST}/share/applications/${FLATPAK_ID}.desktop
- |
for s in 16 32 48 64 128 256 512; do
src="unpack/usr/share/icons/hicolor/${s}x${s}/apps/messenger.png"
if [ -f "$src" ]; then
install -Dm0644 "$src" \
"${FLATPAK_DEST}/share/icons/hicolor/${s}x${s}/apps/${FLATPAK_ID}.png"
fi
done
- install -Dm0644 ru.example.Messenger.metainfo.xml
${FLATPAK_DEST}/share/metainfo/${FLATPAK_ID}.metainfo.xml
sources:
- type: file
path: ./messenger_5.2.0_amd64.deb
dest-filename: messenger.deb
- type: file
path: ./messenger.sh
- type: file
path: ./ru.example.Messenger.metainfo.xml
Распаковка идёт через dpkg-deb, который есть в SDK Astra. Готовых утилит
bsdtar и desktop-file-edit, которыми пользуются манифесты Flathub, в
SDK нет, поэтому ключи desktop-файла правятся sed.
Файл chrome-sandbox удаляется: с потерянным битом setuid он всё равно
неработоспособен, а его наличие сбивает движок с толку. Изоляцию обеспечивает
обёртка.
Если поставка приходит архивом#
Меняются только источник и первые две команды:
build-commands:
- tar -xf messenger.tar.xz
- mv Messenger ${FLATPAK_DEST}/Messenger
- rm -f ${FLATPAK_DEST}/Messenger/chrome-sandbox
- install -Dm0755 messenger.sh ${FLATPAK_DEST}/bin/messenger
- install -Dm0644 ru.example.Messenger.desktop
${FLATPAK_DEST}/share/applications/${FLATPAK_ID}.desktop
- install -Dm0644 icon.png
${FLATPAK_DEST}/share/icons/hicolor/256x256/apps/${FLATPAK_ID}.png
- install -Dm0644 ru.example.Messenger.metainfo.xml
${FLATPAK_DEST}/share/metainfo/${FLATPAK_ID}.metainfo.xml
sources:
- type: file
path: ./messenger-5.2.0-linux-x64.tar.xz
dest-filename: messenger.tar.xz
- type: file
path: ./messenger.sh
- type: file
path: ./ru.example.Messenger.desktop
- type: file
path: ./icon.png
- type: file
path: ./ru.example.Messenger.metainfo.xml
В архиве обычно нет ни desktop-файла, ни иконки, поэтому оба готовятся рядом с
манифестом. Desktop-файл пишется сразу с правильными значениями, и править его
sed не нужно:
[Desktop Entry]
Type=Application
Name=Messenger
Exec=messenger %U
Icon=ru.example.Messenger
Terminal=false
Categories=Network;
Сборка и установка#
cd messenger
flatpak-builder --force-clean --repo=./repo --default-branch=stable \
build-dir ru.example.Messenger.yml
sudo flatpak remote-add --if-not-exists --no-gpg-verify local-repo ./repo
sudo flatpak install -y local-repo ru.example.Messenger
flatpak run ru.example.Messenger
Проверить, что изоляция движка действительно работает, можно по дочерним
процессам приложения: у процессов Browser, GPU, Utility и Tab должен быть
включён режим фильтрации системных вызовов. Если обёртка не подхватилась,
Chromium сообщает об этом при старте сообщением про chrome-sandbox.
Тонкости Electron#
Идентификаторы приводятся к идентификатору приложения. Desktop-файл и иконки в
поставке названы по имени программы, а Flatpak ждёт имя вида
<app-id>.desktop, поэтому файлы переименовываются при установке, а ключи
Exec и Icon внутри правятся отдельно.
Самообновление отключается. Electron-приложения часто умеют скачивать себе
новую версию в /app. В песочнице /app доступен только для чтения, и
попытка обновления заканчивается ошибкой. Механизм отключается настройкой
поставщика или правкой файла с описанием сборки внутри поставки.
Хранение паролей идёт через org.freedesktop.secrets. Без этого разрешения
приложение переключается на хранение в открытом виде или не запускается совсем,
в зависимости от программы.
Значок в области уведомлений требует org.kde.StatusNotifierWatcher, а сами
уведомления - org.freedesktop.Notifications. Оба имени на сессионной шине.
Доступ ко всему домашнему каталогу Electron-приложению обычно не нужен:
открытие и сохранение файлов идут через портал, а загрузки достаточно направить
в xdg-download.