The Maestro suites remain runnable locally through scripts/run_maestro.py and scripts/run_maestro_ci.py; drop the workflow, the test that parsed it, and the CONTRIBUTING reference to automatic PR coverage.
6.5 KiB
Contributing
Getting Started
- Fork and clone the repository
- Run
flutter pub getto install dependencies - Run
scripts/codegen.shto generate translations and Dart model code - Start developing!
Development
- Follow Dart/Flutter conventions
- Run
dart format .to format Dart code (note: generated files like*.g.dartare excluded from CI checks) - Run
scripts/format_native.sh --fixto format Kotlin, Swift, C++, C, Objective-C, and native headers - Run
flutter analyzebefore submitting to check for issues - Run
scripts/run_tests.shto run the test suite (same asflutter test, but scaled to your core count) - Test your changes thoroughly
Code Quality Checks
The project includes automated CI checks that run on all pull requests:
-
Code Formatting: Ensures code follows Dart and native formatting standards
- Run locally:
dart format .to format Dart files - Run locally:
scripts/format_native.sh --fixto format native files - Note: CI only checks non-generated files (excludes
.g.dart,.freezed.dart) - Generated files are reformatted automatically by build tools
- Run locally:
-
Static Analysis: Checks for code issues and potential bugs
- Run locally:
flutter analyze - Note: CI excludes generated files from analysis (configured in
analysis_options.yaml)
- Run locally:
-
Generated Code: Ensures generated translations and model files are current
- Run locally:
scripts/codegen.sh --check
- Run locally:
-
Tests: Runs unit and widget tests (when available)
- Run locally:
scripts/run_tests.sh - This is
flutter testwith-jset to the core count. The default is half your cores, which leaves most of the machine idle because the suite is dominated by per-file compilation. Arguments are forwarded, soscripts/run_tests.sh test/widgets/some_test.dartworks.
- Run locally:
All these checks must pass before your changes can be merged.
Maestro end-to-end tests
Android E2E tests use Maestro against a disposable, pre-seeded Jellyfin container.
Prerequisites: Java 17, Flutter and Android SDK/platform tools, a running Android emulator, Docker, and the Maestro CLI.
Run the suites from the repository root (py -3 can replace python3 on Windows):
python3 scripts/run_maestro.py basic # Basic user flows
python3 scripts/run_maestro.py catalog # Catalog and music flows
python3 scripts/run_maestro.py media # Codec playback and track selection
Run one flow with --flow:
python3 scripts/run_maestro.py basic --flow .maestro/flows/04_search.yaml
Use --skip-build to reuse the debug APK and --skip-jellyfin-build to reuse the Jellyfin image. Set
--device <adb-serial> when multiple devices are connected; physical devices also require --adb-reverse.
Top-level flows live in .maestro/flows/, shared setup in .maestro/subflows/, and focused regressions in
.maestro/regression_flows/. Automatic groups are declared in scripts/run_maestro_ci.py::GROUPS. Every top-level
regression flow must be registered either there or in DESTRUCTIVE_MANUAL_TARGETS; reusable subflows are not
independent tests. A manual-only classification must state why the flow cannot run automatically.
The profile-isolation and profile-teardown regressions create and remove profile connections, so they are a destructive manual target rather than an automatic group. Run them only against the pre-seeded Jellyfin fixture and a disposable emulator, using the required opt-in:
python3 scripts/run_maestro_ci.py profile-regressions --disposable-emulator
The target refuses to start without --disposable-emulator. Each profile flow writes to its own Jellyfin log and
diagnostics directory under build/maestro-profile-regressions/.
Production container image updates
Production images in server/Dockerfile and server/docker-compose.yml use a readable version or source-revision tag
plus an authoritative multi-platform index digest. The adjacent Platforms declaration records the supported
linux/amd64 and linux/arm64 variants. Never replace these references with a mutable tag or a single-platform child
manifest.
Update a production image only through a reviewed change:
- For the Bugs service, first record the running container's image ID, repository digest, platform, and OCI source revision without printing its environment. Prefer that reviewed running identity; selecting anything else is a service upgrade, not a routine pin refresh.
- Review the upstream source revision and changelog, provenance, vulnerability results, and manifest contents. Resolve the readable tag and digest-qualified reference independently and confirm they identify the same OCI index in two clean caches. The index must contain both declared platforms; provenance/attestation descriptors do not count as runnable platforms.
- Change the readable tag, full
sha256index digest, and adjacent platform declaration together. Include the old and new identities, manifest/platform evidence, review findings, smoke results, and rollback notes in the change. - Before changing the Bugs digest, exercise it with non-production configuration and a disposable volume. Review
migrations, take a restorable
bugs_databackup, then validate a cloned volume. A forward-only migration rolls back with the prior digest and pre-change backup, not by changing the image reference alone. - Run
python3 scripts/check_container_image_pins.py,python3 scripts/test_check_container_image_pins.py, and(cd server && go test ./...). Inspect the rendered Compose configuration and rebuilt images locally without exposing configuration values. Do not publish or deploy from a review checkout, and never fall back tolatestwhen a digest is unavailable.
Internationalization (i18n)
This project uses slang for internationalization with JSON files.
Adding New Strings
-
Add your string to
lib/i18n/strings.i18n.json:{ "section": { "myNewString": "My new text" } } -
Run
dart run slangto regenerate translation files -
Use in your code:
Text(t.section.myNewString)
Adding New Languages
- Create new JSON file:
lib/i18n/[locale].i18n.json - Copy structure from
en.i18n.jsonand translate values - Run
dart run slangto regenerate files
Guidelines
- Organize strings logically in nested objects
- Use camelCase for keys
- Keep strings concise and clear
- Always run
dart run slangafter changes