feat: update to v1.7.3 with smart asset detection and auto-install
Some checks failed
CI / run (push) Waiting to run
Upload store MSIX to release / upload-store-msix-to-release (push) Has been cancelled

This commit is contained in:
Umbrix Developer
2026-01-18 20:14:19 +03:00
parent e79b508531
commit 95383d09fc
14 changed files with 940 additions and 8 deletions

View File

@@ -1,8 +1,8 @@
abstract class Constants {
static const appName = "Umbrix";
static const githubUrl = "https://github.com/umbrix-app/umbrix";
static const githubReleasesApiUrl = "https://api.github.com/repos/umbrix-app/umbrix/releases";
static const githubLatestReleaseUrl = "https://github.com/umbrix-app/umbrix/releases/latest";
static const githubUrl = "https://update.umbrix.net/vodorod/umbrix";
static const githubReleasesApiUrl = "https://update.umbrix.net/api/v1/repos/vodorod/umbrix/releases";
static const githubLatestReleaseUrl = "https://update.umbrix.net/vodorod/umbrix/releases/latest";
static const appCastUrl = "http://localhost:8000/api/appcast.xml";
static const telegramChannelUrl = "https://t.me/umbrix_app";
static const privacyPolicyUrl = "https://umbrix.net/privacy.html";
@@ -18,12 +18,14 @@ abstract class Constants {
// 🖥️ Для Linux десктопа используйте: "http://localhost:8000/api/appcast.xml"
// 📱 Для Android эмулятора используйте: "http://10.0.2.2:8000/api/appcast.xml"
// См. документацию в папке: update-server/README.md
static const customUpdateServerUrl = "http://localhost:8000/api/appcast.xml";
// ТЕСТ: Используем httpbin.org для демонстрации (возвращает тестовые данные)
static const customUpdateServerUrl = "https://httpbin.org/json";
// Использовать собственный сервер обновлений вместо GitHub
// true = использовать customUpdateServerUrl (для приватного репозитория)
// false = использовать GitHub Releases (для публичного репозитория)
static const useCustomUpdateServer = true;
static const useCustomUpdateServer = false;
}
const kAnimationDuration = Duration(milliseconds: 250);

View File

@@ -23,6 +23,20 @@ abstract class GithubReleaseParser {
}
final preRelease = json["prerelease"] as bool;
final publishedAt = DateTime.parse(json["published_at"] as String);
// Парсим assets
final List<ReleaseAsset> assets = [];
if (json["assets"] != null) {
for (final assetJson in json["assets"] as List) {
final asset = assetJson as Map<String, dynamic>;
assets.add(ReleaseAsset(
name: asset["name"] as String,
downloadUrl: asset["browser_download_url"] as String,
size: asset["size"] as int,
));
}
}
return RemoteVersionEntity(
version: version,
buildNumber: buildNumber,
@@ -31,6 +45,7 @@ abstract class GithubReleaseParser {
url: json["html_url"] as String,
publishedAt: publishedAt,
flavor: flavor,
assets: assets,
);
}
}

View File

@@ -15,8 +15,27 @@ class RemoteVersionEntity with _$RemoteVersionEntity {
required String url,
required DateTime publishedAt,
required Environment flavor,
@Default([]) List<ReleaseAsset> assets,
}) = _RemoteVersionEntity;
String get presentVersion =>
flavor == Environment.prod ? version : "$version ${flavor.name}";
/// Найти asset по расширению файла
String? findAssetByExtension(String extension) {
try {
return assets.firstWhere((asset) => asset.name.endsWith(extension)).downloadUrl;
} catch (_) {
return null;
}
}
}
@freezed
class ReleaseAsset with _$ReleaseAsset {
const factory ReleaseAsset({
required String name,
required String downloadUrl,
required int size,
}) = _ReleaseAsset;
}

View File

@@ -57,23 +57,55 @@ class NewVersionDialog extends HookConsumerWidget with PresLogger {
downloadProgress.value = 0.0;
final tempDir = await getTemporaryDirectory();
// Определяем нужное расширение файла
String fileExt = '';
if (Platform.isWindows)
fileExt = '.exe';
else if (Platform.isMacOS)
fileExt = '.dmg';
else if (Platform.isLinux) fileExt = '.AppImage';
else if (Platform.isLinux)
fileExt = '.deb';
// Ищем asset с нужным расширением
final downloadUrl = newVersion.findAssetByExtension(fileExt);
if (downloadUrl == null) {
if (context.mounted) {
CustomToast.error('Файл установки $fileExt не найден в релизе').show(context);
}
return;
}
final savePath = '${tempDir.path}/umbrix-${newVersion.version}$fileExt';
final file = File(savePath);
if (await file.exists()) await file.delete();
final dio = Dio();
await dio.download(newVersion.url, savePath, onReceiveProgress: (received, total) {
await dio.download(downloadUrl, savePath, onReceiveProgress: (received, total) {
if (total != -1) downloadProgress.value = received / total;
});
loggy.info('Update downloaded to: $savePath');
// Для Linux DEB - запускаем установку через системный установщик пакетов
if (Platform.isLinux && fileExt == '.deb') {
try {
// Пытаемся установить через pkexec apt install
final result = await Process.run('pkexec', ['apt', 'install', '-y', savePath]);
if (result.exitCode == 0) {
if (context.mounted) {
CustomToast.success('Обновление установлено! Перезапустите приложение.').show(context);
context.pop();
}
return;
}
} catch (e) {
loggy.warning('Failed to install via pkexec apt: $e');
}
}
// Для других платформ или если apt не сработал - просто открываем файл
final result = await OpenFile.open(savePath);
if (result.type != ResultType.done && context.mounted) {