diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..b4a19c2 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,62 @@ +name: CI + +# Активируется, когда на инфре B зарегистрирован self-hosted act_runner с меткой `f7-android` +# (JDK 21 + Android SDK 36 — как на билд-машине). Пока раннера нет — джобы ждут исполнителя. +# Регистрация раннера — инфра-шаг ячейки B (см. docs/CI.md). + +on: + push: + branches: [main] + tags: ['v*'] + pull_request: + +env: + JAVA_HOME: /usr/lib/jvm/java-21-openjdk-amd64 + ANDROID_HOME: /opt/android-sdk + GRADLE_OPTS: "-Dorg.gradle.daemon=false" + +jobs: + verify: + # lint + unit-тесты + debug-сборка на каждый push в main и на PR + runs-on: [self-hosted, f7-android] + if: github.ref_type != 'tag' + steps: + - uses: actions/checkout@v4 + - name: Unit-тесты + run: ./gradlew testDebugUnitTest --no-daemon + - name: Android Lint (release) + run: ./gradlew :app:lintRelease --no-daemon + - name: Debug-сборка (артефакт для QA) + run: ./gradlew :app:assembleDebug --no-daemon + - name: Публикация debug-APK + uses: actions/upload-artifact@v3 + with: + name: f7cloud-mobile-debug + path: app/build/outputs/apk/debug/*.apk + + release: + # по тегу v*: подписанный release + архив исходников (GPL corresponding source) в Gitea Release + runs-on: [self-hosted, f7-android] + if: github.ref_type == 'tag' + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Развернуть keystore из secret + run: | + echo "${{ secrets.F7_KEYSTORE_BASE64 }}" | base64 -d > "$RUNNER_TEMP/release.jks" + - name: Release-сборка (подписанная) + env: + F7_KEYSTORE_FILE: ${{ runner.temp }}/release.jks + F7_KEYSTORE_PASSWORD: ${{ secrets.F7_KEYSTORE_PASSWORD }} + F7_KEY_ALIAS: ${{ secrets.F7_KEY_ALIAS }} + F7_KEY_PASSWORD: ${{ secrets.F7_KEY_PASSWORD }} + run: ./gradlew :app:assembleRelease --no-daemon + - name: Архив исходников (GPL corresponding source) + run: bash scripts/package-source.sh "$RUNNER_TEMP/dist" + - name: Публикация Gitea Release (APK + исходники) + uses: akkuman/gitea-release-action@v1 + with: + files: | + app/build/outputs/apk/release/*.apk + ${{ runner.temp }}/dist/*-source.tar.gz diff --git a/CHANGELOG.md b/CHANGELOG.md index c1eb75e..946282e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ Формат: `ГГГГ-ММ-ДД | версия | изменение | контракты | риск` +- 2026-07-07 | v0.5.114 (122) | Этап 1 (CI + GPL-публикация): добавлен Gitea Actions workflow .gitea/workflows/ci.yml (push/PR → тесты+lint+assembleDebug; тег v* → подписанный release + архив исходников в Gitea Release) — Gitea 1.26.1 поддерживает Actions. Скрипт scripts/package-source.sh (GPL corresponding source §6: архив всего дерева вкл. vendor GPL/LICENSE/NOTICE — проверен, 6.7 МБ) закрывает п.4c, работает и вручную. docs/CI.md. НЕ активно до регистрации self-hosted раннера на инфре B (нужен Gitea-админ для токена — инфра-шаг) | процесс сборки/релиза | нет: конфиг CI, кода приложения не трогает - 2026-07-07 | v0.5.114 (122) | Этап 1 (баги данных, test-first): заведена тестовая инфраструктура (core:network testImplementation junit) + первые 12 unit-тестов. Починено: (1) ICS-парсер дат — суффикс `Z` теперь UTC (был локальным), учитывается TZID, floating→локаль; (2) parseProps сохраняет параметры свойств — несколько ATTENDEE больше НЕ схлопываются, CN/PARTSTAT/ROLE читаются (была порча участников при редактировании→PUT на сервер); (3) unescape single-pass (последовательные replace ломались на `\\n`); (4) CalDavClient.parseIcsInstant делегирует в CalendarIcs — устранён рассинхрон календарь↔задачи; (5) QR-логин: маркер-парсер вместо split('&') — пароль с `&`/`:` больше не теряется; (6) курсор пагинации почты Int→Long (обрезка Unix-времени, ломалось ~2038). Проверено: 12 тестов зелёные + assembleRelease | контракты не менялись; исправлена запись битых ATTENDEE на сервер | низкий: чинит порчу данных, покрыто тестами - 2026-07-07 | v0.5.114 (122) | Этап 0 п.5 (gradle-гигиена): org.gradle.parallel=true (ускорение сборки 20 модулей); удалены мёртвые модули core:data, core:ui (пустые заготовки) и feature:widgets (не был в settings.gradle, не компилировался) + чистка settings.gradle. nonTransitiveRClass и убирание jetifier — ПРОБОВАЛ, откатил: vendor-форк talk-android ломается (R транзитивно + legacy android.support.* в его depS); причины в комментах gradle.properties. Проверено сборкой: assembleRelease BUILD SUCCESSFUL. **Этап 0 завершён** (кроме п.4c — публикация исходников, к CI этапа 1) | контракты не менялись | низкий: удалён неиспользуемый код, ускорена сборка - 2026-07-07 | v0.5.114 (122) | Требование владельца: звонки/push ДОЛЖНЫ работать при заблокированном телефоне. (1) Биометрическая привязка app password ОТКЛОНЕНА (вариант А) — она неустранимо блокировала приём звонка без разблокировки; откачена из main, сохранена на ветке wip/biometric-binding. App password остаётся защищён шифрованием в покое (Keystore AES-256-GCM из п.3, работает в фоне → звонки/push/приём при блокировке ОК). (2) Приватность локскрина: уведомления сообщений и звонков → VISIBILITY_PRIVATE + generic public-версия («Новое сообщение»/«Входящий звонок»), канал lockscreenVisibility=PRIVATE. Полный контент (отправитель/текст/имя звонящего) — только после разблокировки. Push у приложения plaintext, пароль для показа/звонка НЕ нужен. Проверено сборкой: assembleRelease 53 МБ | контракты не менялись | низкий: только UX уведомлений; звонки/push в фоне работают как раньше diff --git a/docs/CI.md b/docs/CI.md new file mode 100644 index 0000000..17323dd --- /dev/null +++ b/docs/CI.md @@ -0,0 +1,47 @@ +# CI — Gitea Actions + +Пайплайн: [`.gitea/workflows/ci.yml`](../.gitea/workflows/ci.yml). Gitea на git.f7cloud.ru — **1.26.1** +(Actions поддерживаются). + +## Что делает + +| Триггер | Джоба | Действия | +|---|---|---| +| push в `main`, любой PR | `verify` | `testDebugUnitTest` + `:app:lintRelease` + `:app:assembleDebug` (+ артефакт debug-APK) | +| тег `v*` | `release` | подписанный `assembleRelease` + архив исходников (GPL §6) → Gitea Release | + +## Предпосылка: self-hosted раннер (инфра ячейки B) + +Проект тяжёлый (vendor talk-android + WebRTC), поэтому раннер — **self-hosted** с тёплым +Gradle-кэшем, а не контейнер (иначе WebRTC качается каждый прогон). Требования к машине раннера — +как у билд-машины: **JDK 21** (`/usr/lib/jvm/java-21-openjdk-amd64`) + **Android SDK 36** +(`/opt/android-sdk`, platform android-36, build-tools 36.0.0). + +Регистрация (нужен registration-токен из Gitea → Settings → Actions → Runners, **права Gitea-админа**): +```bash +# на машине раннера (инфра B): +act_runner register --instance https://git.f7cloud.ru --token --labels f7-android +act_runner daemon +``` +Метка `f7-android` соответствует `runs-on: [self-hosted, f7-android]` в workflow. + +## Secrets репозитория (Gitea → Settings → Actions → Secrets) + +Для джобы `release`: +- `F7_KEYSTORE_BASE64` — боевой keystore, `base64 -w0 release.jks` +- `F7_KEYSTORE_PASSWORD`, `F7_KEY_ALIAS`, `F7_KEY_PASSWORD` — см. [RELEASE-SIGNING.md](RELEASE-SIGNING.md) + +## GPL corresponding source + +`scripts/package-source.sh` собирает архив всего исходника (вкл. vendor/talk-android GPL, LICENSE, +NOTICE) — обязательство GPLv3 §6. Работает и вручную: +```bash +scripts/package-source.sh dist/ # → dist/f7cloud-mobile-v--source.tar.gz +``` +В CI прикладывается к каждому Gitea Release. До появления раннера — публиковать исходник +вручную этим скриптом к каждой распространяемой сборке. + +## Статус + +Workflow и скрипт готовы и в репо. **Не активны, пока не зарегистрирован раннер** (инфра-шаг ячейки B, +нужен Gitea-админ для токена). До активации: сборки — вручную на билд-машине, исходники — скриптом. diff --git a/scripts/package-source.sh b/scripts/package-source.sh new file mode 100755 index 0000000..dcaed06 --- /dev/null +++ b/scripts/package-source.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# GPL corresponding source: архив ВСЕГО исходного кода распространяемой сборки +# (включая vendor/talk-android — он под GPL). Обязательство GPLv3 §6: к каждой +# распространяемой сборке публиковать соответствующий исходник. +# +# Использование: scripts/package-source.sh [OUT_DIR] (по умолчанию ./dist) +set -euo pipefail + +OUT_DIR="${1:-dist}" +mkdir -p "$OUT_DIR" + +# версия из app/build.gradle (versionName/versionCode) +VNAME=$(grep -oE "versionName '[^']+'" app/build.gradle | grep -oE "[0-9][0-9.]*" | head -1) +VCODE=$(grep -oE "versionCode [0-9]+" app/build.gradle | grep -oE "[0-9]+" | head -1) +REV=$(git rev-parse --short HEAD) +NAME="f7cloud-mobile-v${VNAME:-0}-${VCODE:-0}-${REV}-source.tar.gz" + +# git archive включает всё отслеживаемое дерево (в т.ч. vendor/talk-android, LICENSE, NOTICE.md). +# Секреты в дерево не входят (google-services.json/keystore в .gitignore). +git archive --format=tar.gz --prefix="f7cloud-mobile-${VNAME}/" -o "$OUT_DIR/$NAME" HEAD + +echo "Corresponding source: $OUT_DIR/$NAME" +echo " размер: $(du -h "$OUT_DIR/$NAME" | cut -f1)" +echo " включает vendor/talk-android (GPL), LICENSE, NOTICE.md"