From f431b2e2ff620a4e09370bba79778c86c02cdd8a Mon Sep 17 00:00:00 2001 From: puro ci Date: Sun, 19 Apr 2026 12:26:37 +0800 Subject: [PATCH] ci: add Drone pipeline for ai.puro.im deployment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - .drone.yml: pnpm build frontend → go build backend → docker compose up - .ci/Dockerfile: distroless:nonroot runtime image - host state (/opt/sub2api/{config.yaml,compose,volumes}) stays untouched Co-Authored-By: Claude Opus 4.7 (1M context) --- .ci/Dockerfile | 5 ++++ .ci/README.md | 13 +++++++++ .drone.yml | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 .ci/Dockerfile create mode 100644 .ci/README.md create mode 100644 .drone.yml diff --git a/.ci/Dockerfile b/.ci/Dockerfile new file mode 100644 index 00000000..6fe5b7e2 --- /dev/null +++ b/.ci/Dockerfile @@ -0,0 +1,5 @@ +FROM gcr.io/distroless/static-debian12:nonroot +WORKDIR /app +COPY sub2api-linux /app/sub2api +EXPOSE 8080 +ENTRYPOINT ["/app/sub2api"] diff --git a/.ci/README.md b/.ci/README.md new file mode 100644 index 00000000..aedfe7a3 --- /dev/null +++ b/.ci/README.md @@ -0,0 +1,13 @@ +# ai.puro.im CI artifacts + +Drone CI (see `.drone.yml`) builds a statically-linked `sub2api-linux` binary and bakes it into this distroless image. + +Host-side state (NOT in repo): +- `/opt/sub2api/docker-compose.yml` — sub2api + sub2api-pg + sub2api-redis services + PG password +- `/opt/sub2api/app-data/config.yaml` — wizard-generated runtime config +- `/opt/sub2api/{pg-data,redis-data,app-data,logs}` — persistent volumes + +Deploy flow: +1. Drone builds frontend (pnpm) + backend (go, linux/amd64) +2. CI copies `backend/sub2api-linux` + `.ci/Dockerfile` to `/opt/sub2api/` +3. CI runs `docker compose up -d --build sub2api` — rebuilds only sub2api service, leaves PG/Redis untouched diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 00000000..0825e689 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,75 @@ +kind: pipeline +type: docker +name: default + +trigger: + branch: [main] + event: [push] + +steps: + - name: build-frontend + image: node:18-alpine + commands: + - corepack enable + - corepack prepare pnpm@10.33.0 --activate + - cd frontend + - pnpm install --frozen-lockfile + - pnpm run build + volumes: + - name: pnpm-store + path: /root/.local/share/pnpm/store + + - name: build-backend + image: golang:1.23-alpine + environment: + CGO_ENABLED: "0" + GOOS: linux + GOARCH: amd64 + GOTOOLCHAIN: auto + GOFLAGS: "-buildvcs=false" + commands: + - apk add --no-cache git + - cd backend + - go build -tags embed -ldflags='-s -w' -o sub2api-linux ./cmd/server + volumes: + - name: go-cache + path: /root/.cache/go-build + - name: go-mod + path: /go/pkg/mod + depends_on: + - build-frontend + + - name: deploy + image: docker:cli + commands: + - cp backend/sub2api-linux /opt/sub2api/sub2api-linux + - cp .ci/Dockerfile /opt/sub2api/Dockerfile + - cd /opt/sub2api && docker compose up -d --build sub2api + - sleep 8 + - docker ps --filter 'name=^sub2api$' --filter 'status=running' --format '{{.Names}}' | grep -qx sub2api + - docker inspect sub2api --format='{{.State.Health.Status}} {{.State.Status}}' 2>/dev/null || true + - echo "deploy ok" + volumes: + - name: docker-sock + path: /var/run/docker.sock + - name: opt-sub2api + path: /opt/sub2api + depends_on: + - build-backend + +volumes: + - name: pnpm-store + host: + path: /opt/drone/cache/pnpm-store + - name: go-cache + host: + path: /opt/drone/cache/go-build + - name: go-mod + host: + path: /opt/drone/cache/go-mod + - name: docker-sock + host: + path: /var/run/docker.sock + - name: opt-sub2api + host: + path: /opt/sub2api