Add Gitea CI and automatic dev deployment
Main CI / check-and-build (push) Successful in 1m18s
Development CI / check-and-build (push) Successful in 34s
GitHub Git mirror / mirror (push) Successful in 7s
Development CI / deploy-development (push) Successful in 25s

This commit is contained in:
root committed 2026-10-29 21:30:28 +08:00
1 parent 543564a29c
commit b4f29d4966
20 files changed
+1109 -29

No files matched your search

+13 -3
View File
@@ -1,8 +1,18 @@
FROM python:3.13-slim
COPY --from=ghcr.io/astral-sh/uv:0.12 /uv /usr/local/bin/uv
ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy UV_PROJECT_ENVIRONMENT=/opt/venv PATH=/opt/venv/bin:$PATH
FROM python:3.13-slim AS base
COPY --from=ghcr.io/astral-sh/uv:0.12 /uv /uvx /usr/local/bin/
ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy UV_NO_CACHE=1 UV_PROJECT_ENVIRONMENT=/opt/venv PATH=/opt/venv/bin:$PATH
WORKDIR /app
COPY pyproject.toml uv.lock ./
RUN uv sync --locked --no-dev --no-install-project
COPY . .
RUN uv sync --locked --no-dev
FROM base AS check
RUN uv sync --locked && uv run pytest && uvx ruff==0.14.14 check --select F,E9,B src tests
FROM base AS runtime
ARG APP_VERSION=source
ARG APP_COMMIT=unknown
ENV APP_VERSION=$APP_VERSION APP_COMMIT=$APP_COMMIT
LABEL org.opencontainers.image.version=$APP_VERSION org.opencontainers.image.revision=$APP_COMMIT
LABEL org.opencontainers.image.source=https://git.134333.xyz/eezd/AlphaPulse
+15 -4
View File
@@ -1,11 +1,22 @@
FROM node:24-alpine AS build
RUN corepack enable
FROM node:24-alpine AS check
RUN corepack enable && corepack prepare [email protected] --activate
WORKDIR /app
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
RUN pnpm lint:check && pnpm typecheck && pnpm test
FROM nginx:alpine
FROM check AS build
RUN pnpm build
ARG APP_VERSION=source
ARG APP_COMMIT=unknown
ENV APP_VERSION=$APP_VERSION APP_COMMIT=$APP_COMMIT
RUN node -e 'require("fs").writeFileSync("dist/version.json", JSON.stringify({version:process.env.APP_VERSION,commit:process.env.APP_COMMIT}))'
FROM nginx:alpine AS runtime
ARG APP_VERSION=source
ARG APP_COMMIT=unknown
LABEL org.opencontainers.image.version=$APP_VERSION org.opencontainers.image.revision=$APP_COMMIT
LABEL org.opencontainers.image.source=https://git.134333.xyz/eezd/AlphaPulse
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
+45
View File
@@ -0,0 +1,45 @@
# AlphaPulse CI/CD
Gitea (`https://git.134333.xyz/eezd/AlphaPulse`) is the source repository; GitHub is a public, one-way mirror of all branches, tags and history. Develop on `dev`. `main` has independent checks and no deployed instance or release workflow.
## Checks and images
Both branch workflows run Python 3.13/uv checks, Node 24/pnpm 12.11.2 checks, Linux amd64 image builds and acceptance against temporary PostgreSQL/Redis. Acceptance runs on an internal Docker network with synthetic data; no worker or external model/trading service starts there. Foreign PRs do not enter the shared Docker runner.
Only a `dev` push publishes the accepted images:
- `git.134333.xyz/eezd/dev/backend/alphapulse:YYYYMMDD-xxxxxxxx`
- `git.134333.xyz/eezd/dev/web/alphapulse:YYYYMMDD-xxxxxxxx`
The date comes from the commit's committer timestamp in UTC, and the suffix is the first eight characters of its full SHA. Existing versions are immutable. Images are public on Gitea only; private configuration and data are excluded. `/api/health` and `/version.json` identify the deployed API and web revision. `build.json` is the CI receipt, not a tracked source file.
## The single data-bearing instance
`/docker/gitea/dev/AlphaPulse` contains the runtime Compose configuration, `.env`, digest selection, deployment receipts and `.deploy.lock`. The repository-root `.env` points to that sole private configuration. The Compose project is `alphapulse-dev`; `AlphaPulse-dev` is the web container, and the other five service names append `-api`, `-crawler`, `-market`, `-calendar` or `-trader`.
This project's DEV intentionally uses the existing database, Redis DB 14 (`ap:*` only), model credentials and Demo trading configuration. The user authorized restoring all six services. It is the only instance, served at `https://alpha.134333.xyz` through `127.0.0.1:21005`. CI never uses these data for testing. Do not start a second Compose project against this configuration.
Automatic and manual updates use the same command and persistent lock:
```sh
python3 scripts/deploy.py update --build /path/to/accepted/build.json
```
The command checks image identities, current dev SHA and database migration compatibility before updating. It waits for active Chat/trading analysis, records pending state, then starts API/web before the workers. An outdated dev build is skipped. A pending deployment blocks updates. Do not invoke `docker compose up` directly to bypass the lock.
A failed update with a previous deployment restores the prior images/configuration while retaining data. With no previous deployment, the pending marker remains for diagnosis. Recovery and rollback are explicit operator commands:
```sh
python3 scripts/deploy.py recover
python3 scripts/deploy.py rollback
```
Neither command restores database contents or resets trading orders. Schema migrations require separate authorization and are never part of CI auto-deployment. No scheduled backup is installed.
## Credentials and mirroring
Gitea secrets are `ALPHAPULSE_GITEA_TOKEN` (image publication) and `ALPHAPULSE_GITHUB_SSH_KEY` (a write-enabled deploy key restricted to the GitHub mirror). Ordinary checks and the deployment job do not receive publication secrets. Credentials are removed from job-local files on exit.
Git mirroring is independent of build results. Push/create/delete events, hourly reconciliation and manual dispatch use the same `.mirror.lock` in `/docker/gitea/mirrors/AlphaPulse`. Only business heads/tags are managed; internal PR references are excluded. Missing or incomplete source reads fail rather than imply deletion. GitHub is not an independent development source. GitHub Actions is disabled on the mirror.
Only resources labeled for the current CI attempt are cleaned; there is no global Docker prune. The existing runner registration, capacity and other projects' mount allowances are preserved.
+25 -12
View File
@@ -1,43 +1,56 @@
name: alphapulse
# Run through scripts/deploy.py so every deployment shares the instance lock.
name: alphapulse-dev
x-backend: &backend
build:
context: ../backend
dockerfile: ../deploy/Dockerfile.backend
image: alphapulse-backend
env_file: ../.env
image: ${ALPHAPULSE_BACKEND_IMAGE:?Set a verified backend digest}
env_file: .env
restart: unless-stopped
stop_grace_period: 60s
networks: [dev_default]
services:
api:
<<: *backend
container_name: AlphaPulse-dev-api
command: [uvicorn, alphapulse.api.main:app, --host, 0.0.0.0, --port, "8000", --proxy-headers, --forwarded-allow-ips, "172.18.0.0/16"]
healthcheck:
test: [CMD, python, -c, "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/api/health', timeout=3)"]
interval: 5s
timeout: 4s
retries: 12
crawler:
<<: *backend
container_name: AlphaPulse-dev-crawler
command: [python, -m, alphapulse.crawler.manager]
# 留时间给 scrapy 子进程优雅退出并刷完日志
stop_grace_period: 30s
market:
<<: *backend
container_name: AlphaPulse-dev-market
command: [python, -m, alphapulse.market.worker]
trader:
<<: *backend
container_name: AlphaPulse-dev-trader
command: [python, -m, alphapulse.trading.engine]
calendar:
<<: *backend
container_name: AlphaPulse-dev-calendar
command: [python, -m, alphapulse.calendar.worker]
web:
build:
context: ../web
dockerfile: ../deploy/Dockerfile.web
image: ${ALPHAPULSE_WEB_IMAGE:?Set a verified web digest}
container_name: AlphaPulse-dev
restart: unless-stopped
depends_on: [api]
depends_on:
api:
condition: service_healthy
healthcheck:
test: [CMD, wget, -q, -O, /dev/null, "http://127.0.0.1/api/health"]
interval: 5s
timeout: 4s
retries: 12
ports:
- 127.0.0.1:21005:80
networks: [dev_default]
+26 -6
View File
@@ -1,10 +1,30 @@
#!/bin/sh
# 迁移前先备份:只有一套环境,迁移即生产操作
# Migration remains an explicitly authorized operator action; never call this from CI.
set -eu
cd "$(dirname "$0")"
mkdir -p ../backups
dump="../backups/alphapulse-premigrate-$(date +%Y%m%d-%H%M%S).dump"
umask 077
instance=/docker/gitea/dev/AlphaPulse
exec 9>"$instance/.deploy.lock"
flock -x 9
if [ -e "$instance/deployment.pending.json" ]; then
echo '存在未完成部署,请先处理部署状态' >&2
exit 1
fi
if [ -z "${ALPHAPULSE_BACKEND_IMAGE:-}" ]; then
echo '请通过 ALPHAPULSE_BACKEND_IMAGE 明确指定已验收的候选镜像摘要;迁移须独立授权' >&2
exit 1
fi
case "$ALPHAPULSE_BACKEND_IMAGE" in
git.134333.xyz/eezd/dev/backend/alphapulse@sha256:*) ;;
*) echo '迁移必须使用开发镜像摘要' >&2; exit 1 ;;
esac
if [ -n "$(docker ps -q --filter label=com.docker.compose.project=alphapulse-dev)" ]; then
echo '迁移前须在维护窗口内停止应用服务;不要并发运行采集或交易' >&2
exit 1
fi
# Keep migration backups explicit; no periodic backup is introduced.
backup_dir="$(dirname "$0")/../backups"
mkdir -p "$backup_dir"
dump="$backup_dir/alphapulse-premigrate-$(date +%Y%m%d-%H%M%S).dump"
docker exec postgres pg_dump -U alphapulse -Fc alphapulse > "$dump"
echo "已备份:$dump"
docker compose build api
docker compose run --rm -T api alembic upgrade head
docker run --rm --network dev_default --env-file "$instance/.env" "$ALPHAPULSE_BACKEND_IMAGE" alembic upgrade head