Creating CLI tool using rust, build and release: Do it like wooshh ๐Ÿš€

Creating CLI tool using rust, build and release: Do it like wooshh ๐Ÿš€

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 2or 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

Related Posts

Auto-reload Development Mode โ€” For celery worker using docker-compose and Django management commands.

Auto-reload Development Mode โ€” For celery worker using docker-compose and Django management commands.

If you are using docker-compose for Django projects with celery workers, I can feel your frustration, and here is a possible solution to that problem.

Read More
Setting up a Microk8s cluster with a domain and SSL certificate

Setting up a Microk8s cluster with a domain and SSL certificate

Microk8s is a lightweight Kubernetes distribution that’s perfect for development, testing, and small-scale deployments.

Read More
How do I setup my dev box ?

How do I setup my dev box ?

Hey, This blog post serves as a guide to how I set up my MacBook and the tools I rely on.

Read More