Run `cargo update` to bump transitive dependencies, fixing 10 of 11
alerts (h2, ring, idna, mio, tokio, bytes, time, quinn-proto,
rustls-webpki, unsafe-libyaml).
Replace deprecated `tempdir` dev-dependency with `tempfile` to
eliminate the `remove_dir_all` vulnerability (the last alert).
* refactor: replace reqwest with ureq to reduce binary size
reqwest's blocking API is built on top of its async implementation,
pulling in tokio, hyper, futures, and ~84 other transitive dependencies
even though we only make simple synchronous HTTP calls.
ureq is a synchronous-only HTTP client that eliminates the async
runtime entirely. This reduces transitive dependencies from 227 to 143
and the linked dylib from 4.5 MB to 3.7 MB (-18%). The .a archive
drops from 28 MB to 26 MB, but real savings will be larger once
linked into libflutter with dead code stripping.
The network API surface is unchanged — three functions (patch check,
file download, event reporting) using POST/GET with JSON.
* chore: add ureq to spell check dictionary
* refactor: use into_body() instead of body_mut() where response is consumed
* fix: simplify network error matching to avoid fragile string checks
Consolidate HostNotFound, ConnectionFailed, and all Io errors into
a single network-error arm instead of pattern-matching on error
message strings that could change across OS versions or locales.
* chore: add TODO for misleading network error message
* test: test api calls
* chore: add docs
* chore: fix cspell
* fix: use no-op network hooks in multi_engine tests
Set no-op network hooks after init_for_testing so the fire-and-forget
thread spawned by report_launch_success completes instantly without
network I/O, preventing leaked threads from interfering with subsequent
serial tests that use mock servers.
* fix: improve inflate error handling and validate compressed patches
Addresses #2989: "pipe reader has been dropped" / "failed to fill whole
buffer" errors during patch inflation were masking the real root cause.
Three changes:
1. Join the decompression thread and propagate its error as the primary
failure, rather than fire-and-forget logging. The patching thread's
broken-pipe error is a side-effect, not the cause. Also drop the pipe
reader before joining to avoid deadlock when patching fails.
2. Validate the downloaded compressed patch (non-empty, valid zstd magic
bytes) before attempting decompression, so corrupt/truncated downloads
produce a clear error instead of cryptic pipe errors.
3. Log the download size in download_to_path to help diagnose truncated
downloads in the field.
* docs: expand comment on drop(fresh_r) to clarify deadlock risk
* feat: log app_id, patch number, and version before download
* test: add inflate tests for corrupt data and invalid magic
* test: cover decompression-error-as-primary-cause path in inflate
Uses a valid zstd frame followed by a corrupt second frame so that
bipatch::Reader::new succeeds but decompression fails midway, verifying
that the decompression error is reported as the primary cause.
* feat: add enhanced error messages for file operations
Add a file_errors module that provides context-aware error messages
for file operations. When file operations fail, users now see:
- The specific operation that failed (create, read, write, rename, etc.)
- The full path involved
- Helpful hints about possible causes based on error type
- Android-specific hints for permission errors (SELinux, Work Profile,
MDM/Knox policies, app cloning features)
This helps diagnose issues like "Permission denied (os error 13)" by
indicating which operation failed and suggesting possible causes.
The scenario is:
1. User is running patch 2 (booted successfully, so last_booted_patch = 2)
2. While the app is still running, they call the check-for-update API
3. Patch 3 is downloaded and installed (next_boot_patch = 3)
4. Before the app restarts, they check again and patch 4 is available
5. The buggy code is supposed to delete patch 3 (never booted), but instead deletes patch 2 (the last known-good patch)
6. Patch 4 is set as next_boot_patch
If patch 4 boots fine, nobody notices. But if patch 4 fails to boot and the system tries to roll back to patch 2, those artifacts are gone.
* feat: move patch verification from boot time to install time
* feat: make it switchable
* chore: update comments
* fix: test invalid yaml
* chore: update readme
* feat: add more comments to readme
* doc: more readme updates
* chore: rename to patch_verification
* fix: make tests pass on Windows
* Run ci on all supported building OSes
* Add os name to CI step
* Build rust crates on all oses
* tweak
* Only run rust on multiple oses for now
* chore: add cspell checking and make pass
* chore(shorebird_code_push): minor improvements to example (#242)
* chore(shorebird_code_push): v2.0.2 (#243)
* chore: fix cspell
---------
Co-authored-by: Felix Angelov <felix@shorebird.dev>
* 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>
* fix: don't return true from isNewPatchAvailableForDownload if the new patch will not be installed
* fix: update current_boot_patch to always return currently running patch
* add C api tests
* add extra test check
* feat: add rollback support
* Cleanup
* add test
* docs and cleanup
* log full PatchCheckResponse
* remove unnecessary file permission spec from test helper
* report launch failure when a patch was in the process of booting on app start
* refactor: extract shared fake patch test logic into function
* cleanup
* remove commented-out code
* cleanup
* Cleanup
* cleanup
* rename
* update comments
* add todo
* remove added logs
* update comments
* make patch_event a proper constructor
* Split with_mut_state out from with_state
* update comment
* feat: track known_bad_patches instead of highest_seen_patch to support rollbacks (#191)
* feat: track known_bad_patches instead of highest_seen_patch to support rollbacks
* docs
* do not send patch number, do not install already installed patches
* remove patch number from PatchCheckRequest
* tests
* more tests
* fix merge issues
* fix: track patches while they are booting
* update comment
* update comment
* add test
* refactor: only check whether a patch failed to boot on initialization
* fix test
* fix: track patches while they are booting
* update comment
* update comment
* add test
* only init UpdaterState if set_config succeeds
* pr feedback
* cleanup
* pr feedback
* add todo referencing github bug re: patch install failure events
* document lifetime of currently_booting_patch
* more details about on_init lifetime
* add todo re: validation
* update test comments
* update test to regenerate patch manager when simulating a fresh start
* Introduce InitError for better testing
* fix test
* add extra assert in PatchManager on_init_tests