home

LLMs can't make (file)

S := .sentinels
PROJECT_ROOT := $(abspath ../../)
TARGET_DIR := $(abspath ../../target/aarch64-unknown-linux-musl/debug)
define nl

endef
define CONTAINER
	
	touch $(and $2, $(shell kubectl get -n $1 pod -l app=$2 -o json | jq '.items[0].metadata.creationTimestamp' -r | gsed -e 's/\(.*\)T\(.*\)\([0-9][0-9]\)Z/#t \1\2.\3/;s/[-:]//g;s/#/-/g') $S/$3)$(nl)
	touch -A 020000 $@
endef


POD = $(if $2,$(shell kubectl get pod -n $1 -l app=$2 -o name | cut -d/ -f2))
GET_TS = $(if $2,(kubectl exec -n $1 $(POD) -- bash -c 'stat /app/$2 2>/dev/null || stat /app/$2'))
TS = $(and $3,$(shell $(GET_TS) | gsed '/Mod/!{d;b};s/.*: \(.*\) \(.*\)\..*/\1 \2/; s/[-:]//g;s/\([0-9]\+\) \([0-9]\+\)\([0-9][0-9]\)/touch -t \1\2.\3/'))
# Check binary inside container
define CONTAINER_BIN
	$(if $3, $(TS) $S/$3)
	touch -A 020000 $S/$3

endef

define FRESH_TARGET
E2E_CONTAINER_SENTINELS += $S/$2-$1-container-fresh
$S/$2-$1-container-fresh: $S/$2-$1-binary-fresh
	@echo "Restarting $2..."
	kubectl rollout restart deployment/$2 -n $1
	$$(call CONTAINER,$1,$2,$2-e2e-container)
endef

define FRESH_BINARY_TARGET
E2E_BINARY_SENTINELS += $S/$2-$1-binary-fresh
$S/$2-$1-binary-fresh: $(TARGET_DIR)/$2
	kubectl cp $(TARGET_DIR)/$2 -n $1 $(POD):/app/$2
	$(call CONTAINER_BIN,$1,$2,$2-e2e-binary-fresh)
endef

define TARGET_CHECK
$(TARGET_DIR)/$1: $(shell fd -e rs -e toml . $(abspath $(PROJECT_ROOT)/$1))
	echo $(abspath $(PROJECT_ROOT)/$1)
	cd $(PROJECT_ROOT) && cargo zigbuild --target aarch64-unknown-linux-musl --bin $1
	touch $$@

endef
$(foreach APP,$E2E_APPS, \
	$(eval $(call TARGET_CHECK,$(APP))))

Scared yet?

This is a Makefile I just sunsetted. It’s not that it didn’t work (it worked quite well, actually - the code you’re seeing above generated special .sentinel/SOMETHING files with timestamp equivalent to the files in k8s containters and it worked well.

But I’m a solo dev, so I employ pi.dev agent to help me with various stuff. 7 times out of 10 it works but I noticed that when it comes to Makefiles (not even that complicated as above meta-definitions) it just f***s s**t up.


Because I needed to orchestrate configuration/rollout steps depending on the changes made, Makefile was helpful but after 3 fiascos I stood before the choice:

  • Accept 1.5h change tax and update Makefiles manually
  • Switch to something else

Since I was already working with YAMLs and agents were able to handle it just fine I looked towards Taskfile. I havemixed feelings about it. On one side, I’ve used it before to do automation, but then on the other hand at some stage it’s becoming cumbersome to have reusable tasks, task templates etc. Makefile is also much terser. Still worth a try.

I asked agent to rewrite Makefile to Taskfile (making sure it has documentation of Taskfile as a context) and it one shotted it. I guess I’m using Taskfile now.


Though I’ve identified few patterns that seems consistent across models (though I haven’t metered it anyway):

Technology Rating Comments
Cue lang ★★☆☆☆ Can do basic transformation, code is verbose and not really reusable.
Jsonnet ★★★☆☆ When it has guidelines it performs better, by itself it creates hermetic, non-reusable patterns
k8s manifests ★★★★☆ Consistent and usually successful at requests
GNU Make ☆☆☆☆☆ Destroys existing patterns, remove (used) constructs which are deemed unneeded, chaos in variable use
Go ★★★☆☆ Panics during runtime, edge cases usually not handled well, requires handholding when it comes to structure.
Rust ★★★★☆ If it compiles it usually works, still too happy to unwrap. Very good at following set architecture or founding a new, idiomatic one on demand.
Haskell ★★★★☆ Similar to Rust. Code is terse, which makes it easy to review.
Taskfile ★★★…? Able to one shot demands and debugs it quite well. Not enough experience to form opinion though.

Some things worth mentioning:

  • GraphML generation seems to be working quite well

Przemysław Alexander Kamiński
vel xlii vel exlee

cb | gl | gh | li | rss

Powered by hugo and hugo-theme-nostyleplease.