Releases: guilyx/rostree
Release list
v0.4.0
v0.3.0 made big trees fast. This one makes them yours: on a sourced ROS 2
machine most of what rostree can see belongs to the distro, and until now there
was no way to say so.
Added
- Scope filters on every command that walks the graph (
tree,graph,why,
rdeps,check,diff):-w/--only-workspace— ignore packages installed under/opt/ros--include GLOB/--exclude GLOB— repeatable shell globs on package names
(--include 'nav2_*',--exclude '*_msgs'); excludes win over includes- A filtered-out package is neither shown nor followed, so anything reachable
only through it disappears with it. Commands report what was hidden rather
than silently presenting a smaller tree as the whole truth.
--dep-type runtime|build|test|allto choose whichpackage.xmltags to
follow.-r/--runtimestays as a shorthand for--dep-type runtime.rostree diff— what did this package gain, lose or bump?rostree diff <a> <b>compares two packagesrostree diff <pkg> --save FILEsnapshots the current dependency set, and
--against FILEcompares against it after a rebuild- Reports added / removed / version-changed and exits non-zero on any drift
rostree check --junit FILEwrites a JUnit XML report for CI dashboards.DependencyNode.hidden_childrenrecords how many dependencies a truncated node
is not showing, so… N morenever promises more than the tree would print.build_dependency_tree()andbuild_dependency_graph()acceptpackage_filter,
reportand (on the tree)include_tags.- Bandit runs in CI and pre-commit and is in the
devextra, so
bandit -c pyproject.toml -r src testsreproduces the security scan that gates
pull requests instead of it existing only in a dashboard..codacy.yamlrecords
which rules are switched off for the test suite and why; accepted findings under
src/carry an inline suppression with its reason, and
development.md writes down where those
comments have to sit, which is less obvious than it sounds.
Changed
rdeps --workspace-onlyis now--only-workspace; the old spelling still works.- The JUnit writer moved out of
cli.pyintocore/junit.py. It is the only code
in rostree that writes XML and never reads any, and keeping it separate lets that
argument be made once, at the top of a short file, instead of on every line of a
1,300-line module. - The TUI's widget guards no longer catch bare
Exception. Eleventry/except Exception: passblocks aroundquery_onebecame
contextlib.suppress(QueryError, ScreenStackError), so a bug inside a guarded
block raises instead of disappearing. The two guards that are deliberately
broad — best-effort tree expansion and collapse, which a background rebuild can
interrupt — say so in a comment.
Security
package.xmlis now parsed with defusedxml,
a new runtime dependency.core/parser.pyis the only place rostree reads XML it
did not write, and a manifest is just a file in a workspace: one declaring
entities could previously make the parser expand them until it ran out of memory.
Such a manifest is now refused, which reports the package as unreadable instead
of hanging. ADOCTYPEthat declares nothing still parses, so this drops no
package that used to work.
v0.3.0
Large trees used to take tens of seconds — or never finish. This release makes
them near-instant and reworks the interface around them.
Performance
-
Dependency trees are linear, not exponential. A ROS dependency graph is a
DAG:rcutilssits under nearly every branch. Expanding every path separately
produced tens of thousands of duplicate nodes. Each package is now expanded once,
where it first appears, and marked↩ see aboveelsewhere — the same convention
cargo treeuses. Measured on a 135-package install space plus a 24-package
source workspace, resolvingnav2_bringupwith-r:depth before after 5 4,152 nodes / 1.02 s 475 nodes / 0.02 s 6 18,273 nodes / 4.44 s 548 nodes / 0.02 s 7 68,081 nodes / 17.41 s 552 nodes / 0.02 s unlimited (the CLI default) did not finish in 4 minutes 552 nodes / 0.02 s Pass
--fulltorostree treefor the old fully-expanded behaviour. -
Package discovery happens once. New
rostree.core.index.PackageIndexscans
every install prefix and source tree a single time and resolves names from memory.
Previously every node of a tree could trigger a fresh recursiveos.walk. -
package.xml parsing is memoized by path, mtime and size.
-
Source scans prune
build/,install/,log/, VCS metadata and directories
markedCOLCON_IGNORE/AMENT_IGNORE, and stop descending once a manifest is found. -
rostree graphresolves the whole DAG in one breadth-first pass instead of
building a separate full tree per package.
Added
rostree why <package> <dependency>— shortest dependency paths between two
packages, answering "why is this in my tree at all?".rostree rdeps <package>— reverse dependency lookup, with--transitive
and--workspace-only.rostree check— reports dependency cycles and unresolved dependencies and
exits non-zero, so it can gate CI.rostree list --filter TEXTto narrow the package list.rostree tree --full,--expand-repeats,--max-nodesand-vfor descriptions.--no-coloron all commands (NO_COLORis honoured too).NodeStatusenum onDependencyNode(ok,repeat,cycle,missing,
parse_error,truncated) plusis_error/is_placeholder, replacing string
markers stuffed intodescription.to_dict()now includesstatus.- Public API:
build_graph(),reverse_dependencies(),get_index(),tree_stats(). - TUI: live filter over every package, reverse-dependency view (
v), dependency
scope toggle (t), and a help screen (?).
Fixed
- Graphs no longer drop edges to unresolved packages. Dependencies without a
manifest are drawn dashed and grey instead of being silently removed, which used
to leave workspace graphs as a field of unconnected boxes.--hide-missing
restores the old behaviour. rostree graph -w PATHnow works on a workspace that is not sourced — the
workspace's own packages are added to the search path.- Unbuilt packages in a sourced workspace are found again. The source root was
derived as<ws>/install/srcinstead of<ws>/src, sosrc-only packages were
invisible whenever the workspace was discovered throughAMENT_PREFIX_PATHor
COLCON_PREFIX_PATH. Both the merged (<ws>/install) and isolated
(<ws>/install/<pkg>) colcon layouts are now handled. - Packages named
lib*are no longer discarded.libstatistics_collectorand
libyaml_vendorare real ROS 2 packages; only dashed rosdep keys
(libboost-dev,python3-numpy) are treated as system dependencies, and those
are now kept onPackageInfo.system_dependenciesrather than dropped. - The TUI no longer freezes while a tree is built: resolution runs on a worker
thread and rows are created only as nodes are expanded. - The TUI package list is no longer capped at 80 entries per source.
- Text trees use correct box-drawing characters (
└──for last children, proper
vertical guides); previously every child was drawn as├──. - An unknown package name is now an error with suggestions and a non-zero exit,
instead of a one-node tree and exit 0. - The five duplicated hand-rolled
<name>XML scrapers were replaced by a single
quick_package_name()helper. - Worker results in the TUI are routed to the callback that asked for them, rather
than to whichever handler saw the completion event first. ↩ see abovenow always refers to something printed earlier. Expansion was
chosen by shortest distance from the root while the tree renders depth-first, so
a back-reference could point at a subtree printed below it. Each package is now
expanded where the tree first prints it, which also removes a whole breadth-first
pass from tree building.- Reverse dependencies were cached without keying on the dependency tag set, so a
runtime-only lookup and a full lookup returned whichever ran first. rostree why <pkg> <pkg>reported a dependency path for a package that does not
exist; the validation loop skipped both arguments when they were equal.- Global flags such as
--no-colorare accepted after the subcommand too
(rostree tree rclcpp --no-colorused to be a usage error). - TUI: pressing
e(expand all) or running a search no longer duplicates every
row of an already-expanded tree. - TUI: selecting a second package while a tree is still building no longer leaves
the app pointing at a tree it never rendered. - TUI: adding a source path while a tree is open now rescans, instead of leaving
the new packages invisible. - TUI:
Escleaves the dependents view when it was opened from the package list. --openno longer goes through a shell on Windows: it usesos.startfile, so a
path containing shell metacharacters cannot be misinterpreted. External tools
(dot,open,xdg-open) are resolved to absolute paths before being executed.
Changed
rostree treegroups sibling back-references onto one line
(↩ 5 already shown above: …);--expand-repeatslists them individually.- Depth-limited nodes report how many dependencies are hidden (
… 5 more). richis now a direct dependency (it was already installed viatextual).- Lint rules are pinned in
pyproject.tomlso a new ruff release cannot change
what CI enforces.
v0.2.2
Added
- TUI background loading: Package scanning now starts immediately when app opens (before pressing Enter)
- Loading indicator: Shows spinner and status while scanning for packages
- Ready status: Welcome screen shows package count when scanning completes (e.g., "✓ 123 packages found")
Changed
- TUI uses cached packages from background scan for instant navigation
- Refresh action (
r) now clears cache and rescans in background
v0.2.1
Fixed
- Dynamic versioning:
rostree --versionnow correctly reads version from package metadata instead of hardcoded value - TUI banner alignment: Fixed ASCII art banner with inconsistent character alignment causing visual shifting
Changed
- Version is now sourced from
importlib.metadatafor single source of truth (pyproject.toml)
v0.2.0
[0.2.0] - 2026-02-05
Added
rostree graphcommand: Generate dependency graphs in DOT (Graphviz) or Mermaid format- Direct image rendering:
--render png|svg|pdfcreates image files - Two rendering backends: Graphviz (system) or matplotlib (pip:
rostree[viz]) - Auto-open:
--openopens the rendered image in default viewer - Single package:
rostree graph rclpy --render png - Entire workspace:
rostree graph --render pngorrostree graph -w /path/to/ws --render svg - Output to file with
-o/--outputor stdout - Support for depth limiting with
-d/--depth - Runtime-only dependencies with
-r/--runtime - Formats:
--format dot(default) or--format mermaid - Progress output when processing multiple packages
- Direct image rendering:
- TUI improvements:
- Full-page welcome screen with centered banner and app description
- Search functionality (
/orfto search,n/Nto navigate matches) - Details panel toggle (
dto show/hide) - Keyboard-only navigation throughout
Changed
- Welcome screen is now a full view instead of a modal overlay
- Improved package source categorization display