|
1 | | -name: Deploy to Server |
| 1 | +name: Auto Deploy to Server |
2 | 2 |
|
3 | 3 | on: |
4 | | - push: |
5 | | - branches: |
6 | | - - 'release/**' |
| 4 | + schedule: |
| 5 | + - cron: '0 16 */2 * *' # Every 2 days at 00:00 Beijing Time (16:00 UTC) |
| 6 | + workflow_dispatch: # Allow manual trigger |
7 | 7 |
|
8 | 8 | jobs: |
9 | | - deploy: |
| 9 | + check-and-deploy: |
10 | 10 | runs-on: ubuntu-latest |
11 | 11 |
|
12 | 12 | steps: |
13 | | - - name: Checkout code |
14 | | - uses: actions/checkout@v4.2.0 |
15 | | - |
16 | | - - name: Get latest image version |
17 | | - id: version |
18 | | - run: | |
19 | | - # 获取最新的短 commit SHA |
20 | | - VERSION="$(git rev-parse --short=7 HEAD)" |
21 | | - echo "version=$VERSION" >> $GITHUB_OUTPUT |
22 | | - echo "📦 Deploying Version: $VERSION" |
23 | | -
|
24 | | - - name: Deploy to server via SSH |
| 13 | + - name: Check for new Docker image and deploy |
25 | 14 | uses: appleboy/ssh-action@v1.2.1 |
26 | 15 | env: |
27 | 16 | DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} |
28 | 17 | DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} |
29 | | - VERSION: ${{ steps.version.outputs.version }} |
30 | 18 | with: |
31 | 19 | host: ${{ secrets.SERVER_HOST }} |
32 | 20 | username: ${{ secrets.SERVER_USERNAME }} |
33 | 21 | port: ${{ secrets.SERVER_PORT }} |
34 | 22 | password: ${{ secrets.SERVER_PASSWORD }} |
35 | 23 | command_timeout: 30m |
36 | | - envs: DOCKER_USERNAME,DOCKER_PASSWORD,VERSION |
| 24 | + envs: DOCKER_USERNAME,DOCKER_PASSWORD |
37 | 25 | script: | |
38 | 26 | IMAGE="${DOCKER_USERNAME}/docflow:latest" |
39 | | -
|
40 | | - echo "================================" |
41 | | - echo "🚀 Starting deployment" |
42 | | - echo "📦 Version: $VERSION" |
43 | | - echo "🐳 Image: $IMAGE" |
44 | | - echo "================================" |
45 | | -
|
46 | | - # 配置 Docker 镜像加速器 |
47 | | - echo "🔧 Configuring Docker registry mirrors..." |
48 | | - sudo tee /etc/docker/daemon.json > /dev/null <<'EOF' |
| 27 | + |
| 28 | + # Get remote image digest |
| 29 | + echo "🔍 Checking for updates..." |
| 30 | + REMOTE_DIGEST=$(docker manifest inspect $IMAGE 2>/dev/null | grep -m1 '"digest"' | cut -d'"' -f4) |
| 31 | + |
| 32 | + if [ -z "$REMOTE_DIGEST" ]; then |
| 33 | + echo "❌ Failed to fetch remote image info" |
| 34 | + exit 1 |
| 35 | + fi |
| 36 | + |
| 37 | + # Get current running container digest |
| 38 | + CURRENT_DIGEST=$(docker inspect docflow 2>/dev/null | grep -m1 '"Image"' | cut -d'"' -f4 | xargs docker inspect 2>/dev/null | grep -m1 '"Id"' | cut -d'"' -f4) |
| 39 | + |
| 40 | + # Compare digests |
| 41 | + if [ "$REMOTE_DIGEST" = "$CURRENT_DIGEST" ]; then |
| 42 | + echo "✅ Already running latest version" |
| 43 | + exit 0 |
| 44 | + fi |
| 45 | + |
| 46 | + echo "🆕 New version available, deploying..." |
| 47 | + |
| 48 | + # Configure Docker mirrors (only if not configured) |
| 49 | + if [ ! -f /etc/docker/daemon.json ] || ! grep -q "registry-mirrors" /etc/docker/daemon.json; then |
| 50 | + sudo tee /etc/docker/daemon.json > /dev/null <<'EOF' |
49 | 51 | { |
50 | 52 | "registry-mirrors": [ |
51 | 53 | "https://docker.m.daocloud.io", |
52 | 54 | "https://docker.1panel.live", |
53 | | - "https://hub.rat.dev", |
54 | | - "https://dockerproxy.com", |
55 | | - "https://hub-mirror.c.163.com" |
| 55 | + "https://hub.rat.dev" |
56 | 56 | ] |
57 | 57 | } |
58 | 58 | EOF |
59 | | -
|
60 | | - echo "✅ Registry mirrors configured" |
61 | | - cat /etc/docker/daemon.json |
62 | | -
|
63 | | - # 重启 Docker 服务 |
64 | | - echo "" |
65 | | - echo "🔄 Restarting Docker service..." |
66 | | - sudo systemctl daemon-reload |
67 | | - sudo systemctl restart docker |
68 | | - sleep 8 |
69 | | -
|
70 | | - # 等待 Docker 启动 |
71 | | - echo "⏳ Waiting for Docker to be ready..." |
72 | | - for i in {1..30}; do |
73 | | - if docker info > /dev/null 2>&1; then |
74 | | - echo "✅ Docker is ready" |
75 | | - break |
76 | | - fi |
77 | | - echo "Waiting... ($i/30)" |
78 | | - sleep 2 |
79 | | - done |
80 | | -
|
81 | | - # 验证镜像加速器配置 |
82 | | - echo "" |
83 | | - echo "📋 Docker registry mirrors:" |
84 | | - docker info | grep -A 10 "Registry Mirrors" || echo "Mirror info not available" |
85 | | -
|
86 | | - # 拉取镜像 |
87 | | - echo "" |
88 | | - echo "📦 Pulling image..." |
89 | | - docker pull $IMAGE |
90 | | -
|
91 | | - echo "✅ Image pulled successfully" |
92 | | -
|
93 | | - # 停止旧容器 |
94 | | - echo "" |
95 | | - echo "🛑 Stopping old container..." |
| 59 | + sudo systemctl daemon-reload |
| 60 | + sudo systemctl restart docker |
| 61 | + sleep 5 |
| 62 | + fi |
| 63 | + |
| 64 | + # Pull new image |
| 65 | + docker pull $IMAGE || exit 1 |
| 66 | + |
| 67 | + # Stop and remove old container |
96 | 68 | docker stop docflow 2>/dev/null || true |
97 | 69 | docker rm docflow 2>/dev/null || true |
98 | | -
|
99 | | - # 启动新容器 |
100 | | - echo "" |
101 | | - echo "🚀 Starting new container..." |
| 70 | + |
| 71 | + # Start new container |
102 | 72 | CONTAINER_ID=$(docker run -d \ |
103 | 73 | --name docflow \ |
104 | 74 | --restart unless-stopped \ |
105 | 75 | -p 3000:3000 \ |
106 | 76 | -e NODE_ENV=production \ |
107 | 77 | $IMAGE) |
108 | | -
|
| 78 | + |
109 | 79 | if [ -z "$CONTAINER_ID" ]; then |
110 | 80 | echo "❌ Failed to start container" |
111 | | - docker logout 2>/dev/null |
112 | 81 | exit 1 |
113 | 82 | fi |
114 | | -
|
115 | | - echo "Container ID: $CONTAINER_ID" |
116 | | -
|
117 | | - # 等待容器启动 |
118 | | - echo "⏳ Waiting for container to start..." |
| 83 | + |
| 84 | + # Wait and verify |
119 | 85 | sleep 5 |
120 | | -
|
121 | | - # 检查容器状态 |
122 | | - echo "" |
123 | | - echo "================================" |
124 | | - echo "📊 Container Status Check" |
125 | | - echo "================================" |
126 | | -
|
127 | 86 | CONTAINER_STATUS=$(docker inspect -f '{{.State.Status}}' docflow 2>/dev/null) |
128 | | -
|
| 87 | + |
129 | 88 | if [ "$CONTAINER_STATUS" = "running" ]; then |
130 | | - echo "✅ Container is running!" |
131 | | - echo "" |
132 | | - echo "📋 Container Info:" |
133 | | - docker ps --filter "name=docflow" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" |
134 | | - echo "" |
135 | | - echo "📋 Recent Logs:" |
136 | | - docker logs --tail 30 docflow |
137 | | - echo "" |
138 | | - echo "🎉 Deployment successful!" |
| 89 | + echo "✅ Deployment successful" |
| 90 | + docker logs --tail 20 docflow |
139 | 91 | else |
140 | | - echo "❌ Container is not running (Status: $CONTAINER_STATUS)" |
141 | | - echo "" |
142 | | - echo "📋 Container Logs:" |
| 92 | + echo "❌ Container failed to start" |
143 | 93 | docker logs docflow 2>&1 |
144 | | - echo "" |
145 | | - docker logout 2>/dev/null |
146 | 94 | exit 1 |
147 | 95 | fi |
148 | | -
|
149 | | - docker logout 2>/dev/null |
150 | | - echo "Done!" |
151 | | -
|
152 | | - - name: Deployment summary |
153 | | - if: always() |
154 | | - run: | |
155 | | - echo "## 🚀 Deployment Summary" >> $GITHUB_STEP_SUMMARY |
156 | | - echo "" >> $GITHUB_STEP_SUMMARY |
157 | | - echo "- 📦 Version: \`${{ steps.version.outputs.version }}\`" >> $GITHUB_STEP_SUMMARY |
158 | | - echo "- 🐳 Image: [Docker Hub](https://hub.docker.com/r/${{ secrets.DOCKER_USERNAME }}/docflow)" >> $GITHUB_STEP_SUMMARY |
159 | | - echo "- 🌿 Branch: \`${{ github.ref_name }}\`" >> $GITHUB_STEP_SUMMARY |
160 | | - echo "- ⏰ Time: $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_STEP_SUMMARY |
0 commit comments