Skip to content

Posts

Allowing flags anywhere on the CLI

Writing CLIs in Go is great, but there's one annoyance with the stdlib flag package:

flags must be defined before any positional arguments.

For example, if you have a --debug flag, you can't use it after a positional argument, it'll be treated as a 2nd positional argument.

# ✅ Works as expeceted
$ mycli --debug run

# ❌ Does not work as expected
$ mycli run --debug

Lesser-known but useful buf commands

The buf CLI is a multipurpose tool for working with Protocol Buffers. The core features include linting, formatting, breaking change detection, code generation, dependency management, etc.

But the goal of this post is to highlight some of the lesser-known commands I've personally found useful. Emphasis on personally. This is an evolving post of the lesser-known buf commands and notes on how I use them.

Finally, a blogging setup I like

Native blog support recently landed in Material for MkDocs 🎉. This is huge, because technical documentation + blog can co-exist in the same place and actually look nice!

Kudos to Martin (@squidfunk on ) for the hard work and dedication to the project. I've been following the project for a while and it's been a pleasure to watch it grow. I've used my fair share of documentation frameworks and tools, but I keep coming back to this one because...

Useful Git (and GitHub) stuff

Create a new GitHub repository and clone it locally

This will create the directory myproject and clone the repository into it.

gh repo create mfridman/myproject --add-readme --license mit --clone --private

Squashing

Squash all the commits between your current HEAD and origin/main into a single new commit.

git reset --soft origin/main && git commit -m "squash all"

Switching default branch

https://www.git-tower.com/learn/git/faq/git-rename-master-to-main

git branch -m master main
git push -u origin main
## May need to switch default branch in the UI, or use the gh CLI
gh repo edit owner/repo --default-branch main

git push origin --delete master

Updating fork

How to update a fork to have the same branches/tags as upstream repository?

# Set up the upstream remote for your forked repository:
git remote add upstream https://github.com/owner/repo
# Verify the remote with:
git remote -v

# Fetch latest tags from the upstream repository:
git fetch --tags upstream

# Push all tags from local repository to the forked repository:
Run git push --tags origin

Useful Go Stuff

Update direct dependencies only

Updates all direct dependencies of the current Go module to their latest versions.

go get $(go list -f '{{if not (or .Main .Indirect)}}{{.Path}}{{end}}' -m all)

This can be used instead of go get -u ./..., which updates all dependencies, including transitive dependencies. The latter may not always be desirable.

Update all dependencies

Checks for and applies all available updates to module's dependencies.

go list -u -m all | grep '\[' | awk '{print $1}' | xargs -n1 go get -u

Get specific dependency version from go.mod

go list -m -json github.com/pressly/goose/v3 | jq -r .Version

List latest stable version regardless of go.mod

go list -m -versions github.com/pressly/goose/v3 | tr ' ' '\n' | sort -V | grep -v '-' | tail -n 1

The sort -V command is a version of the sort utility that performs version number sorting. It understands the logical order of version numbers, which may not always correspond to alphabetical or numerical order.

Example:

1.10
1.2
1.1
2.0
1.11

Regular sort:

1.1
1.10
1.11
1.2
2.0

But sort -V would correctly order them as:

1.1
1.2
1.10
1.11
2.0

This is because sort -V understands that in version numbers, 1.2 comes before 1.10.

Explain dependencies

# Lists all module dependencies (excluding the main module) by skipping the first line.
# For each dependency, explains why it's needed in the module.
go mod why -m $(go list -m all | tail -n +2)

This can be particularly useful when:

  • Auditing dependencies
  • Trying to reduce the size of the module
  • Understanding the dependency tree

Identify which direct dependencies are pulling in various indirect dependencies.

Shine on you crazy interface

Go's interfaces are brilliant. It's not immediately apparent and at first can be confusing, but after a while its brilliance shines through.

TL;DR - if a concrete type has the necessary behavior (methods) to satisfy an interface, it can implicitly implement that interface.