From 4e6643bd3c26ba6908c7773771bbfc81614bb2ff Mon Sep 17 00:00:00 2001 From: Miroslav Mazel Date: Wed, 22 Mar 2023 13:50:46 +0100 Subject: [PATCH] Resources for Flatpak generation --- flatpak/README | 8 + flatpak/de.wger.flutter.appdata.xml | 72 +++++++ flatpak/de.wger.flutter.desktop | 11 + flatpak/flatpak_generator.dart | 305 ++++++++++++++++++++++++++++ flatpak/logo128.png | Bin 0 -> 4888 bytes flatpak/logo512.png | Bin 0 -> 11147 bytes flatpak/logo64.png | Bin 0 -> 3323 bytes flatpak/spec.json | 28 +++ 8 files changed, 424 insertions(+) create mode 100644 flatpak/README create mode 100755 flatpak/de.wger.flutter.appdata.xml create mode 100755 flatpak/de.wger.flutter.desktop create mode 100644 flatpak/flatpak_generator.dart create mode 100644 flatpak/logo128.png create mode 100644 flatpak/logo512.png create mode 100644 flatpak/logo64.png create mode 100644 flatpak/spec.json diff --git a/flatpak/README b/flatpak/README new file mode 100644 index 00000000..42b64942 --- /dev/null +++ b/flatpak/README @@ -0,0 +1,8 @@ +To generate a release: + +1. Build the Linux release using Flutter. +2. Add the new release version and date to the start of the "releases" list in the "spec.json" file (and adjust other parameters in the file if needed). +3. Run "dart flatpak_generator.dart spec.json" in this folder. +4. Upload the generated tar.gz file as a Github release, using the app's version name for the tag (e.g. "1.0.0"). +5. Test the Flatpak using the guide at https://docs.flatpak.org/en/latest/first-build.html, using the generated json manifest as your Flatpak manifest. +6. Update your Flathub manifest file in your Flathub Github repo. (If the "flathub.json" file is not there yet, upload that too.) diff --git a/flatpak/de.wger.flutter.appdata.xml b/flatpak/de.wger.flutter.appdata.xml new file mode 100755 index 00000000..80dec61f --- /dev/null +++ b/flatpak/de.wger.flutter.appdata.xml @@ -0,0 +1,72 @@ + + + de.wger.flutter + wger + Fitness/workout, nutrition and weight tracker + + CC0-1.0 + AGPL-3.0-or-later + + touch + pointing + keyboard + + wger + https://wger.de/ + https://github.com/wger-project/flutter/issues + + + workstation + mobile + + + +

From fitness lovers to fitness lovers – get your health organized with WGER, your Workout Manager!

+

Have you already found your #1 fitness app and do you love to create your own sports routines? No matter what type of sporty beast you are – we all have something in common: We love to keep track of our health data <3

+

So we don’t judge you for still managing your fitness journey with your handy little workout log book but welcome to 2021!

+

We have developed a 100% free digital health and fitness tracker app for you, sized down to the most relevant features to make your life easier. Get started, keep training and celebrate your progress!

+

wger is an Open Source project and all about:

+
    +
  • Your Body
  • +
  • Your Workouts
  • +
  • Your Progress
  • +
  • Your Data
  • +
+

Your Body: + No need to google for the ingredients of your favourite treats – choose your daily meals from more than 78000 products and see the nutritional values. Add meals to the nutritional plan and keep an overview of your diet in the calendar.

+

Your Workouts: + You know what is best for your body. Create your own workouts out of a growing variety from 200 different exercises. Then, use the Gym Mode to guide you through the training while you log your weights with one tap.

+

Your Progress: + Never lose sight of your goals. Track your weight and keep your statistics.

+

Your Data: + wger is your personalized fitness diary – but you own your data. Use the REST API to access and do amazing things with it.

+

Please note: This free app is not based on additional fundings and we don’t ask you to donate money. More than that it is a community project which is growing constantly. So be prepared for new features anytime!

+

OpenSource – what does that mean?

+

Open Source means that the whole source code for this app and the server it talks to is free and available to anybody:

+
    +
  • Do you want to run wger on your own server for you or your local gym? Go ahead!
  • +
  • Do you miss a feature and want to implement it? Start now!
  • +
  • Do you want to check that nothing is being sent anywhere? You can!
  • +
+

Join our community and become a part of sport enthusiasts and IT geeks from all over the world. We keep working on adjusting and optimizing the app customized to our needs. We love your input so feel free to jump in anytime and contribute your wishes and ideas!

+ +
+ + + + wger's dashboard + https://github.com/wger-project/flutter/raw/master/fastlane/metadata/android/en-US/images/phoneScreenshots/01%20-%20dashboard.png + + + + + + + + + + de.wger.flutter.desktop +
\ No newline at end of file diff --git a/flatpak/de.wger.flutter.desktop b/flatpak/de.wger.flutter.desktop new file mode 100755 index 00000000..5e4cf795 --- /dev/null +++ b/flatpak/de.wger.flutter.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Version=1.0 +Type=Application +Name=wger +Comment=Fitness/workout, nutrition and weight tracker +Categories=Education;Utility;Sports; +Icon=de.wger.flutter +Exec=wger +StartupWMClass=wger +X-Purism-FormFactor=Workstation;Mobile; +X-KDE-FormFactors=desktop;tablet;handset;mediacenter; diff --git a/flatpak/flatpak_generator.dart b/flatpak/flatpak_generator.dart new file mode 100644 index 00000000..9d4a6225 --- /dev/null +++ b/flatpak/flatpak_generator.dart @@ -0,0 +1,305 @@ +// ignore_for_file: avoid_classes_with_only_static_members, avoid_print + +import 'dart:convert'; +import 'dart:io'; + +void main(List arguments) { + if (arguments.length != 1) { + throw Exception('Must have only one argument: the path to the JSON specification.'); + } + if (!Platform.isLinux) { + throw Exception('Must be run under x86_64 Linux'); + } + final jsonFile = File(arguments[0]); + if (!jsonFile.existsSync()) { + throw Exception('The provided JSON file does not exist.'); + } + final specJson = SpecJson.fromJson(jsonFile); + + final outputDir = Directory.current; + + final packageGenerator = PackageGenerator(inputDir: jsonFile.parent, specJson: specJson); + packageGenerator.generatePackage(Directory.current, _Platform.x86_64); + + if (specJson.linuxArmReleaseBundleDirPath != null) { + packageGenerator.generatePackage(Directory.current, _Platform.aarch64); + } + + final sha256x64 = packageGenerator.sha256x64; + + if (sha256x64 == null) { + throw Exception('Could not generate SHA256 for the created package'); + } + + final sha256aarch64 = packageGenerator.sha256aarch64; + + final manifestContent = + FlatpakManifestGenerator(specJson).getFlatpakManifest(sha256x64, sha256aarch64); + final manifestPath = '${outputDir.path}/${specJson.appId}.json'; + final manifestFile = File(manifestPath); + manifestFile.writeAsStringSync(manifestContent); + print('Generated $manifestPath'); + + if (specJson.linuxArmReleaseBundleDirPath == null) { + final flathubJsonPath = '${outputDir.path}/${FlathubJsonGenerator.filename}'; + final flathubJsonFile = File(flathubJsonPath); + flathubJsonFile.writeAsStringSync(FlathubJsonGenerator.generate()); + print('Generated $flathubJsonPath'); + } +} + +enum _Platform { x86_64, aarch64 } + +class PackageGenerator { + final Directory inputDir; + final SpecJson specJson; + String? sha256x64; + String? sha256aarch64; + + PackageGenerator({required this.inputDir, required this.specJson}); + + void generatePackage(Directory outputDir, _Platform platform) { + final tempDir = outputDir.createTempSync('flutter_package_generator'); + final appId = specJson.appId; + + // desktop file + final desktopFile = File('${inputDir.path}/${specJson.desktopPath}'); + + if (!desktopFile.existsSync()) { + throw Exception( + 'The desktop file does not exist under the specified path: ${desktopFile.path}'); + } + + desktopFile.copySync( + '${tempDir.path}/$appId.desktop'); //todo does "$appName" have to be in the path too? + + // icons + final iconTempDir = Directory('${tempDir.path}/icons'); + + for (final icon in specJson.icons) { + final iconFile = File('${inputDir.path}/${icon.path}'); + if (!iconFile.existsSync()) { + 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}/$appId.${icon.fileExtension}'); + } + + // appdata file + final origAppDataFile = File('${inputDir.path}/${specJson.appDataPath}'); + if (!origAppDataFile.existsSync()) { + throw Exception( + 'The app data file does not exist under the specified path: ${origAppDataFile.path}'); + } + + final editedAppDataContent = + AppDataModifier.replaceVersions(origAppDataFile.readAsStringSync(), specJson.releases); + + final editedAppDataFile = File('${tempDir.path}/$appId.appdata.xml'); + editedAppDataFile.writeAsStringSync(editedAppDataContent); + + // build files + final bundlePath = platform == _Platform.aarch64 + ? specJson.linuxArmReleaseBundleDirPath + : specJson.linuxReleaseBundleDirPath; + final buildDir = Directory(bundlePath!); + if (!buildDir.existsSync()) { + throw Exception( + 'The linux build directory does not exist under the specified path: ${buildDir.path}'); + } + final destDir = Directory('${tempDir.path}/bin'); + destDir.createSync(); + + final platformSuffix = platform == _Platform.aarch64 ? 'aarch64' : 'x86_64'; + final packagePath = + '${outputDir.absolute.path}/${specJson.lowercaseAppName}-linux-$platformSuffix.tar.gz'; + + Process.runSync('cp', [ + '-r', + '${buildDir.absolute.path}/.', + destDir.absolute.path + ]); //todo test with spaces in name + Process.runSync('tar', ['-czvf', packagePath, '.'], + workingDirectory: tempDir.absolute.path); //todo test with spaces in name + + print('Generated $packagePath'); + + final preShasum = Process.runSync('shasum', ['-a', '256', packagePath]); + + if (platform == _Platform.aarch64) { + sha256aarch64 = preShasum.stdout.toString().split(' ').first; + } else { + sha256x64 = preShasum.stdout.toString().split(' ').first; + } + + tempDir.deleteSync(recursive: true); + } +} + +// updates releases in ${appName}.appdata.xml +class AppDataModifier { + static String replaceVersions(String origAppDataContent, List versions) { + final joinedReleases = + versions.map((v) => '').join('\n'); + final releasesSection = '\n$joinedReleases\n'; + if (origAppDataContent.contains('', multiLine: true), releasesSection); + } else { + return origAppDataContent.replaceFirst('', '$releasesSection\n'); + } + } +} + +// ${appId}.json +class FlatpakManifestGenerator { + final SpecJson specJson; + + FlatpakManifestGenerator(this.specJson); + + String getFlatpakManifest(String sha256x64, String? sha256aarch64) { + final appName = specJson.lowercaseAppName; + final appId = specJson.appId; + const encoder = JsonEncoder.withIndent(' '); + return encoder.convert({ + 'app-id': appId, + 'runtime': 'org.freedesktop.Platform', + 'runtime-version': specJson.runtimeVersion, + 'sdk': 'org.freedesktop.Sdk', + 'command': appName, + 'separate-locales': false, + 'finish-args': specJson.finishArgs, + 'modules': [ + ...specJson.extraModules ?? [], + { + 'name': appName, + 'buildsystem': 'simple', + 'build-commands': [ + 'cp -R $appName/bin/ /app/$appName', + 'chmod +x /app/$appName/$appName', + 'mkdir /app/bin/', + 'mkdir /app/lib/', + 'ln -s /app/$appName/$appName /app/bin/$appName', + ...specJson.flatpakCommandsAfterUnpack ?? [], + ...specJson.icons.map((icon) => + 'install -Dm644 $appName/icons/${icon.type}/$appId.${icon.fileExtension} /app/share/icons/hicolor/${icon.type}/apps/$appId.${icon.fileExtension}'), //TODO THIS DOES NOT ACCOUNT FOR THE symbolic icon name!!! + 'install -Dm644 $appName/$appId.desktop /app/share/applications/$appId.desktop', + 'install -Dm644 $appName/$appId.appdata.xml /app/share/applications/$appId.appdata.xml' + ], + 'sources': [ + { + 'type': 'archive', + 'only-arches': ['x86_64'], + 'url': + 'https://github.com/${specJson.githubReleaseOrganization}/${specJson.githubReleaseProject}/releases/download/${specJson.releases.first.version}/${specJson.lowercaseAppName}-linux-x86_64.tar.gz', + 'sha256': sha256x64, + 'dest': specJson.lowercaseAppName + }, + if (specJson.linuxArmReleaseBundleDirPath != null) + { + 'type': 'archive', + 'only-arches': ['aarch64'], + 'url': + 'https://github.com/${specJson.githubReleaseOrganization}/${specJson.githubReleaseProject}/releases/download/${specJson.releases.first.version}/${specJson.lowercaseAppName}-linux-aarch64.tar.gz', + 'sha256': sha256aarch64, + 'dest': specJson.lowercaseAppName + } + ] + } + ] + }); + } +} + +// flathub.json +class FlathubJsonGenerator { + static const String filename = 'flathub.json'; + + static String generate() { + const encoder = JsonEncoder.withIndent(' '); + return encoder.convert({ + 'only-arches': ['x86_64'] + }); + } +} + +class Release { + final String version; + final String date; + + Release({required this.version, required this.date}); +} + +class Icon { + final String type; + final String path; + late final String fileExtension; + + Icon({required this.type, required this.path}) { + fileExtension = path.split('.').last; + } +} + +class SpecJson { + //todo allow extra modules + final String appId; + final String lowercaseAppName; + final List releases; + final String runtimeVersion; + final String linuxReleaseBundleDirPath; + final String? linuxArmReleaseBundleDirPath; + final String appDataPath; + final String desktopPath; + final List icons; + final List? flatpakCommandsAfterUnpack; + final List? extraModules; + final List finishArgs; + final String githubReleaseOrganization; + final String githubReleaseProject; + + SpecJson( + {required this.appId, + required this.lowercaseAppName, + required this.releases, + required this.runtimeVersion, + required this.linuxReleaseBundleDirPath, + this.linuxArmReleaseBundleDirPath, + required this.appDataPath, + required this.desktopPath, + required this.icons, + required this.flatpakCommandsAfterUnpack, + this.extraModules, + required this.finishArgs, + required this.githubReleaseOrganization, + required this.githubReleaseProject}); + + static SpecJson fromJson(File jsonFile) { + try { + final json = jsonDecode(jsonFile.readAsStringSync()); + return SpecJson( + appId: json['appId'], + lowercaseAppName: json['lowercaseAppName'], + releases: (json['releases'] as List).map((r) { + final rMap = r as Map; + return Release(version: rMap['version'], date: rMap['date']); + }).toList(), + runtimeVersion: json['runtimeVersion'], + linuxReleaseBundleDirPath: json['linuxReleaseBundleDirPath'], + appDataPath: json['appDataPath'], + desktopPath: json['desktopPath'], + icons: (json['icons'] as Map).entries.map((mapEntry) { + return Icon(type: mapEntry.key as String, path: mapEntry.value as String); + }).toList(), + flatpakCommandsAfterUnpack: + (json['buildCommandsAfterUnpack'] as List?)?.map((bc) => bc as String)?.toList(), + linuxArmReleaseBundleDirPath: json['linuxArmReleaseBundleDirPath'] as String?, + extraModules: json['extraModules'] as List?, + finishArgs: (json['finishArgs'] as List).map((fa) => fa as String).toList(), + githubReleaseOrganization: json['githubReleaseOrganization'], + githubReleaseProject: json['githubReleaseProject']); + } catch (e) { + throw Exception('Could not parse JSON file, due to this error:\n$e'); + } + } +} diff --git a/flatpak/logo128.png b/flatpak/logo128.png new file mode 100644 index 0000000000000000000000000000000000000000..98f5c2d5ddec8b3e868907a3dcecb933f97866da GIT binary patch literal 4888 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 AbN~PV literal 0 HcmV?d00001 diff --git a/flatpak/logo512.png b/flatpak/logo512.png new file mode 100644 index 0000000000000000000000000000000000000000..7f0d02245eec58556e5550d0aa5354f8b9b8a938 GIT binary patch literal 11147 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

Z5-jM)RGwy(TXD zWFjT#LCXjc`uu%VVP??D;8%2I&KV`%0}9`ld2AhJ5@T9E3P_FB2WDRH zYg;@F)9*GcAJ=xqY@{LBN;~%|_{OyCwYc!r^z}36s`C}9d->CaQYlT5x7>5BzYJ&9 nApK{DQOCj2^xn4@1~xz=wB1dw3Zu!aUwY${=K4=`vEly(paqP! literal 0 HcmV?d00001 diff --git a/flatpak/spec.json b/flatpak/spec.json new file mode 100644 index 00000000..d897e6aa --- /dev/null +++ b/flatpak/spec.json @@ -0,0 +1,28 @@ +{ + "appId": "de.wger.flutter", + "releases": [ + { + "version": "1.5.3", + "date": "2023-03-16" + } + ], + "lowercaseAppName": "wger", + "runtimeVersion": "22.08", + "linuxReleaseBundleDirPath": "../build/linux/x64/release/bundle", + "appDataPath": "de.wger.flutter.appdata.xml", + "desktopPath": "de.wger.flutter.desktop", + "icons": { + "64x64": "logo64.png", + "128x126": "logo128.png", + "512x512": "logo512.png" + }, + "finishArgs": [ + "--share=ipc", + "--share=network", + "--socket=fallback-x11", + "--socket=wayland", + "--device=dri" + ], + "githubReleaseOrganization": "wger-project", + "githubReleaseProject": "flutter" +} \ No newline at end of file