Skip to content

Commit

Permalink
Merge pull request #484 from 12people/flatpak
Browse files Browse the repository at this point in the history
Flatpak updates
  • Loading branch information
rolandgeider authored Jan 10, 2024
2 parents 7887f7f + 988304f commit 5d1da07
Show file tree
Hide file tree
Showing 15 changed files with 323 additions and 270 deletions.
7 changes: 3 additions & 4 deletions .github/workflows/build-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
File renamed without changes.
8 changes: 1 addition & 7 deletions flatpak/flatpak_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
123 changes: 73 additions & 50 deletions flatpak/scripts/flatpak_packager.dart
Original file line number Diff line number Diff line change
@@ -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<String> 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(
Expand All @@ -22,123 +32,136 @@ void main(List<String> 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<CPUArchitecture, String> shaByArch = {};
final String? addedTodaysVersion;

PackageGenerator({required this.inputDir, required this.meta});
PackageGenerator({required this.inputDir, required this.meta, required this.addedTodaysVersion});

Future<void> 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<bool> 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<Release> versions) {
// updates releases in ${appName}.metainfo.xml
class AppStreamModifier {
static String replaceVersions(String origAppStreamContent, List<Release> versions) {
final joinedReleases =
versions.map((v) => '\t\t<release version="${v.version}" date="${v.date}" />').join('\n');
final releasesSection = '<releases>\n$joinedReleases\n\t</releases>'; //todo check this
if (origAppDataContent.contains('<releases')) {
return origAppDataContent
final releasesSection = '<releases>\n$joinedReleases\n\t</releases>'; //TODO check this
if (origAppStreamContent.contains('<releases')) {
return origAppStreamContent
.replaceAll('\n', '<~>')
.replaceFirst(RegExp('<releases.*</releases>'), releasesSection)
.replaceAll('<~>', '\n');
} else {
return origAppDataContent.replaceFirst('</component>', '\n\t$releasesSection\n</component>');
return origAppStreamContent.replaceFirst(
'</component>', '\n\t$releasesSection\n</component>');
}
}
}
Loading

0 comments on commit 5d1da07

Please sign in to comment.