Skip to content

Commit 7ab8987

Browse files
committed
Add CPU GHCR publish and compose; align CPU Dockerfile and entrypoint
1 parent 232ddf6 commit 7ab8987

5 files changed

Lines changed: 142 additions & 39 deletions

File tree

.github/workflows/publish-cpu.yml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: Publish CPU Docker image (GHCR)
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
tags:
7+
- "v-*"
8+
9+
permissions:
10+
contents: read
11+
packages: write
12+
13+
env:
14+
REGISTRY: ghcr.io
15+
IMAGE_NAME: gene-weaver/leafmachine2
16+
17+
jobs:
18+
build-and-push:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: Checkout
22+
uses: actions/checkout@v4
23+
24+
- name: Free disk space (runner)
25+
run: |
26+
set -eux
27+
df -h
28+
sudo rm -rf /usr/share/dotnet || true
29+
sudo rm -rf /usr/local/lib/android || true
30+
sudo rm -rf /opt/ghc || true
31+
sudo rm -rf /usr/local/share/boost || true
32+
sudo rm -rf /usr/local/share/powershell || true
33+
sudo rm -rf /usr/share/swift || true
34+
docker system prune -af || true
35+
df -h
36+
37+
- name: Set up Docker Buildx
38+
uses: docker/setup-buildx-action@v3
39+
40+
- name: Log in to GHCR
41+
uses: docker/login-action@v3
42+
with:
43+
registry: ${{ env.REGISTRY }}
44+
username: ${{ github.actor }}
45+
password: ${{ secrets.GITHUB_TOKEN }}
46+
47+
- name: Extract Docker metadata (CPU)
48+
id: meta
49+
uses: docker/metadata-action@v5
50+
with:
51+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
52+
tags: |
53+
type=ref,event=tag,prefix=cpu-
54+
type=raw,value=cpu-latest
55+
56+
- name: Build and push CPU image
57+
uses: docker/build-push-action@v6
58+
with:
59+
context: .
60+
file: docker/Dockerfile.cpu
61+
push: true
62+
tags: ${{ steps.meta.outputs.tags }}
63+
labels: ${{ steps.meta.outputs.labels }}

docker/Dockerfile.cpu

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ FROM python:3.11-slim
55
# Avoid interactive prompts during apt installs
66
ENV DEBIAN_FRONTEND=noninteractive
77

8-
# Python / pip ergonomics
8+
# Python ergonomics
99
ENV PYTHONUNBUFFERED=1 \
1010
PYTHONDONTWRITEBYTECODE=1 \
11-
PIP_DISABLE_PIP_VERSION_CHECK=1
11+
PIP_DISABLE_PIP_VERSION_CHECK=1 \
12+
PIP_NO_CACHE_DIR=1
1213

1314
# System deps (opencv, pillow, etc.). Keep lean but practical.
1415
RUN apt-get update && apt-get install -y --no-install-recommends \
@@ -23,7 +24,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
2324
libgl1 \
2425
&& rm -rf /var/lib/apt/lists/*
2526

26-
# Create a non-root user (best practice)
27+
# ------------------------------------------------------------
28+
# Non-root user
29+
# ------------------------------------------------------------
2730
ARG USERNAME=lm2
2831
ARG USER_UID=1000
2932
ARG USER_GID=1000
@@ -32,44 +35,52 @@ RUN groupadd --gid ${USER_GID} ${USERNAME} \
3235

3336
WORKDIR /app
3437

35-
# -------------------------------------------------------------------
36-
# Dependency install (cache-friendly):
37-
# 1) Copy only requirements first so Docker can cache this layer
38-
# 2) Install uv + dependencies using uv's resolver (fast, robust)
39-
# 3) Then copy the full repo
40-
# -------------------------------------------------------------------
41-
42-
# Copy requirements separately for better layer caching
38+
# ------------------------------------------------------------
39+
# Cache-friendly dependency install:
40+
# Copy requirements first, install deps, then copy repo.
41+
# ------------------------------------------------------------
4342
COPY requirements.txt /app/requirements.txt
4443

45-
# Install uv + Python deps (system install inside container)
46-
# Notes:
47-
# - --system installs into the container's system Python (no venv).
48-
# - UV_LINK_MODE=copy avoids hardlink warnings in some Docker setups.
44+
# Install pip + uv
45+
# UV_LINK_MODE=copy avoids hardlink warnings in some Docker setups.
4946
ENV UV_LINK_MODE=copy
50-
RUN python -m pip install --no-cache-dir -U pip \
51-
&& pip install --no-cache-dir uv \
52-
&& uv pip install --system -r /app/requirements.txt \
53-
&& uv pip install --system "git+https://github.com/waspinator/pycococreator.git@fba8f4098f3c7aaa05fe119dc93bbe4063afdab8#egg=pycococreatortools" \
47+
RUN python -m pip install --no-cache-dir -U pip setuptools wheel \
48+
&& python -m pip install --no-cache-dir uv
49+
50+
# Install Python deps from requirements using BuildKit cache mount
51+
# Do NOT rm -rf the mount target; it's a BuildKit-managed cache.
52+
ENV UV_CACHE_DIR=/root/.cache/uv
53+
RUN --mount=type=cache,target=/root/.cache/uv \
54+
uv pip install --system --no-cache-dir -r /app/requirements.txt
55+
56+
# Required explicit installs (mirrors GPU approach)
57+
RUN uv pip install --system \
58+
"git+https://github.com/waspinator/pycococreator.git@fba8f4098f3c7aaa05fe119dc93bbe4063afdab8#egg=pycococreatortools" \
5459
&& uv pip install --system "pycocotools>=2.0.5" \
5560
&& uv pip install --system "opencv-contrib-python-headless==4.7.0.72" \
56-
&& uv pip install --system "vit-pytorch==0.37.1" \
57-
&& uv pip install --system torch torchvision torchaudio
61+
&& uv pip install --system "vit-pytorch==0.37.1"
5862

59-
# Optional sanity check (uncomment if you want build-time verification)
60-
# RUN python -c "import cv2, torch; print('cv2', cv2.__version__); print('torch', torch.__version__)"
63+
# Torch CPU build (more deterministic than letting it resolve implicitly)
64+
# If you prefer "latest compatible", you can remove the version pins.
65+
RUN uv pip install --system \
66+
"torch==2.3.1" "torchvision==0.18.1" "torchaudio==2.3.1" \
67+
--index-url https://download.pytorch.org/whl/cpu
6168

6269
# Now copy the rest of the repo into image
6370
COPY . /app
6471

6572
# Runtime environment variables expected by LM2
6673
ENV LM2_MODELS_DIR=/models \
67-
LM2_DATA_DIR=/data
74+
LM2_DATA_DIR=/data \
75+
MPLCONFIGDIR=/tmp/matplotlib \
76+
YOLO_CONFIG_DIR=/tmp/ultralytics
6877

6978
# Create default mount points
70-
RUN mkdir -p /data /models /output \
71-
&& chown -R ${USERNAME}:${USERNAME} /app /data /models /output
79+
# Keep ownership consistent for non-root execution.
80+
RUN mkdir -p /data /models /output /tmp/matplotlib /tmp/ultralytics \
81+
&& chown -R ${USERNAME}:${USERNAME} /app /data /models /output /tmp/matplotlib /tmp/ultralytics
7282

83+
# Entrypoint
7384
COPY docker/entrypoint.sh /entrypoint.sh
7485
RUN chmod +x /entrypoint.sh
7586

docker/compose.cpu.public.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
services:
2+
lm2-cpu:
3+
image: ghcr.io/gene-weaver/leafmachine2:cpu-latest
4+
# Keep the same volumes/working_dir/environment you already use:
5+
volumes:
6+
- ..:/app
7+
working_dir: /app
8+
environment:
9+
HOME: /tmp
10+
MPLCONFIGDIR: /tmp/matplotlib
11+
XDG_CACHE_HOME: /tmp/.cache
12+
TORCHINDUCTOR_CACHE_DIR: /tmp/torchinductor
13+
TORCH_HOME: /tmp/torch
14+
HF_HOME: /tmp/hf
15+
TRANSFORMERS_CACHE: /tmp/hf/transformers
16+
TMPDIR: /tmp
17+
YOLO_CONFIG_DIR: /tmp/ultralytics
18+
command: ["python3", "test_cpu_only.py"]
19+

docker/compose.gpu.public.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
services:
22
lm2-gpu:
3-
image: ghcr.io/gene-weaver/leafmachine2:gpu-v-1
3+
image: ghcr.io/gene-weaver/leafmachine2:gpu-latest
44
# Keep the same volumes/working_dir/environment you already use:
55
volumes:
66
- ..:/app

docker/entrypoint.sh

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,33 @@
11
#!/usr/bin/env bash
22
set -euo pipefail
33

4-
# ---- Cache dirs (always writable) ----
4+
# ---- Default cache locations (writable under non-root) ----
5+
export MPLCONFIGDIR="${MPLCONFIGDIR:-/tmp/matplotlib}"
6+
export YOLO_CONFIG_DIR="${YOLO_CONFIG_DIR:-/tmp/ultralytics}"
7+
export HF_HOME="${HF_HOME:-/tmp/hf}"
8+
export TRANSFORMERS_CACHE="${TRANSFORMERS_CACHE:-/tmp/hf/transformers}"
9+
export TORCH_HOME="${TORCH_HOME:-/tmp/torch}"
10+
export XDG_CACHE_HOME="${XDG_CACHE_HOME:-/tmp/.cache}"
11+
export TORCHINDUCTOR_CACHE_DIR="${TORCHINDUCTOR_CACHE_DIR:-/tmp/torchinductor}"
12+
13+
# ---- Ensure dirs exist ----
514
mkdir -p \
6-
/tmp/matplotlib \
7-
/tmp/.cache \
8-
/tmp/torchinductor \
9-
/tmp/torch \
10-
/tmp/hf/transformers \
11-
/tmp/ultralytics
15+
"$MPLCONFIGDIR" \
16+
"$XDG_CACHE_HOME" \
17+
"$TORCHINDUCTOR_CACHE_DIR" \
18+
"$TORCH_HOME" \
19+
"$HF_HOME" \
20+
"$TRANSFORMERS_CACHE" \
21+
"$YOLO_CONFIG_DIR"
1222

1323
echo "LeafMachine2 container starting"
1424
echo " Working dir: $(pwd)"
1525
echo " Running as: $(id -u):$(id -g)"
1626
echo " Python: $(python --version 2>/dev/null || true)"
17-
echo " YOLO_CONFIG_DIR: ${YOLO_CONFIG_DIR:-}"
18-
echo " MPLCONFIGDIR: ${MPLCONFIGDIR:-}"
27+
echo " YOLO_CONFIG_DIR: ${YOLO_CONFIG_DIR}"
28+
echo " MPLCONFIGDIR: ${MPLCONFIGDIR}"
29+
echo " HF_HOME: ${HF_HOME}"
30+
echo " TORCH_HOME: ${TORCH_HOME}"
31+
echo " XDG_CACHE_HOME: ${XDG_CACHE_HOME}"
1932

20-
# IMPORTANT:
21-
# Your Dockerfiles run as a non-root user (USER lm2).
22-
# Therefore, do NOT attempt useradd/groupadd/chown here, and do NOT use gosu.
2333
exec "$@"

0 commit comments

Comments
 (0)