Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .DS_Store
Binary file not shown.
77 changes: 77 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: CI

on:
push:
branches: ["**"]
pull_request:
branches: ["**"]

jobs:
lint-and-test:
name: Lint & Test (Python ${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10"]

env:
POETRY_VERSION: "1.8.3"
PIP_DISABLE_PIP_VERSION_CHECK: "1"
PYTHONUNBUFFERED: "1"
OMP_NUM_THREADS: "1"
ALLOW_MOCK_FALLBACK: "0"

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: "pip"

- name: Install system deps (OpenCV/MediaPipe)
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
libgl1 \
libglib2.0-0

- name: Install Poetry
run: |
pip install "poetry==${POETRY_VERSION}"
poetry --version

- name: Configure Poetry (no venv in project)
run: |
poetry config virtualenvs.in-project false
poetry config installer.max-workers 4

- name: Cache Poetry virtualenv
uses: actions/cache@v4
with:
path: |
~/.cache/pypoetry/virtualenvs
key: ${{ runner.os }}-py${{ matrix.python-version }}-poetry-${{ hashFiles('**/poetry.lock') }}
restore-keys: |
${{ runner.os }}-py${{ matrix.python-version }}-poetry-

- name: Install dependencies
run: |
poetry install --no-interaction --no-ansi

- name: Ruff (lint)
run: |
poetry run ruff check .

- name: Black (format check)
run: |
poetry run black --check .

- name: Run tests (pytest, exclude e2e)
run: |
poetry run pytest -q -k "not test_e2e"
env:
AIVM_HEADLESS: "1"
49 changes: 49 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# syntax=docker/dockerfile:1.7
# Minimal, reproducible container to run the CLI.
# Expects a Poetry-based project with a console script entry point (e.g., "prdoc").

FROM python:3.11-slim AS base

# Prevents Python from writing .pyc files & ensures stdout/err are unbuffered
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1

# Install OS deps (build tools, git for editable installs)
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential curl git ca-certificates \
&& rm -rf /var/lib/apt/lists/*

# Install Poetry (no virtualenvs inside container: we use the global env)
ENV POETRY_VERSION=1.8.3 \
POETRY_HOME=/opt/poetry \
POETRY_VIRTUALENVS_CREATE=false
RUN curl -sSL https://install.python-poetry.org | python3 - \
&& ln -s /opt/poetry/bin/poetry /usr/local/bin/poetry

WORKDIR /app

# Copy only dependency files first for better layer caching
COPY pyproject.toml poetry.lock* ./

# Install deps (no dev by default; override with --build-arg if needed)
ARG WITH_DEV_DEPS="false"
RUN if [ "$WITH_DEV_DEPS" = "true" ]; then \
poetry install --no-interaction --no-ansi; \
else \
poetry install --no-interaction --no-ansi --only main; \
fi

# Now copy the source after deps are cached
COPY src ./src
# If you ship scripts or data files outside src, copy them as needed:
# COPY README.md ./

# Reinstall to pick up the local package (editable)
RUN poetry install --no-interaction --no-ansi

# Set an entrypoint to your console script (change "prdoc" if yours differs)
# If your script needs tokens/keys, pass them at `docker run -e KEY=...`
ENTRYPOINT ["prdoc"]
# Or, to keep it flexible:
# CMD ["--help"]
89 changes: 89 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Project Variables
PACKAGE ?= prdoc # import/console script name
IMAGE_NAME ?= $(PACKAGE) # docker image repo/name
IMAGE_TAG ?= $(shell poetry version -s 2>/dev/null || echo latest)
REGISTRY ?= ghcr.io/your-org # e.g., ghcr.io/your-user-or-org
PYTHON ?= python3
POETRY ?= poetry

# Helpers
define log
@printf "\033[1;36m%s\033[0m\n" "$(1)"
endef

# Setup / Quality
.PHONY: install
install: ## Create venv & install all deps (incl. dev)
$(call log,"Installing with Poetry...")
$(POETRY) install

.PHONY: fmt
fmt: ## Format code (ruff+black if configured in pyproject)
$(call log,"Formatting code...")
$(POETRY) run ruff format
$(POETRY) run ruff check --fix

.PHONY: lint
lint: ## Lint only
$(call log,"Linting...")
$(POETRY) run ruff check

.PHONY: test
test: ## Run tests with coverage (if configured)
$(call log,"Running tests...")
$(POETRY) run pytest -q

# Build & Publish (PyPI)
dist: ## Build wheels/sdist into ./dist
$(call log,"Building package artifacts...")
$(POETRY) build

.PHONY: publish
publish: ## Publish to PyPI (needs POETRY_PYPI_TOKEN_PYPI env var)
$(call log,"Publishing to PyPI...")
$(POETRY) publish --username __token__ --password "$$POETRY_PYPI_TOKEN_PYPI"

# Example:
# export POETRY_PYPI_TOKEN_PYPI=pfp_xxx...
# make publish

# Version
.PHONY: version
version: ## Show version from pyproject
@$(POETRY) version

# Docker
.PHONY: docker-build
docker-build: ## Build Docker image (prod deps only by default)
$(call log,"Building Docker image $(IMAGE_NAME):$(IMAGE_TAG)...")
docker build \
--build-arg WITH_DEV_DEPS=false \
-t $(IMAGE_NAME):$(IMAGE_TAG) .

.PHONY: docker-build-dev
docker-build-dev: ## Build Docker image with dev deps
$(call log,"Building Docker image (dev) $(IMAGE_NAME):$(IMAGE_TAG)...")
docker build \
--build-arg WITH_DEV_DEPS=true \
-t $(IMAGE_NAME):$(IMAGE_TAG)-dev .

.PHONY: docker-run
docker-run: ## Run the CLI inside the container (override CMD/args as needed)
$(call log,"Running $(IMAGE_NAME):$(IMAGE_TAG)...")
docker run --rm -it \
$(IMAGE_NAME):$(IMAGE_TAG) --help

.PHONY: docker-tag
docker-tag: ## Tag image for registry
$(call log,"Tagging image for registry...")
docker tag $(IMAGE_NAME):$(IMAGE_TAG) $(REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)

.PHONY: docker-push
docker-push: docker-tag ## Push image to registry
$(call log,"Pushing image to $(REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)...")
docker push $(REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)

# Help
.PHONY: help
help: ## Show this help
@awk 'BEGIN {FS = ":.*##"; print "Available targets:\n"} /^[a-zA-Z0-9_.-]+:.*?##/ {printf " \033[1m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
2 changes: 1 addition & 1 deletion config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ camera:
width: 1280
height: 720
region_of_interest:
enabled: true
enabled: false
x_min: 0.203
x_max: 1.034
y_min: 0.126
Expand Down
8 changes: 4 additions & 4 deletions implementation.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,30 +120,30 @@
- **Step Dependencies**: 5, 8, 9
- **User Instructions**: `poetry add --group dev pytest && pytest -q`.

- [ ] **Step 14: Integration test with prerecorded frames**
- [x] **Step 14: Integration test with prerecorded frames**

- **Task**: Validate end‑to‑end gesture→mouse event sequence via PyAutoGUI mock.
- **Files**:
- `tests/test_e2e.py`: new
- `tests/fixtures/frames/*.jpg`: new
- **Step Dependencies**: 6, 7, 8

- [ ] **Step 15: GitHub Actions workflow**
- [x] **Step 15: GitHub Actions workflow**
- **Task**: Lint, unit, integration tests on push.
- **Files**:
- `.github/workflows/ci.yaml`: new
- **Step Dependencies**: 13, 14

## 9 ‒ Packaging & Distribution

- [ ] **Step 16: CLI entry‑point**
- [x] **Step 16: CLI entry‑point**

- **Task**: Add `python -m ai_virtual_mouse` bootstrapping using `if __name__ == "__main__"`.
- **Files**:
- `src/ai_virtual_mouse/__main__.py`: new
- **Step Dependencies**: 10, 11, 12

- [ ] **Step 17: Build Dockerfile & publish to PyPI**
- [x] **Step 17: Build Dockerfile & publish to PyPI**
- **Task**: Provide containerized run option; configure Poetry publish workflow.
- **Files**:
- `Dockerfile`: new
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
name = "ai-virtual-mouse"
version = "0.1.0"
description = "Camera-based virtual mouse controlled by hand gestures"
authors = ["Bethvour bethvourc@gmail.com"]
authors = ["Bethvour <bethvourc@gmail.com>"]
readme = "README.md"
packages = [{ include = "ai_virtual_mouse", from = "src" }]

Expand Down
Binary file modified src/.DS_Store
Binary file not shown.
Loading