惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cloudbric
Cloudbric
酷 壳 – CoolShell
酷 壳 – CoolShell
人人都是产品经理
人人都是产品经理
G
Google Developers Blog
美团技术团队
MongoDB | Blog
MongoDB | Blog
博客园 - 聂微东
博客园 - Franky
罗磊的独立博客
雷峰网
雷峰网
F
Fortinet All Blogs
Stack Overflow Blog
Stack Overflow Blog
T
The Blog of Author Tim Ferriss
宝玉的分享
宝玉的分享
H
Help Net Security
Vercel News
Vercel News
Last Week in AI
Last Week in AI
Microsoft Security Blog
Microsoft Security Blog
B
Blog
Microsoft Azure Blog
Microsoft Azure Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 叶小钗
Apple Machine Learning Research
Apple Machine Learning Research
L
LangChain Blog
量子位
F
Full Disclosure
Hugging Face - Blog
Hugging Face - Blog
C
Check Point Blog
WordPress大学
WordPress大学
IT之家
IT之家
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Recorded Future
Recorded Future
D
Docker
T
Tailwind CSS Blog
博客园 - 司徒正美
博客园 - 三生石上(FineUI控件)
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Blog — PlanetScale
Blog — PlanetScale
阮一峰的网络日志
阮一峰的网络日志
GbyAI
GbyAI
Y
Y Combinator Blog
月光博客
月光博客
aimingoo的专栏
aimingoo的专栏
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Jina AI
Jina AI
Recent Announcements
Recent Announcements
V
Visual Studio Blog
The Cloudflare Blog
P
Proofpoint News Feed

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Дистрибуция Avalonia-приложений под Linux через Flatpak
Роман Калачик · 2026-06-23 · via Все публикации подряд на Хабре

Средний

12 мин

129

Avalonia UI решает задачу кроссплатформенной разработки. Правильно написанный код без изменений работает под Windows, Linux и macOS. Сложности начинаются на следующем этапе - при подготовке приложения к распространению. Под Windows и macOS есть универсальные форматы инсталлеров, которые работают на всех версиях этих ОС. А вот с Linux ситуация иная. Экосистема Linux фрагментирована. Применяется несколько разных форматов упаковки приложений: deb, rpm. Применяются разные менеджеры пакетов. К этому добавляется проблема версий системных библиотек: сборка, работающая на одном дистрибутиве, может не запуститься на другом из-за иной версии glibc или отсутствия необходимой .so-библиотеки. В результате один и тот же бинарник Avalonia-приложения требуется упаковывать несколько раз, разными инструментами, с разными конфигурациями сборки, и каждую такую сборку затем отдельно тестировать, поскольку успешная сборка пакета не гарантирует его работоспособность у конечного пользователя.

Сегодня мы рассмотрим одно из решений этой проблемы - Flatpak. Поддержка Flatpak официально заявлена и документирована у крупных российских дистрибутивов:

  • Astra Linux (входит в базовый репозиторий начиная с версии 1.7)

  • РЕД ОС (отдельная статья в официальной базе знаний)

  • Альт Линукс

Поэтому для разработок, ориентированных на российский рынок, Flatpak - вполне подходящий вариант.

Flatpak версия “Delta Design Домашняя”

У нас недавно был релиз хоббийной версии Delta Design. Мы писали о нём в этой статье. Линукс версия “Delta Design Домашняя” получила унифицированный Flatpak инсталлер. Инсталлер в теории может работать на любой Linux-системе, которая поддерживает Flatpak, но мы в системных требованиях сейчас указываем только те дистрибутивы, на которых у нас запускаются автоматизированные тесты.

Как это устроено внутри

Чтобы не пересказывать документацию по Flatpak, рассмотрим только самые интересные места и проблемы, с которыми мы столкнулись. Если где-то будет непонятно - приходите в комментарии.

DeltaDesignHome представляет собой Avalonia-приложение, которое публикуется в CI в заданную папку.

dotnet publish DeltaDesignHome.Setup.slnf  -bl -c Release --use-current-runtime --self-contained -o bin/PublishProduction

Далее эти бинарные файлы пакуются с помощью утилиты flatpak-builder. Рассмотрим файл манифеста для упаковки нашего приложения.

app-id: ru.eremex.deltadesignhome
runtime: org.gnome.Platform
runtime-version: '49'
sdk: org.gnome.Sdk
command: delta-design-home-wrapper

app-id - уникальный идентификатор приложения в обратной нотации домена, по нему Flatpak различает пакеты, хранит для них отдельные каталоги с данными и настройками. runtime - это базовый образ, набор системных библиотек фиксированной версии, который Flatpak подкладывает приложению вместо того, чтобы полагаться на то, что установлено в системе. Он нужен для того, чтобы GUI-приложению было на чём работать: GTK или его аналоги, графический стек, базовые зависимости рабочего стола - всё то, что у десктопного приложения почти всегда есть под капотом, даже если оно написано на Avalonia и сама Avalonia это не использует напрямую. org.gnome.Platform - один из самых ходовых runtime’ов на Flathub, есть ещё org.kde.Platform и более минимальный org.freedesktop.Platform; на практике для конкретного приложения часто можно взять runtime, который уже использует похожее по типу приложение, и не подбирать состав зависимостей с нуля. sdk - то же самое, но с инструментами для сборки, нужен только на этапе flatpak-builder и в финальный пакет не попадает.

command - какой исполняемый файл запускать при старте приложения. В нашем случае это не сам бинарник DeltaDesignHome, а обёртка delta-design-home-wrapper - её назначение рассматривается далее.

finish-args: что разрешено снаружи песочницы

Это самая важная с точки зрения безопасности часть манифеста - по умолчанию Flatpak-приложение не видит почти ничего, кроме себя самого. Всё, что ему нужно от системы, нужно прописать явно.

В этом манифесте права разбиваются на четыре смысловые группы.

Системные ресурсы:

- --share=ipc
- --share=network
- --socket=pulseaudio
- --socket=x11
- --device=all

--share=network - выход в интернет. --socket=x11 - отрисовка окна на экране (без этого графическое приложение просто не сможет показать UI). --socket=pulseaudio - звук. --device=all - доступ ко всем устройствам, в том числе GPU; для CAD-системы с 3D-отрисовкой без него не заведётся аппаратное ускорение.

Здесь следует учитывать особенность, которая на практике приводит к проблемам при запуске: на момент написания статьи Avalonia-приложения используют только X11 (нативной поддержки протокола Wayland у фреймворка нет), поэтому достаточно одного --socket=x11 - в манифесте DeltaDesignHome указан именно он. Если же приложение поддерживает нативный Wayland и требуется доступ к обоим бэкендам, указывать --socket=x11 и --socket=wayland одновременно как два равноправных сокета не следует - это создаёт конфликт. Для такого случая в Flatpak предусмотрен отдельный флаг --socket=fallback-x11, который даёт доступ к X11 только если Wayland недоступен, и используется в паре с --socket=wayland. Корректная комбинация для приложения с нативной поддержкой обоих бэкендов выглядит так:

- --socket=fallback-x11
- --socket=wayland

а не --socket=x11 плюс --socket=wayland напрямую.

Интеграция с рабочим столом:

- --talk-name=org.freedesktop.Notifications
- --talk-name=org.gnome.Mutter.IdleMonitor
- --talk-name=org.kde.StatusNotifierWatcher
- --talk-name=com.canonical.AppMenu.Registrar

Notifications - системные уведомления, StatusNotifierWatcher - иконка в трее (KDE-вариант), AppMenu.Registrar - глобальное меню в духе Unity/Ubuntu. Здесь перечислены сервисы и для GNOME, и для KDE одновременно - приложение не знает заранее, в каком окружении его запустят, поэтому запрашивает права на оба варианта сразу.

Доступ к файлам:

- --filesystem=home
- --talk-name=org.freedesktop.portal.FileChooser

Этот пункт требует отдельного рассмотрения. --filesystem=home - это разрешение на чтение и запись всего домашнего каталога пользователя, не только конкретных подпапок. Это широкое право, и в общем случае хорошей практикой считается запрашивать более узкие права, такие как --filesystem=xdg-documents. Но для CAD-приложения, где пользователь может открыть проект из произвольного места на диске, а не только из ~/Documents, это осознанный компромисс: иначе пользователю придётся каждый раз проходить через диалог выбора файла при открытии проекта вне стандартных папок.

Постоянное хранение данных:

- --persist=.local/share/ru.eremex.deltadesignhome/data
- --persist=.config/ru.eremex.deltadesignhome
- --filesystem=~/.local/share/ru.eremex.deltadesignhome/data:rw

--persist - без этого атрибута данные будут считаться частью песочницы и не будут сохраняться при переустановке приложения.

Модули: из чего собирается /app

Далее идёт modules - список того, что попадает внутрь финального пакета, в порядке выполнения. У каждого модуля свой buildsystem (тут везде simple - просто список shell-команд, никакой автоматической сборки из исходников) и свой набор sources.

custom-fonts - копирует шрифты в /app/share/fonts.

DeltaDesignHome - собственно само приложение. Берёт опубликованную (dotnet publish) сборку из ./bin/PublishProduction, плюс .desktop-файл, иконку, appdata.xml и тот самый delta-design-home-wrapper. Здесь же создаются стандартные каталоги Flatpak-пакета:

- mkdir -p /app/share/applications
- mkdir -p /app/share/icons/hicolor/scalable

.desktop-файл и иконка кладутся по стандартным для Linux путям (applications, icons/hicolor) - именно по этой структуре система понимает, что появилось новое приложение, и рисует для него ярлык в меню.

Отдельного внимания заслуживает delta-design-home-wrapper - command в начале манифеста указывает именно на него, а не на сам бинарник DeltaDesignHome. Перед запуском основного UI требуется выполнить некоторую проверку или подготовку. Назначение этого скрипта рассматривается в отдельном разделе, посвящённом wrapper-скрипту. Сам .NET-бинарник Avalonia-приложения практически никогда не запускают напрямую как command в продакшен-манифестах - обёртка предоставляет точку, через которую можно изменить процесс старта, не затрагивая код самого приложения.

Автоматизированное тестирование инсталляции

Успешная сборка Flatpak-пакета не означает завершение работы. Манифест может скомпилироваться без единой ошибки и при этом приводить к неработающему приложению у пользователя: может не хватить права на нужный D-Bus интерфейс, persist может не подхватить каталог с данными после обновления, либо приложение может завершаться сразу после старта в чистом окружении, где отсутствуют необходимые шрифты. Таким образом, успешная сборка пакета не гарантирует его работоспособности - собранный артефакт необходимо проверять.

Для этого используются обычные MSTest-тесты - тот же фреймворк, что и для остальной кодовой базы на .NET. Идея состоит в следующем: тестовый набор запускается не на изолированном коде, а на уже установленном Flatpak-пакете, так что проверяется именно то, что в итоге получит пользователь - со всеми правами из finish-args, с тем же .NET runtime, что работает внутри песочницы, и с той же файловой структурой.

Процесс состоит из трёх шагов.

Сборка тестового проекта и копирование его внутрь установленного пакета. Flatpak-приложение после установки располагается в /var/lib/flatpak/app/<app-id>/current/active/files - это содержимое того самого /app, который собирался модулями в манифесте. Туда же, рядом со штатным бинарником, копируется собранный MSTest-проект:

cp artifacts/bin/DeltaDesign.E2E.InstallDDHome.Tests/release/DeltaDesign.E2E.InstallDDHome.Tests \
   /var/lib/flatpak/app/ru.eremex.deltadesignhome/current/active/files/bin/

Отдельной упаковки теста в манифест не требуется - он оказывается там же, где и всё остальное содержимое пакета, и использует тот же .NET runtime, те же зависимости. Отметим, что Flatpak-пакет может быть установлен на уровне системы или в пользовательскую папку. Это влияет на путь к бинарным файлам Flatpak. В данном примере рассматривается первый вариант.

Запуск тестов через подмену команды, а не самого приложения. У flatpak run есть параметр --command, который переопределяет точку входа, заданную в манифесте полем command. Вместо запуска delta-design-home-wrapper напрямую вызывается тестовый бинарник:

flatpak run --command=DeltaDesign.E2E.InstallDDHome.Tests \
  ru.eremex.deltadesignhome \
  $TEST_ARGS --results-directory=$CI_PROJECT_DIR

Ключевой момент здесь - тесты выполняются внутри той же песочницы, что и сама программа. Если в finish-args чего-то не хватает (например, не указан --persist для каталога с данными), это будет выявлено уже на данном этапе, а не после релиза пакета. $TEST_ARGS - стандартные параметры MSTest (фильтры, конфигурация запуска), а --results-directory=$CI_PROJECT_DIR направляет отчёт о тестах в каталог CI-джобы, откуда его впоследствии можно прикрепить как артефакт пайплайна.

Получение отчёта в привычном формате. Поскольку используется MSTest, результаты выходят в том же формате (.trx и так далее), что и обычные unit-тесты проекта - отдельный парсер вывода под Flatpak не требуется, CI читает их так же, как любой другой тестовый прогон.

Практическое преимущество такого подхода: тест видит то же окружение, в котором будет работать приложение у пользователя. Если приложению не хватает прав на сеть, на конкретный D-Bus сервис или на каталог с данными, тест выявит это так же, как столкнулся бы с этим реальный пользователь - но на этапе CI, до релиза.

Адаптация Flatpak к российским реалиям: установка без интернета

Flatpak по умолчанию рассчитан на постоянное подключение к удалённому репозиторию, такому как Flathub. Пакет и его рантаймы скачиваются оттуда при установке. Однако это не означает, что Flatpak в принципе нельзя установить на изолированную машину.

flatpak create-usb - эта команда копирует приложение вместе со всеми его зависимостями (включая нужный runtime) в заданный каталог.

flatpak create-usb /media/user/FLATPAKS ru.eremex.deltadesignhome

В результате в заданной папке появляется полноценный мини-репозиторий - внутри него каталог .ostree/repo, такой же по структуре, как настоящий удалённый репозиторий.

Установка на машине без интернета выполняется следующей командой:

flatpak install --sideload-repo=/media/user/FLATPAKS/.ostree/repo flathub ru.eremex.deltadesignhome

Для корректной работы данного механизма необходимо учитывать ряд условий:

  • Remote должен быть настроен заранее. --sideload-repo указывает, откуда брать данные пакета, но сам факт наличия remote с именем flathub (или любым другим) на офлайн-машине должен быть обеспечен ещё до этой команды - create-usb переносит содержимое пакетов, а не сам источник.

  • Репозиторий должен быть подписан. Для офлайн-установки нужны GPG-подписи у репозитория и настроенный collection ID - и на стороне сервера, откуда исходно ставилось приложение, и в локальной конфигурации remote’а на офлайн-машине. Без этого Flatpak откажется доверять данным с носителя.

Таким образом, ограничение в виде закрытой сети не является препятствием для использования Flatpak. Процесс установки версии “Delta Design Домашняя” без интернета описан в этой статье.

Первый запуск вместо мастера установки

Ещё одна особенность, с которой мы столкнулись, - отсутствие мастера установки. Если приложение ранее распространялось через классический Windows-инсталлятор, пользователи привыкли к графической оболочке, в которой показывается EULA и настраиваются некоторые параметры установки. У flatpak install отсутствует какой-либо интерактивный мастер настройки. Команда либо загружает пакет и проверяет его подпись, либо разворачивает уже скачанные данные в /var/lib/flatpak - без диалогов, без мастера настройки, без возможности показать пользователю что-либо и запросить согласие непосредственно в процессе установки.

Таким образом, если приложению ранее требовалось показывать лицензионное соглашение или настраивать параметры подключения к базе данных при установке, эту логику необходимо переносить не на этап установки, а на этап первого запуска самого приложения. Технически это реализуется по следующей схеме:

  1. При старте приложение проверяет, есть ли в его каталоге настроек ($XDG_CONFIG_HOME, то есть ~/.var/app/<app-id>/config) маркер-файл или запись в конфигурации, означающая, что первичная настройка уже выполнена.

  2. Если маркер отсутствует - это «первый запуск». Приложение отображает собственный диалог: с EULA, с настройкой подключения к БД, с любым другим содержимым, ранее реализованным мастером инсталлятора.

  3. После того как пользователь прошёл этот шаг (принял лицензию, указал параметры), приложение записывает маркер в тот же $XDG_CONFIG_HOME и более не показывает этот экран.

Преимущества такого подхода: маркер располагается в персистентном каталоге пользователя, а не в /app, и поэтому сохраняется при обновлении пакета - пользователь не увидит EULA повторно после каждого flatpak update, если только не требуется показывать его снова при смене версии лицензии. Поскольку данная логика реализована в коде приложения, а не в отдельном скрипте установщика, она работает одинаково независимо от способа установки пакета - как через Flathub, так и офлайн через create-usb.

Рассмотрим подробнее delta-design-home-wrapper из манифеста - этот файл упоминался ранее при разборе поля command. Именно этот скрипт реализует логику первого запуска, описанную выше.

Принцип работы заключается в следующем: command в манифесте, а значит и Exec в .desktop-файле, указывает не на основной .NET-бинарник DeltaDesignHome, а на промежуточный bash-скрипт. При запуске приложения пользователем выполняется именно этот скрипт, который определяет дальнейшие действия.

Логика внутри минимальная:

#!/bin/sh

CONFIG_MARKER="$XDG_CONFIG_HOME/ru.eremex.deltadesignhome/initialized"

if [ ! -f "$CONFIG_MARKER" ]; then
  # маркера нет - это первый запуск
  exec /app/bin/DeltaDesignHome firstrun
else
  # обычный запуск
  exec /app/bin/DeltaDesignHome
fi

Проверка основана на наличии файла-маркера в $XDG_CONFIG_HOME - каталоге настроек, который физически располагается в ~/.var/app/ru.eremex.deltadesignhome/config и сохраняется при обновлениях пакета. Если маркер отсутствует, скрипт запускает основной исполняемый файл с дополнительным аргументом firstrun, и приложение, получив этот параметр при старте, отображает экран первичной настройки (EULA, параметры подключения к базе и так далее), а после прохождения пользователем этого шага создаёт файл-маркер. При следующем запуске обёртка обнаруживает наличие маркера и передаёт управление приложению без дополнительных параметров.

Переменные окружения

Если приложение использует переменные окружения хоста, следует учитывать, что внутри песочницы окружение отличается от окружения вне её. Часть переменных Flatpak действительно передаёт в приложение по умолчанию, если не указан флаг --clear-env, однако полагаться на это не следует - набор переданных переменных зависит от конкретной системы и версии Flatpak, и это не тот контракт, на который стоит ориентироваться в продакшен-приложении.

Если приложению для работы нужна конкретная переменная окружения - будь то путь к лицензионному серверу, флаг конфигурации, который раньше выставлялся в /etc/environment, или что-то специфичное для рендеринга - её нужно объявить явно в finish-args через --env:

- --env=VARIABLE_NAME=значение

Если переменных много или их значения нежелательно задавать непосредственно в манифесте (например, они различаются для разных сборок), у flatpak run есть runtime-альтернатива - flatpak override --env=VAR=VALUE <app-id>, которая сохраняет переопределение постоянно для конкретного пользователя без пересборки пакета. Однако для переменных, без которых приложение не может корректно запуститься, правильное место - именно finish-args в манифесте: тогда переменная будет установлена у любого пользователя сразу после установки, а не только у тех, кто отдельно выполнил override.

Заключение

Один унифицированный Flatpak-инсталлер обеспечивает установку пакета одинаковым образом на Ubuntu, Fedora и openSUSE, без отдельной работы с зависимостями и версиями системных библиотек для каждого дистрибутива. Для Avalonia-приложений этот подход особенно удобен, поскольку .NET-рантайм и необходимые нативные библиотеки упаковываются вместе с приложением и не зависят от того, что предустановлено в целевой системе.

У нас были опасения: не приведёт ли изоляция в песочнице к снижению производительности по сравнению с обычной установкой. На практике для рассматриваемого приложения заметного снижения производительности зафиксировано не было - ни при запуске, ни при работе. На более тяжёлых I/O-сценариях разница, по всей видимости, может проявляться сильнее, однако как общий аргумент против использования Flatpak довод о снижении производительности в данном случае не подтвердился.

Flatpak не подойдёт, если приложение должно работать в виде демона - классические deb/rpm будут удобнее. Также, если требуется компактный размер дистрибутива, лучше остаться на deb/rpm.

Во всех остальных случаях Flatpak существенно снижает издержки, связанные с фрагментацией Linux-экосистемы, и позволяет избежать поддержки отдельного пайплайна под каждый пакетный менеджер. Изолированная от интернета сеть также не является препятствием.