chore(#335): pin drift, commit pubspec.lock, guard the web asset version

Owner asked whether the web assets must be re-downloaded on every drift bump.
They do not - the version can be pinned - but doing that safely needed three
changes, because the failure mode is silent.

1. drift and drift_flutter are pinned EXACTLY (2.34.2 / 0.3.1), no caret. The
   committed web assets are built for a specific drift release, so a caret
   would let pub resolve a newer drift than the shipped binaries and break the
   web build with no signal at compile time.

2. pubspec.lock is now committed (removed from .gitignore). It was ignored,
   which for an application means resolution can differ between machines and
   CI. That undermines the pin: the assets are matched against the RESOLVED
   version, so resolution has to be reproducible. Owner-authorized, and it is
   a repo-wide change rather than a storage-only one.

3. New test asserting the shipped assets match the resolved drift version,
   plus that both files exist and the wasm really starts with the WebAssembly
   magic bytes. Verified in both directions: it passes when matched and fails
   loudly with re-download instructions when skewed.

Without (3) a stale asset compiles, deploys, and only fails in the user's
browser - the exact silent-failure class SAFELANE 6 forbids and that produced
#333 earlier.

Upgrading drift is now a deliberate step: bump the pin, re-download both
assets, update web/drift_assets.version. The test fails until you do.

flutter analyze clean, tests pass.
integration/290-306-335
Strycher 23 hours ago
parent 847a28df3e
commit c25b7ccbdf

1
.gitignore vendored

@ -30,7 +30,6 @@ migrate_working_dir/
.flutter-plugins-dependencies .flutter-plugins-dependencies
.pub-cache/ .pub-cache/
.pub/ .pub/
pubspec.lock
/build/ /build/
/coverage/ /coverage/
# fvm project files # fvm project files

File diff suppressed because it is too large Load Diff

@ -73,8 +73,12 @@ dependencies:
ml_dataframe: ^1.0.0 ml_dataframe: ^1.0.0
llamadart: '>=0.6.8 <0.7.0' llamadart: '>=0.6.8 <0.7.0'
flutter_langdetect: ^0.0.1 flutter_langdetect: ^0.0.1
drift: ^2.34.2 # PINNED (#335): web ships version-matched sqlite3.wasm + drift_worker.js
drift_flutter: ^0.3.1 # from the drift release. A caret here would let pub resolve a newer drift
# than the committed assets, breaking web silently. Bump deliberately, then
# re-download both assets. tool/check_drift_assets.dart enforces the match.
drift: 2.34.2
drift_flutter: 0.3.1
hooks: hooks:
user_defines: user_defines:

@ -0,0 +1,74 @@
import 'dart:io';
import 'package:flutter_test/flutter_test.dart';
/// Guards the one way the web build can break silently (#335).
///
/// drift on the web needs two binaries shipped in `web/`, and they must match
/// the resolved `drift` package. If someone bumps drift without re-downloading
/// them, nothing fails at build time: the app compiles, deploys, and then
/// fails in the browser. SAFELANE 6 says that must not be silent, so this
/// turns it into a red test with instructions.
///
/// Update `web/drift_assets.version` whenever the assets are re-downloaded.
void main() {
test('shipped drift web assets match the pinned drift version', () {
final root = Directory.current.path;
final lock = File('$root/pubspec.lock');
expect(
lock.existsSync(),
isTrue,
reason:
'pubspec.lock must be committed so dependency resolution is '
'reproducible; the drift web assets are matched against it.',
);
final resolved = RegExp(
r'^ drift:\n(?:.*\n)*? version: "([^"]+)"',
multiLine: true,
).firstMatch(lock.readAsStringSync())?.group(1);
expect(resolved, isNotNull, reason: 'drift not found in pubspec.lock');
final stamp = File('$root/web/drift_assets.version');
expect(
stamp.existsSync(),
isTrue,
reason:
'web/drift_assets.version is missing. It records which drift release '
'web/sqlite3.wasm and web/drift_worker.js came from.',
);
expect(
stamp.readAsStringSync().trim(),
resolved,
reason:
'\nDrift web assets are STALE.\n'
'pubspec.lock resolves drift $resolved, but the shipped assets are '
'from ${stamp.readAsStringSync().trim()}.\n'
'The web build would compile and then fail in the browser.\n\n'
'Fix:\n'
' gh release download drift-$resolved --repo simolus3/drift \\\n'
' --pattern drift_worker.js --pattern sqlite3.wasm --clobber\n'
' (run inside web/, then write $resolved into web/drift_assets.version)\n',
);
for (final name in ['sqlite3.wasm', 'drift_worker.js']) {
expect(
File('$root/web/$name').existsSync(),
isTrue,
reason: 'web/$name is missing; the web build would fail at runtime.',
);
}
// Cheap integrity check: the wasm must actually be WebAssembly.
final magic = File('$root/web/sqlite3.wasm').openSync().readSync(4);
expect(
magic,
orderedEquals([0x00, 0x61, 0x73, 0x6d]),
reason:
'web/sqlite3.wasm does not start with the WebAssembly magic '
'bytes (\\0asm) — the download is corrupt or is not a wasm file.',
);
});
}
Loading…
Cancel
Save

Powered by TurnKey Linux.