feat: bundle mode with atomic deploys, seed assets, and version-gated caching#482
feat: bundle mode with atomic deploys, seed assets, and version-gated caching#482divyanshub024 wants to merge 3 commits into
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds opt-in bundle mode to ChangesStac package bundle mode
stac_cli atomic bundle deployment
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
- StacBundle/StacBundleConfig models; SharedPreferences bundle store with schema-version guard - StacBundleService: conditional sync (ETag/since), seed-asset hydration (highest version wins), updates stream, offline fallback - StacBundleUpdater: sync on app-resume + optional foreground polling - StacCloud serves screens/themes from the bundle when enabled (opt-in; falls through to legacy per-artifact fetch) - CLI: single atomic POST /bundles with checksum + payload size log, seed asset emission with pubspec warning, --legacy fallback flag; build now cleans stale .build outputs
11bd5a3 to
3b58309
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
packages/stac/lib/src/services/stac_bundle_service.dart (2)
104-167: 🩺 Stability & Availability | 🔵 TrivialNo backoff on repeated sync failures.
On non-2xx/non-304/204 statuses or network errors,
sync()simply returns the stale bundle without recording failure state. Combined withStacBundleUpdater's fixed polling interval and resume-triggered checks, a persistently failing/rate-limiting server will be hit at the same fixed cadence indefinitely. Consider adding lightweight backoff (e.g., skip the next N scheduled checks after consecutive failures) for resilience during outages.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/stac/lib/src/services/stac_bundle_service.dart` around lines 104 - 167, Add lightweight failure backoff to the bundle sync flow so repeated errors do not keep hitting the server at a fixed cadence. Update StacBundleService._syncInternal to record consecutive failures for non-2xx/304/204 responses and caught exceptions, then have StacBundleUpdater skip the next scheduled sync attempts for a short backoff window after repeated failures. Reset the failure state after a successful sync so normal polling resumes.
169-194: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winChecksum is stored but never verified.
_bundleFromResponsecaptureschecksumfrom the response into the persistedStacBundle, but nothing in this file (or the reviewed cohort) compares it against a locally computed hash ofscreens/themesbefore trusting and persisting the payload on a200. If the intent (per the CLI'scomputeBundleChecksum) is end-to-end integrity verification, a corrupted/incomplete-but-valid-JSON response would currently be accepted and cached without detection.If checksum verification is intentionally deferred to the model layer or CLI-only (deploy-time verification), this is fine — otherwise consider validating it here before swapping in the new bundle.
♻️ Possible verification step
final bundle = _bundleFromResponse( Map<String, dynamic>.from(data), projectId: projectId, etagHeader: response.headers.value('etag'), ); if (bundle == null) { Log.w('StacBundleService: Bundle response is missing a version'); return cached; } + if (bundle.checksum != null && + !_matchesChecksum(bundle)) { + Log.w('StacBundleService: Bundle checksum mismatch, discarding'); + return cached; + }Please confirm (or search) whether checksum verification against the bundle payload is implemented elsewhere in the
StacBundlemodel, or whether it's deploy-time-only by design.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/stac/lib/src/services/stac_bundle_service.dart` around lines 169 - 194, The checksum captured in _bundleFromResponse is persisted but never validated against the bundle payload before accepting a 200 response. Check whether StacBundle or any related service already verifies the checksum; if not, add verification in StacBundleService._bundleFromResponse before constructing the StacBundle, comparing the response checksum to a locally computed hash of screens/themes and rejecting mismatches. If checksum validation is intentionally handled elsewhere, document that design clearly in this path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/stac_cli/lib/src/services/deploy_service.dart`:
- Around line 137-182: The deploy flow in deploy_service.dart currently writes
the seed asset even when the decoded 2xx response has no version, which can
overwrite a valid bundle with an invalid empty seed. Update the deploy handling
around the response body/version check in the deploy method to validate that
body['version'] is present before calling _writeSeedAsset, and either skip the
write or throw a StacException for malformed success responses; keep the
existing success/info logging and checksum handling in place.
In `@packages/stac/lib/src/framework/stac_service.dart`:
- Around line 202-207: The bundle initialization path in StacService should use
the caller’s dio and validate options up front: pass the same dio used by
StacNetworkService into the bundle sync flow so StacBundleService.sync honors
auth/interceptors/proxy settings, and guard the _bundleConfig.enabled branch so
StacBundleUpdater.start() and prefetch only run when options is present. If
bundle mode is enabled without options, fail fast during initialization with a
clear validation error instead of letting the later _projectId access in
StacBundleService or the updater throw asynchronously.
---
Nitpick comments:
In `@packages/stac/lib/src/services/stac_bundle_service.dart`:
- Around line 104-167: Add lightweight failure backoff to the bundle sync flow
so repeated errors do not keep hitting the server at a fixed cadence. Update
StacBundleService._syncInternal to record consecutive failures for
non-2xx/304/204 responses and caught exceptions, then have StacBundleUpdater
skip the next scheduled sync attempts for a short backoff window after repeated
failures. Reset the failure state after a successful sync so normal polling
resumes.
- Around line 169-194: The checksum captured in _bundleFromResponse is persisted
but never validated against the bundle payload before accepting a 200 response.
Check whether StacBundle or any related service already verifies the checksum;
if not, add verification in StacBundleService._bundleFromResponse before
constructing the StacBundle, comparing the response checksum to a locally
computed hash of screens/themes and rejecting mismatches. If checksum validation
is intentionally handled elsewhere, document that design clearly in this path.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 356aed86-6eb8-47f6-ae9f-429fd5ea4c48
⛔ Files ignored due to path filters (1)
packages/stac_cli/pubspec.lockis excluded by!**/*.lock
📒 Files selected for processing (19)
packages/stac/lib/src/framework/stac.dartpackages/stac/lib/src/framework/stac_service.dartpackages/stac/lib/src/models/models.dartpackages/stac/lib/src/models/stac_bundle.dartpackages/stac/lib/src/models/stac_bundle.g.dartpackages/stac/lib/src/models/stac_bundle_config.dartpackages/stac/lib/src/services/services.dartpackages/stac/lib/src/services/stac_bundle_service.dartpackages/stac/lib/src/services/stac_bundle_store.dartpackages/stac/lib/src/services/stac_bundle_updater.dartpackages/stac/lib/src/services/stac_cloud.dartpackages/stac/test/models/stac_bundle_test.dartpackages/stac/test/services/stac_bundle_service_test.dartpackages/stac_cli/CHANGELOG.mdpackages/stac_cli/lib/src/commands/deploy_command.dartpackages/stac_cli/lib/src/services/build_service.dartpackages/stac_cli/lib/src/services/deploy_service.dartpackages/stac_cli/pubspec.yamlpackages/stac_cli/test/services/deploy_service_test.dart
| if (_bundleConfig.enabled) { | ||
| StacBundleUpdater.start(); | ||
| if (_bundleConfig.prefetchOnInit && options != null) { | ||
| unawaited(StacBundleService.sync()); | ||
| } | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
ast-grep outline packages/stac/lib/src/services/stac_bundle_service.dart
rg -n -B2 -A5 'static\s+Dio\s+(get\s+)?dio' packages/stac/lib/src/services/stac_bundle_service.dartRepository: StacDev/stac
Length of output: 220
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## relevant files\n'
git ls-files | rg 'packages/stac/lib/src/.*(stac_service\.dart|bundle|updater|network|service)'
printf '\n## locate bundle service/updater symbols\n'
rg -n 'class StacBundleService|class StacBundleUpdater|static .*dio|dio\s*=' packages/stac/lib/src
printf '\n## inspect initialize block\n'
sed -n '180,240p' packages/stac/lib/src/framework/stac_service.dart
printf '\n## inspect bundle-related implementations\n'
for f in $(git ls-files | rg 'packages/stac/lib/src/.*(bundle|updater|network|service).*\.dart'); do
echo
echo "### $f"
wc -l "$f"
doneRepository: StacDev/stac
Length of output: 5339
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## StacBundleService relevant sections\n'
sed -n '1,120p' packages/stac/lib/src/services/stac_bundle_service.dart
printf '\n---\n'
sed -n '120,240p' packages/stac/lib/src/services/stac_bundle_service.dart
printf '\n---\n'
sed -n '240,330p' packages/stac/lib/src/services/stac_bundle_service.dart
printf '\n## StacBundleUpdater relevant sections\n'
sed -n '1,180p' packages/stac/lib/src/services/stac_bundle_updater.dartRepository: StacDev/stac
Length of output: 13675
Propagate dio to bundle mode, and fail fast when options is missing. packages/stac/lib/src/framework/stac_service.dart:202-207
dio:only reachesStacNetworkService;StacBundleService.sync()still uses its own default client, so bundle requests won’t honor caller auth/interceptors/proxy settings.- When
bundleConfig.enabledis set withoutoptions,StacBundleUpdater.start()still runs and the first resume/poll sync throws from_projectIdoutside thetry, instead of surfacing as an init-time validation error.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/stac/lib/src/framework/stac_service.dart` around lines 202 - 207,
The bundle initialization path in StacService should use the caller’s dio and
validate options up front: pass the same dio used by StacNetworkService into the
bundle sync flow so StacBundleService.sync honors auth/interceptors/proxy
settings, and guard the _bundleConfig.enabled branch so
StacBundleUpdater.start() and prefetch only run when options is present. If
bundle mode is enabled without options, fail fast during initialization with a
clear validation error instead of letting the later _projectId access in
StacBundleService or the updater throw asynchronously.
…dater Stac.initialize runs before runApp in most apps, so WidgetsBinding.instance may not exist when bundle mode registers its lifecycle observer. Create the binding defensively (idempotent) instead of requiring every app to add WidgetsFlutterBinding.ensureInitialized before Stac.initialize.
What
Adds opt-in bundle mode: instead of fetching every screen/theme with its own request, apps download all artifacts as a single versioned bundle, cache it, and only re-download when the server has a newer version.
Client (
packages/stac)StacBundleConfigonStac.initialize(enabled: falseby default — zero behavior change unless opted in):seedAsset,baseUrl,checkOnResume,pollingInterval.StacBundleService: conditional sync (ETag/If-None-Match+?sincefallback) —304/204keep the cache,200swaps and persists; offline falls back to the cached bundle;updatesstream notifies hosts when a new version lands.StacBundleUpdater: re-checks on app-resume (default on) and on an optional foreground polling interval.CLI (
packages/stac_cli)stac deployis now atomic: one POST with all screens/themes + checksum; any failure exits non-zero with nothing partially applied. Logs artifact counts and payload size.--legacykeeps the old per-file behavior for one release.assets/stac_bundle.json) stamped with the server-authoritative version after every deploy, and warns if pubspec doesn't declare it.stac buildnow cleans stale outputs so deleted screens can't linger in.build.Tests
packages/stac), 8 deploy service tests (packages/stac_cli) — first tests in the CLI package (test: Write tests for stac_cli #459 start).dart analyzeclean in both packages.Notes
/bundlesendpoint (rolling out server-side).Summary by CodeRabbit
bundleConfigoption to initialize, sync, and serve screens/themes from a single versioned bundle (with resume checks and optional polling).--legacyto keep the previous upload flow.screens/themesto avoid redeploying stale JSON.