brush-v0.4.0
chore: Release package brush version 0.4.0
chore: Release package brush version 0.4.0
This is a significant release, containing several months of work and more than 200 merged PRs. It includes a broad set of new features, bug fixes, and quality improvements, most notably a myriad of bash compatibility fixes that materially raise the bar on which real-world scripts and interactive sessions brush can handle.
Some key highlights:
set -e, set -u, pipefail, failglob, the ERR trap, coprocesses, and a great deal more.wasm32-wasip2 is now exercised in CI.preexec/precmd hooks, experimental terminal integration, expanded readline-macro support, and many completion improvements.winnow-based parser, a generic Shell<Extensions> for embedders, an opt-in bundled-coreutils build, and serde features for both AST and shell state. This work has required breaking changes to API surface, mostly to brush-core. Some changes were also made to brush-parser's API but we expect migration for parser-only consumers to be straightforward and relatively minimal. Please reach out to us if you encounter challenges in this area.A heartfelt thank-you to everyone who filed issues, reproduced bugs on unusual platforms, and contributed code over the last several months 🙏. A significant percentage of the work below is in direct response to community reports.
This release expands brush's coverage of widely-used bash language features. Among the most commonly-relied-on additions:
set -e (errexit) and pipefail (#852) are implemented with bash-faithful semantics, including the long list of exemptions bash has accumulated over the years. See the compatibility reference for the current status of supported set options.set -u (nounset) (#774, #1007) now actually errors on unset variable references, including the subtle interaction with ${#arr[i]}.failglob (#1011) treats no-match globs as an error.ERR trap (#1020) is implemented, complementing the EXIT trap work from the previous release. As a corollary, EXIT traps now also fire correctly under -c (#1080).coproc) (#1029, #1068), both parsing and execution. See the compatibility reference.--noglob/-f command-line flag (#1091), proper -- option-terminator handling for -c (#1076), and an exit code of 2 for unknown command-line options (#1050).Heads up: because
set -e,set -u,pipefail, andfailglobpreviously had limited effect in brush, scripts that ran cleanly under v0.3.0 may now exit with errors that bash would also have produced. This is the intended behavior. If you see a divergence from bash, please file an issue.
Beyond the headline options, this release lands a long list of more focused compatibility fixes. Many are small, but in aggregate they account for the bulk of the change list. Highlights worth calling out:
$[expr] syntax (#1004), the missing |= and ^= assignment operators (#911), array indices inside array indices (#910), arithmetic-for parsing/eval fixes (#1018), and infinite-recursion detection (#1021).nullglob + quoted-empty-string interaction (#1035).caller was added (#812), read is now much closer to feature-complete (#914), mapfile -O works (#558), getopts got OPTERR semantics (#1048) and short-option fixes (#827), compgen -A picked up binding, command, and reserved-word completion (#814, #1027, #1047), printf %q quotes empty strings as '' (#1026), and the rules for shadowing special builtins with functions are now correct (#922).BASH_ARGC/BASH_ARGV (#1013) and $_ (#1030) are populated; OSTYPE is set on macOS (#1097); prompts handle \l, \[/\], and $? correctly (#913, #909, #798); and $- correctly reflects -c (#767).If you've previously hit a bash-compatibility issue with brush, there's a decent chance it's resolved here. If not, please open an issue!
v0.4.0 also includes a substantial amount of plumbing work that won't show up in our feature lists but contributed to raising our quality. A big thanks to contributors who put these (and others) together:
unwrap() audit (#921) hardened brush against a class of unusual-input edge cases.| head pipeline (#873, #875).dup failures from clone) are now reported cleanly rather than aborting (#1051).We aim for portability and this release advances that goal on several fronts:
chsh target on a Mac, please give it a try./dev/null emulation (#1044, #1104), and CI now exercises the Windows test suite (#1083) to keep the platform on solid footing. Windows support remains in an experimental state, but it has matured meaningfully this cycle. If you've tried brush on Windows previously, this is a good time to revisit, and we'd appreciate hearing how it goes.split_paths is now safe under WASM (#1064), and basic wasm32-wasip2 smoke tests run in CI (#1098).The interactive experience picked up several things that interactive-shell users have requested:
brush-shell: an opt-in alternative to shell-script-based configuration for brush-specific shell-level settings (#895). See the configuration reference for the file format, location, and available settings. Experimental.preexec / precmd hooks in the style of zsh: useful for prompt frameworks, timing, integration tools (#652). Enable via the [experimental] zsh-hooks setting documented in the configuration reference. Experimental.[experimental] terminal-shell-integration setting in the configuration reference. Experimental.inputrc files (#967). Many additional fixes to improve compatibility with shell extension tools like starship, atuin, fzf, and others.. and .. are offered (#887); mark-directories is honored (#817); COMP_KEY/COMP_TYPE are populated for completion functions (#1008); COMP_WORDBREAKS default matches bash (#828); word tokenizing during completion was corrected (#816); and deduplication of completions is deferred to the presentation layer so functions see the full list (#1012).The brush crates are published to crates.io, and several of the changes in this release are aimed at people building on top of brush-core, brush-parser, et al. The most important ones:
Shell is now generic over a ShellExtensions type parameter (#941). This is the scaffolding that lets embedders extend brush with custom builtins, custom variable behavior, and other hooks without forking. Existing code that names Shell directly will need to either pick a concrete extension type or thread the generic through; a default extension type is provided for the common case. We plan to continue building on this for better low-overhead, static inspection and extension of brush-core behaviors.Shell fields are now private (#900) and shell.rs was split into shell/*.rs (#945). Callers using the public re-exports and accessor/builder methods are unaffected; callers reaching into private state will need to migrate.serde features are now available on both brush-parser (#783) and brush-core (#831) for AST and shell-state serialization. This makes a number of new use cases viable, e.g.: memoizing initialization, snapshotting shell state.winnow-based parser (#974). The PEG parser remains the production parser; the winnow work is groundwork for a faster, more diagnosable replacement in a future release.brush-parser: ParsingNearToken was renamed to ParsingNear (#1022).fuzz-testing Cargo feature was renamed to arbitrary (#844) to better reflect what it actually gates.cargo xtask subcommands (#898); see cargo xtask --help.A few items in this release are intentionally marked experimental; they are usable today, but the shape of the API or UX may evolve based on feedback (hint: we want your feedback!):
ls, cat, etc.) internally, useful for container/embedded scenarios where a coreutils package isn't available, or for platforms like Windows that don't typically offer these utilities. See the experimental features reference.save builtin (#835): primarily a debugging and tooling aid today, but a building block for future features. See the experimental features reference.preexec/precmd, and terminal-integration features mentioned above are all in the experimental bucket. The experimental features reference covers how to enable each one. Try them and tell us what feels off.If you're a script author or interactive user (rather than an embedder), these are the changes most likely to be visible to you:
set -e / set -u / pipefail / failglob enforcement. These options had limited effect in v0.3.0; they now behave like bash. Scripts that previously ran without surfacing errors may need updates.readonly now operates on global scope (#1003), matching bash. Code that depended on the prior local behavior will observe a difference.2 (#1050), matching bash.Releases like this one don't happen without the people who file thoughtful issues, reproduce bugs on platforms the maintainers don't have access to, and send code. Thank you to everyone who contributed to this release! Whether your name appears below or you helped via an issue, a discussion, or a reproducing test case we value and count on your contribution.
Special thanks go to the contributors whose work shows up in many of the bullet points above:
winnow-based parser. Thank you for the sustained, foundational contributions.And thank you to everyone else who authored or co-authored a commit in this release:
If you contributed and aren't listed here, that's a bug and unintentional! Please let me know and I'll correct it.
For the exhaustive, auto-generated list of every commit and PR since brush v0.3.0, see CHANGELOG.md.
chore: Release package brush-parser version 0.4.0
chore: Release package brush-interactive version 0.4.0
chore: Release package brush-experimental-builtins version 0.1.0
chore: Release package brush-coreutils-builtins version 0.1.0
chore: Release package brush-core version 0.5.0
chore: Release package brush-builtins version 0.2.0
chore: Release package brush version 0.3.0
This is a major release, containing architectural improvements and compatibility-focused features and fixes. A big thanks to all of the contributors over the last few months, including issue filers, question-askers, and code contributors 🙏.
Based on user-filed issues (thank you!), targeted work went into improving compatibility with popular tools like fzf and neofetch.
Additionally, a significant amount of work went into refactoring and improving usability of the public API surface within the crate-based libraries that make up brush's core. These come with breaking changes for programmatic clients, but we're confident they will come with significant benefit.
The following summary covers the most notable changes and the full change details are below.
fc builtin received its initial implementation!bind implementation received support for binding key sequences to readline functions (e.g., "\C-a":beginning-of-line).trap now fully implements the EXIT trap.BASH_XTRACEFD was added. A new command-line parameter to brush (--inherit-fd) enables allow-listing inherited file descriptors for use within the shell.for loops has been implemented for improved compatibility.\' in double-quoted word contexts."${var:+'x'}").$(emit-a-lot-of-text)).exec (e.g., exec 2>/dev/null) are now reflected immediately, even when in a loop context.Changes in this section pertain to programmatic clients of the brush-core, brush-parser, and brush-interactive crates. They do not apply to application-level shell users.
brush-builtinsAll shell builtins have been moved into a new dedicated brush-builtins crate. With this move, significant refactoring was done to ensure that these builtins are all implementable using public APIs exposed by brush-core. In doing so, this also significantly advanced the richness of the latter APIs.
Important
Programmatic clients of the brush-core crate will find that a constructed Shell is not automatically populated with the usual set of builtins. New optional APIs exist in the brush-builtins crate to retrieve default builtins and in brush-core to register them with a Shell instance.
As an important step on our journey to evolve brush's parsers, @lu_zero has made contributions that start enriching our parsed Abstract Syntax Trees (ASTs) with links back to the source text. As part of this work, a new SourceLocation trait was introduced and implemented for many AST node types. Work will continue in the coming releases.
To improve ergonomics, we've introduce builder-based construction utilities to key structures like brush_core::Shell. We will continue to expand on this adoption over time to simplify the work of constructing these values.
Note that the signature of Shell::new() has changed and may require adjustment in callers.
As preparatory work for future improvements, we've overhauled the core error types and execution result types in brush-core:
Error has been renamed to ErrorKind, with a new wrapping Error struct type that can carry additional context. This will be used in the future for improved diagnostics.
Clients of brush-core can now provide their own custom error formatter to render a displayable string from an Error value. This will be used by some of our frontends for fancier diagnostics in the future. It also decouples error formatting from writing.
Multiple overlapping result types across the core interpreter, command utilities, and builtins have been consolidated into a coherent set of ExecutionResult, ExecutionExitCode, and ExecutionControlFlow types. Along the way a handful of bugs for edge cases were identified and resolved as part of the streamlining.
Shell builder pattern in more code by @reubeno in #688Full Changelog: brush-shell-v0.2.23...brush-shell-v0.3.0