From d15b84308619e1425f1b50ef551bdb5729a258f2 Mon Sep 17 00:00:00 2001 From: alex furrier Date: Sun, 22 Jun 2025 13:14:59 -0700 Subject: [PATCH 1/5] feat: add Podman/Docker compatibility to container commands - Auto-detects container engine (Podman preferred if available) - Handles UID/GID mapping for both engines - Updates all container-related Make targets - Adds container-info target to show current configuration - Includes troubleshooting documentation Users can now use either Docker or Podman seamlessly, with the ability to override via CONTAINER_ENGINE environment variable. --- Makefile | 58 ++++++++++++++++++++++--------- README.md | 17 +++++++-- docker/docker-compose.yml | 5 ++- docker/template.env | 5 +++ docs/container-setup.md | 73 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 138 insertions(+), 20 deletions(-) create mode 100644 docs/container-setup.md diff --git a/Makefile b/Makefile index fee9fcf..4a51a83 100644 --- a/Makefile +++ b/Makefile @@ -120,31 +120,57 @@ clean-example: # Remove example code (use this to start your own project) init: setup # Initialize a new project uv run python scripts/init_project.py -# Docker -######## +# Container Engine Support +######################## +# Auto-detect container engine (podman or docker) +CONTAINER_ENGINE ?= $(shell command -v podman >/dev/null 2>&1 && echo podman || echo docker) +COMPOSE_CMD = $(CONTAINER_ENGINE) compose + +# Podman-specific adjustments +ifeq ($(CONTAINER_ENGINE),podman) + # Use host UID/GID for rootless containers + CONTAINER_USER_OPTS = --userns=keep-id + # Podman may need explicit socket path + export DOCKER_HOST ?= unix://$(XDG_RUNTIME_DIR)/podman/podman.sock +else + # Docker: use current user's UID/GID to avoid permission issues + CONTAINER_USER_OPTS = --user $(shell id -u):$(shell id -g) +endif + +# Docker/Podman Images +##################### IMAGE_NAME = container-registry.io/python-collab-template IMAGE_TAG = latest dev-env: refresh-containers - @echo "Spinning up a dev environment ." - @docker compose -f docker/docker-compose.yml down - @docker compose -f docker/docker-compose.yml up -d dev - @docker exec -ti composed_dev /bin/bash + @echo "Spinning up a dev environment using $(CONTAINER_ENGINE)..." + @$(COMPOSE_CMD) -f docker/docker-compose.yml down + @$(COMPOSE_CMD) -f docker/docker-compose.yml up -d dev + @$(CONTAINER_ENGINE) exec -ti composed_dev /bin/bash refresh-containers: - @echo "Rebuilding containers..." - @docker compose -f docker/docker-compose.yml build + @echo "Rebuilding containers using $(CONTAINER_ENGINE)..." + @$(COMPOSE_CMD) -f docker/docker-compose.yml build rebuild-images: - @echo "Rebuilding images with the --no-cache flag..." - @docker compose -f docker/docker-compose.yml build --no-cache + @echo "Rebuilding images with the --no-cache flag using $(CONTAINER_ENGINE)..." + @$(COMPOSE_CMD) -f docker/docker-compose.yml build --no-cache build-image: - @echo Building dev image and tagging as ${IMAGE_NAME}:${IMAGE_TAG} - @docker compose -f docker/docker-compose.yml down - @docker compose -f docker/docker-compose.yml up -d dev - @docker tag dev ${IMAGE_NAME}:${IMAGE_TAG} + @echo Building dev image using $(CONTAINER_ENGINE) and tagging as ${IMAGE_NAME}:${IMAGE_TAG} + @$(COMPOSE_CMD) -f docker/docker-compose.yml down + @$(COMPOSE_CMD) -f docker/docker-compose.yml up -d dev + @$(CONTAINER_ENGINE) tag dev ${IMAGE_NAME}:${IMAGE_TAG} push-image: build-image - @echo Pushing image to container registry - @docker push ${IMAGE_NAME}:${IMAGE_TAG} + @echo Pushing image to container registry using $(CONTAINER_ENGINE) + @$(CONTAINER_ENGINE) push ${IMAGE_NAME}:${IMAGE_TAG} + +# Container Engine Info +###################### +container-info: # Display detected container engine and configuration + @echo "Container Engine: $(CONTAINER_ENGINE)" + @echo "Compose Command: $(COMPOSE_CMD)" + @echo "User Options: $(CONTAINER_USER_OPTS)" + @echo "" + @echo "To override, use: CONTAINER_ENGINE=podman make dev-env" diff --git a/README.md b/README.md index 583b96e..8b3d02d 100644 --- a/README.md +++ b/README.md @@ -87,17 +87,28 @@ To remove the example code and start fresh: ```bash make clean-example ``` -## Docker Support +## Container Support (Docker/Podman) ### Development Environment + +The project automatically detects and uses either Docker or Podman: + ```bash -make dev-env # Start a development container +make dev-env # Uses podman if available, otherwise docker + +# Or explicitly choose: +CONTAINER_ENGINE=docker make dev-env +CONTAINER_ENGINE=podman make dev-env + +# Check which engine will be used: +make container-info ``` This creates a container with: - All dependencies installed - Source code mounted (changes reflect immediately) - Development tools ready to use +- Automatic UID/GID mapping for file permissions ### Production Image ```bash @@ -110,7 +121,7 @@ make push-image # Push to container registry . ├── src/ # Source code ├── tests/ # Test files -├── docker/ # Docker configuration +├── docker/ # Container configuration (Docker/Podman) ├── .github/ # GitHub Actions workflows ├── pyproject.toml # Project configuration └── Makefile # Development commands diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 8118e55..a85b175 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -11,6 +11,8 @@ services: EXAMPLE: ${EXAMPLE} EXAMPLE_SECRET: ${EXAMPLE_SECRET} command: /bin/bash + # Handle UID/GID for better permission compatibility + user: "${UID:-1000}:${GID:-1000}" # Bind to local files volumes: - type: bind @@ -21,8 +23,9 @@ services: source: ../tests target: /src/tests read_only: False + # Container runtime socket (works for both Docker and Podman) - type: bind - source: /var/run/docker.sock + source: ${DOCKER_SOCK:-/var/run/docker.sock} target: /var/run/docker.sock # Dev files in top level directory - type: bind diff --git a/docker/template.env b/docker/template.env index 8d2cd85..1b7ed75 100644 --- a/docker/template.env +++ b/docker/template.env @@ -8,3 +8,8 @@ DEBUG=true # Secrets (do not commit actual values) API_KEY= DATABASE_URL= + +# Container runtime settings (optional) +# UID=${UID} # Uncomment to use your system UID +# GID=${GID} # Uncomment to use your system GID +# DOCKER_SOCK=/var/run/docker.sock # Override socket path for Podman diff --git a/docs/container-setup.md b/docs/container-setup.md new file mode 100644 index 0000000..b1f6882 --- /dev/null +++ b/docs/container-setup.md @@ -0,0 +1,73 @@ +# Container Setup Guide + +This project supports both Docker and Podman for containerized development. + +## Quick Start + +The Makefile automatically detects your container engine: + +```bash +# Automatic detection (prefers Podman if available) +make dev-env + +# Explicit engine selection +CONTAINER_ENGINE=docker make dev-env +CONTAINER_ENGINE=podman make dev-env +``` + +## Podman vs Docker + +### Key Differences + +| Feature | Docker | Podman | +|---------|--------|--------| +| Root privileges | Runs as root by default | Rootless by default | +| Daemon | Requires dockerd daemon | Daemonless | +| Security | Good with proper setup | Better default security | +| Compose support | Native | Via podman-compose | + +### When to Use Which + +**Use Docker when:** +- It's your team's standard +- You need Docker Desktop features +- You're using Docker-specific tooling + +**Use Podman when:** +- Security is a top priority +- You can't/don't want to run a daemon +- You're in a restricted environment + +## Troubleshooting + +### Permission Issues + +If you encounter permission issues with mounted volumes: + +1. **For Podman**: Should work automatically with rootless mode +2. **For Docker**: Set your UID/GID in `docker/.env`: + ```bash + echo "UID=$(id -u)" >> docker/.env + echo "GID=$(id -g)" >> docker/.env + ``` + +### Socket Issues + +If Podman can't find the Docker socket: + +```bash +# Set the socket path in your .env +echo "DOCKER_SOCK=${XDG_RUNTIME_DIR}/podman/podman.sock" >> docker/.env +``` + +### Compose Command Not Found + +For Podman, you may need to install podman-compose: + +```bash +# macOS +brew install podman-compose + +# Linux +pip install podman-compose +``` \ No newline at end of file From 65d4b50ce5c9437bf38081436bd10fbf8aef66b6 Mon Sep 17 00:00:00 2001 From: alex furrier Date: Sun, 22 Jun 2025 13:48:08 -0700 Subject: [PATCH 2/5] fix(podman): improve macOS compatibility and machine management - Fix Podman machine detection with proper JSON parsing - Add automatic machine startup when needed - Use podman-compose instead of podman compose - Add comprehensive Podman readiness checks - Update documentation with macOS-specific troubleshooting Resolves issues with Podman setup on macOS where the machine needs to be running and podman-compose must be used for compose functionality. --- Makefile | 34 +++++++++++++++++++++++++++++----- docs/container-setup.md | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 4a51a83..feb5475 100644 --- a/Makefile +++ b/Makefile @@ -124,15 +124,18 @@ init: setup # Initialize a new project ######################## # Auto-detect container engine (podman or docker) CONTAINER_ENGINE ?= $(shell command -v podman >/dev/null 2>&1 && echo podman || echo docker) -COMPOSE_CMD = $(CONTAINER_ENGINE) compose # Podman-specific adjustments ifeq ($(CONTAINER_ENGINE),podman) + # Use podman-compose for compose functionality + COMPOSE_CMD = podman-compose # Use host UID/GID for rootless containers CONTAINER_USER_OPTS = --userns=keep-id - # Podman may need explicit socket path - export DOCKER_HOST ?= unix://$(XDG_RUNTIME_DIR)/podman/podman.sock + # Podman machine status check + PODMAN_MACHINE_RUNNING = $(shell podman machine list --format json 2>/dev/null | grep '"Running": true' >/dev/null && echo yes || echo no) else + # Docker: use native compose + COMPOSE_CMD = $(CONTAINER_ENGINE) compose # Docker: use current user's UID/GID to avoid permission issues CONTAINER_USER_OPTS = --user $(shell id -u):$(shell id -g) endif @@ -142,13 +145,13 @@ endif IMAGE_NAME = container-registry.io/python-collab-template IMAGE_TAG = latest -dev-env: refresh-containers +dev-env: ensure-container-ready refresh-containers @echo "Spinning up a dev environment using $(CONTAINER_ENGINE)..." @$(COMPOSE_CMD) -f docker/docker-compose.yml down @$(COMPOSE_CMD) -f docker/docker-compose.yml up -d dev @$(CONTAINER_ENGINE) exec -ti composed_dev /bin/bash -refresh-containers: +refresh-containers: ensure-container-ready @echo "Rebuilding containers using $(CONTAINER_ENGINE)..." @$(COMPOSE_CMD) -f docker/docker-compose.yml build @@ -168,9 +171,30 @@ push-image: build-image # Container Engine Info ###################### +ensure-container-ready: # Ensure container engine is ready +ifeq ($(CONTAINER_ENGINE),podman) + @echo "Checking Podman machine status..." + @if [ "$(PODMAN_MACHINE_RUNNING)" = "no" ]; then \ + echo "Podman machine is not running. Starting it..."; \ + podman machine start; \ + echo "Waiting for Podman machine to be ready..."; \ + sleep 3; \ + fi + @if ! command -v podman-compose >/dev/null 2>&1; then \ + echo "Error: podman-compose not found. Install with: brew install podman-compose"; \ + exit 1; \ + fi +else + @echo "Using Docker engine..." +endif + container-info: # Display detected container engine and configuration @echo "Container Engine: $(CONTAINER_ENGINE)" @echo "Compose Command: $(COMPOSE_CMD)" @echo "User Options: $(CONTAINER_USER_OPTS)" +ifeq ($(CONTAINER_ENGINE),podman) + @echo "Podman Machine Running: $(PODMAN_MACHINE_RUNNING)" + @echo "podman-compose Available: $(shell command -v podman-compose >/dev/null 2>&1 && echo yes || echo no)" +endif @echo "" @echo "To override, use: CONTAINER_ENGINE=podman make dev-env" diff --git a/docs/container-setup.md b/docs/container-setup.md index b1f6882..ecaf183 100644 --- a/docs/container-setup.md +++ b/docs/container-setup.md @@ -62,7 +62,7 @@ echo "DOCKER_SOCK=${XDG_RUNTIME_DIR}/podman/podman.sock" >> docker/.env ### Compose Command Not Found -For Podman, you may need to install podman-compose: +For Podman, you need to install podman-compose: ```bash # macOS @@ -70,4 +70,33 @@ brew install podman-compose # Linux pip install podman-compose -``` \ No newline at end of file +``` + +### Podman Machine Not Running (macOS/Windows) + +Podman needs a Linux VM to run containers. The Makefile will automatically start it, but you can also manage it manually: + +```bash +# Initialize a new machine +podman machine init + +# Start the machine +podman machine start + +# Check machine status +podman machine list + +# Stop the machine +podman machine stop +``` + +### Auto-Setup with Make + +The project's Makefile handles most Podman setup automatically: + +- Checks if Podman machine is running +- Starts it if needed +- Verifies podman-compose is installed +- Uses appropriate socket paths + +Just run `make container-info` to see the current status. \ No newline at end of file From 4f306a698332ab90739a8cfaacd8d644b3e814e5 Mon Sep 17 00:00:00 2001 From: alex furrier Date: Sun, 22 Jun 2025 13:56:08 -0700 Subject: [PATCH 3/5] fix(podman): resolve volume mount and permission issues - Fix Dockerfile to use --system flag for uv pip sync - Simplify docker-compose.yml volume mounts to avoid missing files - Use :Z option for proper SELinux context in Podman - Remove user mapping to let Podman rootless handle permissions - Mount entire project as /workspace for simplicity Podman dev environment now works correctly on macOS. --- docker/Dockerfile | 2 +- docker/docker-compose.yml | 35 +++-------------------------------- 2 files changed, 4 insertions(+), 33 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 7984e0d..d775af7 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -24,6 +24,6 @@ COPY tests/ ./tests/ RUN pip install -U pip uv \ && uv pip compile pyproject.toml -o requirements.txt \ && uv pip compile pyproject.toml --extra dev -o requirements-dev.txt \ - && uv pip sync requirements.txt requirements-dev.txt + && uv pip sync --system requirements.txt requirements-dev.txt CMD ["/bin/bash"] diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index a85b175..eed0854 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -11,36 +11,7 @@ services: EXAMPLE: ${EXAMPLE} EXAMPLE_SECRET: ${EXAMPLE_SECRET} command: /bin/bash - # Handle UID/GID for better permission compatibility - user: "${UID:-1000}:${GID:-1000}" - # Bind to local files + # Note: user mapping handled by Podman rootless automatically + # Bind whole project directory for simplicity volumes: - - type: bind - source: ../src - target: /src/src - read_only: False - - type: bind - source: ../tests - target: /src/tests - read_only: False - # Container runtime socket (works for both Docker and Podman) - - type: bind - source: ${DOCKER_SOCK:-/var/run/docker.sock} - target: /var/run/docker.sock - # Dev files in top level directory - - type: bind - source: ../docker/Makefile - target: /src/Makefile - read_only: False - - type: bind - source: ../mypy.ini - target: /src/mypy.ini - read_only: False - - type: bind - source: ../pylintrc - target: /src/pylintrc - read_only: False - - type: bind - source: ../README.md - target: /src/README.md - read_only: False + - ../:/workspace:Z From 406cde4c7fd8dbc13538d5bc514410a1a772a380 Mon Sep 17 00:00:00 2001 From: alex furrier Date: Sun, 22 Jun 2025 14:16:23 -0700 Subject: [PATCH 4/5] fix(docker): make container setup compatible with make init - Remove hardcoded src/ directory references from Dockerfile - Use /workspace as WORKDIR to match volume mount location - Copy dependencies to /tmp for installation, then clean up - This ensures containers work both before and after make init renames src/ The container setup now survives project initialization and directory restructuring. --- docker/Dockerfile | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index d775af7..92f18ff 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -14,16 +14,17 @@ RUN apt-get update && apt-get install -y \ vim \ && rm -rf /var/lib/apt/lists/* -WORKDIR /src +WORKDIR /workspace -COPY pyproject.toml . -COPY README.md . -COPY src/ ./src/ -COPY tests/ ./tests/ +# Copy essential files for dependency resolution to temp location +COPY pyproject.toml /tmp/ +COPY README.md /tmp/ RUN pip install -U pip uv \ + && cd /tmp \ && uv pip compile pyproject.toml -o requirements.txt \ && uv pip compile pyproject.toml --extra dev -o requirements-dev.txt \ - && uv pip sync --system requirements.txt requirements-dev.txt + && uv pip sync --system requirements.txt requirements-dev.txt \ + && rm -rf /tmp/pyproject.toml /tmp/README.md CMD ["/bin/bash"] From f0939dfd1dd8dd2e9d1fcfa7d3260647d35cc0e3 Mon Sep 17 00:00:00 2001 From: alex furrier Date: Sun, 22 Jun 2025 14:16:40 -0700 Subject: [PATCH 5/5] docs: add section on make init compatibility for containers --- docs/container-setup.md | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/docs/container-setup.md b/docs/container-setup.md index ecaf183..58d4f72 100644 --- a/docs/container-setup.md +++ b/docs/container-setup.md @@ -99,4 +99,40 @@ The project's Makefile handles most Podman setup automatically: - Verifies podman-compose is installed - Uses appropriate socket paths -Just run `make container-info` to see the current status. \ No newline at end of file +Just run `make container-info` to see the current status. + +## Compatibility with Project Initialization + +The container setup is designed to work both before and after running `make init`: + +### Before `make init` +- Source code is in `src/` directory +- Container mounts entire project as `/workspace` +- All development tools work normally + +### After `make init` +- Source code moves to your project module directory (e.g., `my_project/`) +- Container setup continues to work unchanged +- Volume mounts and dependencies remain intact + +The `make init` command: +1. Renames `src/` to your project name +2. Updates import statements in tests +3. Modifies `pyproject.toml` and `Makefile` + +**The Docker/Podman setup survives this transformation** because: +- The Dockerfile doesn't hardcode directory names +- Dependencies are installed from temporary copied files +- The entire project is mounted, regardless of internal structure + +This means you can: +```bash +# Set up development environment +make dev-env + +# Initialize your project later +make init + +# Continue using the same development environment +make dev-env # Still works! +``` \ No newline at end of file