From 22685739fddd17b315b5f1c27e42aade179235d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yann=20Le=C3=A3o?= Date: Tue, 4 Aug 2026 10:36:41 -0300 Subject: [PATCH 1/8] ci(github): automate pull request and release gates --- .github/workflows/ci.yml | 128 +++++++++++++ .github/workflows/dependency-submission.yml | 33 ++++ .github/workflows/release.yml | 198 ++++++++++++++++++++ scripts/release/verify-apk.sh | 27 ++- 4 files changed, 378 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/dependency-submission.yml create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7bb0bc3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,128 @@ +name: CI + +on: + pull_request: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + dependency-review: + name: Dependency review + if: github.event_name == 'pull_request' + runs-on: ubuntu-24.04 + timeout-minutes: 10 + steps: + - name: Checkout + uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + persist-credentials: false + + - name: Review dependency changes + uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4.9.0 + with: + fail-on-severity: high + license-check: true + + quality: + name: Quality and debug APK + runs-on: ubuntu-24.04 + timeout-minutes: 45 + steps: + - name: Checkout + uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + persist-credentials: false + + - name: Set up JDK 21 + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: "21" + + - name: Validate wrapper and set up Gradle + uses: gradle/actions/setup-gradle@4733eaac7c1b0da527e4206b7671e0061de1ce37 # v6 + with: + validate-wrappers: true + cache-read-only: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} + + - name: Run quality gates and assemble debug APK + run: ./gradlew qualityCheck assembleDebug --no-daemon --stacktrace + + - name: Upload quality reports + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: quality-reports-${{ github.run_attempt }} + if-no-files-found: warn + retention-days: 14 + path: | + app/build/reports/ + app/build/test-results/ + app/build/outputs/unit_test_code_coverage/ + + - name: Upload debug APK + if: success() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: medtrack-debug-${{ github.sha }} + if-no-files-found: error + retention-days: 7 + path: app/build/outputs/apk/debug/app-debug.apk + + instrumented: + name: Instrumented tests (API 35) + runs-on: ubuntu-24.04 + timeout-minutes: 50 + steps: + - name: Checkout + uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + persist-credentials: false + + - name: Set up JDK 21 + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: "21" + + - name: Validate wrapper and set up Gradle + uses: gradle/actions/setup-gradle@4733eaac7c1b0da527e4206b7671e0061de1ce37 # v6 + with: + validate-wrappers: true + cache-read-only: true + + - name: Enable KVM + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \ + | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + + - name: Run instrumented suite + uses: ReactiveCircus/android-emulator-runner@4c44018e59b437e86cdfc41da381398f93ed8808 # v2 + with: + api-level: 35 + target: google_apis + arch: x86_64 + profile: pixel_6 + disable-animations: true + emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + script: ./gradlew connectedDebugAndroidTest --no-daemon --stacktrace + + - name: Upload instrumented test reports + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: instrumented-reports-api-35-${{ github.run_attempt }} + if-no-files-found: warn + retention-days: 14 + path: | + app/build/outputs/androidTest-results/ + app/build/reports/androidTests/ diff --git a/.github/workflows/dependency-submission.yml b/.github/workflows/dependency-submission.yml new file mode 100644 index 0000000..717562b --- /dev/null +++ b/.github/workflows/dependency-submission.yml @@ -0,0 +1,33 @@ +name: Dependency submission + +on: + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: write + +concurrency: + group: dependency-submission-${{ github.ref }} + cancel-in-progress: true + +jobs: + submit: + name: Submit Gradle dependency graph + runs-on: ubuntu-24.04 + timeout-minutes: 20 + steps: + - name: Checkout + uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + persist-credentials: false + + - name: Set up JDK 21 + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: "21" + + - name: Generate and submit Gradle dependency graph + uses: gradle/actions/dependency-submission@4733eaac7c1b0da527e4206b7671e0061de1ce37 # v6 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..a308556 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,198 @@ +name: Release APK + +on: + push: + tags: + - "v*.*.*" + +permissions: + contents: write + +concurrency: + group: release-${{ github.ref }} + cancel-in-progress: false + +jobs: + release: + name: Signed release APK + runs-on: ubuntu-24.04 + timeout-minutes: 90 + environment: production + env: + MEDTRACK_RELEASE_TAG: ${{ github.ref_name }} + MEDTRACK_API_BASE_URL: ${{ secrets.MEDTRACK_API_BASE_URL }} + MEDTRACK_SCAN_URL: ${{ secrets.MEDTRACK_SCAN_URL }} + MEDTRACK_KEYSTORE_BASE64: ${{ secrets.MEDTRACK_KEYSTORE_BASE64 }} + MEDTRACK_KEYSTORE_PASSWORD: ${{ secrets.MEDTRACK_KEYSTORE_PASSWORD }} + MEDTRACK_KEY_ALIAS: ${{ secrets.MEDTRACK_KEY_ALIAS }} + MEDTRACK_KEY_PASSWORD: ${{ secrets.MEDTRACK_KEY_PASSWORD }} + steps: + - name: Checkout tagged commit + uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Validate stable tag and protected configuration + shell: bash + run: | + set -euo pipefail + [[ "$MEDTRACK_RELEASE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] || { + echo "A release exige uma tag estável vMAJOR.MINOR.PATCH." >&2 + exit 1 + } + for variable in \ + MEDTRACK_API_BASE_URL \ + MEDTRACK_SCAN_URL \ + MEDTRACK_KEYSTORE_BASE64 \ + MEDTRACK_KEYSTORE_PASSWORD \ + MEDTRACK_KEY_ALIAS \ + MEDTRACK_KEY_PASSWORD; do + [[ -n "${!variable:-}" ]] || { + echo "Configuração obrigatória ausente: $variable" >&2 + exit 1 + } + done + [[ "$MEDTRACK_API_BASE_URL" == https://* ]] || exit 1 + [[ "$MEDTRACK_SCAN_URL" == https://* ]] || exit 1 + [[ "$MEDTRACK_API_BASE_URL" != *invalid* && "$MEDTRACK_SCAN_URL" != *invalid* ]] || exit 1 + [[ "$MEDTRACK_API_BASE_URL" != *localhost* && "$MEDTRACK_SCAN_URL" != *localhost* ]] || exit 1 + [[ "$MEDTRACK_API_BASE_URL" != *10.0.2.2* && "$MEDTRACK_SCAN_URL" != *10.0.2.2* ]] || exit 1 + git merge-base --is-ancestor "$GITHUB_SHA" origin/main || { + echo "A tag de release deve apontar para um commit presente na main." >&2 + exit 1 + } + + - name: Set up JDK 21 + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: "21" + + - name: Validate wrapper and set up Gradle + uses: gradle/actions/setup-gradle@4733eaac7c1b0da527e4206b7671e0061de1ce37 # v6 + with: + validate-wrappers: true + cache-read-only: true + + - name: Materialize temporary keystore + shell: bash + run: | + set -euo pipefail + umask 077 + keystore_path="$RUNNER_TEMP/medtrack-release.jks" + printf '%s' "$MEDTRACK_KEYSTORE_BASE64" | base64 --decode >"$keystore_path" + [[ -s "$keystore_path" ]] || { + echo "O keystore decodificado está vazio." >&2 + exit 1 + } + echo "MEDTRACK_KEYSTORE_FILE=$keystore_path" >>"$GITHUB_ENV" + + - name: Run quality gates + run: ./gradlew qualityCheck --no-daemon --stacktrace + + - name: Build, shrink, sign and inventory release + run: ./gradlew releaseReadiness --no-daemon --stacktrace + + - name: Validate APK and prepare deterministic assets + shell: bash + run: | + set -euo pipefail + apk="app/build/outputs/apk/release/app-release.apk" + expected_version="${MEDTRACK_RELEASE_TAG#v}" + apkanalyzer_path="$(find "$ANDROID_HOME" -type f -name apkanalyzer -perm -u+x | sort -V | tail -n 1)" + [[ -n "$apkanalyzer_path" ]] || { + echo "apkanalyzer não encontrado no Android SDK." >&2 + exit 1 + } + actual_version="$("$apkanalyzer_path" manifest version-name "$apk")" + [[ "$actual_version" == "$expected_version" ]] || { + echo "A versão do APK não corresponde à tag." >&2 + exit 1 + } + scripts/release/verify-apk.sh "$apk" + assets="$RUNNER_TEMP/release-assets" + mkdir -p "$assets" + release_apk="$assets/medtrack-${MEDTRACK_RELEASE_TAG}.apk" + cp "$apk" "$release_apk" + ( + cd "$assets" + sha256sum "$(basename "$release_apk")" >"$(basename "$release_apk").sha256" + ) + + - name: Upload private release evidence + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: release-evidence-${{ github.ref_name }} + if-no-files-found: error + retention-days: 30 + path: | + ${{ runner.temp }}/release-assets/ + app/build/outputs/mapping/release/mapping.txt + app/build/reports/cyclonedx-direct/bom.json + app/build/reports/cyclonedx-direct/bom.xml + + - name: Create draft GitHub Release assets + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + set -euo pipefail + apk="$RUNNER_TEMP/release-assets/medtrack-${MEDTRACK_RELEASE_TAG}.apk" + checksum="${apk}.sha256" + if gh release view "$MEDTRACK_RELEASE_TAG" >/dev/null 2>&1; then + gh release upload "$MEDTRACK_RELEASE_TAG" "$apk" "$checksum" --clobber + else + gh release create "$MEDTRACK_RELEASE_TAG" "$apk" "$checksum" \ + --verify-tag \ + --draft \ + --title "MedTrack ${MEDTRACK_RELEASE_TAG}" \ + --generate-notes + fi + + - name: Download and verify draft assets + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + set -euo pipefail + downloaded="$RUNNER_TEMP/downloaded-release" + mkdir -p "$downloaded" + gh release download "$MEDTRACK_RELEASE_TAG" \ + --dir "$downloaded" \ + --pattern "medtrack-${MEDTRACK_RELEASE_TAG}.apk*" + ( + cd "$downloaded" + sha256sum --check "medtrack-${MEDTRACK_RELEASE_TAG}.apk.sha256" + ) + + - name: Enable KVM + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \ + | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + + - name: Install and launch downloaded release APK + uses: ReactiveCircus/android-emulator-runner@4c44018e59b437e86cdfc41da381398f93ed8808 # v2 + with: + api-level: 35 + target: google_apis + arch: x86_64 + profile: pixel_6 + disable-animations: true + emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + script: | + adb install "$RUNNER_TEMP/downloaded-release/medtrack-${MEDTRACK_RELEASE_TAG}.apk" + adb shell am start -W -n com.medtrack.mobile/.MainActivity + adb shell pidof com.medtrack.mobile + + - name: Publish verified GitHub Release + env: + GH_TOKEN: ${{ github.token }} + run: gh release edit "$MEDTRACK_RELEASE_TAG" --draft=false + + - name: Remove temporary credentials + if: always() + shell: bash + run: rm -f "$RUNNER_TEMP/medtrack-release.jks" diff --git a/scripts/release/verify-apk.sh b/scripts/release/verify-apk.sh index 79bf2ba..3865009 100755 --- a/scripts/release/verify-apk.sh +++ b/scripts/release/verify-apk.sh @@ -12,12 +12,23 @@ if [[ ! -f "$apk_path" ]]; then exit 2 fi -for tool in apksigner apkanalyzer sha256sum; do - if ! command -v "$tool" >/dev/null 2>&1; then - echo "Ferramenta obrigatória ausente no PATH: $tool" >&2 - exit 2 +resolve_android_tool() { + local tool="$1" + if command -v "$tool" >/dev/null 2>&1; then + command -v "$tool" + return fi -done + if [[ -n "${ANDROID_HOME:-}" ]]; then + find "$ANDROID_HOME" -type f -name "$tool" -perm -u+x 2>/dev/null | sort -V | tail -n 1 + fi +} + +apksigner_path="$(resolve_android_tool apksigner)" +apkanalyzer_path="$(resolve_android_tool apkanalyzer)" +if [[ -z "$apksigner_path" || -z "$apkanalyzer_path" ]] || ! command -v sha256sum >/dev/null 2>&1; then + echo "apksigner, apkanalyzer e sha256sum são obrigatórios." >&2 + exit 2 +fi budget_file="$(dirname "$0")/../../config/release/budgets.properties" max_bytes="$(sed -n 's/^maxApkSizeBytes=//p' "$budget_file")" @@ -27,9 +38,9 @@ if (( apk_bytes > max_bytes )); then exit 1 fi -apksigner verify --verbose "$apk_path" -version_name="$(apkanalyzer manifest version-name "$apk_path")" -version_code="$(apkanalyzer manifest version-code "$apk_path")" +"$apksigner_path" verify --verbose "$apk_path" +version_name="$("$apkanalyzer_path" manifest version-name "$apk_path")" +version_code="$("$apkanalyzer_path" manifest version-code "$apk_path")" apk_directory="$(dirname "$apk_path")" apk_name="$(basename "$apk_path")" From 3d87526834f22ed12eb0b07d402d8c4fea796541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yann=20Le=C3=A3o?= Date: Tue, 4 Aug 2026 10:36:49 -0300 Subject: [PATCH 2/8] chore(governance): enforce repository ownership and templates --- .github/CODEOWNERS | 11 +++++ .github/ISSUE_TEMPLATE/bug.yml | 59 +++++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 5 +++ .github/ISSUE_TEMPLATE/feature.yml | 31 +++++++++++++ .github/dependabot.yml | 70 ++++++++++++++++++++++++++++++ .github/pull_request_template.md | 7 +++ CONTRIBUTING.md | 7 +++ 7 files changed, 190 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/ISSUE_TEMPLATE/bug.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature.yml create mode 100644 .github/dependabot.yml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..4d3e24d --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,11 @@ +# Revisão compartilhada do projeto. +* @YannLeao @EllenRocha1 @MClaraFerreira5 + +# Mudanças de supply chain, release e segurança exigem revisão explícita dos owners. +/.github/ @YannLeao @EllenRocha1 @MClaraFerreira5 +/gradle/ @YannLeao @EllenRocha1 @MClaraFerreira5 +/app/build.gradle.kts @YannLeao @EllenRocha1 @MClaraFerreira5 +/build.gradle.kts @YannLeao @EllenRocha1 @MClaraFerreira5 +/SECURITY.md @YannLeao @EllenRocha1 @MClaraFerreira5 +/config/release/ @YannLeao @EllenRocha1 @MClaraFerreira5 +/scripts/release/ @YannLeao @EllenRocha1 @MClaraFerreira5 diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 0000000..8ee408c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,59 @@ +name: Relatar problema +description: Registre um comportamento incorreto sem incluir dados pessoais ou credenciais. +title: "fix: " +labels: [bug, triage] +body: + - type: markdown + attributes: + value: | + Não inclua tokens, endpoints privados, imagens de medicamentos ou dados reais de pacientes. + Vulnerabilidades devem ser reportadas pelo canal privado exibido no seletor de issues. + - type: textarea + id: description + attributes: + label: Descrição + description: O que ocorreu e qual era o comportamento esperado? + validations: + required: true + - type: textarea + id: reproduction + attributes: + label: Reprodução + description: Use dados sintéticos e passos mínimos. + placeholder: | + 1. Abra... + 2. Toque em... + 3. Observe... + validations: + required: true + - type: input + id: version + attributes: + label: Versão ou commit + placeholder: v1.2.3 ou SHA + validations: + required: true + - type: dropdown + id: api + attributes: + label: Android API + options: + - API 26-28 + - API 29-32 + - API 33-35 + - API 36+ + - Não se aplica + validations: + required: true + - type: textarea + id: evidence + attributes: + label: Evidências sanitizadas + description: Logs mínimos ou screenshots sem dados sensíveis. + - type: checkboxes + id: safety + attributes: + label: Privacidade + options: + - label: Removi credenciais, tokens, imagens e dados pessoais das evidências. + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..09eed3a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Reportar vulnerabilidade em privado + url: https://github.com/MedTrack-Project/MedTrack-Mobile/security/advisories/new + about: Não abra vulnerabilidades, credenciais ou dados sensíveis em uma issue pública. diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml new file mode 100644 index 0000000..bde2c32 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -0,0 +1,31 @@ +name: Propor melhoria +description: Descreva uma necessidade de produto ou melhoria técnica. +title: "feat: " +labels: [enhancement, triage] +body: + - type: textarea + id: problem + attributes: + label: Problema + description: Qual necessidade deve ser atendida? Não inclua dados reais de pacientes. + validations: + required: true + - type: textarea + id: outcome + attributes: + label: Resultado esperado + description: Descreva o comportamento observável e critérios de sucesso. + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: Alternativas e impactos + description: Considere acessibilidade, privacidade, offline, compatibilidade e manutenção. + - type: checkboxes + id: scope + attributes: + label: Escopo + options: + - label: A proposta não contém credenciais, endpoints privados ou dados pessoais. + required: true diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..e483c57 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,70 @@ +version: 2 +updates: + - package-ecosystem: gradle + directory: / + target-branch: main + schedule: + interval: weekly + day: monday + time: "09:00" + timezone: America/Sao_Paulo + open-pull-requests-limit: 6 + reviewers: + - YannLeao + - EllenRocha1 + - MClaraFerreira5 + labels: + - dependencies + - android + groups: + android-toolchain: + patterns: + - com.android.* + - org.jetbrains.kotlin.* + - com.google.devtools.ksp + androidx-compose: + patterns: + - androidx.compose.* + - androidx.activity:activity-compose + - androidx.lifecycle:lifecycle-runtime-compose + - androidx.navigation:navigation-compose + androidx-runtime: + patterns: + - androidx.* + exclude-patterns: + - androidx.compose.* + - androidx.activity:activity-compose + - androidx.lifecycle:lifecycle-runtime-compose + - androidx.navigation:navigation-compose + networking: + patterns: + - com.squareup.retrofit2:* + - com.squareup.okhttp3:* + - com.google.code.gson:gson + quality-tooling: + patterns: + - io.gitlab.arturbosch.detekt + - org.jlleitschuh.gradle.ktlint + - org.jetbrains.kotlinx.kover + - org.cyclonedx.bom + + - package-ecosystem: github-actions + directory: / + target-branch: main + schedule: + interval: weekly + day: monday + time: "09:30" + timezone: America/Sao_Paulo + open-pull-requests-limit: 4 + reviewers: + - YannLeao + - EllenRocha1 + - MClaraFerreira5 + labels: + - dependencies + - ci + groups: + github-actions: + patterns: + - "*" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index c6a29d0..5a7dcd7 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -10,6 +10,10 @@ +## Alterações visuais + + + ## Riscos e rollback @@ -25,6 +29,9 @@ - [ ] A cobertura dos pacotes tocados não foi reduzida sem justificativa. - [ ] Registrei testes manuais para câmera, notificação, migração, background ou navegação. - [ ] Não incluí secrets, endpoints antigos, credenciais ou dados reais de pacientes. +- [ ] Revisei logs, artifacts, screenshots e payloads quanto a dados pessoais ou de saúde. +- [ ] Alterações visuais incluem evidência e verificação de acessibilidade, quando aplicável. +- [ ] Mudanças de release preservam assinatura, versionamento, checksum, SBOM e rollback. - [ ] Atualizei documentação/ADR quando alterei uma decisão técnica. - [ ] Documentei rollback para migration, autenticação ou release. - [ ] Não misturei upgrade amplo de dependências com refatoração funcional. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 770f483..f17e78f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,6 +23,10 @@ Alterações diretas na `main` não são permitidas. Todo trabalho deve passar p revisão e checks obrigatórios. Prefira branches de curta duração e atualize-a com a `main` antes da revisão final conforme a política adotada pelo time. +Os checks obrigatórios são `Dependency review`, `Quality and debug APK` e +`Instrumented tests (API 35)`. Não reinicie um job apenas para obter resultado verde sem registrar e +corrigir a causa da falha. + ## Conventional Commits Formato: @@ -77,3 +81,6 @@ Não reduza cobertura ou amplie baselines/exclusões sem uma justificativa expl - Não faça force push depois do início da revisão sem avisar os revisores. Falhas de segurança não devem ser abertas como issue pública. Siga `SECURITY.md`. + +Configuração administrativa, owners e processo de release estão descritos em +`docs/governance/repository-settings.md`. From e32675b122acf4eb087ad76352a3a02926fc1299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yann=20Le=C3=A3o?= Date: Tue, 4 Aug 2026 10:36:58 -0300 Subject: [PATCH 3/8] docs(readme): replace legacy media with project guide --- README.md | 353 +++++++++++++++++++++---------------------------- docs/README.md | 6 +- 2 files changed, 157 insertions(+), 202 deletions(-) diff --git a/README.md b/README.md index 77e9666..65ea433 100644 --- a/README.md +++ b/README.md @@ -1,236 +1,187 @@ -
- Logo do MedTrack -

MedTrack: Aplicação Mobile

-
+# MedTrack Mobile -> Aplicativo Android para controle inteligente de medicação, validação por foto e notificações +Aplicativo Android nativo para acompanhamento de medicamentos, lembretes de dose e validação por +foto. CameraX captura a imagem e a API de scan realiza o reconhecimento; quando não há conexão, o +WorkManager mantém o envio na fila. -## Visão Geral +O projeto está em modernização e os endpoints definitivos do backend e do scan ainda não estão +disponíveis. Builds debug podem usar serviços locais. Um APK release somente pode ser publicado após +configuração e aprovação do environment `production`. -
- Demonstração do MedTrack -
+## Stack -O **MedTrack Mobile** auxilia o acompanhamento correto de medicamentos, unindo **validação por -foto, notificações e acessibilidade**. A imagem capturada é enviada à API de scan; o aplicativo não -executa reconhecimento local. +- Kotlin, Coroutines e Flow; +- Jetpack Compose e Material 3; +- MVVM/UDF com casos de uso e domínio independente de Android; +- Hilt e KSP; +- Room; +- Retrofit, OkHttp e Gson; +- CameraX; +- WorkManager e AlarmManager; +- Gradle Kotlin DSL com Version Catalog. -- 🔔 **Notificações inteligentes** -- 📸 **Validação por foto** processada pela API de scan -- ♿ **Acessibilidade** como prioridade +Consulte [stack](docs/context/stack.md) e [visão geral da arquitetura](docs/architecture/overview.md) +para mais contexto. -**Público-alvo:** -- 👴 Idosos e pacientes com muitos rémedios que dificulte a organização -- 🧑‍⚕️ Cuidadores e familiares para monitoramento +## Pré-requisitos -## ✨ Destaques Técnicos +- Git; +- JDK 21; +- Android SDK com `compileSdk` 37 e platform tools; +- Android Studio compatível com AGP 9.2.1 e Kotlin 2.3.21; +- dispositivo ou emulador com Android 8.0/API 26 ou superior. -### 🏗️ Arquitetura do Projeto -O MedTrack foi desenvolvido seguindo os princípios do **MVVM (Model-View-ViewModel)** para garantir uma separação clara de responsabilidades e facilitar a manutenção do código. Utilizamos componentes modernos do Android Jetpack como: -- ViewModel para gerenciamento de dados da UI -- StateFlow imutável e UDF para atualizações reativas -- Coroutines para operações assíncronas +Use sempre o Gradle Wrapper versionado; não é necessário instalar Gradle globalmente. -
- Diagrama MVVM -
+## Configuração local -### 🎨 Interface Gráfica -Desenvolvida inteiramente com **Jetpack Compose**, a interface prioriza: -- Design moderno e intuitivo -- Acessibilidade +Clone o repositório e crie a configuração local: -> ⏰ **Lista inteligente de horários** -> - 💊 Contínuo (emoji de infinito 🔄) -> - ⏳ Temporário (emoji de calendário 📅) +```bash +git clone https://github.com/MedTrack-Project/MedTrack-Mobile.git +cd MedTrack-Mobile +cp local.properties.example local.properties +``` -
- Lista de horários vazia - Lista de horários completa -
+As propriedades esperadas são: -> 💡 **Pop-ups intuitivos** +```properties +MEDTRACK_API_BASE_URL= +MEDTRACK_SCAN_URL= +``` -
- Pop-up Editar - Pop-up Erro - Pop-up Sucesso -
+`MEDTRACK_API_BASE_URL` deve terminar com `/`. Debug aceita HTTP somente para `localhost` e +`10.0.2.2`; os releases aceitam apenas HTTPS. Credenciais e tokens nunca pertencem a esses campos. -### 📸 Captura e scan +As regras completas de precedência, rede e configuração estão em: -- CameraX exibe o enquadramento e captura a imagem em armazenamento privado. -- A API de scan realiza o reconhecimento; ML Kit não é embarcado no APK. -- Sem rede, WorkManager mantém a captura na fila para processamento posterior. -- Imagens, tokens e dados clínicos não são escritos em logs. +- [setup local](docs/setup/local-setup.md); +- [ambientes e segurança de rede](docs/security/environment-and-network.md); +- [contrato das APIs](docs/contracts/api-v1.md). -### 💾 Armazenamento Local -Para persistência de dados, utilizamos: -- **Room Database** como camada de abstração sobre SQLite -- Armazenamento seguro de informações sensíveis -- Sincronização eficiente com o backend +## Build e execução -```kotlin -@Database( - entities = [Usuario::class, Medicamento::class, Notificacao::class, Confirmacao::class], - version = 4 -) - abstract class AppDatabase : RoomDatabase() { - abstract fun medicineDao(): MedicineDao -} -```` +No Linux/macOS: -### 🌐 Comunicação com API -Integração com o backend através de: +```bash +./gradlew assembleDebug +``` -- Retrofit para requisições HTTP +No Windows: -- Gson para serialização/desserialização JSON +```powershell +.\gradlew.bat assembleDebug +``` -Tratamento robusto de erros e estados de carregamento: +O APK debug fica em `app/build/outputs/apk/debug/app-debug.apk`. Abra o projeto no Android Studio, +selecione o módulo `app` e execute em um dispositivo ou emulador. -````kotlin -interface ApiService { +## Qualidade e testes - @POST("auth/mobile/login") - suspend fun login(@Body loginRequest: LoginRequest): Response +O gate local principal executa verificação de segredos, KtLint, Detekt, Android Lint, testes +unitários e limites de cobertura: - @GET("usuario/mobile") - suspend fun getUsuario(@Header("Authorization") token: String): Response +```bash +./gradlew qualityCheck +``` - @GET("medicamento/mobile/lista") - suspend fun getMedicamentos(@Header("Authorization") token: String): Response> +Para a suíte instrumentada: - @POST("/api/confirmacao") - suspend fun confirmarMedicamento( - @Header("Authorization") token: String, - @Body request: DadosConfirmacaoRequest - ) +```bash +./gradlew connectedDebugAndroidTest +``` -} -```` +Antes de abrir um Pull Request, execute também: -### 🔧 Outras Bibliotecas +```bash +./gradlew assembleDebug +git diff --check +``` -- AlarmManager para agendamento de notificações -- Material3 para componentes UI modernos +Consulte a [estratégia de testes](docs/testing/test-strategy.md) para execução por contexto, +relatórios, determinismo e gates Kover. -## 🚀 Como Executar +## Arquitetura -1. **Pré-requisitos**: - - Android Studio Giraffe+ - - Dispositivo/emulador com Android 9+ +O código é organizado em `ui`, `domain`, `data` e `di`: -2. **Configuração**: - - Clonar repositório -```bash -git clone https://github.com/seu-usuario/medtrack-mobile.git -```` -> Configure os endpoints em `local.properties` para desenvolvimento: +```text +Compose -> ViewModel -> Use case -> Repository -> Room/Retrofit/WorkManager +``` -```properties -MEDTRACK_API_BASE_URL=http://10.0.2.2:8081/ -MEDTRACK_SCAN_URL=http://10.0.2.2:8000/detect -```` - -Builds de release exigem tag SemVer, endpoints HTTPS e assinatura fornecidos externamente. Consulte -[`docs/setup/build-release.md`](docs/setup/build-release.md). Enquanto backend e scan não tiverem -deploy definitivo, gere apenas o APK fake de validação descrito nessa documentação. - -## 🌐 MedTrack: Versão Web - - - -### Plataforma Complementar -O **MedTrack Web** é a interface administrativa do sistema, desenvolvida para: - -- 👩‍⚕️ **Profissionais de saúde** gerenciarem pacientes -- 👨‍👩‍👧 **Familiares** acompanharem a medicação remota -- 📊 Visualização de relatórios e histórico completo - -
- Dashboard Web -
- -### Integração Mobile-Web -- 🔄 Sincronização em tempo real dos dados de medicação -- 🔐 Autenticação unificada JWT -- 📩 Notificações complementares via email - -## 🌟 Time de Contribuidores - -
- - - - - - - -
- Ellen Rocha
- Ellen Rocha -
- - - - - - -
-
- Maria Clara
- Maria Clara -
- - - - - - -
-
- Yann Leão
- Yann Leão -
- - - - - - -
-
- -
- -## 🎓 Orientação - -
- - - - - -
- Prof. Igor Amaral
- Prof. Igor Amaral -
- - - -
- Orientador -
- -
- -## 📄 Licença - -Projeto acadêmico desenvolvido para a disciplina de **Projeto Interdisciplinar de Engenharia da Computação 1 (PIEC1)** -Universidade Federal Rural de Pernambuco — Unidade Acadêmica de Belo Jardim (UFRPE/UABJ) +As principais referências são: + +- [arquitetura](docs/architecture/overview.md); +- [camada de UI](docs/architecture/ui-layer.md); +- [camada de domínio](docs/architecture/domain-layer.md); +- [camada de dados](docs/architecture/data-layer.md); +- [integração com APIs](docs/architecture/api-integration.md); +- [estratégia offline](docs/architecture/offline-first.md); +- [decisões arquiteturais](docs/decisions/). + +## CI/CD e release + +Todo Pull Request para `main` executa três checks obrigatórios: + +- `Dependency review`; +- `Quality and debug APK`; +- `Instrumented tests (API 35)`. + +Tags estáveis `vMAJOR.MINOR.PATCH` acionam o workflow de release. O job aguarda aprovação do +environment `production`, valida endpoints/secrets, gera APK assinado e minificado, confere tamanho e +assinatura, produz SBOM/checksum, valida os assets baixados e instala o APK em emulador antes de +publicar apenas o APK e o SHA-256 no GitHub Release. + +Enquanto as APIs definitivas não estiverem publicadas, não crie tags estáveis. Para validação local, +use o APK fake sem publicar descrito em [build e release](docs/setup/build-release.md). + +Documentação operacional: + +- [geração e verificação de APK](docs/setup/apk-generation.md); +- [release hardening](docs/release/release-hardening.md); +- [rollback e revogação](docs/release/rollback-runbook.md); +- [configuração de governança](docs/governance/repository-settings.md). + +## Troubleshooting + +### Endpoint rejeitado + +Confira HTTPS, barra final da URL base e a precedência descrita no setup. Release rejeita valores +ausentes, `.invalid`, `localhost`, `10.0.2.2` e HTTP. + +### Dispositivo físico não acessa `10.0.2.2` + +Esse endereço pertence ao emulador. Use um endpoint HTTPS acessível pelo dispositivo; não amplie +cleartext sem revisão do Network Security Config. + +### Falha de ADB ou instrumentação + +Execute `adb devices`, confirme que o dispositivo está autorizado e repita apenas o grupo afetado. +Veja a [estratégia de testes](docs/testing/test-strategy.md). + +### Falha de migration + +Não use destructive migration. Verifique os schemas versionados e siga a documentação do +[banco local](docs/architecture/local-database.md). + +### Release sem assinatura ou versão + +Use `releaseReadiness` e configure todas as variáveis listadas em +[build e release](docs/setup/build-release.md). Nunca versione o keystore. + +## Contribuição e segurança + +Pull Requests são obrigatórios. Leia [CONTRIBUTING.md](CONTRIBUTING.md), use Conventional Commits e +preencha o template do PR. Vulnerabilidades não devem ser abertas como issue pública; siga +[SECURITY.md](SECURITY.md). + +CODEOWNERS: + +- [Yann Leão](https://github.com/YannLeao); +- [Ellen Rocha](https://github.com/EllenRocha1); +- [Clara Ferreira](https://github.com/MClaraFerreira5). + +## Licença +Projeto acadêmico desenvolvido para a disciplina Projeto Interdisciplinar de Engenharia da +Computação 1 da Universidade Federal Rural de Pernambuco, Unidade Acadêmica de Belo Jardim. diff --git a/docs/README.md b/docs/README.md index 74262da..e32fcee 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,8 +8,12 @@ Esta pasta centraliza o contexto tecnico e arquitetural do aplicativo Android Me - `context/`: contexto de produto, stack, convencoes, glossario e premissas tecnicas. - `decisions/`: ADRs, ou registros de decisoes arquiteturais. - `setup/`: preparacao local, build, release e geracao de APK. +- `release/`: hardening, publicação, rollback e revogação. +- `governance/`: regras administrativas e operação do repositório. +- `security/`: ambientes, rede, segredos e privacidade. +- `testing/`: estratégia, pirâmide e execução dos testes. - `tasks/`: tarefas em andamento, template e historico. -- `_assets/`: imagens e midias usadas pela documentacao e pelo README principal. +- `_assets/`: mídia histórica; não usar no README principal ou em nova documentação sem revisão. ## Como usar From 14348ca602fad2d121e7f998919b78c5cdeaf2fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yann=20Le=C3=A3o?= Date: Tue, 4 Aug 2026 10:37:23 -0300 Subject: [PATCH 4/8] docs(release): document governance and rollback operations --- .../adr-005-ci-release-governance.md | 32 +++++++++ docs/governance/repository-settings.md | 72 +++++++++++++++++++ docs/release/release-hardening.md | 6 +- docs/release/rollback-runbook.md | 63 ++++++++++++++++ docs/setup/build-release.md | 6 +- 5 files changed, 174 insertions(+), 5 deletions(-) create mode 100644 docs/decisions/adr-005-ci-release-governance.md create mode 100644 docs/governance/repository-settings.md create mode 100644 docs/release/rollback-runbook.md diff --git a/docs/decisions/adr-005-ci-release-governance.md b/docs/decisions/adr-005-ci-release-governance.md new file mode 100644 index 0000000..9812b0f --- /dev/null +++ b/docs/decisions/adr-005-ci-release-governance.md @@ -0,0 +1,32 @@ +# ADR-005 — CI obrigatória e release somente por tag protegida + +## Status + +Aceita. + +## Contexto + +A `main` aceita mudanças somente por Pull Request. APKs de produção incorporam endpoints públicos, +dependem de assinatura privada e precisam ser reproduzíveis e auditáveis. Builds locais não devem +ter autoridade para publicar um artifact oficial. + +## Decisão + +- Pull Requests executam qualidade/build e instrumentação em jobs separados. +- Actions externas são fixadas por SHA e atualizadas pelo Dependabot com revisão. +- Os jobs têm permissões mínimas, timeout, concurrency e artifacts de diagnóstico com retenção. +- Release é acionada apenas por tag estável `vMAJOR.MINOR.PATCH`. +- O job usa o environment protegido `production`, com aprovação humana e secrets próprios. +- O workflow cria um draft, baixa e confere os assets, instala/inicializa o APK em emulador e somente + então publica; APK e checksum são os únicos assets públicos. +- Mapping R8, SBOM e relatórios ficam em artifact privado com retenção limitada. +- Tags de validação e endpoints sintéticos não podem produzir GitHub Release. + +## Consequências + +Uma release exige configuração administrativa além dos arquivos versionados. A instrumentação torna +a CI mais lenta, mas isola falhas de dispositivo. A publicação fica consistente e auditável; perda ou +indisponibilidade do environment impede release em vez de produzir APK parcialmente configurado. + +O processo de emergência publica uma nova versão e não move tags existentes, conforme o runbook de +rollback e revogação. diff --git a/docs/governance/repository-settings.md b/docs/governance/repository-settings.md new file mode 100644 index 0000000..86853c6 --- /dev/null +++ b/docs/governance/repository-settings.md @@ -0,0 +1,72 @@ +# Governança do repositório + +Arquivos versionados definem owners, templates, workflows e atualização de dependências. Regras de +branch e environments são estado administrativo do GitHub e devem ser configuradas após o merge. + +## Owners + +- [Ellen Rocha](https://github.com/EllenRocha1) +- [Clara Ferreira](https://github.com/MClaraFerreira5) +- [Yann Leão](https://github.com/YannLeao) + +O `CODEOWNERS` atribui os três owners ao projeto e reforça caminhos de CI, build e segurança. A regra +de branch deve exigir revisão de code owner; isso não significa exigir aprovação dos três em todo PR. + +## Ruleset da `main` + +Em `Settings > Rules > Rulesets`, criar uma regra ativa para a branch padrão: + +- bloquear criação, atualização e exclusão direta, permitindo atualização somente via Pull Request; +- exigir pelo menos uma aprovação; +- exigir revisão de CODEOWNERS; +- dispensar aprovações antigas quando novos commits forem enviados; +- exigir resolução de todas as conversas; +- exigir branch atualizada antes do merge; +- exigir histórico linear; +- bloquear force push e exclusão; +- não permitir bypass, inclusive para administradores, salvo procedimento emergencial auditado; +- exigir os checks `Dependency review`, `Quality and debug APK` e `Instrumented tests (API 35)`; +- exigir que o resultado venha da GitHub Action `CI` e não aceitar checks antigos ou de outra origem. + +Teste a regra com um PR propositalmente quebrado antes de considerá-la ativa. + +## Environment `production` + +Criar `Settings > Environments > production`: + +- adicionar os três owners como required reviewers e impedir self-review; +- restringir deployment às tags protegidas que correspondam a `v*.*.*`; +- não permitir bypass administrativo; +- cadastrar como Environment Secrets: + - `MEDTRACK_API_BASE_URL`; + - `MEDTRACK_SCAN_URL`; + - `MEDTRACK_KEYSTORE_BASE64`; + - `MEDTRACK_KEYSTORE_PASSWORD`; + - `MEDTRACK_KEY_ALIAS`; + - `MEDTRACK_KEY_PASSWORD`. + +Endpoints são configuração compilada, mas ficam em secrets para mascaramento consistente dos logs. +Nunca cadastrar valores fake, localhost ou `10.0.2.2` no environment de produção. + +## Tags e release + +Criar ruleset para tags `v*.*.*` que bloqueie atualização e exclusão. A publicação deve ocorrer +somente pelo workflow `Release APK`. Tags de validação com sufixo não são publicáveis. + +## Dependências + +O Dependabot abre PRs semanais agrupados. Cada PR deve passar por toda a CI e revisão humana. Não +habilitar auto-merge para AGP/Kotlin/KSP/Gradle, bibliotecas de segurança, Room ou CameraX. +Habilite Dependency Graph, Dependabot alerts e Dependabot security updates. O workflow +`Dependency submission` atualiza o grafo Gradle após mudanças na `main`; o PR falha para nova +vulnerabilidade de severidade alta ou crítica. + +## Auditoria periódica + +Trimestralmente ou após mudança de owners: + +1. revisar acessos, bypasses, secrets e required reviewers; +2. confirmar que os checks exigidos ainda correspondem aos nomes dos jobs; +3. validar um PR bloqueado e uma release de homologação; +4. rotacionar credenciais conforme política e remover artifacts expirados; +5. conferir alertas do Dependabot, dependency review e Security Advisories. diff --git a/docs/release/release-hardening.md b/docs/release/release-hardening.md index 897dc33..edcb404 100644 --- a/docs/release/release-hardening.md +++ b/docs/release/release-hardening.md @@ -52,11 +52,11 @@ Até essa decisão: Para adotar um fornecedor, criar ADR e implementar coleta desabilitada por padrão, consentimento revogável, redaction testada, ambientes separados, retenção mínima e teste de payload. São proibidos: JWT, credenciais, request/response bodies, URL de imagem, nome/posologia, identificadores de usuário e -arquivos capturados. A Etapa 8 só pode fazer upload de mapping ao fornecedor após essa aprovação. +arquivos capturados. O workflow não envia mapping a fornecedor; isso exige aprovação e ADR próprios. ## Supply chain -`./gradlew :app:cyclonedxDirectBom` gera SBOM CycloneDX 1.6. Na Etapa 8, os arquivos serão artifacts privados e +`./gradlew :app:cyclonedxDirectBom` gera SBOM CycloneDX 1.6. No workflow, os arquivos são artifacts privados e entrada de um scanner de vulnerabilidades/dependency review e da auditoria de licenças. A resolução remota de metadados fica desabilitada no Gradle para tornar a geração determinística; o scanner da CI deve enriquecer o inventário. Vulnerabilidade alta/crítica explorável deve bloquear release; exceções @@ -69,4 +69,4 @@ da distribuição. - smoke test de sucesso ponta a ponta; - escolha jurídica/técnica do fornecedor de observabilidade; - métricas representativas para decidir Baseline Profile; -- workflow e proteção do ambiente `production` (Etapa 8). +- configuração administrativa e aprovação do environment `production`. diff --git a/docs/release/rollback-runbook.md b/docs/release/rollback-runbook.md new file mode 100644 index 0000000..2c87e6a --- /dev/null +++ b/docs/release/rollback-runbook.md @@ -0,0 +1,63 @@ +# Runbook de rollback e revogação + +## Objetivo + +Conter uma release Android incorreta ou comprometida sem sobrescrever artifacts, esconder evidências +ou reutilizar uma versão já distribuída. GitHub Release não equivale a atualização forçada nos +dispositivos; a correção sempre requer uma nova versão. + +## Classificação inicial + +Interrompa novas publicações e registre um incidente privado. Classifique: + +- falha funcional sem risco de dados; +- contrato ou endpoint incorreto; +- migration incompatível ou perda de dados; +- vazamento de token, keystore ou outro secret; +- dependência vulnerável; +- exposição de dados pessoais ou de saúde. + +Não copie payloads, imagens ou credenciais para issues, logs ou canais públicos. + +## Contenção + +1. Desative o workflow/environment `production` ou remova temporariamente seus aprovadores. +2. Marque o GitHub Release afetado como draft ou remova apenas os assets públicos, preservando + evidências privadas do job e o commit/tag para auditoria. +3. Se a API estiver envolvida, bloqueie a versão no backend apenas se houver mecanismo previamente + testado e comunicação ao usuário. +4. Em caso de credencial, revogue primeiro; alterar o repositório ou apagar logs não revoga secrets. + +## Credenciais e assinatura + +- Endpoint ou token: rotacione no provedor, atualize o environment e audite acessos. +- Keystore exposto: restrinja distribuição imediatamente e siga o processo de troca de chave da + plataforma de distribuição. Não gere silenciosamente outra chave esperando compatibilidade. +- Secret do GitHub: remova/rotacione e revise logs/artifacts de todas as execuções acessíveis. +- Mapping R8 exposto: remova o artifact público, revise alcance e preserve uma cópia privada. + +## Correção + +1. Crie branch de correção a partir do commit adequado da `main`. +2. Reproduza e adicione teste de regressão quando possível. +3. Reavalie migrations e compatibilidade de dados; nunca reduza a versão Room. +4. Execute CI completa e smoke test do APK minificado. +5. Incremente SemVer e crie uma nova tag. Nunca mova, apague ou recrie a tag afetada para substituir + binários sob o mesmo nome. +6. Publique nova release e valide checksum, assinatura, endpoints e instalação/upgrade. + +## Validação pós-publicação + +- baixar APK e checksum do GitHub Release em ambiente limpo; +- executar `sha256sum --check`; +- verificar assinatura e versionName/versionCode; +- instalar sobre a versão anterior e em instalação limpa; +- executar login, banco, notificações, câmera, scan e fila offline; +- confirmar que endpoints e secrets revogados não funcionam mais; +- monitorar falhas sem registrar dados sensíveis. + +## Comunicação e encerramento + +Documente linha do tempo, versões afetadas, impacto, decisões, owners e ações preventivas. Se houver +dados pessoais ou de saúde, acione os responsáveis legais antes de comunicação pública. Encerre +somente depois de validar a nova release, confirmar revogações e criar tarefas de prevenção. diff --git a/docs/setup/build-release.md b/docs/setup/build-release.md index 6dd449b..ac0199a 100644 --- a/docs/setup/build-release.md +++ b/docs/setup/build-release.md @@ -5,7 +5,8 @@ O aplicativo está tecnicamente preparado para gerar APK release assinado, minificado e auditável. Backend e API de scan ainda não possuem endpoints definitivos; portanto, APKs gerados com domínios `.invalid` são artefatos locais de validação e **não podem ser publicados**. O upload para GitHub -Release será habilitado somente na Etapa 8, protegido pelo ambiente `production`. +Release está automatizado por tag e protegido pelo environment `production`, mas permanece bloqueado +até os endpoints definitivos e secrets serem cadastrados e aprovados. ## Contrato de configuração @@ -81,7 +82,7 @@ Após os endpoints definitivos existirem: 1. validar o mesmo commit com `qualityCheck`, testes instrumentados e `releaseReadiness`; 2. criar tag anotada `vMAJOR.MINOR.PATCH` no commit aprovado; -3. a Etapa 8 materializará o keystore temporariamente e fornecerá endpoints/secrets; +3. o workflow materializará o keystore temporariamente e fornecerá endpoints/secrets; 4. o workflow verificará assinatura, budgets, SBOM e checksum antes de anexar o APK; 5. falha em qualquer gate deve impedir a publicação e remover credenciais temporárias. @@ -98,3 +99,4 @@ fluxo, identifique a classe afetada e adicione a regra mínima. Uma release não é sobrescrita. Para rollback, corrigir/reverter o commit, incrementar a versão e publicar uma nova tag. Se houver risco de credencial, revogar a chave/secret antes da nova versão. +Consulte o [runbook de rollback e revogação](../release/rollback-runbook.md). From b12f0c71b534b0ff324fe1bb53cea951f70e0a51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yann=20Le=C3=A3o?= Date: Tue, 4 Aug 2026 11:07:02 -0300 Subject: [PATCH 5/8] docs(project): finalize modernization documentation --- PLANO_DE_MODERNIZACAO.md | 576 ------------------ config/release/budgets.properties | 2 +- .../architecture/adr/0001-layer-boundaries.md | 12 +- docs/architecture/data-layer.md | 2 +- docs/architecture/sync-strategy.md | 5 +- docs/context/conventions.md | 5 +- docs/context/stack.md | 3 +- docs/context/technical-context.md | 2 +- docs/contracts/api-v1.md | 4 +- docs/release/release-hardening.md | 2 +- docs/security/environment-and-network.md | 5 +- docs/setup/quality-baseline.md | 161 ++--- 12 files changed, 70 insertions(+), 709 deletions(-) delete mode 100644 PLANO_DE_MODERNIZACAO.md diff --git a/PLANO_DE_MODERNIZACAO.md b/PLANO_DE_MODERNIZACAO.md deleted file mode 100644 index 1fd479c..0000000 --- a/PLANO_DE_MODERNIZACAO.md +++ /dev/null @@ -1,576 +0,0 @@ -# Plano de Modernização — MedTrack Mobile - -## 1. Escopo e premissas - -Este plano foi elaborado a partir da inspeção estática do repositório e de uma tentativa de executar -`testDebugUnitTest`, `koverHtmlReport` e `lintDebug`. - -A tentativa de validação Gradle foi interrompida pelo ambiente antes da conclusão. Portanto, este -documento não assume que o build, os testes, o lint ou o relatório de cobertura estejam aprovados. -Durante a execução foi confirmado ao menos um aviso: a permissão -`android.permission.USE_FULL_SCREEN_INTENT` está declarada duas vezes no Manifest. - -O trabalho deve ser entregue em Pull Requests pequenos, sequenciais e reversíveis. Cada etapa abaixo -corresponde a um PR. Uma etapa só deve começar depois que a anterior estiver integrada à `main`. -Mudanças de dependências, arquitetura e comportamento não devem ser misturadas no mesmo PR. - -As versões listadas neste documento são as encontradas no repositório em 29/07/2026. Antes de qualquer -upgrade, deve-se confirmar compatibilidade nas notas oficiais de AGP, Gradle, Kotlin, KSP, Compose e -demais bibliotecas; não se deve atualizar todas as dependências de uma vez. - -## 2. Resumo do diagnóstico atual - -### 2.1 Estrutura e arquitetura - -O projeto possui um único módulo Android, `:app`, com aproximadamente 82 arquivos Kotlin de produção -e 5,4 mil linhas. A organização por pacotes implementa uma arquitetura em camadas próxima de -**MVVM com Clean Architecture pragmática**: - -- `ui`: telas e componentes Jetpack Compose, navegação e ViewModels; -- `domain`: modelos, mappers, um use case e serviços; -- `data`: Room, Retrofit, DTOs, sessão e repositories; -- `di`: módulos Hilt; -- `utils`: conectividade, notificações, formatação e utilitários. - -O fluxo predominante é `Compose -> ViewModel -> Repository -> Retrofit/Room`, com modelos de domínio -na volta. Há DTOs, entities e mappers separados, Hilt como contêiner de injeção e abstração -`MedicamentoRepositoryContract`. - -Essa separação é útil, mas ainda não caracteriza Clean Architecture estrita: - -- `domain/service` contém infraestrutura Android (`CameraService`, `DetectionService` e o - `CoroutineWorker` `ScanUpload`), fazendo a camada de domínio depender de Android, CameraX, - WorkManager, notificações e Hilt; -- repositories concretos dependem diretamente de `Context`, `AppDatabase`, WorkManager, - `NotificationScheduler`, Retrofit e DAOs, concentrando rede, cache, regras, arquivos, - agendamento e efeitos colaterais; -- somente o repository de medicamentos possui contrato; `AuthRepository`, `ScanRepository`, - câmera, relógio e armazenamento de sessão não estão atrás de abstrações testáveis; -- ViewModels importam classes concretas da camada `data`, tratam exceções genéricas e expõem vários - `LiveData` independentes, o que permite estados inconsistentes; -- `CameraViewModel` recebe `PreviewView`, `LifecycleOwner`, `Rect`, `Uri` e serviço CameraX, - misturando coordenação de UI/framework com estado e regra de apresentação; -- `MainActivity`, navegação, notificações e workers compartilham payloads e ações por strings, - aumentando acoplamento e risco em deep links; -- há apenas um use case explícito (`OrdenarMedicamentos`); regras relevantes permanecem em - repositories e ViewModels; -- os documentos arquiteturais existentes descrevem a intenção, mas contêm divergências, como - mencionar banco na versão 8 enquanto `AppDatabase` está na versão 9. - -Recomendação de direção: manter MVVM, adotar fluxo unidirecional de dados em cada tela e tornar as -fronteiras `ui -> domain -> data` explícitas. A modularização física deve acontecer somente após -essas fronteiras estarem estabilizadas e justificadas por tempo de build, ownership ou reuso. - -### 2.2 Ecossistema Kotlin/Android - -Inventário encontrado: - -| Item | Estado atual | -|---|---| -| Build scripts | Kotlin DSL (`.gradle.kts`) | -| Módulos | Apenas `:app` | -| Version Catalog | `gradle/libs.versions.toml` em uso | -| Gradle Wrapper | 9.4.1 | -| Android Gradle Plugin | 9.2.1 | -| Kotlin | 2.3.21 | -| KSP | 2.3.2 | -| Java/JVM target | 21 | -| SDK | `compileSdk 37`, `targetSdk 36`, `minSdk 26` | -| UI | Jetpack Compose + Material 3; não foram encontradas telas XML | -| Compose BOM | 2026.05.01 | -| Estado assíncrono | Coroutines 1.11.0; predomínio de LiveData, uso pontual de StateFlow | -| Navegação | Navigation Compose 2.9.8 | -| DI | Hilt 2.59.2 + KSP | -| Rede | Retrofit 3.0.0, OkHttp 5.3.2, Gson 2.14.0 | -| Persistência | Room 2.8.4, schema version 9 e schemas 8/9 exportados | -| Background | WorkManager 2.11.2 e AlarmManager | -| Câmera/IA local | CameraX 1.6.1 e ML Kit | -| Cobertura | Kover 0.9.8 | - -Pontos positivos: - -- stack declarada centralmente no Version Catalog; -- Compose Compiler gerenciado pelo plugin Kotlin Compose; -- KSP no lugar de KAPT; -- Hilt, Room, WorkManager e coroutines já fazem parte do projeto; -- schemas Room recentes estão versionados; -- endpoints já são expostos via `BuildConfig`. - -Problemas e oportunidades: - -- não há verificação automatizada de compatibilidade entre Gradle, AGP, Kotlin, KSP e JDK; -- o `gradlew` está versionado sem bit executável, exigindo `bash gradlew` em ambientes Unix; -- há propriedades AGP experimentais e supressões na configuração, que devem ser justificadas ou - removidas após validação; -- não existe política automatizada para atualização de dependências (Dependabot/Renovate) nem - verificação de lockfile/dependency verification; -- há mistura de LiveData e Flow; Compose observa diversos LiveData em vez de consumir um - `StateFlow` lifecycle-aware; -- `runtime-livedata`, `lifecycle-livedata` e versões Compose individuais merecem revisão para evitar - versões redundantes fora do BOM; -- `camera-camera2` e `camera-camera2-pipe` aparecem simultaneamente; é preciso comprovar a necessidade - de ambos; -- o código usa Gson diretamente em repository/worker, dificultando troca e testes; -- `release` está com `isMinifyEnabled = false`, apesar de declarar regras ProGuard/R8; -- `versionCode` e `versionName` são fixos e não há estratégia de versionamento por tag; -- o namespace/application ID originalmente encontrado, `com.example.piec_1`, era provisório e - inadequado para distribuição; ele foi substituído por `com.medtrack.mobile` em um PR próprio. - -### 2.3 Configuração de APIs, segurança e privacidade - -Existem duas configurações: - -- `MEDTRACK_API_BASE_URL`; -- `MEDTRACK_SCAN_URL`. - -Elas podem vir de propriedades Gradle ou `local.properties`, mas possuem fallbacks com IPs privados -hardcoded. O Manifest permite cleartext globalmente (`usesCleartextTraffic="true"`) e os exemplos da -documentação usam HTTP. Isso torna possível gerar por engano um APK apontando para infraestrutura -antiga/local e permite tráfego sem TLS. - -Outros riscos observados: - -- OkHttp registra `BODY` em todos os builds, podendo expor credenciais, JWTs, dados médicos e imagens; -- `LoginViewModel` registra o token e `TelaLogin` registra usuário e senha; -- JWT é armazenado em `SharedPreferences` comum; -- backup do aplicativo está habilitado, enquanto as regras ainda contêm comentários/TODOs padrão; -- URLs completas de scan são aceitas via `@Url`, exigindo validação para evitar hosts inesperados; -- permissões incluem `RECORD_AUDIO`, `FOREGROUND_SERVICE`, full-screen intent duplicada e acesso a - storage legado; deve-se comprovar a necessidade de cada uma; -- `android:showWhenLocked`, `turnScreenOn` e full-screen notifications têm impacto de privacidade e - políticas de plataforma; -- arquivos de scans e payloads de medicamentos podem permanecer em armazenamento local ou em extras - de Intent; retenção, descarte e exposição devem ser auditados; -- `file.delete()` no worker não verifica falha e não há política explícita de limpeza; -- não há Network Security Config por variante, redaction de logs ou validação de configuração de - release. - -Endpoints de backend e IA em modernização devem permanecer parametrizados. URLs reais não são -segredos criptográficos, mas devem ser tratadas como configuração de ambiente; tokens, keystores, -senhas e credenciais de assinatura são secrets e nunca devem entrar no Git ou em `BuildConfig`. - -### 2.4 Concorrência, persistência e trabalho em background - -- O uso de `viewModelScope` e `withContext(Dispatchers.IO)` está disseminado, mas dispatchers e relógio - não são injetáveis, reduzindo determinismo dos testes. -- `NotificationReceiver` cria um `CoroutineScope(Dispatchers.IO)` manual após `goAsync`; isso requer - timeout/cancelamento e garantia rigorosa de `finish()`. -- O worker `ScanUpload` usa EntryPoint manual de Hilt, estados de fila como strings e processa todos - os itens em sequência; faltam política explícita de idempotência, backoff, limite de tentativas e - distinção entre erro permanente e transitório. -- `ScanRepository` testa três nomes de multipart em sequência, sugerindo contrato instável com a API. -- A fila exige rede `UNMETERED`, o que pode postergar indefinidamente um envio que poderia aceitar - qualquer rede. -- O banco está na versão 9, mas a lista de migrations salta de `3_4` para `6_7`; é necessário provar - todos os caminhos suportados por testes de migração e definir a menor versão de origem suportada. -- `fallbackToDestructiveMigration(false)` protege contra destruição silenciosa, porém não substitui - testes de migração. -- DAOs são obtidos de `AppDatabase` dentro dos repositories, em vez de serem injetados diretamente. -- Operações remotas e locais não possuem um modelo uniforme de resultado/erro nem estratégia - transacional documentada. - -### 2.5 Qualidade e testes - -Foram encontrados 11 arquivos de teste local (`app/src/test`), aproximadamente 875 linhas: - -- utilitários de data e comparação de texto; -- mappers de domínio e remotos; -- converters Room; -- `OrdenarMedicamentos`; -- `MedicamentoViewModel`; -- `DoseHorarioViewModel`; -- regra de dispatcher principal para coroutines. - -Dependências declaradas: JUnit 4, AndroidX Arch Core Testing e Coroutines Test. Não há MockK nem -Mockito; os testes existentes usam fakes em alguns pontos. - -Lacunas: - -- não existe diretório/teste em `app/src/androidTest`, embora Espresso e Compose Test estejam - declarados; -- não foram encontrados testes de UI Compose, navegação, acessibilidade, Room instrumentado, - migrations, Retrofit/serialização, WorkManager, AlarmManager, notificações, autenticação, - `LoginViewModel`, `CameraViewModel`, repositories ou fluxo offline; -- não há MockWebServer, Room Testing, WorkManager Testing nem Hilt Android Testing declarados; -- Kover está configurado, porém exclui quase toda a infraestrutura, repositories principais, - duas ViewModels, UI, navegação, workers e serviços. Um percentual alto poderia ser enganoso; -- não há limiar mínimo de cobertura visível; -- não foram encontrados ktlint, detekt, Android lint customizado, baseline ou configuração de - formatação; -- não há automação de CI visível em `.github/workflows`; -- a tentativa de `testDebugUnitTest koverHtmlReport lintDebug` não terminou, logo o baseline real - ainda precisa ser estabelecido na Etapa 0. - -### 2.6 Prioridades - -| Prioridade | Tema | Motivo | -|---|---|---| -| P0 | Remover vazamento de credenciais/logs e impedir release com endpoints fallback | Segurança e risco de produção | -| P0 | Validar build, testes, lint e migrations em ambiente reproduzível | Não há baseline confiável | -| P0 | Parametrizar e validar endpoints de backend/IA por variante | APIs serão republicadas | -| P1 | Reduzir acoplamento e unificar estado de UI com StateFlow | Testabilidade e previsibilidade | -| P1 | Cobrir autenticação, confirmação, fila offline e migrations | Fluxos críticos de saúde/dados | -| P1 | Robustecer WorkManager, notificações e persistência | Confiabilidade offline | -| P2 | Ativar R8/resource shrinking e assinatura segura de release | Distribuição e segurança | -| P2 | Modularizar quando houver evidência | Escalabilidade sem refatoração prematura | - -## 3. Estratégia de Pull Requests - -### Etapa 0 — Diagnóstico executável e setup base - -**Objetivo do PR** - -Criar um baseline reproduzível de build/qualidade, padronizar o projeto e tornar falhas atuais -visíveis sem mudar comportamento funcional. - -**Tarefas detalhadas** - -- Corrigir o bit executável do Gradle Wrapper e validar seu checksum/distribuição. -- Documentar JDK 21, Android SDK e comandos oficiais de desenvolvimento. -- Executar separadamente `assembleDebug`, `testDebugUnitTest`, `lintDebug` e relatório Kover, - registrando resultados iniciais. -- Adicionar ktlint (ou Spotless com ktlint) e detekt com versões no Version Catalog. -- Criar configurações explícitas, inicialmente com baseline apenas para dívida preexistente; - arquivos novos/modificados não podem adicionar violações. -- Corrigir problemas mecânicos de baixo risco: permissão full-screen duplicada, imports/formatação, - warnings de Manifest e divergência da documentação sobre schema Room 9. -- Revisar propriedades experimentais do `gradle.properties`; remover as desnecessárias e documentar - as mantidas. -- Revisar aliases e dependências potencialmente redundantes, sem upgrade em massa. -- Configurar Kover para produzir XML e HTML, publicar o baseline real e reduzir exclusões injustificadas. -- Adicionar tarefas agregadoras, por exemplo `qualityCheck`, sem esconder tarefas oficiais. -- Criar uma matriz de compatibilidade JDK/Gradle/AGP/Kotlin/KSP e registrar a versão do Android Studio - recomendada. -- Opcionalmente habilitar Gradle dependency verification depois de gerar e revisar metadados. - -**Critérios de aceite/verificação** - -- `./gradlew --version` funciona em Unix e usa JDK 21. -- `./gradlew clean assembleDebug testDebugUnitTest lintDebug koverXmlReport` termina com sucesso, - ou falhas legadas justificadas estão registradas e isoladas em baseline aprovado. -- ktlint/Spotless e detekt executam localmente e falham com uma violação introduzida de propósito - durante o teste do PR. -- Nenhuma tela ou fluxo funcional é alterado. -- `git diff --check` não reporta whitespace inválido. -- O relatório Kover é gerado e seu denominador/exclusões são revisados no PR. - -### Etapa 1 — Configuração de ambientes e segurança imediata - -**Objetivo do PR** - -Impedir vazamento de dados sensíveis e garantir que cada APK use endpoints explícitos e válidos, -sem depender das URLs antigas. - -**Tarefas detalhadas** - -- Definir variantes/configurações claras para `debug` e `release` (e `staging`, se houver ambiente). -- Ler `MEDTRACK_API_BASE_URL` e `MEDTRACK_SCAN_URL` por Gradle properties ou variáveis de ambiente; - aceitar `local.properties` apenas para desenvolvimento local. -- Remover IPs antigos como fallback. Debug pode usar um valor local documentado; release deve falhar - no configuration phase se endpoints estiverem ausentes, forem HTTP, não tiverem host válido ou - estiverem sem o formato exigido pelo Retrofit. -- Manter endpoints fora do código Kotlin. Não armazenar tokens ou credenciais em `BuildConfig`. -- Criar abstração tipada de configuração (`ApiEndpoints`) injetada por Hilt. -- Restringir cleartext por Network Security Config somente ao debug/local; release deve exigir HTTPS. -- Habilitar logging HTTP apenas em debug e aplicar redaction aos headers `Authorization` e cookies; - usar nível `NONE` em release. -- Remover logs de token, usuário e senha e revisar logs que incluam dados médicos, URLs de imagem ou - caminhos locais. -- Revisar backup/data extraction e excluir token, banco/arquivos sensíveis conforme decisão de produto. -- Revisar permissões e remover as não usadas/duplicadas; documentar justificativa para full-screen - intent e comportamento na tela bloqueada. -- Restringir/validar o host do scan, evitando chamadas arbitrárias via `@Url`. -- Adicionar scanner de secrets ao processo de qualidade e conferir que `local.properties`, - keystores e arquivos de credenciais estejam ignorados. - -**Critérios de aceite/verificação** - -- Build de release sem as duas URLs falha com mensagem clara antes da compilação. -- Build de release rejeita HTTP e não contém os IPs antigos ao inspecionar APK/string resources. -- Debug configurado acessa mocks/ambiente local; release usa somente valores injetados. -- OkHttp não registra body nem `Authorization` em release. -- Busca por tokens/senhas/IPs antigos não encontra hardcodes em fontes ou artefatos. -- Android lint e testes de validação da configuração passam. -- Manifest mesclado de release não permite cleartext. - -### Etapa 2 — Fronteiras arquiteturais e domínio testável - -**Objetivo do PR** - -Corrigir dependências entre camadas sem alterar os fluxos visíveis ao usuário. - -**Tarefas detalhadas** - -- Registrar uma ADR com a arquitetura alvo: MVVM, UDF, camadas `ui`, `domain` e `data`, regras de - dependência e estratégia de erros. -- Mover `CameraService`, `DetectionService` e `ScanUpload` para pacotes de infraestrutura adequados - (`data/camera`, `data/worker` ou equivalente); o domínio não deve importar Android. -- Criar interfaces no domínio para autenticação, medicamentos, scan, sessão, câmera/arquivo, - agendamento e conectividade quando forem consumidas por ViewModels/use cases. -- Injetar DAOs diretamente nos repositories em vez de expor `AppDatabase`. -- Extrair casos de uso para login/sincronização, confirmação de dose, scan e fila offline. -- Extrair relógio/gerador de datas e dispatchers para abstrações injetáveis. -- Substituir exceções genéricas e mensagens vindas de infraestrutura por erros tipados/resultados de - domínio; mapear mensagens amigáveis apenas na camada de UI. -- Remover dependência direta de `Context`, `Uri`, WorkManager, Retrofit e entidades Room das regras de - negócio. -- Manter o projeto em módulo único neste PR; preparar packages/fronteiras antes de decidir módulos. - -**Critérios de aceite/verificação** - -- Uma verificação de dependência (teste arquitetural ou detekt rule) impede imports Android/Compose/ - Retrofit/Room no domínio. -- ViewModels dependem de casos de uso/interfaces, não de repositories concretos de `data`. -- Testes existentes continuam passando e novos testes cobrem os casos de uso extraídos. -- Fluxos de login, listagem, confirmação, câmera e fila offline mantêm comportamento. -- Não há mudança de schema Room nem contrato HTTP neste PR. - -### Etapa 3 — Estado de UI moderno e navegação previsível - -**Objetivo do PR** - -Adotar UDF com estado imutável e lifecycle-aware, reduzindo estados inválidos e eventos duplicados. - -**Tarefas detalhadas** - -- Migrar uma tela por vez de múltiplos LiveData para um único `StateFlow` imutável. -- Modelar loading, conteúdo, vazio e erro como estados explícitos; modelar ações como eventos/intents. -- Coletar flows no Compose com `collectAsStateWithLifecycle`. -- Tratar navegação, snackbar e diálogo como eventos consumíveis, evitando booleans que reaparecem - após recomposição/restauração. -- Remover `postValue` quando o estado já estiver no Main dispatcher. -- Evitar que `CameraViewModel` receba `PreviewView` ou `LifecycleOwner`; manter binding CameraX em - adaptador/controlador lifecycle-aware na UI. -- Introduzir rotas tipadas/argumentos serializáveis de forma segura e centralizar chaves de Intent. -- Salvar apenas estado necessário com `SavedStateHandle`; não transportar payload médico grande via - JSON em Intent quando uma chave/ID persistida for suficiente. -- Separar composables stateful/stateless para previews e testes. -- Revisar acessibilidade: content descriptions, touch targets, contraste, font scaling e semântica. - -**Critérios de aceite/verificação** - -- Cada ViewModel migrada expõe apenas estado público imutável. -- Rotação/recriação não dispara novamente confirmação, navegação ou mensagens. -- Testes de ViewModel cobrem transições de estado, concorrência e erros. -- Testes Compose validam pelo menos login, lista vazia/erro e confirmação. -- Navegação por notificação/deep link possui teste de happy path e argumento inválido. - -### Etapa 4 — Camada de dados, contrato de APIs e sessão - -**Objetivo do PR** - -Adaptar com segurança o aplicativo aos novos backends, tornar rede/cache testáveis e proteger sessão. - -**Tarefas detalhadas** - -- Congelar os novos contratos de backend e IA com exemplos versionados e matriz de compatibilidade. -- Revisar paths, multipart, nomes de campos, autenticação, timeouts e códigos de erro. -- Eliminar a tentativa sequencial de multipart `"file"`, `"image"` e `"photo"` depois que o contrato - oficial estiver definido. -- Criar interceptador de autenticação e política uniforme de erro/expiração de sessão. -- Avaliar Moshi ou Kotlin Serialization em PR separado; se Gson permanecer, centralizar adapters e - testes de compatibilidade. Não combinar troca de serializer com mudança de API. -- Implementar repositories usando remote/local data sources explícitos. -- Definir source of truth local e estratégia de cache/sincronização por agregado. -- Proteger token usando armazenamento apoiado por Android Keystore ou solução oficialmente suportada, - com migração segura do valor existente e política de logout/expiração. -- Aplicar transações Room onde persistências relacionadas precisem ser atômicas. -- Padronizar DTO -> domain -> entity, nullability, datas/horários e timezone. -- Criar testes com MockWebServer para sucesso, 4xx, 5xx, timeout, payload inválido e expiração de token. -- Não usar APIs reais em testes automatizados. - -**Critérios de aceite/verificação** - -- Contract tests passam contra fixtures aprovadas dos novos backends. -- Nenhum teste depende de rede externa. -- Token antigo é migrado sem logout inesperado; token não aparece em logs, backup ou texto do APK. -- Erros HTTP são convertidos em erros de domínio previsíveis. -- Cache permanece consistente após falha parcial de rede. -- Configurações de backend e IA continuam injetáveis por ambiente. - -### Etapa 5 — Room, offline-first, WorkManager e notificações - -**Objetivo do PR** - -Garantir confiabilidade de dados, processamento offline idempotente e notificações compatíveis com a -plataforma. - -**Tarefas detalhadas** - -- Adicionar `room-testing` e testes instrumentados para migrations suportadas até a versão 9. -- Verificar por que faltam migrations `4_5` e `5_6`; adicionar caminhos válidos ou declarar - formalmente a menor versão atualizável. -- Revisar schemas versionados e exigir diff/revisão a cada alteração. -- Substituir status string da fila por tipo fechado persistido com conversor e estados explícitos. -- Definir idempotency key para scan/confirmacão, unique work, backoff exponencial, máximo de tentativas - e política para erro permanente. -- Usar Hilt Worker (`@HiltWorker`) em vez de EntryPoint manual, se compatível com a stack validada. -- Rever `UNMETERED` versus `CONNECTED` conforme custo/tamanho e requisito de produto. -- Definir retenção e limpeza de imagens temporárias; verificar o resultado de exclusão. -- Proteger o receiver assíncrono com timeout e mover trabalho durável para WorkManager. -- Revisar agendamentos após reboot, mudança de timezone/horário e atualização do app. -- Adequar full-screen intents às regras atuais da plataforma e fornecer fallback para notificação - comum quando a permissão/capacidade não estiver disponível. -- Testar canais, permissão de notificação e deep links sem expor conteúdo sensível na lock screen. - -**Critérios de aceite/verificação** - -- Testes de migration abrem snapshots das versões suportadas sem perda de dados. -- O mesmo job executado mais de uma vez não duplica confirmação, upload ou notificação. -- Erros 4xx permanentes não entram em retry infinito; falhas transitórias respeitam backoff. -- Arquivos temporários são removidos no sucesso e mantidos/limpos conforme política em falha. -- Testes WorkManager cobrem sucesso, retry, failure e ausência de sessão. -- Cenários offline/online, reboot e mudança de timezone passam em dispositivo/emulador. - -### Etapa 6 — Expansão da pirâmide de testes e quality gates - -**Objetivo do PR** - -Criar proteção automatizada proporcional ao risco dos fluxos e tornar a cobertura um sinal confiável. - -**Tarefas detalhadas** - -- Adotar fakes como padrão; adicionar MockK/Mockito somente onde mocks reduzam custo sem acoplar testes - à implementação. -- Cobrir casos de uso, ViewModels, repositories, mappers, matching, datas e validações de configuração. -- Adicionar MockWebServer, Room Testing, WorkManager Testing e Hilt Testing conforme necessário. -- Criar testes Compose para fluxos críticos, estados e acessibilidade. -- Criar poucos testes end-to-end instrumentados: login simulado, listagem, scan simulado, - confirmação e fila offline. -- Isolar câmera/ML Kit atrás de adapters; testar contrato com imagens fixture pequenas e testes de - dispositivo separados. -- Reconfigurar Kover para medir código relevante; excluir apenas gerados/boilerplate com justificativa. -- Definir meta incremental baseada no baseline da Etapa 0, com mínimo por PR e aumento gradual nos - pacotes críticos, evitando uma meta global cosmética. -- Publicar JUnit XML, lint, detekt e cobertura como artifacts de CI. -- Tratar testes flaky: seed/clock/dispatcher determinísticos, retries somente para diagnóstico e - ownership documentado. - -**Critérios de aceite/verificação** - -- Unit tests e instrumented tests passam em ambiente limpo. -- Fluxos P0/P1 têm cobertura de sucesso, erro e cancelamento. -- Kover falha quando a meta acordada é reduzida. -- Exclusões de cobertura não incluem repositories/ViewModels inteiros sem ADR. -- Testes Compose usam semântica, não delays arbitrários. -- Relatórios permitem localizar facilmente teste/linha que falhou. - -### Etapa 7 — Release hardening, desempenho e observabilidade - -**Objetivo do PR** - -Produzir um APK de release seguro, enxuto, diagnosticável e próximo das condições reais de produção. - -**Tarefas detalhadas** - -- Ativar R8/minificação e resource shrinking em release; adicionar keep rules mínimas e testadas. -- Configurar assinatura somente via variáveis/arquivos temporários no CI; nunca versionar keystore, - alias ou senhas. -- Definir versionamento semântico por tag e derivação determinística de `versionCode`/`versionName`. -- Adicionar Baseline Profiles/Macrobenchmark para startup e fluxos críticos, se métricas justificarem. -- Medir startup, tamanho do APK, memória e impacto de CameraX/ML Kit; criar budgets. -- Revisar dependências e recursos não usados, incluindo módulos CameraX potencialmente redundantes. -- Adicionar observabilidade com redaction e consentimento: crashes e métricas técnicas, sem tokens, - imagens, nomes de medicamentos ou outros dados de saúde. -- Gerar SBOM/lista de dependências e executar análise de vulnerabilidades/licenças. -- Realizar smoke test do APK minificado em API mínima e target, incluindo login simulado, banco, - notificações, câmera e worker. - -**Critérios de aceite/verificação** - -- `assembleRelease` gera APK assinado quando secrets estão presentes e falha claramente quando faltam. -- APK minificado instala e completa smoke tests nas APIs definidas. -- Nenhum secret aparece em Gradle logs, artifacts ou APK. -- Tamanho/startup respeitam os budgets registrados. -- Crash reporting de teste funciona e payload foi inspecionado quanto a dados sensíveis. -- SBOM e relatório de dependências são artifacts do pipeline. - -### Etapa 8 — CI/CD, governança e documentação final - -**Objetivo do PR** - -Fechar a modernização com gates obrigatórios em PR e entrega automatizada de APK em GitHub Release. - -**Tarefas detalhadas** - -- Criar workflow de CI para `pull_request` com: - - checkout e validação do Gradle Wrapper; - - JDK 21 e cache Gradle seguro; - - ktlint/format check e detekt; - - Android lint; - - testes unitários e gate Kover; - - `assembleDebug`; - - testes instrumentados em emulator runner, em job separado; - - upload de relatórios mesmo em falha. -- Aplicar `concurrency` para cancelar execuções antigas da mesma branch, timeouts e permissões mínimas. -- Fixar actions por SHA ou política equivalente e habilitar atualização automatizada revisada. -- Criar workflow de release acionado por tag validada (por exemplo `vX.Y.Z`) ou publicação de Release: - - validar correspondência entre tag e versão; - - receber `MEDTRACK_API_BASE_URL` e `MEDTRACK_SCAN_URL` via GitHub Environment variables/secrets; - - materializar keystore temporariamente a partir de secret base64; - - fornecer alias e senhas por secrets mascarados; - - executar quality gates e `assembleRelease`; - - localizar e renomear deterministicamente o APK; - - gerar checksum SHA-256 e, se adotado, SBOM; - - anexar APK e checksum diretamente aos assets do GitHub Release; - - nunca publicar `local.properties`, keystore ou outputs intermediários sensíveis; - - limpar credenciais temporárias ao final. -- Usar GitHub Environment `production` com aprovação manual e secrets próprios. Endpoints não devem - ser reutilizados de ambientes de PR. -- Opcionalmente gerar AAB em paralelo para futura Play Store, sem substituir o requisito de anexar APK. -- Criar template de PR com escopo, evidências, riscos, screenshots, testes e checklist de privacidade. -- Criar templates de bug/feature/security e `CODEOWNERS`. -- Configurar proteção da `main`: PR obrigatório, aprovações, conversas resolvidas, branch atualizada, - status checks obrigatórios, bloqueio de force push/delete e revisão de CODEOWNERS. -- Habilitar Dependabot/Renovate com PRs agrupados por ecossistema e execução de toda a CI. -- Atualizar `README.md` com pré-requisitos, setup, variáveis sem valores reais, build, testes, - arquitetura, troubleshooting e processo de release. -- Atualizar documentação/ADRs para refletir a implementação final e adicionar runbook de rollback/ - revogação de release. - -**Critérios de aceite/verificação** - -- Um PR de teste executa todos os checks e não pode ser mesclado quando um deles falha. -- Um commit sem formatação, teste quebrado ou queda de cobertura é bloqueado. -- Testes instrumentados executam em emulador reproduzível. -- Uma tag de homologação produz APK assinado, checksum e artifacts no GitHub Release. -- O APK publicado contém exatamente os endpoints fornecidos ao job e rejeita os IPs antigos. -- Logs do workflow não exibem endpoints classificados como secrets, senhas, keystore ou token. -- Download, checksum, instalação e smoke test do APK anexado são validados. -- Branch protection, templates e CODEOWNERS estão ativos e documentados. -- Uma pessoa nova consegue clonar, configurar, executar testes e gerar debug seguindo apenas o README. - -## 4. Gates obrigatórios entre etapas - -Todo PR deve: - -- ter escopo único e referência à etapa; -- estar atualizado com a `main`; -- incluir testes para comportamento novo ou alterado; -- executar format check, detekt, lint, testes e build aplicáveis; -- não reduzir cobertura dos pacotes tocados sem justificativa aprovada; -- incluir evidência de teste manual quando envolver câmera, notificação, migração ou background; -- não conter secrets, endpoints antigos, credenciais ou dados reais de pacientes; -- atualizar ADR/documentação quando mudar decisão arquitetural ou operacional; -- explicitar plano de rollback quando houver migration, autenticação ou mudança de release. - -Não combinar no mesmo PR: - -- upgrade amplo de dependências e refatoração funcional; -- mudança de serializer e mudança de contrato HTTP; -- migration Room e refatoração de repositories; -- ativação de R8 e grandes mudanças de navegação/UI; -- criação do pipeline e correções extensas feitas apenas para fazê-lo passar. - -## 5. Resultado esperado - -Ao final da Etapa 8, o MedTrack Mobile deverá possuir: - -- arquitetura MVVM/UDF com domínio independente de Android e fronteiras testáveis; -- estado Compose lifecycle-aware com StateFlow; -- integração configurável com os novos serviços de backend e IA; -- release exclusivamente HTTPS, sem logs/armazenamento indevido de credenciais; -- cache, migrations, fila offline e notificações cobertos por testes; -- quality gates locais e em CI; -- APK de produção assinado, versionado e anexado automaticamente ao GitHub Release em cada tag; -- governança da `main`, templates, ownership e documentação suficientes para manutenção contínua. diff --git a/config/release/budgets.properties b/config/release/budgets.properties index fd2221a..333014e 100644 --- a/config/release/budgets.properties +++ b/config/release/budgets.properties @@ -1,4 +1,4 @@ -# Limites iniciais da Etapa 7. Ajustes exigem evidência, justificativa e revisão no PR. +# Limites de release. Ajustes exigem evidência, justificativa e revisão no PR. maxApkSizeBytes=31457280 maxColdStartupMs=2500 maxWarmStartupMs=1200 diff --git a/docs/architecture/adr/0001-layer-boundaries.md b/docs/architecture/adr/0001-layer-boundaries.md index 643cbb0..d135f61 100644 --- a/docs/architecture/adr/0001-layer-boundaries.md +++ b/docs/architecture/adr/0001-layer-boundaries.md @@ -2,7 +2,6 @@ - Status: aceita - Data: 2026-08-01 -- Etapa: 2 — Fronteiras arquiteturais e domínio testável ## Contexto @@ -14,8 +13,7 @@ negócio e mensagens de UI. ## Decisão -O projeto continuará em módulo único nesta etapa e adotará MVVM com fluxo unidirecional de dados -(UDF) como arquitetura alvo. A migração de estado da UI ocorrerá incrementalmente na Etapa 3. +O projeto permanece em módulo único e adota MVVM com fluxo unidirecional de dados (UDF). As dependências seguem esta direção: @@ -26,7 +24,7 @@ ui -> domain <- data ``` - `domain` contém modelos, erros tipados, contratos, relógio, dispatchers e casos de uso puros. -- `data` implementa contratos e concentra Android, CameraX, ML Kit, Room, Retrofit, OkHttp, +- `data` implementa contratos e concentra Android, CameraX, Room, Retrofit, OkHttp, WorkManager, persistência de sessão e conversões DTO/entity. - `ui` depende de casos de uso e modelos do domínio. Integrações estritamente visuais ou de lifecycle podem usar adaptadores Android próprios da UI até serem isoladas completamente. @@ -53,8 +51,10 @@ não devem revelar dados técnicos ou sensíveis. ## Consequências O domínio pode ser testado no JVM sem Android e as integrações ficam substituíveis por fakes. Há -mais tipos e bindings no Hilt, mas o acoplamento passa a ser explícito. A retirada de `PreviewView` e -`LifecycleOwner` do fluxo da câmera será concluída na Etapa 3, quando a UI for migrada para UDF. +mais tipos e bindings no Hilt, mas o acoplamento passa a ser explícito. `PreviewView` e +`LifecycleOwner` permanecem contidos no adaptador de câmera da UI, sem atravessar a fronteira do +domínio. O ML Kit, presente no contexto original desta decisão, foi removido quando o scan passou a +ser responsabilidade do serviço remoto. ## Rollback diff --git a/docs/architecture/data-layer.md b/docs/architecture/data-layer.md index a8ed04f..9dc5c94 100644 --- a/docs/architecture/data-layer.md +++ b/docs/architecture/data-layer.md @@ -48,7 +48,7 @@ em uma transacao Room e o cache local e a source of truth apresentada pela aplic ## Persistencia local O projeto usa Room com `AppDatabase`, atualmente na versao `10`. O snapshot correspondente fica em -`app/schemas` para permitir revisao de schema e futuros testes de migration. +`app/schemas` para permitir revisão de schema e testes de migration contra versões preservadas. Entities registradas: diff --git a/docs/architecture/sync-strategy.md b/docs/architecture/sync-strategy.md index fa885a4..f9ac51b 100644 --- a/docs/architecture/sync-strategy.md +++ b/docs/architecture/sync-strategy.md @@ -24,8 +24,9 @@ Ao confirmar um medicamento: 3. Envia a confirmacao para a API. 4. Persiste ou atualiza `ConfirmacaoEntity` como sincronizada em transacao. -Uma falha remota nao cria confirmacao local incorretamente marcada como concluida. O fluxo -offline-first de confirmacoes duraveis permanece planejado para a Etapa 5. +Uma falha remota nao cria confirmacao local incorretamente marcada como concluida. Confirmações +duráveis offline ainda não estão implementadas e permanecem como limitação conhecida para um PR +funcional próprio. ### Scans offline diff --git a/docs/context/conventions.md b/docs/context/conventions.md index db25b36..b5a1deb 100644 --- a/docs/context/conventions.md +++ b/docs/context/conventions.md @@ -45,9 +45,10 @@ ## Testes - Toda nova implementacao mobile deve incluir testes relacionados quando houver regra, mapper, ViewModel, Repository ou persistencia alterada. -- O comando minimo antes de abrir PR e `./gradlew test`. +- O gate mínimo antes de abrir PR é `./gradlew qualityCheck`. - A metrica de cobertura dos testes unitarios deve ser gerada com Kover usando `./gradlew :app:koverHtmlReportDebug :app:koverXmlReportDebug` quando solicitada. -- O relatorio Kover atual mede o escopo unitario: dominio, mappers, utilitarios puros e ViewModels testaveis com fakes. UI Compose, Room/DAO, Camera, notificacoes, Hilt e repositories concretos ficam fora dessa metrica ate a etapa de testes instrumentados/integracao. +- O relatório Kover mede testes JVM; a cobertura instrumentada de UI Compose, Room/DAO, câmera, + notificações e integrações Android é validada separadamente e não entra nesse percentual. - Testes unitarios ficam em `app/src/test/` e nao devem depender de backend, internet ou Android Framework. - Testes instrumentados ficam em `app/src/androidTest/` e devem ser usados para Room, DAO, migrations e comportamentos que dependem do Android. diff --git a/docs/context/stack.md b/docs/context/stack.md index ff34500..0345c5b 100644 --- a/docs/context/stack.md +++ b/docs/context/stack.md @@ -23,7 +23,8 @@ - Room - SQLite -- SharedPreferences privado para token JWT, excluido de backup; migracao para Keystore planejada +- SharedPreferences privado com JWT cifrado por chave não exportável do Android Keystore e excluído + de backup - Gson ## Rede diff --git a/docs/context/technical-context.md b/docs/context/technical-context.md index 1d2a625..888bca4 100644 --- a/docs/context/technical-context.md +++ b/docs/context/technical-context.md @@ -41,7 +41,7 @@ O projeto ja possui separacao em `data`, `di`, `domain`, `ui` e `utils`. Alguns pontos ainda sao pragmaticos e podem evoluir: -- DI esta em `data/di`, embora seja transversal. +- DI possui o pacote transversal `di`, usado como composition root. - `StateFlow` imutavel e intents formam o padrao de estado dos fluxos principais. - Adapters de infraestrutura Android concentram WorkManager e CameraX fora do domínio. - A estrategia offline existe para scan e deve ser expandida para confirmacoes. diff --git a/docs/contracts/api-v1.md b/docs/contracts/api-v1.md index 9926707..74232b2 100644 --- a/docs/contracts/api-v1.md +++ b/docs/contracts/api-v1.md @@ -1,6 +1,6 @@ # Contratos HTTP consumidos pelo aplicativo -Este documento congela o contrato esperado pelo aplicativo na Etapa 4. Alteracoes incompatíveis no +Este documento congela o contrato HTTP v1 esperado pelo aplicativo. Alteracoes incompatíveis no backend ou no servico de IA exigem nova versao deste documento, fixtures atualizadas e contract tests. | Operacao | Metodo e path | Autenticacao | Corpo/resposta | Compatibilidade | @@ -18,7 +18,7 @@ backend ou no servico de IA exigem nova versao deste documento, fixtures atualiz distintos. Mensagens de infraestrutura nao chegam diretamente a UI. - O cliente adiciona `Authorization: Bearer ` centralmente, exceto no login. - O host do scan continua validado e injetado por ambiente; nenhuma fixture executa chamadas externas. -- Gson permanece como serializer nesta etapa. Nomes divergentes, como `agente_ativo`, usam +- Gson é o serializer atual. Nomes divergentes, como `agente_ativo`, usam `@SerializedName` e nao contaminam modelos de dominio. Fixtures aprovadas ficam em `app/src/test/resources/contracts/v1`. diff --git a/docs/release/release-hardening.md b/docs/release/release-hardening.md index edcb404..a9b08d0 100644 --- a/docs/release/release-hardening.md +++ b/docs/release/release-hardening.md @@ -38,7 +38,7 @@ startup e login/listagem/scan; adicionar o módulo somente se o ganho ou uma reg ## Observabilidade e privacidade -Nenhum SDK externo de crash/analytics foi ativado nesta etapa. O aplicativo trata dados de saúde e +Nenhum SDK externo de crash/analytics está ativado. O aplicativo trata dados de saúde e ainda não há decisão de fornecedor, consentimento, retenção, residência dos dados nem configuração de produção. Ativar coleta antes dessas definições contrariaria minimização e privacy by default. diff --git a/docs/security/environment-and-network.md b/docs/security/environment-and-network.md index ff6e84b..66c9e48 100644 --- a/docs/security/environment-and-network.md +++ b/docs/security/environment-and-network.md @@ -70,8 +70,9 @@ não usa essas capacidades. Permanecem: - `USE_FULL_SCREEN_INTENT`, usada pelo lembrete de dose categorizado como alarme. O conteúdo das notificações foi marcado como privado na tela bloqueada. Full-screen intent, -`showWhenLocked` e `turnScreenOn` permanecem por fazerem parte do fluxo de lembrete crítico, mas -devem ser reavaliados na Etapa 5 frente às políticas da plataforma e à decisão de produto. +`showWhenLocked` e `turnScreenOn` permanecem por fazerem parte do fluxo de lembrete crítico. Antes +de testes públicos e de cada release, esse comportamento deve ser validado contra as políticas da +plataforma e a decisão de produto. ## Verificação diff --git a/docs/setup/quality-baseline.md b/docs/setup/quality-baseline.md index 2039965..32678ec 100644 --- a/docs/setup/quality-baseline.md +++ b/docs/setup/quality-baseline.md @@ -1,4 +1,8 @@ -# Baseline de qualidade +# Estado de qualidade + +Este documento registra os gates e a referência comparável do projeto. Ele deve refletir o estado +atual da `main`; resultados históricos que não representam mais o código devem permanecer no +histórico Git, não como instrução operacional. ## Toolchain @@ -12,7 +16,7 @@ - Kover: 0.9.8 As versões são centralizadas em `gradle/libs.versions.toml`. Upgrades devem ser feitos em PRs -focados, após consulta à matriz oficial de compatibilidade. +focados, após consulta à matriz oficial de compatibilidade e com rollback definido. ## Gates locais @@ -24,40 +28,59 @@ A tarefa agregadora é: Ela executa: +- verificação de segredos; - `:app:ktlintCheck`; - `:app:detekt`; - `:app:lintDebug`; - `:app:testDebugUnitTest`; -- `:app:koverVerifyDebug`; -- `:app:koverXmlReportDebug`; -- `:app:koverHtmlReportDebug`. +- verificação e geração dos relatórios Kover. + +O Detekt opera sem baseline de supressões: todo achado deve ser corrigido ou, quando a regra não se +aplicar legitimamente ao código, suprimido no menor escopo possível com justificativa revisável. +Para corrigir somente formatação, execute `./gradlew :app:ktlintFormat`. -O build do APK debug permanece explícito: +O build e os testes instrumentados permanecem gates explícitos: ```bash ./gradlew assembleDebug +./gradlew connectedDebugAndroidTest ``` -Para corrigir somente formatação: +Os resultados ficam em `app/build/test-results/`, `app/build/reports/tests/`, +`app/build/reports/kover/`, `app/build/reports/lint-results-debug.html` e +`app/build/reports/detekt/`. APKs ficam em `app/build/outputs/apk/`. -```bash -./gradlew :app:ktlintFormat -``` +## Referência comparável — 03/08/2026 + +Com 78 testes JVM, o relatório sem exclusões funcionais registrou **761 de 3.606 linhas**, ou +**21,1%**. As variantes Kover usadas pelos gates mediram: + +| Escopo | Cobertura | Gate | +|---|---:|---:| +| Aplicação | 21,1% | 20% | +| Casos de uso | 75,9% | 75% | +| Repositories | 68,3% | 65% | +| ViewModels | 51,7% | 50% | -Para revisar a dívida estática existente, consulte `config/detekt/baseline.xml`. O baseline não -deve crescer sem justificativa no Pull Request. A Etapa 6 elevou o gate global do Kover para -**20%** e adicionou variantes para pacotes críticos: casos de uso **75%**, repositories **65%** e -ViewModels **50%**. As metas devem subir gradualmente, sem ampliar exclusões para obter um -percentual artificial. +Reduzir qualquer gate, ampliar exclusões ou adicionar uma supressão exige justificativa e aprovação +no PR. As metas devem subir gradualmente conforme novos fluxos sejam cobertos. -Os resultados ficam em `app/build/test-results/testDebugUnitTest/` (JUnit XML), -`app/build/reports/tests/testDebugUnitTest/` (JUnit HTML), `app/build/reports/kover/` (XML e HTML), -`app/build/reports/lint-results-debug.html` e `app/build/reports/detekt/`. +## Pontos de atenção conhecidos -## Validação manual desta etapa +- `android.disallowKotlinSourceSets=false` é experimental. Sua remoção deve ser validada em PR + específico para evitar mudança não intencional no build. +- O Gradle reporta APIs deprecadas antes da versão 10. Use `--warning-mode all` para identificar a + origem e trate a compatibilidade em upgrades focados. +- Recursos não usados, densidades de bitmaps, ícone monocromático e qualificadores redundantes devem + ser auditados visualmente antes da remoção. +- Mensagens sobre Kotlin daemons órfãos são operacionais; se persistirem, encerre os daemons e + confirme que não alteram o artefato. -Por decisão operacional, os comandos Gradle finais serão executados manualmente pelo responsável do -repositório. Antes de abrir o Pull Request: +O ML Kit e dependências CameraX diretas sem uso já foram removidos. Warnings antigos referentes às +bibliotecas nativas dessas dependências não representam o APK atual e devem ser investigados de novo +somente se reaparecerem em um build limpo. + +## Verificação antes do Pull Request ```bash ./gradlew qualityCheck @@ -65,96 +88,6 @@ repositório. Antes de abrir o Pull Request: git diff --check ``` -Também confirme: - -- `./gradlew --version` usa JDK 21; -- o Manifest mesclado não contém permissões duplicadas; -- os relatórios existem em `app/build/reports`; -- os testes JUnit possuem XML em `app/build/test-results`; -- o APK debug existe em `app/build/outputs/apk/debug`; -- somente arquivos gerados ignorados aparecem após o build. - -## Baseline comparável da Etapa 6 — 03/08/2026 - -Após a expansão para 78 testes JVM, o relatório sem exclusões funcionais registrou **761 de 3.606 -linhas**, ou **21,1%**. As variantes Kover, que são a fonte dos gates, mediram: - -| Escopo | Cobertura | Gate | -|---|---:|---:| -| Aplicação | 21,1% | 20% | -| Casos de uso | 75,9% | 75% | -| Repositories | 68,3% | 65% | -| ViewModels | 51,7% | 50% | - -Reduzir qualquer gate exige justificativa e aprovação no PR. Repositories ou ViewModels não podem -ser excluídos integralmente para recuperar percentual. - -## Observações do baseline - -- O namespace/application ID foi renomeado para `com.medtrack.mobile`; a regra `package-name` do - ktlint voltou a ser aplicada. -- Composables usam PascalCase e o singleton Room usa `INSTANCE`, seguindo convenções Android; as - regras de nomenclatura conflitantes do ktlint estão desabilitadas. -- O detekt usa baseline apenas para problemas preexistentes. Código novo continua sujeito à - configuração de `config/detekt/detekt.yml`. -- A propriedade `android.disallowKotlinSourceSets=false` é experimental e ainda produz warning. - Sua remoção deve ser validada em PR específico para evitar mudança não intencional no build. -- O Gradle 9.4.1 reporta uso de APIs deprecadas antes do Gradle 10. Execute com - `--warning-mode all` e trate cada origem em PR focado. - -## Resultado registrado em 29/07/2026 - -O primeiro `qualityCheck` e `assembleDebug` concluíram com sucesso: - -- testes unitários: **32 executados, 0 falhas, 0 erros e 0 ignorados**; -- detekt: **0 novos achados** após aplicação do baseline; -- ktlint: **0 violações**; -- Android lint: **0 erros e 46 warnings**; -- APK debug: gerado com **73.213.272 bytes**; -- Kover antes da correção das exclusões: 327 de 332 linhas, ou **98,49%**. - -O percentual de 98,49% não deve ser usado como referência: naquele relatório, repositories, -ViewModels, UI, workers, navegação e outras áreas relevantes estavam excluídos. As exclusões foram -reduzidas para manter apenas código gerado/boilerplate de Android, Hilt e Dagger. Após essa correção, -execute novamente `./gradlew qualityCheck`; o novo percentual será o primeiro baseline comparável. - -Após a remoção dos achados resolvidos por essa renomeação, o baseline do detekt contém **51 -ocorrências preexistentes**: - -| Regra | Ocorrências | -|---|---:| -| `FunctionNaming` | 25 | -| `LongMethod` | 11 | -| `TooGenericExceptionCaught` | 8 | -| `ReturnCount` | 2 | -| `ComplexCondition` | 1 | -| `ConstructorParameterNaming` | 1 | -| `MaxLineLength` | 1 | -| `PrintStackTrace` | 1 | -| `SwallowedException` | 1 | - -As 93 ocorrências de `PackageNaming` associadas ao namespace legado foram removidas do baseline. -Nomenclatura de Composables explica parte relevante de `FunctionNaming`. Os achados restantes devem -ser reduzidos por refatorações focadas; regenerar o baseline sem revisar o diff não é permitido. - -### Classificação dos warnings - -| Origem | Quantidade/estado | Decisão | -|---|---:|---| -| Versões disponíveis | 15 ocorrências | Não atualizar em massa na Etapa 0; criar PRs focados com matriz de compatibilidade. | -| Recursos não usados | 18 | Auditar visualmente antes de remover; alguns podem ser assets reservados. | -| Bitmaps em `drawable` | 10 | Migrar para densidades adequadas ou `drawable-nodpi` em PR de assets. | -| Ícone monocromático ausente | 2 | Criar asset aprovado pelo design antes de alterar adaptive icons. | -| Qualificador `mipmap-anydpi-v26` redundante | 1 | Consolidar junto ao PR de launcher icons. | -| `android.disallowKotlinSourceSets=false` experimental | 1 | Mantido até teste específico sem a flag. | -| API Gradle deprecada | 1 | Originada pelo plugin detekt 1.23.8; atualizar quando uma versão compatível remover o uso. | -| Bibliotecas nativas sem strip | informativo | Esperado para binários prebuilt no debug; reavaliar no release hardening. | -| Múltiplos Kotlin daemons | informativo | Encerrar daemons órfãos se persistir; não afeta o artefato produzido. | - -Os warnings de bibliotecas nativas (`libandroidx.graphics.path.so`, -`libimage_processing_util_jni.so`, `libmlkitcommonpipeline.so` e `libsurface_util_jni.so`) indicam -que o AGP empacotou binários prebuilt sem remover símbolos. Como o artefato validado é debug, isso -não bloqueia a Etapa 0. Tamanho e stripping do release pertencem à etapa de release hardening. - -Na Etapa 7, ML Kit e os módulos CameraX sem uso foram removidos. Os nomes acima permanecem neste -documento somente como registro histórico do baseline; o APK release deve ser medido novamente. +Quando o escopo tocar câmera, banco, notificações, background ou navegação, execute também os grupos +instrumentados relevantes, conforme `docs/testing/test-strategy.md`. Confirme que relatórios e APKs +foram gerados nos caminhos acima e que somente arquivos ignorados surgiram após o build. From c20ec5ad6e36ca999ecb41c07f605e949572710c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yann=20Le=C3=A3o?= Date: Tue, 4 Aug 2026 11:07:17 -0300 Subject: [PATCH 6/8] build(quality): remove obsolete detekt baseline --- app/build.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 925ec62..cfcad7b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -293,7 +293,6 @@ detekt { buildUponDefaultConfig = true allRules = false config.setFrom(rootProject.files("config/detekt/detekt.yml")) - baseline = rootProject.file("config/detekt/baseline.xml") } tasks.withType().configureEach { From 3400fc1964bf71fe1aad80758abb8aad63c3d259 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yann=20Le=C3=A3o?= Date: Tue, 4 Aug 2026 11:07:25 -0300 Subject: [PATCH 7/8] docs(governance): add community standards and MIT license --- CODE_OF_CONDUCT.md | 57 ++++++++++++++++++++++++++++++++++++++++++++++ CONTRIBUTING.md | 4 +++- LICENSE | 21 +++++++++++++++++ README.md | 18 ++++++++------- 4 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 CODE_OF_CONDUCT.md create mode 100644 LICENSE diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..2bb5a74 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,57 @@ +# Código de Conduta + +## Nosso compromisso + +Como integrantes, contribuidores e responsáveis pelo MedTrack, comprometemo-nos a tornar a +participação no projeto uma experiência livre de assédio, independentemente de idade, aparência, +deficiência, etnia, identidade ou expressão de gênero, nível de experiência, nacionalidade, +religião, orientação sexual ou condição socioeconômica. + +Agiremos de forma a contribuir para uma comunidade aberta, acolhedora, diversa, inclusiva e +saudável. + +## Comportamento esperado + +Exemplos de comportamento que contribuem para um ambiente positivo incluem: + +- demonstrar empatia e respeito por diferentes opiniões e experiências; +- oferecer e aceitar feedback técnico de maneira construtiva; +- assumir responsabilidade por erros, desculpar-se e buscar corrigir seus impactos; +- priorizar o interesse do projeto e de sua comunidade; +- respeitar a privacidade e os limites de outras pessoas. + +Comportamentos inaceitáveis incluem: + +- linguagem ou imagens sexualizadas e atenção sexual indesejada; +- ataques pessoais, insultos, comentários depreciativos ou assédio público ou privado; +- divulgação de informações privadas sem permissão explícita; +- intimidação, perseguição ou interrupção deliberada de discussões; +- qualquer outra conduta que possa ser considerada inadequada em um ambiente acadêmico ou + profissional. + +## Aplicação + +Este código se aplica aos espaços do projeto e também quando alguém representa oficialmente o +MedTrack em ambientes públicos ou privados relacionados ao projeto. + +Os responsáveis pelo repositório devem esclarecer e aplicar estes padrões de forma justa e +consistente. Conteúdo ou contribuições incompatíveis podem ser editados, rejeitados ou removidos. +Dependendo da gravidade e recorrência, as medidas podem incluir orientação privada, advertência, +restrição temporária ou exclusão permanente dos espaços do projeto. + +## Reporte + +Comportamentos abusivos ou inaceitáveis devem ser reportados privadamente aos maintainers indicados +no `CODEOWNERS`, pelos canais de contato disponíveis em seus perfis do GitHub. Não exponha a pessoa +afetada nem detalhes sensíveis em issue, discussão ou Pull Request público. + +Todo relato será analisado com confidencialidade, imparcialidade e respeito à segurança de quem o +realizou. Os responsáveis devem informar as medidas cabíveis às partes afetadas quando isso puder +ser feito com segurança. + +Questões de segurança do software seguem uma política própria em [SECURITY.md](SECURITY.md). + +## Atribuição + +Este código foi adaptado do [Contributor Covenant, versão 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html), +disponível sob a licença [Creative Commons Attribution 4.0](https://creativecommons.org/licenses/by/4.0/). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f17e78f..36af321 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,7 @@ # Como contribuir +Ao participar, você concorda em seguir o [Código de Conduta](CODE_OF_CONDUCT.md). + ## Pré-requisitos - JDK 21; @@ -70,7 +72,7 @@ Se o PR alterar câmera, banco, notificações, background ou navegação, regis manuais executados. Mudanças de schema devem incluir migration, snapshot atualizado e teste de migração. -Não reduza cobertura ou amplie baselines/exclusões sem uma justificativa explícita no PR. +Não reduza cobertura, amplie exclusões ou adicione supressões sem uma justificativa explícita no PR. ## Escopo e revisão diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..374b6ac --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 MedTrack contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 65ea433..9a0d9c4 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,10 @@ Aplicativo Android nativo para acompanhamento de medicamentos, lembretes de dose foto. CameraX captura a imagem e a API de scan realiza o reconhecimento; quando não há conexão, o WorkManager mantém o envio na fila. -O projeto está em modernização e os endpoints definitivos do backend e do scan ainda não estão -disponíveis. Builds debug podem usar serviços locais. Um APK release somente pode ser publicado após -configuração e aprovação do environment `production`. +O repositório possui gates de qualidade, testes e entrega automatizada. Os endpoints definitivos do +backend e do scan ainda não estão disponíveis; builds debug podem usar serviços locais. Um APK +release somente pode ser publicado após a configuração dos serviços e a aprovação do environment +`production`. ## Stack @@ -171,9 +172,9 @@ Use `releaseReadiness` e configure todas as variáveis listadas em ## Contribuição e segurança -Pull Requests são obrigatórios. Leia [CONTRIBUTING.md](CONTRIBUTING.md), use Conventional Commits e -preencha o template do PR. Vulnerabilidades não devem ser abertas como issue pública; siga -[SECURITY.md](SECURITY.md). +Pull Requests são obrigatórios. Leia [CONTRIBUTING.md](CONTRIBUTING.md), siga o +[Código de Conduta](CODE_OF_CONDUCT.md), use Conventional Commits e preencha o template do PR. +Vulnerabilidades não devem ser abertas como issue pública; siga [SECURITY.md](SECURITY.md). CODEOWNERS: @@ -183,5 +184,6 @@ CODEOWNERS: ## Licença -Projeto acadêmico desenvolvido para a disciplina Projeto Interdisciplinar de Engenharia da -Computação 1 da Universidade Federal Rural de Pernambuco, Unidade Acadêmica de Belo Jardim. +Distribuído sob a [Licença MIT](LICENSE). Projeto acadêmico desenvolvido para a disciplina Projeto +Interdisciplinar de Engenharia da Computação 1 da Universidade Federal Rural de Pernambuco, +Unidade Acadêmica de Belo Jardim. From b09e7acbd4a7b38dfcaa3f8db0b16c09ca75afe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yann=20Le=C3=A3o?= Date: Tue, 4 Aug 2026 11:07:37 -0300 Subject: [PATCH 8/8] chore(repository): normalize versioned file attributes --- .gitattributes | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..57f67bc --- /dev/null +++ b/.gitattributes @@ -0,0 +1,28 @@ +* text=auto + +# Texto versionado usa LF para produzir diffs e artefatos consistentes entre plataformas. +*.kt text eol=lf +*.kts text eol=lf +*.gradle text eol=lf +*.xml text eol=lf +*.md text eol=lf +*.yml text eol=lf +*.yaml text eol=lf +*.toml text eol=lf +*.properties text eol=lf +*.pro text eol=lf +*.json text eol=lf +*.sh text eol=lf +gradlew text eol=lf +*.bat text eol=crlf + +# Artefatos binários não devem receber normalização de conteúdo. +*.jar binary +*.jks binary +*.keystore binary +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.webp binary +*.ttf binary