Skip to content

Commit 38a608b

Browse files
committed
feat(linux): Added an experimental way to build Flatpaks.
1 parent a4adcd0 commit 38a608b

File tree

16 files changed

+422
-108
lines changed

16 files changed

+422
-108
lines changed

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,19 @@ lib/app.dart
5353
**/*.g.dart
5454
/assets/images/*.si
5555
/assets/images/**/*.si
56+
57+
# Snap
5658
snap/meta/polkit
5759
open-authenticator_*.snap
5860

61+
# Flatpak
62+
flatpak/.flatpak-builder
63+
flatpak/build-dir
64+
flatpak/repo
65+
flatpak/*.flatpak
66+
flatpak/*.tar.gz
67+
flatpak.sh
68+
5969
# Firebase files
6070
firebase
6171
lib/firebase_options.dart

.metadata

Lines changed: 0 additions & 45 deletions
This file was deleted.

bin/build.dart

Lines changed: 70 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,73 +7,91 @@ import 'package:pubspec_parse/pubspec_parse.dart';
77
void main() {
88
exitCode = 0;
99
stdout.writeln('Running `flutter clean`...');
10-
Process.runSync('flutter', ['clean'], runInShell: true);
10+
runSync('flutter', ['clean']);
1111
stdout.writeln('Done.');
1212
stdout.writeln('Running `flutter pub get`...');
13-
Process.runSync('flutter', ['pub', 'get'], runInShell: true);
14-
stdout.writeln('Done.');
15-
stdout.writeln('Running `dart run slang`...');
16-
Process.runSync('dart', ['run', 'slang'], runInShell: true);
13+
runSync('flutter', ['pub', 'get']);
1714
stdout.writeln('Done.');
1815
stdout.writeln('Running `dart run build_runner build --delete-conflicting-outputs`...');
19-
Process.runSync('dart', ['run', 'build_runner', 'build', '--delete-conflicting-outputs'], runInShell: true);
16+
runSync('dart', ['run', 'build_runner', 'build', '--delete-conflicting-outputs']);
17+
stdout.writeln('Done.');
18+
stdout.writeln('Running `dart run slang`...');
19+
runSync('dart', ['run', 'slang']);
2020
stdout.writeln('Done.');
2121
stdout.writeln('For which platform do you want to build ? (android/ios/macos/windows/linux)');
2222
String platform = stdin.readLineSync() ?? '';
2323
switch (platform.toLowerCase()) {
2424
case 'android':
2525
stdout.writeln('Running `flutter build appbundle`...');
26-
Process.runSync('flutter', ['build', 'appbundle'], runInShell: true);
26+
runSync('flutter', ['build', 'appbundle']);
2727
stdout.writeln('Done.');
2828
break;
2929
case 'ios':
3030
stdout.writeln('Updating pods...');
31-
Process.runSync('pod', ['update'], workingDirectory: File('ios').path, runInShell: true);
31+
runSync('pod', ['update'], workingDirectory: File('ios').path);
3232
stdout.writeln('Done.');
3333
stdout.writeln('Running `flutter build ios`...');
34-
Process.runSync('flutter', ['build', 'ios']);
34+
runSync('flutter', ['build', 'ios']);
3535
stdout.writeln('Done.');
3636
break;
3737
case 'macos':
3838
stdout.writeln('Updating pods...');
39-
Process.runSync('pod', ['update'], workingDirectory: File('macos').path, runInShell: true);
39+
runSync('pod', ['update'], workingDirectory: File('macos').path);
4040
stdout.writeln('Done.');
4141
stdout.writeln('Running `flutter build macos`...');
42-
Process.runSync('flutter', ['build', 'macos'], runInShell: true);
42+
runSync('flutter', ['build', 'macos']);
4343
stdout.writeln('Done.');
4444
break;
4545
case 'windows':
4646
stdout.writeln('Running `dart run msix:create`...');
47-
Process.runSync('dart', ['run', 'msix:create'], runInShell: true);
47+
runSync('dart', ['run', 'msix:create']);
4848
stdout.writeln('Done.');
4949
case 'linux':
50-
File pubspecFile = File('./pubspec.yaml');
51-
if (!pubspecFile.existsSync()) {
52-
stderr.writeln('Cannot find pubspec.yaml at "${pubspecFile.path}".');
53-
return;
54-
}
55-
String pubspecContent = pubspecFile.readAsStringSync();
56-
Pubspec pubspec = Pubspec.parse(pubspecContent);
57-
if (pubspec.version == null) {
58-
stderr.writeln('Cannot find current version.');
59-
return;
60-
}
61-
stdout.writeln('Current version is "${pubspec.version}".');
62-
stdout.writeln('Running `snapcraft`...');
63-
Process.runSync('snapcraft', [], runInShell: true);
64-
stdout.writeln('Done.');
65-
String snapName = 'open-authenticator_${pubspec.version!.major}.${pubspec.version!.minor}.${pubspec.version!.patch}_amd64.snap';
66-
File snap = File(snapName);
67-
if (!snap.existsSync()) {
68-
stderr.writeln('Cannot find snap at "${snap.path}".');
69-
return;
70-
}
71-
stdout.writeln('Do you want to upload it ? (Y/N)');
72-
String yN = stdin.readLineSync() ?? '';
73-
if (yN.toLowerCase() == 'y') {
74-
stdout.writeln('Running `snapcraft upload --release=stable $snapName`...');
75-
Process.runSync('snapcraft', ['upload', '--release=stable', snapName], runInShell: true);
76-
stdout.writeln('Done.');
50+
stdout.writeln('For which variant do you want to build ? (snap/flatpak)');
51+
String variant = stdin.readLineSync() ?? '';
52+
switch (variant) {
53+
case 'snap':
54+
File pubspecFile = File('./pubspec.yaml');
55+
if (!pubspecFile.existsSync()) {
56+
stderr.writeln('Cannot find pubspec.yaml at "${pubspecFile.path}".');
57+
break;
58+
}
59+
String pubspecContent = pubspecFile.readAsStringSync();
60+
Pubspec pubspec = Pubspec.parse(pubspecContent);
61+
if (pubspec.version == null) {
62+
stderr.writeln('Cannot find current version.');
63+
break;
64+
}
65+
stdout.writeln('Current version is "${pubspec.version}".');
66+
stdout.writeln('Running `snapcraft`...');
67+
runSync('snapcraft', []);
68+
stdout.writeln('Done.');
69+
String snapName = 'open-authenticator_${pubspec.version!.major}.${pubspec.version!.minor}.${pubspec.version!.patch}_amd64.snap';
70+
File snap = File(snapName);
71+
if (!snap.existsSync()) {
72+
stderr.writeln('Cannot find snap at "${snap.path}".');
73+
break;
74+
}
75+
stdout.writeln('Do you want to upload it ? (Y/N)');
76+
String yN = stdin.readLineSync() ?? '';
77+
if (yN.toLowerCase() == 'y') {
78+
stdout.writeln('Running `snapcraft upload --release=stable $snapName`...');
79+
runSync('snapcraft', ['upload', '--release=stable', snapName]);
80+
stdout.writeln('Done.');
81+
}
82+
break;
83+
case 'flatpak':
84+
stdout.writeln('Building Docker image...');
85+
runSync('docker', ['build', '--platform', 'linux/amd64', '-t', 'flutterpack:1.0.0', './flatpak']);
86+
stdout.writeln('Done.');
87+
stdout.writeln('Building Flatpak file...');
88+
runSync('docker', ['run', '--rm', '--privileged', '--platform', 'linux/amd64', '-u', 'root', '-v', './work', '-w', '/work/flatpak', 'flutterpack:1.0.0', './build-flutter-app.sh']);
89+
stdout.writeln('Done.');
90+
break;
91+
default:
92+
stderr.writeln('Invalid variant.');
93+
exitCode = -1;
94+
return;
7795
}
7896
break;
7997
default:
@@ -85,10 +103,21 @@ void main() {
85103
String yN = stdin.readLineSync() ?? '';
86104
if (yN.toLowerCase() == 'y') {
87105
stdout.writeln('Running `flutter clean`...');
88-
Process.runSync('flutter', ['clean'], runInShell: true);
106+
runSync('flutter', ['clean']);
89107
stdout.writeln('Done.');
90108
stdout.writeln('Running `flutter pub get`...');
91-
Process.runSync('flutter', ['pub', 'get'], runInShell: true);
109+
runSync('flutter', ['pub', 'get']);
92110
stdout.writeln('Done.');
93111
}
94112
}
113+
114+
void runSync(String executable, List<String> arguments, {String? workingDirectory}) {
115+
ProcessResult result = Process.runSync(executable, arguments);
116+
if (result.exitCode != 0) {
117+
stderr.writeln('Error : ${result.stderr}');
118+
stderr.write(result.stderr);
119+
exitCode = result.exitCode;
120+
} else if (result.stdout.isNotEmpty) {
121+
stdout.write(result.stdout);
122+
}
123+
}

bin/generate_polkit_policy.dart

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,12 @@ Future<void> main() async {
3838
''';
3939
}
4040
policyFileContent += '</policyconfig>';
41-
File policyFile = File('snap/meta/polkit/polkit.app.openauthenticator.policy');
42-
policyFile.parent.createSync(recursive: true);
43-
policyFile.writeAsStringSync(policyFileContent);
44-
stdout.writeln('Done : ${policyFile.path}.');
41+
for (String path in ['snap/meta/polkit/polkit.app.openauthenticator.policy', 'docs/public/polkit/app.openauthenticator.policy']) {
42+
File policyFile = File(path);
43+
policyFile.parent.createSync(recursive: true);
44+
policyFile.writeAsStringSync(policyFileContent);
45+
stdout.writeln('Done : ${policyFile.path}.');
46+
}
4547

4648
for (String locale in locales) {
4749
if (locale == 'en') {

docs/locales/en.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@
8686
"5": {
8787
"question": "Why data synchronization is not enabled on Linux ?",
8888
"answer": "<p>It is not enabled because both Firebase Auth and Firebase Firestore are <a href=\"https://github.com/Skyost/OpenAuthenticator/blob/main/linux/README.md\">currently unsupported</a> on Linux. Your current option is to manually import all your TOTPs on Linux. You can also use the backup feature in the app to do so.</p>"
89+
},
90+
"6": {
91+
"question": "I cannot enable local authentication on Linux. What should I do ?",
92+
"answer": "<p>If you're in this case, it probably means that Open Authenticator has been installed via Flatpak.</p><p>Local authentication on Linux relies on a Polkit policy file that must be present on your host under <code>/usr/share/polkit-1/actions</code>.</p><p>If it's not present, you can install it using <code>sudo wget -O /usr/share/polkit-1/actions/app.openauthenticator.policy https://openauthenticator.app/polkit/app.openauthenticator.policy && sudo chmod 644 /usr/share/polkit-1/actions/app.openauthenticator.policy</code>. Then restart Open Authenticator (or your session).</p>"
8993
}
9094
},
9195
"questionLeft": {

docs/locales/fr.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@
8686
"5": {
8787
"question": "Pourquoi la synchronisation ne peut-elle pas être activée sur Linux ?",
8888
"answer": "<p>Elle ne peut pas être activtée car ni Firebase Auth, ni Firebase Firestore ne sont <a href=\"https://github.com/Skyost/OpenAuthenticator/blob/main/linux/README.md\">disponibles sur Linux</a> pour le moment. Votre option actuelle est de simplement importer tous vos TOTPs sur Linux. Vous pouvez également utiliser le système de sauvegardes de l'application pour le faire.</p>"
89+
},
90+
"6": {
91+
"question": "Je ne peux pas activer l’authentification locale sur Linux. Que faire ?",
92+
"answer": "<p>Si vous êtes dans ce cas, cela signifie probablement qu’Open Authenticator a été installé via Flatpak.</p><p>L’authentification locale sous Linux repose sur un fichier de politique Polkit qui doit être présent sur votre système hôte dans <code>/usr/share/polkit-1/actions</code>.</p><p>S’il n’est pas présent, vous pouvez l’installer avec la commande suivante : <code>sudo wget -O /usr/share/polkit-1/actions/app.openauthenticator.policy https://openauthenticator.app/polkit/app.openauthenticator.policy && sudo chmod 644 /usr/share/polkit-1/actions/app.openauthenticator.policy</code>. Redémarrez ensuite Open Authenticator (ou votre session).</p>"
8993
}
9094
},
9195
"questionLeft": {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<policyconfig>
3+
<action id="app.openauthenticator.openApp">
4+
<description gettext-domain="polkit.app.openauthenticator">Authentication required</description>
5+
<message gettext-domain="polkit.app.openauthenticator">Authenticate to access the app.</message>
6+
<defaults>
7+
<allow_any>auth_self</allow_any>
8+
<allow_inactive>auth_self</allow_inactive>
9+
<allow_active>auth_self</allow_active>
10+
</defaults>
11+
</action>
12+
<action id="app.openauthenticator.sensibleAction">
13+
<description gettext-domain="polkit.app.openauthenticator">Authentication required</description>
14+
<message gettext-domain="polkit.app.openauthenticator">Authenticate to continue.</message>
15+
<defaults>
16+
<allow_any>auth_self</allow_any>
17+
<allow_inactive>auth_self</allow_inactive>
18+
<allow_active>auth_self</allow_active>
19+
</defaults>
20+
</action>
21+
<action id="app.openauthenticator.enable">
22+
<description gettext-domain="polkit.app.openauthenticator">Authentication required</description>
23+
<message gettext-domain="polkit.app.openauthenticator">Authenticate to enable local authentication.</message>
24+
<defaults>
25+
<allow_any>auth_self</allow_any>
26+
<allow_inactive>auth_self</allow_inactive>
27+
<allow_active>auth_self</allow_active>
28+
</defaults>
29+
</action>
30+
<action id="app.openauthenticator.disable">
31+
<description gettext-domain="polkit.app.openauthenticator">Authentication required</description>
32+
<message gettext-domain="polkit.app.openauthenticator">Authenticate to disable local authentication.</message>
33+
<defaults>
34+
<allow_any>auth_self</allow_any>
35+
<allow_inactive>auth_self</allow_inactive>
36+
<allow_active>auth_self</allow_active>
37+
</defaults>
38+
</action>
39+
</policyconfig>

flatpak/Dockerfile

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
FROM ubuntu:24.04
2+
3+
RUN apt-get update && DEBIAN_FRONTEND=noninteractive TZ=Europe apt-get -y install tzdata
4+
RUN apt-get -y install tree curl file git unzip xz-utils zip clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev
5+
RUN apt-get -y install libsecret-1-dev libpolkit-gobject-1-dev
6+
RUN apt-get -y install flatpak flatpak-builder
7+
RUN flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
8+
RUN flatpak install -y org.freedesktop.Sdk/x86_64/25.08
9+
RUN flatpak install -y org.freedesktop.Platform/x86_64/25.08
10+
RUN flatpak install -y flathub org.freedesktop.appstream-glib
11+
12+
RUN git clone https://github.com/flutter/flutter.git -b stable /opt/flutter
13+
ENV PATH="/opt/flutter/bin:${PATH}"
14+
RUN git config --global --add safe.directory /opt/flutter
15+
RUN flutter --disable-analytics && flutter upgrade
16+
RUN chown -R "$(id -u):$(id -g)" /opt/flutter

flatpak/app.desktop

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[Desktop Entry]
2+
Name=Open Authenticator
3+
Comment=A cross-platform OTP app, free and open-source
4+
Exec=open_authenticator
5+
Icon=app.openauthenticator.OpenAuthenticator
6+
Terminal=false
7+
Type=Application
8+
Categories=Utility;Security;

0 commit comments

Comments
 (0)