How to create a CLI program in Go and host it on npm
Tags: go, npm, gorelease, tooling
Published: 2020-12-19 00:29:43

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](https://www.npmjs.com/package/@wachira/simp-cli), 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 a HTTP server.

## Lets get started

Create a new folder inside

```bash
$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

```bash
go mod init
```

I have my directory setup as below:

```bash
.
+-- 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`

```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`

```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

```go
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`

```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](https://blog.bywachira.com/post/create-cli-with-golang-and-publish-on-npm)
# 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

- Join my discord, I answer all questions, [here](https://discord.gg/E8cBkAH8hx)

> I have web monetization enabled so you can support my work or give me feedback on this article on what I should improve on.

# Socials

- [Twitter](https://twitter.com/wachira_dev)