Creating CLI tool using rust, build and release: Do it like wooshh ๐
- Aniket Patel
- Rust , CLI tools , Github actions
- September 8, 2023
Introduction
Command line tools are useful for performing various tasks in a terminal. Rust is a fast and reliable programming language that can be used to create command line tools. In this blog post, I will show you how to write a simple command line tool in Rust
๐ฆ, how to create a GitHub workflow to build binary cross-platform, and how to create a Brew package with a tar file.
I learned through creating simple project called โwooshhโ. It was command line tool which can become replacement for time command utility in linux. It plays sound when command finishes to signal the user that command finished its run. You can checkout project wooshh here.
Writing a command line tool in Rust with clap
Clap
ย is a command line argument parser forย Rust
ย ๐ฆ. It provides a macro to declare the application’s arguments and subcommands. It also generates help and version messages and provides autocompletion forย bash
,ย zsh
, andย fish
ย viaย clap_complete
Creating the project
cargo init
// This will install clap package. It will add package to cargo.toml
cargo add clap --features derive
Writing the code
Define the cli structure. In my case, Use command would be something like wooshh sleep 2
or wooshh -o output.txt sleep 10
struct Cli {
// Define a command field to hold the command to execute
#[clap(name = "command")]
command: String,
// Define an args field to hold arguments for the command
#[clap(name = "args")]
args: Vec<String>,
// Define an append field to specify whether to append to an output file
#[clap(
short = 'a',
long,
help = "Append to output file instead of overwriting"
)]
append: bool,
// Define an output field to specify an output file
#[clap(short = 'o', long, help = "Write output to a file instead of stdout")]
output: Option<String>,
}
You can check remaining logic here. wooshh/src/main.rs
Compiling and running the program
You can test running following commands.
cargo run -- sleep 2
cargo run -- -o output.txt sleep 10
Creating a GitHub workflow to build binary cross-platform
Adding the workflow file.
name: Release
permissions:
contents: write
on:
push:
tags:
- v*
jobs:
create-release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: taiki-e/create-gh-release-action@v1
with:
changelog: CHANGELOG.md
title: $version
token: ${{ secrets.GH_PAT }}
upload-assets:
strategy:
matrix:
include:
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
- target: x86_64-apple-darwin
os: macos-latest
# - target: aarch64-unknown-linux-gnu
# os: ubuntu-latest
# audio_backend: alsa_backend
- target: aarch64-apple-darwin
os: macos-latest
# Universal macOS binary is supported as universal-apple-darwin.
# - target: universal-apple-darwin
# os: macos-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Install alsa
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get install libasound2-dev
- name: Install libjack
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get install libjack-jackd2-dev libjack-jackd2-0
- name: Install cross-compilation tools
uses: taiki-e/setup-cross-toolchain-action@v1
with:
target: ${{ matrix.target }}
if: startsWith(matrix.os, 'ubuntu')
- uses: taiki-e/upload-rust-binary-action@v1
with:
# (required) Comma-separated list of binary names (non-extension portion of filename) to build and upload.
# Note that glob pattern is not supported yet.
bin: wooshh
# (optional) Target triple, default is host triple.
target: ${{ matrix.target }}
archive: $bin-$tag-$target
# (required) GitHub token for uploading assets to GitHub Releases.
token: ${{ secrets.GH_PAT }}
As you can see in above workflow file, We are using taiki-e/create-gh-release-action@v1
, taiki-e/setup-cross-toolchain-action@v1
and taiki-e/upload-rust-binary-action@v1
when we add release tag to CHANGELOG.md It will create release, it will generate binary for mentioned platforms and upload it to create release as tar files with release tag.
Publishing a Rust CLI to Homebrew
I publish wooshh - Revolutionary tool ๐ using homebrew and you can do it too.
As release packages already been generated in last step. You can see wooshh releases here .
I’m explicitly defining tap with github repo as I’m not following homebrew conventions. You can create repo with name like homebrew-tools
to avoid URL mention.
brew tap mehuaniket/tools https://github.com/mehuaniket/tools.git
brew install mehuaniket/tools/wooshh
After you create github repo, To create homebrew Formula you can need to create formula file under Formula folder. ex: Formula/wooshh.rb
class Wooshh < Formula
desc "A tool to play sound when cmd run successfully"
homepage "https://github.com/mehuaniket/woshh"
url "https://github.com/mehuaniket/wooshh/releases/download/v0.2.1/wooshh-v0.2.1-x86_64-apple-darwin.tar.gz"
sha256 "859732114c5e83b8cc921af4aed1b5958774502a32d6bbeaadfb7fc102475fc3"
version "0.2.1"
def install
bin.install "wooshh"
end
end
You just need to replace the strings to match your pacakge.
Done ๐ย and please try tool wooshh
References and learn more:
https://lib.rs/crates/toastify
https://betterprogramming.pub/building-cli-apps-in-rust-what-you-should-consider-99cdcc67710c