How to create a CLI program in Go and host it on npm
By: Erick Wachira
Tags: Publishing, NPM, Code, Golang, Cli

Recently I started building tools that will help me automate my app deployment process easier and cheaper, to do that I knew CLIs are definitely going to be involved one way or another. So I started building two CLIs:

- Simp CLI: It’s like Makefile just easy

- Thrusta CLI: It’s a remote command execution tool

Both CLIs were built with Golang but only one is hosted on npm here, this tutorial will guide you on how you can do the same.

Note: Am still new to Go

Let’s jump into it

Prerequisites

Basic Golang syntax and concepts (Beginner friendly)

NPM installed

Golang installed (its a process, so here is a [link](https://golang.org/doc/install))

What are we building?

We are going to create a CLI that serves a directory over an HTTP server.

Let's get started

Create a new folder inside

$GOPATH/src/<github.com|gitea.com|gitlab.com|bitbucket.com>/<username>/html-server-cli

Next, we will create our go.mod file to track packages we are going to use

go mod init

I have my directory setup as below:

.

+ — cli

+ — cli.go

+ — helpers

+ — helpers.go

+ — server

+ — server.go

+ — go.mod

+ — go.sum

+ — html-server.go

Let add some code to our helpers/helpers.go

package helpers import (

“math/rand”

“time”

) // GeneratePortNumber handle generating port number

func GeneratePortNumber() int {

rand.Seed(time.Now().UnixNano())

min := 1000

max := 99999

port := rand.Intn(max-min+1) + min

return port

}

The block of code above enables us to generate a random port number, you can add more logic to see if a port is used.

Next, let’s add some code to our server/server.go

package server import (

“log”

“net/http”

“strconv”

“github.com/bywachira/html-server/helpers”

)

// Serve handles serving the directory

func Serve() {

// Generate our port number

port := helpers.GeneratePortNumber()

// Inform the user which port we are running

log.Println(“We are running on port: “ + strconv.Itoa(port))

// Exit to a live server

log.Fatal(http.ListenAndServe(“:”+strconv.Itoa(port), http.FileServer(http.Dir(“.”))))

}

Let’s create a method to initialize our CLI

package cli import (

“fmt”

“sort”

“github.com/bywachira/html-server/server”

“github.com/urfave/cli/v2”

) // SetupCLI initialize cli

func SetupCLI() *cli.App {

app := &cli.App{

// List your commands here

Commands: []*cli.Command{

{

// Provide details about our cli flags

Name: “run”,

Aliases: []string{“r”},

Usage: “Serve your HTML file”,

Action: func(c *cli.Context) error {

// The logic to be called when this commad is ran

fmt.Println(“We are serving this directory”)

server.Serve()

return nil

},

},

},

}

sort.Sort(cli.FlagsByName(app.Flags))

sort.Sort(cli.CommandsByName(app.Commands))

// Return our cli

return app

}

Let’s update our package main which is html-server.go

package main import (

“log”

“os”

“github.com/bywachira/html-server/cli”

) func main() {

// Assign our cli to the app variable

app := cli.SetupCLI()

err := app.Run(os.Args)

// Exit program when we get an error and show error

if err != nil {

log.Fatal(“Error: “, err)

}

}

Note: You don’t have to use github.com/urfave/cli/v2 package by default, you can use whatever package you want because all we need to publish is the binary file

The Publishing Part

Click here to continue reading this part

Summary

We made a CLI in Golang

We generated binaries for all major OSs

We published our package

Add dist folder created by goreleaser to .gitignore file

Questions