Skip to content

Commit 0fc303a

Browse files
Add Docker and CI setup
ADD: - Provide Dockerfile, compose, README, and .env example - Configure GitHub Actions workflow for lint, tests, and build UPDATE: - Align environment variables with integration tests requirements
1 parent c07e0ec commit 0fc303a

5 files changed

Lines changed: 387 additions & 0 deletions

File tree

.docker/Dockerfile

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
ARG PYTHON_VERSION=3.9
2+
FROM python:${PYTHON_VERSION}-slim
3+
4+
# Create non-root user for security
5+
RUN adduser --disabled-password --gecos '' appuser
6+
7+
# Set working directory
8+
WORKDIR /src
9+
10+
# Copy project configuration files first (rarely change)
11+
COPY pyproject.toml setup.cfg ./
12+
COPY requirements.txt requirements_dev.txt requirements_test.txt ./
13+
14+
# Install dependencies as root before switching to appuser
15+
RUN pip install --upgrade pip && \
16+
pip install --no-cache-dir -r requirements_test.txt
17+
18+
# Copy source code and tests
19+
COPY iloveimg ./iloveimg
20+
COPY tests ./tests
21+
COPY pytest.ini ./
22+
23+
# Change ownership to appuser
24+
RUN chown -R appuser:appuser /src
25+
26+
USER appuser
27+
28+
# Set Python environment variables
29+
ENV PYTHONPATH=/src
30+
ENV PYTHONUNBUFFERED=1
31+
ENV PATH="/home/appuser/.local/bin:$PATH"
32+
33+
# Install package in development mode
34+
RUN pip install --user -e .
35+
36+
# Verify imports work correctly
37+
RUN python -c "import iloveimg; print('✓ iloveimg import successful')"
38+
39+
# Default command: run all tests (JSON format for proper signal handling)
40+
CMD ["sh", "-c", "pytest tests/unit && pytest tests/integration"]

.docker/README.md

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# Docker & Environment Setup
2+
3+
This directory contains Docker configuration files for developing and testing the **iLoveIMG Python** library in an isolated, reproducible environment.
4+
5+
---
6+
7+
## Prerequisites
8+
9+
- [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/) installed on your system.
10+
- Environment variables configured as described below.
11+
12+
---
13+
14+
## Environment Variables
15+
16+
Before running Docker containers, you must set up the required environment variables:
17+
18+
1. Copy the sample environment file:
19+
20+
```bash
21+
cp .docker/.env.sample .docker/.env
22+
```
23+
24+
2. Edit `.docker/.env` and set the following variables:
25+
26+
- `ILOVEIMG_PUBLIC_KEY` – Your iLoveIMG project public key
27+
- `ILOVEIMG_SECRET_KEY` – Your iLoveIMG project secret key
28+
- `FOLDER_SAMPLE_PATH` – Path to sample files (default: `tests/integration/files_samples`)
29+
30+
**Note:**
31+
The `.env` file is used by both local development and Docker containers. Never commit your real credentials.
32+
33+
---
34+
35+
## Quick Start
36+
37+
### 1. Build Docker Images
38+
39+
To build all Python version images (3.9–3.12):
40+
41+
```bash
42+
docker-compose -f .docker/docker-compose.yml build
43+
```
44+
45+
To build a specific Python version (e.g., Python 3.12):
46+
47+
```bash
48+
docker-compose -f .docker/docker-compose.yml build python312
49+
```
50+
51+
To force a rebuild (ignore cache):
52+
53+
```bash
54+
docker-compose -f .docker/docker-compose.yml build --no-cache
55+
```
56+
57+
### 2. Run Tests
58+
59+
To run all tests (unit and integration) in a specific Python version:
60+
61+
```bash
62+
docker-compose -f .docker/docker-compose.yml run python39
63+
```
64+
65+
For Python 3.10:
66+
67+
```bash
68+
docker-compose -f .docker/docker-compose.yml run python310
69+
```
70+
71+
Available services: `python39`, `python310`, `python311`, `python312`
72+
73+
#### Run Only Unit or Integration Tests
74+
75+
```bash
76+
docker-compose -f .docker/docker-compose.yml run python39 pytest tests/unit
77+
docker-compose -f .docker/docker-compose.yml run python39 pytest tests/integration
78+
```
79+
80+
#### Run a Specific Test File
81+
82+
```bash
83+
docker-compose -f .docker/docker-compose.yml run python39 pytest tests/unit/test_compress_task.py
84+
```
85+
86+
---
87+
88+
## Development Workflow
89+
90+
- The project directory is mounted into the container as a volume.
91+
- **Live code editing:** Changes to Python files are immediately reflected in the container.
92+
- **No rebuilds needed:** Modify code and run tests without rebuilding the image.
93+
- **Efficient iteration:** Quickly test changes across multiple Python versions.
94+
95+
Example workflow:
96+
97+
```bash
98+
# Terminal 1: Run tests in Python 3.9
99+
docker-compose -f .docker/docker-compose.yml run python39
100+
101+
# Terminal 2: Edit code in your IDE
102+
# Changes are instantly available in the running container
103+
```
104+
105+
---
106+
107+
## Using Direct Docker Commands
108+
109+
If you prefer to use Docker directly instead of Docker Compose:
110+
111+
```bash
112+
# Build
113+
docker build -t iloveimg-python39 -f .docker/Dockerfile .
114+
115+
# Run with environment file
116+
docker run --env-file .docker/.env iloveimg-python39
117+
```
118+
119+
---
120+
121+
## Troubleshooting
122+
123+
### Container exits after running tests
124+
125+
This is expected. The container runs tests and exits when complete.
126+
To keep it running for debugging:
127+
128+
```bash
129+
docker-compose -f .docker/docker-compose.yml run python39 bash
130+
```
131+
132+
### Changes not reflected in container
133+
134+
- Ensure files are saved in your editor.
135+
- Re-run tests (container picks up changes from mounted volume).
136+
137+
### Clear cache and rebuild
138+
139+
```bash
140+
docker-compose -f .docker/docker-compose.yml down
141+
docker-compose -f .docker/docker-compose.yml build --no-cache
142+
```
143+
144+
---
145+
146+
## Reference
147+
148+
- Main project documentation: [../README.md](../README.md)
149+
- Environment variable sample: [.env.sample](.env.sample)
150+
- Official iLoveIMG API docs: [https://developer.iloveimg.com/docs](https://developer.iloveimg.com/docs)

.docker/docker-compose.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
x-python-base: &python_base
2+
tty: true
3+
env_file:
4+
- ../.env
5+
volumes:
6+
- ../:/src # Mount entire project for live code changes
7+
working_dir: /src
8+
9+
services:
10+
python39:
11+
<<: *python_base
12+
image: iloveimg-python39
13+
build:
14+
context: ../
15+
dockerfile: .docker/Dockerfile
16+
args:
17+
PYTHON_VERSION: "3.9"
18+
container_name: iloveimg-python39
19+
20+
python310:
21+
<<: *python_base
22+
image: iloveimg-python310
23+
build:
24+
context: ../
25+
dockerfile: .docker/Dockerfile
26+
args:
27+
PYTHON_VERSION: "3.10"
28+
container_name: iloveimg-python310
29+
30+
python311:
31+
<<: *python_base
32+
image: iloveimg-python311
33+
build:
34+
context: ../
35+
dockerfile: .docker/Dockerfile
36+
args:
37+
PYTHON_VERSION: "3.11"
38+
container_name: iloveimg-python311
39+
40+
python312:
41+
<<: *python_base
42+
image: iloveimg-python312
43+
build:
44+
context: ../
45+
dockerfile: .docker/Dockerfile
46+
args:
47+
PYTHON_VERSION: "3.12"
48+
container_name: iloveimg-python312

.env.example

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# iLoveIMG API Configuration
2+
# Copy this file to .env and fill in your actual values
3+
# DO NOT commit .env to version control!
4+
5+
# Required: Your iLoveIMG API credentials
6+
# Get these from https://developer.iloveimg.com
7+
ILOVEIMG_PUBLIC_KEY=your_public_key_here
8+
ILOVEIMG_SECRET_KEY=your_secret_key_here
9+
10+
# Optional: Python logging level
11+
# Options: DEBUG, INFO, WARNING, ERROR, CRITICAL
12+
# Default: INFO
13+
PYTHONLOGLEVEL=INFO
14+
15+
# Optional: API server configuration (usually not needed)
16+
# START_SERVER_URL=https://api.ilovepdf.com
17+
# API_HOST=api.ilovepdf.com
18+
# DEFAULT_TIMEOUT_SECONDS=10

.github/workflows/ci.yml

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
name: Python CI
2+
3+
on:
4+
push:
5+
branches: [main, develop]
6+
pull_request:
7+
branches: [main, develop]
8+
workflow_dispatch:
9+
10+
env:
11+
PIP_DISABLE_PIP_VERSION_CHECK: 1
12+
13+
jobs:
14+
lint:
15+
runs-on: ubuntu-latest
16+
container:
17+
image: catthehacker/ubuntu:act-latest
18+
timeout-minutes: 10
19+
strategy:
20+
matrix:
21+
python-version: ["3.9", "3.12"]
22+
fail-fast: false
23+
steps:
24+
- uses: actions/checkout@v4
25+
26+
- uses: actions/setup-python@v5
27+
with:
28+
python-version: ${{ matrix.python-version }}
29+
30+
- name: Upgrade pip
31+
run: python -m pip install --upgrade pip
32+
33+
- name: Install dependencies
34+
run: pip install -r requirements_dev.txt
35+
36+
- name: Lint with flake8
37+
run: flake8 .
38+
39+
- name: Check formatting with black
40+
run: black --check .
41+
42+
- name: Check imports with isort
43+
run: isort --check-only .
44+
45+
- name: Lint with pylint
46+
run: pylint iloveimg/ --fail-under=8.0
47+
48+
unit_test:
49+
runs-on: ubuntu-latest
50+
container:
51+
image: catthehacker/ubuntu:act-latest
52+
timeout-minutes: 15
53+
strategy:
54+
matrix:
55+
python-version: ["3.9", "3.12"]
56+
fail-fast: false
57+
steps:
58+
- uses: actions/checkout@v4
59+
60+
- uses: actions/setup-python@v5
61+
with:
62+
python-version: ${{ matrix.python-version }}
63+
64+
- name: Upgrade pip
65+
run: python -m pip install --upgrade pip
66+
67+
- name: Install dependencies
68+
run: pip install -r requirements_test.txt
69+
70+
- name: Run unit tests with coverage
71+
run: |
72+
pytest tests/unit \
73+
--cov=iloveimg \
74+
--cov-report=xml \
75+
--cov-report=term \
76+
--cov-fail-under=75 \
77+
--timeout=30 \
78+
-v \
79+
--tb=short
80+
81+
- name: Upload coverage to Codecov
82+
if: matrix.python-version == '3.12'
83+
uses: codecov/codecov-action@v3
84+
with:
85+
file: ./coverage.xml
86+
fail_ci_if_error: false
87+
verbose: true
88+
continue-on-error: true
89+
90+
integration_test:
91+
runs-on: ubuntu-latest
92+
timeout-minutes: 20
93+
if: |
94+
github.event_name == 'workflow_dispatch' ||
95+
(github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop'))
96+
strategy:
97+
matrix:
98+
python-version: ["3.9", "3.12"]
99+
fail-fast: false
100+
needs: [lint, unit_test]
101+
steps:
102+
- uses: actions/checkout@v4
103+
104+
- uses: actions/setup-python@v5
105+
with:
106+
python-version: ${{ matrix.python-version }}
107+
108+
- name: Upgrade pip
109+
run: python -m pip install --upgrade pip
110+
111+
- name: Install dependencies
112+
run: pip install -r requirements_test.txt
113+
114+
- name: Verify required secrets
115+
run: |
116+
if [ -z "${{ secrets.ILOVEIMG_PUBLIC_KEY }}" ] || \
117+
[ -z "${{ secrets.ILOVEIMG_SECRET_KEY }}" ]; then
118+
echo "::error::Missing required secrets: ILOVEIMG_PUBLIC_KEY or ILOVEIMG_SECRET_KEY"
119+
exit 1
120+
fi
121+
122+
- name: Run integration tests
123+
env:
124+
ILOVEIMG_PUBLIC_KEY: ${{ secrets.ILOVEIMG_PUBLIC_KEY }}
125+
ILOVEIMG_SECRET_KEY: ${{ secrets.ILOVEIMG_SECRET_KEY }}
126+
run: |
127+
pytest tests/integration \
128+
-v \
129+
--tb=short \
130+
--maxfail=3 \
131+
--timeout=60

0 commit comments

Comments
 (0)