Skip to content

Commit 47c2cfb

Browse files
committed
feat: complete CPLEX integration with proper LFS tracking
- Re-submitted with Git LFS for large binaries - Implemented robust offline extraction for CPLEX & CP Optimizer - Pre-installed cplex/docplex Python APIs via uv - Enhanced GitHub Actions with disk cleanup and Nix cache - Fixed runtime permissions for non-root user (1000)
1 parent c917cbb commit 47c2cfb

File tree

6 files changed

+576
-54
lines changed

6 files changed

+576
-54
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pkgs/cplex/*.bin filter=lfs diff=lfs merge=lfs -text

.github/workflows/build.yml

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,21 @@ jobs:
3131
checks: write
3232

3333
steps:
34+
- name: Free Disk Space (Ubuntu)
35+
uses: jlumbroso/free-disk-space@main
36+
with:
37+
tool-cache: true
38+
android: true
39+
dotnet: true
40+
haskell: true
41+
large-packages: true
42+
docker-images: true
43+
swap-storage: true
44+
3445
- name: Checkout
3546
uses: actions/checkout@v4
47+
with:
48+
lfs: true # 必须开启 LFS 以便检出 CPLEX 安装包
3649

3750
- name: Install Nix 2.31.1
3851
run: |
@@ -43,6 +56,9 @@ jobs:
4356
# 验证安装
4457
nix --version
4558
59+
- name: Magic Nix Cache
60+
uses: DeterminateSystems/magic-nix-cache-action@main
61+
4662
- name: Configure Nix
4763
run: |
4864
# 加载 Nix 环境
@@ -420,28 +436,32 @@ jobs:
420436
exit 1
421437
fi
422438
423-
# 测试Python和UV(使用临时文件系统挂载)
439+
# 测试Python和UV (不使用 --user root,验证权限修复是否成功)
424440
echo "Testing Python:"
425-
docker run --rm --tmpfs /tmp:noexec,nosuid,size=100m --user root ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest /bin/bash -c "export PATH=\${PATH} && python --version" || echo "Python not found"
441+
docker run --rm --tmpfs /tmp:noexec,nosuid,size=100m ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest /bin/bash -c "python --version" || echo "Python not found"
426442
427443
echo "Testing UV:"
428-
docker run --rm --tmpfs /tmp:noexec,nosuid,size=100m --user root ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest /bin/bash -c "export PATH=\${PATH} && uv --version" || echo "UV not found"
444+
docker run --rm --tmpfs /tmp:noexec,nosuid,size=100m ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest /bin/bash -c "uv --version" || echo "UV not found"
429445
430446
# 测试安全限制
431447
echo "Testing security restrictions:"
432-
docker run --rm --tmpfs /tmp:noexec,nosuid,size=100m --user root ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest /bin/bash -c "python -c \"try: import os; print('ERROR: os should be restricted'); exit(1); except ImportError: print('OK: os is restricted')\"" || echo "Security test failed"
448+
docker run --rm --tmpfs /tmp:noexec,nosuid,size=100m ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest /bin/bash -c "python -c \"try: import os; print('ERROR: os should be restricted'); exit(1); except ImportError: print('OK: os is restricted')\"" || echo "Security test failed"
433449
434450
# 测试Gurobi可用性
435451
echo "Testing Gurobi availability:"
436-
docker run --rm --tmpfs /tmp:noexec,nosuid,size=100m --user root ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest /bin/bash -c "python -c \"import gurobipy; print('OK: Gurobi available')\"" || echo "Gurobi test failed"
452+
docker run --rm --tmpfs /tmp:noexec,nosuid,size=100m ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest /bin/bash -c "python -c \"import gurobipy; print('OK: Gurobi available')\"" || echo "Gurobi test failed"
437453
438454
# 测试OR-Tools可用性
439455
echo "Testing OR-Tools availability:"
440-
docker run --rm --tmpfs /tmp:noexec,nosuid,size=100m --user root ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest /bin/bash -c "python -c \"import ortools; from ortools.linear_solver import pywraplp; print('OK: OR-Tools available')\"" || echo "OR-Tools test failed"
456+
docker run --rm --tmpfs /tmp:noexec,nosuid,size=100m ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest /bin/bash -c "python -c \"import ortools; from ortools.linear_solver import pywraplp; print('OK: OR-Tools available')\"" || echo "OR-Tools test failed"
457+
458+
# 测试CPLEX可用性
459+
echo "Testing CPLEX availability:"
460+
docker run --rm --tmpfs /tmp:noexec,nosuid,size=100m ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest /verify-cplex.sh || echo "CPLEX test failed"
441461
442462
# 测试PuLP可用性
443463
echo "Testing PuLP availability:"
444-
docker run --rm --tmpfs /tmp:exec,nosuid,size=100m --user root ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest /bin/bash -c "cd /tmp && python -c \"import pulp; prob = pulp.LpProblem('test', pulp.LpMaximize); x = pulp.LpVariable('x', 0, 10); prob += x; prob.solve(pulp.PULP_CBC_CMD(msg=0)); print('OK: PuLP and CBC solver available')\"" || echo "PuLP test failed"
464+
docker run --rm --tmpfs /tmp:exec,nosuid,size=100m ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest /bin/bash -c "cd /tmp && python -c \"import pulp; prob = pulp.LpProblem('test', pulp.LpMaximize); x = pulp.LpVariable('x', 0, 10); prob += x; prob.solve(pulp.PULP_CBC_CMD(msg=0)); print('OK: PuLP and CBC solver available')\"" || echo "PuLP test failed"
445465
446466
# 测试科学计算包
447467
echo "Testing scientific packages:"
@@ -471,7 +491,7 @@ jobs:
471491
echo "**Features:**" >> $GITHUB_STEP_SUMMARY
472492
echo "- ✅ Secure Python 3.12 environment" >> $GITHUB_STEP_SUMMARY
473493
echo "- ✅ UV package manager" >> $GITHUB_STEP_SUMMARY
474-
echo "- ✅ Optimization solvers: Gurobi, OR-Tools, PuLP" >> $GITHUB_STEP_SUMMARY
494+
echo "- ✅ Optimization solvers: Gurobi, CPLEX, OR-Tools, PuLP" >> $GITHUB_STEP_SUMMARY
475495
echo "- ✅ Scientific computing packages" >> $GITHUB_STEP_SUMMARY
476496
echo "- ✅ Non-root user execution" >> $GITHUB_STEP_SUMMARY
477497
echo "- ✅ Resource limits and security restrictions" >> $GITHUB_STEP_SUMMARY

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ A secure, production-ready Docker environment for Python development with UV pac
99
- **UV Package Manager**: Fast Python package and dependency manager
1010
- **Optimization Solvers Pre-installed**:
1111
- **Gurobi**: Commercial optimization solver (12.0.3) - requires license
12+
- **CPLEX**: IBM ILOG CPLEX Optimization Studio (22.1.2) - requires license
1213
- **OR-Tools**: Google's open-source suite (GLOP, CBC, SCIP, CP-SAT) - no license required
1314
- **Non-root User**: Runs as non-privileged user for security
1415
- **Resource Limits**: Built-in CPU and memory limits
@@ -79,6 +80,22 @@ docker run --rm \
7980
ghcr.io/reaslab/docker-python-runner:secure-latest python optimization.py
8081
```
8182

83+
### With CPLEX Optimization
84+
85+
```bash
86+
# Run CPLEX optimization code
87+
# CPLEX is pre-installed at /opt/ibm/ILOG/CPLEX_Studio221
88+
docker run --rm -v $(pwd):/app \
89+
ghcr.io/reaslab/docker-python-runner:secure-latest python -c "
90+
import cplex
91+
print(f'CPLEX Version: {cplex.__version__}')
92+
# ... your optimization code ...
93+
"
94+
95+
# Verify CPLEX installation
96+
docker run --rm ghcr.io/reaslab/docker-python-runner:secure-latest /verify-cplex.sh
97+
```
98+
8299
### With OR-Tools Optimization
83100

84101
```bash
@@ -129,6 +146,7 @@ docker run --rm ghcr.io/reaslab/docker-python-runner:secure-latest /verify-ortoo
129146
- **Visualization**: seaborn
130147
- **Optimization**:
131148
- **gurobipy** (Gurobi 12.0.3) - Pre-installed, requires license
149+
- **cplex** (IBM CPLEX 22.1.2) - Pre-installed, requires license
132150
- **ortools** (Google OR-Tools) - Pre-installed, no license required ✨
133151
- **Build Tools**: cython
134152
- **Package Manager**: uv
@@ -221,6 +239,9 @@ docker run --rm ghcr.io/reaslab/docker-python-runner:secure-latest uv --version
221239
# Test Gurobi (requires license)
222240
docker run --rm -v /path/to/gurobi.lic:/app/gurobi.lic:ro ghcr.io/reaslab/docker-python-runner:secure-latest python -c "import gurobipy; print('Gurobi available')"
223241

242+
# Test CPLEX (verify installation)
243+
docker run --rm ghcr.io/reaslab/docker-python-runner:secure-latest /verify-cplex.sh
244+
224245
# Test OR-Tools (pre-installed, verify)
225246
docker run --rm ghcr.io/reaslab/docker-python-runner:secure-latest /verify-ortools.sh
226247

build.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,9 @@ echo "📋 Secure Image Configuration:"
148148
echo " - Python Version: 3.12 (restricted environment)"
149149
echo " - Security: Dangerous modules blocked (os, subprocess, sys, etc.)"
150150
echo " - Resource Limits: 1GB memory, CPU share limits"
151-
echo " - Safe Packages: pip, setuptools, wheel, cython, numpy, scipy, pandas, matplotlib, scikit-learn, yfinance, seaborn"
151+
echo " - Safe Packages: pip, setuptools, wheel, cython, numpy, scipy, pandas, matplotlib, scikit-learn, seaborn, gurobipy, cplex, ortools, pulp"
152152
echo " - Gurobi: 12.0.3 (via nixpkgs)"
153+
echo " - CPLEX: 22.1.2 (from installer)"
153154
echo " - Container: Read-only root filesystem, non-root user (1000:1000)"
154155
echo " - Network: Restricted (disabled by default)"
155156
echo " - Tools: Minimal set (bash, coreutils, curl, tar, gzip)"

0 commit comments

Comments
 (0)