Compare commits
11 Commits
829bf7512a
..
v0.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 22aa22ce76 | |||
| d7d0668e29 | |||
| 76edfc5721 | |||
| 99a79d8df9 | |||
| 3affa455d0 | |||
| 961a8d3918 | |||
| 761372fb03 | |||
| 3f35c670ca | |||
| a32bfd5108 | |||
| 9261ec341e | |||
| 7ec7b20e74 |
@@ -0,0 +1,21 @@
|
||||
name: Build and deploy the latest docker container
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: nixos
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build docker
|
||||
run: devenv shell build-docker
|
||||
|
||||
- name: Deploy docker
|
||||
env:
|
||||
REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_GITEA_USERNAME }}
|
||||
REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_GITEA_PASSWORD }}
|
||||
run: devenv shell deploy-docker
|
||||
@@ -0,0 +1,37 @@
|
||||
name: Build and release the latest android apk
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
rolling-release-apk:
|
||||
runs-on: nixos
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build apk
|
||||
run: devenv shell build-android
|
||||
|
||||
- name: Move latest Tag
|
||||
run: |
|
||||
git config user.name "gitea-actions"
|
||||
git config user.email "gitea-actions@localhost"
|
||||
|
||||
# Forcefully creates/moves the 'latest' tag to the current commit
|
||||
git tag -f latest
|
||||
|
||||
# Force-pushes the updated tag back to your Gitea repository
|
||||
git push origin -f latest
|
||||
|
||||
- name: Update Gitea Release & Swap APK
|
||||
uses: akkuman/gitea-release-action@v1
|
||||
env:
|
||||
NODE_OPTIONS: '--experimental-fetch'
|
||||
with:
|
||||
tag_name: latest # Explicitly target the tag we just moved
|
||||
name: 'Latest Development Build'
|
||||
body: 'This rolling release is automatically generated and always contains the newest code from the main branch.'
|
||||
files: build/app/outputs/flutter-apk/*.apk
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -0,0 +1,23 @@
|
||||
name: Build and release a versioned android apk
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
release-apk:
|
||||
runs-on: nixos
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build apk
|
||||
run: devenv shell build-android
|
||||
|
||||
- name: Create Gitea Release & Upload APK
|
||||
uses: akkuman/gitea-release-action@v1
|
||||
env:
|
||||
NODE_OPTIONS: '--experimental-fetch' # if nodejs < 18
|
||||
with:
|
||||
files: build/app/outputs/flutter-apk/*.apk
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -56,3 +56,4 @@ devenv.local.nix
|
||||
.android
|
||||
android/app/.cxx/
|
||||
/.gradle/
|
||||
/.gitea/workflows/.gradle/
|
||||
|
||||
Vendored
-93
@@ -1,93 +0,0 @@
|
||||
pipeline {
|
||||
agent {
|
||||
node {
|
||||
label 'devenv'
|
||||
}
|
||||
}
|
||||
triggers {
|
||||
pollSCM('H/10 * * * *')
|
||||
}
|
||||
stages {
|
||||
stage('Test devenv') {
|
||||
steps {
|
||||
sh 'devenv test'
|
||||
}
|
||||
}
|
||||
stage('Flutter builds') {
|
||||
steps {
|
||||
sh 'devenv shell dart run pdfrx:remove_wasm_modules --revert' // Make sure WASM is put in output
|
||||
sh 'devenv shell flutter build web --release'
|
||||
|
||||
sh 'devenv shell dart run pdfrx:remove_wasm_modules' // NOT idempotent!
|
||||
sh 'devenv shell flutter build apk --release'
|
||||
|
||||
sh 'devenv shell dart run pdfrx:remove_wasm_modules --revert' // Reset again, just to be sure
|
||||
}
|
||||
}
|
||||
stage('Parallel Deploy') {
|
||||
parallel {
|
||||
stage('Web') {
|
||||
steps {
|
||||
sh 'docker build -t harbor.julian-mutter.de/sheetless/sheetless-frontend .'
|
||||
withDockerRegistry([credentialsId: 'harbor', url: 'https://harbor.julian-mutter.de']) {
|
||||
sh 'docker push harbor.julian-mutter.de/sheetless/sheetless-frontend'
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Android') {
|
||||
stages {
|
||||
stage('Checkout F-Droid Repo') {
|
||||
steps {
|
||||
dir("fdroid-repo") {
|
||||
git branch: 'master', url: "https://gitlab.julian-mutter.de/julian/fdroid-frajul.git", credentialsId: "gitea", poll: "false"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('Decrypt keys') {
|
||||
steps {
|
||||
dir("fdroid-repo") {
|
||||
sh 'devenv shell ./decrypt.sh'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('Copy APK') {
|
||||
steps {
|
||||
sh "cp build/app/outputs/apk/release/app-release.apk fdroid-repo/fdroid/repo/sheetless.apk"
|
||||
}
|
||||
}
|
||||
|
||||
stage('Update Metadata') {
|
||||
steps {
|
||||
dir("fdroid-repo") {
|
||||
sh 'devenv shell ./update-repo.sh'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('Push updates') {
|
||||
steps {
|
||||
dir("fdroid-repo") {
|
||||
withCredentials([gitUsernamePassword(credentialsId: 'gitea')]) {
|
||||
sh '''
|
||||
git config --global user.email "jenkins@comumail.de"
|
||||
git config --global user.name "Jenkins CI"
|
||||
./push-updates.sh
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
step([$class: 'Mailer', notifyEveryUnstableBuild: true, recipients: emailextrecipients([developers(), requestor()])])
|
||||
cleanWs()
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-62
@@ -16,62 +16,6 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1767039857,
|
||||
"owner": "NixOS",
|
||||
"repo": "flake-compat",
|
||||
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"git-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1769939035,
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"rev": "a8ca480175326551d6c4121498316261cbb5b260",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"git-hooks",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1762808025,
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "cb5e3fdca1de58ccbc3ef53de65bd372b48f567c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1770115704,
|
||||
@@ -90,14 +34,10 @@
|
||||
"root": {
|
||||
"inputs": {
|
||||
"devenv": "devenv",
|
||||
"git-hooks": "git-hooks",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"pre-commit-hooks": [
|
||||
"git-hooks"
|
||||
]
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
}
|
||||
+23
-5
@@ -1,5 +1,4 @@
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
{pkgs, ...}: {
|
||||
android = {
|
||||
enable = true;
|
||||
emulator.enable = false;
|
||||
@@ -46,10 +45,29 @@
|
||||
};
|
||||
|
||||
scripts = {
|
||||
deploy-docker.exec = ''
|
||||
build-docker.exec = ''
|
||||
dart run pdfrx:remove_wasm_modules --revert // Make sure WASM is put in output
|
||||
|
||||
flutter build web --release
|
||||
docker build -t harbor.julian-mutter.de/sheetless/sheetless-frontend .
|
||||
docker push harbor.julian-mutter.de/sheetless/sheetless-frontend
|
||||
|
||||
docker build -t gitlab.julian-mutter.de/julian/sheetless-frontend:latest .
|
||||
'';
|
||||
|
||||
deploy-docker.exec = ''
|
||||
if [ -n "''${REGISTRY_USERNAME:-}" ] && [ -n "''${REGISTRY_PASSWORD:-}" ]; then
|
||||
docker login gitlab.julian-mutter.de --username "$REGISTRY_USERNAME" --password "$REGISTRY_PASSWORD"
|
||||
fi
|
||||
|
||||
docker push gitlab.julian-mutter.de/julian/sheetless-frontend
|
||||
'';
|
||||
|
||||
build-android.exec = ''
|
||||
export GRADLE_USER_HOME="$(pwd)/.gradle"
|
||||
|
||||
dart run pdfrx:remove_wasm_modules --revert // Make sure WASM is put in output
|
||||
dart run pdfrx:remove_wasm_modules // NOT idempotent!
|
||||
|
||||
flutter build apk --release
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,12 +11,16 @@ class SyncResult {
|
||||
final bool isOnline;
|
||||
final int changesSynced;
|
||||
final int annotationsSynced;
|
||||
final int changesUnsynced;
|
||||
final int annotationsUnsynced;
|
||||
|
||||
SyncResult({
|
||||
required this.sheets,
|
||||
required this.isOnline,
|
||||
this.changesSynced = 0,
|
||||
this.annotationsSynced = 0,
|
||||
required this.changesSynced,
|
||||
required this.annotationsSynced,
|
||||
required this.changesUnsynced,
|
||||
required this.annotationsUnsynced,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -35,8 +39,8 @@ class SyncService {
|
||||
SyncService({
|
||||
required ApiClient apiClient,
|
||||
required StorageService storageService,
|
||||
}) : _apiClient = apiClient,
|
||||
_storageService = storageService;
|
||||
}) : _apiClient = apiClient,
|
||||
_storageService = storageService;
|
||||
|
||||
/// Performs a full sync operation.
|
||||
///
|
||||
@@ -80,6 +84,8 @@ class SyncService {
|
||||
|
||||
// 3. Upload pending annotations
|
||||
annotationsSynced = await _uploadPendingAnnotations();
|
||||
final remainingAnnotations = await _storageService
|
||||
.readPendingAnnotationUploads();
|
||||
|
||||
// 4. Apply any remaining local changes (in case some failed to upload)
|
||||
final changeQueue = await _storageService.readChangeQueue();
|
||||
@@ -102,6 +108,8 @@ class SyncService {
|
||||
isOnline: true,
|
||||
changesSynced: changesSynced,
|
||||
annotationsSynced: annotationsSynced,
|
||||
changesUnsynced: changeQueue.length,
|
||||
annotationsUnsynced: remainingAnnotations.length,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -127,9 +135,16 @@ class SyncService {
|
||||
}
|
||||
}
|
||||
|
||||
final remainingAnnotations = await _storageService
|
||||
.readPendingAnnotationUploads();
|
||||
|
||||
return SyncResult(
|
||||
sheets: sheets,
|
||||
isOnline: false,
|
||||
changesSynced: 0,
|
||||
annotationsSynced: 0,
|
||||
changesUnsynced: changeQueue.length,
|
||||
annotationsUnsynced: remainingAnnotations.length,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -219,6 +219,7 @@ class _HomePageState extends State<HomePage> with RouteAware {
|
||||
onLogout: _handleLogout,
|
||||
appName: _appName,
|
||||
appVersion: _appVersion,
|
||||
syncFuture: _syncFuture,
|
||||
),
|
||||
body: RefreshIndicator(onRefresh: _refreshSheets, child: _buildBody()),
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sheetless/core/services/sync_service.dart';
|
||||
|
||||
/// Callback for shuffle state changes.
|
||||
typedef ShuffleCallback = void Function(bool enabled);
|
||||
@@ -12,12 +13,14 @@ class AppDrawer extends StatelessWidget {
|
||||
final VoidCallback onLogout;
|
||||
final String? appName;
|
||||
final String? appVersion;
|
||||
final Future<SyncResult> syncFuture;
|
||||
|
||||
const AppDrawer({
|
||||
super.key,
|
||||
required this.isShuffling,
|
||||
required this.onShuffleChanged,
|
||||
required this.onLogout,
|
||||
required this.syncFuture,
|
||||
this.appName,
|
||||
this.appVersion,
|
||||
});
|
||||
@@ -32,7 +35,7 @@ class AppDrawer extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
_buildActions(),
|
||||
_buildAppInfo(),
|
||||
Column(children: [_buildSyncStatus(), _buildAppInfo()]),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -44,10 +47,7 @@ class AppDrawer extends StatelessWidget {
|
||||
return Column(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: Icon(
|
||||
Icons.shuffle,
|
||||
color: isShuffling ? Colors.blue : null,
|
||||
),
|
||||
leading: Icon(Icons.shuffle, color: isShuffling ? Colors.blue : null),
|
||||
title: const Text('Shuffle'),
|
||||
onTap: () => onShuffleChanged(!isShuffling),
|
||||
),
|
||||
@@ -60,6 +60,47 @@ class AppDrawer extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSyncStatus() {
|
||||
return Center(
|
||||
// padding: const EdgeInsets.all(5.0),
|
||||
child: FutureBuilder<SyncResult>(
|
||||
future: syncFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState != ConnectionState.done) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
if (snapshot.hasError) {
|
||||
return Text(
|
||||
"Error: ${snapshot.error.toString()}",
|
||||
style: const TextStyle(color: Colors.red),
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
|
||||
if (snapshot.hasData) {
|
||||
final changes = snapshot.data!.changesUnsynced;
|
||||
final annotations = snapshot.data!.annotationsUnsynced;
|
||||
if (changes == 0 && annotations == 0) {
|
||||
return Text(
|
||||
"All synced!",
|
||||
style: const TextStyle(color: Colors.black),
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
return Text(
|
||||
"$changes changes and $annotations annotations unsynchronized!",
|
||||
style: const TextStyle(color: Colors.red),
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAppInfo() {
|
||||
final versionText = appName != null && appVersion != null
|
||||
? '$appName v$appVersion'
|
||||
@@ -67,10 +108,7 @@ class AppDrawer extends StatelessWidget {
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
versionText,
|
||||
style: const TextStyle(color: Colors.grey),
|
||||
),
|
||||
child: Text(versionText, style: const TextStyle(color: Colors.grey)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
version: 0.1.2
|
||||
version: 0.2.0
|
||||
|
||||
environment:
|
||||
sdk: ^3.0.0
|
||||
|
||||
Reference in New Issue
Block a user