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)
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
$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
- Join my discord, I answer all questions, here
I have web monetization enabled so you can support my work or give me feedback on this article on what I should improve on.