- Introduced `setDeviceIdOverride` method in `ShorebirdUpdater` to allow clients to set a custom device ID for patch checks.
- Implemented the method in `ShorebirdUpdaterImpl` for both IO and web platforms.
- Updated the `Updater` class to handle the device ID override in native bindings.
- Added tests for the new functionality in both IO and web test suites, ensuring proper behavior when the updater is available and unavailable.
The C surface in `library/src/c_api` was a single bucket of `pub extern "C"`
functions covering both consumers — `package:shorebird_code_push` (via
ffigen) and Shorebird's Flutter engine fork (via direct C++ link). That
made it hard to reason about which symbols are stable ABI versus
internal, and ffigen was generating bindings for engine-only symbols
that no Dart code calls.
Split into two self-contained submodules and two cbindgen-generated
headers:
- `c_api::dart` → `include/updater_dart.h` (stable ABI; ffigen entry
point). Defines `UpdateResult`, the `SHOREBIRD_*` status constants, and
the five Dart-stable functions: `shorebird_current_boot_patch_number`,
`shorebird_next_boot_patch_number`,
`shorebird_check_for_downloadable_update`,
`shorebird_update_with_result`, `shorebird_free_update_result`.
- `c_api::engine` → `include/updater_engine.h` (no stability guarantee).
Defines `AppParameters`, `FileCallbacks`, and the engine-only functions:
`shorebird_init`, `shorebird_should_auto_update`,
`shorebird_validate_next_boot_patch`, `shorebird_next_boot_patch_path`,
`shorebird_free_string`, `shorebird_start_update_thread`, and the
`shorebird_report_launch_*` trio.
Each bucket file is self-contained: cbindgen scans only the file
(`with_src` in build.rs) and emits the items it defines plus the C
types they reference. There are no exclude/include lists in the
cbindgen configs — adding a function to one bucket automatically lands
it in the right header, and items in the other bucket cannot leak.
`mod.rs` shrinks to a thin layer of private helpers shared by both
buckets (`to_rust`, `allocate_c_string`, `free_c_string`, `log_on_error`)
plus the test module.
`include/updater.h` is removed; consumers include the specific header
for their use case. The Flutter engine's
`shell/common/shorebird/updater.cc` will be updated in a follow-up
engine-repo PR to include `updater_engine.h` directly.
Also drops two retired Dart-side symbols:
- `shorebird_update` (replaced by `shorebird_update_with_result` in the
Dart 2.0 rewrite, Nov 2024).
- `shorebird_check_for_update` (replaced by
`shorebird_check_for_downloadable_update` in the same rewrite).
The shorebird_code_push package's `_legacyFallback` was the only path
that still called `shorebird_update`. The package's `flutter: >=3.24.5`
constraint guarantees the engine has `shorebird_update_with_result`, so
the fallback was unreachable in practice. Removing it lets us drop the
ABI symbol.
Bumps shorebird_code_push to 2.0.7. Bindings regenerated via ffigen now
contain only the five Dart-stable symbols.
Follow-up engine PR will: include `updater_engine.h` instead of the
removed `updater.h`; clean up `android_exports.lst` (drop the ghost
`shorebird_active_path` and `shorebird_active_patch_number` exports,
drop `shorebird_check_for_update`).
When `update()` is called while another update (typically the automatic
updater thread) is already running, the Rust updater previously bailed
with `UpdateError::UpdateAlreadyInProgress`, which surfaced in Dart as
`UpdateException: Update already in progress (unknown)`. This is the
single highest-volume `UpdateException` in customer telemetry, yet the
underlying situation is benign — the in-flight update continues on its
own, the caller simply did not start a new one.
Add a new `UpdateStatus::UpdateInProgress` variant and matching C
status code `SHOREBIRD_UPDATE_IN_PROGRESS = 4`. `updater::update()`
catches the `UpdateAlreadyInProgress` error from the lock helper and
maps it to `Ok(UpdateStatus::UpdateInProgress)`. The Dart wrapper
treats the new status as a successful return alongside
`SHOREBIRD_UPDATE_INSTALLED`, so `update()` no longer throws for this
case.
Update the existing `usage_during_hung_update` c_api test to assert the
new contract, and add a Dart test covering the in-progress return path.
Version skew: new Dart on an old engine still sees the legacy
`SHOREBIRD_UPDATE_ERROR` + "Update already in progress" message and
will still throw. The fix lands once both sides ship.
Partially addresses shorebirdtech/shorebird#3682 — does not resolve the
broader asymmetry of `update()` semantics (it still does not wait for
someone else's in-flight update to finish), which remains as v2 design
work in shorebirdtech/shorebird#3684.
`ShorebirdUpdater.update()` previously threw `UpdateException: No update
(noUpdate)` whenever the patch check returned no available update, even
though that is a successful outcome of calling `update()` — the app is
already running the latest patch.
The wrapper only returned normally on `SHOREBIRD_UPDATE_INSTALLED`;
every other status (including `SHOREBIRD_NO_UPDATE`, value 0) fell
through to the generic `throw UpdateException(...)` path, which is
why callers saw `UpdateException: No update (noUpdate)` in their
exception telemetry.
Treat `SHOREBIRD_NO_UPDATE` as a successful return alongside
`SHOREBIRD_UPDATE_INSTALLED`. No FFI change, safe against any engine
version (status code 0 is stable).
Fixesshorebirdtech/shorebird#3681
Previously, checkForUpdate returned upToDate after a rollback because
the condition `next != null && current?.number != next.number` treated
a null next patch as "up to date". After a rollback, the Rust updater
correctly uninstalls the patch (next becomes null), but the app is still
running the rolled-back patch (current is non-null). The simplified
condition `current?.number != next?.number` correctly detects this
mismatch and returns restartRequired.
Fixes https://github.com/shorebirdtech/shorebird/issues/3206
Users sometimes gate app startup on checkForUpdate() or update()
completing (e.g. awaiting in initState before showing content), which
can cause the app to appear stuck on the splash screen when the
network is slow.
Add warning doc comments to both methods recommending the .then()
pattern for startup code, and update README examples to use .then().
Fixes https://github.com/shorebirdtech/shorebird/issues/3179
* feat(shorebird_code_push): track support
* cleanup and add todos
* docs
* run ffigen
* use c_char instead of char
* Add channel support
* tests
* tests
* update podfile.lock
* Update example to include tracks selector
---------
Co-authored-by: Bryan Oltman <bryan@shorebird.dev>
Co-authored-by: Bryan Oltman <bryanoltman@gmail.com>
* break: move shorebird_code_push_io and _web into src/
Users were sometimes importing the _web version thinking it
was the "web safe" import, rather than shorebird_code_push.dart
and then being surprised when ShorebirdCodePush did nothing.
Fixes https://github.com/shorebirdtech/shorebird/issues/1209
* Update formating for Dart 3.1
* feat: add shorebird_current_boot_patch_number function
* Continue support for next_patch_version
* rename, return null in case of 0
* feat(shorebird_code_push): add download update functionality
* rename defaultValue to fallbackValue
* add forTest constructor to ShorebirdCodePush
* feat(shorebird_code_push): add updater bindings and dart support
* newlines
* docs
* feat(shorebird_code_push): update example to use code push bindings
* Move checkForUpdate into setState
* Update to reflect patch number being sent as int
* polish
* updater test coverage
* Update shorebird_code_push/example/lib/main.dart
Co-authored-by: Felix Angelov <felix@shorebird.dev>
* Analysis
---------
Co-authored-by: Felix Angelov <felix@shorebird.dev>
* feat(shorebird_code_push): add updater bindings and dart support
* newlines
* docs
* Remove unused support for dart cli
* clarify Android-specific setup in readme