forked from eatvio/eatvio_app
77 lines
5.0 KiB
Markdown
77 lines
5.0 KiB
Markdown
# AGENTS.md
|
|
|
|
Flutter/Dart mobile app (iOS + Android, Russian-market nutrition/consulting product).
|
|
Gitea-hosted; CI is Gitea Actions in `.gitea/workflows/` (not GitHub Actions, though it references `github.com/actions/*`).
|
|
|
|
## Toolchain (trust these over README — README version numbers are stale)
|
|
|
|
- CI runs **Flutter 3.41.2 stable**, **Java/JDK 21** (`.gitea/workflows/test.yml`).
|
|
- `pubspec.yaml` requires **Dart SDK ^3.10.4**; app version is `1.3.23+105` (README claims 1.3.0+79 — wrong).
|
|
- Build/test/lint via Flutter directly or the `Makefile`. The repo has a `package.json` **only** to wire a git pre-push hook via `simple-git-hooks`; it is not a Node project.
|
|
|
|
## CRITICAL: generated code is NOT committed
|
|
|
|
`.gitignore` excludes `*.g.dart`, `*.freezed.dart`, `*.gr.dart`, `*.mocks.dart`. After any checkout / pull / model change you **must regenerate** before `flutter analyze`, `flutter test`, or any build, or they fail on missing files:
|
|
|
|
```bash
|
|
dart run build_runner build --delete-conflicting-outputs
|
|
# or: make build-generated-files
|
|
```
|
|
|
|
Generators in use: `freezed`, `json_serializable`, `retrofit_generator`, `mockito` (`build_runner`).
|
|
Exception: `lib/generated/` (l10n output from `intl_utils` / `flutter_intl`) **is** committed — don't regenerate-and-clobber it casually.
|
|
|
|
## `.env` (required for running the app, NOT for unit tests)
|
|
|
|
- The app loads `.env` at startup (`lib/core/app_initializer.dart` → `dotenv.load`) and ships it as a Flutter asset (`pubspec.yaml` assets).
|
|
- There is **no `env.example` in the repo** despite README/`ENV_SETUP.md` saying `cp env.example .env`. Locally you must obtain `.env` from the team. In CI it is checked out from a **private `eatvio/eatvio_app_env` repo**.
|
|
- `flutter test` does **not** need `.env`: tests seed env via `dotenv.loadFromString(...)` (see `test/core/app_config_test.dart`). Access values only through `lib/core/env_config.dart` (`EnvConfig`).
|
|
- Build types: `AppBuildType { prod, tests, dev }` (`lib/core/app_initializer.dart`). `prod` → `BASE_URL`; `tests`/`dev` → `DEV_BASE_URL`. `main.dart` hardcodes `prod`.
|
|
|
|
## Commands
|
|
|
|
```bash
|
|
make run-analyse # flutter analyze
|
|
make run-tests # flutter test
|
|
make run-checks # analyze THEN test (also the pre-push hook)
|
|
make build-generated-files # build_runner --delete-conflicting-outputs
|
|
make clean-coverage # strips generated files from coverage/lcov.info
|
|
```
|
|
|
|
Run a single test file / single test:
|
|
```bash
|
|
flutter test test/core/app_config_test.dart
|
|
flutter test test/core/app_config_test.dart --plain-name "creates config with prod build type"
|
|
```
|
|
|
|
Coverage (must be run with the flag): `flutter test --coverage`, then `make clean-coverage`. `analyze_coverage.py` / `analyze_coverage_by_uncovered.py` summarize `coverage/lcov.info`.
|
|
|
|
## CI gate (binding constraint on PRs)
|
|
|
|
`.gitea/workflows/test.yml` runs on every PR, in order:
|
|
1. `flutter clean` → `flutter pub get` → `dart run build_runner build --delete-conflicting-outputs`
|
|
2. `flutter analyze`
|
|
3. `flutter test --coverage`
|
|
4. **Coverage must be ≥ 70%** (generated files excluded) or the job fails — keep this in mind when adding untested code.
|
|
5. `flutter build apk --release --dart-define=HIDE_PLANS=false --dart-define=BUILD_DATE=...`
|
|
|
|
Note: other workflow files (`build.yml`, `docker-run-tests.yml`) are simpler/older and do **not** run `build_runner`; `test.yml` is the authoritative one.
|
|
|
|
## Architecture map
|
|
|
|
- Entry: `lib/main.dart` → `AppInitializer.initialize` (env → DI → Sentry → Firebase → analytics) → runs app under Sentry.
|
|
- DI: `get_it` (`sl` global), registered in `lib/core/locator_service.dart` via `lib/core/di/*_registrations.dart` (app_config, external, service, repository, cubit).
|
|
- State: `flutter_bloc` (Cubits/Blocs). Global providers declared in `lib/core/app_config.dart` (`globalBlocProviders`).
|
|
- Routing: `go_router` (`lib/core/app_router.dart`), with deep-link/push handling in `lib/core/notifications/`.
|
|
- Networking: `dio` + `retrofit` (→ `.gr.dart`); auth token interceptor in `lib/core/interceptors/`.
|
|
- Models: `freezed` + `json_serializable` (→ `.freezed.dart` / `.g.dart`).
|
|
- Realtime: Pusher channels via `lib/core/websockets/` (`WsManager`).
|
|
- Features: ~40 self-contained folders under `lib/features/<feature>/`, conventionally `.../presentation/bloc/<name>_cubit/<name>_cubit.dart`. Tests mirror `lib/` under `test/` with fixtures in `test/fixtures/`.
|
|
- Localization: Russian only (`ru`); strings via generated `S` class from `lib/generated/l10n.dart`.
|
|
|
|
## Conventions / gotchas
|
|
|
|
- `analysis_options.yaml` enables `flutter_lints` + extra `prefer_const_*` rules and **excludes generated + l10n files** from analysis. `dart_code_metrics` is also configured (cyclomatic ≤20, ≤50 lines executable, ≤4 params, nesting ≤5) but metrics exclude `test/**`.
|
|
- Don't commit secrets: `.env`, `android/key.properties`, `*.jks` keystores are all gitignored.
|
|
- Comments and many code identifiers are in Russian (UI strings, doc comments like `/// Загрузка переменных окружения`); match surrounding style.
|