diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..4bdfcce --- /dev/null +++ b/.dockerignore @@ -0,0 +1,31 @@ +.git +.github +.idea +.vscode + +.coverage +.coverage.* +.mypy_cache +.pytest_cache +.ruff_cache +.uv-cache +.venv +venv +env +ENV + +.integration-deps +.tmp +pytest-live + +__pycache__ +*.py[cod] +*.pyo + +build +dist +htmlcov +*.egg-info +coverage.xml + +docs/_build diff --git a/Makefile b/Makefile index a2c745d..0de0fc2 100644 --- a/Makefile +++ b/Makefile @@ -8,8 +8,13 @@ INTEGRATION_PORT_POLL_INTERVAL ?= 0.1 INTEGRATION_WORKERS ?= 2 INTEGRATION_DIST ?= loadscope INTEGRATION_TARGETS ?= tests/integration +CONTAINER_ENGINE ?= docker +DOCKER_IMAGE_REPOSITORY ?= kentbull/sigpy +DOCKERFILE ?= images/signifypy.dockerfile +DOCKER_BUILD_CONTEXT ?= . +DOCKER_VERSION ?= $(shell awk -F'"' '/^version = / { print $$2; exit }' pyproject.toml) -.PHONY: help sync sync-integration-deps sync-integration test test-fast test-ci test-integration test-integration-ci test-integration-parallel test-integration-parallel-ci build dist-check release-patch release-minor release-major release-bump docs clean guard-clean-worktree +.PHONY: help sync sync-integration-deps sync-integration test test-fast test-ci test-integration test-integration-ci test-integration-parallel test-integration-parallel-ci build dist-check docker-build docker-push docker-publish release-patch release-minor release-major release-bump docs clean guard-clean-worktree guard-docker-version help: ## Show available maintainer tasks @awk 'BEGIN {FS = ":.*## "}; /^[a-zA-Z0-9_-]+:.*## / {printf "%-18s %s\n", $$1, $$2}' $(MAKEFILE_LIST) @@ -60,6 +65,19 @@ build: ## Build source and wheel distributions dist-check: clean build ## Build release artifacts and validate them with twine @$(UV_CACHE) $(UV_ENV) $(UV) run --with twine twine check dist/* +docker-build: guard-docker-version ## Build Docker Hub version and latest tags for SignifyPy + @$(CONTAINER_ENGINE) build \ + -f "$(DOCKERFILE)" \ + -t "$(DOCKER_IMAGE_REPOSITORY):$(DOCKER_VERSION)" \ + -t "$(DOCKER_IMAGE_REPOSITORY):latest" \ + "$(DOCKER_BUILD_CONTEXT)" + +docker-push: guard-docker-version ## Push Docker Hub version and latest tags for SignifyPy + @$(CONTAINER_ENGINE) push "$(DOCKER_IMAGE_REPOSITORY):$(DOCKER_VERSION)" + @$(CONTAINER_ENGINE) push "$(DOCKER_IMAGE_REPOSITORY):latest" + +docker-publish: guard-clean-worktree docker-build docker-push ## Build and push SignifyPy Docker Hub tags + release-patch: ## Prepare and commit a patch release @$(MAKE) release-bump BUMP=patch @@ -89,5 +107,8 @@ guard-clean-worktree: exit 1; \ fi +guard-docker-version: + @test -n "$(DOCKER_VERSION)" || { echo "Unable to determine Docker image version from pyproject.toml."; exit 1; } + clean: ## Remove build, docs, and test artifacts @rm -rf build dist docs/_build .pytest_cache .ruff_cache .uv-cache src/signifypy.egg-info diff --git a/images/signifypy.dockerfile b/images/signifypy.dockerfile new file mode 100644 index 0000000..17c94d0 --- /dev/null +++ b/images/signifypy.dockerfile @@ -0,0 +1,45 @@ +FROM python:3.12.8-slim AS builder + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + build-essential \ + ca-certificates \ + curl \ + git \ + libsodium-dev \ + && rm -rf /var/lib/apt/lists/* + +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + +COPY --from=ghcr.io/astral-sh/uv:0.9.5 /uv /uvx /bin/ + +ENV PATH="/root/.cargo/bin:${PATH}" \ + UV_LINK_MODE=copy \ + UV_PROJECT_ENVIRONMENT=/opt/signifypy + +WORKDIR /app + +COPY pyproject.toml uv.lock README.md LICENSE pytest.ini ./ +COPY src/ src/ +COPY scripts/ scripts/ + +RUN uv sync --locked --no-dev --no-editable + +FROM python:3.12.8-slim + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + ca-certificates \ + libsodium23 \ + && rm -rf /var/lib/apt/lists/* + +ENV PATH="/opt/signifypy/bin:${PATH}" \ + PYTHONUNBUFFERED=1 \ + PYTHONIOENCODING=UTF-8 + +WORKDIR /app + +COPY --from=builder /opt/signifypy /opt/signifypy +COPY scripts/ scripts/ + +CMD ["sigpy", "--help"]