DEV Community

Berkeli Halmyradov
Berkeli Halmyradov

Posted on

CodingChallenges in GO - initial setup

Hello world!

Procrastination is real and I've been putting off starting a "blog" for far too long. Why not use this opportunity and go over the fairly new codingchallenges.fyi.

In this series, I will be using Go and Test-Driven-Development while walking through the challenges and explaining my thoughts process. So without further ado, let's get going.

1. Tools

  1. go - I will be coding in golang, so https://go.dev/doc/install is essential.
  2. cobra-cli I will also be using cobra go package as most of the challenges are cli oriented. Cobra provides a CLI tool that can help you produce boilerplate code to save you some time.
  3. [OPTIONAL] gotestsum - this is an excellent go testing tool that has many features over the standard go test command. I hihgly recommend!
  4. [OPTIONAL] Unix environment - this is not a requirement, but a nice to have. Most of the tools we will be creating are only available in unix, so having a system that runs unix or WSL for windows will be handy when you need to do a parity check.

2. Repository Setup

To get started, I have created an empty git repository on github and cloned it to my computer.

Now we need to initialize our go module, I will cd into the newly cloned directory and run the following command:

go mod init github.com/berkeli/coding-challenges
Enter fullscreen mode Exit fullscreen mode

I have given my module the path to my git repository, as that makes it easy to import & reference things.

3. Creating hello world

Now we will initialize our CLI application and write simple Hello World! to the terminal.

I will run cobra-cli init . which creates the basic boilerplate for our application. Once done, you should be able to run the application and it will display the description for our root command

go run .                                                                                                                                                                                                                                              
A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:

Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.
Enter fullscreen mode Exit fullscreen mode

Let's update the handler function for the rootCmd (located in cmd/root.go) so we have our hello world:

from:

// Uncomment the following line if your bare application
// has an action associated with it:
// Run: func(cmd *cobra.Command, args []string) { },
Enter fullscreen mode Exit fullscreen mode

to:

// Uncomment the following line if your bare application
// has an action associated with it:
Run: func(cmd *cobra.Command, args []string) {
    fmt.Println("hello world!")
},
Enter fullscreen mode Exit fullscreen mode

and voila! we have our hello world:

go run .                                                                                                                                                                                                                                                   
hello world!
Enter fullscreen mode Exit fullscreen mode

But we won't be attaching a handler to the rootCmd, so we can undo our change and the cleanup comments and descriptions so it matches what we are trying to do.

A few notes on project structure

For this project I will be following the follwowing structure

├── cmd <-- most of our work will live here
│   ├── go-wc <-- subdirectory for each command/challenge, e.g. wc command
│   │   ├── main.go <-- logic for wc command
│   │   ├── main_test.go <-- tests for wc command
│   │   └── test.txt <-- any additional fixtures needed for testing
│   └── root.go <-- our root command, this will only register child commands
├── go.mod
├── go.sum
└── main.go 
Enter fullscreen mode Exit fullscreen mode

cobra-cli has a useful command for adding new commands cobra-cli add <new-command>, but I won't be using that much as it won't be following the folder structure I've chosen. It creates files directly in cmd folder without subdirectories which could get very busy in this series.

Tests & CI

Of course, you can't have TDD without proper CI (continuous integration) setup. So let's get that out of the way and introduce a github action that will test our code.

We can create a YAML file in .github/workflows/ directory, I named mine go-test.yaml and place the following inside of it:

name: Go Test

on: [push]

jobs:
  example:
    name: run tests
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version-file: ./go.mod
        id: go
      - name: Run Go tests
        run: go run gotest.tools/gotestsum@latest --junitfile unit-tests.xml
      - name: Test Summary
        uses: test-summary/action@v2
        with:
          paths: "unit-tests.xml"
        if: always()

Enter fullscreen mode Exit fullscreen mode

The action about has 3 steps:

  1. Checkout the repository (get the latest commit)
  2. Install gotestsum
  3. Run go tests with gotestsum
  4. Create a summary from the output of gotestsum

That's it for the setup, let's go implement wc command next!

The code for this series is available at https://github.com/berkeli/coding-challenges
The code for this post is on this Pull Request

UPDATE [18/02/24] - I realized that this series will contain other types of applications that CLI, so I have made a small re-org of the repo to make it a GO monorepo.

The new structure is as follows:

.
├── cmd
│   └── cli <-- these will be individual applications
│       └── main.go
├── go.mod
├── go.sum
└── pkg <-- this will contain individual challenges as importable packages
    └── wc <-- wc package, which is the next challenge
        ├── main.go
        ├── main_test.go
        └── test.txt
Enter fullscreen mode Exit fullscreen mode

So from the example above, we will have a cli package which is our gocc (go coding challenges) app that will register subcommands like wc, ls, etc.

These changes are recording in 2nd Pull request

To run our cli tool, we will now be running go run ./cmd/cli

Top comments (0)