Skip to content

Instantly share code, notes, and snippets.

@legionus
Last active February 5, 2026 18:49
Show Gist options
  • Select an option

  • Save legionus/3e3e566df6bb1af9ee7da7bca9f68d00 to your computer and use it in GitHub Desktop.

Select an option

Save legionus/3e3e566df6bb1af9ee7da7bca9f68d00 to your computer and use it in GitHub Desktop.
The project allows you to manage isolated containers with AI agents.
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (C) 2026 Alexey Gladkov <gladkov.alexey@gmail.com>
CURNAME = devkit
CURFILE = $(lastword $(MAKEFILE_LIST))
V = $(VERBOSE)
Q = $(if $(V),,@)
define require-utility
$(eval $(1) := $(shell command -v $(2) 2>/dev/null))
$(if $($(1)),,$(error Required utility '$(2)' not found))
endef
$(call require-utility,GIT,git)
$(call require-utility,PODMAN,podman)
$(call require-utility,CURL,curl)
GITPROJDIR = $(shell $(GIT) rev-parse --show-toplevel 2>/dev/null)
PROJNAME = $(notdir $(GITPROJDIR))
$(if $(PROJNAME),,$(error Unable to locate the git repository.))
DEF_AGENT = copilot
DEF_DEVNAME = $(PROJNAME)
AGENT = $(shell $(GIT) config get devkit.agent || echo $(DEF_AGENT))
DEVNAME = $(shell $(GIT) config get devkit.name || echo $(DEF_DEVNAME))
DEVPKGS = $(shell $(GIT) config get --all devkit.packages)
SHAHASH = $(shell echo $(AGENT) $(sort $(DEVPKGS)) | sha256sum | cut -f1 -d\ )
AGENT.copilot.HOME = https://github.com/github/copilot-cli/releases/latest
AGENT.copilot.URL = $(AGENT.copilot.HOME)/download/copilot-linux-x64.tar.gz
AGENT.copilot.BIN = copilot
AGENT.copilot.VOLUMES = $(HOME)/.copilot:/root/.copilot:rw
AGENT.copilot.BASE = docker.io/library/ubuntu:latest
AGENT.codex.HOME = https://github.com/openai/codex/releases/latest
AGENT.codex.URL = $(AGENT.codex.HOME)/download/codex-x86_64-unknown-linux-gnu.tar.gz
AGENT.codex.BIN = codex-x86_64-unknown-linux-gnu
AGENT.codex.VOLUMES = $(HOME)/.codex:/root/.codex:rw
AGENT.codex.BASE = docker.io/library/ubuntu:latest
AGENT.opencode.HOME = https://github.com/anomalyco/opencode/releases/latest
AGENT.opencode.URL = $(AGENT.opencode.HOME)/download/opencode-linux-x64.tar.gz
AGENT.opencode.BIN = opencode
AGENT.opencode.VOLUMES = $(HOME)/.config/opencode:/root/.config/opencode:rw
AGENT.opencode.BASE = docker.io/library/ubuntu:latest
AGENT.gemini.HOME = https://github.com/google-gemini/gemini-cli/releases/latest
AGENT.gemini.URL =
AGENT.gemini.BIN = gemini
AGENT.gemini.VOLUMES = $(HOME)/.gemini:/root/.gemini:rw
AGENT.gemini.VERSION = $(shell $(CURL) --silent --head --no-location "$(AGENT.gemini.HOME)" | sed -n 's/^location: http.*\/tag\/v//p')
AGENT.gemini.BASE = us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:$(AGENT.gemini.VERSION)
IMG_WORKDIR = /srv/$(PROJNAME)
IMG_VOLUMES = $(GITPROJDIR):$(IMG_WORKDIR) $(AGENT.$(AGENT).VOLUMES)
# Rules
.PHONY: _check-image _create-image help init clean check upgrade list run
.ONESHELL:
help:
@echo ""
echo "Usage: make -f $(CURFILE) [ help$(foreach x,init clean check upgrade list run, | $(x)) ]"
echo ""
echo "The project allows you to manage isolated containers with AI agents."
echo ""
echo "Commands:"
echo " init creates the initial configuration in git-config."
echo " list shows all devkit known images."
echo " check shows current and available agent versions."
echo " upgrade upgrades podman image for current devkit."
echo " run starts devkit container."
echo " clean deletes all images for the current devkit."
echo " clean-all deletes all devkit images."
echo " help display this help and exit."
echo ""
echo "Report bugs to authors."
echo ""
init:
$(Q)if ! $(GIT) config get devkit.name >/dev/null 2>&1; then
$(GIT) config set devkit.name "$(DEVNAME)";
$(GIT) config set devkit.agent "$(AGENT)";
$(GIT) config set devkit.packages "bash";
else
echo "Discovered the existing configuration and cowardly refuse to break it." >&2;
fi
get-image-id = $(shell $(PODMAN) image list --filter label=local.devkit.hash=$(SHAHASH) --format '{{.Id}}')
get-avail-version = $(shell $(CURL) --silent --head --no-location "$(AGENT.$(AGENT).HOME)" | sed -n 's/^location: .*\/tag\///p')
_create-image:
$(Q)[ -n "$(get-image-id)" ] || printf '%s\n' \
"FROM $(AGENT.$(AGENT).BASE)" \
"USER root" \
"RUN apt-get update" \
"RUN apt-get -y install $(sort ca-certificates bash curl tar $(DEVPKGS))" \
"RUN apt-get -y clean" \
"RUN ln -s -- '/bin/bash' '/usr/bin/agent'" \
"RUN [ '$(words $(AGENT.$(AGENT).URL))' -eq 0 ] || { curl -fsSL '$(AGENT.$(AGENT).URL)' | tar -C /usr/local/bin -zxf-; }" \
"RUN [ '$(words $(AGENT.$(AGENT).BIN))' -eq 0 ] || { ln -sf -- \"\`command -v $(AGENT.$(AGENT).BIN)\`\" '/usr/bin/agent'; }" \
"LABEL local.devkit.name=$(DEVNAME)" \
"LABEL local.devkit.hash=$(SHAHASH)" \
"LABEL local.devkit.agent=$(AGENT)" \
"LABEL local.devkit.agent.version=$(get-avail-version)" \
"CMD /usr/bin/agent" |
$(PODMAN) image build --squash --force-rm -t "localhost/$(CURNAME)/$(DEVNAME):latest" -f-;
_check-image:
$(Q)[ -n "$(get-image-id)" ] || $(MAKE) -f "$(CURFILE)" _create-image
run: _check-image
$(Q)$(PODMAN) container run $(addprefix -v ,$(IMG_VOLUMES)) --workdir="$(IMG_WORKDIR)" --rm --tty --interactive -- "$(get-image-id)" $(CMD) || \
echo "container exit status $$?"
check:
$(Q)image_id="$(get-image-id)";
avail_ver="$(get-avail-version)";
image_ver="`[ -z "$$image_id" ] || $(PODMAN) image inspect "$$image_id" --format '{{index .Labels "local.devkit.agent.version"}}'`";
echo "The $(AGENT) information:";
echo " - release home page: $(AGENT.$(AGENT).HOME)";
echo " - available version: $${avail_ver:-*unavailable*}";
echo " - current version: $${image_ver:-*unknown*}";
clean-all:
$(Q)$(PODMAN) image list --filter label=local.devkit.name --format '{{.Id}}' | xargs -r $(PODMAN) image rm
clean:
$(Q)$(PODMAN) image list --filter label=local.devkit.name=$(DEVNAME) --format '{{.Id}}' | xargs -r $(PODMAN) image rm
upgrade: clean
$(Q)$(MAKE) -f "$(CURFILE)" _create-image
list:
$(Q)$(PODMAN) image list --filter label=local.devkit.name
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment