Files
flamenet-messenger-android/.gitlab-ci.yml
T
Paul HittandClaude Opus 5 d6beeafa2a Fetch the published vectors in CI - the split removed the sibling they lived in
The engine job failed on its first real run: PublishedVectorsTest and
AccountKdfTest search sibling checkouts for the canonical vector files, and the
standalone repository has no ../../engine and no flamenet-e2e clone beside it
on a runner. Both tests already honour an env override, so CI fetches both
files from flamenet-e2e at HEAD and points the tests at them.

Deliberately NOT a copied vector file in this repo: a copy drifts, and an
engine tested against its own copy has quietly stopped being tested against
the published ones. The job now fails if flamenet-e2e is unreachable, which is
the correct failure - an engine that cannot see the vectors must not report
green.

Verified by reproducing CI locally: standalone checkout, env vars only,
:engine:test passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 08:23:53 -04:00

136 lines
5.5 KiB
YAML

# flamenet-messenger-android — the Android app and its libraries.
#
# 🔴 **This repository was a hand-made extract and it drifted.** It carried only
# `:engine`, from before `:client`, `:app` and `:interop` existed, and its
# `settings.gradle.kts` still declared one module while upstream declared four.
# Nothing automated the copy, so it fell six days behind and nobody could tell
# by looking at it. It is a mirror now, pushed by CI, and this pipeline is what
# makes "it is current" checkable rather than asserted.
#
# ⚠️ **`:engine` and `:client` are plain Kotlin/JVM libraries on purpose.** The
# protocol has nothing to do with Android, so their tests run on a bare JDK
# image with no SDK and no emulator — which is why the interesting half of this
# pipeline is fast and hermetic. Only `:app` needs the Android SDK.
stages: [test, build]
default:
interruptible: true
# 🔴 **`vps`, not the shared runners.** Every job in this pipeline failed with
# `ci_quota_exceeded` on first run: GitLab.com's shared minutes are spent, and
# a public project does not get them for free on this plan. The `ovh-…` runner
# was already serving the iOS repository and is not locked to it, so it now
# serves all four.
#
# ⚠️ It is registered with `run_untagged = false`, so a job WITHOUT this tag is
# not picked up at all — it sits pending forever rather than failing, which is
# the more confusing outcome of the two. Do not remove the tag to "let it run
# anywhere".
tags: [vps]
variables:
GIT_DEPTH: "20"
GRADLE_OPTS: "-Dorg.gradle.daemon=false -Dorg.gradle.console=plain"
.rules: &rules
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == "main"
# The protocol, against the same published vectors the JS and Swift engines are
# held to. No SDK, no emulator, no device.
# 🔴 **The vectors live in flamenet-e2e, and this repo does not carry a copy.**
# That is deliberate — a copied vector file is one that drifts, and an engine
# tested against its own copy has quietly stopped being tested against the
# published ones. The test searches sibling checkouts and honours
# FLAMENET_VECTORS; in CI there are no siblings, so the pipeline fetches the
# file from the canonical repository at HEAD and points the tests at it.
#
# ⚠️ This makes the job fail if flamenet-e2e is unreachable, and that is the
# correct failure: an engine that cannot see the vectors must not report green.
engine:
<<: *rules
stage: test
image: gradle:9-jdk21
variables:
FLAMENET_VECTORS: /tmp/protocol-v1.json
FLAMENET_ACCOUNT_VECTORS: /tmp/account-kdf-v1.json
before_script:
- curl -fsSL -o "$FLAMENET_VECTORS"
"https://gitlab.com/paulhitt/flamenet-e2e/-/raw/main/test/vectors/protocol-v1.json"
- curl -fsSL -o "$FLAMENET_ACCOUNT_VECTORS"
"https://gitlab.com/paulhitt/flamenet-e2e/-/raw/main/test/vectors/account-kdf-v1.json"
script:
- gradle :engine:test
# The transport: HTTP, auth, the §8 routes. Also plain JVM.
#
# 🔴 This was never in CI, and it is the module where a silent message loss
# lived — a send to somebody with no registered device returned an empty
# failure map, which every caller reads as success. `SendToNobodyTest` pins it,
# and pinning it matters only if something runs it.
client:
<<: *rules
stage: test
image: gradle:9-jdk21
script:
- gradle :client:test
# The app's unit tests, which need the Android SDK.
#
# ⚠️ Robolectric, not an emulator. `:app` tests run on the JVM with a shadowed
# Android — that is what keeps them in CI at all. Anything needing a real device
# is an androidTest and is NOT run here; see below.
app:
<<: *rules
stage: test
image: mingc/android-build-box:latest
script:
- ./gradlew :app:testDebugUnitTest
timeout: 40 minutes
apk:
<<: *rules
stage: build
image: mingc/android-build-box:latest
script:
- ./gradlew :app:assembleDebug
artifacts:
paths: [app/build/outputs/apk/debug/app-debug.apk]
expire_in: 1 week
timeout: 40 minutes
# 🔴 **Named so its absence is visible, not omitted so it looks covered.**
#
# The instrumented suites — CrudSurfaceTest, ChatFlowTest, CrossPlatformTest —
# need a running emulator AND a reachable relay, and several are live tests that
# create real accounts. A CI runner with neither would report them as skipped,
# which is indistinguishable from passing to anybody reading the pipeline.
#
# Run them on a machine with an emulator before merging anything under
# `app/src/main`.
instrumented:
stage: test
rules: [{ when: never }]
script:
- ./gradlew :app:connectedDebugAndroidTest
# What a mirror gets wrong is missing files, not failing logic.
complete:
<<: *rules
stage: test
image: alpine:3.20
script:
- test -f settings.gradle.kts || { echo "no settings.gradle.kts"; exit 1; }
- test -f LICENSE || { echo "no LICENSE"; exit 1; }
- test -f gradlew || { echo "no gradle wrapper — this will not build on a clean clone"; exit 1; }
- test -d engine/src || { echo "no engine sources"; exit 1; }
- test -d client/src || { echo "no client sources"; exit 1; }
- test -d app/src || { echo "no app sources"; exit 1; }
# 🔴 The drift that actually happened, made into a test: the mirrored
# settings file declared one module while upstream declared four.
- for m in engine client app interop; do
grep -q "include(\":$m\")" settings.gradle.kts
|| { echo "settings.gradle.kts does not declare :$m — this mirror is a partial copy"; exit 1; };
done