From 0bc5e8e9e66eae9ebaec9197f2a34f0af1359a6b Mon Sep 17 00:00:00 2001 From: Github-actions Date: Wed, 10 Jan 2024 14:45:21 +0000 Subject: [PATCH 1/2] Automatic linting --- lib/providers/gallery.dart | 3 +- lib/widgets/add_exercise/preview_images.dart | 60 ++++++++--------- .../add_exercise/steps/step2variations.dart | 66 ++++++++----------- lib/widgets/dashboard/calendar.dart | 40 ++++++----- lib/widgets/exercises/exercises.dart | 7 +- lib/widgets/nutrition/charts.dart | 4 +- lib/widgets/workouts/day.dart | 18 +++-- lib/widgets/workouts/log.dart | 30 ++++----- 8 files changed, 104 insertions(+), 124 deletions(-) diff --git a/lib/providers/gallery.dart b/lib/providers/gallery.dart index f843e0443..f55a81191 100644 --- a/lib/providers/gallery.dart +++ b/lib/providers/gallery.dart @@ -31,8 +31,7 @@ class GalleryProvider extends WgerBaseProvider with ChangeNotifier { List images = []; - GalleryProvider(super.auth, List entries, [super.client]) - : images = entries; + GalleryProvider(super.auth, List entries, [super.client]) : images = entries; /// Clears all lists void clear() { diff --git a/lib/widgets/add_exercise/preview_images.dart b/lib/widgets/add_exercise/preview_images.dart index 65a465670..7b4efc2fe 100644 --- a/lib/widgets/add_exercise/preview_images.dart +++ b/lib/widgets/add_exercise/preview_images.dart @@ -18,41 +18,39 @@ class PreviewExerciseImages extends StatelessWidget with ExerciseImagePickerMixi return SizedBox( height: 300, child: ListView(scrollDirection: Axis.horizontal, children: [ - ...selectedImages - .map( - (file) => SizedBox( - height: 200, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Stack( - children: [ - Image.file(file), - Positioned( - bottom: 0, - right: 0, - child: Padding( - padding: const EdgeInsets.all(3.0), - child: Container( - decoration: BoxDecoration( - color: Colors.grey.withOpacity(0.5), - borderRadius: const BorderRadius.all(Radius.circular(20)), - ), - child: IconButton( - iconSize: 20, - onPressed: () => - context.read().removeExercise(file.path), - color: Colors.white, - icon: const Icon(Icons.delete), - ), - ), + ...selectedImages.map( + (file) => SizedBox( + height: 200, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Stack( + children: [ + Image.file(file), + Positioned( + bottom: 0, + right: 0, + child: Padding( + padding: const EdgeInsets.all(3.0), + child: Container( + decoration: BoxDecoration( + color: Colors.grey.withOpacity(0.5), + borderRadius: const BorderRadius.all(Radius.circular(20)), + ), + child: IconButton( + iconSize: 20, + onPressed: () => + context.read().removeExercise(file.path), + color: Colors.white, + icon: const Icon(Icons.delete), ), ), - ], + ), ), - ), + ], ), - ) - , + ), + ), + ), const SizedBox( width: 10, ), diff --git a/lib/widgets/add_exercise/steps/step2variations.dart b/lib/widgets/add_exercise/steps/step2variations.dart index ce25185ea..5e30c0a35 100644 --- a/lib/widgets/add_exercise/steps/step2variations.dart +++ b/lib/widgets/add_exercise/steps/step2variations.dart @@ -28,44 +28,37 @@ class Step2Variations extends StatelessWidget { child: Column( children: [ // Exercise bases with variations - ...exerciseProvider.exerciseBasesByVariation.keys - .map( - (key) => Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - //mainAxisSize: MainAxisSize.max, - children: [ - ...exerciseProvider.exerciseBasesByVariation[key]! - .map( - (base) => Text( - base - .getExercise( - Localizations.localeOf(context).languageCode) - .name, - overflow: TextOverflow.ellipsis, - ), - ) - , - const SizedBox(height: 20), - ], + ...exerciseProvider.exerciseBasesByVariation.keys.map( + (key) => Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + //mainAxisSize: MainAxisSize.max, + children: [ + ...exerciseProvider.exerciseBasesByVariation[key]!.map( + (base) => Text( + base + .getExercise(Localizations.localeOf(context).languageCode) + .name, + overflow: TextOverflow.ellipsis, + ), ), - ), - Consumer( - builder: (ctx, provider, __) => Switch( - value: provider.variationId == key, - onChanged: (state) => provider.variationId = key), - ), - ], + const SizedBox(height: 20), + ], + ), + ), + Consumer( + builder: (ctx, provider, __) => Switch( + value: provider.variationId == key, + onChanged: (state) => provider.variationId = key), ), - ) - , + ], + ), + ), // Exercise bases without variations - ...exerciseProvider.bases - .where((b) => b.variationId == null) - .map( + ...exerciseProvider.bases.where((b) => b.variationId == null).map( (base) => Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -92,8 +85,7 @@ class Step2Variations extends StatelessWidget { ), ], ), - ) - , + ), ], ), ), diff --git a/lib/widgets/dashboard/calendar.dart b/lib/widgets/dashboard/calendar.dart index f7ba8ba60..60f30c45b 100644 --- a/lib/widgets/dashboard/calendar.dart +++ b/lib/widgets/dashboard/calendar.dart @@ -250,27 +250,25 @@ class _DashboardCalendarWidgetState extends State valueListenable: _selectedEvents, builder: (context, value, _) => Column( children: [ - ...value - .map((event) => ListTile( - title: Text((() { - switch (event.type) { - case EventType.caloriesDiary: - return AppLocalizations.of(context).nutritionalDiary; - - case EventType.session: - return AppLocalizations.of(context).workoutSession; - - case EventType.weight: - return AppLocalizations.of(context).weight; - - case EventType.measurement: - return AppLocalizations.of(context).measurement; - } - })()), - subtitle: Text(event.description), - //onTap: () => print('$event tapped!'), - )) - + ...value.map((event) => ListTile( + title: Text((() { + switch (event.type) { + case EventType.caloriesDiary: + return AppLocalizations.of(context).nutritionalDiary; + + case EventType.session: + return AppLocalizations.of(context).workoutSession; + + case EventType.weight: + return AppLocalizations.of(context).weight; + + case EventType.measurement: + return AppLocalizations.of(context).measurement; + } + })()), + subtitle: Text(event.description), + //onTap: () => print('$event tapped!'), + )) ], ), ), diff --git a/lib/widgets/exercises/exercises.dart b/lib/widgets/exercises/exercises.dart index a60e10bef..607bd2238 100644 --- a/lib/widgets/exercises/exercises.dart +++ b/lib/widgets/exercises/exercises.dart @@ -341,12 +341,9 @@ class MuscleWidget extends StatelessWidget { return Stack( children: [ SvgPicture.asset('assets/images/muscles/$background.svg'), - ...muscles - .map((m) => SvgPicture.asset('assets/images/muscles/main/muscle-${m.id}.svg')) - , + ...muscles.map((m) => SvgPicture.asset('assets/images/muscles/main/muscle-${m.id}.svg')), ...musclesSecondary - .map((m) => SvgPicture.asset('assets/images/muscles/secondary/muscle-${m.id}.svg')) - , + .map((m) => SvgPicture.asset('assets/images/muscles/secondary/muscle-${m.id}.svg')), ], ); } diff --git a/lib/widgets/nutrition/charts.dart b/lib/widgets/nutrition/charts.dart index e4ec2dd28..b7f9b2571 100644 --- a/lib/widgets/nutrition/charts.dart +++ b/lib/widgets/nutrition/charts.dart @@ -158,7 +158,7 @@ class NutritionalDiaryChartWidgetFl extends StatefulWidget { const NutritionalDiaryChartWidgetFl({ super.key, required NutritionalPlan nutritionalPlan, - }) : _nutritionalPlan = nutritionalPlan; + }) : _nutritionalPlan = nutritionalPlan; final NutritionalPlan _nutritionalPlan; @@ -397,7 +397,7 @@ class FlNutritionalDiaryChartWidget extends StatefulWidget { const FlNutritionalDiaryChartWidget({ super.key, required NutritionalPlan nutritionalPlan, - }) : _nutritionalPlan = nutritionalPlan; + }) : _nutritionalPlan = nutritionalPlan; final Color barColor = Colors.red; final Color touchedBarColor = Colors.deepOrange; diff --git a/lib/widgets/workouts/day.dart b/lib/widgets/workouts/day.dart index dbd923dcc..1a50098d7 100644 --- a/lib/widgets/workouts/day.dart +++ b/lib/widgets/workouts/day.dart @@ -131,16 +131,14 @@ class _WorkoutDayWidgetState extends State { child: Column( children: [ if (set.comment != '') MutedText(set.comment), - ...set.settingsFiltered - .map( - (setting) => SettingWidget( - set: set, - setting: setting, - expanded: _expanded, - toggle: _toggleExpanded, - ), - ) - , + ...set.settingsFiltered.map( + (setting) => SettingWidget( + set: set, + setting: setting, + expanded: _expanded, + toggle: _toggleExpanded, + ), + ), const Divider(), ], ), diff --git a/lib/widgets/workouts/log.dart b/lib/widgets/workouts/log.dart index 1909b2565..d6ab15624 100644 --- a/lib/widgets/workouts/log.dart +++ b/lib/widgets/workouts/log.dart @@ -124,23 +124,21 @@ class _DayLogWidgetState extends State { ) else Container(), - ...widget._exerciseData[base]! - .map( - (log) => Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(log.singleLogRepTextNoNl), - IconButton( - icon: const Icon(Icons.delete), - onPressed: () async { - showDeleteDialog( - context, exercise.name, log, exercise, widget._exerciseData); - }, - ), - ], + ...widget._exerciseData[base]!.map( + (log) => Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(log.singleLogRepTextNoNl), + IconButton( + icon: const Icon(Icons.delete), + onPressed: () async { + showDeleteDialog( + context, exercise.name, log, exercise, widget._exerciseData); + }, ), - ) - , + ], + ), + ), Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: ExerciseLogChart(base, widget._date), From 988304ff55e9e3edcdd3161642eb03e674b3b3fe Mon Sep 17 00:00:00 2001 From: Miroslav Mazel Date: Wed, 10 Jan 2024 18:53:04 +0100 Subject: [PATCH 2/2] Flatpak script updates --- .github/workflows/build-release.yml | 7 +- ...pdata.xml => de.wger.flutter.metainfo.xml} | 0 flatpak/flatpak_meta.json | 8 +- flatpak/scripts/flatpak_packager.dart | 123 +++++++------ flatpak/scripts/flatpak_shared.dart | 166 +++++++++++------- flatpak/scripts/manifest_generator.dart | 43 +++-- flatpak/scripts/pubspec.lock | 18 +- 7 files changed, 219 insertions(+), 146 deletions(-) rename flatpak/{de.wger.flutter.appdata.xml => de.wger.flutter.metainfo.xml} (100%) diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 85c1fb8d5..5aa9fc479 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -12,10 +12,9 @@ jobs: uses: actions/checkout@v4 - name: Setup Java - uses: actions/setup-java@v4 + uses: actions/setup-java@v3 with: - java-version: '17' - distribution: 'oracle' + java-version: 17.x - name: Setup Ruby uses: ruby/setup-ruby@v1 @@ -64,7 +63,7 @@ jobs: flutter build linux --release cd flatpak/scripts dart pub get - dart flatpak_packager.dart --meta ../flatpak_meta.json --github + dart flatpak_packager.dart --meta ../flatpak_meta.json --github --addTodaysVersion ${{ env.VERSION }} - name: Build AAB run: flutter build appbundle --release diff --git a/flatpak/de.wger.flutter.appdata.xml b/flatpak/de.wger.flutter.metainfo.xml similarity index 100% rename from flatpak/de.wger.flutter.appdata.xml rename to flatpak/de.wger.flutter.metainfo.xml diff --git a/flatpak/flatpak_meta.json b/flatpak/flatpak_meta.json index 5d408ead3..253cf82ef 100644 --- a/flatpak/flatpak_meta.json +++ b/flatpak/flatpak_meta.json @@ -3,14 +3,8 @@ "lowercaseAppName": "wger", "githubReleaseOrganization": "wger-project", "githubReleaseProject": "flutter", - "localReleases": [ - - ], - "localReleaseAssets": [ - - ], "localLinuxBuildDir": "../build/linux", - "appDataPath": "de.wger.flutter.appdata.xml", + "appStreamPath": "de.wger.flutter.metainfo.xml", "desktopPath": "de.wger.flutter.desktop", "icons": { "64x64": "logo64.png", diff --git a/flatpak/scripts/flatpak_packager.dart b/flatpak/scripts/flatpak_packager.dart index 5346a6056..92cf27681 100644 --- a/flatpak/scripts/flatpak_packager.dart +++ b/flatpak/scripts/flatpak_packager.dart @@ -1,17 +1,27 @@ // ignore_for_file: avoid_print import 'dart:io'; - import 'flatpak_shared.dart'; +/// Creates an archive containing all the sources for the Flatpak package for a +/// specific architecture. +/// /// arguments: /// --meta [file] +/// Required argument for providing the metadata file for this script. + /// --github +/// Use this option to pull release info from Github rather than the metadata file. + +/// --addTodaysVersion [version] +/// If pulling data from Github, this provides a way to specify the release to be released today. + void main(List arguments) async { - if (Platform.isWindows) { - throw Exception('Must be run under a UNIX-like operating system.'); + if (!Platform.isLinux) { + throw Exception('Must be run under Linux'); } + // PARSE ARGUMENTS final metaIndex = arguments.indexOf('--meta'); if (metaIndex == -1) { throw Exception( @@ -22,123 +32,136 @@ void main(List arguments) async { } final metaFile = File(arguments[metaIndex + 1]); - if (!metaFile.existsSync()) { + if (!(await metaFile.exists())) { throw Exception('The provided metadata file does not exist.'); } - final meta = FlatpakMeta.fromJson(metaFile); - final fetchFromGithub = arguments.contains('--github'); + final addTodaysVersionIndex = arguments.indexOf('--addTodaysVersion'); + if (addTodaysVersionIndex != -1 && arguments.length == addTodaysVersionIndex + 1) { + throw Exception('The --addTodaysVersion flag must be followed by the version name.'); + } + + final addedTodaysVersion = + addTodaysVersionIndex != -1 ? arguments[addTodaysVersionIndex + 1] : null; + + // GENERATE PACKAGE + + final meta = FlatpakMeta.fromJson(metaFile, skipLocalReleases: fetchFromGithub); + final outputDir = Directory('${Directory.current.path}/flatpak_generator_exports'); - outputDir.createSync(); + await outputDir.create(); - final packageGenerator = PackageGenerator(inputDir: metaFile.parent, meta: meta); + final packageGenerator = PackageGenerator( + inputDir: metaFile.parent, meta: meta, addedTodaysVersion: addedTodaysVersion); - packageGenerator.generatePackage( - outputDir, - PackageGenerator.runningOnARM() ? CPUArchitecture.aarch64 : CPUArchitecture.x86_64, - fetchFromGithub, - ); + await packageGenerator.generatePackage( + outputDir, + await PackageGenerator.runningOnARM() ? CPUArchitecture.aarch64 : CPUArchitecture.x86_64, + fetchFromGithub); } class PackageGenerator { final Directory inputDir; final FlatpakMeta meta; - final Map shaByArch = {}; + final String? addedTodaysVersion; - PackageGenerator({required this.inputDir, required this.meta}); + PackageGenerator({required this.inputDir, required this.meta, required this.addedTodaysVersion}); Future generatePackage( Directory outputDir, CPUArchitecture arch, bool fetchReleasesFromGithub) async { - final tempDir = outputDir.createTempSync('flutter_generator_temp'); + final tempDir = await outputDir.createTemp('flutter_generator_temp'); final appId = meta.appId; // desktop file final desktopFile = File('${inputDir.path}/${meta.desktopPath}'); - if (!desktopFile.existsSync()) { + if (!(await desktopFile.exists())) { throw Exception( 'The desktop file does not exist under the specified path: ${desktopFile.path}'); } - desktopFile.copySync('${tempDir.path}/$appId.desktop'); + await desktopFile.copy('${tempDir.path}/$appId.desktop'); // icons final iconTempDir = Directory('${tempDir.path}/icons'); for (final icon in meta.icons) { final iconFile = File('${inputDir.path}/${icon.path}'); - if (!iconFile.existsSync()) { + if (!(await iconFile.exists())) { throw Exception('The icon file ${iconFile.path} does not exist.'); } final iconSubdir = Directory('${iconTempDir.path}/${icon.type}'); - iconSubdir.createSync(recursive: true); - iconFile.copySync('${iconSubdir.path}/${icon.getFilename(appId)}'); + await iconSubdir.create(recursive: true); + await iconFile.copy('${iconSubdir.path}/${icon.getFilename(appId)}'); } - // appdata file - final origAppDataFile = File('${inputDir.path}/${meta.appDataPath}'); - if (!origAppDataFile.existsSync()) { + // AppStream metainfo file + final origAppStreamFile = File('${inputDir.path}/${meta.appStreamPath}'); + if (!(await origAppStreamFile.exists())) { throw Exception( - 'The app data file does not exist under the specified path: ${origAppDataFile.path}'); + 'The app data file does not exist under the specified path: ${origAppStreamFile.path}'); } - final editedAppDataContent = AppDataModifier.replaceVersions( - origAppDataFile.readAsStringSync(), await meta.getReleases(fetchReleasesFromGithub)); + final editedAppStreamContent = AppStreamModifier.replaceVersions( + await origAppStreamFile.readAsString(), + await meta.getReleases(fetchReleasesFromGithub, addedTodaysVersion)); - final editedAppDataFile = File('${tempDir.path}/$appId.appdata.xml'); - editedAppDataFile.writeAsStringSync(editedAppDataContent); + final editedAppStreamFile = File('${tempDir.path}/$appId.metainfo.xml'); + await editedAppStreamFile.writeAsString(editedAppStreamContent); // build files final bundlePath = '${inputDir.path}/${meta.localLinuxBuildDir}/${arch.flutterDirName}/release/bundle'; final buildDir = Directory(bundlePath); - if (!buildDir.existsSync()) { + if (!(await buildDir.exists())) { throw Exception( 'The linux build directory does not exist under the specified path: ${buildDir.path}'); } final destDir = Directory('${tempDir.path}/bin'); - destDir.createSync(); + await destDir.create(); + + final baseFilename = '${meta.lowercaseAppName}-linux-${arch.flatpakArchCode}'; + final packagePath = '${outputDir.absolute.path}/$baseFilename.tar.gz'; + final shaPath = '${outputDir.absolute.path}/$baseFilename.sha256'; - final baseFileName = '${meta.lowercaseAppName}-linux-${arch.flatpakArchCode}'; + await Process.run('cp', ['-r', '${buildDir.absolute.path}/.', destDir.absolute.path]); + await Process.run('tar', ['-czvf', packagePath, '.'], workingDirectory: tempDir.absolute.path); - final packagePath = '${outputDir.absolute.path}/$baseFileName.tar.gz'; - Process.runSync('cp', ['-r', '${buildDir.absolute.path}/.', destDir.absolute.path]); - Process.runSync('tar', ['-czvf', packagePath, '.'], workingDirectory: tempDir.absolute.path); print('Generated $packagePath'); - final preShasum = Process.runSync('shasum', ['-a', '256', packagePath]); - final sha256 = preShasum.stdout.toString().split(' ').first; + final preShasum = await Process.run('shasum', ['-a', '256', packagePath]); + final shasum = preShasum.stdout.toString().split(' ').first; - final shaFile = await File('${outputDir.path}/$baseFileName.sha256').writeAsString(sha256); - print('Generated ${shaFile.path}'); + await File(shaPath).writeAsString(shasum); - shaByArch.putIfAbsent(arch, () => sha256); + print('Generated $shaPath'); - tempDir.deleteSync(recursive: true); + await tempDir.delete(recursive: true); } - static bool runningOnARM() { - final unameRes = Process.runSync('uname', ['-m']); + static Future runningOnARM() async { + final unameRes = await Process.run('uname', ['-m']); final unameString = unameRes.stdout.toString().trimLeft(); return unameString.startsWith('arm') || unameString.startsWith('aarch'); } } -// updates releases in ${appName}.appdata.xml -class AppDataModifier { - static String replaceVersions(String origAppDataContent, List versions) { +// updates releases in ${appName}.metainfo.xml +class AppStreamModifier { + static String replaceVersions(String origAppStreamContent, List versions) { final joinedReleases = versions.map((v) => '\t\t').join('\n'); - final releasesSection = '\n$joinedReleases\n\t'; //todo check this - if (origAppDataContent.contains('') .replaceFirst(RegExp(''), releasesSection) .replaceAll('<~>', '\n'); } else { - return origAppDataContent.replaceFirst('', '\n\t$releasesSection\n'); + return origAppStreamContent.replaceFirst( + '', '\n\t$releasesSection\n'); } } } diff --git a/flatpak/scripts/flatpak_shared.dart b/flatpak/scripts/flatpak_shared.dart index f411aabef..d3ad12adb 100644 --- a/flatpak/scripts/flatpak_shared.dart +++ b/flatpak/scripts/flatpak_shared.dart @@ -3,20 +3,21 @@ import 'dart:io'; import 'package:http/http.dart' as http; +/// Shared files for the two Flatpak-related scripts. + class Release { final String version; - final String date; //todo add resources + final String date; //TODO add resources Release({required this.version, required this.date}); } enum CPUArchitecture { x86_64('x86_64', 'x64'), - aarch64('aarch64', 'aarch64'); + aarch64('aarch64', 'arm64'); final String flatpakArchCode; final String flutterDirName; - const CPUArchitecture(this.flatpakArchCode, this.flutterDirName); } @@ -43,8 +44,9 @@ class Icon { _fileExtension = path.split('.').last; } - String getFilename(String appId) => - (type == _symbolicType) ? '$appId-symbolic.$_fileExtension' : '$appId.$_fileExtension'; + String getFilename(String appId) => (type == _symbolicType) + ? '$appId-symbolic.$_fileExtension' + : '$appId.$_fileExtension'; } class GithubReleases { @@ -55,25 +57,26 @@ class GithubReleases { GithubReleases(this.githubReleaseOrganization, this.githubReleaseProject); - Future> getReleases() async { + Future> getReleases(bool canBeEmpty) async { if (_releases == null) { - await _fetchReleasesAndAssets(); + await _fetchReleasesAndAssets(canBeEmpty); } return _releases!; } Future?> getLatestReleaseAssets() async { if (_releases == null) { - await _fetchReleasesAndAssets(); + await _fetchReleasesAndAssets(false); } return _latestReleaseAssets; } - Future _fetchReleasesAndAssets() async { + Future _fetchReleasesAndAssets(bool canBeEmpty) async { final releaseJsonContent = (await http.get(Uri( scheme: 'https', host: 'api.github.com', - path: '/repos/$githubReleaseOrganization/$githubReleaseProject/releases'))) + path: + '/repos/$githubReleaseOrganization/$githubReleaseProject/releases'))) .body; final decodedJson = jsonDecode(releaseJsonContent) as List; @@ -84,27 +87,33 @@ class GithubReleases { await Future.forEach(decodedJson, (dynamic releaseDynamic) async { final releaseMap = releaseDynamic as Map; - final releaseDateAndTime = DateTime.parse(releaseMap['published_at'] as String); - final releaseDateString = releaseDateAndTime.toIso8601String().split('T').first; + final releaseDateAndTime = + DateTime.parse(releaseMap['published_at'] as String); + final releaseDateString = + releaseDateAndTime.toIso8601String().split('T').first; if (latestReleaseAssetDate == null || (latestReleaseAssetDate?.compareTo(releaseDateAndTime) == -1)) { - final assets = await _parseReleaseAssets(releaseMap['assets'] as List); + final assets = + await _parseGithubReleaseAssets(releaseMap['assets'] as List); if (assets != null) { _latestReleaseAssets = assets; latestReleaseAssetDate = releaseDateAndTime; } } - releases.add(Release(version: releaseMap['name'] as String, date: releaseDateString)); + releases.add(Release( + version: releaseMap['name'] as String, date: releaseDateString)); }); - if (releases.isNotEmpty) { + if (releases.isNotEmpty || canBeEmpty) { _releases = releases; + } else { + throw Exception("Github must contain at least 1 release."); } } - Future?> _parseReleaseAssets(List assetMaps) async { + Future?> _parseGithubReleaseAssets(List assetMaps) async { String? x64TarballUrl; String? x64Sha; String? aarch64TarballUrl; @@ -115,7 +124,8 @@ class GithubReleases { final downloadUrl = amMap['browser_download_url'] as String; final filename = amMap['name'] as String; final fileExtension = filename.substring(filename.indexOf('.') + 1); - final filenameWithoutExtension = filename.substring(0, filename.indexOf('.')); + final filenameWithoutExtension = + filename.substring(0, filename.indexOf('.')); final arch = filenameWithoutExtension.endsWith('aarch64') ? CPUArchitecture.aarch64 @@ -166,15 +176,14 @@ class GithubReleases { return res.isEmpty ? null : res; } - Future _readSha(String shaUrl) async { - return (await http.get(Uri.parse(shaUrl))).body.split(' ').first; - } + Future _readSha(String shaUrl) async => + (await http.get(Uri.parse(shaUrl))).body.split(' ').first; } class FlatpakMeta { final String appId; final String lowercaseAppName; - final String appDataPath; + final String appStreamPath; final String desktopPath; final List icons; @@ -202,7 +211,7 @@ class FlatpakMeta { required List? localReleases, required List? localReleaseAssets, required this.localLinuxBuildDir, - required this.appDataPath, + required this.appStreamPath, required this.desktopPath, required this.icons, required this.freedesktopRuntime, @@ -212,88 +221,117 @@ class FlatpakMeta { : _localReleases = localReleases, _localReleaseAssets = localReleaseAssets { if (githubReleaseOrganization != null && githubReleaseProject != null) { - _githubReleases = GithubReleases(githubReleaseOrganization!, githubReleaseProject!); + _githubReleases = + GithubReleases(githubReleaseOrganization!, githubReleaseProject!); } } - Future> getReleases(bool fetchReleasesFromGithub) async { + Future> getReleases( + bool fetchReleasesFromGithub, String? addedTodaysVersion) async { + final releases = List.empty(growable: true); + if (addedTodaysVersion != null) { + releases.add(Release( + version: addedTodaysVersion, + date: DateTime.now().toIso8601String().split("T").first)); + } if (fetchReleasesFromGithub) { if (_githubReleases == null) { throw Exception( 'Metadata must include Github repository info if fetching releases from Github.'); } - return await _githubReleases!.getReleases(); + releases.addAll( + await _githubReleases!.getReleases(addedTodaysVersion != null)); } else { - if (_localReleases == null) { - throw Exception('Metadata must include releases if not fetching releases from Github.'); + if (_localReleases == null && addedTodaysVersion == null) { + throw Exception( + 'Metadata must include releases if not fetching releases from Github.'); + } + if (_localReleases?.isNotEmpty ?? false) { + releases.addAll(_localReleases!); } - return _localReleases!; } + return releases; } - Future?> getReleaseAssets(bool fetchReleasesFromGithub) async { + Future?> getLatestReleaseAssets( + bool fetchReleasesFromGithub) async { if (fetchReleasesFromGithub) { if (_githubReleases == null) { throw Exception( 'Metadata must include Github repository info if fetching releases from Github.'); } - return _githubReleases!.getLatestReleaseAssets(); + return await _githubReleases!.getLatestReleaseAssets(); } else { if (_localReleases == null) { - throw Exception('Metadata must include releases if not fetching releases from Github.'); + throw Exception( + 'Metadata must include releases if not fetching releases from Github.'); } return _localReleaseAssets; } } - static FlatpakMeta fromJson(File jsonFile) { + static FlatpakMeta fromJson(File jsonFile, {bool skipLocalReleases = false}) { try { final dynamic json = jsonDecode(jsonFile.readAsStringSync()); return FlatpakMeta( appId: json['appId'] as String, lowercaseAppName: json['lowercaseAppName'] as String, - githubReleaseOrganization: json['githubReleaseOrganization'] as String?, + githubReleaseOrganization: + json['githubReleaseOrganization'] as String?, githubReleaseProject: json['githubReleaseProject'] as String?, - localReleases: (json['localReleases'] as List?)?.map((dynamic r) { - final rMap = r as Map; - return Release(version: rMap['version'] as String, date: rMap['date'] as String); - }).toList(), - localReleaseAssets: (json['localReleaseAssets'] as List?)?.map((dynamic ra) { - final raMap = ra as Map; - final archString = raMap['arch'] as String; - final arch = (archString == CPUArchitecture.x86_64.flatpakArchCode) - ? CPUArchitecture.x86_64 - : (archString == CPUArchitecture.aarch64.flatpakArchCode) - ? CPUArchitecture.aarch64 - : null; - if (arch == null) { - throw Exception( - 'Architecture must be either "${CPUArchitecture.x86_64.flatpakArchCode}" or "${CPUArchitecture.aarch64.flatpakArchCode}"'); - } - final tarballPath = '${jsonFile.parent.path}/${raMap['tarballPath'] as String}'; - final preShasum = Process.runSync('shasum', ['-a', '256', tarballPath]); - final shasum = preShasum.stdout.toString().split(' ').first; - if (preShasum.exitCode != 0) { - throw Exception(preShasum.stderr); - } - return ReleaseAsset( - arch: arch, - tarballUrlOrPath: tarballPath, - isRelativeLocalPath: true, - tarballSha256: shasum); - }).toList(), + localReleases: skipLocalReleases + ? null + : (json['localReleases'] as List?)?.map((dynamic r) { + final rMap = r as Map; + return Release( + version: rMap['version'] as String, + date: rMap['date'] as String); + }).toList(), + localReleaseAssets: skipLocalReleases + ? null + : (json['localReleaseAssets'] as List?)?.map((dynamic ra) { + final raMap = ra as Map; + final archString = raMap['arch'] as String; + final arch = (archString == + CPUArchitecture.x86_64.flatpakArchCode) + ? CPUArchitecture.x86_64 + : (archString == CPUArchitecture.aarch64.flatpakArchCode) + ? CPUArchitecture.aarch64 + : null; + if (arch == null) { + throw Exception( + 'Architecture must be either "${CPUArchitecture.x86_64.flatpakArchCode}" or "${CPUArchitecture.aarch64.flatpakArchCode}"'); + } + final tarballFile = File( + '${jsonFile.parent.path}/${raMap['tarballPath'] as String}'); + final tarballPath = tarballFile.absolute.path; + final preShasum = + Process.runSync('shasum', ['-a', '256', tarballPath]); + final shasum = preShasum.stdout.toString().split(' ').first; + if (preShasum.exitCode != 0) { + throw Exception(preShasum.stderr); + } + return ReleaseAsset( + arch: arch, + tarballUrlOrPath: tarballPath, + isRelativeLocalPath: true, + tarballSha256: shasum); + }).toList(), localLinuxBuildDir: json['localLinuxBuildDir'] as String, - appDataPath: json['appDataPath'] as String, + appStreamPath: json['appStreamPath'] as String, desktopPath: json['desktopPath'] as String, icons: (json['icons'] as Map).entries.map((mapEntry) { - return Icon(type: mapEntry.key as String, path: mapEntry.value as String); + return Icon( + type: mapEntry.key as String, path: mapEntry.value as String); }).toList(), freedesktopRuntime: json['freedesktopRuntime'] as String, buildCommandsAfterUnpack: (json['buildCommandsAfterUnpack'] as List?) ?.map((dynamic bc) => bc as String) .toList(), extraModules: json['extraModules'] as List?, - finishArgs: (json['finishArgs'] as List).map((dynamic fa) => fa as String).toList()); + finishArgs: (json['finishArgs'] as List) + .map((dynamic fa) => fa as String) + .toList()); } catch (e) { throw Exception('Could not parse JSON file, due to this error:\n$e'); } diff --git a/flatpak/scripts/manifest_generator.dart b/flatpak/scripts/manifest_generator.dart index da27f8609..8957a3d16 100644 --- a/flatpak/scripts/manifest_generator.dart +++ b/flatpak/scripts/manifest_generator.dart @@ -1,8 +1,20 @@ +// ignore_for_file: avoid_print + import 'dart:convert'; import 'dart:io'; - import 'flatpak_shared.dart'; +/// Generates the Flatpak manifest. +/// (Separate from the package generation, as those are generated per each +/// architecture.) +/// +/// arguments: +/// --meta [file] +/// Required argument for providing the metadata file for this script. + +/// --github +/// Use this option to pull release info from Github rather than the metadata file. + void main(List arguments) async { if (Platform.isWindows) { throw Exception('Must be run under a UNIX-like operating system.'); @@ -14,7 +26,8 @@ void main(List arguments) async { 'You must run this script with a metadata file argument, using the --meta flag.'); } if (arguments.length == metaIndex + 1) { - throw Exception('The --meta flag must be followed by the path to the metadata file.'); + throw Exception( + 'The --meta flag must be followed by the path to the metadata file.'); } final metaFile = File(arguments[metaIndex + 1]); @@ -22,21 +35,25 @@ void main(List arguments) async { throw Exception('The provided metadata file does not exist.'); } - final meta = FlatpakMeta.fromJson(metaFile); - final fetchFromGithub = arguments.contains('--github'); - final outputDir = Directory('${Directory.current.path}/flatpak_generator_exports'); + final meta = + FlatpakMeta.fromJson(metaFile, skipLocalReleases: fetchFromGithub); + + final outputDir = + Directory('${Directory.current.path}/flatpak_generator_exports'); outputDir.createSync(); final manifestGenerator = FlatpakManifestGenerator(meta); - final manifestContent = await manifestGenerator.generateFlatpakManifest(fetchFromGithub); + final manifestContent = + await manifestGenerator.generateFlatpakManifest(fetchFromGithub); final manifestPath = '${outputDir.path}/${meta.appId}.json'; final manifestFile = File(manifestPath); manifestFile.writeAsStringSync(manifestContent); print('Generated $manifestPath'); - final flathubJsonContent = await manifestGenerator.generateFlathubJson(fetchFromGithub); + final flathubJsonContent = + await manifestGenerator.generateFlathubJson(fetchFromGithub); if (flathubJsonContent != null) { final flathubJsonPath = '${outputDir.path}/flathub.json'; final flathubJsonFile = File(flathubJsonPath); @@ -56,7 +73,7 @@ class FlatpakManifestGenerator { Future generateFlatpakManifest(bool fetchFromGithub) async { final appName = meta.lowercaseAppName; final appId = meta.appId; - final assets = await meta.getReleaseAssets(fetchFromGithub); + final assets = await meta.getLatestReleaseAssets(fetchFromGithub); if (assets == null) { throw Exception('There are no associated assets.'); @@ -88,7 +105,7 @@ class FlatpakManifestGenerator { ...meta.icons.map((icon) => 'install -Dm644 $appName/icons/${icon.type}/${icon.getFilename(appId)} /app/share/icons/hicolor/${icon.type}/apps/${icon.getFilename(appId)}'), 'install -Dm644 $appName/$appId.desktop /app/share/applications/$appId.desktop', - 'install -Dm644 $appName/$appId.appdata.xml /app/share/applications/$appId.appdata.xml' + 'install -Dm644 $appName/$appId.metainfo.xml /app/share/metainfo/$appId.metainfo.xml' ], 'sources': assets .map((a) => { @@ -105,7 +122,7 @@ class FlatpakManifestGenerator { } Future generateFlathubJson(bool fetchFromGithub) async { - final assets = await meta.getReleaseAssets(fetchFromGithub); + final assets = await meta.getLatestReleaseAssets(fetchFromGithub); if (assets == null) { throw Exception('There are no associated assets.'); @@ -115,7 +132,8 @@ class FlatpakManifestGenerator { const encoder = JsonEncoder.withIndent(' '); - final onlyArchListInput = fetchFromGithub ? _githubArchSupport! : _localArchSupport!; + final onlyArchListInput = + fetchFromGithub ? _githubArchSupport! : _localArchSupport!; final onlyArchList = List.empty(growable: true); for (final e in onlyArchListInput.entries) { @@ -131,7 +149,8 @@ class FlatpakManifestGenerator { } } - void _lazyGenerateArchSupportMap(bool fetchFromGithub, List assets) { + void _lazyGenerateArchSupportMap( + bool fetchFromGithub, List assets) { if (fetchFromGithub) { if (_githubArchSupport == null) { _githubArchSupport = { diff --git a/flatpak/scripts/pubspec.lock b/flatpak/scripts/pubspec.lock index d3d4f8469..4da5d192b 100644 --- a/flatpak/scripts/pubspec.lock +++ b/flatpak/scripts/pubspec.lock @@ -13,18 +13,18 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.18.0" http: dependency: "direct main" description: name: http - sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" + sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" url: "https://pub.dev" source: hosted - version: "0.13.5" + version: "0.13.6" http_parser: dependency: transitive description: @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.11.0" path: dependency: transitive description: @@ -77,9 +77,9 @@ packages: dependency: transitive description: name: typed_data - sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" sdks: - dart: ">=2.18.5 <4.0.0" + dart: ">=2.19.0 <4.0.0"