Skip to content

Commit 21224f5

Browse files
bakerboy448claude
andcommitted
feat: Add Docker containerization with CI/CD pipeline
- Add Dockerfile with Python 3.11-slim, non-root user, health checks - Add docker-compose.yml with environment variables and volume mounts - Add .dockerignore for clean builds excluding sensitive files - Add GitHub Actions workflow for multi-platform builds (amd64/arm64) - Support ghcr.io and Docker Hub registries with manual approval for main - Add pre-commit hooks for code quality and security validation - Update CLAUDE.md with Docker deployment documentation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent c0d1295 commit 21224f5

File tree

6 files changed

+411
-0
lines changed

6 files changed

+411
-0
lines changed

.dockerignore

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Git
2+
.git
3+
.gitignore
4+
5+
# Python
6+
__pycache__/
7+
*.py[cod]
8+
*$py.class
9+
*.so
10+
.Python
11+
build/
12+
develop-eggs/
13+
dist/
14+
downloads/
15+
eggs/
16+
.eggs/
17+
lib/
18+
lib64/
19+
parts/
20+
sdist/
21+
var/
22+
wheels/
23+
*.egg-info/
24+
.installed.cfg
25+
*.egg
26+
27+
# Virtual environments
28+
venv/
29+
ENV/
30+
env/
31+
.venv
32+
.env
33+
34+
# IDE
35+
.vscode/
36+
.idea/
37+
*.swp
38+
*.swo
39+
*~
40+
.DS_Store
41+
42+
# Sensitive files
43+
config.json
44+
*.config.json
45+
config.*.json
46+
47+
# Database files (will be mounted as volume)
48+
*.db
49+
*.db-journal
50+
*.sqlite
51+
*.sqlite3
52+
53+
# Logs (will be mounted as volume)
54+
*.log
55+
logs/
56+
log/
57+
58+
# Testing
59+
.coverage
60+
.pytest_cache/
61+
.tox/
62+
htmlcov/
63+
.hypothesis/
64+
65+
# Documentation
66+
*.md
67+
docs/
68+
69+
# Backup files
70+
*.bak
71+
*.backup
72+
*.old
73+
74+
# Temporary files
75+
tmp/
76+
temp/
77+
.tmp/
78+
79+
# System files
80+
Thumbs.db
81+
desktop.ini
82+
83+
# Docker
84+
Dockerfile
85+
docker-compose.yml
86+
.dockerignore
87+
88+
# Reddit specific
89+
processed_actions.json
90+
modlog_backup_*.json

.github/workflows/docker-build.yml

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
name: Build and Push Docker Images
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
pull_request:
8+
branches:
9+
- main
10+
workflow_dispatch:
11+
inputs:
12+
deploy_to_main:
13+
description: 'Deploy to main branch registries'
14+
required: true
15+
default: false
16+
type: boolean
17+
18+
env:
19+
REGISTRY_GHCR: ghcr.io
20+
REGISTRY_DOCKERHUB: docker.io
21+
IMAGE_NAME: ${{ github.repository }}
22+
23+
jobs:
24+
build:
25+
runs-on: ubuntu-latest
26+
permissions:
27+
contents: read
28+
packages: write
29+
30+
steps:
31+
- name: Checkout
32+
uses: actions/checkout@v4
33+
34+
- name: Set up Docker Buildx
35+
uses: docker/setup-buildx-action@v3
36+
37+
- name: Login to GitHub Container Registry
38+
if: github.event_name != 'pull_request' && (github.ref_type == 'tag' || inputs.deploy_to_main == true)
39+
uses: docker/login-action@v3
40+
with:
41+
registry: ${{ env.REGISTRY_GHCR }}
42+
username: ${{ github.actor }}
43+
password: ${{ secrets.GITHUB_TOKEN }}
44+
45+
- name: Login to Docker Hub
46+
if: github.event_name != 'pull_request' && (github.ref_type == 'tag' || inputs.deploy_to_main == true)
47+
uses: docker/login-action@v3
48+
with:
49+
registry: ${{ env.REGISTRY_DOCKERHUB }}
50+
username: ${{ secrets.DOCKERHUB_USERNAME }}
51+
password: ${{ secrets.DOCKERHUB_TOKEN }}
52+
53+
- name: Extract metadata
54+
id: meta
55+
uses: docker/metadata-action@v5
56+
with:
57+
images: |
58+
${{ env.REGISTRY_GHCR }}/${{ env.IMAGE_NAME }}
59+
${{ env.REGISTRY_DOCKERHUB }}/${{ env.IMAGE_NAME }}
60+
tags: |
61+
type=ref,event=branch
62+
type=ref,event=pr
63+
type=semver,pattern={{version}}
64+
type=semver,pattern={{major}}.{{minor}}
65+
type=semver,pattern={{major}}
66+
type=raw,value=latest,enable={{is_default_branch}}
67+
68+
- name: Build and push
69+
uses: docker/build-push-action@v5
70+
with:
71+
context: .
72+
platforms: linux/amd64,linux/arm64
73+
push: ${{ github.event_name != 'pull_request' && (github.ref_type == 'tag' || inputs.deploy_to_main == true) }}
74+
tags: ${{ steps.meta.outputs.tags }}
75+
labels: ${{ steps.meta.outputs.labels }}
76+
cache-from: type=gha
77+
cache-to: type=gha,mode=max
78+
79+
- name: Update Docker Hub description
80+
if: github.event_name != 'pull_request' && (github.ref_type == 'tag' || inputs.deploy_to_main == true)
81+
uses: peter-evans/dockerhub-description@v3
82+
with:
83+
username: ${{ secrets.DOCKERHUB_USERNAME }}
84+
password: ${{ secrets.DOCKERHUB_TOKEN }}
85+
repository: ${{ env.IMAGE_NAME }}
86+
readme-filepath: ./README.md

.pre-commit-config.yaml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
repos:
2+
- repo: https://github.com/pre-commit/pre-commit-hooks
3+
rev: v4.4.0
4+
hooks:
5+
- id: trailing-whitespace
6+
- id: end-of-file-fixer
7+
- id: check-yaml
8+
- id: check-added-large-files
9+
- id: check-merge-conflict
10+
- id: debug-statements
11+
- id: check-executables-have-shebangs
12+
13+
- repo: https://github.com/psf/black
14+
rev: 23.7.0
15+
hooks:
16+
- id: black
17+
language_version: python3
18+
args: [--line-length=88]
19+
20+
- repo: https://github.com/pycqa/flake8
21+
rev: 6.0.0
22+
hooks:
23+
- id: flake8
24+
args: [--max-line-length=88, --extend-ignore=E203,W503]
25+
26+
- repo: https://github.com/pycqa/isort
27+
rev: 5.12.0
28+
hooks:
29+
- id: isort
30+
args: [--profile=black]
31+
32+
- repo: https://github.com/Yelp/detect-secrets
33+
rev: v1.4.0
34+
hooks:
35+
- id: detect-secrets
36+
args: ['--baseline', '.secrets.baseline']
37+
exclude: |
38+
(?x)^(
39+
config_template\.json|
40+
\.secrets\.baseline
41+
)$
42+
43+
- repo: https://github.com/pre-commit/mirrors-mypy
44+
rev: v1.4.1
45+
hooks:
46+
- id: mypy
47+
additional_dependencies: [types-requests]
48+
args: [--ignore-missing-imports]
49+
50+
- repo: local
51+
hooks:
52+
- id: reddit-config-check
53+
name: Check Reddit config safety
54+
entry: python -c "import json; config = json.load(open('config.json')) if __import__('os').path.exists('config.json') else {}; exit(1) if not config.get('anonymize_moderators', True) else exit(0)"
55+
language: system
56+
files: config\.json$
57+
pass_filenames: false

CLAUDE.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,80 @@ User profile links are a privacy concern and not useful for modlog purposes.
240240
- ✅ Database schema at version 5 with all required columns
241241
- ✅ Consistent Reddit API field usage (action.details vs action.description)
242242

243+
## Docker Deployment
244+
245+
### Building and Running
246+
247+
```bash
248+
# Build the Docker image
249+
docker build -t reddit-modlog:latest .
250+
251+
# Run with docker-compose (recommended)
252+
docker-compose up -d
253+
254+
# Run directly with Docker
255+
docker run -d \
256+
--name reddit-modlog-bot \
257+
--restart unless-stopped \
258+
-e REDDIT_CLIENT_ID=your_client_id \
259+
-e REDDIT_CLIENT_SECRET=your_client_secret \
260+
-e REDDIT_USERNAME=your_username \
261+
-e REDDIT_PASSWORD=your_password \
262+
-e SOURCE_SUBREDDIT=your_subreddit \
263+
-v ./data:/app/data \
264+
-v ./logs:/app/logs \
265+
reddit-modlog:latest
266+
```
267+
268+
### Environment Configuration
269+
270+
Create a `.env` file in the project root:
271+
272+
```env
273+
# Reddit API credentials (REQUIRED)
274+
REDDIT_CLIENT_ID=your_client_id
275+
REDDIT_CLIENT_SECRET=your_client_secret
276+
REDDIT_USERNAME=your_bot_username
277+
REDDIT_PASSWORD=your_bot_password
278+
279+
# Application settings
280+
SOURCE_SUBREDDIT=your_subreddit
281+
WIKI_PAGE=modlog
282+
RETENTION_DAYS=30
283+
BATCH_SIZE=50
284+
UPDATE_INTERVAL=300
285+
286+
# Wiki actions to process
287+
WIKI_ACTIONS=removelink,removecomment,addremovalreason,spamlink,spamcomment,approvelink,approvecomment
288+
```
289+
290+
### CI/CD Pipeline
291+
292+
The project includes GitHub Actions for automated Docker builds:
293+
- **Triggers**: Git tags (v*) and manual workflow dispatch
294+
- **Registries**: GitHub Container Registry (ghcr.io) and Docker Hub
295+
- **Platforms**: linux/amd64, linux/arm64
296+
- **Manual approval required** for main branch deployments
297+
298+
### Pre-commit Hooks
299+
300+
Install and use pre-commit hooks for code quality:
301+
302+
```bash
303+
# Install pre-commit
304+
pip install pre-commit
305+
306+
# Install hooks
307+
pre-commit install
308+
309+
# Run manually
310+
pre-commit run --all-files
311+
```
312+
243313
## Development Guidelines
244314

245315
### Git Workflow
316+
- **NEVER push to main without manual approval**
246317
- If branch is not main, you may commit and push if a PR is draft or not open
247318
- Use conventional commits for all changes
248319
- Use multiple commits if needed, or patch if easier

Dockerfile

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
FROM python:3.11-slim
2+
3+
# Set working directory
4+
WORKDIR /app
5+
6+
# Install system dependencies
7+
RUN apt-get update && apt-get install -y \
8+
sqlite3 \
9+
&& rm -rf /var/lib/apt/lists/*
10+
11+
# Copy requirements first for better Docker layer caching
12+
COPY requirements.txt .
13+
14+
# Install Python dependencies
15+
RUN pip install --no-cache-dir -r requirements.txt
16+
17+
# Copy application files
18+
COPY modlog_wiki_publisher.py .
19+
COPY config_template.json .
20+
21+
# Create directories for data persistence
22+
RUN mkdir -p /app/data /app/logs
23+
24+
# Create non-root user for security
25+
RUN groupadd -r modlogbot && useradd -r -g modlogbot modlogbot
26+
RUN chown -R modlogbot:modlogbot /app
27+
USER modlogbot
28+
29+
# Set environment variables
30+
ENV PYTHONUNBUFFERED=1
31+
ENV DB_PATH=/app/data/modlog.db
32+
ENV LOGS_DIR=/app/logs
33+
34+
# Expose health check port (if we add one)
35+
EXPOSE 8080
36+
37+
# Health check
38+
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
39+
CMD python -c "import sqlite3; conn = sqlite3.connect('${DB_PATH}'); conn.close()" || exit 1
40+
41+
# Default command - can be overridden
42+
CMD ["python", "modlog_wiki_publisher.py", "--continuous"]

0 commit comments

Comments
 (0)