This directory contains the Flatpak manifest and build tooling for packaging Lotti as a Flatpak application for distribution on Flathub and other Flatpak repositories.
The Flatpak build process involves several stages:
- Local Development: Building and testing locally with network access
- Offline Preparation: Converting to fully offline build for Flathub compliance
- CI/CD Pipeline: Automated builds and testing in GitHub Actions
- Flathub Submission: Final packaging for distribution
-
Manifest Files
com.matthiasn.lotti.source.yml- Source manifest template with placeholderscom.matthiasn.lotti.yml- Generated manifest with pinned commit (local builds)- Generated manifest in
flathub-build/output/- Fully offline manifest for Flathub
-
Build Tools
create_local_manifest.sh- Pins commit for local buildsprepare_flathub_submission.sh- Thin wrapper over the Python orchestratormanifest_tool/- Python utilities and CLI for manifest manipulation (see below)
-
manifest_tool Python Utilities (modular architecture)
- core/: Core manifest operations and utilities
manifest.py: Document handling, validationutils.py: Shared utilities (logging, YAML operations)validation.py: Flathub compliance checking
- flutter/: Flutter-specific operations
sdk.py: Flutter SDK managementhelpers.py: Build helpers and commandsplugins.py: Plugin dependency handling (SQLite, mimalloc)patches.py: Offline build patchesrust.py: Rust/Cargo integration for Flutterbuild.py: Build configuration
- operations/: High-level manifest operations
manifest.py: Complex manifest transformationssources.py: Source managementci.py: CI/CD integration
- build/: Build preparation utilities
utils.py: Build-specific utilities
- core/: Core manifest operations and utilities
-
Dependencies
- Flutter SDK - Dart/Flutter framework
- Rust toolchain - For native Rust dependencies
- Native libraries - libmpv, libsecret, libkeybinder, libplacebo, libass
- Flutter plugins - Various plugins with native components
[Source Code] → [Local Manifest] → [Local Build]
↓
[prepare_flathub_submission.sh]
↓
[Initial Build with Network]
↓
[Extract Dependencies from Build Cache]
↓
[Generate Offline Manifests]
↓
[Apply Compliance Patches]
↓
[Bundle All Sources]
↓
[CI/CD Testing]
↓
[Flathub Submission]
- Install flatpak, flatpak-builder, and required runtimes:
# Install Flatpak and builder sudo apt install flatpak flatpak-builder # Install XDG Desktop Portal (required for screenshots) # For GNOME/GTK systems: sudo apt install xdg-desktop-portal xdg-desktop-portal-gtk # For KDE/Plasma systems: sudo apt install xdg-desktop-portal xdg-desktop-portal-kde # Add Flathub repository flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo # Install required runtimes and SDK extensions flatpak install org.freedesktop.Platform//24.08 org.freedesktop.Sdk//24.08 \ org.freedesktop.Sdk.Extension.llvm20 org.freedesktop.Sdk.Extension.rust-stable
cd flatpak
./create_local_manifest.sh
flatpak-builder --user --install --force-clean build-dir com.matthiasn.lotti.ymlImportant Notes:
- Always run
./create_local_manifest.shbefore building - This replaces
commit: COMMIT_PLACEHOLDERwith your current git HEAD - The script includes safety guards to prevent building with placeholders
After installation:
flatpak run com.matthiasn.lottiFlathub has strict requirements for security and reproducibility:
- No network access during build - All dependencies must be pre-fetched
- Reproducible builds - Same inputs must produce same outputs
- Source verification - All sources must have checksums
- No binary blobs - Everything built from source
-
Ensure your branch is pushed:
git push origin <your-branch>
The branch must be accessible from GitHub for the offline build tools to work.
Ensure
pubspec.yamlcontains aversion:entry—the orchestrator now derives the release number from that field and will fail if it is missing. If you manage Flutter with FVM, the orchestrator reads.fvm/fvm_config.jsonto determine the Flutter SDK tag; keep that file in sync with your desired release channel. -
Install Python dependencies (requires Python 3.10+):
sudo apt-get install python3-packaging python3-toml python3-yaml python3-requests
-
Run the preparation script:
cd flatpak ./prepare_flathub_submission.sh
The script performs these operations:
**Phase 1: Initial Build with Network**:
- Performs test build to populate dependency cache
- Downloads all Dart packages via pub
- Downloads all Cargo crates
- Caches Flutter tools dependencies
**Phase 2: Dependency Extraction**:
- Locates pubspec.lock files (app, flutter_tools, cargokit)
- Fetches pinned Cargo.lock files for Rust plugins directly from upstream
- Extracts dependency information from build cache
- Downloads missing source archives
**Phase 3: Manifest Generation**:
- Uses flatpak-flutter tool (or fallback generators)
- Creates flutter-sdk module manifest
- Generates pubspec-sources.json for Dart packages
- Generates cargo-sources.json for Rust crates
- Creates rustup configuration
**Phase 4: Offline Patches**:
- Adds SQLite3 and mimalloc sources for CMake plugins
- Patches cargokit scripts to use --offline flag
- Configures Cargo to use vendored sources
- Pre-places CMake FetchContent dependencies
**Phase 5: Compliance Enforcement**:
- Removes all --share=network from build-args (not finish-args)
- Adds --offline to all flutter pub get commands
- Adds --no-pub to flutter build commands
- Removes flutter config commands
- Validates no network access remains
**Phase 6: Source Bundling**:
- Creates app archive (`lotti-<commit>.tar.xz`)
- Downloads all referenced sources
- Verifies checksums
- Copies everything to output directory
4. **Test the offline build** (optional but recommended):
```bash
TEST_BUILD=true ./prepare_flathub_submission.sh
- Submit to Flathub:
- Files are generated in
flatpak/flathub-build/output/ - Fork flathub/flathub
- Copy generated files to your fork
- Create pull request
- Files are generated in
Fine-tune the preparation process:
| Variable | Default | Description |
|---|---|---|
FLATPAK_FLUTTER_TIMEOUT |
None | Timeout for flatpak-flutter (seconds) |
NO_FLATPAK_FLUTTER |
false | Skip flatpak-flutter, use fallback generators |
PIN_COMMIT |
true | Pin app source to current commit |
USE_NESTED_FLUTTER |
false | Use nested Flutter SDK modules |
DOWNLOAD_MISSING_SOURCES |
true | Download sources not in cache |
CLEAN_AFTER_GEN |
true | Remove work directory after generation |
TEST_BUILD |
false | Run test build after preparation |
SQLITE_AUTOCONF_VERSION |
sqlite-autoconf-3500400 | Overrides the SQLite version applied to plugin patches |
SQLITE_AUTOCONF_SHA256 |
hash for sqlite-autoconf-3500400 | Overrides the expected SHA256 for the SQLite archive |
The script includes fail-fast assertions that will exit with error if:
- Application pubspec.lock is missing
- Python manifest_tool operations fail
- Flathub compliance violations are detected
- Required source files cannot be generated
The manifest_tool/ directory contains modular Python utilities organized by functionality:
manifest_tool/
├── cli.py # Command-line interface
├── core/ # Core functionality
│ ├── __init__.py
│ ├── manifest.py # Document operations, OperationResult
│ ├── utils.py # Logging, YAML handling
│ └── validation.py # Flathub compliance checks
├── flutter/ # Flutter-specific operations
│ ├── __init__.py
│ ├── build.py # Build configuration
│ ├── helpers.py # Setup helpers, commands
│ ├── patches.py # Offline patches (CMake, Cargo)
│ ├── plugins.py # Plugin dependencies (SQLite, mimalloc)
│ ├── rust.py # Rust/Cargo configuration
│ └── sdk.py # Flutter SDK management
├── operations/ # High-level operations
│ ├── __init__.py
│ ├── ci.py # CI/CD support
│ ├── manifest.py # Complex transformations
│ └── sources.py # Source management
├── build/ # Build utilities
│ ├── __init__.py
│ └── utils.py # Build helpers
└── tests/ # Comprehensive test suite
├── conftest.py # Test fixtures
├── flutter/ # Flutter operation tests
│ ├── test_build.py
│ ├── test_helpers.py
│ ├── test_patches.py
│ ├── test_patches_edge_cases.py
│ ├── test_plugins.py
│ ├── test_rust.py
│ └── test_sdk.py
├── operations/ # Operations tests
│ ├── test_ci.py
│ ├── test_manifest.py
│ ├── test_manifest_helpers.py
│ └── test_sources.py
├── core/ # Core tests
│ ├── test_manifest.py
│ ├── test_utils.py
│ └── test_validation.py
└── ... # Integration and other tests
# Add offline build patches (SQLite3, cargokit, Cargo config)
python3 manifest_tool/cli.py add-offline-build-patches --manifest output.yml
# Add media kit mimalloc source
python3 manifest_tool/cli.py add-media-kit-mimalloc-source --manifest output.yml
# Add SQLite3 source for CMake
python3 manifest_tool/cli.py add-sqlite3-source --manifest output.yml
# Remove network access from build-args
python3 manifest_tool/cli.py remove-network-from-build-args --manifest output.yml
# Add offline flags
python3 manifest_tool/cli.py ensure-flutter-pub-get-offline --manifest output.yml
python3 manifest_tool/cli.py ensure-dart-pub-offline-in-build --manifest output.yml
# Check Flathub compliance
python3 manifest_tool/cli.py check-flathub-compliance --manifest output.ymlOffline Build Patches (flutter/patches.py):
add_cmake_offline_patches(): Patches for CMake-based pluginsadd_cargokit_offline_patches(): Cargo offline configuration
Plugin Dependencies (flutter/plugins.py):
add_sqlite3_source(): Pre-places SQLite for sqlite3_flutter_libsadd_media_kit_mimalloc_source(): Handles mimalloc for media_kit- Supports bundled sources (using 'path' instead of 'url')
- Multi-architecture support (x86_64, aarch64)
Flutter SDK Management (flutter/sdk.py):
ensure_nested_sdk(): Manages nested Flutter SDK modulesnormalize_flutter_sdk_module(): Cleans up SDK build commandsconvert_flutter_git_to_archive(): Converts git to archive sources- Automatic removal of redundant flutter-sdk modules
Compliance Validation (core/validation.py):
- Checks for network access in build-args
- Validates offline flags presence
- Ensures no forbidden operations
- Comprehensive violation reporting
Python 3.10+ is required due to use of modern Python features (PEP 604 unions, str.removesuffix).
The manifest_tool/ includes comprehensive tests (220+ test cases):
cd flatpak/manifest_tool
python3 -m venv .venv
source .venv/bin/activate
pip install pytest pyyaml toml packaging
python -m pytest tests/ -v
deactivateTest coverage includes:
- Manifest manipulation operations
- Idempotency verification
- Command insertion and ordering
- Edge cases and error handling
- Compliance validation
- Plugin dependency handling
- SDK management
- Offline patch application
# Full end-to-end test
TEST_BUILD=true ./prepare_flathub_submission.sh
# Verify generated files
ls -la flathub-build/output/"FATAL: Application pubspec.lock not found"
- Run a local build first to generate pubspec.lock
- Ensure you're in the correct directory
"Failed to generate pubspec-sources.json"
- Check Python error output in console
- Verify pubspec.lock files exist
- Ensure Python dependencies installed
- Check Python version is 3.10+
Build fails with network errors
- Check all --offline flags are present
- Verify sources are properly bundled
- Review CI assertions output
CMake can't find dependencies
- Check manifest_tool plugin handlers
- Verify files placed at correct paths
- Check architecture-specific paths
# Check generated manifest
python3 -c "import yaml; yaml.safe_load(open('flathub-build/output/com.matthiasn.lotti.yml'))"
# Verify sources
find flathub-build/output -name "*.tar.*" -o -name "*.zip" | sort
# Check for network access
grep -n "share=network" flathub-build/output/com.matthiasn.lotti.yml
# Verify offline flags
grep -n "pub get" flathub-build/output/com.matthiasn.lotti.ymlThe app follows the principle of least privilege:
--share=network- Sync features and online functionality (runtime only - this is allowed in finish-args)- Note: Network access is forbidden during build (build-args) but allowed at runtime (finish-args)
--share=ipc- Inter-process communication for GUI--socket=pulseaudio- Audio recording/playback--socket=wayland+--socket=fallback-x11- Display protocols--device=dri- Hardware-accelerated graphics
--filesystem=xdg-documents/Lotti:create- App's document folder--filesystem=xdg-pictures:ro- Read-only Pictures access--filesystem=xdg-download/Lotti:create- App's download folder- App data in
~/.var/app/com.matthiasn.lotti/(sandboxed)
- ❌ No broad home directory access
- ❌ No unnecessary system access
- ✅ Minimal permissions
- ✅ Read-only where possible
When modifying the build system:
- Test locally first with
create_local_manifest.sh - Run
prepare_flathub_submission.shto verify offline build - Check CI passes in pull request
- Update tests if adding new manifest operations
- Ensure Python 3.10+ compatibility
- Keep complexity low (< 10 cyclomatic complexity)
manifest_tool/: Custom Python utilities because:
flatpak-flutterdoesn't handle all our specific needs- Need to manipulate manifest for Flathub compliance
- Plugin dependencies require special handling
- Automation of repetitive tasks
- Ensure idempotent operations
Two-manifest approach:
- Source manifest stays clean with placeholders
- Generated manifests are gitignored
- Prevents accidental commits of generated files
Offline conversion complexity:
- Flutter's package system assumes network
- Plugins download during build
- Everything must be pre-fetched and placed correctly
- Multiple dependency systems (pub, cargo, cmake)
Modular code organization:
- Separation of concerns (core, flutter, operations, build)
- Easier testing and maintenance
- Clear dependency hierarchy
- Reusable components
- To invoke the orchestrator directly without the shell wrapper:
Available options include
python3 flatpak/manifest_tool/cli.py prepare-flathub \ --repo-root <repo> --flatpak-dir <repo>/flatpak
--work-dir,--output-dir, and--flathub-dirto override defaults.