# cnf
A distribution-agnostic "command not found"-handler.
By [Andreas Hartmann (@hartan)][0]
![cnf demo screenshot][1]
[0]: https://gitlab.com/hartan
[1]: ./assets/cnf_screenshot.png
## Table of contents
1. [Project description][a]
1. [Who this project is for][b]
1. [Project dependencies][c]
1. [Installation][d]
1. [Configuration][e]
1. [Usage][f]
1. [Additional documentation][g]
1. [Troubleshooting][h]
1. [Contributing][i]
1. [Getting help][j]
1. [License and Copying][k]
[a]: #project-description
[b]: #who-this-project-is-for
[c]: #project-dependencies
[d]: #installation
[e]: #configuration
[f]: #usage
[g]: #additional-documentation
[h]: #troubleshooting
[i]: #contributing
[j]: #getting-help
[k]: #license-and-copying
## Project description
With `cnf` you can replace pesky "command not found" errors in interactive terminals with an
immediately actionable TUI that helps you do something about it. The main features of `cnf` are:
- Hooks right into your terminal (optional), no manual invocation necessary
- Look for installed commands in [`toolbx`][a0] or [`distrobox`][a1] containers
- Query various package managers for the missing command and install it if found
- "Aliases" to memorize where a missing command should be taken from
- User-provided extensions to look for commands wherever you want
[a0]: https://containertoolbx.org/
[a1]: https://distrobox.it/
## Who this project is for
This project is primarily targeted at terminal users that occasionally find themselves looking for
missing commands. In particular, this project may be of interest to anyone using:
- One of our [supported environments][b0] (i.e. toolbx, distrobox, ...)
- One of our [supported providers][b1] (i.e. apt, dnf, pacman, cargo, ...)
[b0]: https://docs.rs/cnf-lib/latest/cnf_lib/environment/enum.Environment.html
[b1]: https://docs.rs/cnf-lib/latest/cnf_lib/provider/enum.Provider.html
## Project dependencies
- **Runtime dependencies**
- *None*, except for any package managers you'd like to query for missing packages
- **Build dependencies**
- Refer to the [CONTRIBUTING guide][c0]
[c0]: ./CONTRIBUTING.md
## Installation
### Binary only
> No matter which installation method you choose, we highly recommend you check out the shell
> integration mentioned in the next section!
- Via `cargo`:
```bash
cargo install --locked cnf
```
- Via CI artifacts: Please check the [latest release][da0] for binaries for your platform
- From source:
```bash
bin/make release && install -Dm755 target/bin/cnf ~/.local/bin/cnf
```
> You must ensure that `~/.local/bin` is in your `$PATH` for this to work
[da0]: https://gitlab.com/hartang/rust/cnf/-/releases
### Shell integration
First, install the `cnf` binary as described in the section above. If you want to automatically run
`cnf` whenever a specific command isn't found, extend your `.bashrc`/`.zshrc` like this:
```bash
# If you're using bash, append this to '~/.bashrc'
eval "$(cnf --hooks bash)"
# Likewise for zsh, append this to '~/.zshrc'
eval "$(cnf --hooks zsh)"
```
If you don't know which shell you're currently using, the output of the
following command should tell you:
```bash
basename $(readlink -f /proc/$$/exe)
```
Now restart your shell or open a new terminal tab/window and try it out!
## Configuration
When you run this command for the first time, it will create a default configuration file in
`~/.config/cnf/cnf.yml` (or the matching equivalent for your platform). The options should be
self-explanatory. If not, refer to the [cnf config module][e1].
[e1]: https://docs.rs/cnf/latest/cnf/config/index.html
## Usage
The examples below assume you have already installed `cnf` and the necessary shell hooks, described
above. If you haven't, you must prepend `cnf` to the relevant command lines.
### cnf with toolbx or distrobox
If you are a toolbx or distrobox user, `cnf` can support you in transparently forwarding commands
between your host and a chosen toolbx or distrobox container. Here's an example of how `cnf` can
replace `toolbox run`:
```bash
# Before
$ htop
htop: command not found
$ toolbox run htop
# After
$ htop
# ... navigate the UI and execute
```
It also works the other way around. Assume you're currently inside your toolbx or distrobox
container and want to execute a command on your host. Here's an example of how `cnf` can replace
`flatpak-spawn --host`:
```bash
# Before
⬢ $ podman run hello-world
podman: command not found
⬢ $ flatpak-spawn --host podman run hello-world
# After
⬢ $ podman run hello-world
# ... navigate the UI and execute
```
### Command aliases
By default, `cnf` shows an interactive TUI with all results, allowing you to browse results and
select the appropriate one. When calling the same command repeatedly, interacting with the UI each
time quickly becomes annoying.
Command aliases are a mechanism that aims to solve two problems:
1. Eliminate user-interaction with the UI for certain commands (according to user choice)
2. Provide commands that aren't available in the current environment (e.g. your host OS) by calling
them in another environment (e.g. toolbx/distrobox).
To learn more, visit the [alias documentation][fb0].
[fb0]: https://docs.rs/cnf/latest/cnf/alias/index.html
## Additional documentation
All documentation currently takes place in the source code. You can browse an HTML-version of the
generated docs at the following locations:
- [`cnf` docs][g0], for the binary providing the TUI
- [`cnf-lib` docs][g1], for the library doing the heavy lifting
[g0]: https://docs.rs/cnf/latest/
[g1]: https://docs.rs/cnf-lib/latest/
## Troubleshooting
*Please let us know if the project gave you trouble that isn't listed below!*
### `cnf` and sudo
When running commands with `sudo`, you will realize that a default "command not found" text is
displayed. That is because `sudo` performs its own executable lookups, and if it can't find the
command you were asking it to execute, it will print this error and exit.
``` bash
$ sudo foobar
sudo: foobar: command not found
```
Depending on what you were trying to do, this issue can be worked around like this:
1. **If you wanted to run an *alias* command with elevated privileges**: Replace `sudo` with `sual`,
which is a custom shell function set up for you by the `cnf` shell hooks:
```bash
sual foobar
```
2. **If you wanted to run a command that isn't found and has no *alias***: Wrap the command in `cnf`
manually, like this:
```bash
sudo cnf foobar
```
## Contributing
We accept various contributions (including bug reports, bug fixes, feature suggestions, ...). Please
refer to [the CONTRIBUTING guide][i0] for additional information.
[i0]: ./CONTRIBUTING.md
## Getting help
If you need help, take a look at [our issue tracker][issue_tracker]. If you feel your particular
problem isn't handled there, [please open an issue][open_an_issue] and let us know.
## License and Copying
Copyright (C) 2022 Andreas Hartmann
This program is free software: you can redistribute it and/or modify it under the terms of the GNU
General Public License as published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License in [the license file][k0] along
with this program. If not, see <https://www.gnu.org/licenses/>.
[k0]: ./COPYING
[open_an_issue]: https://gitlab.com/hartang/rust/cnf/-/issues/new?type=ISSUE
[issue_tracker]: https://gitlab.com/hartang/rust/cnf/-/issues?state=all