build(todo): Todo deployable beside Learn's server, and released as an APK, on F-Droid and as a snap (ADR 0033, step 5)
deploy/compose.todo.yaml adds Todo's server at todo.halkyon.eu, a member of Learn's register, its database halkyon_todo in the instance's PostgreSQL, its image built as Train's. redeploy.sh and backup.sh go over every app server .env switches on (train, todo). Each compose file names its server to the register in a variable of its own (INSTANCE_MEMBER_TRAIN, INSTANCE_MEMBER_TODO), which halkyon_base adds to INSTANCE_MEMBERS, each address once, as a second compose file's default would have replaced Train's. apps/todo/release.dart configures the release tool for Todo's APK and snap: a key of its own (pinned once made; until then nothing is released), the F-Droid listing (Time, NonFreeNet), and the snap from apps/todo/snap/, which the tool builds in a clone of the repository at the tag with that folder in place of snap/, as snapcraft builds one recipe a folder and the root's is Learn's. What's new for 0.1.0 in English and German, now shown. Android's backup and transfer are off, as Train's (D53): the reminders scheduled keep their tasks' titles unsealed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
e673aa33aa
commit
e61d3abb4f
15 changed files with 963 additions and 107 deletions
File diff suppressed because one or more lines are too long
|
|
@ -18,7 +18,6 @@ android {
|
|||
}
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId = "app.halkyon.todo"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||
|
|
@ -32,13 +31,46 @@ android {
|
|||
versionName = flutter.versionName
|
||||
}
|
||||
|
||||
// The app's own key signs release builds when tool/release.dart hands it over
|
||||
// through the environment, as Learn's and Train's do: the keystore's path and the
|
||||
// key's alias, and the password asked for at each release, never written down. Other
|
||||
// release builds are signed with the debug key, so `flutter run --release` works
|
||||
// anywhere; the release tool releases no APK signed otherwise.
|
||||
val releaseKeystore = System.getenv("HALKYON_ANDROID_KEYSTORE")
|
||||
signingConfigs {
|
||||
if (releaseKeystore != null) {
|
||||
create("release") {
|
||||
storeFile = file(releaseKeystore)
|
||||
keyAlias = System.getenv("HALKYON_ANDROID_KEY_ALIAS")
|
||||
// A PKCS #12 keystore has one password, for the store and its key.
|
||||
storePassword = System.getenv("HALKYON_ANDROID_KEYSTORE_PASSWORD")
|
||||
keyPassword = System.getenv("HALKYON_ANDROID_KEYSTORE_PASSWORD")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
// TODO: Add your own signing config for the release build.
|
||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
signingConfig =
|
||||
signingConfigs.findByName("release") ?: signingConfigs.getByName("debug")
|
||||
}
|
||||
}
|
||||
|
||||
// The native libraries go into the APK compressed, as Learn's: one APK serves
|
||||
// every phone, holding them for three architectures, and Android unpacks the
|
||||
// phone's own as it installs the app.
|
||||
packaging {
|
||||
jniLibs {
|
||||
useLegacyPackaging = true
|
||||
}
|
||||
}
|
||||
|
||||
// Leaves out the list of dependencies Gradle would otherwise add to the APK,
|
||||
// encrypted for Google Play alone; F-Droid's and others' scanners flag it.
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
includeInBundle = false
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- The app talks to its instance: release builds need this too (Flutter's
|
||||
template grants it to debug and profile builds only). -->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
|
@ -8,10 +9,19 @@
|
|||
them off; without, Android may bring a reminder a few minutes late). -->
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
|
||||
<!-- No backup and no transfer to another phone (ADR 0033, step 5, as
|
||||
Train's ADR 0030, D53): the reminders scheduled, with their tasks'
|
||||
titles, are kept unsealed. Through Android 11 allowBackup and
|
||||
fullBackupContent stop both, from 12 on the extraction rules stop the
|
||||
transfer. -->
|
||||
<application
|
||||
android:label="Halkyon Todo"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:allowBackup="false"
|
||||
android:fullBackupContent="false"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
tools:targetApi="s">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Nothing of Todo's goes to a backup or to another phone, from Android 12
|
||||
on (ADR 0033, step 5, as Train's ADR 0030, D53): allowBackup="false"
|
||||
stops the backup, and only these rules stop the transfer. What Todo keeps
|
||||
unsealed (the reminders it has scheduled, with their tasks' titles) stays
|
||||
on this phone; what is sealed comes back from the instance on the next. -->
|
||||
<data-extraction-rules>
|
||||
<cloud-backup>
|
||||
<exclude domain="root" path="."/>
|
||||
<exclude domain="file" path="."/>
|
||||
<exclude domain="database" path="."/>
|
||||
<exclude domain="sharedpref" path="."/>
|
||||
<exclude domain="external" path="."/>
|
||||
<exclude domain="device_root" path="."/>
|
||||
<exclude domain="device_file" path="."/>
|
||||
<exclude domain="device_database" path="."/>
|
||||
<exclude domain="device_sharedpref" path="."/>
|
||||
</cloud-backup>
|
||||
<device-transfer>
|
||||
<exclude domain="root" path="."/>
|
||||
<exclude domain="file" path="."/>
|
||||
<exclude domain="database" path="."/>
|
||||
<exclude domain="sharedpref" path="."/>
|
||||
<exclude domain="external" path="."/>
|
||||
<exclude domain="device_root" path="."/>
|
||||
<exclude domain="device_file" path="."/>
|
||||
<exclude domain="device_database" path="."/>
|
||||
<exclude domain="device_sharedpref" path="."/>
|
||||
</device-transfer>
|
||||
</data-extraction-rules>
|
||||
|
|
@ -0,0 +1 @@
|
|||
- Die erste Version von Halkyon Todo: Aufgaben im Eingang und in Projekten mit Abschnitten, Heute und Demnächst, wiederkehrende Aufgaben und Erinnerungen, jedes Projekt als Liste, Board oder Kalender, alles versiegelt.
|
||||
|
|
@ -0,0 +1 @@
|
|||
- The first release of Halkyon Todo: tasks in an Inbox and in projects with sections, Today and Upcoming, repeating tasks and reminders, each project as a list, a board or a calendar, everything sealed.
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:halkyon_app/halkyon_app.dart';
|
||||
import 'package:halkyon_todo_flutter/src/app/licences.dart';
|
||||
import 'package:halkyon_todo_flutter/src/app/todo_app.dart';
|
||||
|
|
@ -28,12 +29,23 @@ Future<void> main() async {
|
|||
);
|
||||
await instances.load(usedBefore: device.storage.usedBefore());
|
||||
runApp(
|
||||
TodoApp(
|
||||
choice: instances,
|
||||
// Only an instance of Halkyon Todo: one of another Halkyon app's
|
||||
// answers too, but holds nothing of this app's.
|
||||
checker: ServerpodInstanceChecker(app: 'todo'),
|
||||
open: (instance) => TodoDependencies.create(instance, device),
|
||||
// What's new in each release (Train's ADR 0030, D61), from
|
||||
// assets/whats_new/, shown once after an update.
|
||||
WhatsNewScope(
|
||||
whatsNew: WhatsNew(
|
||||
AssetWhatsNewSource(rootBundle),
|
||||
FileWhatsNewStore(File(p.join(device.support, 'whats_new.json'))),
|
||||
version: device.appVersion,
|
||||
),
|
||||
// An instance chosen before: the app ran on this device already.
|
||||
usedBefore: instances.current != null,
|
||||
child: TodoApp(
|
||||
choice: instances,
|
||||
// Only an instance of Halkyon Todo: one of another Halkyon app's
|
||||
// answers too, but holds nothing of this app's.
|
||||
checker: ServerpodInstanceChecker(app: 'todo'),
|
||||
open: (instance) => TodoDependencies.create(instance, device),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,6 +80,10 @@ flutter:
|
|||
# The instance the build names, unless --dart-define=SERVER_URL=… does
|
||||
# (serverpod_flutter's getServerUrl).
|
||||
- assets/config.json
|
||||
# What each release changed, one file a release and language
|
||||
# (<version>.<language>.txt), drafted by tool/release.dart and edited
|
||||
# (Train's ADR 0030, D61): halkyon_app's What's new shows them.
|
||||
- assets/whats_new/
|
||||
# The app's icon, Lucide's list-todo (ADR 0031), and Lucide's licence for the
|
||||
# licence list (the repository's tool/app_icons.py writes both).
|
||||
- assets/icon/halkyon_todo_icon.png
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
/// Nothing of Todo's leaves the phone by Android's backup or a transfer to
|
||||
/// another phone (ADR 0033, step 5, as Train's ADR 0030, D53): the reminders
|
||||
/// scheduled are kept unsealed, with their tasks' titles, and what is sealed
|
||||
/// comes back from the instance anyway. Read as text, the files are small and
|
||||
/// Android's own.
|
||||
void main() {
|
||||
const folder = 'android/app/src/main';
|
||||
|
||||
test('backup is off, through Android 11 as from 12 on', () {
|
||||
final manifest = File('$folder/AndroidManifest.xml').readAsStringSync();
|
||||
final application = RegExp(
|
||||
'<application([^>]*)>',
|
||||
).firstMatch(manifest)![1]!;
|
||||
|
||||
expect(application, contains('android:allowBackup="false"'));
|
||||
expect(application, contains('android:fullBackupContent="false"'));
|
||||
expect(
|
||||
application,
|
||||
contains('android:dataExtractionRules="@xml/data_extraction_rules"'),
|
||||
);
|
||||
});
|
||||
|
||||
test("from Android 12 on, neither a backup nor a transfer takes any of the app's "
|
||||
'folders', () {
|
||||
// allowBackup="false" leaves a transfer to another phone on from Android
|
||||
// 12 on: the rules have to exclude it too, folder by folder.
|
||||
final rules = File(
|
||||
'$folder/res/xml/data_extraction_rules.xml',
|
||||
).readAsStringSync();
|
||||
for (final kind in ['cloud-backup', 'device-transfer']) {
|
||||
final section = RegExp(
|
||||
'<$kind>(.*?)</$kind>',
|
||||
dotAll: true,
|
||||
).firstMatch(rules)?[1];
|
||||
expect(section, isNotNull, reason: '$kind is missing');
|
||||
expect(section, isNot(contains('<include')), reason: kind);
|
||||
final excluded = {
|
||||
for (final exclude in RegExp(
|
||||
r'<exclude domain="(\w+)" path="\."/>',
|
||||
).allMatches(section!))
|
||||
exclude[1],
|
||||
};
|
||||
expect(excluded, {
|
||||
'root',
|
||||
'file',
|
||||
'database',
|
||||
'sharedpref',
|
||||
'external',
|
||||
'device_root',
|
||||
'device_file',
|
||||
'device_database',
|
||||
'device_sharedpref',
|
||||
}, reason: kind);
|
||||
}
|
||||
});
|
||||
}
|
||||
60
apps/todo/halkyon_todo_server/Dockerfile
Normal file
60
apps/todo/halkyon_todo_server/Dockerfile
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# Todo's server image (ADR 0033, step 5). Built from the repository root (the build
|
||||
# context), because the workspace's root pubspec and lockfile live there:
|
||||
#
|
||||
# docker build -f apps/todo/halkyon_todo_server/Dockerfile .
|
||||
#
|
||||
# Train's server image (apps/train/halkyon_train_server/Dockerfile) for Todo, as
|
||||
# Learn's: the same Dart, the same Ubuntu runtime and start command; no git, no
|
||||
# repositories folder, no data of its own.
|
||||
|
||||
# ── Build stage: compile the server to a native executable ────────────────────────
|
||||
FROM dart:3.13.4 AS build
|
||||
WORKDIR /app
|
||||
COPY pubspec.yaml pubspec.lock ./
|
||||
COPY tool/workspace_only.sh tool/
|
||||
COPY apps/todo/halkyon_todo_server apps/todo/halkyon_todo_server
|
||||
COPY packages/halkyon_base_server packages/halkyon_base_server
|
||||
COPY packages/halkyon_base_client packages/halkyon_base_client
|
||||
COPY packages/halkyon_core packages/halkyon_core
|
||||
COPY packages/halkyon_llm packages/halkyon_llm
|
||||
COPY packages/halkyon_oidc_server packages/halkyon_oidc_server
|
||||
COPY packages/halkyon_oidc_client packages/halkyon_oidc_client
|
||||
|
||||
# The workspace root with only the server and the modules it builds on, as Learn's
|
||||
# image has it (the reasons there).
|
||||
RUN sh tool/workspace_only.sh apps/todo/halkyon_todo_server packages/halkyon_base_server packages/halkyon_base_client packages/halkyon_core packages/halkyon_llm packages/halkyon_oidc_server packages/halkyon_oidc_client
|
||||
RUN dart pub get
|
||||
WORKDIR /app/apps/todo/halkyon_todo_server
|
||||
RUN dart build cli --target bin/main.dart --output build
|
||||
RUN mv build/bundle/bin/main build/bundle/bin/server
|
||||
RUN mkdir -p config migrations
|
||||
|
||||
# ── Runtime stage ─────────────────────────────────────────────────────────────────
|
||||
# Ubuntu 24.04 as Learn's; CA certificates for outgoing TLS (the operator's
|
||||
# assistants, which the module forwards, ADR 0021).
|
||||
FROM ubuntu:24.04
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends ca-certificates \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
WORKDIR /app
|
||||
|
||||
ENV runmode=production
|
||||
ENV serverid=default
|
||||
ENV logging=normal
|
||||
ENV role=monolith
|
||||
|
||||
COPY --from=build /app/apps/todo/halkyon_todo_server/build/bundle/ ./
|
||||
COPY --from=build /app/apps/todo/halkyon_todo_server/config/ config/
|
||||
COPY --from=build /app/apps/todo/halkyon_todo_server/migrations/ migrations/
|
||||
COPY --from=build /app/apps/todo/halkyon_todo_server/lib/src/generated/protocol.yaml lib/src/generated/protocol.yaml
|
||||
|
||||
# No root rights needed: it listens on 8080 and 8082 and writes nothing.
|
||||
RUN useradd --system --no-create-home --shell /usr/sbin/nologin app
|
||||
USER app
|
||||
|
||||
# The API server, and the web server the register tells of accounts gone (ADR
|
||||
# 0016), reached on the instance's own network alone.
|
||||
EXPOSE 8080 8082
|
||||
|
||||
ENTRYPOINT ["/bin/sh", "-c", "exec ./bin/server --mode=$runmode --server-id=$serverid --logging=$logging --role=$role \"$@\"", "--"]
|
||||
CMD []
|
||||
|
|
@ -1,27 +1,20 @@
|
|||
# This is the configuration file for your production environment.
|
||||
# Typically, you will want to route the traffic through a load balancer
|
||||
# which adds SSL security through https. If you use Serverpod's standard
|
||||
# Terraform scripts to deploy your server, all you need to change in
|
||||
# this file is the examplepod.com domain name.
|
||||
# Todo's server on learn.halkyon.eu's server, at todo.halkyon.eu (ADR 0033, step 5),
|
||||
# behind the instance's Caddy (deploy/compose.todo.yaml): its API and its web server
|
||||
# on the one domain, as Learn's and Train's; its database in the instance's PostgreSQL.
|
||||
|
||||
# Configuration for the main API server.
|
||||
apiServer:
|
||||
port: 8080
|
||||
publicHost: api.examplepod.com
|
||||
publicHost: todo.halkyon.eu
|
||||
publicPort: 443
|
||||
publicScheme: https
|
||||
|
||||
# Configuration for the Insights server.
|
||||
insightsServer:
|
||||
port: 8081
|
||||
publicHost: insights.examplepod.com
|
||||
publicPort: 443
|
||||
publicScheme: https
|
||||
# No insightsServer, as Learn's: without the block Serverpod doesn't start Insights.
|
||||
|
||||
# Configuration for the web server.
|
||||
webServer:
|
||||
port: 8082
|
||||
publicHost: app.examplepod.com
|
||||
publicHost: todo.halkyon.eu
|
||||
publicPort: 443
|
||||
publicScheme: https
|
||||
|
||||
|
|
@ -30,11 +23,12 @@ webServer:
|
|||
# If you are connecting on a public IP (e.g. on AWS or Google Cloud Run), you
|
||||
# connect on the public IP of the database e.g. database.examplepod.com.
|
||||
database:
|
||||
host: database.private-production.examplepod.com
|
||||
host: postgres
|
||||
port: 5432
|
||||
name: serverpod
|
||||
user: postgres
|
||||
requireSsl: true
|
||||
name: halkyon_todo
|
||||
user: halkyon_todo
|
||||
# On the instance's own network, never leaving the server.
|
||||
requireSsl: false
|
||||
#isUnixSocket: true # defaults to false
|
||||
#maxConnectionCount: 10 # Defaults to 10, a negative or null value removes the limit
|
||||
|
||||
|
|
|
|||
44
apps/todo/release.dart
Normal file
44
apps/todo/release.dart
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
// How Halkyon Todo is released (`dart run tool/release.dart todo`): the
|
||||
// release tool's configuration of it, registered in tool/src/release/apps.dart.
|
||||
import '../../tool/src/release/app_release.dart';
|
||||
|
||||
/// Halkyon Todo's releases (ADR 0033, step 5): the APK and the snap, both
|
||||
/// connecting to todo.halkyon.eu, published on code.halkyon.app, the APK in
|
||||
/// the project's F-Droid repository beside Learn and Train.
|
||||
const todoRelease = AppRelease(
|
||||
id: 'todo',
|
||||
name: 'Halkyon Todo',
|
||||
applicationId: 'app.halkyon.todo',
|
||||
directory: 'apps/todo',
|
||||
flutterPackage: 'apps/todo/halkyon_todo_flutter',
|
||||
artifactName: 'halkyon-todo',
|
||||
serverUrl: 'https://todo.halkyon.eu/',
|
||||
forgejo: 'https://code.halkyon.app',
|
||||
repository: 'project-halkyon/halkyon',
|
||||
// A PKCS #12 keystore of its own outside the repository (also in the
|
||||
// maintainer's password manager), as Learn's and Train's: it is the app's
|
||||
// identity, and lost, installed apps can't be updated; one key for several
|
||||
// apps would let either's build pass for the other's update.
|
||||
android: AndroidSigning(
|
||||
certificateFile: 'tool/release/todo-android-certificate.sha256',
|
||||
keystoreVariable: 'HALKYON_TODO_ANDROID_KEYSTORE',
|
||||
defaultKeystore: '.local/share/halkyon-todo/android-release.p12',
|
||||
aliasVariable: 'HALKYON_TODO_ANDROID_KEY_ALIAS',
|
||||
defaultAlias: 'halkyon-todo',
|
||||
),
|
||||
fdroid: FdroidListing(
|
||||
script: 'tool/fdroid_repo.sh',
|
||||
listing: 'tool/release/fdroid',
|
||||
indexCertificateFile: 'tool/release/fdroid-index-certificate.sha256',
|
||||
repositoryUrl: 'https://halkyon.app/fdroid/repo',
|
||||
),
|
||||
// Built in a clone of its own (`SnapRelease.projectPath`), as the root's
|
||||
// snap/ is Learn's. To the Store's edge channel, as Learn's, once the
|
||||
// name is registered (`snapcraft register halkyon-todo`).
|
||||
snap: SnapRelease(
|
||||
name: 'halkyon-todo',
|
||||
part: 'halkyon-todo',
|
||||
directory: 'apps/todo/snap',
|
||||
storeChannel: 'edge',
|
||||
),
|
||||
);
|
||||
9
apps/todo/snap/gui/halkyon-todo.desktop
Normal file
9
apps/todo/snap/gui/halkyon-todo.desktop
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Halkyon Todo
|
||||
Comment=Tasks and projects, sealed and synced between your devices
|
||||
Exec=halkyon-todo
|
||||
Icon=${SNAP}/meta/gui/halkyon-todo.png
|
||||
Terminal=false
|
||||
Categories=Office;ProjectManagement;
|
||||
StartupWMClass=app.halkyon.todo
|
||||
BIN
apps/todo/snap/gui/halkyon-todo.png
Normal file
BIN
apps/todo/snap/gui/halkyon-todo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
145
apps/todo/snap/snapcraft.yaml
Normal file
145
apps/todo/snap/snapcraft.yaml
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
# Halkyon Todo's Linux app as a Snap (ADR 0033, step 5), built in LXD by the release
|
||||
# tool (`dart run tool/release.dart todo`): snapcraft builds one recipe a folder, from
|
||||
# its snap/snapcraft.yaml, and the root's snap/ is Learn's, so the tool builds this one
|
||||
# in a clone of the repository at the release's tag with this folder in place of the
|
||||
# clone's snap/ (`SnapRelease.projectPath`), but for snap/local/, which the recipes
|
||||
# share. By hand, the same: in such a clone, `snapcraft pack`.
|
||||
#
|
||||
# It builds what is committed (the source is the clone's HEAD, the tag), never a
|
||||
# working tree with its build caches, and always for todo.halkyon.eu. Flutter is
|
||||
# pinned like the local and CI one (FLUTTER_VERSION in .forgejo/workflows/ci.yaml),
|
||||
# spelled out as in Learn's recipe, which says why.
|
||||
name: halkyon-todo
|
||||
title: Halkyon Todo
|
||||
base: core24
|
||||
adopt-info: halkyon-todo # the version, from the app's pubspec.yaml
|
||||
summary: Tasks and projects, sealed and synced between your devices
|
||||
description: |
|
||||
Halkyon Todo keeps your tasks: an Inbox for what comes up, projects with
|
||||
sections, sub-tasks and labels, Today with what is overdue, and Upcoming as
|
||||
an agenda that opens into the month. Tasks repeat (every day, every other
|
||||
Tuesday, the first Monday of each month), have reminders, deadlines and
|
||||
priorities, and are typed the quick way: "Call Anna tomorrow 9am #Home p1".
|
||||
|
||||
Each project shows as a list, a board or a calendar, grouped, sorted and
|
||||
filtered as you set it, the same on every device. Tasks are dragged between
|
||||
sections, days and hours.
|
||||
|
||||
Everything is sealed on your device with your account's keys before it
|
||||
reaches the instance, which can't read it. You sign in with SimpleLogin, by
|
||||
Proton, or with an email address, a password and an authenticator app. It
|
||||
connects to todo.halkyon.eu, the project's instance, or to any other:
|
||||
everything an instance needs is in the repository, to run your own. The app
|
||||
is AGPL-3.0 and the server SSPL-1.0, so all of its code is public.
|
||||
|
||||
Questions and reports: support@halkyon.app
|
||||
license: AGPL-3.0-or-later
|
||||
source-code: https://code.halkyon.app/project-halkyon/halkyon
|
||||
# The project's, not an instance's: the app works with any instance.
|
||||
website: https://halkyon.app/todo
|
||||
# No contact or issues, as Learn's (its recipe says why): the address is in the
|
||||
# description.
|
||||
# The app's icon with rounded corners, made by the repository's tool/app_icons.py; also
|
||||
# the desktop file's.
|
||||
icon: snap/gui/halkyon-todo.png
|
||||
grade: stable
|
||||
confinement: strict
|
||||
platforms:
|
||||
amd64:
|
||||
|
||||
apps:
|
||||
halkyon-todo:
|
||||
command: bin/halkyon_todo_flutter
|
||||
# How the session is kept: see snap/local/secret-backend (Learn's, kept in the
|
||||
# clone).
|
||||
command-chain: [bin/secret-backend]
|
||||
# GTK, WebKitGTK (the sign-in's page) and libsecret come from the GNOME runtime,
|
||||
# the reminders' notifications through the desktop's own; files and the browser
|
||||
# go through the desktop's portals.
|
||||
extensions: [gnome]
|
||||
plugs:
|
||||
- network
|
||||
# SimpleLogin's sign-in comes back on a loopback address the app listens on.
|
||||
- network-bind
|
||||
# Never connected by itself; whoever connects it by hand gets the keyring.
|
||||
- password-manager-service
|
||||
# As Learn's: the sync's connection ended at once when the network goes;
|
||||
# not connected until the Store grants it.
|
||||
- network-manager-observe
|
||||
|
||||
parts:
|
||||
flutter:
|
||||
plugin: nil
|
||||
source: https://github.com/flutter/flutter.git
|
||||
source-tag: '3.47.8'
|
||||
source-depth: 1
|
||||
build-packages:
|
||||
- clang
|
||||
- cmake
|
||||
- curl
|
||||
- git
|
||||
- libgtk-3-dev
|
||||
- lld
|
||||
- llvm-18
|
||||
- ninja-build
|
||||
- pkg-config
|
||||
- unzip
|
||||
- xz-utils
|
||||
- zip
|
||||
override-build: |
|
||||
mkdir -p "$CRAFT_PART_INSTALL/usr/libexec" "$CRAFT_PART_INSTALL/usr/bin"
|
||||
cp -r "$CRAFT_PART_SRC" "$CRAFT_PART_INSTALL/usr/libexec/flutter"
|
||||
ln -s ../libexec/flutter/bin/flutter "$CRAFT_PART_INSTALL/usr/bin/flutter"
|
||||
ln -s ../libexec/flutter/bin/dart "$CRAFT_PART_INSTALL/usr/bin/dart"
|
||||
"$CRAFT_PART_INSTALL/usr/bin/flutter" --disable-analytics
|
||||
"$CRAFT_PART_INSTALL/usr/bin/flutter" precache --linux
|
||||
# Needed to build, not in the Snap.
|
||||
override-prime: ''
|
||||
|
||||
halkyon-todo:
|
||||
after: [flutter]
|
||||
plugin: nil
|
||||
source: .
|
||||
source-type: git
|
||||
# sodium's build hook compiles libsodium with its configure script and make
|
||||
# (ADR 0017), with the Flutter part's clang.
|
||||
build-packages:
|
||||
- make
|
||||
override-build: |
|
||||
set -eu
|
||||
export PATH="$CRAFT_STAGE/usr/bin:$PATH"
|
||||
cd apps/todo/halkyon_todo_flutter
|
||||
craftctl set version="$(sed -n 's/^version: *\([^+]*\).*/\1/p' pubspec.yaml)"
|
||||
flutter pub get --enforce-lockfile
|
||||
flutter build linux --release \
|
||||
--dart-define=SERVER_URL=https://todo.halkyon.eu/
|
||||
mkdir -p "$CRAFT_PART_INSTALL/bin"
|
||||
cp -r build/linux/x64/release/bundle/. "$CRAFT_PART_INSTALL/bin/"
|
||||
|
||||
secret-backend:
|
||||
plugin: dump
|
||||
source: snap/local
|
||||
organize:
|
||||
secret-backend: bin/secret-backend
|
||||
|
||||
# As Learn's: every file the GNOME runtime or the base has at the same path
|
||||
# goes, so that nothing staged hides the runtime's own (Learn's recipe says
|
||||
# what that broke there). Todo stages no package today; the part keeps a
|
||||
# later one from doing so unnoticed.
|
||||
cleanup:
|
||||
after: [halkyon-todo, secret-backend]
|
||||
plugin: nil
|
||||
build-snaps: [core24, gnome-46-2404]
|
||||
override-prime: |
|
||||
set -eu
|
||||
for snap in core24 gnome-46-2404; do
|
||||
cd "/snap/$snap/current/usr"
|
||||
find . \( -type f -o -type l \) -print0 |
|
||||
while IFS= read -r -d '' path; do
|
||||
case "$path" in */blas/* | */lapack/*) continue ;; esac
|
||||
target="$CRAFT_PRIME/usr/$path"
|
||||
if [ -L "$target" ] || [ -f "$target" ]; then
|
||||
rm -f "$target"
|
||||
fi
|
||||
done
|
||||
done
|
||||
Loading…
Reference in a new issue