Add a build-and-test workflow - #470
Merged
Merged
Conversation
This repo had no CI at all. Every push and pull request against main now
builds the app and runs its JVM unit tests.
Two jobs, because they fail for different reasons.
sdk-aar builds URnetworkSdk.aar with gomobile. It is not optional: the app
has no source of its own for the SDK -- app/app/build.gradle's only
dependency on it is a fileTree over ${bringyourHomeDir}/sdk/build/android,
nothing is published to download, and over a hundred files import
com.bringyour.sdk -- so without the .aar nothing compiles, including the
main sources the unit tests pull in. It checks out sdk beside connect, glog
and goidenticons (the `replace ../` directives in sdk/go.mod and
sdk/build/go.mod require exactly those directory names), carries the same
conditional goidenticons RenderPngV2 shim as urnetwork/windows, installs the
pinned NDK, and runs `make init_tools build_android` -- init_tools rather
than init because init ends in `go clean -cache && go clean -modcache`,
which would discard everything setup-go had just restored. Go is pinned via
go-version-file: sdk/build/go.mod (1.26.5) rather than the house-usual
`stable`: stable is 1.27, and sdk/build/Makefile states that gomobile must be
built with go <= 1.26 or the runtime fatals on the GODEBUG its own recipe
exports.
gradle consumes that .aar and runs :app:testGithubDebugUnitTest -- 19 test
classes under app/app/src/test that the release pipeline never executes --
plus :app:assembleGithubDebug to prove the app still packages, plus
compile*ReleaseKotlin for all four flavors, which is the only thing that
compiles each flavor's own java srcDir. It writes app/local.properties
first, because versionName/versionCode are read from it at CONFIGURATION
time and otherwise the build shells out to a warpctl that does not exist on
a runner and dies before any task starts.
Build and test only. No release: assemble*Release resolves a keystore under
${WARP_HOME}/release that is not in this repo, and the instrumented suite
needs an emulator and a live account fixture from the vault. Neither is
attempted and no secret is referenced.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MAXFxG1EK4jTxQ1iW73BUr
The first run finished in 3m24s (sdk-aar) and 7m02s (gradle), so 90/60 was far looser than needed. 45/40 keeps roughly a 6x margin while bounding a stalled fetch to minutes instead of GitHub's 6h default -- the same reasoning urnetwork/linux's build.yml documents on its jobs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MAXFxG1EK4jTxQ1iW73BUr
Ryanmello07
marked this pull request as ready for review
August 22, 2026 05:16
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This repo has no
.githubdirectory today, so nothing is built or tested on push. This adds a first CI: build and test only, no release, no signing, no secrets.Verified green on a real runner, first attempt — run 32550013491. Details at the bottom.
What it runs
Two jobs on
ubuntu-latest, because they fail for completely different reasons.sdk-aar— the Go half. BuildsURnetworkSdk.aarwith gomobile. This is not optional scaffolding: the app has no source of its own for the SDK.app/app/build.gradle's only dependency on it isnothing is published to download, and over a hundred files
import com.bringyour.sdk— including the main sources the unit tests compile against. Without the.aarthat fileTree resolves empty and nothing in the app compiles at all.The job checks out
urnetwork/sdkbesideconnect,glogandgoidenticons(thereplace ../directives insdk/go.modandsdk/build/go.modrequire exactly those directory names), carries the same conditionalRenderPngV2shim asurnetwork/windows, installs the pinned NDK, and runsmake init_tools build_android.gradle— the Kotlin half. Downloads that.aarand runs, fromapp/:testGithubDebugUnitTestis the real suite — 19 test classes underapp/app/src/test/java/com/bringyour/networkon junit 4.13.2, 177 tests, which ran green. It is worth saying that the release pipeline never runs these:build/all/run.shonly doesclean assemblePlayRelease bundlePlayRelease assembleSolana_dappRelease assembleEthos_dappRelease. This closes a real gap, not a formality.assembleGithubDebugproves the app still packages. The other three flavors getcompile*ReleaseKotlinrather than an assemble (see below) — which is not a token check either, since each flavor adds its own java srcDir (src/ungoogle,src/google,src/solana_dapp,src/ethos_dapp) and this is the only thing that compiles those sources at all.Triggers:
pushandpull_requestonmain, plusworkflow_dispatch.permissions: contents: read. Concurrency group per ref withcancel-in-progress, andtimeout-minuteson both jobs.What it deliberately does not do
signingConfigresolves${WARP_HOME}/release/android/signing/app.jks, which is not in this repo and is a genuine secret. So noassemble*Release/bundle*Release.compile*ReleaseKotlinnever schedules a packaging orvalidateSigningtask, which is why the release variants can still be compile-checked without one.build.shalready uses that same idiom for the flavor it cannot assemble locally.app/app/src/androidTest, driven bytest-main.sh, needs an emulator and a live account fixture fromvault/main/test-acceptance.yml.build.shregeneratesres/values*/strings.xmlfrom the sibling store first. The generated files are committed, so CI does not need the store — but it therefore will not catch key drift the way the release pipeline does.The workflow references no
secrets.*at all. The only signing input is the standard~/.android/debug.keystore, generated in-job from the publishedandroid/androiddebugkeyconstants (validateSigningGithubDebugran and passed).Verified run
Run 32550013491, both jobs success on the first attempt.
sdk-aargomobile binditself was 2m 28sgradleBUILD SUCCESSFUL in 6m 33s, 131 actionable tasks, 131 executed.aaris real: 39,425,248 bytes, with all three JNI slices present —jni/arm64-v8a/libgojni.so,jni/armeabi-v7a/libgojni.so,jni/x86_64/libgojni.so— plus a 547 KB sources jar.unit-test-reportartifact on every run, pass or fail.kapt*ReleaseKotlin+compile*ReleaseKotlinexecuted, so Hilt annotation processing is covered per flavor.stripGithubDebugDebugSymbolsandpackageGithubDebugexecuted, so the NDK and packaging paths are genuinely exercised.This came in far under the hour I had budgeted, mostly because the pinned NDK
29.0.14206865is already on the runner image (the install step took 9 seconds) and the bind is faster than expected. Job timeouts are set to 45 and 40 minutes — roughly 6x headroom, bounding a stalled fetch to minutes rather than GitHub's 6h default, the same reasoningurnetwork/linux'sbuild.ymldocuments.For a maintainer to decide
main/master, matchingsdk's andwindows' workflows. That means an unrelated push tosdk,connect,glogorgoidenticonscan turn every open PR here red at once.server's workflow has the same tradeoff written up in a comment; if it proves noisy, pinref:per sibling and bump deliberately.pushis limited tomainper thesdk/connectconvention, so pushes to topic branches only get CI once a PR is open.server's workflow deliberately triggers on every branch instead — that is the precedent to copy if you want branch coverage.assembleGithubDebug— keep? It is the only proof the app packages, andsplits { abi }withuniversalApk truemeans it emits three APKs. At 7 minutes for the whole gradle job it is comfortably affordable, so I kept it; it is the first thing to drop if that ever changes.Two toolchain notes that will look odd in review
go-version-file: sdk/build/go.mod(1.26.5), notgo-version: stablelikesdk/connect/linuxuse.stableis 1.27 now, andsdk/build/Makefilesays so in thebuild_androidrecipe itself: "gomobile/gobind must be built with go <= 1.26 — the go 1.27 runtime fatals when this GODEBUG is set",GODEBUG=gotypesalias=0being exactly what that recipe exports.GOTOOLCHAIN: localis set so a straygodirective cannot silently self-upgrade past the pin.make init_tools, notmake init.initends withgo clean -cache && go clean -modcache, which would discard everythingsetup-gohad just restored, and itgo getsx/mobile/bind— already a pinned indirect require inbuild/go.modat the sameGOMOBILE_VERSION.init_toolsis the split the Makefile documents and the onebuild/all/android/setup.shand:app:buildSdkAcceptancerely on.Also:
app/local.propertiesis written before any gradle invocation.versionName/versionCodeare read from it at configuration time, and without it the build shells out towarp/warpctl/build/<os>/<arch>/warpctl— which does not exist on a runner — and dies before any task starts, with an IOException that names nothing about the cause. The file is git-ignored and the two values only name the archive.