tom_build_base 2.22.1
tom_build_base: ^2.22.1 copied to clipboard
Unified CLI framework for workspace traversal, tool definition, pipeline execution, and build configuration.
2.22.1 #
Fixed — a repeated multi option kept only its last value #
OptionDefinition.multi options given more than once (testkit :mutations --module cache --module cmdline, testkit :fuzz --target a --target b) were
stored one over the other, so only the last value reached the command, in
either position. Repeated values are now kept as a list; a single occurrence
stays a plain string, as before. Tests BB-MULTI-1..5.
2.22.0 #
Added — an item's own exit code, and killProcessTree #
ItemResult.failure(exitCode: n)lets a command say how badly an item failed (testkit's mutation sweep exits with its count of surviving and invalid mutants).ToolResult.exitCodeis 0 on success, else the sum of the failed items' codes (1 for an item without one), capped at 125 — an exit status is one byte, and 256 would read as success.runToCompletionsets the process exit code to it; the result tagging kept every field but this one, and now carries it too.killProcessTree(pid)kills a process and every process it started (pgrep -Pwalk, then SIGKILL;taskkill /T /Fon Windows), for a hungdart testwhose test VMs would otherwise keep running.
Tests BB-EXIT-1..4, BB-KILL-1.
2.21.0 #
Added — the context-boundary check #
A package built as several bounded contexts keeps them apart only by
convention. checkContextBoundaries reads the contexts from a boundaries:
section of the package's buildkit.yaml (the libraries forming each context as
globs under lib/, its public entries, the contexts it may depend on, and
optionally its tests under test/) and checks the package's import and export
graph against them: an import of another context's internal library
(internal-import), or of a context not in dependsOn
(undeclared-dependency), is a violation naming file, line and imported
library. A rule that matches nothing, a library two contexts claim, an unknown
dependency or an invalid section is a rule error, so a rotted rule is reported
rather than skipped; a package without the section is not measured, never
clean. scanDartDirectives reads the directives without the analyzer,
ignoring comments and strings. buildkit runs the check as :boundaries.
Tests BB-CB-1..14.
2.20.1 #
Changed — -r / --recursive says what it does #
The help every tool printed for -r was "Recurse into subdirectories", which
reads as if a non-recursive scan stayed in the top folder. It does not: folders
without a pubspec.yaml (containers) are always scanned, and -r only adds
descent into projects, for the projects nested inside them (test/ and
example/ fixtures). The help now says so, as do TraversalInfo.recursive and
the navigation docs. Behaviour is unchanged; TRV-PROJ-2 pins it.
2.20.0 #
Added — isFlutterPackage / pubspecDeclaresFlutterSdk #
One public answer to "is this a Flutter package?": the pubspec declares
dependencies: flutter: sdk: flutter. Three copies existed — the nature
detector's private check (any sdk: key), ciIsFlutterPackage (which threw on
an unreadable pubspec) and one in testkit, which picks flutter test by it. The
nature detector and ciIsFlutterPackage now use it, and a missing or
unreadable pubspec is not a Flutter package. Tests BB-FLP-1..8.
2.19.1 #
Fixed — a package search does not enter scratch or build folders #
prefixedPackagesIn (seams.dart) skipped only hidden folders and
node_modules, so a tool that unpacks published archives into ztmp/
(ztmp/path_deps_archives/tom_forge_core-2.0.0/) made that folder a repository
of the prefix, and a licence audit over it read 47 unresolved scratch copies as
packages. It now also skips ztmp, build and testlog; the set is
unsearchedPackageFolders.
2.19.0 #
Changed — build order ignores dev_dependencies by default #
A dev dependency is not needed to build a package, and pub allows a cycle
through one (a package whose tests use a tool that depends on the package, e.g.
tom_reflection → tom_reflection_generator, tom_ast_generator → tom_d4rt_exec).
Counting them made buildkit's build order fail on every run over the workspace.
ProjectTraversalInfo.includeDevDependencies now defaults to false;
--include-dev (CliArgs.includeDev, NavigationArgs.includeDev) opts in, and
--exclude-dev, now the default, still wins when both are given.
Changed — the cycle report names the cycles, not everything behind them #
BuildOrderResult.cycleParticipants lists only the members of a cycle (its
strongly connected component), no longer every project that merely depends on
one; BuildOrderResult.cycles gives each cycle with its edges
(BuildOrderCycle, BuildOrderEdge, dev edges marked). The traversal warning
prints one line per cycle with its edges. Tests BO-COMP-9..12, BO-TRAV-5,
BB-CLI-8c, BB-CLI-32c.
2.18.0 #
Removed — short-id in tom_project.yaml #
NatureDetector reads a project's id from project_id only; the hyphenated
short-id it also accepted is no longer read, so TomBuildFolder.shortId is
null for a tom_project.yaml that still names it. Every tom_project.yaml in the
workspace uses project_id; the three zom fixtures that still said short-id
now say project_id. Tests BB-NAT-39 and BB-NAT-40.
2.17.2 #
Fixed — an absolute --project path through a symbolic link selects its project #
2.17.1 stopped rejecting such a path; FilterPipeline.matchesProjectPattern
still compared it with the scanned folders literally, so it selected nothing
(--project selector matched no project: '/var/.../_build' (scanned /private/var/...)). An absolute pattern that does not match literally is now
compared again with symbolic links resolved on both sides. A link to another
folder still does not match.
2.17.1 #
Fixed — a --project path through a symbolic link is no longer rejected #
validateProjectPathsWithinRoot compared the execution root and an absolute
--project path lexically. The cwd a tool reports is the resolved path, so a
workspace reached through a link (on macOS everything under /var is
/private/var) rejected its own projects: project path is outside the workspace and was rejected: /var/.../_build (workspace root: /private/var/...).
Both sides are now resolved before the comparison, as
validateScanPathWithinRoot already did; a link inside the root that points
outside it is now rejected, where the lexical check let it through.
2.17.0 #
Added — ci.dart, a framework for a CI run by hand #
CiRun runs a repository's stages strictly in order, past any red, each
step's child processes under the workspace flutter lock (CiLock) with every
output line written to its log as it arrives (CiProcessRunner), every log
kept in one timestamped folder with a summary.txt, and one summary line at
the end naming every red step and its log (ciSummaryLine). What a stage does
is the repository's: a CiStageRunner callback. ciToolDefinition and
ciToolRunner put the stages on the tool framework (one command per stage
plus all). It came from Webwork's manual CI, which keeps only its stage
list.
Added — seams.dart, the engine of a package-boundary audit #
Reading source without a parser (directiveUris, stripDartComments,
dartCodeWithoutComments, topLevelDeclarations, callArgumentsFrom), the
import graph (importClosureOf, siblingImportClosureOf, reachableNamesOf,
packageImportsUnder, exportedDeclarationsOf), the package graph (Pubspec,
packagesIn, prefixedPackagesIn, resolvedPackagesOf, dependencyReach,
declaredTypeNamesUnder), entry-point rules (EntryPointRule,
auditEntryPointRules), the counted-root detector (upwardPathDerivations)
and the runner (SeamAudit, runSeamAudits, resolveRepositoryRoot,
resolveWorkspaceRoot). It came from Webwork's seam audit, which keeps its
rules. A one-line /* … */ comment before the directives no longer ends the
directive scan.
2.16.1 #
Fixed — FolderScanner honours kAlwaysSkipDirectories; _ai added (binbuild2) #
buildkit's pipelines walk with FolderScanner, but only scanForDartProjects
applied the always-skip list. A recursive scan therefore descended into nested
ztmp/ scratch copies, build/ outputs and the AI state mount: on
tom_agent_container it processed 15 non-projects, among them
_ai/quests/tom_brain/bench and 14 packages under nested ztmp/ folders
(one of them a build/ dir). FolderScanner now skips every directory in
kAlwaysSkipDirectories, and _ai (tom_agent_state) joins the list — quests
park experiment packages there that must never be versioned or built.
2.16.0 #
Added — resolveDartExecutable, so a compiled tool can spawn dart (sce51) #
Platform.resolvedExecutable is the Dart runtime ONLY under dart run. An
AOT-compiled tom CLI is its own resolvedExecutable, so
Process.run(Platform.resolvedExecutable, ['analyze', ...]) re-invokes the
tool.
That is not hypothetical. d4rtgen's --verify-output did exactly this: the
child parsed analyze as a positional, generated bridges in the working
directory, printed nothing the parent recognised as a diagnostic and exited 0 —
so the parent reported "N generated file(s) analysed clean" having analysed
nothing. The verification was a silent no-op in every compiled run. It only
became visible when the flag was made default-on, at which point the child
verified too and it became a fork bomb; 69 processes made the point that three
years of green "analysed clean" lines had not.
resolveDartExecutable answers the same "is this process the Dart runtime?"
question toolOriginLine already asks, and lives beside it. Order: the
executable itself when it IS dart, then DART_SDK, then FLUTTER_ROOT's
bundled SDK, then PATH, then the bare name dart. The last resort is
deliberately a bare name rather than the current executable — a command that
cannot be found fails loudly, where one that re-enters the tool does not fail
at all.
Seven tests; the load-bearing one asserts only that an AOT tool never resolves to ITSELF, because every other answer is survivable and that one is not.
2.15.0 #
Added — negatable flags and flag defaults in the v2 CLI parser (sce51) #
OptionDefinition has carried negatable and defaultValue since v2, and the
parser read neither. The consequence was not cosmetic: a default-on flag was
not expressible at all. --no-x fell through to the generic branch and landed
in extraOptions under the key no-x, so the opt-out silently did nothing and
the flag still read as unset at every call site.
Now:
--no-<flag>sets a DECLAREDnegatable: trueflag tofalseunder its own key, so one read site answers both forms.- A declared flag with
defaultValue: 'true'is seeded intoextraOptionswhen the command line does not mention it — a tool no longer has to invert the test at each read site, which is the same bug waiting in each of them. --helpnames the opt-out and the default, because a default the reader cannot see is a trap.
Narrowed on purpose. Only a declared negatable flag is recognised, so
--no-anything-else keeps its old meaning — an extra option whose key starts
with no-. The globals resolved by name (--no-skip) are matched before the
negation path and are unchanged. Eight tests pin both the new behaviour and
that narrowing; the suite is 900 passing.
The first consumer is d4rtgen --verify-output, which becomes default-on with
--no-verify-output to opt out.
2.14.0 #
Added — the pub-cache pre-flight is now reachable from the tools that compile #
PubCacheIntegrity (2.9.0) turns "a locked package is missing from the cache"
from Undefined name at every use site into one line naming the package. It
shipped with one caller, testkit — but the sighting that motivated it was an
AOT compile, where the same cause surfaces as Bad state: Generating AOT kernel dill failed! from inside the compiler and names nothing at all.
Two entry points so a call site is an if rather than a paragraph:
-
PubCacheIntegrity.preflight(projectPath:)returns the report, or null when the cache can supply everything. For any tool — the bridge and reflection generators have no executor, they just open an analysis context. -
CommandExecutor.pubCachePreflight(context)returns a failureItemResult, or null. For a buildkit-style executor:final preflight = pubCachePreflight(context); if (preflight != null) return preflight;
This is opt-in by design and is NOT called from ToolRunner. Most commands
read no package sources, would pay a stat per dependency for nothing, and a
check that fires everywhere is a check that gets muted. An executor calls it
once it knows it has real work to do.
src/v2/core/pub_cache_integrity.dart is now exported from the
tom_build_base_v2.dart barrel as well. pubCachePreflight is part of that
barrel's surface, so a tool importing only v2 could call the helper but could
not name the type to use it directly.
An unresolved project stays silent: no lock means nothing is locked, so nothing can be missing from it. Reporting there would fire on every fresh checkout.
2.13.0 #
Added — CliArgs.optionsFor, so an option means the same in either position #
The parser routes an option by WHERE it is written: before the command name it
lands in extraOptions, after it in commandArgs[<command>].options. An
executor reading only one silently ignored the other, and the documented form
is the trailing one — CLAUDE.md shows testkit :test --test-args="..." and
testkit's own help prints testkit :baseline --test-args="--tags e2e". So the
form everyone is told to use was the one being dropped:
testkit :baseline --test-args="--name nomatch" ran the whole suite and
reported on tests the caller never asked about.
optionsFor(commandName, {aliases}) merges both, per-command winning, so an
executor asks what its options ARE rather than where they were typed.
aliases is not optional in practice: the parser keys per-command options by
the name the CALLER typed, so testkit :b --test-args=X keys them under b.
A command with short forms must name them. BB-OPTPOS-5 pins that, including
that the lookup finds nothing without them.
This belongs here rather than in each tool. buildkit had already worked around it privately in EIGHT executors — bumpversion, compiler, publisher, runner, cleanup, versioner, dependencies and buildsorter — each carrying its own copy of "per-command options for this command, else global". Those copies also only chose between the two rather than merging them.
2.12.0 #
Fixed — -n / --dry-run is refused by tools that do not implement it #
Every tool built on tom_build_base accepted -n and --dry-run, because
cli_arg_parser parses them as global flags for all tools. A tool's
NavigationFeatures.dryRun did not gate that, or anything else: the only read
of the field in the package was the serializer that writes dry_run: into
--dump-definitions output. The declaration was decorative, so a tool that
declared dryRun: false still ran normally when asked for a preview -- which
is how d4rtgen -n used to regenerate files for real.
It was also advertised. The help text offered -n, --dry-run Show what would be done for every tool, from a hardcoded line in the command-help generator
and from commonOptions, so a tool that ignored the flag also promised it.
Four changes make the declaration load-bearing:
ToolRunnerrefuses a run whose args carrydryRunwhile the tool declaresdryRun: false, naming the tool, before any wiring, traversal or executor runs.--helpand--versionare unaffected, so-n --helpstill prints help rather than an error.allGlobalOptionsfilters--dry-runout ofcommonOptionsfor such a tool, and the command-help generator gates its own Common Options line.NestedToolExecutorforwards--dry-runonly to a nested tool that declares a dry-run mode, and a host dry run reports[DRY RUN] would run <tool>for one that does not, rather than running it. The capability is read from the nested tool's own--dump-definitions, which the wiring already queries.
This changes behaviour for callers. <tool> -n on a tool with
dryRun: false now exits non-zero instead of doing the work. That is the
point -- the caller asked for a preview -- but a script that passed -n and
relied on the work happening will now fail. The tools declaring dryRun: false
at this release are testkit, issuekit, the reflection generator, and the
tom_reflector analyzer / reflection-analyzer / reflector tools.
Two tests that pinned the old behaviour were rewritten to the new contract:
BB-TDF-20 asserted dry-run was present "regardless of the dryRun feature
flag", and BB-NTE-3 asserted unconditional forwarding.
2.11.0 #
Changed — dcli constraint widened to admit both majors #
dcli is now >=8.4.2 <11.0.0 rather than ^8.4.2, so a consumer may
resolve either the 8.x or the 10.x line.
The constraint was the binding reason no package in the workspace could move
to dcli 10: tom_build_base is a transitive dependency of nearly every tom
CLI, so its caret pin propagated a 8.x ceiling to all of them, and version
solving failed for any package that asked for ^10.0.0.
A major bump to ^10.0.0 would have swapped the problem for its mirror —
twelve packages still declare dcli: ^8.4.2, and forcing them off 8.x in one
step is a migration, not a constraint fix. Widening unblocks the 10.x
consumers without moving anyone who is not ready.
The widening is safe because this package's dcli surface is four symbols —
exists, read, toParagraph (lib/src/build_config.dart) and
createSymLink (lib/src/v2/core/mklink_executor.dart) — and all four are
unchanged between 8.4.2 and 10.0.0. The 9.0 removals were of deprecated APIs
and entry-point libraries this package never imported, and 10.0's rename of
stacktrace to stackTrace on DCliException touches no call site here.
Verified against dcli 10.0.0: analyzer clean, suite 864/0/0.
2.10.0 #
Added — --version says WHERE the tool is running from (sce8) #
Every tom CLI's version banner gains a second line naming the copy that answered:
d4rtgen v1.26.2+7
binary: /Users/dev/.tom/bin/darwin_arm64/d4rtgen
d4rtgen v1.26.2+7
source: /work/tom_d4rt_generator/bin/d4rtgen.dart
A version alone cannot distinguish a working tree carrying an unpublished fix
from a pub-cache copy, or from a precompiled binary on PATH out of a
DIFFERENT clone of the workspace — a real arrangement on at least one
developer's machine. SCC70 discarded a whole bridge-regeneration batch because
a stale binary and the working-tree source printed the same banner and nobody
could attribute the output.
binary: when the process IS the tool, source: when a Dart runtime is
executing it — in which case the runtime is the same string for every tool in
the workspace and the entry point is the thing worth naming. The source kind
is not labelled: a working tree, a .pub-cache copy and a .dart_tool/pub/bin
snapshot are told apart by their paths, which is what a reader needs and what
cannot go out of date.
The first line is unchanged, byte for byte, so anything parsing the banner is unaffected.
It lives here rather than in each tool because the banner does — ToolRunner
prints it, so every tom CLI gains the line at once and none of them can word
it differently.
2.9.0 #
Added — PubCacheIntegrity: a locked package missing from the cache is named, not left to the compiler (scd8_aicx) #
A hosted package whose pubspec.lock entry has no directory in the pub cache
produces no resolution error. The lock is satisfiable, so dart pub get
reports success, and the failure lands later as Error: Undefined name '<Symbol>' at every use site — followed by AOT compilation failed when
compiling. That points at the file using the symbol, so it reads as an API
renamed upstream, and nothing in the output mentions the cache. Measured twice
in one session (tom_build_base-2.7.1, then tom_d4rt-1.29.0), and the
misreading is deterministic: no compiler output can distinguish an absent cache
directory from an absent symbol.
PubCacheIntegrity.checkProject(projectPath:) stats every hosted lock entry
and returns a PubCacheProblem per package the cache cannot supply —
missingDirectory (repaired by dart pub get) or missingPubspec, the shape
where the directory exists but is empty, which dart pub get walks straight
past because pub treats the directory as installed. describe() renders them
with the package, the expected path and the repair. path and sdk entries are
not checked: they do not live in the cache and fail on their own terms.
Consumers call it as a pre-flight — tom_test_kit runs it before launching the
test runner, so the workspace regression gate names the package instead of
reporting a compile error.
2.8.0 #
Fixed #
-
--exclude-devno longer disappears when placeholders are resolved.CliArgs.withResolvedStringsrebuilt the instance by naming 39 of the class's 40 fields, andexcludeDevwas the one it left out. Because the field defaults tofalse, the loss was silent: the rebuiltCliArgsreported that dev dependencies should be included, so any tool that resolved placeholders before traversing went back to ordering its projects by their dev dependencies as well — the exact thing the caller had asked it not to do. Covered by BB-CLI-100.This is the third field to be dropped this way (
nestedanddumpDefinitionswere the first two, hence BB-CLI-79/80), so the fix addresses the shape rather than the field — see below.
Added #
-
CliArgs.copyWith(...)— a copy that starts from the current instance, so a caller names only what it changes.withResolvedStringsis now written in terms of it and mentions just the three fields it actually resolves (positionalArgs,commandArgs,extraOptions); everything else is carried over by construction instead of by transcription.copyWithstill lists the fields once, so the guarantee is held by tests rather than by the type system: BB-CLI-96 reads the constructor's parameter list out of the source and fails if a field is missing from the test's field projection, BB-CLI-97 requires the round-trip fixture to differ from the defaults in every field, and BB-CLI-98/99 assert field-for-field equality acrosswithResolvedStringsandcopyWith. Together they fail when a field is added toCliArgswithout being carried through, which is what the previous field-at-a-time tests could not do.Null means "unchanged", following
WorkspaceNavigationArgs.copyWithinnavigation_bridge.dart; a nullable field such asscanorconfigPathcannot be cleared throughcopyWith.
2.7.1 #
Fixed #
- A pipeline
shell-scanstep no longer passes for having run nowhere. The unmatched-selector rule added in 2.7.0 covered the tool paths but notshell-scan, which reported success whenever every selected folder succeeded — trivially true of no folders. It now reports the unmatched selector and fails the step, and honours--allow-emptylike everything else. Covered by BB-PLX-8.
Added #
describeUnmatchedProjectPatterns(patterns, scanRoot:)andProjectTraversalInfo.absoluteScanRoot— the message wording and the "where did it look" resolution, shared so every caller that rejects an unmatched selector words it identically.
2.7.0 #
Changed #
-
A
--projectselector that matches no project now fails the run. Previously any selector that matched nothing — a mistyped id or name, a partial id such ascore/tom_core_d4rtinstead of the fulltom_ai/core/tom_core_d4rt, or a glob like./*— produced a silent, successful no-op: nothing selected, nothing executed, exit 0. A mistyped selector was therefore indistinguishable from a run that did its job. Such patterns are now reported individually and the run exits non-zero, naming the pattern and the root it was searched under. Nothing is executed, so a selection containing one bad selector never runs half of itself.The two existing path guards (out-of-workspace absolute paths, and non-glob paths naming a missing directory) only covered the path shape of a selector; ids, names and globs were exempt by design because they "may match zero". The audit replaces that shape-based exemption with the general rule: whatever its form, a selector is checked against the projects that were actually scanned.
The check runs against the scanned projects, before the exclude filters, so
--project a --exclude-projects aremains a coherent (empty, successful) request rather than an unmatched selector. Covered by BB-RUN-69..74.
Added #
--allow-empty— opts out of the rule above for callers that use a selector as an "if present" filter, e.g. a shared script run across workspaces that do not all contain the project. Available on every tool with project traversal.FilterPipeline.unmatchedProjectPatterns(folders, patterns, executionRoot:)— the audit primitive: the subset ofpatternsmatching none offolders, in pattern order, each at most once.ProcessingResult.unmatchedProjectPatternsplusBuildBase.traverse'sadditionalProjectPatternsandallowUnmatchedProjectPatternsparameters, so callers that apply further--projectselectors of their own (per-command patterns inToolRunner) get them audited by the same rule.
Fixed #
FilterPipeline.applyProjectFiltersinlined a duplicate ofmatchesProjectPattern's five-way match; it now calls the method, so filter and audit cannot disagree about what a pattern matches.
2.6.32 #
Added #
- Shell completion delivery (
--completion <shell>) — every tool built on the v2ToolRunnernow accepts a global--completion bash|zsh|fishoption that prints a shell completion script generated from the tool's own command and option definitions, then exits. Intercepted early (alongside--dump-definitions), so it runs anywhere without traversal. An unknown shell prints an error and exits non-zero. The generation itself (CompletionGenerator) already existed; this wires it into the framework so the capability is delivered uniformly with no per-tool code. Seedoc/cli_shell_completion.md. BuildOrderComputer.computeBuildOrderResult— a cycle-aware variant ofcomputeBuildOrderreturning aBuildOrderResult(the ordered paths, or the names of the projects participating in a dependency cycle).computeBuildOrderis retained and now delegates to it.ToolRunner.normalizeArgs— the legacy single-dash flag normalization (-help→--help,-version→--version, at any position) thatrunalready applied internally is now exposed as a public static method. Tools that pre-parse arguments before delegating torun(e.g. testkit's--tuidetection, issuekit's pre-token help/version early-exit) can call the shared method instead of carrying their own duplicate helper. Covered by BB-RUN-67.- Canonical
OutputFormat/OutputSpec(src/v2/core/output_format.dart, exported fromtom_build_base_v2.dart) — the shared CLI output-format enum (plain/csv/json/md, withtext/markdownaliases) and the<format>[:<file>]spec parser that testkit and issuekit each duplicated.OutputSpeccarriesformat+ optionalfilePath(withhasFileand adefaultSpecof plain→stdout), splits at the first colon so file paths may contain colons, and treats a trailing empty file part (csv:) as "no file". Consumers migrate onto it (issuekit'sfilenamefield reconciled tofilePath) once published. Covered by BB-OUT-1..10. ToolRunner.runToCompletion— folds the shared CLI entrypoint tail (run→ printrenderRunSummary()when non-empty → setexitCode = 1on failure, neverexit()) that buildkit/testkit/issuekit each repeated into a single method, so the exit-code contract indoc/cli_error_handling.mdcannot drift per tool. Returns theToolResult; leavesexitCodeuntouched on success. Consumers collapse their tail to a single call once published. Covered by BB-RUN-68.
Changed #
- Build-order traversal now reports dependency cycles instead of silently
falling back to scan order. Previously a circular dependency made
computeBuildOrderreturnnull, whichBuildBase.traverseswallowed with?? [], so build-order mode quietly proceeded in scan order. It now emits a clearstderrwarning naming the projects involved in the cycle before falling back, so callers can trust (and debug) execution-order semantics. - Pipeline
shell/stdindry-run previews now carry a[DRY RUN]indicator.ToolPipelineExecutor._runShell/_runStdinpreviewed a dry-run step under the same[PIPELINE:shell]/[PIPELINE:stdin]marker used for verbose real runs, so the only cue that a step was skipped was the absence of execution output — ambiguous, since a real command may also be silent. Dry-run previews are now prefixed[DRY RUN] [PIPELINE:shell] …/[DRY RUN] [PIPELINE:stdin] …, consistent with the mklink/versioner executors'[DRY RUN]precedent; verbose real runs keep the bare marker. Covered by BB-PLX-6/BB-PLX-7.
Fixed #
--projectpath guards now also cover command-level patterns. The within-root and existence guards (validateProjectPathsWithinRoot,validateProjectPathsExist) were only applied to the globalcliArgs.projectPatterns, so a bad path passed after the command (tool :cmd --project sub/nonexistent, which lands incommandArgs[cmd].projectPatterns) slipped past both guards — an out-of-root or non-existent path scanned, matched nothing, and exited0. Both guard sites now validate the combined global + per-command pattern list via a shared_guardProjectPathshelper, preserving the within-root-before-existence ordering. Covered by BB-RUN-63..66.
2.6.31 #
Fixed #
- Nested tool output is no longer swallowed in the traversal path — when a
host tool (e.g.
buildkit) ran a wired nested tool during project traversal,NestedToolExecutorcaptured the child's stdout/stderr and only echoed it when--verbosewas set, the child failed, or the output happened to contain the words "error"/"warn"/"fail". On a quiet, successful run this hid all legitimate output: a flag like--show-cache-statusran correctly but its report never reached the terminal, making the option look like a no-op, and per-file progress was invisible. The executor now streams the child's stdout/stderr live (viarunBinaryStreaming/inheritStdio), matching how the pipeline path already runs nested tools (ToolPipelineExecutor._runToolCommand). Failure is determined by the child's exit code.
2.6.30 #
Fixed #
- Non-existent
--projectpath now fails loudly instead of exiting 0 — passing a non-glob--projectpath that does not exist (e.g.--project _build/nonexistent) previously scanned, matched nothing, and exited0with no output, masking the typo (the bug #19 regression thatdependencies_test's DEP_ERR01 guards).ToolRunnernow validates such path patterns up front via the newvalidateProjectPathsExistand returns a failure with a clearproject path not found: …message. Project ids, names, and glob path patterns are unaffected — they may legitimately match zero projects and are never existence-checked.
2.6.29 #
Fixed #
- Pipeline rejects unknown/unprefixed commands with a clear stdout error — a
pipeline command that is neither a built-in tool command (prefixed with the
tool name) nor an explicitly allowed
shell/shell-scan/stdin/printcommand — e.g. a barerm -rf /— must never reach process execution. The pipeline parser already rejected such commands, but theFormatExceptionit threw escaped to stderr as an unhandled error (exit 255) and its message read "Unsupported pipeline command prefix", surfacing nothing actionable on stdout. The message is now reworded to "Unknown command "…" … Only built-in tool commands … are allowed", andToolRunnercatches theFormatExceptionat the pipeline-invocation site, writing the rejection to stdout (Error: …) and returning a failure result (non-zero exit) instead of crashing. Covered by newpipeline_config(BB-PLC-11) andtool_runner(BB-RUN-58) tests; satisfiestom_build_kit'sSEC_CMD01.
2.6.28 #
Fixed #
--scannow rejects paths outside the workspace —--scannames the real directory the traversal walks (Directory(scan): a relative value resolves against the current directory, an absolute value is used as-is), so either an absolute path like/tmpor a relative../../siblingcould escape the workspace. Previously such a scan returned an empty result and exit0, masking that the tool was pointed at a location it must not walk. The newvalidateScanPathWithinRoothelper (workspace_utils.dart) canonicalises both the scan path and the execution root (resolving symlinks so a symlinked temp root is not confused with its real target) and requires the scan to equal or be contained within the root; bothToolRunnerproject-traversal paths now reject an out-of-root scan before scanning with a clearError: scan path is outside the workspace ...message and a non-zero exit. Covered by newworkspace_utilsregression tests.
2.6.27 #
Fixed #
--projectnow rejects absolute paths outside the workspace — a--projectpattern is a filter resolved against the scanned tree (project id, project name, folder-name glob, or relative path), all of which are contained by construction. The one way to escape the workspace is an absolute path that resolves outside the execution root. Previously such a path silently matched nothing and the tool exited0, looking like a successful no-op while actually being pointed at a path it must never operate on. The newvalidateProjectPathsWithinRoothelper (workspace_utils.dart) checks every absolute--projectpattern against the resolved execution/workspace root; theToolRunnertraversal paths now reject an out-of-root path before any scanning with a clearError: project path is outside the workspace ...message and a non-zero exit. Covered by newworkspace_utilsregression tests.
2.6.26 #
Fixed #
ztmpscratch directory is no longer scanned for projects —ztmpis the workspace's canonical gitignored scratch dir, but the recursive project scanner descended into it and discovered stale full-project copies (e.g.ztmp/d4gen199,ztmp/refgen112) left over from debugging. Those carry outdated dependency constraints, so:pubupdateand the build pipeline picked up versions far behind the published ones.ztmpis now inkAlwaysSkipDirectoriesand the repository-id lookup ignore set, and the non-recursive scan branch now honorskAlwaysSkipDirectoriestoo (it previously only skipped dot- andzom_-prefixed dirs). Covered by a newworkspace_utilsregression test.
2.6.25 #
Added #
- Shared end-of-run summary block for every
tom_build_basetool —ToolResult.renderRunSummary()produces a single, consistent errors/skips report so outcomes are easy to read after a long run, instead of each tool re-implementing its own footer. The block lists a=== Skipped ===section (deliberately skipped items) above either a=== Errors ===section naming every failed item with anN error(s) in M project(s).tally, or the cleanDone. No errors.footer when nothing failed. It returns an empty string when no items were processed (e.g.--version/--help/single-shot commands), so callers can print it unconditionally without a spurious footer. NewItemResult.skipped(...)constructor +ItemResult.skippedflag distinguish a configured/conditional skip (a non-failing success that never affects the exit code) from real work; the runner preserves the flag when tagging results. Covered by regression testsSUMM01–SUMM13.
2.6.24 #
Fixed #
- Non-ASCII tool/subprocess output now renders correctly on Windows — added a UTF-8 console + process-output guard (
console_encoding.dart).ProcessRunner.run/runShelland the shell executor now capture raw bytes and decode them as UTF-8 (decodeProcessOutput, tolerating malformed sequences) instead of relying on the host's ANSI code page, which previously turned UTF-8 diagnostics such asdart compile's "für" into double-mojibake ("für").ToolRunner.runcallsenableUtf8Console()at startup, which switches the Windows console code pages to UTF-8 (CP_UTF8, thechcp 65001equivalent via kernel32) and routes thestdout/stderrsinks through UTF-8. No-op on non-Windows hosts and idempotent. Streaming decoders now tolerate malformed bytes so a single bad chunk no longer aborts the rest of the stream. Covered by regression testsENC01–ENC09.
2.6.23 #
Fixed #
- Per-command value options no longer shadowed by colliding global flags — a command that declares its own value-bearing option (e.g. versioner's
--version <v>override) now receives the value when invoked asbuildkit :versioner --version 9.9.9, instead of the token being captured by the global--versionprint-version flag. A bare--version(no value) still prints the tool version. Covered by regression testsBB-CLI-93–BB-CLI-95.
2.6.22 #
Fixed #
--helpno longer advertises macros/defines/pipelines in an ineligible context — the runtime macro / persistent define help appendix is now gated on_isMacroDefineFeatureEligible()(a<tool>_master.yamlexists in the workspace), matching the gating already applied to the commands themselves. Previously the appendix was shown whenever the tool wasmultiCommand, even with no master yaml present, misleading users about unavailable features.--project <absolute path>now matches the target project on every platform —FilterPipelineproject matching previously only recognised path patterns containing/, and never compared a folder's full path. A--projectargument given as an absolute filesystem path (e.g.versioner --project C:\repo\_buildon Windows, or any absolute POSIX path) therefore matched zero projects. Project/path matching is now separator-agnostic (/and\are equivalent) and absolute paths are compared against each folder's own path (case-insensitively on Windows). Covered by regression testsBB-FLT-41–BB-FLT-44.
2.6.21 #
Fixed #
- Nested mode executes per-project logic —
_runNestedModeand_runNestedCommandnow create aCommandContextfrom CWD and callexecute()instead of the no-opexecuteWithoutTraversal(). This fixes nested tools (e.g., d4rtgen called via buildkit) silently doing nothing.
2.6.20 #
Fixed #
- Cross-platform path display —
CommandContext.relativePathnow always uses forward slashes, preventing backslash stripping by console markdown on Windows.
2.6.19 #
Added #
ItemResult.commandName— Optional field to track which command produced each result (e.g.,'runner','compiler'). Populated byToolRunnerwhen executing command chains.
Changed #
- Compact tool output —
ToolRunnernow always prints one-line-per-command status (-> :cmd message) instead of verbose multi-line output. Project headers (>>> path) are always shown. - Nested tool output buffering —
NestedToolExecutorbuffers subprocess stdout/stderr and only displays on error, signal words, or--verbosemode.
2.6.18 #
Changed #
- Project-name alias matching — Project resolution and filtering now treat
pubspec.yamlnameas a first-class project-name alias alongsidetom_project.yamlandbuildkit.yamlmetadata. - Workspace boundary markers —
tom_workspace.yamlis no longer used as a workspace boundary marker; workspace boundary detection now relies onbuildkit_master.yaml.
2.6.14 #
Added #
printpipeline prefix — Added a dedicatedprint <message>command prefix for pipelines to emit resolved messages without shell invocation.- Mklink executor API — Added dcli-backed symlink execution support (
MkLinkExecutor) for reusable cross-platform link creation in tool implementations.
Changed #
- Pipeline help examples — Updated built-in help topic examples to use
printfor message output instead of shell-based echo commands.
2.6.13 #
Changed #
- Repository ID lookup source —
RepositoryIdLookupnow readsrepository_idandnamefromtom_repository.yamlfiles in the workspace instead of using a hardcoded ID map. - Module filter resolution —
--modulesand--skip-modulesresolution now uses workspace metadata (tom_repository.yaml) via execution-root-aware lookup.
Added #
- Lookup cache controls — Added
RepositoryIdLookup.clearCache()for test/runtime cache invalidation when repository metadata changes.
2.6.12 #
Fixed #
- Bootstrap environment bypass —
TOM_BOOTSTRAP_ALLOW_MISSING_SETUP=1environment variable now correctly bypassesrequired-environmentchecks inToolRunner.run(). This allowsbootstrap_binaries.shto run the versioner step even when optional binaries (astgen, reflector) are not yet installed. The:doctorcommand always runs full checks regardless of the bypass.
2.6.6 #
Fixed #
- FolderScanner recursive semantics — The
recursiveflag now correctly controls recursion inside project directories only. Non-project (container) directories are always traversed to find projects, which is the fundamental purpose of scanning. Previously,recursive: false(the default) stopped all descent after the scan root, meaning nested projects inside container directories were never found.
Documentation #
- Corrected
FolderScanner.scan()doc to explain thatrecursivecontrols descent into project folders (containingpubspec.yaml), not overall directory traversal. - Corrected
CliArgs.toProjectTraversalInfo()doc: hardcoded default isrecursive: false, and clarified that non-project directories are always traversed regardless of the flag.
2.6.5 #
Fixed #
- Pipeline nested workspace discovery —
_discoverNestedWorkspacesnow skipstest/,build/,node_modules/, andexample/directories when searching for nested workspaces to delegate pipeline execution to. Previously,buildkit_master.yamlfiles inside test fixtures (e.g.,test/fixtures/) were incorrectly treated as real nested workspaces, causing pipeline delegation failures.
2.6.4 #
Fixed #
- Nature parsing in tool wiring —
ToolDefinitionSerializer.fromYamlMap()now correctly parsesworks_with_naturesandrequired_naturesfields from--dump-definitionsYAML output. Previously, these fields were only serialized (viatoYaml()) but not parsed when deserializing, causing nested tool commands to fail with "has no nature configuration" errors during wiring.
Added #
- Private helpers — Added
_stringToType()to convert nature type names ("DartProjectFolder", "FsFolder", etc.) back toTypeobjects, and_parseNaturesList()to parse YAML lists toSet<Type>.
2.6.3 #
versionStringsupport — Added optionalversionStringfield toToolDefinitionfor custom version display format. When set,--versionwill output this string instead of the default "name version" format.
2.6.2 #
- Build fixes — Minor fixes for tool compilation.
2.6.1 #
- Fix: Added missing
WorkspaceScannerexport to both barrel files (tom_build_base.dartandtom_build_base_v2.dart)
2.6.0 #
Breaking Changes #
- Removed V1 API — The following V1 classes, functions, and files have been removed:
ConfigLoader,LoadedConfig,PlaceholderDefinition,PlaceholderContext,resolvePlaceholders()(config_loader.dart)ConfigMerger(config_merger.dart)ProcessingResult— V1 version removed (processing_result.dart); V2ToolResult/ItemResultremainsisPathContained(),validatePathContainment()(path_utils.dart)isBuildYamlBuilderDefinition(),hasBuildYamlConsumerConfig(),isBuildYamlBuilderEnabled(),getBuildYamlBuilderOptions()(build_yaml_utils.dart)showVersions(),ShowVersionsResult,ShowVersionsOptions,readPubspecVersion()(show_versions.dart)bin/show_versions.dartCLI tool
- Unified barrel —
tom_build_base.dartnow exports the full API (framework + utility classes). Thetom_build_base_v2.dartbarrel is still available for backwards compatibility. - Retained utility classes —
TomBuildConfig,hasTomBuildConfig(),ProcessRunner,ToolLogger,yamlToMap(),yamlListToList(),toStringList()remain available.
2.5.16 #
Added #
- Comprehensive help topics — Added
definesHelpTopic,macrosHelpTopic,pipelinesHelpTopic,wiringHelpTopicas built-in help topics. All multiCommand tools automatically expose these viahelp defines,help macros,help pipelines,help wiring. --modesin global help — Added--modestocommonOptionsso it appears in--helpoutput for all tools.{TOOL}placeholder in help topics —generateTopicHelp()now replaces{TOOL}with the tool name in help topic content.- Placeholder resolution in pipeline shell/stdin —
shellandstdinpipeline commands now resolve standard%{...}placeholders (root, current-os, current-platform, etc.) before execution.shell-scanalready had full placeholder support.
Changed #
- Pipeline help — Converted hardcoded pipeline help from
tool_runner.dartto a properHelpTopic. Updated documentation to use correct%{...}placeholder syntax. - Macro/define help routing —
help definesandhelp macrosnow show comprehensive topic documentation instead of brief command summaries. - Help appendix — Global help appendix now lists all 5 help topics (defines, macros, pipelines, placeholders, wiring).
2.5.15 #
Added #
cli_arg_parser.dart— Added--modesas a recognized global CLI flag. Accepts comma-separated mode names (e.g.,--modes DEVor--modes DEV,CI). Parsed intoCliArgs.modesasList<String>.tool_runner.dart—@[name]define placeholders are now resolved per-folder during traversal. Defines are loaded from{tool}_master.yaml(default + mode-specific sections based on--modes), then merged with per-projectbuildkit.yamldefines (project overrides master). Resolution happens after%{name}placeholder resolution and before executor execution.tool_runner.dart— Per-projectbuildkit.yamldefine loading: project-levelbuildkit.yamlfiles can override master defines viadefines:or{tool}: defines:sections, including mode-specific{MODE}-defines:sections.
2.5.14 #
Fixed #
macro_expansion.dart— Missing positional arguments ($1–$9) in macros are now treated as empty strings instead of throwingMacroExpansionException. This allows macros like@vcto be invoked without arguments even when the macro value contains placeholders.
2.5.13 #
Improved #
tool_runner.dart— When:macro,:define,:defines,:undefine,:unmacro, or:macroscommands are used but{tool}_master.yamlcannot be found, a clear error message is shown explaining which file is missing and the detected workspace root. Previously these commands silently fell through to "Unknown command".tool_runner.dart— Help text for macro/define/pipeline commands is now shown even when the master yaml is missing, so users can understand how to set things up.
2.5.12 #
Fixed #
tool_runner.dart— Macro expansion (@macroName) now actually works. TheexpandMacros()function frommacro_expansion.dartwas never called duringrun(), so@macroinvocations were passed through unparsed. Expansion now happens before arg parsing, after loading persisted macros.
2.5.11 #
Added #
tool_definition.dart— AddedversionStringproperty toToolDefinitionfor custom--versionoutput. When provided, this string (typically from versioner-generated code) is shown instead of the default "name vX.X.X" format.tool_runner.dart—:defineand:undefinecommands now support-m MODEor--mode MODEflag for mode-specific defines (e.g.,buildkit :define -m DEV DEBUG=true).tool_runner.dart—:definescommand now lists all defines including mode-specific ones (e.g.,DEV-defines:,CI-defines:).tool_runner.dart— Macros now stored in{tool}_master.yamlundermacros:section instead of separate{tool}_macros.yamlfile.tool_runner.dart— Defines stored under{tool}:section in master.yaml with structure:{tool}: defines:for default and{tool}: {MODE}-defines:for mode-specific.
Changed #
cli_arg_parser.dart— Extended special-case greedy argument handling to include:undefinecommand (all args after:undefineare treated as positional to allow-m MODE namesyntax).
2.5.10 #
Fixed #
tool_runner.dart— Runtime macros (:macro,:macros,:unmacro) now persist to{workspace_root}/{tool_name}_macros.yaml. Previously, macros defined in onebuildkitinvocation were lost in the next invocation because they were stored only in an in-memory map. The file is written on everyadd/removeand loaded lazily on the first macro operation of each invocation; it is deleted automatically when the last macro is removed.
2.5.9 #
Fixed #
cli_arg_parser.dart—:commandtokens appearing after a:macroor:definecommand are now treated as positional arguments (part of the macro value) rather than being dispatched as separate commands. Previously,buildkit :macro vc=:v $1 :comp $2would execute:compimmediately and store only:vas the macro value. Now the full token sequence is captured as the value.
2.5.6 #
Fixed #
cli_arg_parser.dart— Global navigation/feature flags (--dry-run,--verbose,-n,-v,--force,--list, etc.) now route to global state regardless of whether they appear before or after a command name. Previouslybuildkit :compiler --dry-runwas silently ignored; now it works identically tobuildkit --dry-run :compiler.
2.5.5 #
Added #
tool_runner.dart— Added required-environment validation support from<tool>_master.yaml(includingbuildkit_master.yamlfallback forbuildkit), with checks for environment variables, folders, binaries, and caret-version constraints.tool_runner.dart— Added doctor-mode execution flow for tools: doctor requests now print requirement warnings/errors and return success/failure based on hard requirement violations.
Fixed #
tool_runner.dart— Normalized doctor token detection so bothdoctorand:doctorforms are recognized consistently in positional and command argument paths.
2.5.4 #
Fixed #
cli_arg_parser.dart— Fixed short option abbreviation collision when multiple commands share the same abbreviation (e.g.-cused by bothrunnerandexecute)._shortToLongnow prioritizes the current command's options before falling through to all commands.
2.5.3 #
Changed #
execute_placeholder.dart— Migrated placeholder syntax from${...}to%{...}to avoid shell variable expansion (${}) and YAML comment stripping (#{}after whitespace). All regex patterns, error messages, and help text updated.builtin_help_topics.dart— Updated all placeholder documentation to use%{...}syntax.
2.5.2 #
Changed #
repository_id_lookup.dart— RemovedCRPT(tom_module_crypto) andCOM(tom_module_communication) repository IDs after module consolidation into tom_module_basics.
2.5.1 #
Fixed #
builtin_help_topics.dart— Escaped*in placeholders help topic context reference table to prevent console_markdown from consuming it as italic markup.
2.5.0 #
Added #
console_markdown_zone.dart— Central console_markdown integration via Dart zones. ProvidesrunWithConsoleMarkdown()(async) andrunWithConsoleMarkdownSync()to wrap CLI tool execution in a zone that renders markdown syntax (**bold**,<cyan>text</cyan>, etc.) to ANSI escape codes.console_markdown_zone.dart—ConsoleMarkdownSinkwrapper class forStringSinkthat applies.toConsole()rendering to all writes, enabling markdown rendering onstdout/stderrsinks.console_markdown_zone.dart—isConsoleMarkdownActivegetter andkConsoleMarkdownZoneKeyzone key for double-processing detection. Prevents nested zones (e.g. when tom_d4rt_dcli already wraps output).tool_runner.dart—ToolRunnernow automatically wraps its output sink withConsoleMarkdownSinkwhen running inside a console_markdown zone, so alloutput.writeln()calls render markdown.pubspec.yaml— Addedconsole_markdown: ^0.0.3dependency.
2.4.0 #
Added #
execute_placeholder.dart—resolveCommand()now acceptsskipUnknownparameter. When true, unrecognized placeholders are left as-is instead of throwing, enabling multi-phase resolution (e.g., general placeholders first, then compiler-specific ones).execute_placeholder.dart— AddedExecutePlaceholderContext.fromCommandContext()factory for easy creation from traversal'sCommandContext.cli_arg_parser.dart— AddedCliArgs.withResolvedStrings()method to create a copy with placeholders resolved in positional args, extra options, and per-command options.tool_runner.dart— ToolRunner now automatically resolves general placeholders (${folder},${dart.name}, etc.) in all CLI args per folder during traversal, giving universal placeholder support to all commands.tom_build_base_v2.dart— Exportedexecute_placeholder.dartfrom the v2 barrel.
2.3.0 #
Added #
help_topic.dart— NewHelpTopicclass for named help sections (topic content, summary, name).builtin_help_topics.dart— Built-inplaceholdersHelpTopicwith comprehensive placeholder documentation.tool_definition.dart— AddedhelpTopicsfield andfindHelpTopic()method.help_generator.dart— AddedgenerateTopicHelp()and "Help Topics" section in tool help.special_commands.dart— Help topic lookup inhandleSpecialCommands()andgeneratePlainToolHelp().tool_runner.dart— Help topic lookup before "Unknown command" error.
2.2.0 #
Added #
filter_pipeline.dart— Added_matchesRelativePath()and_isPathPattern()for path-based pattern matching in--exclude-projectsand--projectfilters. Patterns containing/(e.g.,core/tom_core_kernel) are now matched against relative paths using glob matching, enabling directory-scoped project exclusion.filter_pipeline.dart— UpdatedmatchesProjectPattern()to accept optionalexecutionRootparameter for path-based matching.tool_runner.dart—ToolRunner.run()now handles bareversionas a positional arg (in addition to--version/-V), consistent withhandleSpecialCommands.help_generator.dart—generateCommandHelp()now includes a "Common Options" section showing--help,--verbose, and--dry-run.tool_runner.dart— Per-commandmatchesProjectPattern()calls now passexecutionRootfor path-based pattern support.
2.1.0 #
Added #
navigation_bridge.dart— Re-introducesWorkspaceNavigationArgs,addNavigationOptions(),preprocessRootFlag(),parseNavigationArgs(),resolveExecutionRoot(),isVersionCommand(),isHelpCommand()as v2-clean code (dart:io only, no DCli dependency). These bridge thepackage:argsArgParser to the v2 traversal system for tools that useArgParserfor global option parsing.- Exported from both
tom_build_base.dartandtom_build_base_v2.dartbarrels.
2.0.0 #
Breaking Changes — V1 Navigation System Removed #
Deleted the entire v1 project navigation/discovery system:
workspace_mode.dart—WorkspaceNavigationArgs,ExecutionMode,addNavigationOptions(),parseNavigationArgs(),preprocessRootFlag(),resolveExecutionRoot(), and related helpers are removed.project_discovery.dart—ProjectDiscoveryclass (includingscanForProjects(),resolveProjectPatterns(),hasSkipFile(),getSkipFileName(),applyModulesFilter(),findGitRepositories(),filterByModules(),resolveModulePaths()) is removed.project_navigator.dart—ProjectNavigator,NavigationConfig,NavigationResult,NavigationDefaultsare removed.project_scanner.dart—ProjectScannerclass is removed.
Migration #
All these APIs have v2 replacements in tom_build_base_v2.dart:
| Removed V1 API | V2 Replacement |
|---|---|
WorkspaceNavigationArgs |
CliArgs (from cli_arg_parser.dart) |
addNavigationOptions / parseNavigationArgs |
CliArgParser + OptionDefinition |
ProjectDiscovery.scanForProjects |
FolderScanner + BuildBase.traverse |
ProjectNavigator.navigate |
BuildBase.traverse |
ProjectScanner |
FolderScanner |
ProjectDiscovery.hasSkipFile |
FolderScanner skip logic |
ProjectDiscovery.applyModulesFilter |
FilterPipeline module filtering |
Preserved APIs #
findWorkspaceRoot()— Moved toworkspace_utils.dart(exported from both barrels). Same API, now usesdart:ioinstead of DCli.kBuildkitMasterYaml,kTomWorkspaceYaml,kTomCodeWorkspace,kBuildkitSkipYaml— Constants moved toworkspace_utils.dart.isWorkspaceBoundary()— Moved toworkspace_utils.dart.- All shared utility files (
build_config.dart,config_loader.dart,config_merger.dart,tool_logging.dart,path_utils.dart,processing_result.dart,yaml_utils.dart,build_yaml_utils.dart,show_versions.dart) are unchanged.
Internal #
show_versions.dart— Migrated fromProjectDiscovery/ProjectScannerto inline directory scanning withdart:ioandglob.- Removed v1-specific tests (10 tests removed; 547 remaining tests pass).
1.15.0 #
Breaking Changes #
- Renamed
--all/-ato--no-skip— The global CLI option that ignores skip markers (tom_skip.yaml,*_skip.yaml) has been renamed from--all/-ato--no-skip(no abbreviation). This resolves conflicts with per-command-a/--alloptions in buildkit tools (dependencies, publisher, gitcommit, gitbranch).
Features #
-
--no-skipflag in v1 system — AddednoSkipfield toWorkspaceNavigationArgs, wired throughaddNavigationOptions(),parseNavigationArgs(),ProjectDiscovery.scanForProjects(), andProjectNavigator. Both v1 (buildkit ArgParser) and v2 (CliArgs) systems now support--no-skip. -
--no-skipinprojectTraversalOptions— Added to the standard v2 option definitions for consistent help output.
1.14.0 #
Features #
AnchorWalkerclass — New utility for walking up the directory tree to find workspace/repository root "anchor" directories. Anchors are identified by.git(directory or file),tom_workspace.yaml, orbuildkit_master.yamlmarkers. Enables reusable upward-search logic for tools likegoto.
1.13.0 #
Features #
-
--all/-aflag — New CLI option to traverse into folders that would normally be skipped (subworkspaces,tom_skip.yaml,<tool>_skip.yaml). Skip messages still print but traversal continues. (Renamed to--no-skipin 1.15.0) -
Skip messages to stderr — FolderScanner now always prints skip messages to stderr when encountering workspace boundaries or skip marker files: "Skipping subworkspace: <folder>", "Skipping - tom_skip.yaml found: <folder>", "Skipping - <tool>_skip.yaml found: <folder>".
Bug Fixes #
allGlobalOptionsdedup precedence — Fixed option deduplication to use first-wins (putIfAbsent) instead of last-wins. User-definedglobalOptionsnow correctly take precedence overcommonOptionsdefaults.
Code Quality #
- Fixed
unnecessary_brace_in_string_interpslint issues incompletion_generator.dart. - Fixed
curly_braces_in_flow_control_structureslint issues innature_detector.dart.
1.12.0 #
Features #
-
BuildkitFolder.projectName— BuildkitFolder nature now reads thenamefield frombuildkit.yaml, enabling project name matching for buildkit-configured projects. -
--projectID and name matching — FilterPipeline now matches--projectvalues against project IDs and names from bothtom_project.yamlandbuildkit.yaml:TomBuildFolder: matchesproject_idandshort-idfieldsBuildkitFolder: matchesidandnamefields- Case-insensitive matching
-
handleSpecialCommands()— New utility function for tools to handlehelpandversioncommands consistently without custom parsing. -
BuildOrderComputer— Topological sort (Kahn's algorithm) moved from tom_build_kit to tom_build_base. Available for any tool that needs dependency-ordered traversal.
Breaking Changes #
-
Nature filtering is now mandatory —
BuildBase.traverse()throwsArgumentErrorif neitherrequiredNaturesnorworksWithNaturesis configured. Previously,nullrequiredNaturessilently visited all folders. Tools that want all folders must now setrequiredNatures: {FsFolder}orworksWithNatures: {FsFolder}explicitly. -
ToolRunner validates nature config —
ToolRunner._runWithTraversal()returnsToolResult.failurewith an error message before traversal starts if no nature configuration is present on the command.
Bug Fixes #
-
Nature detection before filter application — Fixed
BuildBase.traverse()to detect folder natures before applying project filters. Previously,applyProjectFilters()was called beforedetectNatures(), causing ID/name-based--projectmatching to always fail. -
tom_project.yamlfield name —NatureDetector._createTomProjectNature()now readsproject_id(underscore) in addition toshort-id(hyphen) fromtom_project.yaml.
Internal #
- ToolLogger / ProcessRunner — Central logging infrastructure with
--verbosesupport for consistent tool output.
1.11.0 #
Features #
-
Command prefix matching —
findCommand()now supports unambiguous command prefixes.:versmatches:versionerif no other command starts with "vers":cois ambiguous if both:compilerand:configexist, returns null- Exact matches (name or alias) always take priority over prefix matches
findCommandsWithPrefix()returns all commands matching a prefix (for error messages)
-
Improved error messages — When a prefix is ambiguous, tool shows all matching commands.
1.10.0 #
Features #
-
ExecutePlaceholderResolver — New placeholder resolution system for execute commands.
- Path placeholders:
${root},${folder},${folder.name},${folder.relative} - Platform placeholders:
${current-os},${current-arch},${current-platform} - Nature existence (boolean):
${dart.exists},${flutter.exists},${git.exists}, etc. - Nature attributes:
${dart.name},${dart.version},${git.branch}, etc. - Ternary syntax:
${condition?(true-value):(false-value)}for boolean placeholders checkCondition()for filtering based on boolean placeholders
- Path placeholders:
-
ExecutePlaceholderContext — Context class holding folder, root, and natures for resolution.
-
UnresolvedPlaceholderException — Exception thrown when placeholder cannot be resolved.
1.9.0 #
Breaking Changes #
- Default traversal behavior changed:
- Default is now
--scan . -R --not-recursive(workspace mode, single directory) - Previously defaulted to current directory without workspace root detection
- Use
-rflag to explicitly enable recursive traversal
- Default is now
Features #
- Traversal cascade: CLI options > buildkit_master.yaml navigation > hardcoded defaults
- Explicit CLI tracking:
scanExplicitlySetandrecursiveExplicitlySetfields in CliArgs - TraversalDefaults class: Loads navigation defaults from buildkit_master.yaml
- Git mode validation:
toGitTraversalInfo()now returns null if git mode not specified - WorkspaceScanner: Unified scanning API with FolderScanner + NatureDetector
- Top repository navigation (
-T, --top-repo): Traverse up to find topmost git repo - DartProjectFolder.isPublishable: Check if package can be published to pub.dev
Classes Modified #
CliArgs— AddedscanExplicitlySet,recursiveExplicitlySetfieldsTraversalDefaults— New class for config defaults withfromMap()factoryToolRunner._runWithTraversal()— Loads defaults, applies cascade, validates git mode_ParseState— Tracks explicit CLI options
1.11.0 #
Features #
-
WorkspaceScanner — Unified scanning API combining FolderScanner + NatureDetector.
scan()returnsScanResultswith type-safebyNature<T>()filtering.findGitRepos(),findDartProjects(),findPublishable()— Nature-based queries.findGitRepoPaths(),findDartProjectPaths(),findPublishablePaths()— Path convenience methods.FolderContextprovides folder + natures together withhasNature<T>(),getNature<T>().
-
DartProjectFolder.isPublishable — New getter to check if package can be published to pub.dev.
Exports #
- V2 traversal API now exported from main barrel:
WorkspaceScanner,GitFolder,DartProjectFolder, etc.
1.10.0 #
Features #
- Top repository navigation (
-T, --top-repo) — New git traversal option.- Traverses UP the directory tree to find the topmost (outermost) git repository.
- Uses that repository as the root for subsequent traversal.
- Can be combined with
-i(inner-first-git) or-o(outer-first-git). - Added
GitRepoFinder.findTopRepo()method for upward git repo discovery. - Example:
buildkit -T -i :compile— finds top repo, then processes inner repos first.
Classes Modified #
CliArgs— AddedtopRepofield.WorkspaceNavigationArgs— AddedtopRepofield and updated execution mode detection.GitRepoFinder— AddedfindTopRepo(String startPath)method.ProjectNavigator— IntegratedtopRepooption in navigation.CliArgParser— Added parsing for-Tand--top-repoflags.OptionDefinition— Addedtop-repotogitTraversalOptions.
1.9.0 #
Features #
- DCli integration — Refactored file operations to use DCli library for improved code readability.
File(path).existsSync()→exists(path)File(path).readAsStringSync()→read(path).toParagraph()Directory(path).listSync()→find('*', types: [Find.directory])- Improved directory filtering with DCli's
find()type filtering.
Dependencies #
- Added
dclipackage as dependency for file and directory operations.
Files Refactored #
build_config.dart— Config file loadingbuild_yaml_utils.dart— Build.yaml utilitiesconfig_loader.dart— Configuration loading with placeholdersproject_discovery.dart— Project discovery and scanningproject_scanner.dart— Project validation and scanningshow_versions.dart— Version display functionalityworkspace_mode.dart— Workspace navigation utilities
1.8.0 #
Features #
-
ConfigLoaderclass — New unified configuration loader with mode processing and placeholder resolution.- Loads
{basename}_master.yaml(workspace) and{basename}.yaml(project) configuration files. - Processes mode-prefixed keys (e.g.,
DEV-target,CI-enabled) with merging behavior. - Resolves
@[...]define placeholders from thedefines:section. - Resolves
@{...}tool placeholders (project-path, project-name, workspace-root, etc.). - Custom tool placeholders via
PlaceholderDefinition.
- Loads
-
Mode system — Workspace-wide configuration dimensions.
--modesCLI option to override active modes (e.g.,--modes=DEV,CI).- Mode sources: CLI option (highest) →
tom_workspace.yamldefault. - UPPERCASE mode prefixes merge in order, later modes override earlier.
-
Skip file system — Directory-level skip markers.
tom_skip.yaml— Skips directory for ALL tools.{basename}_skip.yaml— Skips directory for specific tool only.- Skip reason readable from YAML
reason:field.
-
resolvePlaceholders()function — Standalone placeholder resolution utility.- Supports
@[...]defines,@{...}tool placeholders. - Environment variable resolution with
$VARand$[VAR]syntax. - Recursive resolution (max depth 10).
- Supports
API Changes #
- New
config_loader.dartexported fromtom_build_base.dart. WorkspaceNavigationArgs.modes— New field for active modes.addNavigationOptions()registers--modesoption.parseNavigationArgs()parses modes as comma-separated, uppercased values.ProjectNavigatoraccepts optionaltoolBasenameparameter for tool-specific skip files.ProjectDiscovery.hasSkipFile(basename)— Updated signature with basename parameter.ProjectDiscovery.getSkipFileName(basename)— Returns tool-specific skip filename.ProjectDiscovery.globalSkipFileName— Constant fortom_skip.yaml.- v2
FolderScanner— Now supports tool-specific skip files:- Constructor accepts
toolBasenameparameter (defaults to 'buildkit'). - Checks for
tom_skip.yaml(global skip for all tools). - Checks for
{toolBasename}_skip.yaml(tool-specific skip). - New
skipFilenamegetter returns tool-specific skip filename. - New
kTomSkipYamlconstant exported.
- Constructor accepts
1.7.1 #
- Changelog update for 1.7.0 features.
1.7.0 #
Features #
ProjectNavigatorclass — New unified project navigation and discovery class that can be shared across CLI tools. Supports all navigation modes: project patterns, directory scanning, git-based traversal.NavigationConfigclass — Configurable opt-in/opt-out for navigation features (path exclude, name exclude, modules filter, skip files, master config defaults, build order, git traversal).NavigationDefaultsclass — Navigation defaults loaded from master config.NavigationResultclass — Result container with discovered paths and metadata.- Build order sorting —
ProjectNavigator.sortByBuildOrder()uses Kahn's algorithm for dependency-based topological sorting. - Git repository discovery —
ProjectNavigator.findGitRepositories()recursively scans for.gitfolders. - Static filter methods —
filterByPath(),filterByName(),filterSkippedProjects(),hasSkipFile(). - Master config loading —
loadNavigationDefaults()andloadMasterExcludeProjects()static methods.
API Changes #
- New
project_navigator.dartexported fromtom_build_base.dart. kBuildkitSkipYamlconstant now exported fromworkspace_mode.dart.toStringList()utility added toyaml_utils.dart.
1.6.0 #
Features #
--no-recursivesupport — The--recursiveflag is now negatable. Pass--no-recursiveto suppress recursion when applied viabuildkit.yamlor parent directories.--no-build-ordersupport — The--build-orderflag is now negatable. Pass--no-build-orderto skip dependency-based sorting.recursiveExplicitlySetfield —WorkspaceNavigationArgsnow tracks whether the-r, --recursiveflag was explicitly set by the user, allowing downstream tools to distinguish between defaulted and explicit values.
API Changes #
WorkspaceNavigationArgs.recursiveExplicitlySet— New boolean field indicating explicit user setting.parseNavigationArgs()now useswasParsed('recursive')to detect explicit usage.withDefaults()andwithProjectModeDefaults()respect explicit settings and don't override them.
1.5.0 #
Features #
--modules/-mnavigation option — New include filter to limit project discovery to specific git modules (repositories). Comma-separated list of module names (e.g.,--modules tom_module_d4rt,tom_module_basics). Use "root" or "tom" to reference the main repository.ProjectDiscovery.findGitRepositories()— Static method to discover all git repositories in a workspace.ProjectDiscovery.resolveModulePaths()— Resolve module names to absolute paths.ProjectDiscovery.filterByModules()— Filter project list to only those within specified modules.ProjectDiscovery.applyModulesFilter()— Convenience method combining resolution and filtering.
API Changes #
WorkspaceNavigationArgsnow includes amodulesfield (ListaddNavigationOptions()registers the-m, --modulesoption.parseNavigationArgs()parses the modules option as comma-separated values.- Help text updated with modules documentation.
1.3.2 #
Internal #
- Config filename standardization — Updated all code references from
tom_build.yamltobuildkit.yaml. TheTomBuildConfig.projectFilenameconstant was already correct; this release ensureshasTomBuildConfig()andProjectDiscovery.getProjectRecursiveSetting()use the constant instead of hardcoded strings.
1.3.0 #
Features #
yamlToMap()utility — Public function to recursively convertYamlMapto plainMap<String, dynamic>. Eliminates private YAML-to-Map conversion duplicated across build tools.yamlListToList()utility — Companion function to recursively convertYamlListto plainList<dynamic>.
Internal #
- Replaced private
_convertYamlToMapinbuild_config.dartand_yamlToMap/_yamlListToListinbuild_yaml_utils.dartwith the shared public utilities.
1.2.0 #
Features #
show_versionsCLI tool — New executable inbin/show_versions.dart. Run viadart run tom_build_base:show_versions [workspace-path]or install globally withdart pub global activate tom_build_base.showVersions()library function — Importable API inlib/src/show_versions.dartthat discovers projects and reads their pubspec versions. Returns a structuredShowVersionsResult.readPubspecVersion()helper — Reusable function to read theversion:field from any project'spubspec.yaml.
Improvements #
- Example file now delegates to the library function instead of reimplementing the logic.
1.1.0 #
Improvements #
- Comprehensive example — Rewrote the example as a
show_versionsCLI tool that exercises every library feature: config loading & merging, project scanning & discovery, build.yaml utilities, path validation, and result tracking. - Updated user guide — Complete rewrite of
doc/build_base_user_guide.mdwith accurate API signatures,ConfigMergerdocumentation,ProjectDiscoverysection, and an API quick-reference table. - Updated README — Refreshed usage examples to cover
ConfigMerger,ProjectDiscovery, and allbuild.yamlutility functions.
1.0.0 #
Features #
- TomBuildConfig: Unified configuration loading from
tom_build.yamlfiles with support for project paths, glob patterns, scan directories, recursive traversal, exclusion patterns, and tool-specific options. - ProjectScanner: Directory traversal with configurable project validation. Finds subprojects, scans directories recursively, supports glob-based project matching, and applies exclusion patterns.
- ProjectDiscovery: Advanced project discovery with proper scan vs recursive semantics. Scans until it hits a project boundary; recursive mode also looks inside found projects for nested projects. Supports comma-separated glob patterns with brace group handling.
- build.yaml utilities: Detect builder definitions (
isBuildYamlBuilderDefinition) vs consumer configurations (hasBuildYamlConsumerConfig) — so CLI tools can skip packages that define builders and only process consumer packages. - Path utilities: Path containment validation (
isPathContained) and multi-path validation (validatePathContainment) for security. - ProcessingResult: Simple success/failure/file-count tracking for batch operations.
- Multi-project support:
--projectoption accepts comma-separated lists and glob patterns (e.g.,tom_*,xternal/tom_module_*/*). --listflag support: Tools can list discovered projects without processing.