scripts/ had ~80 flat files. Entry points (ci_*.sh, codegen.sh, run_tests.sh, format_native.sh, setup_hooks.sh, upload-symbols.*) and the shared pubspec_version.py stay at the root; checkers, generators, maestro tooling and release tooling move into subdirectories with their tests. Updated every reference: workflow steps, guard-test glob, Docker COPY paths and .dockerignore whitelist, website audit path, dart test imports, and regenerated the five outputs whose headers embed generator paths.
144 lines
6.5 KiB
Markdown
144 lines
6.5 KiB
Markdown
# Contributing
|
|
|
|
## Getting Started
|
|
|
|
1. Fork and clone the repository
|
|
2. Run `flutter pub get` to install dependencies
|
|
3. Run `scripts/codegen.sh` to generate translations and Dart model code
|
|
4. Start developing!
|
|
|
|
## Development
|
|
|
|
- Follow Dart/Flutter conventions
|
|
- Run `dart format .` to format Dart code (note: generated files like `*.g.dart` are excluded from CI checks)
|
|
- Run `scripts/format_native.sh --fix` to format Kotlin, Swift, C++, C, Objective-C, and native headers
|
|
- Run `flutter analyze` before submitting to check for issues
|
|
- Run `scripts/run_tests.sh` to run the test suite (same as `flutter 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:
|
|
|
|
1. **Code Formatting**: Ensures code follows Dart and native formatting standards
|
|
- Run locally: `dart format .` to format Dart files
|
|
- Run locally: `scripts/format_native.sh --fix` to format native files
|
|
- Note: CI only checks non-generated files (excludes `.g.dart`, `.freezed.dart`)
|
|
- Generated files are reformatted automatically by build tools
|
|
|
|
2. **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`)
|
|
|
|
3. **Generated Code**: Ensures generated translations and model files are current
|
|
- Run locally: `scripts/codegen.sh --check`
|
|
|
|
4. **Tests**: Runs unit and widget tests (when available)
|
|
- Run locally: `scripts/run_tests.sh`
|
|
- This is `flutter test` with `-j` set 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, so `scripts/run_tests.sh test/widgets/some_test.dart` works.
|
|
|
|
All these checks must pass before your changes can be merged.
|
|
|
|
### Maestro end-to-end tests
|
|
|
|
Android E2E tests use [Maestro](https://maestro.mobile.dev/) 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](https://docs.maestro.dev/getting-started/installing-maestro).
|
|
|
|
Run the suites from the repository root (`py -3` can replace `python3` on Windows):
|
|
|
|
```bash
|
|
python3 scripts/maestro/run_maestro.py basic # Basic user flows
|
|
python3 scripts/maestro/run_maestro.py catalog # Catalog and music flows
|
|
python3 scripts/maestro/run_maestro.py media # Codec playback and track selection
|
|
```
|
|
|
|
Run one flow with `--flow`:
|
|
|
|
```bash
|
|
python3 scripts/maestro/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/maestro/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:
|
|
|
|
```bash
|
|
python3 scripts/maestro/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:
|
|
|
|
1. 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.
|
|
2. 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.
|
|
3. Change the readable tag, full `sha256` index 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.
|
|
4. Before changing the Bugs digest, exercise it with non-production configuration and a disposable volume. Review
|
|
migrations, take a restorable `bugs_data` backup, 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.
|
|
5. Run `python3 scripts/checks/check_container_image_pins.py`, `python3 scripts/checks/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 to `latest` when
|
|
a digest is unavailable.
|
|
|
|
## Internationalization (i18n)
|
|
|
|
This project uses `slang` for internationalization with JSON files.
|
|
|
|
### Adding New Strings
|
|
|
|
1. Add your string to `lib/i18n/strings.i18n.json`:
|
|
```json
|
|
{
|
|
"section": {
|
|
"myNewString": "My new text"
|
|
}
|
|
}
|
|
```
|
|
|
|
2. Run `dart run slang` to regenerate translation files
|
|
|
|
3. Use in your code:
|
|
```dart
|
|
Text(t.section.myNewString)
|
|
```
|
|
|
|
### Adding New Languages
|
|
|
|
1. Create new JSON file: `lib/i18n/[locale].i18n.json`
|
|
2. Copy structure from `en.i18n.json` and translate values
|
|
3. Run `dart run slang` to regenerate files
|
|
|
|
### Guidelines
|
|
|
|
- Organize strings logically in nested objects
|
|
- Use camelCase for keys
|
|
- Keep strings concise and clear
|
|
- Always run `dart run slang` after changes
|