Skip to content

Building and distributing your CLIs

To build and distribute you own CLI I recommend you use uv..

In this quick walkthrough we'll be creating a CLI called zit, a basic clone of git.

Creating a new CLI

First, you'll want to create a project. For that, follow uv's most up to date documentation about creating a new project.

A quick summary at the time of writing is:

  1. Create a project directory:

    mkdir zitcd zit

  2. Initialize a project:

    uv init

  3. Install clypi:

    uv add clypi

  4. Code your CLI. uv created a main.py file but you should create your own python package inside a subdirectory called zit. Inside that subdirectory create an empty file called __init__.py and a file called main.py with the following content:

    tree.
    ├── README.md
    ├── pyproject.toml
    ├── uv.lock
    └── zit
    ├── init.py
    └── main.py

    # zit/main.py
    import clypi
    from clypi import Command, arg
    
    class Zit(Command):
        """
        A git clone, but much slower ;)
        """
        verbose: bool = arg(False, short="v")
    
        async def run(self):
            clypi.cprint("Sorry I don't know how to use git, it's too hard!", fg="yellow")
            if self.verbose:
                clypi.cprint("asdkjnbsvaeusbvkajhfnuehfvousadhvuashfqei" * 100)
    
    def main():
        """
        This will be the entrypoint for our CLI
        """
        zit = Zit.parse()
        zit.start()
    
    if __name__ == "__main__":
        main()
    
  5. Test out your new CLI. You can run it locally with:

    uv run ./zit/main.py

  6. You'll need to add a build system so that uv understands this is a package you want to distribute and people to install. Add the following to your pyproject.toml

    + [build-system]
    + requires = ["hatchling"]
    + build-backend = "hatchling.build"
    
  7. Add an entrypoint to your Python package. This tells Python how to execute your program. Add the following lines to your pyproject.toml

    + [project.scripts]
    + zit = "zit.main:main"
    
  8. Install your package locally and run it

    $ uv pip install -e .
    $ zit --verbose
    Sorry I don't know how to use git, it's too hard! ...
    

Building and distributing your CLI

I highly recommend you follow uv's guide on building an publishing packages.

The TLDR is uv build then uv publish, but you'll want to set up your project with the right metadata. The official packaging Python guide is really good to configure all you'll need for distributing a quality package.

[Advanced] Pre-built binaries (Shivs 🔪)

Shiv's provide an easy way to bundle Python code into an executable file. Shiv's are, essentially, an executable zip file with Python files inside.

To build a shiv with uv and clypi given the above zit example, run:

uvx shiv -c zit -o zit-bin ../zit-bin --verboseSorry I don't know how to use git, it's too hard! ...

You now have a binary (zit-bin) that you can distribute and run like any other binary. You'll have to manually add it to a $PATH location though (What is $PATH?).