Go is expressive, concise, clean and efficient. It's a fast, statically typed, compiled language that feels like a dynamically typed, interpreted one — which makes it a superb choice for building command-line tools. In this tutorial we'll use the Cobra library to build a small git CLI that lists all the repositories on a GitHub account.
Along the way you'll see how a Go program is structured, how Cobra wires up commands and flags, and how to talk to the GitHub REST API from Go.
By the end of this tutorial you will:
You'll need Go installed on your machine. We'll use a single external package:
github.com/spf13/cobraCobra is a library for creating powerful, modern CLI applications, offering a simple interface for interfaces that feel like git and go themselves. It powers many well-known Go projects — Kubernetes, Hugo, and the GitHub CLI, to name a few. It provides:
app server, app fetch, etc.-h / --help.Let's get started.
Our main.go does one thing — hand control to the cmd package:
package main
import (
"go-cli-for-git/cmd"
)
func main() {
cmd.Execute()
}Next we initialise the Cobra root command in a rootCmd variable with a short description, and expose an Execute() function that runs it:
package cmd
import (
"fmt"
"github.com/spf13/cobra"
"os"
)
var rootCmd = &cobra.Command{
Use: "cli",
Short: "git cli using cobra to list all repositories and their clone URLs",
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}Now the interesting part. We register a get subcommand on rootCmd and define the flags it needs. The command reads the username and access token, calls the GitHub API, and prints each repository's name, visibility, and clone URL.
package cmd
import (
b64 "encoding/base64"
"encoding/json"
"fmt"
"github.com/spf13/cobra"
"io/ioutil"
"net/http"
)
// addCmd represents the "get" command
var addCmd = &cobra.Command{
Use: "get",
Short: "get repo details",
Long: `Get repo information using the Cobra command`,
Run: func(cmd *cobra.Command, args []string) {
username, _ := rootCmd.Flags().GetString("username")
password, _ := rootCmd.Flags().GetString("password")
auth := fmt.Sprintf("%s:%s", username, password)
authEncode := b64.StdEncoding.EncodeToString([]byte(auth))
url := "https://api.github.com/user/repos"
method := "GET"
client := &http.Client{}
req, err := http.NewRequest(method, url, nil)
if err != nil {
fmt.Println(err)
return
}
req.Header.Add("Authorization", fmt.Sprintf("Basic %s", authEncode))
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
var response []interface{}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return
}
err = json.Unmarshal(body, &response)
for _, repoDetails := range response {
repo := repoDetails.(map[string]interface{})
fmt.Println(" name :", repo["name"], " private :", repo["private"], " clone_url :", repo["clone_url"])
}
},
}
func init() {
rootCmd.AddCommand(addCmd)
rootCmd.PersistentFlags().StringP("username", "u", "", "the username of git")
rootCmd.PersistentFlags().StringP("password", "p", "", "the access token of git")
}A few things worth calling out:
addCmd on rootCmd and define two flags, -u and -p, for the GitHub username and access token.rootCmd.PersistentFlags().StringP("username", "u", "", "…") is how you declare a persistent flag on the command.rootCmd.Flags().GetString("username") is how you read that flag's value while the command runs.This example uses HTTP Basic auth with a personal access token for clarity. GitHub now recommends sending the token as a Bearer / token header, and ioutil.ReadAll is deprecated in favour of io.ReadAll on modern Go — worth updating for production use.
Compile the binary with go build:
go build -o git-cliPerfect — we're all set. Run the project, passing your GitHub username and a personal access token:
./git-cli get -u <username> -p <access_token>
# or, using the long flags:
./git-cli get --username <username> --password <access_token>Success! The Cobra command executes and your GitHub repositories are listed in the terminal — each with its name, visibility and clone URL.
In just three small files you've built a real, extensible CLI: a root command, a subcommand, flags, and a live API call. From here you can add more subcommands, richer output formatting, or Viper-backed configuration — Cobra scales cleanly as your tool grows.
If you get stuck following along, clone the reference repository and run it:
git clone https://github.com/venkateshsuresh/go-cli-for-git.gitThanks for reading — and stay tuned for more.
BootLabs builds cloud automation tooling and platform engineering solutions in Go and beyond. If you're building internal CLIs or developer platforms, our engineering team can help.
Talk to our engineering team — we build CLIs, internal developer platforms, and cloud automation in Go and cloud-native stacks.