feat: cicd配置
center / deploy (push) Failing after 5s

This commit is contained in:
2026-10-04 06:16:33 -07:00
parent a1782cc168
commit 78133f470f
5 changed files with 193 additions and 2 deletions
+16
View File
@@ -0,0 +1,16 @@
.git
.gitea
.idea
.venv
.uv-cache
.pytest_cache
.ruff_cache
__pycache__
*.py[cod]
.env
artifacts
logs
tests
docs
scripts
AGENTS.md
+127
View File
@@ -0,0 +1,127 @@
name: center
run-name: '${{ gitea.event.head_commit.message }}'
on:
push:
branches:
- main
concurrency:
group: deploy-center
cancel-in-progress: true
env:
IMAGE_NAME: center
HOST_PORT: 22788
CONTAINER_PORT: 8100
DOCKER_BUILDKIT: '0'
jobs:
deploy:
runs-on:
- self-hosted
timeout-minutes: 20
steps:
- name: Prepare environment
run: |
STEP_START=$(date +%s)
if ! command -v docker > /dev/null 2>&1; then
apk add docker-cli
fi
if ! command -v git > /dev/null 2>&1; then
apk add git
fi
if ! command -v node > /dev/null 2>&1; then
apk add nodejs
fi
docker --version
echo "Environment preparation took $(( $(date +%s) - STEP_START ))s"
- name: Checkout repository
uses: actions/checkout@v4
- name: Write runtime env file
env:
CENTER_ENV: ${{ secrets.CENTER_ENV }}
run: |
if [ -z "$CENTER_ENV" ]; then
echo "Secret CENTER_ENV is empty, put the full .env content into it"
exit 1
fi
umask 077
printf '%s\n' "$CENTER_ENV" > "$RUNNER_TEMP/center.env"
- name: Build application image
run: |
STEP_START=$(date +%s)
SHORT_SHA=$(echo "${{ gitea.sha }}" | cut -c1-8)
docker build \
--label "git.sha=${{ gitea.sha }}" \
-t $IMAGE_NAME:latest \
-t $IMAGE_NAME:$SHORT_SHA .
docker images $IMAGE_NAME:latest --format "Image size: {{.Size}}"
echo "Application image step took $(( $(date +%s) - STEP_START ))s"
- name: Run database migrations
run: |
STEP_START=$(date +%s)
docker run --rm \
--env-file "$RUNNER_TEMP/center.env" \
-e TZ=Asia/Shanghai \
$IMAGE_NAME:latest \
alembic upgrade head
echo "Migration step took $(( $(date +%s) - STEP_START ))s"
- name: Deploy container
run: |
STEP_START=$(date +%s)
if docker ps -a --filter "name=^${IMAGE_NAME}$" -q | grep -q .; then
docker stop --timeout 15 $IMAGE_NAME || true
docker rm $IMAGE_NAME || true
fi
docker run -d \
--name $IMAGE_NAME \
-p 127.0.0.1:$HOST_PORT:$CONTAINER_PORT \
--env-file "$RUNNER_TEMP/center.env" \
-e TZ=Asia/Shanghai \
--restart unless-stopped \
$IMAGE_NAME:latest
MAX_WAIT=90
WAITED=0
HEALTH="unknown"
while [ $WAITED -lt $MAX_WAIT ]; do
RUNNING_ID=$(docker ps --filter "name=^${IMAGE_NAME}$" --filter "status=running" -q)
HEALTH=$(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}' $IMAGE_NAME 2>/dev/null || echo "unknown")
if [ -n "$RUNNING_ID" ] && [ "$HEALTH" = "healthy" ]; then
break
fi
sleep 2
WAITED=$((WAITED + 2))
done
if [ "$HEALTH" = "healthy" ]; then
docker ps --filter "name=^${IMAGE_NAME}$" --format "Status: {{.Status}}"
echo "Startup wait: ${WAITED}s"
else
echo "Container not healthy after ${MAX_WAIT}s (health=$HEALTH)"
docker inspect $IMAGE_NAME --format 'ExitCode={{.State.ExitCode}} Error={{.State.Error}} StartedAt={{.State.StartedAt}} FinishedAt={{.State.FinishedAt}}' 2>/dev/null || true
docker logs --tail 30 $IMAGE_NAME || true
exit 1
fi
echo "Deployment step took $(( $(date +%s) - STEP_START ))s"
- name: Clean up
if: always()
run: |
rm -f "$RUNNER_TEMP/center.env"
docker image prune -f
- name: Show startup logs
if: always()
run: docker logs --tail 15 $IMAGE_NAME
+2 -1
View File
@@ -11,4 +11,5 @@ htmlcov/
tests/
docs/
AGENTS.md
AGENTS.md
logs/
+36
View File
@@ -0,0 +1,36 @@
FROM python:3.11-slim AS builder
ENV UV_COMPILE_BYTECODE=1 \
UV_LINK_MODE=copy \
UV_PROJECT_ENVIRONMENT=/app/.venv
WORKDIR /app
RUN pip install --no-cache-dir uv
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev --no-install-project
FROM python:3.11-slim
ENV PATH="/app/.venv/bin:$PATH" \
PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1
WORKDIR /app
RUN useradd --system --no-create-home --uid 10001 center
COPY --from=builder /app/.venv /app/.venv
COPY alembic.ini ./
COPY migrations ./migrations
COPY app ./app
USER center
EXPOSE 8100
HEALTHCHECK --interval=10s --timeout=5s --start-period=15s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8100/health', timeout=3)" || exit 1
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8100", "--proxy-headers", "--no-access-log"]
+12 -1
View File
@@ -1,11 +1,22 @@
# 管理共享 Redis 客户端
from redis.asyncio import Redis
from redis.asyncio.retry import Retry
from redis.backoff import ExponentialBackoff
from redis.exceptions import ConnectionError, TimeoutError
from app.core.config import get_settings
settings = get_settings()
redis_client: Redis = Redis.from_url(settings.redis_url, decode_responses=True)
# 远端会关闭空闲连接:先用 PING 检查再复用,坏连接自动重连重试
redis_client: Redis = Redis.from_url(
settings.redis_url,
decode_responses=True,
health_check_interval=30,
socket_keepalive=True,
retry=Retry(ExponentialBackoff(), 2),
retry_on_error=[ConnectionError, TimeoutError],
)
async def get_redis() -> Redis: