Skip to content

release: 0.3.0 — remote_cache_trees by tree kind - #8

Merged
SpencerC merged 3 commits into
mainfrom
code-review-release-pr-f5f023
Aug 23, 2026
Merged

release: 0.3.0 — remote_cache_trees by tree kind#8
SpencerC merged 3 commits into
mainfrom
code-review-release-pr-f5f023

Conversation

@SpencerC

Copy link
Copy Markdown
Owner

What & why

Releases 0.3.0: the remote_cache_trees split from
feature/reproducible-prepare-deps, one correctness fix found reviewing it, and
the changelog cut.

1. feat(flutter)!: split remote_cache_trees by tree kind (unchanged work)

//flutter:remote_cache_trees becomes a string flag — none (default),
workspaces, all — because the assembled pub cache and the prepared/overlay
workspaces have different economics. PrepareFlutterAppWorkspace gains the
execution posture every other tree-producing action already had, and the
dependency-preparation log drops its wall-clock timestamp.

Only the commit subject changed: the original was not a Conventional Commit,
so Commitizen would reject it and ccv would derive no version from it at all.

2. fix(flutter): classify a prepare action by the trees it declares (new)

The split landed with FlutterPrepareDeps still on the default
TREE_WORKSPACE, but that action declares its own pub cache tree whenever
it is not handed a preassembled one:

prepare_outputs = [pub_get_output, pub_deps, dart_tool_dir, prepared_workspace]
if preassembled_cache != None:
    prepare_inputs = prepare_inputs + [preassembled_cache]
else:
    prepare_outputs = prepare_outputs + [pub_cache_dir]   # <- multi-GB when assembling

An action carries one posture for all of its outputs, so
--//flutter:remote_cache_trees=workspaces would have lifted no-remote-cache
off an action uploading a pub cache — exactly the trade the flag was split apart
to avoid. Worst case is the assemble + pub_package combination, where that
tree is the full merge.

prepare_deps_tree_kind now takes the more restrictive kind whenever a pub
cache is among the declared outputs, and the call site derives its argument from
the prepare_outputs list itself rather than re-deriving the branch condition,
so classification cannot drift from the outputs.

Also repairs the three remote_cache_trees docstrings, which kept a dangling
and False from the boolean they used to describe, and documents the
per-action rule in docs/hermeticity.md and the changelog.

An analysis test would be the natural home for the invariant, but Starlark's
Action exposes no execution requirements — ["args", "argv", "content", "env", "inputs", "mnemonic", "outputs", "substitutions"] — so it is pinned as a unit
test on the pure helper instead.

3. chore(release): cut 0.3.0

A breaking flag change is a minor bump while the module is pre-1.0.

⚠️ Do not release this by dispatching Tag a Release

ccv has no 0.x special case —
ccv.go matches
^(fix|feat)(\(.+\))?!: |BREAKING CHANGE: straight to IncMajor(). A dispatch
over this branch would tag v1.0.0, and tag.yaml's own
new-tag-version-type != 'major' guard would then skip the release job,
leaving a major tag with no artifacts behind it.

After merge, push the tag by hand — release.yml triggers on v*.*.*:

git tag v0.3.0 && git push origin v0.3.0

A comment in tag.yaml now records this so the next person does not find out
the hard way.

Checklist

  • bazel test //flutter/tests:all_tests //docs:update_tests passes — 16/16
  • cd e2e/smoke && bazel test //:integration_tests passes — 21/21
  • Ran bazel run //docs:update if any rule/macro API changed — no public API
    change; //docs:update_tests confirms the goldens are current
  • pre-commit run --all-files (buildifier + prettier) is clean
  • Updated docs/README for user-facing changes — docs/hermeticity.md +
    CHANGELOG.md

🤖 Generated with Claude Code

SpencerC and others added 3 commits August 23, 2026 13:48
remote_cache_trees was one boolean over two artifacts with very different
economics. A worker has to materialize the assembled pub cache locally whichever
way the flag is set — it feeds the local-only flutter build, and its own @pub_*
repository inputs must be fetched to compute any action key at all — so
remote-caching it buys a multi-GB download in place of a hardlink assembly.
Remote-caching the ~100MB prepared and overlay workspaces buys ~180MB of
download in place of a full intl_utils plus build_runner codegen run. Those are
not the same decision, so the flag now takes none (default), workspaces or all.

PrepareFlutterAppWorkspace had no execution posture at all, unlike every sibling
that produces a tree. It was therefore remote-execution eligible — which would
mean uploading its no-remote-cache input tree — and its own ~100MB output was
uploaded on every build under --remote_upload_local_results, which is precisely
the end-of-invocation drain this flag exists to prevent.

Also dropped the wall-clock timestamp from the dependency-preparation log: it is
a declared output, so a clock reading in it made the action unreproducible for
nothing.

Documented what workspaces= can and cannot do, because the answer is not
obvious and I got it wrong first: the prepared trees are NOT reproducible.
.dart_tool records the producing sandbox's absolute execroot in ~16 text files,
and — decisively — in the host native-asset binaries JIT codegen links;
.dart_tool/hooks_runner/shared/objective_c/build/<hash>/objective_c.dylib holds
17 copies of it in Mach-O load commands, which are length-prefixed and cannot be
rewritten. Canonicalizing the text was implemented and reverted for that reason;
the finding is recorded next to the action. The SDK filegroup separately
contributes 51 source *directories*, which Bazel fingerprints by mtime rather
than content, so two independently provisioned workers never agree on a key.
workspaces= therefore helps workers that share an output base or a fetched SDK.

BREAKING CHANGE: //flutter:remote_cache_trees is a string flag taking none
(default), workspaces or all. --//flutter:remote_cache_trees becomes
--//flutter:remote_cache_trees=all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The kind split landed with FlutterPrepareDeps still on the default
TREE_WORKSPACE, but that action declares its own pub cache tree whenever it
is not handed a preassembled one — and for the assemble+pub_package
combination that tree is the full multi-GB merge. An action carries a single
execution posture for all of its outputs, so `workspaces` would have lifted
no-remote-cache off an action uploading a pub cache: precisely the trade the
setting was split apart to avoid.

prepare_deps_tree_kind now picks the more restrictive kind whenever a pub
cache is among the declared outputs, and the call site derives its argument
from the outputs list itself rather than re-deriving the branch condition, so
the two cannot drift.

Also repairs the three remote_cache_trees docstrings, which kept a dangling
"and False" from the boolean they used to describe.

An analysis test would be the natural home for this, but Starlark's Action
object exposes no execution requirements (only args/argv/content/env/inputs/
mnemonic/outputs/substitutions), so the invariant is pinned as a unit test on
the pure helper.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Dates the section and adds the compare links. A breaking flag change is a
minor bump while the module is pre-1.0.

Also records why this one cannot go out through the tag workflow: ccv has no
0.x special case, so the BREAKING CHANGE footer maps straight to IncMajor()
and a dispatch would tag v1.0.0 — which the release job's own guard then
skips, leaving a major tag with no artifacts behind it. Push v0.3.0 by hand;
release.yml triggers on the tag.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@SpencerC
SpencerC merged commit 1563074 into main Aug 23, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant