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>
136 lines
5.5 KiB
YAML
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
|