Skip to content

Commit c32d5f8

Browse files
authored
Merge pull request #8 from econ-ark/add-docker
Add Docker and DevContainer support
2 parents ef59f67 + e8f8e00 commit c32d5f8

File tree

16 files changed

+791
-412
lines changed

16 files changed

+791
-412
lines changed

.devcontainer/devcontainer.json

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"name": "Method of Moderation",
3+
"image": "mcr.microsoft.com/devcontainers/python:3.12",
4+
"remoteUser": "vscode",
5+
6+
"onCreateCommand": "sudo apt-get update && sudo apt-get install -y curl git make && curl -fsSL https://deb.nodesource.com/setup_18.x | sudo bash - && sudo apt-get install -y nodejs",
7+
8+
"postCreateCommand": "bash ${containerWorkspaceFolder}/reproduce/docker/setup.sh",
9+
10+
"postStartCommand": "ARCH=$(uname -m); VENV=\"${containerWorkspaceFolder}/.venv-linux-${ARCH}\"; if [ -f \"$VENV/bin/activate\" ]; then source \"$VENV/bin/activate\"; fi",
11+
12+
"containerEnv": {
13+
"PYTHONUNBUFFERED": "1",
14+
"PYTHONPATH": "${containerWorkspaceFolder}/code"
15+
},
16+
17+
"forwardPorts": [8888, 8866],
18+
19+
"portsAttributes": {
20+
"8888": {
21+
"label": "Jupyter Lab",
22+
"onAutoForward": "notify"
23+
},
24+
"8866": {
25+
"label": "Voilà Dashboard",
26+
"onAutoForward": "notify"
27+
}
28+
},
29+
30+
"customizations": {
31+
"vscode": {
32+
"extensions": [
33+
"ms-python.python",
34+
"ms-toolsai.jupyter",
35+
"charliermarsh.ruff",
36+
"tamasfe.even-better-toml"
37+
],
38+
"settings": {
39+
"python.defaultInterpreterPath": "${containerWorkspaceFolder}/.venv-linux-x86_64/bin/python",
40+
"python.terminal.activateEnvironment": true,
41+
"editor.formatOnSave": true,
42+
"editor.codeActionsOnSave": {
43+
"source.fixAll.ruff": "explicit",
44+
"source.organizeImports.ruff": "explicit"
45+
}
46+
}
47+
}
48+
}
49+
}

.dockerignore

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Method of Moderation Docker ignore file
2+
# Prevents local development artifacts from being copied into the Docker build
3+
4+
# Virtual environments (all platform-specific variants)
5+
.venv/
6+
.venv-*/
7+
venv/
8+
env/
9+
__pycache__/
10+
*.pyc
11+
12+
# Git
13+
.git/
14+
.gitignore
15+
16+
# IDE/Editor
17+
.cursor*/
18+
.specstory/
19+
.vscode/
20+
.idea/
21+
*.swp
22+
*.swo
23+
24+
# OS files
25+
.DS_Store
26+
Thumbs.db
27+
28+
# Build artifacts
29+
*.egg-info/
30+
dist/
31+
build/
32+
33+
# Local cache
34+
.cache/
35+
.pytest_cache/
36+
.ruff_cache/
37+
.mypy_cache/
38+
39+
# Jupyter
40+
.ipynb_checkpoints/
41+
42+
# Docker (prevent recursive copies)
43+
.dockerignore

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ celerybeat.pid
113113
# Environments
114114
.env
115115
.venv
116+
.venv-*
116117
env/
117118
venv/
118119
ENV/

.pre-commit-config.yaml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,6 @@ ci:
33
autofix_commit_msg: "style: pre-commit fixes"
44

55
repos:
6-
- repo: https://github.com/astral-sh/uv-pre-commit
7-
rev: 0.8.14
8-
hooks:
9-
- id: pip-compile
10-
args: [pyproject.toml, --output-file, requirements.txt]
11-
files: ^(pyproject\.toml|requirements\.txt)$
12-
136
- repo: https://github.com/pre-commit/pre-commit-hooks
147
rev: "v6.0.0"
158
hooks:

Dockerfile

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Method of Moderation Dockerfile
2+
# Single Source of Truth: reproduce/docker/setup.sh
3+
#
4+
# Build: docker build -t method-of-moderation .
5+
# Run: docker run -it --rm method-of-moderation
6+
# Jupyter: docker run -it --rm -p 8888:8888 method-of-moderation jupyter lab --ip=0.0.0.0 --no-browser
7+
8+
FROM mcr.microsoft.com/devcontainers/python:3.12
9+
10+
# Metadata
11+
LABEL org.opencontainers.image.title="Method of Moderation"
12+
LABEL org.opencontainers.image.description="Development environment for Method of Moderation REMARK"
13+
LABEL org.opencontainers.image.source="https://github.com/econ-ark/method-of-moderation"
14+
15+
# Environment
16+
ENV PYTHONUNBUFFERED=1
17+
ENV DEBIAN_FRONTEND=noninteractive
18+
19+
# Install minimal system dependencies
20+
RUN apt-get update && apt-get install -y --no-install-recommends \
21+
curl \
22+
git \
23+
make \
24+
&& rm -rf /var/lib/apt/lists/*
25+
26+
# Install Node.js 18 (required by MyST for building documentation)
27+
RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
28+
&& apt-get install -y nodejs \
29+
&& rm -rf /var/lib/apt/lists/*
30+
31+
# Create workspace directory with correct ownership
32+
RUN mkdir -p /workspace && chown vscode:vscode /workspace
33+
WORKDIR /workspace
34+
35+
# Copy files with correct ownership
36+
COPY --chown=vscode:vscode . /workspace/
37+
38+
# Set execute permissions on all shell scripts
39+
USER vscode
40+
RUN find /workspace -name "*.sh" -type f -exec chmod +x {} \;
41+
42+
# Run setup (creates architecture-specific venv)
43+
RUN bash /workspace/reproduce/docker/setup.sh
44+
45+
# Set runtime environment
46+
# Note: The actual venv path depends on architecture (e.g., .venv-linux-x86_64 or .venv-linux-aarch64)
47+
ENV PATH="/workspace/.venv-linux-x86_64/bin:/workspace/.venv-linux-aarch64/bin:/home/vscode/.local/bin:$PATH"
48+
ENV PYTHONPATH="/workspace/code"
49+
50+
# Create entrypoint script that activates the venv
51+
RUN echo '#!/bin/bash' > /home/vscode/entrypoint.sh && \
52+
echo 'set -e' >> /home/vscode/entrypoint.sh && \
53+
echo '' >> /home/vscode/entrypoint.sh && \
54+
echo '# Determine architecture-specific venv path' >> /home/vscode/entrypoint.sh && \
55+
echo 'ARCH=$(uname -m)' >> /home/vscode/entrypoint.sh && \
56+
echo 'VENV_PATH="/workspace/.venv-linux-$ARCH"' >> /home/vscode/entrypoint.sh && \
57+
echo '' >> /home/vscode/entrypoint.sh && \
58+
echo '# Activate venv if it exists' >> /home/vscode/entrypoint.sh && \
59+
echo 'if [ -f "$VENV_PATH/bin/activate" ]; then' >> /home/vscode/entrypoint.sh && \
60+
echo ' source "$VENV_PATH/bin/activate"' >> /home/vscode/entrypoint.sh && \
61+
echo 'fi' >> /home/vscode/entrypoint.sh && \
62+
echo '' >> /home/vscode/entrypoint.sh && \
63+
echo '# Execute the command' >> /home/vscode/entrypoint.sh && \
64+
echo 'exec "$@"' >> /home/vscode/entrypoint.sh && \
65+
chmod +x /home/vscode/entrypoint.sh
66+
67+
# Expose common ports
68+
EXPOSE 8888 8866
69+
70+
# Use entrypoint to activate venv, then run command
71+
ENTRYPOINT ["/home/vscode/entrypoint.sh"]
72+
CMD ["/bin/bash"]

binder/README.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Binder Configuration
2+
3+
This directory contains configuration files for [MyBinder.org](https://mybinder.org),
4+
which allows users to launch interactive Jupyter notebooks in the cloud.
5+
6+
## Single Source of Truth
7+
8+
The binder setup draws from the same source as Docker and DevContainer:
9+
10+
| Component | Source |
11+
|-----------|--------|
12+
| Python dependencies | `pyproject.toml` (installed via `uv`) |
13+
| System dependencies | Same as `Dockerfile` (Node.js 18, curl, git, make) |
14+
| Environment setup | Mirrors `reproduce/docker/setup.sh` |
15+
16+
## Files
17+
18+
| File | Purpose |
19+
|------|---------|
20+
| `environment.yml` | Minimal conda env (Python 3.12 + pip) |
21+
| `apt.txt` | System packages (curl, git, make) |
22+
| `postBuild` | Installs Node.js 18, uv, and project dependencies |
23+
| `README.md` | This file |
24+
25+
## How It Works
26+
27+
1. **Binder reads `environment.yml`** → Creates conda environment with Python 3.12
28+
2. **Binder reads `apt.txt`** → Installs system packages via apt-get
29+
3. **Binder runs `postBuild`** → Installs Node.js 18, uv, and all Python dependencies
30+
31+
The `postBuild` script:
32+
- Installs Node.js 18 from nodesource (required by MyST)
33+
- Installs `uv` package manager
34+
- Runs `uv pip install --system -e .` to install from `pyproject.toml`
35+
- Configures `PYTHONPATH` for the `code/` directory
36+
- Warms up matplotlib and numba caches
37+
38+
## Launching on MyBinder
39+
40+
Click the binder badge in the main README, or use this URL:
41+
42+
```
43+
https://mybinder.org/v2/gh/econ-ark/method-of-moderation/main
44+
```
45+
46+
## Testing Locally
47+
48+
To test the binder configuration locally:
49+
50+
```bash
51+
# Option 1: Use Docker (recommended)
52+
docker build -t method-of-moderation .
53+
docker run -it --rm -p 8888:8888 method-of-moderation jupyter lab --ip=0.0.0.0 --no-browser
54+
55+
# Option 2: Use the setup script directly
56+
bash reproduce/docker/setup.sh
57+
source .venv-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m)/bin/activate
58+
jupyter lab
59+
```
60+
61+
## Relationship to Other Environments
62+
63+
```
64+
┌─────────────────────┐
65+
│ pyproject.toml │ ← Single Source of Truth
66+
│ (dependencies) │
67+
└─────────┬───────────┘
68+
69+
┌─────────────────────┼─────────────────────┐
70+
│ │ │
71+
▼ ▼ ▼
72+
┌───────────────┐ ┌─────────────────┐ ┌─────────────────┐
73+
│ Dockerfile │ │ devcontainer │ │ Binder │
74+
│ │ │ .json │ │ postBuild │
75+
└───────┬───────┘ └────────┬────────┘ └────────┬────────┘
76+
│ │ │
77+
▼ ▼ ▼
78+
┌───────────────┐ ┌─────────────────┐ ┌─────────────────┐
79+
│ reproduce/ │ │ reproduce/ │ │ uv pip install │
80+
│ docker/ │ │ docker/ │ │ --system -e . │
81+
│ setup.sh │ │ setup.sh │ │ │
82+
└───────────────┘ └─────────────────┘ └─────────────────┘
83+
```
84+
85+
All three environments install the same dependencies from `pyproject.toml`,
86+
ensuring consistency across local development, CI, and cloud notebooks.

binder/apt.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# System dependencies for Method of Moderation
2+
# These are installed via apt-get before the Python environment is set up
3+
4+
# Build tools
5+
curl
6+
git
7+
make
8+
9+
# Node.js is installed via postBuild using nodesource (apt version is too old for MyST)

binder/environment.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
# Binder environment configuration
2-
# Minimal conda environment - uv handles Python packages via postBuild
2+
# Single Source of Truth: pyproject.toml (dependencies installed via uv in postBuild)
3+
#
4+
# This minimal conda environment provides Python 3.12 and pip.
5+
# All project dependencies are installed from pyproject.toml using uv.
36

47
name: moderation
58
channels:
69
- conda-forge
10+
- defaults
11+
712
dependencies:
813
- python=3.12
914
- pip

0 commit comments

Comments
 (0)