Commit Graph
15 Commits
Author SHA1 Message Date
Zoltan Pappandhchengting 10a35f9215 Handle network changes without restarting the engine (#237)
Network changes no longer restart the engine; they are handled as events.

No network: the reconnect loops suspend instead of retrying against an unreachable network, and the UI and the notification show "No network available". The loops resume with a fresh backoff when connectivity returns.
Network switch: stale management, signal and relay connections are swept and re-dialed immediately on the new network (cross-transport handover fast path: fix: notify Go core on seamless network handover (cellular → WiFi) while VPN is active #243).
WiFi-to-WiFi handovers are detected by network identity, so a same-transport switch triggers the same reconnect path.
Availability is seeded from non-VPN internet networks, so an up tunnel cannot mask a missing underlying network.
New instrumented e2e suites (NetworkTransitionTest, ExitNodeNetworkTransitionTest) cover blackout, transport-switch and handover scenarios with outage budgets.
---------

Co-authored-by: hchengting <74168694+hchengting@users.noreply.github.com>
2026-08-25 20:24:31 +02:00
Zoltán Papp 5498ddbfe3 Anchor the snapshot version name to the nearest release tag 2026-08-17 14:03:06 +02:00
Zoltan Papp 4d1038605c Pin gobind alongside gomobile instead of running gomobile init (#240)
* Pin gobind alongside gomobile instead of running gomobile init

gomobile bind shells out to gobind, and gobind is the tool that actually
generates the Java bindings and the JNI glue. CI cached and installed only
gomobile and left the build script to call `gomobile init`, which installs
gobind from @latest: the driver was pinned while the generator floated, so
the generated API could change without a commit here.

Take the revision from the go.mod the submodule already carries, so the two
tools cannot drift apart and the cache key follows a submodule bump on its
own, and have the build script check that the pair is present rather than
reaching for gomobile init.

The cache key names both tools now. Entries saved under the old key hold no
gobind, so keeping it would hit the cache, skip the install and leave the
build without a generator.

* Install gomobile and gobind automatically at the go.mod pinned revision
2026-08-17 12:10:34 +02:00
Zoltan Papp cf4d28d2b8 Add manual workflow to build a version-code-consistent APK for ad-hoc distribution (#236)
* Add manual workflow to build a version-code-consistent APK for ad-hoc distribution

Adds prepare-publication.yml (workflow_dispatch) that reuses the release
signing key and computes version_code as the combined run count of itself
and build-release.yml, so the two never collide. A shared concurrency
group on both workflows prevents a race on that computation.

* Draw the release version code from the shared run counter

build-release.yml derived its version code from github.run_number, which
counts only its own runs and is blind to prepare-publication.yml. With 25
release runs so far, the first ad-hoc build would take 25+1+40=66 and the
next release would take 26+40=66 as well, then fall behind: 67 after 68 was
already published. The concurrency group cannot fix this, because run_number
is assigned when the run is queued, not when the step executes.

Both workflows now sum the same two counters, so every run of either one
advances the code by exactly one. Reading those counts needs actions: read,
which an explicit permissions block otherwise withholds.

* Name the manual workflow build-snapshot

prepare-publication named a step in a process, while the workflows beside it
name what they produce: build-debug, build-release. What this one produces is
a release-signed build from an arbitrary commit with no tag behind it, which
is what snapshot means.

Not build-rc: release candidates already exist here as published pre-release
tags (v0.6.0-rc.1, v0.3.3-rc.2) and are built by build-release.yml, so the
name would claim a meaning the repository has already given away.

The version code counter is keyed by workflow file name, so the rename is
free only while the workflow has no runs yet.

* Label snapshot builds snapshot- rather than ci-

The version name travels to the management server as the peer's ui_version
and is what the about screen shows, so it is the only thing telling support
which build a peer is running. build-debug.yml already emits ci-<sha> from
the same expression, which left an unsigned PR build and a release-signed
hand-out looking identical in the peer list.

The artifact keeps just the version name; prefixing it again read as
snapshot-artifacts-snapshot-<sha>.

* Resolve the Go version from the submodule's release tags in CI

CI builds used the version only when the submodule sat exactly on a tag and
fell back to ci-<sha> otherwise, which the management server rejects in
NBVersionCheck posture checks: it treats ci- as a development build everywhere
except there. Since the submodule is bumped more often than it is tagged,
release builds effectively always shipped as ci-<sha>.

CI now resolves the version by walking the pinned commit's ancestry back to
the last stable release tag and appending the commit as SemVer build metadata,
e.g. 0.77.0+f06b8c762. The server strips build metadata before every
comparison, so this passes the same gates as a plain 0.77.0 while still naming
the exact commit in the dashboard. Pre-release tags are skipped as a base: a
suffix like -rc.2 lands in SemVer pre-release position, which the server
compares differently from a release.

Local builds now always produce dev-<sha>, which skips every server-side
version gate; a developer who needs a real version passes it as the argument.

The ancestry walk needs full history, but actions/checkout clones submodules
shallow — the tags arrive without the commits between HEAD and the tag, and
the walk would silently come up empty. The composite action therefore
unshallows the submodule before building, guarded because --unshallow on a
complete repository is a hard error.

* Document the three build workflows and their differences

* Fail the build when a run count cannot be fetched

The zero fallback existed for one legitimate case: the runs endpoint returns
404 until a workflow has run or reached the default branch, and treating that
as zero is what lets build-release compute a code before build-snapshot's
first run. But it also swallowed every other failure — a network error or a
revoked token minted a version code far below the published ones, silently
for hand-distributed snapshots.

Keep the 404-means-zero case and abort on everything else, including a
non-numeric response, which bash arithmetic would otherwise fold to zero.
2026-08-16 10:25:21 +02:00
Zoltan Papp 18a1b6e2f9 E2e tests (#229)
* test: port the e2e suite to the redesigned UI

The iOS-style redesign folded the old add-profile / switch / change-server
flow into a single profile editor dialog, so LoginFlow now drives that:
name + setup key in one submit, with the key field revealed via its
row-owned switch. Enrolment success is verified positively — the profile
must appear in the list — since the dialog also dismisses on paths that
never registered the peer. Creating a profile does not activate it, so the
flow switches to it explicitly before connecting.

All navigation now goes through taps (bottom nav and settings rows) rather
than NavController, keeping the app in the state a user would produce.

The force-relay setup step was flaky because scrollForward() flings and a
tap injected while the list is still settling is consumed as a scroll-stop.
Scroll, settle, click and verify now retry as one unit.

Also: EditText writes are read back (soft-keyboard layout shifts silently
swallowed setText), autofilled setup-key fields are cleared first,
screenshots moved to the app's external files dir (scoped storage made
/sdcard/Pictures fail with EACCES), and the first-run screen is dismissed
before the suite starts since it shares view ids with the profile editor.

VpnTestHarness listens through StateListenerAdapter so the interface can
grow without breaking the suite.

* Bump netbird submodule: merge main into android/gui-integration

Brings in #7022, which stops NewAuth from rebuilding the config from
scratch on setup-key enrolment — previously that dropped every stored
field, blanking the profile's display name and regenerating the WireGuard
identity. The e2e suite's positive login check depends on it.

* test: drive the VPN connect from the Home screen toggle

connectAndAwait started the engine through MainActivity.switchConnection()
and waited on a StateListener — a code path no user takes. Tap the Home
screen's connect toggle instead and wait for the on-screen status text to
read Connected, so both the trigger and the feedback are the exact UI a
user sees. The suite runs on an English locale, so the literal status text
is matched.

* test: log the full ping output on every attempt

A reachability failure only reported that the peer never answered; the
evidence — did the name resolve, to what address, or did the reply just
not come back — was discarded. Log the complete ping output (and the
resolve probe's) so the CI logcat artifact shows exactly which of those
it was.

* test: log the in-process resolver's verdict when a ping fails

The CI artifact showed every failing ping with empty stdout — resolution
errors go to stderr, which executeShellCommand does not capture — and the
tunnel's DNS trail shows only search-domain expansions being queried, never
the bare peer name. What is still missing is the resolver's actual error
and whether the app's own resolver (guaranteed to route through the VPN,
unlike the shell-uid ping) agrees. Log InetAddress.getByName() for the
target on every failed attempt so the next run's artifact answers both.

* test: give the data-plane probes 90s to settle

Three CI runs put the evidence together: the tunnel comes up fast, but its
data plane does not. The relay-backed peer resolves and answers 8-12s after
Connected (attempt 1 fails, attempt 3 pings), and the exit-node profile
SERVFAILs upstream DNS until the exit-node path is ready — every failure
was one of these still settling when the 20s window closed, with a
different test tripping first each run. The Robot suite allows ~3 minutes
for the same sequence; 90s on the ping/resolve/port/egress windows keeps
failures reasonably fast while clearing the observed settling time with
margin. Connect stays at 20s — the UI reports Connected within seconds.

* Bump netbird submodule to main

The android/gui-integration work is squash-merged into main — the only code
difference left was main's relay reconnect backoff randomization (#7067),
which the branch did not have.

* test: drop the setup-key env fallback from the build script

The CI script passes both keys as -P instrumentation arguments, so reading
them from the environment never took effect.

* ci: install the JDK from temurin instead of adopt

setup-java fails to resolve the 'adopt' distribution — AdoptOpenJDK was
renamed to Eclipse Temurin and the old endpoints are going away — so the
build died before installing a JDK. Same JDK, current name.

* ci: exclude the e2e package from the instrumented-tests job

The e2e tests need a setup key for a live account, held only by the private
mobile-e2e repo, so every one of them failed here with "setupKey
instrumentation argument is required". They are meant to run from that
repo's workflow, not on every push.
2026-08-10 10:37:13 +02:00
Zoltán Papp 16abeddbd9 ci: install the JDK from temurin instead of adopt
setup-java fails to resolve the 'adopt' distribution — AdoptOpenJDK was
renamed to Eclipse Temurin and the old endpoints are going away — so the
build died before installing a JDK. Same JDK, current name.

(cherry picked from commit 8592de3dff)
2026-08-05 18:08:28 +02:00
Zoltan Papp 7211e4d4dc ci: add workflow to bump netbird submodule on release tags (#175) 2026-04-29 17:10:37 +02:00
Zoltan Papp 029585a971 Add network connectivity stress test for VPN resilience (#162)
* Add network connectivity stress test for VPN resilience

Instrumented test that simulates real-world network disruptions (WiFi/mobile
switching, airplane mode, flapping, long outages) and verifies VPN recovery
via ping. Auto-detects real device vs emulator and uses appropriate network
control strategy for each.

* Fix review findings: resource leak, version bump, markdown lint

- Use try-with-resources in sendEmulatorConsoleCommand to prevent socket leak
- Bump testRules version from 1.6.1 to 1.7.0
- Add language specifier to fenced code block in README

* Fix review findings: restore in finally, label mismatch, readUntilOK error handling

- Always call disruption.restore in finally block to prevent stale network state
- Fix EMU latency label from 5s to 10s to match actual delay value
- Make readUntilOK propagate IOException and detect KO/EOF failures

* Exclude NetworkConnectivityStressTest from CI

This test requires a configured VPN and real network interfaces,
so it cannot run on the CI emulator. Run it manually on a real device.
2026-04-14 16:02:46 +02:00
Maycon Santos d50164314d build debug bundles when pushing to main (#107) 2025-11-25 14:19:32 +01:00
Maycon Santos 1602f54125 Update Go setup and gomobile version in Android build action (#104)
* Update Go setup and gomobile version in Android build action

* Update netbird file configuration

* update submodule
2025-11-22 11:29:54 +01:00
Diego RomarandZoltan Papp d277cf686d restart go engine on network change (#97)
* Add network change detector and listeners

* Add network toggle listener to VPNService

* Add unit tests for ConcreteNetworkChangeListener

* Rename NetworkChangeListener to NetworkAvailabilityListener

* Change EngineRunner's Set implementation

From HashSet to ConcurrentHashMap's KeySet,
which is thread-safe.

* Update submodule to the latest tag (v0.59.6)

* Add EngineRestarter to restart the Go engine

* Add LOGTAG to NetworkChangeDetector

* Add some documentation to NetworkToggleListener

* Use EngineRestarter as implementation of NetworkToggleListener

When subscribing to ConcreteNetworkAvailabilityListener

* Replace HashMap usage with ConcurrentHashMap

The availableNetworkTypes HashMap is accessed from network
callback threads without synchronization

* Wrap connectivityManager.unregisterNetworkCallback with try-catch

* Add restart runnable and timeout callback to EngineRestarter

Restart runnable is used as a debouncing mechanism to
prevent concurrent restarts
Timeout callback is to reset the isRestartInProgress flag's
value if the engine takes too long to restart

* Update git submodule reference to latest tag

* Reverse cleanup order on VPNService's onDestroy

Now it disposes of network listener components
before stopping engineRunner

* Ci tests (#99)

Add testing steps for CI

* Remove line break

---------

Co-authored-by: Zoltan Papp <zoltan.pmail@gmail.com>
2025-11-18 18:31:34 -03:00
Zoltan Papp ec68702e6e Add release CI code and bump lib version (#75) 2025-08-28 14:05:30 +02:00
Zoltan Papp f81f81ce6e rewrite client in Java (#54)
Rewrite the client in native Java.

There were no new features added in this change, but that's the first step in moving to a faster development cycle for NetBird client.
2025-06-05 20:37:51 +02:00
Zoltan Papp 0af012f40f Increase the target sdk version to 34 (#36)
If apps that target Android 14 use a foreground service, they must declare a specific permission, based on the foreground service type, that Android 14 introduces. These permissions appear in the sections labeled "permission that you must declare in your manifest file" in the intended use cases and enforcement for each foreground service type section on this page.

update compile SDK version to 34
update Go version in CI
2024-11-26 14:29:31 +01:00
Maycon Santos 304301cbf2 open-source code 2024-02-28 19:21:08 +01:00