Skip to content

update

update #31

Workflow file for this run

name: Build and Push Docker Images with Nix Flakes
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 2 * * 1' # Weekly on Monday at 2 AM
workflow_dispatch:
inputs:
push_images:
description: 'Push images to registry'
type: boolean
default: true
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write
attestations: write
security-events: write
actions: read
checks: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Nix 2.31.1
run: |
# 安装最新稳定版 Nix 2.31.1 (单用户模式)
sh <(curl -L https://nixos.org/nix/install) --no-daemon --yes
# 重新加载环境
. /home/runner/.nix-profile/etc/profile.d/nix.sh
# 验证安装
nix --version
- name: Configure Nix
run: |
# 加载 Nix 环境
. /home/runner/.nix-profile/etc/profile.d/nix.sh
# 配置 Nix 以支持 Flakes
mkdir -p ~/.config/nix
cat > ~/.config/nix/nix.conf << EOF
experimental-features = nix-command flakes
allow-import-from-derivation = true
EOF
# 验证配置
nix --version
- name: Clean build environment
run: |
# 加载 Nix 环境
. /home/runner/.nix-profile/etc/profile.d/nix.sh
# 清理 Nix 缓存
echo "🧹 Cleaning Nix cache..."
nix-collect-garbage
nix store gc
# 清理 Docker 环境
echo "🧹 Cleaning Docker environment..."
docker image prune -f
docker container prune -f
docker builder prune -f
# 清理可能冲突的镜像
docker rmi ghcr.io/reaslab/docker-python-runner:secure-latest 2>/dev/null || echo " No existing image to remove"
- name: Setup Nix Flake environment
run: |
# 加载 Nix 环境
. /home/runner/.nix-profile/etc/profile.d/nix.sh
# 设置环境变量以允许非自由包(Gurobi)
export NIXPKGS_ALLOW_UNFREE=1
# 进入 Nix 开发环境并构建
echo "🔧 Setting up Nix Flake environment..."
nix develop --command bash -c "
echo '🐍 Building Python Docker image with Nix Flakes...'
echo 'Current directory:'
pwd
echo 'Available files:'
ls -la
# 设置更宽松的构建选项
export NIX_BUILD_CORES=0
export NIX_CONF_DIR=/tmp/nix-conf
mkdir -p \$NIX_CONF_DIR
# 使用与本地构建相同的方式,添加详细输出
echo 'Starting build with detailed output...'
nix build .#docker-image --option sandbox false --impure --rebuild --show-trace --verbose || {
echo '❌ First build attempt failed, trying with different options...'
# 尝试不同的构建选项
nix build .#docker-image --option sandbox false --impure --rebuild --option max-jobs 1 --option cores 1 || {
echo '❌ Second build attempt failed, trying without rebuild...'
nix build .#docker-image --option sandbox false --impure --option max-jobs 1 --option cores 1
}
}
echo 'Build command completed, checking results...'
# 检查构建结果
echo '🔍 Checking build results...'
ls -la
# 验证构建结果
if [ -L result ]; then
echo '✅ Result symlink created successfully'
ls -la result
echo 'Result points to:'
readlink result
echo 'File exists and is readable:'
test -r result && echo 'Yes' || echo 'No'
else
echo '❌ Result symlink not found, checking for other outputs...'
# 查找可能的输出文件
find . -name '*.tar.gz' -o -name 'docker-image*' 2>/dev/null || echo 'No tar.gz files found'
# 检查 Nix store 中的构建结果
echo 'Checking Nix store for build results...'
nix-store --query --outputs \$(nix-instantiate .#docker-image) 2>/dev/null || echo 'No outputs found in store'
exit 1
fi
"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
if: github.event.inputs.push_images != 'false'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Load Docker image
run: |
# 加载 Nix 环境
. /home/runner/.nix-profile/etc/profile.d/nix.sh
# 设置环境变量以允许非自由包(Gurobi)
export NIXPKGS_ALLOW_UNFREE=1
# 在 Nix 开发环境中加载 Docker 镜像
echo "🐳 Loading Docker image from Nix build result..."
nix develop --command bash -c "
if [ -L result ]; then
echo '✅ Result symlink found, loading Docker image...'
docker load < result
echo '✅ Docker image loaded successfully'
else
echo '❌ Result symlink not found, rebuilding...'
nix build .#docker-image --option sandbox false --impure --rebuild
docker load < result
echo '✅ Docker image rebuilt and loaded successfully'
fi
"
- name: Tag Docker image
if: github.event.inputs.push_images != 'false'
run: |
# Get the image ID from the loaded image
IMAGE_ID=$(docker images --format "{{.ID}}" | head -1)
echo "Image ID: $IMAGE_ID"
# Create tags based on trigger type
TAGS=()
# Always create latest tag
TAGS+=("${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest")
# Create version-specific tags based on trigger
if [ "${{ github.event_name }}" = "push" ] && [ "${{ github.ref }}" = "refs/heads/main" ]; then
# Main branch push: create timestamp and SHA tags
TAGS+=("${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-$(date +%Y%m%d-%H%M%S)")
TAGS+=("${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-${{ github.sha }}")
elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
# Manual trigger: create timestamp tag only
TAGS+=("${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-$(date +%Y%m%d-%H%M%S)")
elif [ "${{ github.event_name }}" = "schedule" ]; then
# Scheduled: create timestamp tag only
TAGS+=("${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-$(date +%Y%m%d-%H%M%S)")
fi
# Tag with all tags
for tag in "${TAGS[@]}"; do
echo "Tagging with: $tag"
docker tag "$IMAGE_ID" "$tag"
done
echo "All tags created successfully"
- name: Push Docker image
if: github.event.inputs.push_images != 'false'
run: |
# Create tags based on trigger type (same as tagging step)
TAGS=()
# Always create latest tag
TAGS+=("${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest")
# Create version-specific tags based on trigger
if [ "${{ github.event_name }}" = "push" ] && [ "${{ github.ref }}" = "refs/heads/main" ]; then
# Main branch push: create timestamp and SHA tags
TAGS+=("${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-$(date +%Y%m%d-%H%M%S)")
TAGS+=("${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-${{ github.sha }}")
elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
# Manual trigger: create timestamp tag only
TAGS+=("${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-$(date +%Y%m%d-%H%M%S)")
elif [ "${{ github.event_name }}" = "schedule" ]; then
# Scheduled: create timestamp tag only
TAGS+=("${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-$(date +%Y%m%d-%H%M%S)")
fi
# Push all tags
for tag in "${TAGS[@]}"; do
echo "Pushing: $tag"
docker push "$tag"
done
echo "All tags pushed successfully"
- name: Run security scan
if: github.event.inputs.push_images != 'false'
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest
format: 'sarif'
output: 'trivy-results.sarif'
continue-on-error: true
- name: Install jq for SARIF parsing
if: github.event.inputs.push_images != 'false'
run: |
sudo apt-get update
sudo apt-get install -y jq
- name: Display local security scan results
if: github.event.inputs.push_images != 'false' && always()
run: |
echo "## 🔍 Local Security Scan Results" >> $GITHUB_STEP_SUMMARY
if [ -f "trivy-results.sarif" ]; then
echo "✅ Trivy security scan completed successfully" >> $GITHUB_STEP_SUMMARY
# Extract vulnerability count
VULNERABILITIES=$(jq '.runs[0].results | length' trivy-results.sarif 2>/dev/null || echo "0")
echo "- **Vulnerabilities found:** $VULNERABILITIES" >> $GITHUB_STEP_SUMMARY
# Show high/critical vulnerabilities
HIGH_CRITICAL=$(jq '.runs[0].results[] | select(.level == "error" or .level == "warning") | .level' trivy-results.sarif 2>/dev/null | wc -l || echo "0")
echo "- **High/Critical issues:** $HIGH_CRITICAL" >> $GITHUB_STEP_SUMMARY
# Show scan summary
echo "### 📊 Scan Summary" >> $GITHUB_STEP_SUMMARY
echo "- **Image scanned:** \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest\`" >> $GITHUB_STEP_SUMMARY
echo "- **Scan format:** SARIF" >> $GITHUB_STEP_SUMMARY
echo "- **Results file:** \`trivy-results.sarif\`" >> $GITHUB_STEP_SUMMARY
# Show some sample vulnerabilities if any
if [ "$VULNERABILITIES" -gt 0 ]; then
echo "### 🚨 Sample Vulnerabilities" >> $GITHUB_STEP_SUMMARY
jq -r '.runs[0].results[0:3][] | "- **\(.level)**: \(.message.text)"' trivy-results.sarif 2>/dev/null >> $GITHUB_STEP_SUMMARY || echo "Unable to parse vulnerability details" >> $GITHUB_STEP_SUMMARY
else
echo "### ✅ No vulnerabilities found" >> $GITHUB_STEP_SUMMARY
echo "The Docker image appears to be secure!" >> $GITHUB_STEP_SUMMARY
fi
else
echo "⚠️ Security scan results not available" >> $GITHUB_STEP_SUMMARY
echo "The Trivy scan may have failed or the results file was not generated." >> $GITHUB_STEP_SUMMARY
fi
- name: Upload Trivy scan results
if: github.event.inputs.push_images != 'false' && github.ref == 'refs/heads/main'
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results.sarif'
continue-on-error: true
- name: Display security scan results
if: github.event.inputs.push_images != 'false' && always()
run: |
echo "## 🔒 Security Scan Results" >> $GITHUB_STEP_SUMMARY
# Check if Advanced Security is available
if [ -f "trivy-results.sarif" ]; then
echo "✅ Security scan completed successfully" >> $GITHUB_STEP_SUMMARY
echo "📊 Scan results saved to trivy-results.sarif" >> $GITHUB_STEP_SUMMARY
# Display scan summary
echo "### 📋 Scan Summary" >> $GITHUB_STEP_SUMMARY
if command -v jq >/dev/null 2>&1; then
VULNERABILITIES=$(jq '.runs[0].results | length' trivy-results.sarif 2>/dev/null || echo "0")
echo "- **Vulnerabilities found:** $VULNERABILITIES" >> $GITHUB_STEP_SUMMARY
fi
echo "### 📄 Full Report" >> $GITHUB_STEP_SUMMARY
echo "Detailed scan results are available in the SARIF file." >> $GITHUB_STEP_SUMMARY
else
echo "⚠️ Security scan results not available" >> $GITHUB_STEP_SUMMARY
echo "This may be due to:" >> $GITHUB_STEP_SUMMARY
echo "- Advanced Security not enabled for this repository" >> $GITHUB_STEP_SUMMARY
echo "- Scan failed to complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Note:** Code scanning requires GitHub Advanced Security (paid feature)." >> $GITHUB_STEP_SUMMARY
echo "The Trivy scan still runs locally and results are available in the workflow logs." >> $GITHUB_STEP_SUMMARY
fi
test:
runs-on: ubuntu-latest
needs: build
if: github.event.inputs.push_images != 'false'
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Test Docker image functionality
run: |
echo "Testing Docker image functionality..."
# 拉取镜像
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest
# 基本验证
echo "Image size: $(docker images --format '{{.Size}}' ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest)"
echo "Image user: $(docker inspect --format='{{.Config.User}}' ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest)"
# 测试容器创建
CONTAINER_ID=$(docker create ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest echo "test")
if [ $? -eq 0 ]; then
echo "✅ Container creation successful"
docker rm $CONTAINER_ID
else
echo "❌ Container creation failed"
exit 1
fi
# 测试Python和UV(使用临时文件系统挂载)
echo "Testing Python:"
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"
echo "Testing UV:"
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"
echo "✅ All tests completed successfully"
generate-summary:
runs-on: ubuntu-latest
needs: [build, test]
if: always()
steps:
- name: Generate summary
run: |
echo "## 🐳 Docker Image Build Summary (Nix Flakes)" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Image:** \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Tags:**" >> $GITHUB_STEP_SUMMARY
echo "- \`secure-latest\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Build System:**" >> $GITHUB_STEP_SUMMARY
echo "- ✅ Nix Flakes for reproducible builds" >> $GITHUB_STEP_SUMMARY
echo "- ✅ Declarative environment management" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Features:**" >> $GITHUB_STEP_SUMMARY
echo "- ✅ Secure Python 3.12 environment" >> $GITHUB_STEP_SUMMARY
echo "- ✅ UV package manager" >> $GITHUB_STEP_SUMMARY
echo "- ✅ Gurobi optimization solver" >> $GITHUB_STEP_SUMMARY
echo "- ✅ Scientific computing packages" >> $GITHUB_STEP_SUMMARY
echo "- ✅ Non-root user execution" >> $GITHUB_STEP_SUMMARY
echo "- ✅ Resource limits and security restrictions" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Security:**" >> $GITHUB_STEP_SUMMARY
echo "- ✅ Trivy vulnerability scanning" >> $GITHUB_STEP_SUMMARY
echo "- ⚠️ Code scanning requires GitHub Advanced Security (paid feature)" >> $GITHUB_STEP_SUMMARY
echo "- ✅ Local security scan results available in workflow logs" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Registry:** [GitHub Container Registry](https://github.com/orgs/reaslab/packages)" >> $GITHUB_STEP_SUMMARY