Add a Command
Add a custom command for tasks such as cleanup, setup, or a development server.
Add a custom command for work that does not fit an action. An action runs once and reports an outcome. A development server runs until you stop it, while a cleanup task removes files rather than reporting findings. A command lets you write that behavior and expose it beside your actions.
This guide adds clean, a command that removes the build output of the
project. You need a working harness. If you do not have one yet, follow
Get Started first.
If you want to run several actions together, form a group. Rakko then runs the members and reports their results for you.
Add the Contract Crate
The command uses the types of the contract crate, rakko. Add it to the
dependencies of the harness:
[dependencies]
rakko = "0.1"
rakko-cli = "0.1"
rakko-format-rust = "0.1"
rakko-lint-rust = "0.1"
rakko-test-rust = "0.1"
rakko-cli also exports Clawless, the command-line framework.
Your harness does not need a separate Clawless dependency.
Write the Command
Put the command in its own module so that main.rs stays a short list of
mounts. Create tools/rakko/src/clean.rs with these imports:
use rakko::{Context, Documented, Name, action_name};
use rakko_cli::Command;
use rakko_cli::clawless::CommandResult;
use rakko_cli::clawless::context::Context as ClawlessContext;
A command is a type that implements the trait Command of rakko-cli. Add
the command below the imports:
/// Removes the build output of the project
///
/// Cargo writes its build output into the directory `target` in the root of
/// the project. The command removes that directory, and it says so when there
/// is nothing to remove.
#[derive(Debug, Documented)]
pub(crate) struct Clean;
impl Command for Clean {
type Args = ();
fn name(&self) -> Name {
action_name!("clean")
}
async fn run(
&self,
project: &Context,
clawless: &ClawlessContext,
_args: &Self::Args,
) -> CommandResult {
let target = project.root().get().join("target");
if !target.try_exists()? {
clawless.output().message("nothing to remove").await?;
return Ok(());
}
std::fs::remove_dir_all(&target)?;
clawless.output().message("removed target").await?;
Ok(())
}
}
The code has these parts:
- The
Documentedderive uses the first paragraph of the doc comment as help text. Write that paragraph as one sentence. - The name is what users type after
mise run rakko --. It starts with a lowercase ASCII letter and contains lowercase ASCII letters, digits, and single hyphens.action_name!rejects invalid names at compile time. Argsis the unit type becausecleantakes no arguments. Commands declare arguments in the same way as actions. Build an Action shows how those declarations become flags.runis asynchronous code that receives the project context and the Clawless context. Use the Clawless context to write output.
run returns success or an error. Success produces exit code 0. An error
produces exit code 2 and a report of the error and its causes. The ?
operator stops the command when a step fails, such as removing the directory.
Mount the Command
Declare the module in tools/rakko/src/main.rs, and give the command to
mount:
mod clean;
use clean::Clean;
/// Builds the command line of the project and runs it
fn main() -> std::process::ExitCode {
rakko_cli::builder()
.mount(rakko_format_rust::FormatRust)
.mount(rakko_lint_rust::LintRust)
.mount(rakko_test_rust::TestRust)
.mount(Clean)
.run()
}
Actions, groups, and commands share one namespace. If two mounted entries
have the same name, the harness stops at startup and reports the duplicate.
The names doctor and help are reserved.
Run the Command
Run the command like an action:
mise run rakko -- clean
The help of the harness lists the command beside the actions, with the sentence of its doc comment:
mise run rakko -- --help
Custom commands use the same environment checks as actions. If the tool pins
do not match the mounted actions, the command stops and asks you to run
mise run rakko -- doctor --fix.
Run Actions From a Command
A command can run actions, for example to repeat a check when files change. Pass the required actions from the harness to the command. Commands cannot look up mounted actions themselves.
Run each action through ErasedAction::run in rakko. This path also skips
an action when its tool has no build for the current platform.
doctor reads tool declarations from mounted actions and group members.
If only a custom command holds an action, doctor cannot see it. Mount that
action as well so that doctor --fix includes its tools.