Just and Podman
Gopher in a pod

Why?

In my Go Project series, I recommended using Makefiles for executing project-related tasks and Docker for constructing and exporting images. Lately, I've been replacing these two tools with just instead of make and podman, instead of docker.

The main advantage podman has over Docker in the case where you are only generating images for export is that it does not require an external daemon to run. Additionally, the need for a .hcl is eliminated.

Since most of the cases I've used for make are just commands or tasks that need to run to build and perform operations on the project, and since we are not actually checking dependencies to determine what needs to be built, just, works as well, if not better. Bonus, using just eliminates the need for all of the .PHONY directives in the file.

Installing just.

Visit Just's Github Repository for instructions specific to your operating system. However, if you have rust and cargo installed, you should be able to install it using:

cargo install just

Installing podman.

Usually, podman is provided using your operating system's package manager. For Debian-based systems, you can use:

apt install podman

Setting up subuid and subgid for podman

Podman has the capability of running containers in rootless mode - it handles this by using system uids and gids that are lower than the current user's. Although we are just using Podman to generate containers and not necessarily to run them, you may run into issues when building images. To fix this, you can run the following command to add subuids and subgids to your user (replace <username> with your actual user:

sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 <username>

If you have multiple users on the same system, you may need to run this command for them as well - be sure to use different ranges such as 200000-265535.

After adding the necessary subuids and subgids, it is necessary to migrate podman:

podman system migrate

The Justfile

I've created a branch in the Go Getting Started repository with the changes required to migrate from make and docker to just and podman. I will outline all the changes here.

Copy or rename the Makefile:

mv Makefile Justfile

Remove any .PHONY directives since they are no longer needed.

The TAG variable assignment needs to be changed. You can use backticks to run commands and assign them to a variable, also, we can use interpolation instead of exporting it to the environment, so we can remove the export:

TAG := `git describe --tags --always`

Since we are using variable interpolation, we can make things easier on ourselves by setting additional variables:

APP_NAME := "go-getting-started"
IMAGE_NAME := APP_NAME + ":" + TAG
TAR_NAME := APP_NAME + "-" + TAG + ".tar"

Our build step remains unchanged. For the docker-build we can simplify this using our variable - note that anything within double braces {{}} will be replaced in the command:

docker-build: build
	podman build -t {{IMAGE_NAME}} .

Now to export the image as a tar file, we can update the image step - note, we add the additional step of removing the tar file if it already exists:

image: docker-build
	mkdir -p image/
	if [ -f image/{{TAR_NAME}} ]; then rm image/{{TAR_NAME}}; fi
	podman save -o image/{{TAR_NAME}} {{IMAGE_NAME}}

Since just is a command runner, we can also add additional helper steps such as run - which will build and run our project:

run: build
	./build/app

For full reference, here is our full Justfile:

# Justfile for building the Go application and creating a Docker image.

TAG := `git describe --tags --always`
APP_NAME := "go-getting-started"
IMAGE_NAME := APP_NAME + ":" + TAG
TAR_NAME := APP_NAME + "-" + TAG + ".tar"

build:
	go build -o build/app cmd/app/*.go

run: build
	./build/app

docker-build: build
	podman build -t {{IMAGE_NAME}} .

image: docker-build
	mkdir -p image/
	if [ -f image/{{TAR_NAME}} ]; then rm image/{{TAR_NAME}}; fi
	podman save -o image/{{TAR_NAME}} {{IMAGE_NAME}}
	

clean:
	rm -rf build/
	rm -rf image/

Running the steps

To run any step within the Justfile it works just like make instead, you use just:

just build