diff --git a/.fvmrc b/.fvmrc
index 2983293..a99331c 100644
--- a/.fvmrc
+++ b/.fvmrc
@@ -1,4 +1,4 @@
{
- "flutter": "beta",
+ "flutter": "3.44.4",
"flavors": {}
-}
+}
\ No newline at end of file
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 100a175..e4f7bdf 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -17,8 +17,6 @@
# CI and config: repo owner only
/.github/ @chaudharydeepanshu
-/.gitmodules @chaudharydeepanshu
-/cellar @chaudharydeepanshu
/.githooks/ @chaudharydeepanshu
# Build + supply-chain machinery: repo owner only. Holds the commit-types list
diff --git a/.github/workflows/auto-close.yml b/.github/workflows/auto-close.yml
index 74d88da..d2ee100 100644
--- a/.github/workflows/auto-close.yml
+++ b/.github/workflows/auto-close.yml
@@ -24,7 +24,7 @@ jobs:
auto-close:
permissions:
issues: write # label, comment on, and close resolved issues
- uses: whuppi/ci/.github/workflows/auto-close.yml@v2.0.0
+ uses: whuppi/ci/.github/workflows/auto-close.yml@v2.0.4
with:
# Team slug, not a hardcoded person — membership is managed in the org
# (the same team CODEOWNERS uses), so a maintainer change never edits this.
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5c7e1b3..74b4f4d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -79,7 +79,7 @@ jobs:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- - uses: whuppi/ci/actions/make-target@v2.0.0
+ - uses: whuppi/ci/actions/make-target@v2.0.4
with:
make-target: format
@@ -95,7 +95,7 @@ jobs:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- - uses: whuppi/ci/actions/make-target@v2.0.0
+ - uses: whuppi/ci/actions/make-target@v2.0.4
with:
make-target: analyze
@@ -113,7 +113,7 @@ jobs:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- - uses: whuppi/ci/actions/make-target@v2.0.0
+ - uses: whuppi/ci/actions/make-target@v2.0.4
with:
make-target: analyze-floor
@@ -132,12 +132,12 @@ jobs:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- - uses: whuppi/ci/actions/make-target@v2.0.0
+ - uses: whuppi/ci/actions/make-target@v2.0.4
with:
make-target: platforms
test:
- name: Test guards
+ name: Package tests
needs: [inputs, changes]
if: needs.changes.outputs.code == 'true'
runs-on: ${{ needs.inputs.outputs.runner }}
@@ -148,12 +148,12 @@ jobs:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- - uses: whuppi/ci/actions/make-target@v2.0.0
+ - uses: whuppi/ci/actions/make-target@v2.0.4
with:
make-target: test
test-example-matrix:
- name: Unit tests (VM)
+ name: Example journeys (host VM)
needs: [inputs, changes]
if: needs.changes.outputs.code == 'true'
runs-on: ${{ needs.inputs.outputs.runner }}
@@ -164,7 +164,7 @@ jobs:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- - uses: whuppi/ci/actions/make-target@v2.0.0
+ - uses: whuppi/ci/actions/make-target@v2.0.4
with:
make-target: test-example-matrix
@@ -185,9 +185,8 @@ jobs:
ANALYZE: ${{ needs.analyze.result }}
FLOOR: ${{ needs.analyze-floor.result }}
PLATFORMS: ${{ needs.platforms.result }}
- GUARDS: ${{ needs.test-guards.result }}
- UNIT: ${{ needs.test-unit.result }}
- WEB: ${{ needs.test-web.result }}
+ PKG: ${{ needs.test.result }}
+ JOURNEYS: ${{ needs.test-example-matrix.result }}
run: |
# A failed (not skipped) changes job clears its outputs, so CODE
# arrives empty — which must NOT read as "doc-only". Gate on the job
@@ -200,8 +199,8 @@ jobs:
echo "Doc-only PR — CI gate passes"
exit 0
fi
- if [[ "$FORMAT" != "success" ]] || [[ "$ANALYZE" != "success" ]] || [[ "$FLOOR" != "success" ]] || [[ "$PLATFORMS" != "success" ]] || [[ "$GUARDS" != "success" ]] || [[ "$UNIT" != "success" ]] || [[ "$WEB" != "success" ]]; then
- echo "FAILED: format=$FORMAT analyze=$ANALYZE floor=$FLOOR platforms=$PLATFORMS guards=$GUARDS unit=$UNIT web=$WEB"
+ if [[ "$FORMAT" != "success" ]] || [[ "$ANALYZE" != "success" ]] || [[ "$FLOOR" != "success" ]] || [[ "$PLATFORMS" != "success" ]] || [[ "$PKG" != "success" ]] || [[ "$JOURNEYS" != "success" ]]; then
+ echo "FAILED: format=$FORMAT analyze=$ANALYZE floor=$FLOOR platforms=$PLATFORMS pkg=$PKG journeys=$JOURNEYS"
exit 1
fi
echo "All CI checks passed"
diff --git a/.github/workflows/debug-ssh.yml b/.github/workflows/debug-ssh.yml
index 68a9e65..7822348 100644
--- a/.github/workflows/debug-ssh.yml
+++ b/.github/workflows/debug-ssh.yml
@@ -43,7 +43,7 @@ jobs:
with:
persist-credentials: false
- - uses: whuppi/ci/actions/debug-ssh@v2.0.0
+ - uses: whuppi/ci/actions/debug-ssh@v2.0.4
- name: Keep alive (touch /tmp/stop to end)
shell: bash
diff --git a/.github/workflows/full-test.yml b/.github/workflows/full-test.yml
index 92ce204..81a854b 100644
--- a/.github/workflows/full-test.yml
+++ b/.github/workflows/full-test.yml
@@ -84,6 +84,11 @@ jobs:
matrix:
include:
+ # ── Package tests ──
+ - { name: "pkg: Linux (ubuntu-x64)", runner: ubuntu-24.04, make: test, timeout: 20 }
+ - { name: "pkg: macOS (macos-arm64)", runner: macos-14, make: test, timeout: 20 }
+ - { name: "pkg: Windows (win-x64)", runner: windows-2025-vs2026, make: test, timeout: 30 }
+
# ── Example journeys (host VM, in-memory) ──
- { name: "int: Journeys (ubuntu-x64)", runner: ubuntu-24.04, make: test-example-matrix, timeout: 20 }
- { name: "int: Journeys (macos-arm64) [P]", runner: macos-14, make: test-example-matrix, timeout: 20, portability: true }
@@ -122,7 +127,7 @@ jobs:
steps:
- name: Check filter
id: filter
- uses: whuppi/ci/actions/matrix-filter@v2.0.0
+ uses: whuppi/ci/actions/matrix-filter@v2.0.4
with:
name: ${{ matrix.name }}
filter: ${{ needs.inputs.outputs.filter }}
@@ -132,7 +137,7 @@ jobs:
if: steps.filter.outputs.match == 'true'
with:
persist-credentials: false
- - uses: whuppi/ci/actions/make-target@v2.0.0
+ - uses: whuppi/ci/actions/make-target@v2.0.4
if: steps.filter.outputs.match == 'true'
with:
make-target: ${{ matrix.make }}
diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml
index d310678..1e1611f 100644
--- a/.github/workflows/labels.yml
+++ b/.github/workflows/labels.yml
@@ -23,4 +23,4 @@ jobs:
permissions:
contents: read # checkout reads .github/labels.json
issues: write # labels are managed through the Issues API
- uses: whuppi/ci/.github/workflows/labels.yml@v2.0.0
+ uses: whuppi/ci/.github/workflows/labels.yml@v2.0.4
diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml
index 11644cb..9b4da8d 100644
--- a/.github/workflows/pr-checks.yml
+++ b/.github/workflows/pr-checks.yml
@@ -20,4 +20,4 @@ jobs:
checks:
permissions:
contents: read # the reusable jobs only checkout + lint, read-only
- uses: whuppi/ci/.github/workflows/pr-checks.yml@v2.0.0
+ uses: whuppi/ci/.github/workflows/pr-checks.yml@v2.0.4
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index e9996d6..efb468c 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -18,6 +18,11 @@ on:
default: 'dev'
type: choice
options: [dev, prod]
+ runner:
+ description: "Runner for infra jobs (gate, discover, publish)"
+ type: choice
+ options: [ubuntu-24.04, macos-14, windows-2025-vs2026]
+ default: ubuntu-24.04
permissions: {}
@@ -30,8 +35,9 @@ jobs:
release:
permissions:
contents: write # create the release/tag, edit notes, ephemeral publish commit
- uses: whuppi/ci/.github/workflows/release.yml@v2.0.0
+ uses: whuppi/ci/.github/workflows/release.yml@v2.0.4
with:
branch: ${{ inputs.branch || github.ref_name }}
+ runner: ${{ inputs.runner || 'ubuntu-24.04' }}
secrets:
pub-credentials: ${{ secrets.PUB_CREDENTIALS }}
diff --git a/.github/workflows/retry.yml b/.github/workflows/retry.yml
index 5c627d8..783fa9e 100644
--- a/.github/workflows/retry.yml
+++ b/.github/workflows/retry.yml
@@ -27,4 +27,4 @@ jobs:
retry:
permissions:
actions: write # gh run rerun re-runs the failed jobs of the run
- uses: whuppi/ci/.github/workflows/retry.yml@v2.0.0
+ uses: whuppi/ci/.github/workflows/retry.yml@v2.0.4
diff --git a/.github/workflows/triage.yml b/.github/workflows/triage.yml
index 3aa1b37..a7afcb4 100644
--- a/.github/workflows/triage.yml
+++ b/.github/workflows/triage.yml
@@ -29,4 +29,4 @@ jobs:
contents: read # labeler reads .github/labeler.yml + the PR file list
issues: write # assign maintainer on issue-opened events
pull-requests: write # apply labels, revoke ready-to-test, post notices
- uses: whuppi/ci/.github/workflows/triage.yml@v2.0.0
+ uses: whuppi/ci/.github/workflows/triage.yml@v2.0.4
diff --git a/.github/workflows/upgrade-check.yml b/.github/workflows/upgrade-check.yml
index 3f84fd2..7fd3a18 100644
--- a/.github/workflows/upgrade-check.yml
+++ b/.github/workflows/upgrade-check.yml
@@ -27,7 +27,7 @@ jobs:
permissions:
contents: write # pushes the chore/flutter-sdk + chore/lockfiles branches
pull-requests: write # opens / edits the upgrade PRs
- uses: whuppi/ci/.github/workflows/upgrade-check.yml@v2.0.0
+ uses: whuppi/ci/.github/workflows/upgrade-check.yml@v2.0.4
with:
branch: dev
diff --git a/.gitignore b/.gitignore
index a03e829..ecd1ad1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -321,3 +321,6 @@ pip-wheel-*
*.temp
*.orig
.sass-cache/
+
+# Local co-development override (points cellar at a sibling checkout)
+pubspec_overrides.yaml
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index 2e9c1c7..0000000
--- a/.gitmodules
+++ /dev/null
@@ -1,4 +0,0 @@
-[submodule "cellar"]
- path = cellar
- url = https://github.com/whuppi/cellar.git
- branch = dev
diff --git a/.pubignore b/.pubignore
index 42e627a..2d75ff0 100644
--- a/.pubignore
+++ b/.pubignore
@@ -6,3 +6,9 @@ tool/
Makefile
docs/
.githooks/
+
+# Banner: ship only the optimized *-web-min.webp the README embeds. The
+# heavy *-3x.png sources stay tracked in git (org-quality preservation)
+# but out of the published download — nothing references them on pub.dev.
+assets/cellar_flutter-banner-dark-3x.png
+assets/cellar_flutter-banner-light-3x.png
diff --git a/AGENTS.md b/AGENTS.md
index e683576..d60e455 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -29,7 +29,7 @@ Manual edits to this file will be overwritten on the next stamp.
## What this tool does
cellar_flutter is the Flutter front door for the pure-Dart `cellar`
-object-storage core (pinned as the `cellar/` git submodule). It holds ONLY
+object-storage core (a hosted dependency, version-locked). It holds ONLY
what needs the Flutter engine: `openCellar()`, which resolves the
platform storage roots via path_provider (behind a stub-default
conditional import — web builds never compile path_provider) and
@@ -82,8 +82,9 @@ When in doubt, read existing code in this repo and match it. Per-repo style cons
- **Only engine-bound code lives here.** Anything pure Dart — including
web code (`package:web` is SDK, not Flutter) — belongs in the
- `cellar` core (its own repo; pinned here as a submodule). This package exists because path_provider needs the
- Flutter plugin chain.
+ `cellar` core (its own repo; consumed from pub.dev at the locked
+ version). This package exists because path_provider needs the Flutter
+ plugin chain.
- **path_provider is imported ONLY in `flutter_roots_native.dart`**,
behind the conditional import in `flutter_roots.dart` — its API
returns dart:io types, which don't compile for web. Never import the
@@ -91,10 +92,11 @@ When in doubt, read existing code in this repo and match it. Per-repo style cons
- **`openCellar` mirrors the core's `Cellar` constructor parameter for
parameter.** A new core parameter means the same parameter here,
forwarded, in the same change.
-- **The core is a pinned submodule (`cellar/`, pubspec path dep).**
- Core changes land in whuppi/cellar first, then the pin bumps here —
- commit inside the submodule, push there, `git add cellar` here. Run
- `make check` after any bump.
+- **The core is a hosted dependency; `pubspec.lock` is the pin.**
+ Core changes land and release in whuppi/cellar first; the Dependabot
+ bump PR here certifies them against the matrix. For local
+ co-development use a gitignored `pubspec_overrides.yaml`
+ (`cellar: {path: ../cellar}`).
- **Tests fake path_provider at `PathProviderPlatform.instance`** (the
platform-interface seam) and assert storage actually lands under the
fake roots — never just that calls succeed.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 59b8ce2..e75de26 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -59,3 +59,7 @@ CONTENT RULES (never change)
-->
+
+## 1.0.0
+
+First stable release — cellar for Flutter, zero-setup storage on all six platforms.
diff --git a/CHANGELOG.pre.md b/CHANGELOG.pre.md
index 8c12681..06edc0b 100644
--- a/CHANGELOG.pre.md
+++ b/CHANGELOG.pre.md
@@ -62,9 +62,4 @@ CONTENT RULES (never change)
## 1.0.0-dev.0
-First release — the Flutter front door for `cellar`.
-
-- **API:** `openCellar()` — one call resolves the platform storage roots via path_provider, opens, and returns a ready `Cellar`; mirrors the core constructor's parameters
-- **Re-export:** the full `cellar` core API from one import — apps never also depend on `cellar`
-- **Platforms:** iOS, Android, macOS, Windows, Linux, web — the path_provider glue sits behind a stub-default conditional import, so web builds never compile it
-- **Example:** the seven-tab demo app with device-matrix UI journeys and a per-platform integration smoke
+First release.
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 6012b4d..aa942fe 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -7,7 +7,7 @@ Contributions are welcome.
## Setup
```bash
-git clone --recursive https://github.com/whuppi/cellar_flutter.git
+git clone https://github.com/whuppi/cellar_flutter.git
cd cellar_flutter
make hooks # activates commit-msg + pre-commit (run once)
fvm install # downloads the SDK version pinned in .fvmrc
@@ -16,8 +16,15 @@ fvm flutter test
```
**Requires:** [FVM](https://fvm.app) (`.fvmrc` pins the exact Flutter
-version). The `cellar` core is a pinned git submodule at `cellar/` —
-clone with `--recursive` (or run `git submodule update --init` after).
+version). The `cellar` core resolves from pub.dev at the locked version.
+Co-developing against a local core checkout? Drop a gitignored
+`pubspec_overrides.yaml` next to each pubspec:
+
+```yaml
+dependency_overrides:
+ cellar:
+ path: ../cellar # ../../cellar from example/
+```
**Without FVM:** all Makefile commands accept `DART` and `FLUTTER`
overrides:
@@ -41,9 +48,9 @@ Runs `format` + `analyze` (package + example) + `analyze-floor`
device profile, on the host VM). Must pass. Don't suppress with
`// ignore:` — fix the underlying issue.
-Touching the `cellar` core too? Its changes land in its own repo
-first; then bump the submodule pin here (commit inside `cellar/`, push
-there, `git add cellar` here) and run `make check`.
+Touching the `cellar` core too? Its changes land in its own repo and
+release first; the Dependabot bump PR here runs the full matrix against
+that release.
---
diff --git a/Makefile b/Makefile
index 4d51a60..4046206 100644
--- a/Makefile
+++ b/Makefile
@@ -59,8 +59,8 @@ analyze-floor:
# pana's platform attribution — all six targets must survive the
# conditional-import walk (the stub default is what makes web attribute).
-# The cellar core is a submodule INSIDE the repo, so the gate's snapshot
-# carries it and the `path: cellar` dep resolves — no wrapper needed.
+# The cellar core is a published hosted dep, so pana resolves it exactly
+# as pub.dev will.
platforms:
@DART="$(DART)" EXPECTED_PLATFORMS="android ios linux macos windows web" bash tool/platforms_gate.sh
diff --git a/README.md b/README.md
index 158f6d6..e33da5c 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,20 @@
+
+
+
+
+
+
+
+
+
@@ -6,9 +23,9 @@
-Object storage for Flutter apps. One `openCellar()` call gives you a ready-to-use store on iOS, Android, macOS, Windows, Linux, and web — real files on native, IndexedDB in the browser, identical API everywhere.
+File and data storage for Flutter with zero setup. One call gives you a ready-to-use store on iOS, Android, macOS, Windows, Linux, and web — real files on native, IndexedDB in the browser, identical API everywhere.
-Named partitions with self-cleaning lifecycle rules, tenant scoping, bring-your-own encryption, streaming I/O that never buffers a whole object, atomic writes, typed errors, and platform-local handles for FFI and browser APIs. No `kIsWeb`, no platform branches, no setup — anywhere in your code.
+Partitions with self-cleaning rules, per-user scoping, bring-your-own encryption, streaming that never buffers a whole file, atomic writes, typed errors — and a real path or URL whenever a native or browser API needs one. No `kIsWeb`, no platform branches, anywhere in your code.
> like it? a [⭐ star](https://github.com/whuppi/cellar_flutter) or [👍 like](https://pub.dev/packages/cellar_flutter) is the entire marketing budget. [Bugs & features →](https://github.com/whuppi/cellar_flutter/issues)
@@ -46,7 +63,7 @@ dependencies:
cellar_flutter:
```
-That's the whole setup. No permissions, no manifest entries, no entitlements, no per-platform Dart — storage lands in your app's private area, which every OS grants for free.
+No permissions, no manifest entries, no per-platform Dart — storage lands in your app's private area, which every OS grants for free.
---
@@ -67,7 +84,7 @@ await cellar.delete('notes/hello');
await cellar.close(); // app shutdown, profile switch, etc.
```
-`openCellar` is byte-identical on all six platforms. That's the shape of every call after it too: a `/`-separated key in, bytes or metadata out — `write`, `read`, `head`, `list`, `copy`, `materialize`, same shape, a different verb.
+`openCellar` is byte-identical on all six platforms. That's the shape of every call after it: a `/`-separated key in, bytes or metadata out — `write`, `read`, `head`, `list`, `copy`, `materialize`.
🧩 what openCellar actually does
@@ -271,11 +288,11 @@ Rule of thumb: **Flutter app → this package. Anything else → `cellar`.** You
## Not in the box
-- **Queries, key-value prefs, sync, user-visible files** — the core's [Not in the box](https://github.com/whuppi/cellar#not-in-the-box) covers what cellar deliberately isn't, and what to reach for instead.
-- **Widgets.** This package is storage, not UI — nothing here draws. Pair `materialize` with your image/video widgets.
-- **Permission flows.** Cellar's locations need no permissions on any platform, so there's nothing to request.
+- **Queries, key-value prefs, sync, user-visible files** — the core's [Not in the box](https://github.com/whuppi/cellar#not-in-the-box) covers what cellar deliberately isn't, with a reasoned WONT_DO row each.
+- **Widgets** — this package is storage, not UI; pair `materialize` with your image/video widgets.
+- **Permission flows** — cellar's locations need none on any platform.
-Missing something you need? [Open an issue](https://github.com/whuppi/cellar_flutter/issues); it's how we gauge demand.
+Missing something? [Open an issue](https://github.com/whuppi/cellar_flutter/issues).
---
@@ -293,7 +310,7 @@ The [example app](example/) exercises every capability in one file — seven tab
| [Capabilities](docs/CAPABILITY_ROADMAP.md) | Status per capability |
| [Updating](docs/UPDATING.md) | Maintenance recipes and the pinned-behavior watchlist |
| [Contributing](CONTRIBUTING.md) | Setup, PR workflow, keeping openCellar in lockstep with the core |
-| [cellar's docs](https://github.com/whuppi/cellar/tree/dev/docs) | The engine's architecture, durability mechanics, roadmap |
+| [cellar's docs](https://github.com/whuppi/cellar/tree/v1.0.0/docs) | The engine's architecture, durability mechanics, roadmap |
---
diff --git a/SECURITY.md b/SECURITY.md
index b2b5bbe..ff218a9 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -1,6 +1,6 @@
# Security Policy
-Covers `cellar_flutter` — the Flutter front door. The storage engine itself (encryption seam, tenant scoping, key grammar, durability) is the `cellar` core; its policy lives at [whuppi/cellar](https://github.com/whuppi/cellar/blob/dev/SECURITY.md). The `cellar/` directory here is a pinned submodule of it — engine reports go there.
+Covers `cellar_flutter` — the Flutter front door. The storage engine itself (encryption seam, tenant scoping, key grammar, durability) is the `cellar` core, consumed from pub.dev at the locked version; its policy lives at [whuppi/cellar](https://github.com/whuppi/cellar/blob/v1.0.0/SECURITY.md) — engine reports go there.
## Reporting a vulnerability
@@ -10,7 +10,7 @@ Report privately via [GitHub Security Advisories](https://github.com/whuppi/cell
- **Root resolution landing outside the app's private area** — `openCellar` promises storage under the OS-granted support/cache directories. If the resolution ever hands the core a directory outside the app sandbox (a path-join bug, a platform-channel value used unvalidated), that's a security report.
-- **The re-export lying about the core** — this package re-exports `package:cellar` wholesale. A packaging or pinning mistake that ships a different core than the one named in the release notes (stale submodule, wrong version constraint at publish) is in scope here, because this repo owns the pin.
+- **The re-export lying about the core** — this package re-exports `package:cellar` wholesale. A packaging mistake that ships against a different core than the release notes name (wrong version constraint at publish) is in scope here, because this repo owns the constraint.
## What's NOT in scope
@@ -22,7 +22,7 @@ Report privately via [GitHub Security Advisories](https://github.com/whuppi/cell
## Operational notes (known, accepted)
-- **The core rides in as a pinned submodule** — the pin is bumped deliberately per release, so a core security fix reaches this package one pin-bump later, not automatically. The release flow pins the published `cellar` version range instead, with the same property.
+- **The core version is locked, not floating** — a core security fix reaches this package one dependency bump later, not automatically; the caret range lets consumer apps pick the fix up immediately.
## Response
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 6e393c7..9aec4e8 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -1,10 +1,6 @@
include: package:flutter_lints/flutter.yaml
analyzer:
- exclude:
- # The pinned cellar core submodule — its own repo analyzes it; here
- # it's vendored source (same treatment as pdf_manipulator's vendor/).
- - 'cellar/**'
language:
strict-casts: true
strict-inference: true
diff --git a/assets/cellar_flutter-banner-dark-3x.png b/assets/cellar_flutter-banner-dark-3x.png
new file mode 100644
index 0000000..164a790
Binary files /dev/null and b/assets/cellar_flutter-banner-dark-3x.png differ
diff --git a/assets/cellar_flutter-banner-dark-web-min.webp b/assets/cellar_flutter-banner-dark-web-min.webp
new file mode 100644
index 0000000..db295e9
Binary files /dev/null and b/assets/cellar_flutter-banner-dark-web-min.webp differ
diff --git a/assets/cellar_flutter-banner-light-3x.png b/assets/cellar_flutter-banner-light-3x.png
new file mode 100644
index 0000000..c337141
Binary files /dev/null and b/assets/cellar_flutter-banner-light-3x.png differ
diff --git a/assets/cellar_flutter-banner-light-web-min.webp b/assets/cellar_flutter-banner-light-web-min.webp
new file mode 100644
index 0000000..db11527
Binary files /dev/null and b/assets/cellar_flutter-banner-light-web-min.webp differ
diff --git a/cellar b/cellar
deleted file mode 160000
index 54bf7bf..0000000
--- a/cellar
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 54bf7bf2a3d9c6a70336b6095154a0782a0d2081
diff --git a/deploy/.deploy/secrets.json.example b/deploy/.deploy/secrets.json.example
new file mode 100644
index 0000000..7e171c3
--- /dev/null
+++ b/deploy/.deploy/secrets.json.example
@@ -0,0 +1,5 @@
+{
+ "pub_dev": {
+ "credentials_json": "FULL_CONTENTS_OF_PUB_CREDENTIALS_JSON"
+ }
+}
diff --git a/deploy/.deploy/secrets.sh b/deploy/.deploy/secrets.sh
new file mode 100755
index 0000000..9253153
--- /dev/null
+++ b/deploy/.deploy/secrets.sh
@@ -0,0 +1,301 @@
+#!/bin/bash
+# ============================================================================
+# cellar_flutter secrets manager
+#
+# Manages secrets for whuppi/cellar_flutter ONLY.
+# Bitwarden Secrets Manager is the source of truth.
+# GitHub Environments are CI-accessible copies.
+#
+# Usage:
+# ./secrets.sh set / Store in Bitwarden + push to GitHub
+# ./secrets.sh get / Read from Bitwarden
+# ./secrets.sh list List secret names for an environment
+# ./secrets.sh dump Show values (careful!)
+# ./secrets.sh upload Push all from Bitwarden → GitHub
+# ./secrets.sh rm / Delete from Bitwarden + GitHub
+# ============================================================================
+set -e
+
+REPO="whuppi/cellar_flutter"
+BWS="${HOME}/bin/bws --color no"
+BW_PREFIX="cellar_flutter"
+export BWS_SERVER_URL="${BWS_SERVER_URL:-https://vault.bitwarden.eu}"
+
+# ── Auth ─────────────────────────────────────────────────────────────────────
+
+if [ -z "$BWS_ACCESS_TOKEN" ]; then
+ BWS_ACCESS_TOKEN=$(security find-generic-password -a "whuppi" -s "BWS_ACCESS_TOKEN" -w 2>/dev/null || true)
+ export BWS_ACCESS_TOKEN
+fi
+
+if [ -z "$BWS_ACCESS_TOKEN" ]; then
+ echo "Not authenticated. Run: bws-auth"
+ exit 1
+fi
+
+if ! $BWS project list > /dev/null 2>&1; then
+ echo "Bitwarden auth failed. Run: bws-auth"
+ exit 1
+fi
+
+# ── Project ID ───────────────────────────────────────────────────────────────
+
+PROJECT_ID=$($BWS project list 2>/dev/null | python3 -c "
+import sys,json
+for p in json.load(sys.stdin):
+ if p['name'] == 'whuppi-infra':
+ print(p['id'])
+ break
+")
+
+# ── Helpers ──────────────────────────────────────────────────────────────────
+
+bws_get() {
+ $BWS secret list 2>/dev/null | python3 -c "
+import sys,json
+for s in json.load(sys.stdin):
+ if s['key'] == '$1':
+ print(s['value'])
+ sys.exit(0)
+" 2>/dev/null
+}
+
+bws_get_id() {
+ $BWS secret list 2>/dev/null | python3 -c "
+import sys,json
+for s in json.load(sys.stdin):
+ if s['key'] == '$1':
+ print(s['id'])
+ sys.exit(0)
+" 2>/dev/null
+}
+
+bws_set() {
+ local key="$1" value="$2"
+ local existing_id
+ existing_id=$(bws_get_id "$key")
+ if [ -n "$existing_id" ]; then
+ $BWS secret edit "$existing_id" --key "$key" --value "$value" > /dev/null 2>&1
+ else
+ $BWS secret create "$key" "$value" "$PROJECT_ID" > /dev/null 2>&1
+ fi
+}
+
+validate_env() {
+ case "$1" in
+ dev|prod) return 0 ;;
+ *) echo "Invalid env: $1 (use dev or prod)"; exit 1 ;;
+ esac
+}
+
+# ── Commands ─────────────────────────────────────────────────────────────────
+
+cmd_set() {
+ local path="$1" value="$2"
+ if [ -z "$path" ] || [ -z "$value" ]; then
+ echo "Usage: ./secrets.sh set /"
+ echo "Example: ./secrets.sh set prod/PUB_CREDENTIALS '{...}'"
+ exit 1
+ fi
+
+ local env key
+ env=$(echo "$path" | cut -d/ -f1)
+ key=$(echo "$path" | cut -d/ -f2-)
+ validate_env "$env"
+
+ local bw_key="$BW_PREFIX/$env/$key"
+ bws_set "$bw_key" "$value"
+ echo "✓ Bitwarden: $bw_key"
+
+ local ghn
+ ghn="$key"
+ gh secret set "$ghn" --env "$env" --body "$value" --repo "$REPO"
+ echo "✓ GitHub: $REPO → $env → $ghn"
+}
+
+# Deletes from both backends, scoped to THIS repo only: GitHub by --repo/--env,
+# Bitwarden by the "$BW_PREFIX/$env/" key prefix — it can't reach another repo's
+# secrets. Idempotent: a missing secret is reported, not an error.
+cmd_rm() {
+ local path="$1"
+ if [ -z "$path" ]; then
+ echo "Usage: ./secrets.sh rm /"
+ echo "Deletes the secret from Bitwarden AND this repo's GitHub environment."
+ exit 1
+ fi
+
+ local env key
+ env=$(echo "$path" | cut -d/ -f1)
+ key=$(echo "$path" | cut -d/ -f2-)
+ validate_env "$env"
+
+ local ghn
+ ghn="$key"
+ if gh secret list --env "$env" --repo "$REPO" 2>/dev/null | grep -qE "^${ghn}[[:space:]]"; then
+ if gh secret delete "$ghn" --env "$env" --repo "$REPO" 2>/dev/null; then
+ echo "✓ GitHub: $REPO → $env → $ghn (deleted)"
+ else
+ echo "✗ GitHub: $REPO → $env → $ghn (delete FAILED — still present)"
+ exit 1
+ fi
+ else
+ echo "· GitHub: $REPO → $env → $ghn (not present)"
+ fi
+
+ local bw_key="$BW_PREFIX/$env/$key"
+ local id
+ id=$(bws_get_id "$bw_key")
+ if [ -n "$id" ]; then
+ if $BWS secret delete "$id" > /dev/null 2>&1; then
+ echo "✓ Bitwarden: $bw_key (deleted)"
+ else
+ echo "✗ Bitwarden: $bw_key (delete failed)"
+ exit 1
+ fi
+ else
+ echo "· Bitwarden: $bw_key (not present)"
+ fi
+}
+
+cmd_get() {
+ local path="$1"
+ if [ -z "$path" ]; then
+ echo "Usage: ./secrets.sh get /"
+ exit 1
+ fi
+
+ local env key
+ env=$(echo "$path" | cut -d/ -f1)
+ key=$(echo "$path" | cut -d/ -f2-)
+ validate_env "$env"
+
+ local value
+ value=$(bws_get "$BW_PREFIX/$env/$key")
+ if [ -n "$value" ]; then
+ echo "$value"
+ else
+ echo "(not found: $BW_PREFIX/$env/$key)"
+ exit 1
+ fi
+}
+
+cmd_list() {
+ local env="$1"
+ if [ -z "$env" ]; then
+ echo "Usage: ./secrets.sh list "
+ exit 1
+ fi
+ validate_env "$env"
+
+ $BWS secret list 2>/dev/null | python3 -c "
+import sys,json
+secrets = json.load(sys.stdin)
+prefix = '$BW_PREFIX/$env/'
+matched = sorted([s['key'].replace(prefix, '') for s in secrets if s['key'].startswith(prefix)])
+if not matched:
+ print('No secrets found for: $BW_PREFIX/$env')
+else:
+ for name in matched:
+ print(f' {name}')
+ print(f'\n {len(matched)} secrets')
+"
+}
+
+cmd_dump() {
+ local env="$1"
+ if [ -z "$env" ]; then
+ echo "Usage: ./secrets.sh dump "
+ echo "⚠️ Shows secret VALUES."
+ exit 1
+ fi
+ validate_env "$env"
+
+ $BWS secret list 2>/dev/null | python3 -c "
+import sys,json
+secrets = json.load(sys.stdin)
+prefix = '$BW_PREFIX/$env/'
+matched = sorted([(s['key'].replace(prefix, ''), s['value']) for s in secrets if s['key'].startswith(prefix)])
+if not matched:
+ print('No secrets found for: $BW_PREFIX/$env')
+else:
+ for name, value in matched:
+ display = value if len(value) < 80 else value[:40] + '...[truncated]'
+ print(f' {name}={display}')
+"
+}
+
+cmd_upload() {
+ local target="$1"
+ if [ -z "$target" ]; then
+ echo "Usage: ./secrets.sh upload "
+ exit 1
+ fi
+
+ if [ "$target" = "all" ]; then
+ upload_env "dev"
+ upload_env "prod"
+ else
+ validate_env "$target"
+ upload_env "$target"
+ fi
+
+ echo ""
+ echo "=== Upload complete ==="
+}
+
+upload_env() {
+ local env="$1"
+ echo ""
+ echo "=== $BW_PREFIX/$env → GitHub ==="
+
+ gh api "repos/$REPO/environments/$env" -X PUT > /dev/null 2>&1
+
+ $BWS secret list 2>/dev/null | python3 -c "
+import sys,json
+secrets = json.load(sys.stdin)
+prefix = '$BW_PREFIX/$env/'
+for s in secrets:
+ if s['key'].startswith(prefix):
+ name = s['key'].replace(prefix, '')
+ print(f'{name}\t{s[\"value\"]}')
+" | while IFS=$'\t' read -r key value; do
+ local ghn
+ ghn="$key"
+ if gh secret set "$ghn" --env "$env" --body "$value" --repo "$REPO" 2>/dev/null; then
+ echo " ✓ $ghn"
+ else
+ echo " ✗ $ghn (failed)"
+ fi
+ done
+
+ echo "Done: $env"
+}
+
+# ── Dispatch ─────────────────────────────────────────────────────────────────
+
+case "${1:-}" in
+ set) cmd_set "$2" "$3" ;;
+ get) cmd_get "$2" ;;
+ list) cmd_list "$2" ;;
+ dump) cmd_dump "$2" ;;
+ upload) cmd_upload "$2" ;;
+ rm) cmd_rm "$2" ;;
+ *)
+ echo "cellar_flutter secrets manager"
+ echo ""
+ echo "Usage:"
+ echo " ./secrets.sh set / Store in Bitwarden + GitHub"
+ echo " ./secrets.sh get / Read from Bitwarden"
+ echo " ./secrets.sh list List secret names"
+ echo " ./secrets.sh dump Show values (careful!)"
+ echo " ./secrets.sh upload Push Bitwarden → GitHub"
+ echo " ./secrets.sh rm / Delete from Bitwarden + GitHub"
+ echo ""
+ echo "Examples:"
+ echo " ./secrets.sh set prod/PUB_CREDENTIALS '{\"accessToken\":\"...\",\"refreshToken\":\"...\"}'"
+ echo " ./secrets.sh get prod/PUB_CREDENTIALS"
+ echo " ./secrets.sh list prod"
+ echo " ./secrets.sh upload all"
+ echo " ./secrets.sh rm prod/OLD_KEY"
+ ;;
+esac
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md
index c4e80c8..d3372d4 100644
--- a/docs/ARCHITECTURE.md
+++ b/docs/ARCHITECTURE.md
@@ -4,7 +4,7 @@ How the package is wired. One call (`openCellar`), one conditional
import, one re-export of the core. For capability status see
[`CAPABILITY_ROADMAP.md`](CAPABILITY_ROADMAP.md); for maintenance
recipes see [`UPDATING.md`](UPDATING.md); for the engine itself see the
-core's docs at [`../cellar/docs/`](../cellar/docs/).
+[core's docs](https://github.com/whuppi/cellar/tree/v1.0.0/docs).
---
diff --git a/docs/CAPABILITY_ROADMAP.md b/docs/CAPABILITY_ROADMAP.md
index b75ff02..689a1ed 100644
--- a/docs/CAPABILITY_ROADMAP.md
+++ b/docs/CAPABILITY_ROADMAP.md
@@ -1,8 +1,8 @@
# Capability Roadmap
Statuses: **DONE** · **BUILDING** · **PLANNED** · **WONT_DO** (with
-reason). The core's capabilities live in
-[`cellar/docs/CAPABILITY_ROADMAP.md`](../cellar/docs/CAPABILITY_ROADMAP.md).
+reason). The core's capabilities live in the
+[cellar roadmap](https://github.com/whuppi/cellar/blob/v1.0.0/docs/CAPABILITY_ROADMAP.md).
| Capability | Status | Notes |
|---|---|---|
@@ -11,8 +11,8 @@ reason). The core's capabilities live in
| Stub-default conditional import (web never compiles path_provider) | DONE | |
| Package tests (fake platform seam, roots proven used) | DONE | |
| Example app + journeys + per-platform smoke | DONE | Lives in `example/` |
-| Full gate set (analyze / analyze-floor / lint-shell / pana platforms / verify-web dart2js+wasm) | DONE | Stock platforms gate — the `cellar/` submodule travels inside pana's snapshot, so the path dep resolves; verdict 6/6 platforms |
+| Full gate set (analyze / analyze-floor / lint-shell / pana platforms / verify-web dart2js+wasm) | DONE | Stock gates over the hosted core dep; pana verdict 6/6 platforms |
| CI via the shared workflow repo | BUILDING | Stock single-package callers (fast PR gate + label-triggered full-test with the example legs + release lanes); first real run at repo go-live |
-| Repo go-live (GitHub whuppi/cellar_flutter, submodule URL flip, branch protection) | DONE | Live with device_io-parity settings; `.gitmodules` points at github.com/whuppi/cellar and a fresh `--recursive` clone resolves it |
-| Publish to pub.dev | PLANNED | Maintainer-gated; release flow swaps the submodule path dep to the published `cellar` version |
+| Repo go-live (GitHub whuppi/cellar_flutter, branch protection, environments) | DONE | Live with device_io-parity settings |
+| Publish to pub.dev | PLANNED | Maintainer-gated. The core is a hosted dep (`^1.0.0-dev.0`, lock-certified) — no release-time rewriting needed; cellar releases first when a change spans both |
| Extra Flutter sugar (lifecycle widgets, provider glue) | WONT_DO | The package stays a front door; app-state patterns belong to apps |
diff --git a/docs/UPDATING.md b/docs/UPDATING.md
index 7d72144..ea3b11c 100644
--- a/docs/UPDATING.md
+++ b/docs/UPDATING.md
@@ -18,21 +18,24 @@ Mirror the core's `Cellar` default constructor exactly — openCellar is
a pass-through plus roots. New core constructor parameter → same
parameter here, forwarded. Test: the package test constructs with it.
-## S2 — Bump the core pin
+## S2 — Bump the core
-`cellar` is a pinned git submodule at `cellar/` (pubspec path dep into
-it). To pick up core changes: land them in whuppi/cellar first, then
-`cd cellar && git fetch && git checkout `, `cd .. && git add
-cellar`, commit, and run `make check`. The submodule-commit trap
-applies (workspace rule): never leave the pin pointing at a commit
-that isn't pushed in the core repo.
+`cellar` is a hosted dependency; `pubspec.lock` certifies the exact
+version CI ran against. When cellar releases, Dependabot opens the bump
+PR — the matrix on that PR is the certification. The bump also updates
+the version-tagged core doc links (README, SECURITY, docs/) — the
+`core_link_pin` test fails the PR otherwise. To test against
+UNRELEASED core changes, open a throwaway PR overriding the dep with a
+git ref (`cellar: {git: {url: ../cellar.git, ref: dev}}`-style) — read
+the verdict, close unmerged.
## S3 — Release
Same two-lane changelog model as the core (`CHANGELOG.pre.md` dev /
-`CHANGELOG.md` stable), stamped by the release tooling when the repo
-opens. The packages version independently; this one constrains its
-`cellar` dependency to the compatible range at publish time.
+`CHANGELOG.md` stable). The packages version independently — the
+pubspec's caret floor names the oldest core this release certifies,
+and the lock names the exact one CI ran against. Release cellar first
+when a change spans both; the bump PR (S2) precedes the release here.
---
diff --git a/example/pubspec.lock b/example/pubspec.lock
index 9c24e44..2f8d767 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -28,10 +28,11 @@ packages:
cellar:
dependency: "direct main"
description:
- path: "../cellar"
- relative: true
- source: path
- version: "0.0.0"
+ name: cellar
+ sha256: aca21eb6f772224c7dd08d8849b4b09bc1e14c6c2ef762958ca4636bdb057a66
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.0"
cellar_example_test_support:
dependency: "direct dev"
description:
@@ -219,10 +220,10 @@ packages:
dependency: transitive
description:
name: matcher
- sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6"
+ sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
url: "https://pub.dev"
source: hosted
- version: "0.12.18"
+ version: "0.12.19"
material_color_utilities:
dependency: transitive
description:
@@ -235,10 +236,10 @@ packages:
dependency: transitive
description:
name: meta
- sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
+ sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
url: "https://pub.dev"
source: hosted
- version: "1.17.0"
+ version: "1.18.0"
objective_c:
dependency: transitive
description:
@@ -299,10 +300,10 @@ packages:
dependency: transitive
description:
name: path_provider_platform_interface
- sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
+ sha256: "484838772624c3a4b94f1e44a3e19897fee738f2d5c4ce448443b0417f7c9dda"
url: "https://pub.dev"
source: hosted
- version: "2.1.2"
+ version: "2.1.3"
path_provider_windows:
dependency: transitive
description:
@@ -408,10 +409,10 @@ packages:
dependency: transitive
description:
name: test_api
- sha256: "19a78f63e83d3a61f00826d09bc2f60e191bf3504183c001262be6ac75589fb8"
+ sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
url: "https://pub.dev"
source: hosted
- version: "0.7.8"
+ version: "0.7.11"
typed_data:
dependency: transitive
description:
@@ -432,10 +433,10 @@ packages:
dependency: transitive
description:
name: vm_service
- sha256: "046d3928e16fa4dc46e8350415661755ab759d9fc97fc21b5ab295f71e4f0499"
+ sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360"
url: "https://pub.dev"
source: hosted
- version: "15.1.0"
+ version: "15.2.0"
web:
dependency: transitive
description:
@@ -469,5 +470,5 @@ packages:
source: hosted
version: "3.1.3"
sdks:
- dart: ">=3.11.0-296.3.beta <4.0.0"
+ dart: ">=3.11.0 <4.0.0"
flutter: ">=3.38.4"
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index 8176b5c..ddc435a 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -19,8 +19,7 @@ dependencies:
path: ../
# Direct dep for the low-level kit imports (EncryptedBackend, the
# BYO seams) the Encrypt/Custom tabs demonstrate.
- cellar:
- path: ../cellar
+ cellar: ^1.0.0
dev_dependencies:
flutter_test:
diff --git a/pubspec.lock b/pubspec.lock
index b2e4e6a..1f5b81e 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -28,10 +28,11 @@ packages:
cellar:
dependency: "direct main"
description:
- path: cellar
- relative: true
- source: path
- version: "0.0.0"
+ name: cellar
+ sha256: aca21eb6f772224c7dd08d8849b4b09bc1e14c6c2ef762958ca4636bdb057a66
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.0"
characters:
dependency: transitive
description:
@@ -174,10 +175,10 @@ packages:
dependency: transitive
description:
name: matcher
- sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6"
+ sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
url: "https://pub.dev"
source: hosted
- version: "0.12.18"
+ version: "0.12.19"
material_color_utilities:
dependency: transitive
description:
@@ -190,10 +191,10 @@ packages:
dependency: transitive
description:
name: meta
- sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
+ sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
url: "https://pub.dev"
source: hosted
- version: "1.17.0"
+ version: "1.18.0"
objective_c:
dependency: transitive
description:
@@ -347,10 +348,10 @@ packages:
dependency: transitive
description:
name: test_api
- sha256: "19a78f63e83d3a61f00826d09bc2f60e191bf3504183c001262be6ac75589fb8"
+ sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
url: "https://pub.dev"
source: hosted
- version: "0.7.8"
+ version: "0.7.11"
typed_data:
dependency: transitive
description:
@@ -400,5 +401,5 @@ packages:
source: hosted
version: "3.1.3"
sdks:
- dart: ">=3.11.0-0 <4.0.0"
+ dart: ">=3.11.0 <4.0.0"
flutter: ">=3.38.4"
diff --git a/pubspec.yaml b/pubspec.yaml
index 47ac493..bf6b20c 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,10 +1,9 @@
name: cellar_flutter
version: 0.0.0
description: >-
- The Flutter front door for cellar: openCellar() resolves platform
- storage roots via path_provider and returns an opened, ready-to-use
- Cellar — one identical line on iOS, Android, macOS, Windows, Linux,
- and web. Re-exports the full cellar API.
+ Cross-platform file and data storage for Flutter, zero setup — files
+ land in the right folder on Android, iOS, macOS, Windows, Linux, and
+ web. Caching, auto-cleanup, encryption.
homepage: https://github.com/whuppi/cellar_flutter
repository: https://github.com/whuppi/cellar_flutter
issue_tracker: https://github.com/whuppi/cellar_flutter/issues
@@ -12,26 +11,22 @@ issue_tracker: https://github.com/whuppi/cellar_flutter/issues
topics:
- storage
- cache
+ - files
- encryption
- - indexeddb
- cross-platform
-# Not published yet — this line goes away when the repo opens and the
-# release tooling takes over. Consumed by path from sewali + bokumi.
-publish_to: none
-
environment:
- sdk: ">=3.11.0-0 <4.0.0" # -0 floor: the pinned fvm SDK is a 3.11 beta
+ sdk: ">=3.11.0 <4.0.0"
dependencies:
flutter:
sdk: flutter
- # The pure-Dart core this package fronts (and re-exports) — a pinned
- # git submodule of whuppi/cellar. CI clones --recursive; the release
- # flow swaps this to the published version.
- cellar:
- path: cellar
+ # The pure-Dart core this package fronts (and re-exports). The lock
+ # certifies the exact version CI ran against; Dependabot bumps it per
+ # cellar release. Local co-development: pubspec_overrides.yaml (see
+ # CONTRIBUTING).
+ cellar: ^1.0.0
# Storage roots on native platforms (support + cache directories).
# Imported only behind the native side of a conditional import — web
diff --git a/test/core_link_pin_test.dart b/test/core_link_pin_test.dart
new file mode 100644
index 0000000..c63527c
--- /dev/null
+++ b/test/core_link_pin_test.dart
@@ -0,0 +1,39 @@
+import 'dart:io';
+
+import 'package:flutter_test/flutter_test.dart';
+
+/// Doc links into the core repo must point at the tag of the version the
+/// pubspec names, so readers land on the docs of the exact core in use.
+/// A core bump PR that forgets the links fails here.
+void main() {
+ test('core repo links carry the pubspec version tag', () {
+ final pubspec = File('pubspec.yaml').readAsStringSync();
+ final floor = RegExp(
+ r'^ cellar: \^(\S+)$',
+ multiLine: true,
+ ).firstMatch(pubspec);
+ expect(floor, isNotNull, reason: 'pubspec must carry cellar: ^');
+ final tag = 'v${floor!.group(1)}';
+
+ final docs = [
+ File('README.md'),
+ File('SECURITY.md'),
+ ...Directory(
+ 'docs',
+ ).listSync().whereType().where((f) => f.path.endsWith('.md')),
+ ];
+ final ref = RegExp('whuppi/cellar/(?:blob|tree)/([^/]+)/');
+ for (final doc in docs) {
+ for (final m in ref.allMatches(doc.readAsStringSync())) {
+ expect(
+ m.group(1),
+ tag,
+ reason:
+ '${doc.path} links the core at "${m.group(1)}" — '
+ 'must be $tag (the pubspec version). Update the link when '
+ 'bumping the core dependency.',
+ );
+ }
+ }
+ });
+}
diff --git a/tool/versions.env b/tool/versions.env
index 57560bb..e118a1f 100644
--- a/tool/versions.env
+++ b/tool/versions.env
@@ -1,68 +1,11 @@
-# Single source of truth for pinned tool + binary versions used by whuppi/ci's
-# composite actions and reusable workflows. Sourced by the scripts that need
-# them — bump in ONE place, never inline. Plain `KEY="value"` so it's both
-# shell-sourceable and trivially greppable.
+# Single source of truth for pinned tool versions LOCAL to this package.
+# Sourced by the scripts that need them (tool/platforms_gate.sh) — bump in ONE
+# place, never inline. Plain KEY="value" so it's both shell-sourceable and
+# greppable.
#
-# source "$CI_ROOT/tool/versions.env"
-#
-# Every entry here is a pinned EXTERNAL tool/binary that CI downloads and
-# hash-verifies through tool/fetch_verified.sh.
-#
-# Who bumps what:
-# tool/ci/upgrade.sh (via self-upgrade.yml, daily PR in THIS repo) —
-# everything in this file: FVM_*, CHROME_*, ACTIONLINT_VERSION,
-# ZIZMOR_VERSION, BORE_*. One bump reaches every consumer through the
-# next whuppi/ci release.
-# Dependabot (in THIS repo) — GitHub-action SHAs pinned in the workflows.
-# Consumer repos — their own Flutter SDK (.fvmrc) + lockfiles,
-# via the reusable upgrade-check.yml.
-
-# fvm TOOL — downloaded + verified by actions/capabilities/fvm. Pins the tool;
-# the Flutter SDK itself is pinned separately by each consumer's .fvmrc.
-# Hashes self-computed from the release assets (leoafarias/fvm publishes no
-# digests).
-FVM_VERSION="4.1.2"
-FVM_SHA256_LINUX_X64="12b0656688450972750cb714fc5e44ed2dbdd6b1c6813ba6ba6fb71841c17fe4"
-FVM_SHA256_MACOS_ARM64="0b2a146986c51f06331f135f0bdf2a202eb57f55d7edd420c9078e8520e4c033"
-FVM_SHA256_MACOS_X64="7bbfcb6883ea67ce532163704f5625eba7ecf340084be707cde71a28fefff1d8"
-FVM_SHA256_WINDOWS_X64="9a18b4daac98dac3c3230ff67ccc644d5a7875d1fc09fb7d848cb2900b9478b8"
-
-# actionlint — workflow linter run in the reusable pr-checks gate. Go-installed
-# by this tag; Go verifies the module checksum, so no separate sha is tracked.
-ACTIONLINT_VERSION="1.7.12"
-
-# zizmor — the security half of the pr-checks gate, run via `pipx run`. pipx
-# resolves the version from PyPI, so no separate sha is tracked.
-ZIZMOR_VERSION="1.26.1"
-
-# ── Pinned binary downloads (verified via tool/fetch_verified.sh) ──
-# sha256 values are the upstream-published digest where one exists, else
-# self-computed from the release asset. Bump alongside the version.
-
-# bore tunnel binary (actions/debug-ssh). The action sources this file and
-# builds its download URLs from BORE_VERSION, so the version lives here beside
-# its hashes — one place for upgrade.sh to bump.
-BORE_VERSION="v0.6.0"
-BORE_SHA256_LINUX_X64="e484d1e3acba77169b773f31a5bfb34192d4b660f44a094a658a2522cd2270f7"
-BORE_SHA256_MACOS_ARM64="65f43a67b90874700538bdb6064c5e92276e64dfba24f5cd72ef24a035eec3bc"
-BORE_SHA256_WINDOWS_X64="01709c64fe2787cdc9a21d7030b0f08ad72dff0c36b7ecb72f4f667a55a34b4f"
-
-# Chrome for Testing — chrome + chromedriver pinned per platform, so web tests
-# run a verified, reproducible browser (actions/capabilities/chrome).
-# chrome-for-testing publishes no digests, so these are self-computed.
-# upgrade.sh re-bumps on Chrome's ~4-week Stable cadence; the CDN prunes old
-# versions, so the pin must be kept fresh or the download 404s.
-CHROME_VERSION="150.0.7871.46"
-CHROME_SHA256_LINUX_X64="ad115a7498a17f53f6ed0914458326c6516addc756224db14c32184a9b1ab078"
-CHROME_SHA256_MACOS_ARM64="9b6c6fbbda6f2948c74425828bd18f0e59542fd2beb056697478c38a956d83cb"
-CHROME_SHA256_WINDOWS_X64="2245f764e024897195bce1cf6ff16d60544351e4d1b68edb6956ea875ab8b929"
-CHROMEDRIVER_SHA256_LINUX_X64="2bd858c27c5d913bc9574e4fb7196161acbefd7f911d9a4b1e01a4cd36095716"
-CHROMEDRIVER_SHA256_MACOS_ARM64="fe616458e12b959c23565a4c3b057392e80f8bcb7b59410d9e493d6fcd864f1b"
-CHROMEDRIVER_SHA256_WINDOWS_X64="934274246dfd0d1185f2b9acf5441bc6e72ee2deeafea6996f9a868be3089e13"
# source "$(dirname "$0")/versions.env"
#
-# PANA_VERSION tracks pub.dev's latest that resolves on this repo's SDK —
-# cellar pins the beta channel (.fvmrc), and pana >=0.23.13 requires the
-# stable 3.11 floor the beta doesn't satisfy. Bump past 0.23.12 when the
-# pin moves to stable.
-PANA_VERSION="0.23.12"
+# PANA_VERSION tracks pub.dev's LATEST so the platform gate runs the same pana
+# pub.dev runs. tool/ci/upgrade.sh (the daily upgrade-check.yml pins job) bumps
+# it automatically in its own reviewed PR — the same radar as pdf_manipulator.
+PANA_VERSION="0.23.14"