From 21c380595f3732d4c29a0fbfede1bd70ab33eaee Mon Sep 17 00:00:00 2001 From: ImBenji Date: Wed, 23 Sep 2026 18:53:41 +0100 Subject: [PATCH] Pin by commit in the docs, tags trip pub up Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_013F4NWNvYcdeSgqbWMT1VQ7 --- README.md | 20 +++++++++++++------- docs/local-development.md | 4 ++-- garage_ui/lib/select.dart | 21 ++++++++++++++++----- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 6b89322..ef5f127 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Everything here is MIT licensed, see [LICENSE](LICENSE). ## Install The packages arent on pub.dev. Git-depend on this repo, pick the package with -`path:`, and pin a tag: +`path:`, and pin the commit of a release tag: ```yaml dependencies: @@ -38,20 +38,26 @@ dependencies: git: url: https://git.imbenji.dev/IMBENJI.NET/Garage-SDKs.git path: garage_auth - ref: v0.1.0 + ref: b2692019197e60d44ddd3ecab4917caf71ff45c3 # v0.1.0 garage_entitlements: git: url: https://git.imbenji.dev/IMBENJI.NET/Garage-SDKs.git path: garage_entitlements - ref: v0.1.0 + ref: b2692019197e60d44ddd3ecab4917caf71ff45c3 # v0.1.0 ``` -Pin a tag, dont float `main`. `garage_entitlements` decides who gets the paid +Pin, dont float `main`. `garage_entitlements` decides who gets the paid features, and "it changed under us" is not a fun thing to debug. -`garage_entitlements` and `garage_iap` layer on `garage_auth`. You get it -transitively, but declare it too — you'll be constructing a `GarageAuth` -yourself anyway, and keep all of them on the same `ref`. +Use the tag's **full commit hash**, not the tag name. `garage_entitlements` and +`garage_iap` pull in `garage_auth` by relative path, which pub turns into a git +dep at the resolved commit hash — so if your app asks for `garage_auth` at +`v0.1.0`, pub sees two different sources for the same package and refuses to +resolve. Same hash on every garage package and it just works. Keep the tag name +in a comment so you can tell which release it is. + +You'll be constructing a `GarageAuth` yourself, so declare `garage_auth` +directly even when you only really want entitlements or iap. **Working on the SDKs at the same time as an app?** Point the app at a local checkout with a gitignored `pubspec_overrides.yaml`, so edits hot reload with diff --git a/docs/local-development.md b/docs/local-development.md index 9268996..b23fdaa 100644 --- a/docs/local-development.md +++ b/docs/local-development.md @@ -15,7 +15,7 @@ dependencies: git: url: https://git.imbenji.dev/IMBENJI.NET/Garage-SDKs.git path: garage_ui - ref: v0.1.0 + ref: b2692019197e60d44ddd3ecab4917caf71ff45c3 # v0.1.0 ``` Leave that alone. Clone this repo somewhere, then next to the app's @@ -109,7 +109,7 @@ Once the change works locally: 1. Commit and push it in this repo. 2. Tag a new version, e.g. `v0.1.1`, and push the tag. -3. In each app that should get it, bump `ref:` to the new tag. Do it on purpose, +3. In each app that should get it, bump `ref:` to the new tag's full commit hash. Do it on purpose, per app — dont float `main`. 4. Check the app builds **without** the override. Move it aside, resolve against the real tag, and analyze: diff --git a/garage_ui/lib/select.dart b/garage_ui/lib/select.dart index fe955a6..ff92205 100644 --- a/garage_ui/lib/select.dart +++ b/garage_ui/lib/select.dart @@ -585,7 +585,9 @@ class SelectPopup extends StatefulWidget { /// whether the search field is shown (only in builder mode). final bool enableSearch; - /// whether the list shrink-wraps (static lists do, builder lists scroll). + /// whether the rows are a plain Column (static lists) or a lazy ListView + /// (builder lists, which can be long). Both only take the height they + /// need - see [_listView]. final bool shrinkWrap; const SelectPopup({ @@ -657,9 +659,9 @@ class _SelectPopupState extends State> { // nothing to measure - asking a column how wide it wants to be and then // ignoring the answer is just a layout pass nobody reads. // - // [shrinkWrap] still decides the VERTICAL: a static list is a scrolling - // Column that takes only the height it needs, the builder path is a lazy - // ListView. + // [shrinkWrap] picks which list: a static one is a scrolling Column, the + // builder path is a lazy ListView. Either way the popup ends up as tall + // as its rows and no taller. return _popupSurface(theme, column); } @@ -729,10 +731,19 @@ class _SelectPopupState extends State> { ); } + // shrinkWrap regardless of [widget.shrinkWrap] - that flag is about which + // list we build, not how tall it ends up. A ListView left to expand eats + // every pixel of popupMaxHeight its handed, so a one-row "Search lines" + // popup opened a 312px wall of empty surface under its single row. + // + // This stays lazy: the incoming maxHeight is bounded (the overlay's + // ConstrainedBox, then Flexible), so the shrink wrapping viewport still + // only lays out as far as that extent and stops. Its the *unbounded* case + // that forces a full layout pass, and we never hand it one. return ListView.separated( controller: _scrollController, padding: EdgeInsets.all(inset), - shrinkWrap: widget.shrinkWrap, + shrinkWrap: true, itemCount: count, separatorBuilder: (context, _) => Gap(_popupRowGap(theme)), itemBuilder: (context, index) =>